Package org.renjin.compiler

Source Code of org.renjin.compiler.ByteCodeVisitor

package org.renjin.compiler;

import com.google.common.collect.Maps;

import edu.uci.ics.jung.graph.util.Context;

import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.renjin.compiler.cfg.BasicBlock;
import org.renjin.compiler.ir.ssa.PhiFunction;
import org.renjin.compiler.ir.ssa.SsaVariable;
import org.renjin.compiler.ir.tac.IRLabel;
import org.renjin.compiler.ir.tac.expressions.*;
import org.renjin.compiler.ir.tac.statements.*;
import org.renjin.sexp.FunctionCall;
import org.renjin.sexp.SEXP;
import org.renjin.sexp.Symbol;

import java.util.Map;

public class ByteCodeVisitor implements StatementVisitor, ExpressionVisitor, Opcodes {
 
  private GenerationContext generationContext;
  private MethodVisitor mv;
  private Map<LValue, Integer> variableSlots = Maps.newHashMap();
  private Map<IRLabel, Label> labels = Maps.newHashMap();
 
  private int work1;
  private int localVariablesStart;
 
 
  public ByteCodeVisitor(GenerationContext generationContext, MethodVisitor mv) {
    super();
    this.generationContext = generationContext;
    this.mv = mv;
    this.work1 = generationContext.getFirstFreeLocalVariable();
    this.localVariablesStart = work1 + 1;
  }
 
  @Override
  public void visitAssignment(Assignment assignment) {
    LValue lhs = assignment.getLHS();
    if(lhs instanceof EnvironmentVariable) {
      environmentAssignment(((EnvironmentVariable)lhs).getName(),
          assignment.getRHS());
    } else {
      localVariableAssignment(lhs, assignment.getRHS());
    }
  }


  /**
   * Assigns a value into a local variable slot
   */
  private void localVariableAssignment(LValue lhs, Expression rhs) {
   
    if(rhs instanceof Increment) {
      Increment inc = (Increment) rhs;
      if(inc.getCounter().equals(lhs)) {
        mv.visitIincInsn(getVariableSlot(lhs), 1);
        return;
      }
    } else if(rhs instanceof Constant ) {
      // need to generalize this to accommodate primitive results from
      // methods as well.
      // the following just handles the special case of the integer
      // loop counter for _for_ loops.
     
      Constant constant = (Constant) rhs;
      constant.accept(this);
     
      if(constant.getValue() instanceof Integer) {
        mv.visitVarInsn(ISTORE, getVariableSlot(lhs));
      } else {
        mv.visitVarInsn(ASTORE, getVariableSlot(lhs));
      }
    } else if(rhs instanceof Length) {
      rhs.accept(this);
      mv.visitVarInsn(ISTORE, getVariableSlot(lhs));
     
    } else {
     
      rhs.accept(this);
 
      mv.visitVarInsn(ASTORE, getVariableSlot(lhs));
    }
  }


 
  /**
   * Assign a value into the context's {@code Environment}
   */
  private void environmentAssignment(Symbol name, Expression rhs) {
    loadContext();
    mv.visitMethodInsn(INVOKEVIRTUAL, "org/renjin/eval/Context", "getEnvironment", "()Lorg/renjin/sexp/Environment;");
    mv.visitLdcInsn(name.getPrintName());
    mv.visitMethodInsn(INVOKESTATIC, "org/renjin/sexp/Symbol", "get", "(Ljava/lang/String;)Lorg/renjin/sexp/Symbol;");
   
    rhs.accept(this);

    mv.visitMethodInsn(INVOKEVIRTUAL, "org/renjin/sexp/Environment", "setVariable", "(Lorg/renjin/sexp/Symbol;Lorg/renjin/sexp/SEXP;)V");
  }

  public void startBasicBlock(BasicBlock bb) {
    if(bb.getLabels() != null) {
      for(IRLabel label : bb.getLabels()) {
        mv.visitLabel(getAsmLabel(label));       
      }
    }
  }

  @Override
  public void visitConstant(Constant constant) {
    if(constant.getValue() instanceof SEXP) {
      SEXP exp = (SEXP)constant.getValue();
      exp.accept(new ConstantGeneratingVisitor(mv));
    } else if (constant.getValue() instanceof Integer) {
      ByteCodeUtil.pushInt(mv, (Integer)constant.getValue());
    } else {
      throw new UnsupportedOperationException();
    }
  }

