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;
     }
 
     /* -----------------------------------------------------------------------------------
      * 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(IRManager managerboolean is19) {
         return is19 ? new IRBuilder19(manager) : 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);
         }
         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);
         }
     }
 
     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 ReceiveRestArgInstr18(vargIndex) : 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.
                 s.addInstr(new BreakInstr(rvs.getLexicalParent()));
             } else {
                 // SSS FIXME: If we are not in a closure or a loop, the break instruction will throw a runtime exception
                 // Since we know this right now, should we build an exception instruction here?
                 s.addInstr(new BreakInstr(rvnull));
             }
         }
 
         // Once the break instruction executes, control exits this scope
         return .;
     }
 
     public Operand buildCall(CallNode callNodeIRScope s) {
         Node          callArgsNode = callNode.getArgsNode();
         Node          receiverNode = callNode.getReceiverNode();
         // Though you might be tempted to move this build into the CallInstr as:
         //    new Callinstr( ... , build(receiverNode, s), ...)
         // that is incorrect IR because the receiver has to be built *before* call arguments are built
         // to preserve expected code execution order
         Operand       receiver     = build(receiverNodes);
         List<Operandargs         = setupCallArgs(callArgsNodes);
         Operand       block        = setupCallClosure(callNode.getIterNode(), s);
         Variable      callResult   = s.getNewTemporaryVariable();
         Instr         callInstr    = CallInstr.create(callResultnew MethAddr(callNode.getName()), receiverargs.toArray(new Operand[args.size()]), block);
         s.addInstr(callInstr);
         return callResult;
     }
 
     public Operand buildCase(CaseNode caseNodeIRScope s) {
         // get the incoming case value
         Operand value = build(caseNode.getCaseNode(), s);
 
         // This is for handling case statements without a value (see example below)
         //   case 
         //     when true <blah>
         //     when false <blah>
         //   end
         if (value == nullvalue = .;
 
         Label     endLabel  = s.getNewLabel();
         boolean   hasElse   = (caseNode.getElseNode() != null);
         Label     elseLabel = s.getNewLabel();
         Variable  result    = s.getNewTemporaryVariable();
 
         List<Labellabels = new ArrayList<Label>();
         Map<LabelNodebodies = new HashMap<LabelNode>();
 
         // build each "when"
         for (Node aCase : caseNode.getCases().childNodes()) {
             WhenNode whenNode = (WhenNode)aCase;
             Label bodyLabel = s.getNewLabel();
 
             Variable eqqResult = s.getNewTemporaryVariable();
             labels.add(bodyLabel);
             Operand v1v2;
             if (whenNode.getExpressionNodes() instanceof ListNode) {
                 // SSS FIXME: Note about refactoring:
                 // - BEQInstr has a quick implementation when the second operand is a boolean literal
                 //   If it can be fixed to do this even on the first operand, we can switch around
                 //   v1 and v2 in the UndefinedValue scenario and DRY out this code.
                 // - Even with this asymmetric implementation of BEQInstr, you might be tempted to
                 //   switch around v1 and v2 in the else case.  But, that is equivalent to this Ruby code change:
                 //      (v1 == value) instead of (value == v1)
                 //   It seems that they should be identical, but the first one is v1.==(value) and the second one is
                 //   value.==(v1).  This is just fine *if* the Ruby programmer has implemented an algebraically
                 //   symmetric "==" method on those objects.  If not, then, the results might be unexpected where the
                 //   code (intentionally or otherwise) relies on this asymmetry of "==".  While it could be argued
                 //   that this a Ruby code bug, we will just try to preserve the order of the == check as it appears
                 //   in the Ruby code.
                 if (value == .)  {
                     v1 = build(whenNode.getExpressionNodes(), s);
                     v2 = .getTrue();
                 } else {
                     v1 = value;
                     v2 = build(whenNode.getExpressionNodes(), s);
                 }
             } else {
                 s.addInstr(new EQQInstr(eqqResultbuild(whenNode.getExpressionNodes(), s), value));
                 v1 = eqqResult;
                 v2 = .getTrue();
             }
             s.addInstr(BEQInstr.create(v1v2bodyLabel));
 
             // SSS FIXME: This doesn't preserve original order of when clauses.  We could consider
             // preserving the order (or maybe not, since we would have to sort the constants first
             // in any case) for outputing jump tables in certain situations.
             //
             // add body to map for emitting later
             bodies.put(bodyLabelwhenNode.getBodyNode());
         }
 
         // Jump to else in case nothing matches!
         s.addInstr(new JumpInstr(elseLabel));
 
         // build "else" if it exists
         if (hasElse) {
             labels.add(elseLabel);
             bodies.put(elseLabelcaseNode.getElseNode());
         }
 
         // now emit bodies while preserving when clauses order
         for (Label whenLabellabels) {
             s.addInstr(new LabelInstr(whenLabel));
             Operand bodyValue = build(bodies.get(whenLabel), s);
             // bodyValue can be null if the body ends with a return!
             if (bodyValue != null) {
                // SSS FIXME: Do local optimization of break results (followed by a copy & jump) to short-circuit the jump right away
                // rather than wait to do it during an optimization pass when a dead jump needs to be removed.  For this, you have
                // to look at what the last generated instruction was.
                Label tgt = endLabel;
                s.addInstr(new CopyInstr(resultbodyValue));
                s.addInstr(new JumpInstr(tgt));
             }
         }
 
         if (!hasElse) {
             s.addInstr(new LabelInstr(elseLabel));
             s.addInstr(new CopyInstr(result.getNil()));
             s.addInstr(new JumpInstr(endLabel));
         }
 
         // close it out
         s.addInstr(new LabelInstr(endLabel));
 
         // SSS: Got rid of the marker case label instruction
 
         return result;
     }

    
Build a new class and add it to the current scope (s).
 
     public Operand buildClass(ClassNode classNodeIRScope s) {
         Node superNode = classNode.getSuperNode();
         Colon3Node cpath = classNode.getCPath();
 &n