Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   package org.jruby.ir;
   
   import org.jruby.Ruby;
   import org.jruby.ast.*;
  
  import java.io.File;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  import java.util.Stack;
  
  // This class converts an AST into a bunch of IR instructions
  
  // IR Building Notes
  // -----------------
  //
  // 1. More copy instructions added than necessary
  // ----------------------------------------------
  // Note that in general, there will be lots of a = b kind of copies
  // introduced in the IR because the translation is entirely single-node focused.
  // An example will make this clear
  //
  // RUBY:
  //     v = @f
  // will translate to
  //
  // AST:
  //     LocalAsgnNode v
  //       InstrVarNode f
  // will translate to
  //
  // IR:
  //     tmp = self.f [ GET_FIELD(tmp,self,f) ]
  //     v = tmp      [ COPY(v, tmp) ]
  //
  // instead of
  //     v = self.f   [ GET_FIELD(v, self, f) ]
  //
  // We could get smarter and pass in the variable into which this expression is going to get evaluated
  // and use that to store the value of the expression (or not build the expression if the variable is null).
  //
  // But, that makes the code more complicated, and in any case, all this will get fixed in a single pass of
  // copy propagation and dead-code elimination.
 //
 // Something to pay attention to and if this extra pass becomes a concern (not convinced that it is yet),
 // this smart can be built in here.  Right now, the goal is to do something simple and straightforward that is going to be correct.
 //
 // 2. Returning null vs manager.getNil()
 // ----------------------------
 // - We should be returning null from the build methods where it is a normal "error" condition
 // - We should be returning manager.getNil() where the actual return value of a build is the ruby nil operand
 //   Look in buildIf for an example of this
 //
 // 3. Temporary variable reuse
 // ---------------------------
 // I am reusing variables a lot in places in this code.  Should I instead always get a new variable when I need it
 // This introduces artificial data dependencies, but fewer variables.  But, if we are going to implement SSA pass
 // this is not a big deal.  Think this through!
 
 public class IRBuilder {
     protected static final Operand[] NO_ARGS = new Operand[]{};
     protected static final UnexecutableNil U_NIL = .;
 
     private static String  rubyVersion = "1.8"// default is 1.8
 
     public static void setRubyVersion(String rubyVersion) {
         . = rubyVersion;
     }
 
     public boolean is1_9() {
         return false;
     }
 
     public boolean is2_0() {
         return false;
     }
 
     private Operand buildOperand(Node nodeIRScope sthrows NotCompilableException {
         switch (node.getNodeType()) {
             case return buildAlias((AliasNodenodes);
             case return buildAnd((AndNodenodes);
             case return buildArgsCat((ArgsCatNodenodes);
             case return buildArgsPush((ArgsPushNodenodes);
             case return buildArray(nodes);
             case return buildAttrAssign((AttrAssignNodenodes);
             case return buildBackref((BackRefNodenodes);
             case return buildBegin((BeginNodenodes);
             case return buildBignum((BignumNodenodes);
             case return buildBlock((BlockNodenodes);
             case return buildBreak((BreakNodenodes);
             case return buildCall((CallNodenodes);
             case return buildCase((CaseNodenodes);
             case return buildClass((ClassNodenodes);
             case return buildClassVar((ClassVarNodenodes);
             case return buildClassVarAsgn((ClassVarAsgnNodenodes);
             case return buildClassVarDecl((ClassVarDeclNodenodes);
             case return buildColon2((Colon2Nodenodes);
             case return buildColon3((Colon3Nodenodes);
             case return buildConstDecl((ConstDeclNodenodes);
             case return searchConst(ss, ((ConstNodenode).getName());
             case return buildDAsgn((DAsgnNodenodes);
             case return buildGetDefinitionBase(((DefinedNodenode).getExpressionNode(), s);
             case return buildDefn((MethodDefNodenodes);
             case return buildDefs((DefsNodenodes);
             case return buildDot((DotNodenodes);
             case return buildDRegexp((DRegexpNodenodes);
             case return buildDStr((DStrNodenodes);
             case return buildDSymbol((DSymbolNodenodes);
             case return buildDVar((DVarNodenodes);
             case return buildDXStr((DXStrNodenodes);
             case return buildEnsureNode((EnsureNodenodes);
             case return buildEvStr((EvStrNodenodes);
             case return buildFalse(nodes);
             case return buildFCall((FCallNodenodes);
             case return buildFixnum((FixnumNodenodes);
             case return buildFlip((FlipNodenodes);
             case return buildFloat((FloatNodenodes);
             case return buildFor((ForNodenodes);
             case return buildGlobalAsgn((GlobalAsgnNodenodes);
             case return buildGlobalVar((GlobalVarNodenodes);
             case return buildHash((HashNodenodes);
             case return buildIf((IfNodenodes);
             case return buildInstAsgn((InstAsgnNodenodes);
             case return buildInstVar((InstVarNodenodes);
             case return buildIter((IterNodenodes);
             case return buildLiteral((LiteralNodenodes);
             case return buildLocalAsgn((LocalAsgnNodenodes);
             case return buildLocalVar((LocalVarNodenodes);
             case return buildMatch2((Match2Nodenodes);
             case return buildMatch3((Match3Nodenodes);
             case return buildMatch((MatchNodenodes);
             case return buildModule((ModuleNodenodes);
             case return buildMultipleAsgn((MultipleAsgnNodenodes); // Only for 1.8
             case return buildNewline((NewlineNodenodes);
             case return buildNext((NextNodenodes);
             case return buildNthRef((NthRefNodenodes);
             case return buildNil(nodes);
             case return buildNot((NotNodenodes);
             case return buildOpAsgnAnd((OpAsgnAndNodenodes);
             case return buildOpAsgn((OpAsgnNodenodes);
             case return buildOpAsgnOr((OpAsgnOrNodenodes);
             case return buildOpElementAsgn((OpElementAsgnNodenodes);
             case return buildOr((OrNodenodes);
             case return buildPreExe((PreExeNodenodes);
             case return buildPostExe((PostExeNodenodes);
             case return buildRedo(nodes);
             case return buildRegexp((RegexpNodenodes);
             case :
                 throw new NotCompilableException("rescue body is handled by rescue compilation at: " + node.getPosition());
             case return buildRescue((RescueNodenodes);
             case return buildRetry(nodes);
             case return buildReturn((ReturnNodenodes);
             case :
                 throw new NotCompilableException("Use buildRoot(); Root node at: " + node.getPosition());
             case return buildSClass((SClassNodenodes);
             case return buildSelf((SelfNodenodes);
             case return buildSplat((SplatNodenodes);
             case return buildStr((StrNodenodes);
             case return buildSuper((SuperNodenodes);
             case return buildSValue((SValueNodenodes);
             case return buildSymbol((SymbolNodenodes);
             case return buildToAry((ToAryNodenodes);
             case return buildTrue(nodes);
             case return buildUndef(nodes);
             case return buildUntil((UntilNodenodes);
             case return buildVAlias(nodes);
             case return buildVCall((VCallNodenodes);
             case return buildWhile((WhileNodenodes);
             case assert false : "When nodes are handled by case node compilation."return null;
             case return buildXStr((XStrNodenodes);
             case return buildYield((YieldNodenodes);
             case return buildZArray(nodes);
             case return buildZSuper((ZSuperNodenodes);
             defaultreturn buildVersionSpecificNodes(nodes);
         }
     }
 
     /* -----------------------------------------------------------------------------------
      * Every ensure block has a start label and end label, and at the end, it will jump
      * to an address stored in a return address variable.
      *
      * This ruby code will translate to the IR shown below
      * -----------------
      *   begin
      *       ... protected body ...
      *   ensure
      *       ... ensure block to run
      *   end
      * -----------------
      *  L_region_start
      *     IR instructions for the protected body
      *  L_start:
      *     .. ensure block IR ...
      *     jump %ret_addr
      *  L_end:
      * -----------------
      *
      * If N is a node in the protected body that might exit this scope (exception rethrows
      * and returns), N has to first jump to the ensure block and let the ensure block run.
      * In addition, N has to set up a return address label in the return address var of
      * this ensure block so that the ensure block can transfer control block to N.
      *
      * Since we can have a nesting of ensure blocks, we are maintaining a stack of these
      * well-nested ensure blocks.  Every node N that will exit this scope will have to
      * co-ordinate the jumps in-and-out of the ensure blocks in the top-to-bottom stacked
      * order.
      * ----------------------------------------------------------------------------------- */
     private static class EnsureBlockInfo {
         Label    regionStart;
         Label    start;
         Label    end;
         Label    dummyRescueBlockLabel;
         Variable returnAddr;
         Variable savedGlobalException;
 
         // Innermost loop within which this ensure block is nested, if any
         IRLoop   innermostLoop;
 
         // AST node for any associated rescue node in the case of begin-rescue-ensure-end block
         // Will be null in the case of begin-ensure-end block
         RescueNode matchingRescueNode;
 
         public EnsureBlockInfo(IRScope sRescueNode nIRLoop l) {
              = s.getNewLabel();
                    = s.getNewLabel();
                      = s.getNewLabel();
               = s.getNewTemporaryVariable();
              = s.getNewLabel();
              = null;
              = l;
              = n;
         }
 
         // Emit jump chain by walking up the ensure block stack
         // If we have been passed a loop value, then emit values that are nested within that loop
         public static void emitJumpChain(IRScope sStack<EnsureBlockInfoebStackIRLoop loop) {
             // SSS: There are 2 ways of encoding this:
             // 1. Jump to ensure block 1, return back here, jump ensure block 2, return back here, ...
             //    Generates 3*n instrs. where n is the # of ensure blocks to execute
             // 2. Jump to ensure block 1, then to block 2, then to 3, ...
             //    Generates n+1 instrs. where n is the # of ensure blocks to execute
             // Doesn't really matter all that much since we shouldn't have deep nesting of ensure blocks often
             // but is there a reason to go with technique 1 at all??
             int n = ebStack.size();
             EnsureBlockInfo[] ebArray = ebStack.toArray(new EnsureBlockInfo[n]);
             for (int i = n-1; i >= 0; i--) {
                 EnsureBlockInfo ebi = ebArray[i];
 
                 //
                 if (ebi.innermostLoop != loopbreak;
 
                 Label retLabel = s.getNewLabel();
                 if (ebi.savedGlobalException != null) {
                     s.addInstr(new PutGlobalVarInstr("$!"ebi.savedGlobalException));
                 }
                 s.addInstr(new SetReturnAddressInstr(ebi.returnAddrretLabel));
                 s.addInstr(new JumpInstr(ebi.start));
                 s.addInstr(new LabelInstr(retLabel));
             }
         }
     }
 
     // Stack encoding nested ensure blocks
 
     private static class RescueBlockInfo {
         RescueNode rescueNode;             // Rescue node for which we are tracking info
         Label      entryLabel;             // Entry of the rescue block
         Variable   savedExceptionVariable// Variable that contains the saved $! variable
         IRLoop     innermostLoop;          // Innermost loop within which this ensure block is nested, if any
 
         public RescueBlockInfo(RescueNode nLabel lVariable vIRLoop loop) {
              = n;
              = l;
              = v;
              = loop;
         }
 
         public void restoreException(IRScope sIRLoop currLoop) {
             if (currLoop == s.addInstr(new PutGlobalVarInstr("$!"));
         }
     }
 
     // Stack encoding nested rescue blocks -- this just tracks the start label of the blocks
 
     private int _lastProcessedLineNum = -1;
 
     // Since we are processing ASTs, loop bodies are processed in depth-first manner
     // with outer loops encountered before inner loops, and inner loops finished before outer ones.
     //
     // So, we can keep track of loops in a loop stack which  keeps track of loops as they are encountered.
     // This lets us implement next/redo/break/retry easily for the non-closure cases
     private Stack<IRLooploopStack = new Stack<IRLoop>();
 
     public IRLoop getCurrentLoop() {
         return .isEmpty() ? null : .peek();
     }
 
     protected IRManager manager;
 
     public IRBuilder(IRManager manager) {
         this. = manager;
     }
 
     public static Node buildAST(boolean isCommandLineScriptString arg) {
         Ruby ruby = Ruby.getGlobalRuntime();
 
         // set to IR mode, since we use different scopes, etc for IR
 
         // inline script
         if (isCommandLineScriptreturn ruby.parse(ByteList.create(arg), "-e"null, 0, false);
 
         // from file
         FileInputStream fis = null;
         try {
             File file = new File(arg);
             fis = new FileInputStream(file);
             long size = file.length();
             byte[] bytes = new byte[(int)size];
             fis.read(bytes);
             ..println("-- processing " + arg + " --");
             return ruby.parse(new ByteList(bytes), argnull, 0, false);
         } catch (IOException ioe) {
             throw new RuntimeException(ioe);
         } finally {
             try { if (fis != nullfis.close(); } catch(Exception e) { }
         }
     }
 
     public static IRBuilder createIRBuilder(Ruby runtimeIRManager manager) {
         boolean is19 = runtime.is1_9();
         boolean is20 = runtime.is2_0();
         if (is20) {
             return new IRBuilder20(manager);
         } else if (is19) {
             return new IRBuilder19(manager);
         } else {
             return new IRBuilder(manager);
         }
     }
 
     private boolean hasListener() {
         return .getIRScopeListener() != null;
     }
 
     public IRBuilder newIRBuilder(IRManager manager) {
         if (is2_0()) {
             return new IRBuilder20(manager);
         } else if (is1_9()) {
             return new IRBuilder19(manager);
         } else {
             return new IRBuilder(manager);
         }
     }
 
     public Node skipOverNewlines(IRScope sNode n) {
         if (n.getNodeType() == .) {
             // Do not emit multiple line number instrs for the same line
             int currLineNum = n.getPosition().getStartLine();
             if (currLineNum != ) {
                s.addInstr(new LineNumberInstr(scurrLineNum));
                 = currLineNum;
             }
         }
 
         while (n.getNodeType() == .)
             n = ((NewlineNode)n).getNextNode();
 
         return n;
     }
 
     public Operand build(Node nodeIRScope s) {
         if (node == nullreturn null;
 
         if (s == null) {
             ..println("Got a null scope!");
             throw new NotCompilableException("Unknown node encountered in builder: " + node);
         }
         if (hasListener()) {
             IRScopeListener listener = .getIRScopeListener();
             listener.startBuildOperand(nodes);
         }
         Operand operand = buildOperand(nodes);
         if (hasListener()) {
             IRScopeListener listener = .getIRScopeListener();
             listener.endBuildOperand(nodesoperand);
         }
         return operand;
     }
 
     protected Operand buildVersionSpecificNodes(Node nodeIRScope s) {
         throw new NotCompilableException("Unknown node encountered in builder: " + node.getClass());
     }
 
     protected Variable getSelf(IRScope s) {
         return s.getSelf();
     }
 
     protected Variable copyAndReturnValue(IRScope sOperand val) {
         Variable v = s.getNewTemporaryVariable();
         s.addInstr(new CopyInstr(vval));
         return v;
     }
 
     protected Variable getValueInTemporaryVariable(IRScope sOperand val) {
         if (val != null && val instanceof TemporaryVariablereturn (Variableval;
 
         return copyAndReturnValue(sval);
     }
 
     // Return the last argument in the list -- AttrAssign needs it
     protected Operand buildCallArgs(List<OperandargsListNode argsIRScope s) {
         // unwrap newline nodes to get their actual type
         args = skipOverNewlines(sargs);
         switch (args.getNodeType()) {
             case : {
                 CompoundArray a = (CompoundArray)build(argss);
                 argsList.add(new Splat(a));
                 return a.getAppendedArg();
             }
             case :  {
                 ArgsPushNode ap = (ArgsPushNode)args;
                 Operand v1 = build(ap.getFirstNode(), s);
                 Operand v2 = build(ap.getSecondNode(), s);
                 argsList.add(new Splat(new CompoundArray(v1v2true)));
                 return v2;
             }
             case : {
                 ArrayNode arrayNode = (ArrayNode)args;
                 if (arrayNode.isLightweight()) {
                     List<Nodechildren = arrayNode.childNodes();
                     if (children.size() == 1) {
                         // skipOverNewlines is required because the parser inserts a NewLineNode in between!
                         Node child = skipOverNewlines(schildren.get(0));
                         if (child instanceof SplatNode) {
                             // SSS: If the only child is a splat, the splat is supposed to get through
                             // as an array without being expanded into the call arg list.
                             //
                             // The AST for the foo([*1]) is: ArrayNode(Splat19Node(..))
                             // The AST for the foo(*1) is: Splat19Node(..)
                             //
                             // Since a lone splat in call args is always expanded, we convert the splat
                             // into a compound array: *n --> args-cat([], *n)
                             SplatNode splat = (SplatNode)child;
                             Variable splatArray = getValueInTemporaryVariable(sbuild(splat.getValue(), s));
                             argsList.add(new CompoundArray(new Array(), splatArray));
                             return new Splat(splatArray);
                         } else {
                             Operand childOperand = build(childs);
                             argsList.add(childOperand);
                             return childOperand;
                         }
                     } else {
                         // explode array, it's an internal "args" array
                         for (Node nchildren) {
                             argsList.add(build(ns));
                         }
                     }
                 } else {
                     // use array as-is, it's a literal array
                     argsList.add(build(arrayNodes));
                 }
                 break;
             }
             default: {
                 argsList.add(build(argss));
                 break;
             }
         }
 
         return argsList.isEmpty() ? .getNil() : argsList.get(argsList.size() - 1);
     }
 
     public List<OperandsetupCallArgs(Node argsIRScope s) {
         List<OperandargsList = new ArrayList<Operand>();
         if (args != nullbuildCallArgs(argsListargss);
         return argsList;
     }
 
     public void buildVersionSpecificAssignment(Node nodeIRScope sVariable v) {
         switch (node.getNodeType()) {
         case : {
             Operand valuesArg;
             MultipleAsgnNode childNode = (MultipleAsgnNodenode;
             if (childNode.getHeadNode() != null && ((ListNode)childNode.getHeadNode()).childNodes().size() > 0) {
                 // Invoke to_ary on the operand only if it is not an array already
                 Variable result = s.getNewTemporaryVariable();
                 s.addInstr(new ToAryInstr(resultv.getTrue()));
                 valuesArg = result;
             } else {
                 s.addInstr(new EnsureRubyArrayInstr(vv));
                 valuesArg = v;
             }
             buildMultipleAsgnAssignment(childNodesnullvaluesArg);
             break;
         }
         default:
             throw new NotCompilableException("Can't build assignment node: " + node);
         }
     }
 
     // This method is called to build assignments for a multiple-assignment instruction
     public void buildAssignment(Node nodeIRScope sVariable rhsVal) {
         switch (node.getNodeType()) {
             case :
                 buildAttrAssignAssignment(nodesrhsVal);
                 break;
             case :
                 s.addInstr(new PutClassVariableInstr(classVarDefinitionContainer(s), ((ClassVarAsgnNode)node).getName(), rhsVal));
                 break;
             case :
                 s.addInstr(new PutClassVariableInstr(classVarDeclarationContainer(s), ((ClassVarDeclNode)node).getName(), rhsVal));
                 break;
             case :
                 buildConstDeclAssignment((ConstDeclNodenodesrhsVal);
                 break;
             case : {
                 DAsgnNode variable = (DAsgnNodenode;
                 int depth = variable.getDepth();
                 s.addInstr(new CopyInstr(s.getLocalVariable(variable.getName(), depth), rhsVal));
                 break;
             }
             case :
                 s.addInstr(new PutGlobalVarInstr(((GlobalAsgnNode)node).getName(), rhsVal));
                 break;
             case :
                 // NOTE: if 's' happens to the a class, this is effectively an assignment of a class instance variable
                 s.addInstr(new PutFieldInstr(getSelf(s), ((InstAsgnNode)node).getName(), rhsVal));
                 break;
             case : {
                 LocalAsgnNode localVariable = (LocalAsgnNodenode;
                 int depth = localVariable.getDepth();
                 s.addInstr(new CopyInstr(s.getLocalVariable(localVariable.getName(), depth), rhsVal));
                 break;
             }
             case :
                 throw new NotCompilableException("Shouldn't get here; zeroarg does not do assignment: " + node);
             default:
                 buildVersionSpecificAssignment(nodesrhsVal);
         }
     }
 
     protected LocalVariable getBlockArgVariable(IRScope clString nameint depth) {
         return cl.getLocalVariable(namedepth);
     }
 
     protected void receiveBlockArg(IRScope sVariable vOperand argsArrayint argIndexboolean isClosureArgboolean isSplat) {
         if (argsArray != null) {
             // We are in a nested receive situation -- when we are not at the root of a masgn tree
             // Ex: We are trying to receive (b,c) in this example: "|a, (b,c), d| = ..."
             if (isSplats.addInstr(new RestArgMultipleAsgnInstr(vargsArrayargIndex));
             else s.addInstr(new ReqdArgMultipleAsgnInstr(vargsArrayargIndex));
         } else {
             // argsArray can be null when the first node in the args-node-ast is a multiple-assignment
             // For example, for-nodes
             s.addInstr(isClosureArg ? new ReceiveClosureInstr(v) : (isSplat ? new ReceiveRestArgInstr(vargIndexargIndex) : new ReceivePreReqdArgInstr(vargIndex)));
         }
     }
 
     public void buildVersionSpecificBlockArgsAssignment(Node nodeIRScope sOperand argsArrayint argIndexboolean isMasgnRootboolean isClosureArgboolean isSplat) {
         switch (node.getNodeType()) {
             case : {
                 Variable oldArgs = null;
                 MultipleAsgnNode childNode = (MultipleAsgnNodenode;
                 if (!isMasgnRoot) {
                     // Vars used to receive args should always be local-variables because
                     // these arg values may need to be accessed by some zsuper instruction.
                     // During interpretation, only local-vars are accessible (at least right now)
                     // outside the scope they are defined in.
                     Variable v = s.getLocalVariable("%_masgn_arg_" + argIndex, 0);
                     receiveBlockArg(svargsArrayargIndexisClosureArgisSplat);
                     boolean runToAry = childNode.getHeadNode() != null && (((ListNode)childNode.getHeadNode()).childNodes().size() > 0);
                     if (runToAry) {
                         s.addInstr(new ToAryInstr(vv.getFalse()));
                     } else {
                         s.addInstr(new EnsureRubyArrayInstr(vv));
                     }
                     argsArray = v;
                     // SSS FIXME: Are we guaranteed that splats dont head to multiple-assignment nodes!  i.e. |*(a,b)|?
                 }
                 // Build
                 buildMultipleAsgnAssignment(childNodesargsArraynull);
                 break;
             }
             defaultthrow new NotCompilableException("Can't build assignment node: " + node);
         }
     }
 
     // This method is called to build arguments for a block!
     public void buildBlockArgsAssignment(Node nodeIRScope sOperand argsArrayint argIndexboolean isMasgnRootboolean isClosureArgboolean isSplat) {
         Variable v;
         switch (node.getNodeType()) {
             case :
                 v = s.getNewTemporaryVariable();
                 receiveBlockArg(svargsArrayargIndexisClosureArgisSplat);
                 buildAttrAssignAssignment(nodesv);
                 break;
             case : {
                 DAsgnNode dynamicAsgn = (DAsgnNodenode;
                 v = getBlockArgVariable((IRClosure)sdynamicAsgn.getName(), dynamicAsgn.getDepth());
                 receiveBlockArg(svargsArrayargIndexisClosureArgisSplat);
                 break;
             }
             case :
                 v = s.getNewTemporaryVariable();
                 receiveBlockArg(svargsArrayargIndexisClosureArgisSplat);
                 s.addInstr(new PutClassVariableInstr(classVarDefinitionContainer(s), ((ClassVarAsgnNode)node).getName(), v));
                 break;
             case :
                 v = s.getNewTemporaryVariable();
                 receiveBlockArg(svargsArrayargIndexisClosureArgisSplat);
                 s.addInstr(new PutClassVariableInstr(classVarDeclarationContainer(s), ((ClassVarDeclNode)node).getName(), v));
                 break;
             case :
                 v = s.getNewTemporaryVariable();
                 receiveBlockArg(svargsArrayargIndexisClosureArgisSplat);
                 buildConstDeclAssignment((ConstDeclNodenodesv);
                 break;
             case :
                 v = s.getNewTemporaryVariable();
                 receiveBlockArg(svargsArrayargIndexisClosureArgisSplat);
                 s.addInstr(new PutGlobalVarInstr(((GlobalAsgnNode)node).getName(), v));
                 break;
             case :
                 v = s.getNewTemporaryVariable();
                 receiveBlockArg(svargsArrayargIndexisClosureArgisSplat);
                 // NOTE: if 's' happens to the a class, this is effectively an assignment of a class instance variable
                 s.addInstr(new PutFieldInstr(getSelf(s), ((InstAsgnNode)node).getName(), v));
                 break;
             case : {
                 LocalAsgnNode localVariable = (LocalAsgnNodenode;
                 int depth = localVariable.getDepth();
                 v = getBlockArgVariable((IRClosure)slocalVariable.getName(), depth);
                 receiveBlockArg(svargsArrayargIndexisClosureArgisSplat);
                 break;
             }
             case :
                 throw new NotCompilableException("Shouldn't get here; zeroarg does not do assignment: " + node);
             default:
                 buildVersionSpecificBlockArgsAssignment(nodesargsArrayargIndexisMasgnRootisClosureArgisSplat);
         }
     }
 
     public Operand buildAlias(final AliasNode aliasIRScope s) {
         Operand newName = build(alias.getNewName(), s);
         Operand oldName = build(alias.getOldName(), s);
         s.addInstr(new AliasInstr(getSelf(s), newNameoldName));
 
         return .getNil();
     }
 
     // Translate "ret = (a && b)" --> "ret = (a ? b : false)" -->
     //
     //    v1 = -- build(a) --
     //       OPT: ret can be set to v1, but effectively v1 is false if we take the branch to L.
     //            while this info can be inferred by using attributes, why bother if we can do this?
     //    ret = v1
     //    beq(v1, false, L)
     //    v2 = -- build(b) --
     //    ret = v2
     // L:
     //
     public Operand buildAnd(final AndNode andNodeIRScope s) {
         if (andNode.getFirstNode().getNodeType().alwaysTrue()) {
             // build first node (and ignore its result) and then second node
             build(andNode.getFirstNode(), s);
             return build(andNode.getSecondNode(), s);
         } else if (andNode.getFirstNode().getNodeType().alwaysFalse()) {
             // build first node only and return its value
             return build(andNode.getFirstNode(), s);
         } else {
             Label    l   = s.getNewLabel();
             Operand  v1  = build(andNode.getFirstNode(), s);
             Variable ret = getValueInTemporaryVariable(sv1);
             s.addInstr(BEQInstr.create(v1.getFalse(), l));
             Operand  v2  = build(andNode.getSecondNode(), s);
             s.addInstr(new CopyInstr(retv2));
             s.addInstr(new LabelInstr(l));
             return ret;
         }
     }
 
     public Operand buildArray(Node nodeIRScope s) {
         List<Operandelts = new ArrayList<Operand>();
         for (Node enode.childNodes())
             elts.add(build(es));
 
         return copyAndReturnValue(snew Array(elts));
     }
 
     public Operand buildArgsCat(final ArgsCatNode argsCatNodeIRScope s) {
         Operand v1 = build(argsCatNode.getFirstNode(), s);
         Operand v2 = build(argsCatNode.getSecondNode(), s);
         return new CompoundArray(v1v2);
     }
 
     public Operand buildArgsPush(final ArgsPushNode nodeIRScope s) {
         throw new NotCompilableException("ArgsPush should never be encountered bare in 1.8" + node);
     }
 
     private Operand buildAttrAssign(final AttrAssignNode attrAssignNodeIRScope s) {
         Operand obj = build(attrAssignNode.getReceiverNode(), s);
         List<Operandargs = new ArrayList<Operand>();
         Node argsNode = attrAssignNode.getArgsNode();
         Operand lastArg = (argsNode == null) ? .getNil() : buildCallArgs(argsargsNodes);
         s.addInstr(new AttrAssignInstr(objnew MethAddr(attrAssignNode.getName()), args.toArray(new Operand[args.size()])));
         return lastArg;
     }
 
     public Operand buildAttrAssignAssignment(Node nodeIRScope sOperand value) {
         final AttrAssignNode attrAssignNode = (AttrAssignNodenode;
         Operand obj = build(attrAssignNode.getReceiverNode(), s);
         List<Operandargs = setupCallArgs(attrAssignNode.getArgsNode(), s);
         args.add(value);
         s.addInstr(new AttrAssignInstr(objnew MethAddr(attrAssignNode.getName()), args.toArray(new Operand[args.size()])));
         return value;
     }
 
     public Operand buildBackref(BackRefNode nodeIRScope s) {
         // SSS FIXME: Required? Verify with Tom/Charlie
         return copyAndReturnValue(snew Backref(node.getType()));
     }
 
     public Operand buildBegin(BeginNode beginNodeIRScope s) {
         return build(beginNode.getBodyNode(), s);
     }
 
     public Operand buildBignum(BignumNode nodeIRScope s) {
         // SSS: Since bignum literals are effectively interned objects, no need to copyAndReturnValue(...)
         // Or is this a premature optimization?
         return new Bignum(node.getValue());
     }
 
     public Operand buildBlock(BlockNode nodeIRScope s) {
         Operand retVal = null;
         for (Node child : node.childNodes()) {
             retVal = build(childs);
         }
 
         // Value of the last expression in the block
         return retVal;
     }
 
     public Operand buildBreak(BreakNode breakNodeIRScope s) {
         IRLoop currLoop = getCurrentLoop();
 
         Operand rv = build(breakNode.getValueNode(), s);
         // If we have ensure blocks, have to run those first!
         if (!.empty()) EnsureBlockInfo.emitJumpChain(scurrLoop);
         else if (!.empty()) .peek().restoreException(scurrLoop);
 
         if (currLoop != null) {
             s.addInstr(new CopyInstr(currLoop.loopResultrv));
             s.addInstr(new JumpInstr(currLoop.loopEndLabel));
         } else {
             if (s instanceof IRClosure) {
                 // This lexical scope value is only used (and valid) in regular block contexts.
                 // If this instruction is executed in a Proc or Lambda context, the lexical scope value is useless.
                 IRScope returnScope = s.getLexicalParent();
                 if (is1_9() || is2_0()) {
                     // In 1.9 and later modes, no breaks from evals
                     if (s instanceof IREvalScripts.addInstr(new ThrowExceptionInstr(.));
                     else s.addInstr(new BreakInstr(rvreturnScope));
                 } else {
                     // In pre-1.9 mode, breaks from evals are legitimate!
                     if (s instanceof IREvalScriptreturnScope = returnScope.getLexicalParent();
                     s.addInstr(new BreakInstr(rvreturnScope));
                 }
             } else {
                 // We are not in a closure or a loop => bad break instr!
                 s.addInstr(new ThrowExceptionInstr(.));
             }
         }
 
         // Once the break instruction executes, control exits this scope
         return .;
     }
 
     private void handleNonlocalReturnInMethod(IRScope s) {
         Label rBeginLabel = s.getNewLabel();
         Label rEndLabel   = s.getNewLabel();
         Label gebLabel    = s.getNewLabel();
 
         // protect the entire body as it exists now with the global ensure block
         s.addInstrAtBeginning(new ExceptionRegionStartMarkerInstr(rBeginLabelrEndLabelgebLabelgebLabel));
         s.addInstr(new ExceptionRegionEndMarkerInstr());
 
         // Receive exceptions (could be anything, but the handler only processes IRReturnJumps)
         s.addInstr(new LabelInstr(gebLabel));
         Variable exc = s.getNewTemporaryVariable();
         s.addInstr(new ReceiveExceptionInstr(excfalse));  // no type-checking
 
         // Handle break using runtime helper
         // --> IRRuntimeHelpers.handleNonlocalReturn(scope, bj, blockType)
         Variable ret = s.getNewTemporaryVariable();
         s.addInstr(new RuntimeHelperCall(ret"handleNonlocalReturn"new Operand[]{exc} ));
         s.addInstr(new ReturnInstr(ret));
 
         // End
         s.addInstr(new LabelInstr(rEndLabel));
     }
 
     // Wrap call in a rescue handler that catches the IRBreakJump
     private void receiveBreakException(IRScope sOperand blockCallInstr callInstr) {
         // Check if we have to handle a break
         if (block != null && block instanceof WrappedIRClosure) {
             IRClosure closure = ((WrappedIRClosure)block).getClosure();
             if (!closure.hasBreakInstrs) {
                 // No protection needed -- add the call and return
                 s.addInstr(callInstr);
                 return;
             }
         } else {
             // No protection needed -- add the call and return
             s.addInstr((Instr)callInstr);
             return;
         }
 
         Label rBeginLabel = s.getNewLabel();
         Label rEndLabel   = s.getNewLabel();
         Label rescueLabel = s.getNewLabel();
 
         // Protected region
         s.addInstr(new ExceptionRegionStartMarkerInstr(rBeginLabelrEndLabelnullrescueLabel));
         s.addInstr(callInstr);
         s.addInstr(new JumpInstr(rEndLabel));
         s.addInstr(new ExceptionRegionEndMarkerInstr());
 
         // Receive exceptions (could be anything, but the handler only processes IRBreakJumps)
         s.addInstr(new LabelInstr(rescueLabel));
         Variable exc = s.getNewTemporaryVariable();
         s.addInstr(new ReceiveExceptionInstr(exc));
 
         // Handle break using runtime helper
         // --> IRRuntimeHelpers.handlePropagatedBreak(context, scope, bj, blockType)
         s.addInstr(new RuntimeHelperCall(callInstr.getResult(), "handlePropagatedBreak"new Operand[]{exc} ));
 
         // End
         s.addInstr(new LabelInstr(rEndLabel));
     }
 
     public Operand buildCall(CallNode callNodeIRScope s) {