  @Override
  public void visitDynamicCall(DynamicCall call) {
   
    if(call.getFunctionSexp() instanceof Symbol) {
      loadEnvironment();
      loadContext();
      mv.visitLdcInsn(((Symbol)call.getFunctionSexp()).getPrintName());
      mv.visitMethodInsn(INVOKESTATIC, "org/renjin/sexp/Symbol", "get", "(Ljava/lang/String;)Lorg/renjin/sexp/Symbol;");
      mv.visitMethodInsn(INVOKEVIRTUAL, "org/renjin/sexp/Environment", "findFunctionOrThrow", "(Lorg/renjin/eval/Context;Lorg/renjin/sexp/Symbol;)Lorg/renjin/sexp/Function;");
    } else {
      // otherwise we need to evaluate the function
      call.getFunction().accept(this);   
    }
      
    Label finish = new Label();

   
    // construct a new PairList with the argument
    Label builtinCall = new Label();
    mv.visitInsn(DUP);
    mv.visitTypeInsn(INSTANCEOF, "org/renjin/sexp/BuiltinFunction");
    mv.visitJumpInsn(IFNE, builtinCall);
   
    Label closureCall = new Label();
    mv.visitInsn(DUP);
    mv.visitTypeInsn(INSTANCEOF, "org/renjin/sexp/Closure");
    mv.visitJumpInsn(IFNE, closureCall);
   
    // APPLY SPECIAL
    applySpecialDynamically(call);
    mv.visitJumpInsn(GOTO, finish);
   
    // APPLY closure
    mv.visitLabel(closureCall);
    applyClosureDynamically(call);
    mv.visitJumpInsn(GOTO, finish);
       
    // APPLY builtin
    mv.visitLabel(builtinCall);
    applyBuiltinDynamically(call);
 
    mv.visitLabel(finish);
   
  }
 
  private void applySpecialDynamically(DynamicCall call) {
    // here we just send the arguments untouched as SEXPs
    // to the function.
    //
    // For the most part, we try to translate R's special
    // functions like `if`, `while`, `for` etc to JVM control
    // structure, but it is still possible for them to be called
    // dynamically so we need to be prepared.
   
    loadContext();
    loadEnvironment();
    pushSexp(call.getSExpression());
    mv.visitInsn(DUP);
    mv.visitMethodInsn(INVOKEVIRTUAL, "org/renjin/sexp/FunctionCall", "getArguments", "()Lorg/renjin/sexp/PairList;");
    mv.visitMethodInsn(INVOKEINTERFACE, "org/renjin/sexp/Function", "apply",
        "(Lorg/renjin/eval/Context;Lorg/renjin/sexp/Environment;Lorg/renjin/sexp/FunctionCall;Lorg/renjin/sexp/PairList;)Lorg/renjin/sexp/SEXP;");
  }

  private void loadEnvironment() {
    mv.visitVarInsn(ALOAD, generationContext.getEnvironmentLdc());
  }

