/*
* Copyright 2012 Phil Pratt-Szeliga and other contributors
* http://chirrup.org/
*
* See the file LICENSE for copying permission.
*/
package org.trifort.rootbeer.generate.opencl.body;
import java.util.ArrayList;
import java.util.List;
import org.trifort.rootbeer.configuration.Configuration;
import org.trifort.rootbeer.generate.opencl.OpenCLClass;
import org.trifort.rootbeer.generate.opencl.OpenCLMethod;
import org.trifort.rootbeer.generate.opencl.OpenCLScene;
import org.trifort.rootbeer.generate.opencl.OpenCLType;
import org.trifort.rootbeer.util.Stack;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.Type;
import soot.Unit;
import soot.Value;
import soot.jimple.ArrayRef;
import soot.jimple.AssignStmt;
import soot.jimple.BreakpointStmt;
import soot.jimple.DefinitionStmt;
import soot.jimple.EnterMonitorStmt;
import soot.jimple.ExitMonitorStmt;
import soot.jimple.FieldRef;
import soot.jimple.GotoStmt;
import soot.jimple.IdentityStmt;
import soot.jimple.IfStmt;
import soot.jimple.IntConstant;
import soot.jimple.InvokeStmt;
import soot.jimple.LookupSwitchStmt;
import soot.jimple.NopStmt;
import soot.jimple.RetStmt;
import soot.jimple.ReturnStmt;
import soot.jimple.ReturnVoidStmt;
import soot.jimple.StmtSwitch;
import soot.jimple.TableSwitchStmt;
import soot.jimple.ThrowStmt;
import soot.options.Options;
import soot.rbclassload.NumberedType;
import soot.rbclassload.RootbeerClassLoader;
public class MethodStmtSwitch implements StmtSwitch {
protected StringBuilder m_output;
protected MethodJimpleValueSwitch m_valueSwitch;
protected final OpenCLBody m_parent;
protected SootMethod m_sootMethod;
private List<TrapItem> m_trapItems;
private int m_variableNumber;
private Stack<String> m_oldValueFromMonitorStack;
public MethodStmtSwitch(OpenCLBody parent, SootMethod soot_method){
m_sootMethod = soot_method;
m_output = new StringBuilder();
m_valueSwitch = new MethodJimpleValueSwitch(m_output);
m_parent = parent;
m_variableNumber = 1;
m_oldValueFromMonitorStack = new Stack<String>();
}
private String getVarName(){
String ret = "monitor_var"+m_variableNumber;
m_variableNumber++;
return ret;
}
public void popMonitor(){
m_oldValueFromMonitorStack.pop();
}
protected boolean methodReturnsAValue(){
OpenCLMethod ocl_method = new OpenCLMethod(m_sootMethod, m_sootMethod.getDeclaringClass());
return ocl_method.returnsAValue();
}
public void caseBreakpointStmt(BreakpointStmt arg0) {
//intentionally left blank
}
public void caseInvokeStmt(InvokeStmt arg0) {
arg0.getInvokeExpr().apply(m_valueSwitch);
SootMethod method = arg0.getInvokeExpr().getMethod();
m_output.append(";\n");
if(m_valueSwitch.getCheckException()){
checkException();
}
}
private void caseDefinitionStmt(DefinitionStmt arg0){
Value left_op = arg0.getLeftOp();
Value right_op = arg0.getRightOp();
Type left_op_type = left_op.getType();
OpenCLType ocl_left_op_type = new OpenCLType(left_op_type);
//if the left op is a field ref or array ref we use the setter
if(left_op instanceof FieldRef || left_op instanceof ArrayRef){
m_valueSwitch.setLhs();
left_op.apply(m_valueSwitch);
m_valueSwitch.setRhs();
right_op.apply(m_valueSwitch);
m_output.append(", exception);\n");
if(m_valueSwitch.getCheckException()){
checkException();
}
m_valueSwitch.clearLhsRhs();
}
//if the right op is a field ref or array ref we use the getter
else if(right_op instanceof FieldRef || right_op instanceof ArrayRef){
m_valueSwitch.setLhs();
left_op.apply(m_valueSwitch);
m_output.append(" = ");
m_valueSwitch.setRhs();
right_op.apply(m_valueSwitch);
m_output.append(";\n");
if(m_valueSwitch.getCheckException()){
checkException();
}
m_valueSwitch.clearLhsRhs();
}
//otherwise just use normal assignment
else {
m_valueSwitch.setLhs();
arg0.getLeftOp().apply(m_valueSwitch);
m_output.append(" = ");
m_valueSwitch.setRhs();
arg0.getRightOp().apply(m_valueSwitch);
m_output.append(";\n");
if(m_valueSwitch.getCheckException()){
checkException();
}
m_valueSwitch.clearLhsRhs();
}
}
public void caseAssignStmt(AssignStmt arg0) {
caseDefinitionStmt(arg0);
}
public void caseIdentityStmt(IdentityStmt arg0) {
caseDefinitionStmt(arg0);
}
public void caseEnterMonitorStmt(EnterMonitorStmt arg0) {
String id = getVarName();
String mem = getVarName();
String synch = getVarName();
String count = getVarName();
String old = getVarName();
m_oldValueFromMonitorStack.push(old);
OpenCLClass ocl_class = OpenCLScene.v().getOpenCLClass(m_sootMethod.getDeclaringClass());
OpenCLMethod ocl_method = ocl_class.getMethod(m_sootMethod.getSignature());
m_output.append("int "+id+" = getThreadId();\n");
m_output.append("char * "+mem+" = org_trifort_gc_deref(");
arg0.getOp().apply(m_valueSwitch);
m_output.append(");\n");
m_output.append("char * "+synch+" = org_trifort_gc_deref(");
arg0.getOp().apply(m_valueSwitch);
m_output.append(");\n");
m_output.append(mem+" += 16;\n");
m_output.append("int "+count+" = 0;\n");
m_output.append("int "+old+";\n");
m_output.append("while("+count+" < 100){\n");
m_output.append(" "+old+" = atomicCAS((int *) "+mem+", -1, "+id+");\n");
m_output.append(" if("+old+" != -1 && "+old+" != "+id+"){\n");
m_output.append(" "+count+"++;\n");
m_output.append(" if("+count+" >= 99){\n");
m_output.append(" "+count+" = 0;\n");
m_output.append(" }\n");
m_output.append(" } else {\n");
//the first write gets messed up in synch test cases
m_output.append(" * ( ( int * ) & "+synch+" [ 20 ] ) = 20 ;\n");
}
public void caseExitMonitorStmt(ExitMonitorStmt arg0) {
m_output.append("org_trifort_exitMonitorRef(");
arg0.getOp().apply(m_valueSwitch);
m_output.append(", "+m_oldValueFromMonitorStack.top()+");\n");
}
public void caseGotoStmt(GotoStmt arg0) {
Unit target = arg0.getTarget();
int label_num = m_parent.labelNum(target);
m_output.append("goto label" + Integer.toString(label_num) + ";\n");
}
public void caseIfStmt(IfStmt arg0) {
m_output.append("if (");
arg0.getCondition().apply(m_valueSwitch);
m_output.append(" ) goto label");
Unit target = arg0.getTarget();
int label_num = m_parent.labelNum(target);
m_output.append(Integer.toString(label_num)+";\n");
}
public void caseLookupSwitchStmt(LookupSwitchStmt arg0) {
m_output.append("switch(");
arg0.getKey().apply(m_valueSwitch);
m_output.append("){\n");
List<IntConstant> values = arg0.getLookupValues();
List<Unit> units = arg0.getTargets();
Unit default_target = arg0.getDefaultTarget();
int label_num;
for(int i = 0; i < values.size(); ++i){
m_output.append("case ");
values.get(i).apply(m_valueSwitch);
label_num = m_parent.labelNum(units.get(i));
m_output.append(": goto label"+Integer.toString(label_num) + ";\n");
}
label_num = m_parent.labelNum(default_target);
m_output.append("default: goto label"+Integer.toString(label_num) + ";\n");
m_output.append("}\n");
}
public void caseNopStmt(NopStmt arg0) {
//intentionally left blank
}
public void caseRetStmt(RetStmt arg0) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void caseReturnStmt(ReturnStmt arg0) {
if(m_sootMethod.isSynchronized()){
m_output.append("org_trifort_exitMonitorMem(mem, old);\n");
}
m_output.append("return ");
arg0.getOp().apply(m_valueSwitch);
m_output.append(";\n");
}
public void caseReturnVoidStmt(ReturnVoidStmt arg0) {
if(m_sootMethod.isSynchronized()){
m_output.append("org_trifort_exitMonitorMem(mem, old);\n");
}
m_output.append("return;\n");
}
public void caseTableSwitchStmt(TableSwitchStmt arg0) {
m_output.append("switch(");
arg0.getKey().apply(m_valueSwitch);
m_output.append("){\n");
List<Value> values = new ArrayList<Value>();
for(int i = arg0.getLowIndex(); i < arg0.getHighIndex(); ++i){
values.add(IntConstant.v(i));
}
List<Unit> units = arg0.getTargets();
Unit default_target = arg0.getDefaultTarget();
int label_num;
for(int i = 0; i < values.size(); ++i){
m_output.append("case ");
values.get(i).apply(m_valueSwitch);
label_num = m_parent.labelNum(units.get(i));
m_output.append(": goto label"+Integer.toString(label_num) + ";\n");
}
m_output.append("}\n");
}
public void caseThrowStmt(ThrowStmt arg0) {
m_output.append(" *exception = ");
Value op = arg0.getOp();
op.apply(m_valueSwitch);
m_output.append(";\n");
if(m_sootMethod.isSynchronized()){
m_output.append("org_trifort_exitMonitorMem(mem, old);\n");
}
m_output.append("return");
if(methodReturnsAValue())
m_output.append(" 0;\n");
else
m_output.append(";\n");
}
public void defaultCase(Object arg0) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void append(String string) {
m_output.append(string);
}
@Override
public String toString(){
return m_output.toString();
}
void setTrapItems(List<TrapItem> trap_items) {
m_trapItems = trap_items;
}
void reset() {
m_valueSwitch.reset();
}
boolean hasCaughtExceptionRef(){
return m_valueSwitch.hasCaughtExceptionRef();
}
public String getThisRef() {
return m_valueSwitch.getThisRef();
}
private void checkException() {
//if exceptions are turned off, do not check them
if(Configuration.compilerInstance().getExceptions() == false){
return;
}
String prefix = Options.v().rbcl_remap_prefix();
if(Options.v().rbcl_remap_all() == false){
prefix = "";
}
SootClass oom_cls = Scene.v().getSootClass(prefix+"java.lang.OutOfMemoryError");
SootClass null_cls = Scene.v().getSootClass(prefix+"java.lang.NullPointerException");
int oom_num = RootbeerClassLoader.v().getClassNumber(oom_cls);
int null_num = RootbeerClassLoader.v().getClassNumber(null_cls);
m_output.append("if(*exception != 0) { \n");
if(m_trapItems != null){
m_output.append(" GC_OBJ_TYPE_TYPE ex_type;\n");
//if exception is negative, then we didn't allocate memory for it.
m_output.append(" if(*exception == "+oom_num+" || *exception == "+null_num+"){\n");
m_output.append(" ex_type = *exception;\n");
m_output.append(" } else {\n");
m_output.append(" char * ex_deref = org_trifort_gc_deref(*exception);\n");
m_output.append(" ex_type = org_trifort_gc_get_type(ex_deref);\n");
m_output.append(" }\n");
m_output.append("if(0){}\n");
for(TrapItem item : m_trapItems){
m_output.append("else if(");
List<NumberedType> types = RootbeerClassLoader.v().getDfsInfo().getNumberedHierarchyUp(item.getException());
for(int i = 0; i < types.size(); ++i){
m_output.append("ex_type == "+types.get(i).getNumber());
if(i < types.size() - 1){
m_output.append(" || ");
}
}
m_output.append("){\n");
m_output.append("goto trap"+item.getTrapNum()+";\n");
m_output.append("}\n");
}
}
//mOutput.append("org_trifort_fillInStackTrace(*exception, \""+class_name+"\", \""+method_name+"\");\n");
if(m_sootMethod.isSynchronized()){
m_output.append("org_trifort_exitMonitorMem(mem, old);\n");
}
m_output.append("return ");
if(methodReturnsAValue())
m_output.append("0");
m_output.append("; }\n");
}
}