Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
   package org.jruby.ir;
   
   import java.util.ArrayList;
   import java.util.HashMap;
   import java.util.List;
   import java.util.Map;
   import java.util.Set;
   import java.util.SortedSet;
   import java.util.TreeSet;
  
Right now, this class abstracts the following execution scopes: Method, Closure, Module, Class, MetaClass Top-level Script, and Eval Script In the compiler-land, IR versions of these scopes encapsulate only as much information as is required to convert Ruby code into equivalent Java code. But, in the non-compiler land, there will be a corresponding java object for some of these scopes which encapsulates the runtime semantics and data needed for implementing them. In the case of Module, Class, MetaClass, and Method, they also happen to be instances of the corresponding Ruby classes -- so, in addition to providing code that help with this specific ruby implementation, they also have code that let them behave as ruby instances of their corresponding classes. Examples: - the runtime class object might have refs. to the runtime method objects. - the runtime method object might have a slot for a heap frame (for when it has closures that need access to the method's local variables), it might have version information, it might have references to other methods that were optimized with the current version number, etc. - the runtime closure object will have a slot for a heap frame (for when it has closures within) and might get reified as a method in the java land (but inaccessible in ruby land). So, passing closures in Java land might be equivalent to passing around the method handles. and so on ...
  
  public abstract class IRScope {
      private static final Logger LOG = LoggerFactory.getLogger("IRScope");
  
      private static Integer globalScopeCount = 0;

    
Unique global scope id
  
      private int scopeId;

    
Name
  
      private String name;

    
File within which this scope has been defined
  
      private final String fileName;

    
Starting line for this scope's definition
  
      private final int lineNumber;

    
Lexical parent scope
  
      private IRScope lexicalParent;

    
Parser static-scope that this IR scope corresponds to
  
      private StaticScope staticScope;

    
Live version of module within whose context this method executes
  
      private RubyModule containerModule;

    
List of IR instructions for this method
 
     private List<InstrinstrList;

    
Control flow graph representation of this method's instructions
 
     private CFG cfg;

    
List of (nested) closures in this scope
 
     private List<IRClosurenestedClosures;

    
Local variables defined in this scope
 
     private Set<VariabledefinedLocalVars;

    
Local variables used in this scope
 
     private Set<VariableusedLocalVars;

    
Is %block implicit block arg unused?
 
     private boolean hasUnusedImplicitBlockArg;

    
%current_module and %current_scope variables
 
     private TemporaryVariable currentScopeVar;

    
Map of name -> dataflow problem
 
     private Map<StringDataFlowProblemdfProbs;
 
     private Instr[] linearizedInstrArray;
     private List<BasicBlocklinearizedBBList;
     protected int temporaryVariableIndex;

    
Keeps track of types of prefix indexes for variables and labels
 
     private Map<StringIntegernextVarIndex;
 
     // Index values to guarantee we don't assign same internal index twice
     private int nextClosureIndex;
 
     // List of all scopes this scope contains lexically.  This is not used
     // for execution, but is used during dry-runs for debugging.
 
     protected static class LocalVariableAllocator {
         public int nextSlot;
         public Map<StringLocalVariablevarMap;
 
         public LocalVariableAllocator() {
              = new HashMap<StringLocalVariable>();
              = 0;
         }
 
         public final LocalVariable getVariable(String name) {
             return .get(name);
         }
 
         public final void putVariable(String nameLocalVariable var) {
             .put(namevar);
             ++;
         }
     }
 
Have scope flags been computed?
 
     private boolean flagsComputed;
 
     /* *****************************************************************************************************
      * Does this execution scope (applicable only to methods) receive a block and use it in such a way that
      * all of the caller's local variables need to be materialized into a heap binding?
      * Ex:
      *    def foo(&b)
      *      eval 'puts a', b
      *    end
      *
      *    def bar
      *      a = 1
      *      foo {} # prints out '1'
      *    end
      *
      * Here, 'foo' can access all of bar's variables because it captures the caller's closure.
      *
      * There are 2 scenarios when this can happen (even this is conservative -- but, good enough for now)
      * 1. This method receives an explicit block argument (in this case, the block can be stored, passed around,
      *    eval'ed against, called, etc.).
      *    CAVEAT: This is conservative ... it may not actually be stored & passed around, evaled, called, ...
      * 2. This method has a 'super' call (ZSuper AST node -- ZSuperInstr IR instruction)
      *    In this case, the parent (in the inheritance hierarchy) can access the block and store it, etc.  So, in reality,
      *    rather than assume that the parent will always do this, we can query the parent, if we can precisely identify
      *    the parent method (which in the face of Ruby's dynamic hierarchy, we cannot).  So, be pessimistic.
      *
      * This logic was extracted from an email thread on the JRuby mailing list -- Yehuda Katz & Charles Nutter
      * contributed this analysis above.
      * ********************************************************************************************************/
     private boolean canCaptureCallersBinding;
 
     /* ****************************************************************************
      * Does this scope define code, i.e. does it (or anybody in the downward call chain)
      * do class_eval, module_eval? In the absence of any other information, we default
      * to yes -- which basically leads to pessimistic but safe optimizations.  But, for
      * library and internal methods, this might be false.
      * **************************************************************************** */
     private boolean canModifyCode;
 
     /* ****************************************************************************
      * Does this scope require a binding to be materialized?
      * Yes if any of the following holds true:
      * - calls 'Proc.new'
      * - calls 'eval'
      * - calls 'call' (could be a call on a stored block which could be local!)
      * - calls 'send' and we cannot resolve the message (method name) that is being sent!
      * - calls methods that can access the caller's binding
      * - calls a method which we cannot resolve now!
      * - has a call whose closure requires a binding
      * **************************************************************************** */
     private boolean bindingHasEscaped;

    
Does this scope call any eval
 
     private boolean usesEval;

    
Does this scope receive keyword args?
 
     private boolean receivesKeywordArgs;

    
Does this scope have a break instr?
 
     protected boolean hasBreakInstrs;

    
Can this scope receive breaks
 
     protected boolean canReceiveBreaks;

    
Does this scope have a non-local return instr?
 
     protected boolean hasNonlocalReturns;

    
Can this scope receive a non-local return?
 
     public boolean canReceiveNonlocalReturns;

    
Since backref ($~) and lastline ($_) vars are allocated space on the dynamic scope, this is an useful flag to compute.
 
     private boolean usesBackrefOrLastline;

    
Does this scope call any zsuper
 
     private boolean usesZSuper;

    
Does this scope have loops?
 
     private boolean hasLoops;

    
# of thread poll instrs added to this scope
 
     private int threadPollInstrsCount;

    
Does this scope have explicit call protocol instructions? If yes, there are IR instructions for managing bindings/frames, etc. If not, this has to be managed implicitly as in the current runtime For now, only dyn-scopes are managed explicitly. Others will come in time
 
     private boolean hasExplicitCallProtocol;

    
Should we re-run compiler passes -- yes after we've inlined, for example
 
     private boolean relinearizeCFG;
 
     private IRManager manager;
 
     // Used by cloning code
     protected IRScope(IRScope sIRScope lexicalParent) {
         this. = lexicalParent;
         this. = s.manager;
         this. = s.fileName;
         this. = s.lineNumber;
         this. = s.staticScope;
         this. = s.threadPollInstrsCount;
         this. = s.nextClosureIndex;
         this. = s.temporaryVariableIndex;
         this. = s.hasLoops;
         this. = s.hasUnusedImplicitBlockArg;
         this. = null;
         this. = new ArrayList<IRClosure>();
         this. = new HashMap<StringDataFlowProblem>();
         this. = new HashMap<StringInteger>(); // SSS FIXME: clone!
         this. = null;
         this. = null;
         this. = null;
 
         this. = s.flagsComputed;
         this. = s.canModifyCode;
         this. = s.canCaptureCallersBinding;
         this. = s.receivesKeywordArgs;
         this. = s.hasBreakInstrs;
         this. = s.hasNonlocalReturns;
         this. = s.canReceiveBreaks;
         this. = s.canReceiveNonlocalReturns;
         this. = s.bindingHasEscaped;
         this. = s.usesEval;
         this. = s.usesBackrefOrLastline;
         this. = s.usesZSuper;
         this. = s.hasExplicitCallProtocol;
 
         this. = new LocalVariableAllocator(); // SSS FIXME: clone!
         this.. = s.localVars.nextSlot;
         this. = false;
 
         setupLexicalContainment();
     }
 
     public IRScope(IRManager managerIRScope lexicalParentString name,
             String fileNameint lineNumberStaticScope staticScope) {
         this. = manager;
         this. = lexicalParent;
         this. = name;
         this. = fileName;
         this. = lineNumber;
         this. = staticScope;
         this. = 0;
         this. = 0;
         this. = -1;
         this. = new ArrayList<Instr>();
         this. = new ArrayList<IRClosure>();
         this. = new HashMap<StringDataFlowProblem>();
         this. = new HashMap<StringInteger>();
         this. = null;
         this. = null;
         this. = null;
         this. = false;
         this. = false;
 
         this. = false;
         this. = false;
         this. = false;
         this. = false;
         this. = false;
         this. = false;
 
         // These flags are true by default!
         this. = true;
         this. = true;
         this. = true;
         this. = true;
         this. = true;
         this. = true;
 
         this. = false;
 
         this. = new LocalVariableAllocator();
         synchronized() { this. = ++; }
         this. = false;
 
         setupLexicalContainment();
     }
 
     private final void setupLexicalContainment() {
         if (.isDryRun()) {
              = new ArrayList<IRScope>();
             if ( != null.addChildScope(this);
         }
     }
 
     private boolean hasListener() {
         return .getIRScopeListener() != null;
     }
 
     public int getScopeId() {
         return ;
     }
 
     @Override
     public int hashCode() {
         return ;
     }
 
     @Override
     public boolean equals(Object other) {
         if (other == null || getClass() != other.getClass()) return false;
 
         return  == ((IRScopeother).;
     }
 
     protected void addChildScope(IRScope scope) {
         .add(scope);
     }
 
     public List<IRScopegetLexicalScopes() {
         return ;
     }
 
     public void addClosure(IRClosure c) {
         .add(c);
     }
 
     public Instr getLastInstr() {
         return .get(.size() - 1);
     }
 
     public void addInstrAtBeginning(Instr i) {
         if (hasListener()) {
             IRScopeListener listener = .getIRScopeListener();
             listener.addedInstr(thisi, 0);
         }
         .add(0, i);
     }
 
     public void addInstr(Instr i) {
         // SSS FIXME: If more instructions set these flags, there may be
         // a better way to do this by encoding flags in its own object
         // and letting every instruction update it.
         if (i instanceof ThreadPollInstr++;
         else if (i instanceof BreakInstrthis. = true;
         else if (i instanceof NonlocalReturnInstrthis. = true;
         else if (i instanceof DefineMetaClassInstrthis. = true;
         else if (i instanceof ReceiveKeywordArgInstr || i instanceof ReceiveKeywordRestArgInstrthis. = true;
         if (hasListener()) {
             IRScopeListener listener = .getIRScopeListener();
             listener.addedInstr(thisi.size());
         }
         .add(i);
     }
 
         return getLocalVariable("%flip_" + allocateNextPrefixedName("%flip"), 0);
     }
 
     public void initFlipStateVariable(Variable vOperand initState) {
         // Add it to the beginning
         .add(0, new CopyInstr(vinitState));
     }
 
     public boolean isForLoopBody() {
         return false;
     }
 
     public Label getNewLabel(String prefix) {
         return new Label(prefix + "_" + allocateNextPrefixedName(prefix));
     }
 
     public Label getNewLabel() {
         return getNewLabel("LBL");
     }
 
     public List<IRClosuregetClosures() {
         return ;
     }
 
     public IRManager getManager() {
         return ;
     }

    
Returns the lexical scope that contains this scope definition
 
     public IRScope getLexicalParent() {
         return ;
     }
 
     public StaticScope getStaticScope() {
         return ;
     }
 
     public IRMethod getNearestMethod() {
         IRScope current = this;
 
         while (current != null && !(current instanceof IRMethod)) {
             current = current.getLexicalParent();
         }
 
         return (IRMethodcurrent;
     }
 
     public IRScope getNearestFlipVariableScope() {
         IRScope current = this;
 
         while (current != null && !current.isFlipScope()) {
             current = current.getLexicalParent();
         }
 
         return current;
     }
 
         IRScope current = this;
 
         while (current != null && !current.isTopLocalVariableScope()) {
             current = current.getLexicalParent();
         }
 
         return current;
     }

    
Returns the nearest scope which we can extract a live module from. If this returns null (like for evals), then it means it cannot be statically determined.
 
         IRScope current = this;
 
         while (!(current instanceof IRModuleBody)) {
             // When eval'ing, we dont have a lexical view of what module we are nested in
             // because binding_eval, class_eval, module_eval, instance_eval can switch
             // around the lexical scope for evaluation to be something else.
             if (current == null || current instanceof IREvalScriptreturn null;
 
             current = current.getLexicalParent();
         }
 
         return current;
     }
 
     public String getName() {
         return ;
     }
 
     public void setName(String name) { // This is for IRClosure ;(
         this. = name;
     }
 
     public String getFileName() {
         return ;
     }
 
     public int getLineNumber() {
         return ;
     }

    
Returns the top level scope
 
     public IRScope getTopLevelScope() {
         IRScope current = this;
 
         for (; current != null && !current.isScriptScope(); current = current.getLexicalParent()) {}
 
         return current;
     }
 
     public boolean isNestedInClosure(IRClosure closure) {
         for (IRScope s = thiss != null && !s.isTopLocalVariableScope(); s = s.getLexicalParent()) {
             if (s == closurereturn true;
         }
 
         return false;
     }
 
     public void setHasLoopsFlag(boolean f) {
          = true;
     }
 
     public boolean hasLoops() {
         return ;
     }
 
     public boolean hasExplicitCallProtocol() {
         return ;
     }
 
     public void setExplicitCallProtocolFlag(boolean flag) {
         this. = flag;
     }
 
     public void setCodeModificationFlag(boolean f) {
          = f;
     }
 
     public boolean receivesKeywordArgs() {
         return this.;
     }
 
     public boolean modifiesCode() {
         return ;
     }
 
     public boolean bindingHasEscaped() {
         return ;
     }
 
     public boolean usesBackrefOrLastline() {
         return ;
     }
 
     public boolean usesEval() {
         return ;
     }
 
     public boolean usesZSuper() {
         return ;
     }
 
     public boolean canCaptureCallersBinding() {
         return ;
     }
 
     public boolean canReceiveNonlocalReturns() {
         if (this.) {
             return true;
         }
 
         boolean canReceiveNonlocalReturns = false;
         for (IRClosure cl : getClosures()) {
             if (cl.hasNonlocalReturns || cl.canReceiveNonlocalReturns()) {
                 canReceiveNonlocalReturns = true;
             }
         }
         return canReceiveNonlocalReturns;
     }
 
     public CFG buildCFG() {
          = new CFG(this);
         .build();
         // Clear out instruction list after CFG has been built.
         this. = null;
         return ;
     }
 
     protected void setCFG(CFG cfg) {
        this. = cfg;
     }
 
     public CFG getCFG() {
         return ;
     }
 
     private void setupLabelPCs(HashMap<LabelIntegerlabelIPCMap) {
         for (BasicBlock b) {
             Label l = b.getLabel();
             l.setTargetPC(labelIPCMap.get(l));
         }
     }
 
         checkRelinearization();
 
         if ( != nullreturn // Already prepared
 
         try {
             buildLinearization(); // FIXME: compiler passes should have done this
             depends(linearization());
         } catch (RuntimeException e) {
             .error("Error linearizing cfg: "e);
             CFG c = cfg();
             .error("\nGraph:\n" + c.toStringGraph());
             .error("\nInstructions:\n" + c.toStringInstrs());
             throw e;
         }
 
         // Set up IPCs
         HashMap<LabelIntegerlabelIPCMap = new HashMap<LabelInteger>();
         List<InstrnewInstrs = new ArrayList<Instr>();
         int ipc = 0;
         for (BasicBlock b) {
             labelIPCMap.put(b.getLabel(), ipc);
             List<InstrbbInstrs = b.getInstrs();
             int bbInstrsLength = bbInstrs.size();
             for (int i = 0; i < bbInstrsLengthi++) {
                 Instr instr = bbInstrs.get(i);
 
                 if (instr instanceof Specializeable) {
                     instr = ((Specializeableinstr).specializeForInterpretation();
                     bbInstrs.set(iinstr);
                 }
 
                 if (!(instr instanceof ReceiveSelfInstr)) {
                     newInstrs.add(instr);
                     ipc++;
                 }
             }
         }
 
         // Set up label PCs
         setupLabelPCs(labelIPCMap);
 
         // Exit BB ipc
         cfg().getExitBB().getLabel().setTargetPC(ipc + 1);
 
          = newInstrs.toArray(new Instr[newInstrs.size()]);
         return ;
     }
 
     private void runCompilerPasses() {
         // SSS FIXME: Why is this again?  Document this weirdness!
         // Forcibly clear out the shared eval-scope variable allocator each time this method executes
         initEvalScopeVariableAllocator(true);
         // SSS FIXME: We should configure different optimization levels
         // and run different kinds of analysis depending on time budget.  Accordingly, we need to set
         // IR levels/states (basic, optimized, etc.) and the
         // ENEBO: If we use a MT optimization mechanism we cannot mutate CFG
         // while another thread is using it.  This may need to happen on a clone()
         // and we may need to update the method to return the new method.  Also,
         // if this scope is held in multiple locations how do we update all references?
         CompilerPassScheduler scheduler = getManager().schedulePasses();
         for (CompilerPass passscheduler) {
             pass.run(this);
         }
     }

    
Run any necessary passes to get the IR ready for interpretation
 
     public synchronized Instr[] prepareForInterpretation(boolean isLambda) {
         if (isLambda) {
             // Add a global ensure block to catch uncaught breaks
             // and throw a LocalJumpError.
             if (((IRClosure)this).addGEBForUncaughtBreaks()) {
                 this. = true;
             }
         }
 
         checkRelinearization();
 
         if ( != nullreturn ;
 
         // Build CFG and run compiler passes, if necessary
         if (getCFG() == nullrunCompilerPasses();
 
         // Linearize CFG, etc.
         return prepareInstructionsForInterpretation();
     }
 
     /* SSS FIXME: Do we need to synchronize on this?  Cache this info in a scope field? */
    
Run any necessary passes to get the IR ready for compilation
 
     public Tuple<Instr[], Map<Integer,Label[]>> prepareForCompilation() {
         // Build CFG and run compiler passes, if necessary
         if (getCFG() == nullrunCompilerPasses();
 
         // Add this always since we dont re-JIT a previously
         // JIT-ted closure.  But, check if there are other
         // smarts available to us and eliminate adding this
         // code to every closure there is.
         //
         // Add a global ensure block to catch uncaught breaks
         // and throw a LocalJumpError.
         if (this instanceof IRClosure && ((IRClosure)this).addGEBForUncaughtBreaks()) {
             this. = true;
         }
 
         try {
             buildLinearization(); // FIXME: compiler passes should have done this
             depends(linearization());
         } catch (RuntimeException e) {
             .error("Error linearizing cfg: "e);
             CFG c = cfg();
             .error("\nGraph:\n" + c.toStringGraph());
             .error("\nInstructions:\n" + c.toStringInstrs());
             throw e;
         }
 
         // Set up IPCs
         // FIXME: Would be nice to collapse duplicate labels; for now, using Label[]
         HashMap<IntegerLabel[]> ipcLabelMap = new HashMap<IntegerLabel[]>();
         List<InstrnewInstrs = new ArrayList<Instr>();
         int ipc = 0;
         for (BasicBlock b : ) {
             Label l = b.getLabel();
             ipcLabelMap.put(ipccatLabels(ipcLabelMap.get(ipc), l));
             for (Instr i : b.getInstrs()) {
                 if (!(i instanceof ReceiveSelfInstr)) {
                     newInstrs.add(i);
                     ipc++;
                 }
             }
         }
 
         return new Tuple<Instr[], Map<Integer,Label[]>>(newInstrs.toArray(new Instr[newInstrs.size()]), ipcLabelMap);
     }
 
     private List<Object[]> buildJVMExceptionTable() {
         List<Object[]> etEntries = new ArrayList<Object[]>();
         for (BasicBlock b) {
             // We need handlers for:
             // - Unrescuable    (handled by ensures),
             // - Throwable      (handled by rescues)
             // in that order since Throwable < Unrescuable
             BasicBlock rBB = cfg().getRescuerBBFor(b);
             BasicBlock eBB = cfg().getEnsurerBBFor(b);
             if ((eBB != null) && (rBB == eBB || rBB == null)) {
                 // 1. same rescue and ensure handler ==> just spit out one entry with a Throwable class
                 // 2. only ensure handler            ==> just spit out one entry with a Throwable class
 
                 etEntries.add(new Object[] {b.getLabel(), eBB.getLabel(), Throwable.class});
             } else if (rBB != null) {
                 // Unrescuable comes before Throwable
                 if (eBB != nulletEntries.add(new Object[] {b.getLabel(), eBB.getLabel(), Unrescuable.class});
                 etEntries.add(new Object[] {b.getLabel(), rBB.getLabel(), Throwable.class});
             }
         }
 
         // SSS FIXME: This could be optimized by compressing entries for adjacent BBs that have identical handlers
         // This could be optimized either during generation or as another pass over the table.  But, if the JVM
         // does that already, do we need to bother with it?
         return etEntries;
     }
 
     private static Label[] catLabels(Label[] labelsLabel cat) {
         if (labels == nullreturn new Label[] {cat};
         Label[] newLabels = new Label[labels.length + 1];
         System.arraycopy(labels, 0, newLabels, 0, labels.length);
         newLabels[labels.length] = cat;
         return newLabels;
     }
 
     private boolean computeScopeFlags(boolean receivesClosureArgList<Instrinstrs) {
         for (Instr iinstrs) {
             Operation op = i.getOperation();
             if (op == .) {
                 receivesClosureArg = true;
             } else if (op == .) {
                 this. = true;
                 this. = true;
             } else if (i instanceof CallBase) {
                 CallBase call = (CallBasei;
 
                 if (call.targetRequiresCallersBinding()) this. = true;
 
                 if (call.canBeEval()) {
                     this. = true;
 
                     // If this method receives a closure arg, and this call is an eval that has more than 1 argument,
                     // it could be using the closure as a binding -- which means it could be using pretty much any
                     // variable from the caller's binding!
                     if (receivesClosureArg && (call.getCallArgs().length > 1)) {
                         this. = true;
                     }
                 }
             } else if (op == .) {
                 GlobalVariable gv = (GlobalVariable)((GetGlobalVariableInstr)i).getSource();
                 String gvName = gv.getName();
                 if (gvName.equals("$_") ||
                     gvName.equals("$~") ||
                     gvName.equals("$`") ||
                     gvName.equals("$'") ||
                     gvName.equals("$+") ||
                     gvName.equals("$LAST_READ_LINE") ||
                     gvName.equals("$LAST_MATCH_INFO") ||
                     gvName.equals("$PREMATCH") ||
                     gvName.equals("$POSTMATCH") ||
                     gvName.equals("$LAST_PAREN_MATCH"))
                 {
                     this. = true;
                 }
             } else if (op == .) {
                 GlobalVariable gv = (GlobalVariable)((PutGlobalVarInstr)i).getTarget();
                 String gvName = gv.getName();
                 if (gvName.equals("$_") || gvName.equals("$~"))  = true;
             } else if (op == . || op == . || op == .) {
                 this. = true;
             } else if (op == .) {
                 this. = true;
             } else if (i instanceof NonlocalReturnInstr) {
                 this. = true;
             } else if (i instanceof DefineMetaClassInstr) {
                 // SSS: Inner-classes are defined with closures and
                 // a return in the closure can force a return from this method
                 // For now conservatively assume that a scope with inner-classes
                 // can receive non-local returns. (Alternatively, have to inspect
                 // all lexically nested scopes, not just closures in computeScopeFlags())
                 this. = true;
             }
         }
 
         return receivesClosureArg;
     }
 
     //
     // This can help use eliminate writes to %block that are not used since this is
     // a special local-variable, not programmer-defined local-variable
     public void computeScopeFlags() {
         if () {
             return;
         }
 
         // init
          = true;
          = false;
          = false;
          = false;
          = false;
         // NOTE: bindingHasEscaped is the crucial flag and it effectively is
         // unconditionally true whenever it has a call that receives a closure.
         // See CallInstr.computeRequiresCallersBindingFlag
          = (this instanceof IREvalScript); // for eval scopes, bindings are considered escaped ...
          = false;
          = false;
          = false;
          = false;
 
         // recompute flags -- we could be calling this method different times
         // definitely once after ir generation and local optimizations propagates constants locally
         // but potentially at a later time after doing ssa generation and constant propagation
         if ( == null) {
             computeScopeFlags(falsegetInstrs());
         } else {
             boolean receivesClosureArg = false;
             for (BasicBlock b.getBasicBlocks()) {
                 receivesClosureArg = computeScopeFlags(receivesClosureArgb.getInstrs());
             }
         }
 
         // Compute flags for nested closures (recursively) and set derived flags.
         for (IRClosure cl : getClosures()) {
             cl.computeScopeFlags();
             if (cl.hasBreakInstrs || cl.canReceiveBreaks) {
                  = true;
             }
             if (cl.hasNonlocalReturns || cl.canReceiveNonlocalReturns) {
                  = true;
             }
             if (cl.usesZSuper()) {
                  = true;
             }
         }
 
          = true;
     }
 
     public abstract String getScopeName();
 
     @Override
     public String toString() {
         return getScopeName() + " " + getName() + "[" + getFileName() + ":" + getLineNumber() + "]";
     }
 
     public String toStringInstrs() {
         StringBuilder b = new StringBuilder();
 
         int i = 0;
         for (Instr instr : ) {
             if (i > 0) b.append("\n");
 
             b.append("  ").append(i).append('\t').append(instr);
 
             i++;
         }
 
         if (!.isEmpty()) {
             b.append("\n\n------ Closures encountered in this scope ------\n");
             for (IRClosure c)
                 b.append(c.toStringBody());
             b.append("------------------------------------------------\n");
         }
 
         return b.toString();
     }
 
     public String toPersistableString() {
         StringBuilder b = new StringBuilder();
 
         b.append("Scope:<");
         b.append();
         b.append(">");
         for (Instr instr : ) {
             b.append("\n");
             b.append(instr);
         }
 
         return b.toString();
     }
 
     public String toStringVariables() {
         Map<VariableIntegerends = new HashMap<VariableInteger>();
         Map<VariableIntegerstarts = new HashMap<VariableInteger>();
         SortedSet<Variablevariables = new TreeSet<Variable>();
 
         for (int i = .size() - 1; i >= 0; i--) {
             Instr instr = .get(i);
 
             if (instr instanceof ResultInstr) {
                 Variable var = ((ResultInstrinstr).getResult();
                 variables.add(var);
                 starts.put(vari);
             }
 
             for (Operand operand : instr.getOperands()) {
                 if (operand != null && operand instanceof Variable && ends.get((Variable)operand) == null) {
                     ends.put((Variable)operandi);
                     variables.add((Variable)operand);
                 }
             }
         }
 
         StringBuilder sb = new StringBuilder();
         int i = 0;
         for (Variable var : variables) {
             Integer end = ends.get(var);
             if (end != null) { // Variable is actually used somewhere and not dead
                 if (i > 0) sb.append("\n");
                 i++;
                 sb.append("    ").append(var).append(": ").append(starts.get(var)).append("-").append(end);
             }
         }
 
         return sb.toString();
     }

    
--------------------------------------- SSS FIXME: What is this method for?

Interp:
public void calculateParameterCounts() { for (int i = instrList.size() - 1; i >= 0; i--) { Instr instr = instrList.get(i); } } ------------------------------------------
 
 
     public LocalVariable getSelf() {
         return .;
     }
 
     public Variable getCurrentModuleVariable() {
         // SSS: Used in only 3 cases in generated IR:
         // -> searching a constant in the inheritance hierarchy
        // -> searching a super-method in the inheritance hierarchy
        // -> looking up 'StandardError' (which can be eliminated by creating a special operand type for this)
        return ;
    }
        // SSS: Used in only 1 case in generated IR:
        // -> searching a constant in the lexical scope hierarchy
        return ;
    }
    public abstract LocalVariable getImplicitBlockArg();
    public void markUnusedImplicitBlockArg() {
         = true;
    }
    public LocalVariable findExistingLocalVariable(String nameint depth) {
        return .getVariable(name);
    }

    
Find or create a local variable. By default, scopes are assumed to only check current depth. Blocks/Closures override this because they have special nesting rules.
    public LocalVariable getLocalVariable(String nameint scopeDepth) {
        LocalVariable lvar = findExistingLocalVariable(namescopeDepth);
        if (lvar == null) {
            lvar = new LocalVariable(namescopeDepth.);
            .putVariable(namelvar);
        }
        return lvar;
    }
    public LocalVariable getNewLocalVariable(String nameint depth) {
        throw new RuntimeException("getNewLocalVariable should be called for: " + this.getClass().getName());
    }
    protected void initEvalScopeVariableAllocator(boolean reset) {
        if (reset ||  == null = new LocalVariableAllocator();
    }
        ++;
        return new TemporaryVariable();
    }
        ++;
        return new TemporaryVariable(name);
    }
    public void resetTemporaryVariables() {
         = -1;
    }
    public int getTemporaryVariableSize() {
        return  + 1;
    }
    // Generate a new variable for inlined code
    public Variable getNewInlineVariable(String inlinePrefixVariable v) {
        if (v instanceof LocalVariable) {
            LocalVariable lv = (LocalVariable)v;
            return getLocalVariable(inlinePrefix + lv.getName(), lv.getScopeDepth());
        } else {
            return getNewTemporaryVariable();
        }
    }
    public int getThreadPollInstrsCount() {
        return ;
    }
    public int getLocalVariablesCount() {
        return .;
    }
    public int getUsedVariablesCount() {
        // System.out.println("For " + this + ", # lvs: " + nextLocalVariableSlot);
        // # local vars, # flip vars
        //
        // SSS FIXME: When we are opting local var access,
        // no need to allocate local var space except when we have been asked to!
        return getLocalVariablesCount() + getPrefixCountSize("%flip");
    }
    public void setUpUseDefLocalVarMaps() {
         = new java.util.HashSet<Variable>();
         = new java.util.HashSet<Variable>();
        for (BasicBlock bb : cfg().getBasicBlocks()) {
            for (Instr i : bb.getInstrs()) {
                for (Variable v : i.getUsedVariables()) {
                    if (v instanceof LocalVariable.add(v);
                }
                if (i instanceof ResultInstr) {
                    Variable v = ((ResultInstri).getResult();
                    if (v instanceof LocalVariable.add(v);
                }
            }
        }
        for (IRClosure cl : getClosures()) {
            cl.setUpUseDefLocalVarMaps();
        }
    }
    public boolean usesLocalVariable(Variable v) {
        if ( == nullsetUpUseDefLocalVarMaps();
        if (.contains(v)) return true;
        for (IRClosure cl : getClosures()) {
            if (cl.usesLocalVariable(v)) return true;
        }
        return false;
    }
    public boolean definesLocalVariable(Variable v) {
        if ( == nullsetUpUseDefLocalVarMaps();
        if (.contains(v)) return true;
        for (IRClosure cl : getClosures()) {
            if (cl.definesLocalVariable(v)) return true;
        }
        return false;
    }
    public void