  private void applyClosureDynamically(DynamicCall call) {
   
    mv.visitTypeInsn(CHECKCAST, "org/renjin/sexp/Closure");      
    loadContext();
    loadEnvironment();
   
    pushSexp(call.getSExpression());
   
    // build the pairlist of promises
    mv.visitTypeInsn(NEW, "org/renjin/sexp/PairList$Builder");
    mv.visitInsn(DUP);
    mv.visitMethodInsn(INVOKESPECIAL, "org/renjin/sexp/PairList$Builder", "<init>", "()V");
 
    for(int i=0;i!=call.getArguments().size();++i) {
      Expression argument = call.getArguments().get(i);
      if(argument == Elipses.INSTANCE) {
        loadElipses();
        mv.visitMethodInsn(INVOKEVIRTUAL, "org/renjin/sexp/PairList$Builder", "addAll",
        "(Lorg/renjin/sexp/PairList;)Lorg/renjin/sexp/PairList$Builder;");
      } else {
       
        if(call.getArgumentNames().get(i)!=null) {
          mv.visitLdcInsn(call.getArgumentNames().get(i));
        }
       
        if(argument instanceof IRThunk) {
          if(argument.getSExpression() instanceof Symbol) {
            Symbol symbol = (Symbol) argument.getSExpression();
            // create a promise to a variable in this scope
            mv.visitTypeInsn(NEW, "org/renjin/compiler/runtime/VariablePromise");
            mv.visitInsn(DUP);
            loadContext();
            mv.visitLdcInsn(symbol.getPrintName());
            mv.visitMethodInsn(INVOKESPECIAL, "org/renjin/compiler/runtime/VariablePromise", "<init>",
                "(Lorg/renjin/eval/Context;Ljava/lang/String;)V");
           
          } else {
            // instantatiate our compiled thunk class
            String thunkClass = generationContext.getThunkMap().getClassName((IRThunk)argument);
            mv.visitTypeInsn(NEW, thunkClass);
            mv.visitInsn(DUP);
            loadContext();
            loadEnvironment();
            mv.visitMethodInsn(INVOKESPECIAL, thunkClass , "<init>",
                "(Lorg/renjin/eval/Context;Lorg/renjin/sexp/Environment;)V");
           
          }
        } else {
          argument.accept(this);
        }
       
        if(call.getArgumentNames().get(i)!=null) {
          mv.visitMethodInsn(INVOKEVIRTUAL, "org/renjin/sexp/PairList$Builder", "add",
          "(Ljava/lang/String;Lorg/renjin/sexp/SEXP;)Lorg/renjin/sexp/PairList$Builder;");
        } else {
          mv.visitMethodInsn(INVOKEVIRTUAL, "org/renjin/sexp/PairList$Builder", "add",
              "(Lorg/renjin/sexp/SEXP;)Lorg/renjin/sexp/PairList$Builder;");
        }
      }
    }
    mv.visitMethodInsn(INVOKEVIRTUAL, "org/renjin/sexp/PairList$Builder", "build", "()Lorg/renjin/sexp/PairList;");   
    mv.visitMethodInsn(INVOKEVIRTUAL, "org/renjin/sexp/Closure", "matchAndApply",
        "(Lorg/renjin/eval/Context;Lorg/renjin/sexp/Environment;Lorg/renjin/sexp/FunctionCall;Lorg/renjin/sexp/PairList;)Lorg/renjin/sexp/SEXP;");
  
  }


  private void loadContext() {
    mv.visitVarInsn(ALOAD, generationContext.getContextLdc());
  }

  private void applyBuiltinDynamically(DynamicCall call) {
    mv.visitTypeInsn(CHECKCAST, "org/renjin/sexp/BuiltinFunction");
   
    maybeStoreElipses(call);

    loadContext();
    loadEnvironment();
    pushSexp(call.getCall());
    pushArgNames(call);
    maybeSpliceArgumentNames(call);
   
    if(call.hasElipses()) {
      loadContext();
      pushEvaluatedArgs(call);     
      spliceArgumentValues(call);
    } else {
      pushEvaluatedArgs(call);     
    }

    mv.visitMethodInsn(INVOKEVIRTUAL, "org/renjin/sexp/BuiltinFunction", "apply",
        "(Lorg/renjin/eval/Context;Lorg/renjin/sexp/Environment;Lorg/renjin/sexp/FunctionCall;[Ljava/lang/String;[Lorg/renjin/sexp/SEXP;)Lorg/renjin/sexp/SEXP;");
  }

  /**
   * Pushes the array of argument names onto the stack.
   */
  private void pushArgNames(CallExpression call) {
   
    pushInt(call.getArgumentNames().size());
    mv.visitTypeInsn(ANEWARRAY, "java/lang/String");
  
    for(int i=0;i!=call.getArgumentNames().size();++i) {
      if(call.getArgumentNames().get(i) != null) {
        mv.visitInsn(DUP);
        ByteCodeUtil.pushInt(mv, i);
        mv.visitLdcInsn( call.getArgumentNames().get(i) );
        mv.visitInsn(AASTORE);
      }
    } 
  }

  /**
   * If the call includes an '...' argument, generate the call
   * to splice the remaining arguments to this function into
   * the argument list.
   */
  private void maybeSpliceArgumentNames(CallExpression call) {
    if(call.hasElipses()) {
      // insert the elipses argument names
      mv.visitVarInsn(ALOAD, work1); // '...' pairlist
      pushInt(call.getElipsesIndex());
      mv.visitMethodInsn(INVOKESTATIC, "org/renjin/compiler/runtime/CompiledRuntime", "spliceArgNames",
            "([Ljava/lang/String;Lorg/renjin/sexp/PairList;I)[Ljava/lang/String;");
    }
  }

  private void pushSexp(FunctionCall call) {
    generationContext.getSexpPool().pushSexp(mv, call, "Lorg/renjin/sexp/FunctionCall;");
  }

