/*
* 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.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.trifort.rootbeer.generate.opencl.OpenCLClass;
import org.trifort.rootbeer.generate.opencl.OpenCLScene;
import org.trifort.rootbeer.generate.opencl.OpenCLType;
import org.trifort.rootbeer.generate.opencl.fields.OpenCLField;
import org.trifort.rootbeer.util.Stack;
import soot.*;
import soot.jimple.IntConstant;
import soot.jimple.Jimple;
import soot.jimple.StringConstant;
import soot.options.Options;
import soot.rbclassload.RootbeerClassLoader;
public class VisitorWriteGen extends AbstractVisitorGen {
private Stack<Local> m_CurrObj;
private Stack<Local> m_CurrentMem;
private Local m_Mem;
private Local m_TextureMem;
private Local m_Param0;
private Local m_Param1;
private Local m_RefParam;
private Local m_ReadOnlyParam;
private List<Type> m_OrderedHistory;
private Set<Type> mWriteToHeapMethodsMade;
private Set<String> m_VisitedWriter;
private boolean m_ApplicationClass;
private SootClass m_CurrClass;
private List<Value> m_ValuesWritten;
private Local m_Array;
public VisitorWriteGen(List<Type> ordered_history, String class_name,
BytecodeLanguage bcl){
m_bcl = new Stack<BytecodeLanguage>();
m_CurrObj = new Stack<Local>();
m_OrderedHistory = ordered_history;
mWriteToHeapMethodsMade = new HashSet<Type>();
m_VisitedWriter = new HashSet<String>();
m_ValuesWritten = new ArrayList<Value>();
m_bcl.push(bcl);
m_CurrentMem = new Stack<Local>();
m_gcObjVisitor = new Stack<Local>();
}
public void makeWriteToHeapMethod() {
SootClass obj_cls = Scene.v().getSootClass("java.lang.Object");
BytecodeLanguage bcl = m_bcl.top();
bcl.startMethod("doWriteToHeap", VoidType.v(), obj_cls.getType(), BooleanType.v(), LongType.v(), BooleanType.v());
m_thisRef = bcl.refThis();
m_gcObjVisitor.push(m_thisRef);
m_Param0 = bcl.refParameter(0);
m_Param1 = bcl.refParameter(1);
m_RefParam = bcl.refParameter(2);
m_ReadOnlyParam = bcl.refParameter(3);
m_Mem = bcl.refInstanceField(m_thisRef, "mMem");
m_TextureMem = bcl.refInstanceField(m_thisRef, "mTextureMem");
String label1 = getNextLabel();
String label2 = getNextLabel();
Local mem = bcl.local(m_Mem.getType());
bcl.ifStmt(m_ReadOnlyParam, "==", IntConstant.v(1), label1);
bcl.assign(mem, m_Mem);
bcl.gotoLabel(label2);
bcl.label(label1);
bcl.assign(mem, m_TextureMem);
bcl.label(label2);
m_CurrentMem.push(mem);
//make writers for java.lang.String and char[]
SootClass string_class = Scene.v().getSootClass("java.lang.String");
RefType string_type = string_class.getType();
String label = getNextLabel();
bcl.ifInstanceOfStmt(m_Param0, string_type, label);
makeWriteToHeapBodyForString(string_type);
mWriteToHeapMethodsMade.add(string_type);
bcl.label(label);
ArrayType char_array_type = ArrayType.v(CharType.v(), 1);
label = getNextLabel();
bcl.ifInstanceOfStmt(m_Param0, char_array_type, label);
makeWriteToHeapBodyForArrayType(char_array_type);
mWriteToHeapMethodsMade.add(char_array_type);
bcl.label(label);
for(Type type : m_OrderedHistory){
makeWriteToHeapMethodForType(type);
}
bcl.returnVoid();
bcl.endMethod();
}
private void makeWriteToHeapMethodForType(Type type){
if(type instanceof ArrayType == false &&
type instanceof RefType == false){
return;
}
if(mWriteToHeapMethodsMade.contains(type))
return;
mWriteToHeapMethodsMade.add(type);
if(type instanceof RefType){
RefType ref_type = (RefType) type;
SootClass soot_class = ref_type.getSootClass();
if(soot_class.getName().equals("java.lang.Object"))
return;
if(differentPackageAndPrivate(ref_type)){
return;
}
if(soot_class.isInterface()){
return;
}
if(m_classesToIgnore.contains(ref_type.getSootClass().getName())){
return;
}
}
if(typeIsPublic(type) == false)
return;
String label = getNextLabel();
BytecodeLanguage bcl = m_bcl.top();
bcl.ifInstanceOfStmt(m_Param0, type, label);
if(type instanceof ArrayType){
makeWriteToHeapBodyForArrayType((ArrayType) type);
} else {
RefType ref_type = (RefType) type;
makeWriteToHeapBodyForRefType(ref_type);
}
bcl.label(label);
}
private void makeWriteToHeapBodyForArrayType(ArrayType type) {
BytecodeLanguage bcl = m_bcl.top();
Local object_to_write_from = bcl.cast(type, m_Param0);
Local length = bcl.lengthof(object_to_write_from);
int class_id = RootbeerClassLoader.v().getClassNumber(type.toString());
BclMemory bcl_mem = new BclMemory(bcl, m_CurrentMem.top());
bcl_mem.writeByte((byte) 0); //ref_type count [0]
bcl_mem.writeByte((byte) 0); //garabage collector color [1]
bcl_mem.writeByte((byte) 0); //reserved [2]
bcl_mem.writeByte((byte) 0); //ctor used [3]
bcl_mem.writeInt(class_id); //class number [4]
Local size = bcl.local(IntType.v());
bcl.assign(size, IntConstant.v(Constants.SizeGcInfo));
Local element_size = bcl.local(IntType.v());
if(type.numDimensions == 1){
OpenCLType ocl_type = new OpenCLType(type.baseType);
bcl.assign(element_size, IntConstant.v(ocl_type.getSize()));
} else {
bcl.assign(element_size, IntConstant.v(4));
}
bcl.mult(element_size, length);
bcl.plus(size, element_size);
bcl_mem.writeInt(size); //object size [8]
bcl_mem.writeInt(length); //array length [12]
bcl_mem.writeInt(-1); //monitor [16]
bcl_mem.writeInt(0); //reserved [20]
bcl_mem.writeInt(0); //reserved [24]
bcl_mem.writeInt(0); //reserved [28]
//optimization for single-dimensional arrays of primitive types.
//doesn't work for chars yet because they are still stored as ints on the gpu
if(type.baseType instanceof PrimType && type.numDimensions == 1 &&
type.baseType.equals(CharType.v()) == false){
bcl.pushMethod(m_CurrentMem.top(), "writeArray", VoidType.v(), type);
bcl.invokeMethodNoRet(m_CurrentMem.top(), object_to_write_from);
bcl_mem.incrementAddress(element_size);
bcl.returnVoid();
return;
}
Local space_for_elements = bcl.local(IntType.v());
bcl.assign(space_for_elements, length);
bcl.mult(space_for_elements, IntConstant.v(4));
if(type.numDimensions != 1 || type.baseType instanceof RefType){
bcl_mem.startIntegerList();
bcl_mem.incrementAddress(space_for_elements);
}
Local i = bcl.local(IntType.v());
bcl.assign(i, IntConstant.v(0));
String end_for_label = getNextLabel();
String before_if_label = getNextLabel();
bcl.label(before_if_label);
bcl.ifStmt(i, "==", length, end_for_label);
Local curr = bcl.indexArray(object_to_write_from, i);
if(type.numDimensions != 1){
SootClass object_soot_class = Scene.v().getSootClass("java.lang.Object");
bcl.pushMethod(m_thisRef, "writeToHeap", LongType.v(), object_soot_class.getType(), BooleanType.v());
Local array_element = bcl.invokeMethodRet(m_thisRef, curr, m_Param1);
bcl_mem.addIntegerToList(array_element);
} else if(type.baseType instanceof RefType){
SootClass object_soot_class = Scene.v().getSootClass("java.lang.Object");
bcl.pushMethod(m_thisRef, "writeToHeap", LongType.v(), object_soot_class.getType(), BooleanType.v());
Local array_element = bcl.invokeMethodRet(m_thisRef, curr, m_Param1);
bcl_mem.addIntegerToList(array_element);
} else {
bcl_mem.writeVar(curr);
}
bcl.plus(i, 1);
bcl.gotoLabel(before_if_label);
bcl.label(end_for_label);
if(type.numDimensions != 1 || type.baseType instanceof RefType){
bcl_mem.endIntegerList();
}
//return ret;
bcl.returnVoid();
}
private void makeWriteToHeapBodyForString(RefType type){
BytecodeLanguage bcl = m_bcl.top();
BclMemory bcl_mem = new BclMemory(bcl, m_CurrentMem.top());
int class_id = RootbeerClassLoader.v().getClassNumber(type.toString());
SootClass soot_class = type.getSootClass();
Local object_to_write_from = bcl.cast(type, m_Param0);
int size = Constants.SizeGcInfo + 16;
int gc_count = 1;
bcl_mem.writeByte((byte) gc_count); //ref_type count [0]
bcl_mem.writeByte((byte) 0); //garabage collector color [1]
bcl_mem.writeByte((byte) 0); //reserved [2]
bcl_mem.writeByte((byte) 0); //ctor used [3]
bcl_mem.writeInt(class_id); //class number [4]
bcl_mem.writeInt(size); //object size [8]
bcl_mem.writeInt(0); //reserved [12]
bcl_mem.writeInt(-1); //monitor [16]
int written_size = 1+1+1+1+4+4+4+4;
bcl_mem.incrementAddress(Constants.SizeGcInfo - written_size);
bcl_mem.pushAddress();
int size_minus_gc_info = size - Constants.SizeGcInfo;
bcl_mem.incrementAddress(size_minus_gc_info);
m_CurrClass = soot_class;
m_CurrObj.push(object_to_write_from);
ArrayType char_array_type = ArrayType.v(CharType.v(), 1);
bcl.pushMethod(object_to_write_from, "toCharArray", char_array_type);
Local char_array = bcl.invokeMethodRet(object_to_write_from);
SootClass object_class = Scene.v().getSootClass("java.lang.Object");
bcl.pushMethod(m_gcObjVisitor.top(), "writeToHeap", LongType.v(),
object_class.getType(), BooleanType.v());
Local char_array_address = bcl.invokeMethodRet(m_gcObjVisitor.top(), char_array,
IntConstant.v(1));
m_ValuesWritten.add(char_array_address);
Local after_array_write_address = bcl_mem.getPointer();
bcl_mem.popAddress();
invokeWriteRefs(m_CurrClass, m_CurrentMem.top());
bcl_mem.align();
bcl_mem.setAddress(after_array_write_address);
BclMemory bcl_mem0 = new BclMemory(bcl, m_Mem);
bcl_mem0.align();
BclMemory bcl_mem1 = new BclMemory(bcl, m_TextureMem);
bcl_mem1.align();
bcl.returnVoid();
m_CurrObj.pop();
}
private void makeWriteToHeapBodyForRefType(RefType type){
BytecodeLanguage bcl = m_bcl.top();
BclMemory bcl_mem = new BclMemory(bcl, m_CurrentMem.top());
int class_id = RootbeerClassLoader.v().getClassNumber(type.toString());
SootClass soot_class = type.getSootClass();
Local object_to_write_from = bcl.cast(type, m_Param0);
OpenCLClass ocl_class = OpenCLScene.v().getOpenCLClass(soot_class);
int size = ocl_class.getSize();
int gc_count = ocl_class.getRefFieldsSize();
bcl_mem.writeByte((byte) gc_count); //ref_type count [0]
bcl_mem.writeByte((byte) 0); //garabage collector color [1]
bcl_mem.writeByte((byte) 0); //reserved [2]
bcl_mem.writeByte((byte) 0); //ctor used [3]
bcl_mem.writeInt(class_id); //class number [4]
bcl_mem.writeInt(size); //object size [8]
bcl_mem.writeInt(0); //reserved [12]
bcl_mem.writeInt(-1); //monitor [16]
int written_size = 1+1+1+1+4+4+4+4;
bcl_mem.incrementAddress(Constants.SizeGcInfo - written_size);
bcl_mem.pushAddress();
int size_minus_gc_info = size - Constants.SizeGcInfo;
bcl_mem.incrementAddress(size_minus_gc_info);
m_CurrClass = soot_class;
m_CurrObj.push(object_to_write_from);
writeFields(true);
Local after_array_write_address = bcl_mem.getPointer();
bcl_mem.popAddress();
invokeWriteRefs(m_CurrClass, m_CurrentMem.top());
bcl_mem.align();
writeFields(false);
bcl_mem.setAddress(after_array_write_address);
BclMemory bcl_mem0 = new BclMemory(bcl, m_Mem);
bcl_mem0.align();
BclMemory bcl_mem1 = new BclMemory(bcl, m_TextureMem);
bcl_mem1.align();
bcl.returnVoid();
m_CurrObj.pop();
}
private void writeFields(boolean ref_fields){
if(m_CurrClass.isApplicationClass()){
attachWriter(m_CurrClass.getName(), ref_fields);
callBaseClassWriter(m_CurrClass.getName(), ref_fields);
} else {
insertWriter(m_CurrClass.getName(), ref_fields);
}
}
public void invokeWriteRefs(SootClass curr_class, Local mem_local) {
BytecodeLanguage bcl = m_bcl.top();
if(curr_class.isApplicationClass()){
SootClass mem = Scene.v().getSootClass("org.trifort.rootbeer.runtime.Memory");
String specialization = JavaNameToOpenCL.convert(curr_class.getName())+OpenCLScene.v().getIdent();
bcl.pushMethod(m_CurrObj.top(), "org_trifort_writeRefs"+specialization, VoidType.v(), mem.getType());
bcl.invokeMethodNoRet(m_CurrObj.top(), mem_local);
} else {
BclMemory bcl_mem = new BclMemory(bcl, mem_local);
for(Value value : m_ValuesWritten){
bcl_mem.writeRef(value);
}
}
m_ValuesWritten.clear();
}
public void insertWriter(String class_name, boolean ref_fields){
m_ApplicationClass = false;
boolean application_class = generatingCodeInApplicationClass();
doWriter(class_name, ref_fields, application_class);
SootClass curr_class = Scene.v().getSootClass(class_name);
if(curr_class.hasSuperclass() == false)
return;
SootClass parent = curr_class.getSuperclass();
parent = Scene.v().getSootClass(parent.getName());
if(parent.getName().equals("java.lang.Object") == false){
if(parent.isApplicationClass()){
attachWriter(parent.getName(), ref_fields);
callBaseClassWriter(parent.getName(), ref_fields);
} else {
insertWriter(parent.getName(), ref_fields);
}
}
}
private boolean generatingCodeInApplicationClass() {
BytecodeLanguage bcl = m_bcl.top();
SootClass soot_class = bcl.getSootClass();
RefType gc_type = (RefType) m_gcObjVisitor.top().getType();
if(soot_class.getName().equals(gc_type.getClassName())){
return false;
} else {
return soot_class.isApplicationClass();
}
}
private int sizeRefsArray(SootClass soot_class) {
int ret = 0;
while(true){
ret += getRefFields(soot_class).size();
soot_class = Scene.v().getSootClass(soot_class.getSuperclass().getName());
if(soot_class.getName().equals("java.lang.Object"))
return ret;
if(soot_class.isApplicationClass())
return ret;
}
}
public void attachWriter(String class_name, boolean ref_fields){
String specialization;
if(ref_fields){
specialization = "RefFields";
} else {
specialization = "NonRefFields";
}
specialization += JavaNameToOpenCL.convert(class_name)+OpenCLScene.v().getIdent();
String visited_name = class_name + specialization;
if(m_VisitedWriter.contains(visited_name))
return;
m_VisitedWriter.add(visited_name);
SootClass curr_class = Scene.v().getSootClass(class_name);
SootClass parent = curr_class.getSuperclass();
parent = Scene.v().getSootClass(parent.getName());
if(parent.isApplicationClass()){
attachWriter(parent.getName(), ref_fields);
}
BytecodeLanguage bcl = new BytecodeLanguage();
m_bcl.push(bcl);
bcl.openClass(class_name);
SootClass mem = Scene.v().getSootClass("org.trifort.rootbeer.runtime.Memory");
bcl.startMethod("org_trifort_writeToHeap"+specialization, VoidType.v(), mem.getType(), m_gcObjVisitor.top().getType());
m_CurrObj.push(bcl.refThis());
m_CurrentMem.push(bcl.refParameter(0));
m_gcObjVisitor.push(bcl.refParameter(1));
m_ApplicationClass = true;
SootClass soot_class = Scene.v().getSootClass(class_name);
if(ref_fields){
ArrayType array = ArrayType.v(LongType.v(), 1);
m_Array = bcl.local(array);
bcl.addFieldToClass(m_Array, "org_trifort_refs_array"+OpenCLScene.v().getIdent());
Value array_instance = bcl.newArray(array, IntConstant.v(sizeRefsArray(soot_class)));
bcl.assign(m_Array, array_instance);
bcl.refInstanceFieldFromInput(m_CurrObj.top(), "org_trifort_refs_array"+OpenCLScene.v().getIdent(), m_Array);
}
doWriter(class_name, ref_fields, true);
if(parent.getName().equals("java.lang.Object") == false){
if(parent.isApplicationClass()){
callBaseClassWriter(parent.getName(), ref_fields);
} else {
insertWriter(parent.getName(), ref_fields);
}
}
bcl.returnVoid();
bcl.endMethod();
m_CurrObj.pop();
m_CurrentMem.pop();
m_gcObjVisitor.pop();
if(ref_fields){
bcl.startMethod("org_trifort_writeRefs"+JavaNameToOpenCL.convert(class_name)+OpenCLScene.v().getIdent(), VoidType.v(), mem.getType());
m_CurrObj.push(bcl.refThis());
m_CurrentMem.push(bcl.refParameter(0));
m_Array = bcl.refInstanceField(m_CurrObj.top(), "org_trifort_refs_array"+OpenCLScene.v().getIdent());
BclMemory bcl_mem = new BclMemory(bcl, m_CurrentMem.top());
for(int i = 0; i < sizeRefsArray(soot_class); ++i){
Local ref = bcl.indexArray(m_Array, IntConstant.v(i));
bcl_mem.writeRef(ref);
}
if(parent.isApplicationClass())
invokeWriteRefs(parent, m_CurrentMem.top());
bcl.returnVoid();
bcl.endMethod();
m_CurrObj.pop();
m_CurrentMem.pop();
}
m_bcl.pop();
}
public void doWriter(String class_name, boolean do_ref_fields, boolean is_application){
BytecodeLanguage bcl = m_bcl.top();
SootClass soot_class = Scene.v().getSootClass(class_name);
List<OpenCLField> ref_fields = getRefFields(soot_class);
List<OpenCLField> non_ref_fields = getNonRefFields(soot_class);
if(do_ref_fields){
int count = 0;
for(OpenCLField ref_field : ref_fields){
int constant = 1;
Local local;
if(ref_field.isInstance()){
local = writeRefField(ref_field, IntConstant.v(constant));
m_ValuesWritten.add(local);
} else {
local = writeStaticRefField(ref_field, IntConstant.v(constant));
m_ValuesWritten.add(local);
}
if(is_application){
bcl.assignArray(m_Array, IntConstant.v(count), local);
count++;
}
}
} else {
for(OpenCLField ocl_field : non_ref_fields){
writeNonRefField(ocl_field);
}
}
}
private Local writeRefField(OpenCLField ref_field, Value copy_values){
BytecodeLanguage bcl = m_bcl.top();
SootField soot_field = ref_field.getSootField();
SootClass obj = Scene.v().getSootClass("java.lang.Object");
Local field_value;
if(m_ApplicationClass){
field_value = bcl.refInstanceField(m_CurrObj.top(), soot_field.getName());
} else {
SootClass string = Scene.v().getSootClass("java.lang.String");
bcl.pushMethod(m_gcObjVisitor.top(), "readField", obj.getType(), obj.getType(), string.getType());
field_value = bcl.invokeMethodRet(m_gcObjVisitor.top(), m_CurrObj.top(), StringConstant.v(soot_field.getName()));
}
bcl.pushMethod(m_gcObjVisitor.top(), "writeToHeap", LongType.v(), obj.getType(), BooleanType.v());
return bcl.invokeMethodRet(m_gcObjVisitor.top(), field_value, copy_values);
}
private Local writeStaticRefField(OpenCLField ref_field, IntConstant copy_values) {
SootField soot_field = ref_field.getSootField();
BytecodeLanguage bcl = m_bcl.top();
//field = getField
Local field_value = bcl.refStaticField(m_CurrObj.top(), soot_field.getName());
SootClass object_soot_class = Scene.v().getSootClass("java.lang.Object");
bcl.pushMethod(m_gcObjVisitor.top(), "writeToHeap", LongType.v(), object_soot_class.getType(), BooleanType.v());
return bcl.invokeMethodRet(m_gcObjVisitor.top(), field_value, copy_values);
}
private void writeNonRefField(OpenCLField non_ref_field){
SootField soot_field = non_ref_field.getSootField();
String function_name = "write"+getTypeString(soot_field);
BytecodeLanguage bcl = m_bcl.top();
Local field_value;
if(m_ApplicationClass){
field_value = bcl.refInstanceField(m_CurrObj.top(), soot_field.getName());
} else {
SootClass obj = Scene.v().getSootClass("java.lang.Object");
SootClass string = Scene.v().getSootClass("java.lang.String");
String private_field_fun_name = "read"+getTypeString(soot_field);
Local private_fields = bcl.newInstance("org.trifort.rootbeer.runtime.PrivateFields");
bcl.pushMethod(private_fields, private_field_fun_name, soot_field.getType(), obj.getType(), string.getType(), string.getType());
field_value = bcl.invokeMethodRet(private_fields, m_CurrObj.top(), StringConstant.v(soot_field.getName()), StringConstant.v(soot_field.getDeclaringClass().getName()));
}
bcl.pushMethod(m_CurrentMem.top(), function_name, VoidType.v(), soot_field.getType());
bcl.invokeMethodNoRet(m_CurrentMem.top(), field_value);
}
public void callBaseClassWriter(String class_name, boolean ref_fields) {
SootClass mem = Scene.v().getSootClass("org.trifort.rootbeer.runtime.Memory");
String specialization;
if(ref_fields){
specialization = "RefFields";
} else {
specialization = "NonRefFields";
}
specialization += JavaNameToOpenCL.convert(class_name)+OpenCLScene.v().getIdent();
BytecodeLanguage bcl = m_bcl.top();
bcl.pushMethod(class_name, "org_trifort_writeToHeap"+specialization, VoidType.v(), mem.getType(), m_gcObjVisitor.top().getType());
bcl.invokeMethodNoRet(m_CurrObj.top(), m_CurrentMem.top(), m_gcObjVisitor.top());
}
}