/*
* Copyright 2012 Phil Pratt-Szeliga and other contributors
* http://chirrup.org/
*
* See the file LICENSE for copying permission.
*/
package org.trifort.rootbeer.generate.bytecode;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import org.trifort.rootbeer.configuration.Configuration;
import org.trifort.rootbeer.configuration.RootbeerPaths;
import org.trifort.rootbeer.deadmethods.DeadMethods;
import org.trifort.rootbeer.generate.opencl.OpenCLScene;
import org.trifort.rootbeer.generate.opencl.tweaks.CompileResult;
import soot.*;
import soot.jimple.IntConstant;
import soot.jimple.Jimple;
import soot.jimple.JimpleBody;
import soot.jimple.StringConstant;
import soot.options.Options;
import soot.rbclassload.RootbeerClassLoader;
public class GenerateForKernel {
private MethodCodeSegment m_codeSegment;
private SootClass m_sootClass;
private List<Local> m_firstIterationLocals;
private Jimple m_jimple;
private String m_runtimeBasicBlockClassName;
private String m_serializerClassName;
public GenerateForKernel(SootMethod method, String uuid){
m_jimple = Jimple.v();
m_firstIterationLocals = new ArrayList<Local>();
m_sootClass = method.getDeclaringClass();
m_codeSegment = new MethodCodeSegment(method);
}
public Type getType(){
return m_sootClass.getType();
}
public void makeClass() throws Exception {
m_serializerClassName = m_codeSegment.getSootClass().getName()+"Serializer";
makeCpuBody();
makeGpuBody();
makeIsUsingGarbageCollectorBody();
makeIsReadOnly();
makeExceptionNumbers();
SerializerAdder adder = new SerializerAdder();
adder.add(m_codeSegment);
}
private void makeCpuBody() {
m_codeSegment.makeCpuBody(m_sootClass);
}
private void makeGetCodeMethodThatReturnsBytes(boolean m32, String filename) {
BytecodeLanguage bcl = new BytecodeLanguage();
bcl.openClass(m_sootClass);
SootClass string = Scene.v().getSootClass("java.lang.String");
bcl.startMethod("getCubin" + (m32 ? "32" : "64"), string.getType());
Local thisref = bcl.refThis();
bcl.returnValue(StringConstant.v(filename));
bcl.endMethod();
}
private void makeGetCubinSizeMethod(boolean m32, int length){
BytecodeLanguage bcl = new BytecodeLanguage();
bcl.openClass(m_sootClass);
bcl.startMethod("getCubin"+(m32 ? "32" : "64")+"Size", IntType.v());
Local thisref = bcl.refThis();
bcl.returnValue(IntConstant.v(length));
bcl.endMethod();
}
private void makeGetCubinErrorMethod(boolean m32, boolean error){
BytecodeLanguage bcl = new BytecodeLanguage();
bcl.openClass(m_sootClass);
bcl.startMethod("getCubin"+(m32 ? "32" : "64")+"Error", BooleanType.v());
Local thisref = bcl.refThis();
int intError = 0;
if(error == true){
intError = 1;
}
bcl.returnValue(IntConstant.v(intError));
bcl.endMethod();
}
private void makeGetCodeMethodThatReturnsString(String gpu_code, boolean unix){
//make the getCode method with the results of the opencl code generation
String name = "getCode";
if(unix){
name += "Unix";
} else {
name += "Windows";
}
SootMethod getCode = new SootMethod(name, new ArrayList(), RefType.v("java.lang.String"), Modifier.PUBLIC);
getCode.setDeclaringClass(m_sootClass);
m_sootClass.addMethod(getCode);
RootbeerClassLoader.v().addGeneratedMethod(getCode.getSignature());
JimpleBody body = m_jimple.newBody(getCode);
UnitAssembler assembler = new UnitAssembler();
//create an instance of self
Local thislocal = m_jimple.newLocal("this0", m_sootClass.getType());
Unit thisid = m_jimple.newIdentityStmt(thislocal, m_jimple.newThisRef(m_sootClass.getType()));
assembler.add(thisid);
//java string constants encoded in a class file have a maximum size of 65535...
//$r1 = new java.lang.StringBuilder;
SootClass string_builder_soot_class = Scene.v().getSootClass("java.lang.StringBuilder");
Local r1 = m_jimple.newLocal("r1", string_builder_soot_class.getType());
Value r1_assign_rhs = m_jimple.newNewExpr(string_builder_soot_class.getType());
Unit r1_assign = m_jimple.newAssignStmt(r1, r1_assign_rhs);
assembler.add(r1_assign);
//specialinvoke $r1.<java.lang.StringBuilder: void <init>()>();
SootMethod string_builder_ctor = string_builder_soot_class.getMethod("void <init>()");
Value r1_ctor = m_jimple.newSpecialInvokeExpr(r1, string_builder_ctor.makeRef(), new ArrayList());
Unit r1_ctor_unit = m_jimple.newInvokeStmt(r1_ctor);
assembler.add(r1_ctor_unit);
//r2 = $r1;
Local r2 = m_jimple.newLocal("r2", string_builder_soot_class.getType());
Unit r2_assign_r1 = m_jimple.newAssignStmt(r2, r1);
assembler.add(r2_assign_r1);
SootClass string_class = Scene.v().getSootClass("java.lang.String");
SootMethod string_builder_append = string_builder_soot_class.getMethod("java.lang.StringBuilder append(java.lang.String)");
GpuCodeSplitter splitter = new GpuCodeSplitter();
List<String> blocks = splitter.split(gpu_code);
for(String block : blocks){
Value curr_string_constant = StringConstant.v(block);
//virtualinvoke r2.<java.lang.StringBuilder: java.lang.StringBuilder append(java.lang.String)>("gpu code");
List args = new ArrayList();
args.add(curr_string_constant);
Value invoke_expr = m_jimple.newVirtualInvokeExpr(r2, string_builder_append.makeRef(), args);
Unit invoke_stmt = m_jimple.newInvokeStmt(invoke_expr);
assembler.add(invoke_stmt);
}
//$r5 = virtualinvoke r2.<java.lang.StringBuilder: java.lang.String toString()>();
Local r5 = m_jimple.newLocal("r5", string_class.getType());
SootMethod to_string = string_builder_soot_class.getMethod("java.lang.String toString()");
Value r5_rhs = m_jimple.newVirtualInvokeExpr(r2, to_string.makeRef());
Unit r5_assign = m_jimple.newAssignStmt(r5, r5_rhs);
assembler.add(r5_assign);
assembler.add(m_jimple.newReturnStmt(r5));
assembler.assemble(body);
getCode.setActiveBody(body);
}
private void makeGpuBody() throws Exception {
OpenCLScene.v().addCodeSegment(m_codeSegment);
if(Configuration.compilerInstance().getMode() == Configuration.MODE_GPU){
CompileResult[] result = OpenCLScene.v().getCudaCode();
for (CompileResult res : result) {
String suffix = res.is32Bit() ? "-32" : "-64";
if (res.getBinary() == null) {
makeGetCodeMethodThatReturnsBytes(res.is32Bit(), cubinFilename(false, suffix) + ".error");
makeGetCubinSizeMethod(res.is32Bit(), 0);
makeGetCubinErrorMethod(res.is32Bit(), true);
} else {
byte[] bytes = res.getBinary();
writeBytesToFile(bytes, cubinFilename(true, suffix));
makeGetCodeMethodThatReturnsBytes(res.is32Bit(), cubinFilename(false, suffix));
makeGetCubinSizeMethod(res.is32Bit(), bytes.length);
makeGetCubinErrorMethod(res.is32Bit(), false);
}
}
makeGetCodeMethodThatReturnsString("", true);
makeGetCodeMethodThatReturnsString("", false);
} else {
String[] code = OpenCLScene.v().getOpenCLCode();
//code[0] is unix
//code[1] is windows
PrintWriter writer = new PrintWriter(RootbeerPaths.v().getRootbeerHome()+"pre_dead_unix.c");
writer.println(code[0]);
writer.flush();
writer.close();
writer = new PrintWriter(RootbeerPaths.v().getRootbeerHome()+"pre_dead_windows.c");
writer.println(code[1]);
writer.flush();
writer.close();
System.out.println("removing dead methods...");
DeadMethods dead_methods = new DeadMethods();
dead_methods.parseString(code[0]);
code[0] = dead_methods.getResult();
dead_methods.parseString(code[1]);
code[1] = dead_methods.getResult();
//jpp can't handle declspec very well
code[1] = code[1].replace("void entry(char * gc_info_space,", "__declspec(dllexport)\nvoid entry(char * gc_info_space,");
makeGetCodeMethodThatReturnsString(code[0], true);
makeGetCodeMethodThatReturnsString(code[1], false);
makeGetCodeMethodThatReturnsBytes(true, "");
makeGetCodeMethodThatReturnsBytes(false, "");
}
}
private String cubinFilename(boolean use_class_folder, String suffix){
String class_name = File.separator +
m_serializerClassName.replace(".", File.separator) +
suffix + ".cubin";
if(use_class_folder)
return RootbeerPaths.v().getOutputClassFolder() + class_name;
else
return class_name;
}
private void writeBytesToFile(byte[] bytes, String filename) {
try {
File file = new File(filename);
File parent = file.getParentFile();
parent.mkdirs();
OutputStream os = new FileOutputStream(filename);
os.write(bytes);
os.flush();
os.close();
} catch(Exception ex){
ex.printStackTrace();
}
}
public SootField getField(String name, Type type){
return m_sootClass.getField(name, type);
}
public void addFirstIterationLocal(Local local) {
m_firstIterationLocals.add(local);
}
private void makeIsUsingGarbageCollectorBody() {
BytecodeLanguage bcl = new BytecodeLanguage();
bcl.openClass(m_sootClass);
bcl.startMethod("isUsingGarbageCollector", BooleanType.v());
bcl.refThis();
if(OpenCLScene.v().getUsingGarbageCollector())
bcl.returnValue(IntConstant.v(1));
else
bcl.returnValue(IntConstant.v(0));
bcl.endMethod();
}
public String getRuntimeBasicBlockName() {
return m_runtimeBasicBlockClassName;
}
public String getSerializerName() {
return m_serializerClassName;
}
private void makeIsReadOnly() {
BytecodeLanguage bcl = new BytecodeLanguage();
bcl.openClass(m_sootClass);
bcl.startMethod("isReadOnly", BooleanType.v());
bcl.refThis();
if(OpenCLScene.v().getReadOnlyTypes().isRootReadOnly())
bcl.returnValue(IntConstant.v(1));
else
bcl.returnValue(IntConstant.v(0));
bcl.endMethod();
}
private void makeExceptionNumbers() {
String prefix = Options.v().rbcl_remap_prefix();
if(Options.v().rbcl_remap_all() == false){
prefix = "";
}
makeExceptionMethod("getNullPointerNumber", prefix+"java.lang.NullPointerException");
makeExceptionMethod("getOutOfMemoryNumber", prefix+"java.lang.OutOfMemoryError");
}
private void makeExceptionMethod(String method_name, String cls_name) {
SootClass soot_class = Scene.v().getSootClass(cls_name);
int number = RootbeerClassLoader.v().getClassNumber(soot_class);
BytecodeLanguage bcl = new BytecodeLanguage();
bcl.openClass(m_sootClass);
bcl.startMethod(method_name, IntType.v());
bcl.refThis();
bcl.returnValue(IntConstant.v(number));
bcl.endMethod();
}
}