  /**
   * If our dynamic call resolves to a primitive at runtime, then
   * evaluate the arguments inline to avoid the overhead of creating
   * thunks.
   */
  private void pushEvaluatedArgs(DynamicCall call) {

    // create array
    pushInt(call.getArguments().size());
    mv.visitTypeInsn(ANEWARRAY, "org/renjin/sexp/SEXP");
   
    for(int i=0; i!=call.getArguments().size();++i) {
      if(call.getArguments().get(i) != Elipses.INSTANCE) {
        // keep the array on the stack
        mv.visitInsn(DUP);
        pushInt(i);
   
        Expression arg = call.getArguments().get(i);
        if(arg instanceof IRThunk) {
          SEXP sexp = ((IRThunk) arg).getSEXP();
          if(sexp instanceof Symbol) {
            // since this is a simple case, just do it inline
            visitEnvironmentVariable(new EnvironmentVariable((Symbol) sexp));
          } else {
            // otherwise call out to the corresponding thunk's
            // static method. We rely on the jvm to inline at runtime
            // if necessary
            loadContext();
            loadEnvironment();
           
            String thunkClass = generationContext.getThunkMap().getClassName((IRThunk)arg);
            mv.visitMethodInsn(INVOKESTATIC, thunkClass , "doEval",
                "(Lorg/renjin/eval/Context;Lorg/renjin/sexp/Environment;)Lorg/renjin/sexp/SEXP;");
          }
        } else {
          arg.accept(this);
        }
        mv.visitInsn(AASTORE);
      }
    }
  }


  @Override
  public void visitElementAccess(ElementAccess expr) {
    expr.getVector().accept(this);
    expr.getIndex().accept(this);
   
    mv.visitMethodInsn(INVOKEINTERFACE, "org/renjin/sexp/Vector", "getElementAsSEXP", "(I)Lorg/renjin/sexp/SEXP;");   
  }

  @Override
  public void visitEnvironmentVariable(EnvironmentVariable variable) {
    loadEnvironment();
    mv.visitLdcInsn(variable.getName().getPrintName());
    mv.visitMethodInsn(INVOKEVIRTUAL, "org/renjin/sexp/Environment", "findVariableOrThrow", "(Ljava/lang/String;)Lorg/renjin/sexp/SEXP;");   
    // ensure that promises are forced
    loadContext();
    mv.visitMethodInsn(INVOKEINTERFACE, "org/renjin/sexp/SEXP", "force", "(Lorg/renjin/eval/Context;)Lorg/renjin/sexp/SEXP;");

  }

  @Override
  public void visitIncrement(Increment increment) {
    throw new UnsupportedOperationException();
  }

  @Override
  public void visitLocalVariable(LocalVariable variable) {
    mv.visitVarInsn(ILOAD, getVariableSlot(variable));   
  }
 
  @Override
  public void visitMakeClosure(MakeClosure closure) {
    String closureClass = generationContext.addClosure(closure.getFunction());
    mv.visitTypeInsn(NEW, closureClass);
    mv.visitInsn(DUP);
    loadEnvironment();
    mv.visitMethodInsn(INVOKESPECIAL, closureClass, "<init>", "(Lorg/renjin/sexp/Environment;)V");
  }

  @Override
  public void visitPrimitiveCall(PrimitiveCall call) {

    loadContext();
    loadEnvironment();
   
    // push the original function call on the stack
    pushSexp(call.getSExpression());

    // retrieve the value of '...' from the environment, which
    // will contain a pair list of promises

    maybeStoreElipses(call);

    // push the argument names
    pushArgNames(call);
    maybeSpliceArgumentNames(call);
   
    // push the argument values
   
    if(call.hasElipses()) {
      loadContext();
      pushPrimitiveArgArray(call);
      spliceArgumentValues(call);     
    } else {
      pushPrimitiveArgArray(call);
    }
   
    mv.visitMethodInsn(INVOKESTATIC, call.getWrapperClass().getName().replace('.', '/'), "doApply",
        "(Lorg/renjin/eval/Context;Lorg/renjin/sexp/Environment;Lorg/renjin/sexp/FunctionCall;[Ljava/lang/String;[Lorg/renjin/sexp/SEXP;)Lorg/renjin/sexp/SEXP;");
  }

  private void spliceArgumentValues(CallExpression call) {

   
    // insert the elipses argument values
    mv.visitVarInsn(ALOAD, work1); // '...' pairlist
    pushInt(call.getElipsesIndex());
    mv.visitMethodInsn(INVOKESTATIC, "org/renjin/compiler/runtime/CompiledRuntime", "spliceArgValues",
          "(Lorg/renjin/eval/Context;[Lorg/renjin/sexp/SEXP;Lorg/renjin/sexp/PairList;I)[Lorg/renjin/sexp/SEXP;");
 
  }

  private void maybeStoreElipses(CallExpression call) {
    if(call.hasElipses()) {
      loadElipses();
      mv.visitVarInsn(ASTORE, work1);
    }
  }

  private void loadElipses() {
    loadEnvironment();
    mv.visitFieldInsn(GETSTATIC, "org/renjin/sexp/Symbols", "ELLIPSES", "Lorg/renjin/sexp/Symbol;");
    mv.visitMethodInsn(INVOKEVIRTUAL, "org/renjin/sexp/Environment", "getVariable", "(Lorg/renjin/sexp/Symbol;)Lorg/renjin/sexp/SEXP;");
    mv.visitTypeInsn(CHECKCAST, "org/renjin/sexp/PairList");
  }
 
  private void pushPrimitiveArgArray(PrimitiveCall call) {
    // create array of values
    pushInt(call.getArguments().size());
    mv.visitTypeInsn(ANEWARRAY, "org/renjin/sexp/SEXP");
    for(int i=0;i!=call.getArguments().size();++i) {
      if(call.getArguments().get(i) != Elipses.INSTANCE) {
        mv.visitInsn(DUP);
        pushInt(i);
        call.getArguments().get(i).accept(this);
        mv.visitInsn(AASTORE);
      }
    }
  }
 
  private void pushInt(int i) {
     ByteCodeUtil.pushInt(mv, i);
  }

  @Override
  public void visitLength(Length length) {
    length.getVector().accept(this);
    mv.visitMethodInsn(INVOKEINTERFACE, "org/renjin/sexp/SEXP", "length", "()I");
  }

  @Override
  public void visitTemp(Temp temp) {
    mv.visitVarInsn(ALOAD, getVariableSlot(temp));
  }

  @Override
  public void visitCmpGE(CmpGE cmp) {
    throw new UnsupportedOperationException();
  }

  @Override
  public void visitSsaVariable(SsaVariable variable) {
    throw new UnsupportedOperationException();
  }

  @Override
  public void visitPhiFunction(PhiFunction phiFunction) {
    throw new UnsupportedOperationException();
   
  }

  @Override
  public void visitExprStatement(ExprStatement statement) {
    statement.getRHS().accept(this);
    mv.visitInsn(POP);
  }

  @Override
  public void visitGoto(GotoStatement statement) {
    mv.visitJumpInsn(GOTO, getAsmLabel(statement.getTarget()));
  }

  @Override
  public void visitIf(IfStatement stmt) {
   
    if(stmt.getCondition() instanceof CmpGE) {
    
      CmpGE cmp = (CmpGE) stmt.getCondition();
      mv.visitVarInsn(ILOAD, getVariableSlot((LValue)cmp.getOp1()));
      mv.visitVarInsn(ILOAD, getVariableSlot((LValue)cmp.getOp2()));
    
      mv.visitJumpInsn(IF_ICMPLT, getAsmLabel(stmt.getFalseTarget()));
     
    } else {
   
      stmt.getCondition().accept(this);
     
      mv.visitMethodInsn(INVOKESTATIC, "org/renjin/compiler/runtime/CompiledRuntime",
            "evaluateCondition", "(Lorg/renjin/sexp/SEXP;)Z");
     
      // IFEQ : jump if i==0
      mv.visitJumpInsn(IFEQ, getAsmLabel(stmt.getFalseTarget()));
    }
    mv.visitJumpInsn(GOTO, getAsmLabel(stmt.getTrueTarget()));
  }
 
  private Label getAsmLabel(IRLabel label) {
    Label asmLabel = labels.get(label);
    if(asmLabel == null) {
      asmLabel = new Label();
      labels.put(label, asmLabel);
    }
    return asmLabel;
  }
 
  private int getVariableSlot(LValue lvalue) {
    Integer index = variableSlots.get(lvalue);
    if(index == null) {
      index = variableSlots.size();
      variableSlots.put(lvalue, index);
    }
    return index + localVariablesStart;
  }


  @Override
  public void visitReturn(ReturnStatement returnStatement) {
    returnStatement.getValue().accept(this);
    mv.visitInsn(ARETURN);
  }
 
  @Override
  public void visitPromise(IRThunk promise) {
    // TODO Auto-generated method stub
   
  }
}
TOP

Related Classes of org.renjin.compiler.ByteCodeVisitor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.