private void encodeCallBaseInstr(CallBase instr) { boolean hasClosure = instr.getClosureArg(null) != null; e.encode(instr.getCallType().ordinal()); e.encode(instr.getMethodAddr().getName()); e.encode(instr.getReceiver()); e.encode(calculateArity(instr.getCallArgs(), hasClosure)); for (Operand arg : instr.getCallArgs()) { e.encode(arg); } if (hasClosure) e.encode(instr.getClosureArg(null)); }
public void unbox(Map<Variable, TemporaryLocalVariable> unboxMap) { // System.out.println("BB : " + basicBlock + " in " + this.problem.getScope().getName()); // System.out.println("-- known types on entry:"); // for (Variable v: inState.types.keySet()) { // if (inState.types.get(v) != Object.class) { // System.out.println(v + "-->" + inState.types.get(v)); // } // } // System.out.print("-- unboxed vars on entry:"); // for (Variable v: inState.unboxedVars) { // System.out.print(" " + v); // } // System.out.println("------"); // System.out.print("-- unboxed vars on exit:"); // for (Variable v: outState.unboxedVars) { // System.out.print(" " + v); // } // System.out.println("------"); // Compute UNION(unboxedVarsIn(all-successors)) - this.unboxedVarsOut // All vars in this new set have to be unboxed on exit from this BB boolean scopeBindingHasEscaped = problem.getScope().bindingHasEscaped(); Set<Variable> succUnboxedVars = new HashSet<Variable>(); CFG cfg = problem.getScope().cfg(); for (Edge e : cfg.getOutgoingEdges(basicBlock)) { BasicBlock b = (BasicBlock) e.getDestination().getData(); if (b != cfg.getExitBB()) { UnboxableOpsAnalysisNode x = (UnboxableOpsAnalysisNode) problem.getFlowGraphNode(b); succUnboxedVars.addAll(x.inState.unboxedVars); } } succUnboxedVars.removeAll(outState.unboxedVars); // Only worry about vars live on exit from the BB LiveVariablesProblem lvp = (LiveVariablesProblem) problem.getScope().getDataFlowSolution(DataFlowConstants.LVP_NAME); BitSet liveVarsSet = ((LiveVariableNode) lvp.getFlowGraphNode(basicBlock)).getLiveInBitSet(); // Rescue node, if any IRScope scope = this.problem.getScope(); boolean isClosure = scope instanceof IRClosure; List<Instr> newInstrs = new ArrayList<Instr>(); boolean unboxedLiveVars = false; initSolution(); for (Instr i : basicBlock.getInstrs()) { Variable dst = null; boolean dirtied = false; boolean hitDFBarrier = false; // System.out.println("ORIG: " + i); if (i.getOperation().transfersControl()) { // Add unboxing instrs. for (Variable v : succUnboxedVars) { if (liveVarsSet.get(lvp.getDFVar(v).getId())) { // System.out.println("suv: UNBOXING for " + v); newInstrs.add(new UnboxFloatInstr(getUnboxedVar(unboxMap, v), v)); tmpState.unboxedVars.add(v); } } unboxedLiveVars = true; } else { if (i instanceof ResultInstr) { dst = ((ResultInstr) i).getResult(); } if (i instanceof CopyInstr) { // Copies are easy Operand src = ((CopyInstr) i).getSource(); Class srcType = getOperandType(tmpState, src); setOperandType(tmpState, dst, srcType); // If we have an unboxed type for 'src', we can leave this unboxed. // // FIXME: However, if 'src' is a constant, this could unnecessarily // leave 'src' unboxed and lead to a boxing instruction further down // at the use site of 'dst'. This indicates that leaving this unboxed // should ideally be done 'on-demand'. This indicates that this could // be a backward-flow algo OR that this algo should be run on a // dataflow graph / SSA graph. if (srcType == Float.class) { Operand unboxedSrc = src instanceof Variable ? getUnboxedVar(unboxMap, (Variable) src) : src; TemporaryLocalVariable unboxedDst = getUnboxedVar(unboxMap, dst); newInstrs.add(new CopyInstr(Operation.COPY_UNBOXED, unboxedDst, unboxedSrc)); dirtied = true; } } else if (i instanceof CallBase) { // Process calls specially -- these are what we want to optimize! CallBase c = (CallBase) i; Operand o = c.getClosureArg(null); if (o == null) { MethAddr m = c.getMethodAddr(); Operand r = c.getReceiver(); Operand[] args = c.getCallArgs(); if (args.length == 1 && m.resemblesALUOp()) { Operand a = args[0]; Class receiverType = getOperandType(tmpState, r); Class argType = getOperandType(tmpState, a); // Optimistically assume that call is an ALU op if (receiverType == Float.class || (receiverType == Fixnum.class && argType == Float.class)) { setOperandType(tmpState, dst, Float.class); r = getUnboxedOperand(tmpState.unboxedVars, unboxMap, r, newInstrs); a = getUnboxedOperand(tmpState.unboxedVars, unboxMap, a, newInstrs); TemporaryLocalVariable unboxedDst = getUnboxedVar(unboxMap, dst); newInstrs.add(new AluInstr(m.getUnboxedOp(Float.class), unboxedDst, r, a)); dirtied = true; } else { if (receiverType == Fixnum.class && argType == Fixnum.class) { setOperandType(tmpState, dst, Fixnum.class); } else { setOperandType(tmpState, dst, Object.class); } if (c.targetRequiresCallersBinding()) { hitDFBarrier = true; } } } else { setOperandType(tmpState, dst, Object.class); } } else { if (o instanceof WrappedIRClosure) { // Since binding can escape in arbitrary ways in the general case, // assume the worst for now. If we are guaranteed that the closure binding // is not used outside the closure itself, we can avoid worst-case behavior. hitDFBarrier = true; // Fetch the nested unboxing-analysis problem, creating one if necessary IRClosure cl = ((WrappedIRClosure) o).getClosure(); UnboxableOpsAnalysisProblem subProblem = (UnboxableOpsAnalysisProblem) cl.getDataFlowSolution(DataFlowConstants.UNBOXING); UnboxableOpsAnalysisNode exitNode = (UnboxableOpsAnalysisNode) subProblem.getExitNode(); // Compute solution subProblem.unbox(); // Update types to MEET(new-state-on-exit, current-state) tmpState.computeMEETForTypes(exitNode.outState, true); // As for unboxed var state, since binding can escape in // arbitrary ways in the general case, assume the worst for now. // If we are guaranteed that the closure binding is not used // outside the closure itself, we can avoid worst-case behavior // and only clear vars that are modified in the closure. hitDFBarrier = true; } else { // Cannot analyze hitDFBarrier = true; } } } else { // We dont know how to optimize this instruction. // So, we assume we dont know type of the result. // TOP/class --> BOTTOM setOperandType(tmpState, dst, Object.class); } } if (dirtied) { tmpState.unboxedVars.add(dst); tmpState.unboxedDirtyVars.add(dst); } else { // Since the instruction didn't run in unboxed form, // dirty unboxed vars will have to get boxed here. boxRequiredVars( i, tmpState, unboxMap, dst, hasExceptionsRescued(), hitDFBarrier, newInstrs); } } // Add unboxing instrs. if (!unboxedLiveVars) { for (Variable v : succUnboxedVars) { if (liveVarsSet.get(lvp.getDFVar(v).getId())) { // System.out.println("suv: UNBOXING for " + v); newInstrs.add(new UnboxFloatInstr(getUnboxedVar(unboxMap, v), v)); } } } /* System.out.println("------"); for (Instr i : newInstrs) { System.out.println("NEW: " + i); } */ basicBlock.replaceInstrs(newInstrs); }
public void applyTransferFunction(Instr i) { // Rescue node, if any boolean scopeBindingHasEscaped = problem.getScope().bindingHasEscaped(); Variable dst = null; boolean dirtied = false; boolean hitDFBarrier = false; if (i instanceof ResultInstr) { dst = ((ResultInstr) i).getResult(); } if (i instanceof CopyInstr) { // Copies are easy Operand src = ((CopyInstr) i).getSource(); Class srcType = getOperandType(tmpState, src); setOperandType(tmpState, dst, srcType); // If we have an unboxed type for 'src', we can leave this unboxed. // // FIXME: However, if 'src' is a constant, this could unnecessarily // leave 'src' unboxed and lead to a boxing instruction further down // at the use site of 'dst'. This indicates that leaving this unboxed // should ideally be done 'on-demand'. This indicates that this could // be a backward-flow algo OR that this algo should be run on a // dataflow graph / SSA graph. if (srcType == Float.class) { dirtied = true; } } else if (i instanceof CallBase) { // Process calls specially -- these are what we want to optimize! CallBase c = (CallBase) i; Operand o = c.getClosureArg(null); if (o == null) { MethAddr m = c.getMethodAddr(); Operand r = c.getReceiver(); Operand[] args = c.getCallArgs(); if (args.length == 1 && m.resemblesALUOp()) { Operand a = args[0]; Class receiverType = getOperandType(tmpState, r); Class argType = getOperandType(tmpState, a); // Optimistically assume that call is an ALU op if (receiverType == Float.class || (receiverType == Fixnum.class && argType == Float.class)) { setOperandType(tmpState, dst, Float.class); // If 'r' and 'a' are not already in unboxed forms at this point, // they will get unboxed after this, because we want to opt. this call if (r instanceof Variable) { tmpState.unboxedVars.add((Variable) r); } if (a instanceof Variable) { tmpState.unboxedVars.add((Variable) a); } dirtied = true; } else { if (receiverType == Fixnum.class && argType == Fixnum.class) { setOperandType(tmpState, dst, Fixnum.class); } else { setOperandType(tmpState, dst, Object.class); } if (c.targetRequiresCallersBinding()) { hitDFBarrier = true; } } } else { setOperandType(tmpState, dst, Object.class); } } else { if (o instanceof WrappedIRClosure) { // Fetch the nested unboxing-analysis problem, creating one if necessary IRClosure cl = ((WrappedIRClosure) o).getClosure(); UnboxableOpsAnalysisProblem subProblem = (UnboxableOpsAnalysisProblem) cl.getDataFlowSolution(DataFlowConstants.UNBOXING); if (subProblem == null) { subProblem = new UnboxableOpsAnalysisProblem(); subProblem.setup(cl); cl.setDataFlowSolution(DataFlowConstants.UNBOXING, subProblem); } UnboxableOpsAnalysisNode exitNode = (UnboxableOpsAnalysisNode) subProblem.getExitNode(); UnboxableOpsAnalysisNode entryNode = (UnboxableOpsAnalysisNode) subProblem.getEntryNode(); // Init it to MEET(state-on-entry, state-on-exit). // The meet is required to account for participation of the closure in a loop. // Ex: f = 0.0; n.times { f += 10; } entryNode.inState = new UnboxState(); for (Variable v : tmpState.types.keySet()) { if (v instanceof LocalVariable) { entryNode.inState.types.put(v, tmpState.types.get(v)); } } entryNode.inState.computeMEET(exitNode.outState); // Compute solution subProblem.compute_MOP_Solution(); // Update types to MEET(new-state-on-exit, current-state) tmpState.computeMEETForTypes(exitNode.outState, true); // As for unboxed var state, since binding can escape in // arbitrary ways in the general case, assume the worst for now. // If we are guaranteed that the closure binding is not used // outside the closure itself, we can avoid worst-case behavior // and only clear vars that are modified in the closure. hitDFBarrier = true; } else { // Black hole -- cannot analyze hitDFBarrier = true; } } } else { // We dont know how to optimize this instruction. // So, we assume we dont know type of the result. // TOP/class --> BOTTOM setOperandType(tmpState, dst, Object.class); } if (dirtied) { tmpState.unboxedVars.add(dst); tmpState.unboxedDirtyVars.add(dst); } else { // Since the instruction didn't run in unboxed form, // dirty unboxed vars will have to get boxed here. updateUnboxedVarsInfo(i, tmpState, dst, hasExceptionsRescued(), hitDFBarrier); } }
private static void analyzeProfile() { versionCount++; // if (inlineCount == 2) return; if (codeModificationsCount == 0) numCyclesWithNoModifications++; else numCyclesWithNoModifications = 0; codeModificationsCount = 0; if (numCyclesWithNoModifications < 3) return; // We are now good to go -- start analyzing the profile // System.out.println("-------------------start analysis-----------------------"); final HashMap<IRScope, Long> scopeCounts = new HashMap<IRScope, Long>(); final ArrayList<IRCallSite> callSites = new ArrayList<IRCallSite>(); HashMap<IRCallSite, Long> callSiteCounts = new HashMap<IRCallSite, Long>(); // System.out.println("# call sites: " + callProfile.keySet().size()); long total = 0; for (Long id : callProfile.keySet()) { Long c; CallSiteProfile csp = callProfile.get(id); IRCallSite cs = csp.cs; if (cs.v != scopeVersionMap.get(cs.s).intValue()) { // System.out.println("Skipping callsite: <" + cs.s + "," + cs.v + "> with compiled version: // " + scopeVersionMap.get(cs.s)); continue; } Set<IRScope> calledScopes = csp.counters.keySet(); cs.count = 0; for (IRScope s : calledScopes) { c = scopeCounts.get(s); if (c == null) { c = new Long(0); scopeCounts.put(s, c); } long x = csp.counters.get(s).count; c += x; cs.count += x; } CallBase call = cs.call; if (calledScopes.size() == 1 && !call.inliningBlocked()) { CallSite runtimeCS = call.getCallSite(); if (runtimeCS != null && (runtimeCS instanceof CachingCallSite)) { CachingCallSite ccs = (CachingCallSite) runtimeCS; CacheEntry ce = ccs.getCache(); if (!(ce.method instanceof InterpretedIRMethod)) { // System.out.println("NOT IR-M!"); continue; } else { callSites.add(cs); cs.tgtM = (InterpretedIRMethod) ce.method; } } } total += cs.count; } Collections.sort( callSites, new java.util.Comparator<IRCallSite>() { @Override public int compare(IRCallSite a, IRCallSite b) { if (a.count == b.count) return 0; return (a.count < b.count) ? 1 : -1; } }); // Find top N call sites double freq = 0.0; int i = 0; boolean noInlining = true; Set<IRScope> inlinedScopes = new HashSet<IRScope>(); for (IRCallSite ircs : callSites) { double contrib = (ircs.count * 100.0) / total; // 1% is arbitrary if (contrib < 1.0) break; i++; freq += contrib; // This check is arbitrary if (i == 100 || freq > 99.0) break; // System.out.println("Considering: " + ircs.call + " with id: " + ircs.call.callSiteId + // " in scope " + ircs.s + " with count " + ircs.count + "; contrib " + contrib + "; freq: " + // freq); // Now inline here! CallBase call = ircs.call; IRScope hs = ircs.s; boolean isHotClosure = hs instanceof IRClosure; IRScope hc = isHotClosure ? hs : null; hs = isHotClosure ? hs.getLexicalParent() : hs; IRScope tgtMethod = ircs.tgtM.getIRMethod(); Instr[] instrs = tgtMethod.getInstrsForInterpretation(); // Dont inline large methods -- 500 is arbitrary // Can be null if a previously inlined method hasn't been rebuilt if ((instrs == null) || instrs.length > 500) { // if (instrs == null) System.out.println("no instrs!"); // else System.out.println("large method with " + instrs.length + " instrs. skipping!"); continue; } RubyModule implClass = ircs.tgtM.getImplementationClass(); int classToken = implClass.getGeneration(); String n = tgtMethod.getName(); boolean inlineCall = true; if (isHotClosure) { Operand clArg = call.getClosureArg(null); inlineCall = (clArg instanceof WrappedIRClosure) && (((WrappedIRClosure) clArg).getClosure() == hc); } if (inlineCall) { noInlining = false; long start = new java.util.Date().getTime(); hs.inlineMethod(tgtMethod, implClass, classToken, null, call); inlinedScopes.add(hs); long end = new java.util.Date().getTime(); // System.out.println("Inlined " + tgtMethod + " in " + hs + // " @ instr " + call + " in time (ms): " // + (end-start) + " # instrs: " + instrs.length); inlineCount++; } else { // System.out.println("--no inlining--"); } } for (IRScope x : inlinedScopes) { // update version count for 'hs' scopeVersionMap.put(x, versionCount); // System.out.println("Updating version of " + x + " to " + versionCount); // System.out.println("--- pre-inline-instrs ---"); // System.out.println(x.getCFG().toStringInstrs()); // System.out.println("--- post-inline-instrs ---"); // System.out.println(x.getCFG().toStringInstrs()); } // reset codeModificationsCount = 0; callProfile = new HashMap<Long, CallSiteProfile>(); // Every 1M thread polls, discard stats by reallocating the thread-poll count map if (globalThreadPollCount % 1000000 == 0) { globalThreadPollCount = 0; } }
public void unbox(Map<Variable, TemporaryLocalVariable> unboxMap) { // System.out.println("BB : " + basicBlock + " in " + problem.getScope().getName()); // System.out.println("-- known types on entry:"); // for (Variable v: inState.types.keySet()) { // if (inState.types.get(v) != Object.class) { // System.out.println(v + "-->" + inState.types.get(v)); // } // } // System.out.print("-- unboxed vars on entry:"); // for (Variable v: inState.unboxedVars) { // System.out.print(" " + v); // } // System.out.println("------"); // System.out.print("-- unboxed vars on exit:"); // for (Variable v: outState.unboxedVars) { // System.out.print(" " + v); // } // System.out.println("------"); CFG cfg = getCFG(); // Compute UNION(unboxedVarsIn(all-successors)) - this.unboxedVarsOut // All vars in this new set have to be unboxed on exit from this BB HashMap<Variable, Class> succUnboxedVars = new HashMap<Variable, Class>(); for (BasicBlock b : cfg.getOutgoingDestinations(basicBlock)) { if (b.isExitBB()) continue; Map<Variable, Class> xVars = problem.getFlowGraphNode(b).inState.unboxedVars; for (Variable v2 : xVars.keySet()) { // VERY IMPORTANT: Pay attention! // // Technically, the successors of this node may not all agree on what // the unboxed type ought to be for 'v2'. For example, one successor might // want 'v2' in Fixnum form and other might want it in Float form. If that // happens, we have to add unboxing instructions for each of those expected // types. However, for now, we are going to punt and assume that our successors // agree on unboxed types for 'v2'. succUnboxedVars.put(v2, xVars.get(v2)); } } // Same caveat as above applies here for (Variable v3 : outState.unboxedVars.keySet()) { succUnboxedVars.remove(v3); } // Only worry about vars live on exit from the BB LiveVariablesProblem lvp = (LiveVariablesProblem) problem.getScope().getDataFlowSolution(DataFlowConstants.LVP_NAME); BitSet liveVarsSet = lvp.getFlowGraphNode(basicBlock).getLiveInBitSet(); List<Instr> newInstrs = new ArrayList<Instr>(); boolean unboxedLiveVars = false; initSolution(); for (Instr i : basicBlock.getInstrs()) { Variable dst = null; boolean dirtied = false; boolean hitDFBarrier = false; // System.out.println("ORIG: " + i); if (i.getOperation().transfersControl()) { // Add unboxing instrs. for (Variable v : succUnboxedVars.keySet()) { if (liveVarsSet.get(lvp.getDFVar(v))) { unboxVar(tmpState, succUnboxedVars.get(v), unboxMap, v, newInstrs); } } unboxedLiveVars = true; } else { if (i instanceof ResultInstr) { dst = ((ResultInstr) i).getResult(); } if (i instanceof CopyInstr) { // Copies are easy Operand src = ((CopyInstr) i).getSource(); Class srcType = getOperandType(tmpState, src); setOperandType(tmpState, dst, srcType); // If we have an unboxed type for 'src', we can leave this unboxed. // // FIXME: However, if 'src' is a constant, this could unnecessarily // leave 'src' unboxed and lead to a boxing instruction further down // at the use site of 'dst'. This indicates that leaving this unboxed // should ideally be done 'on-demand'. This indicates that this could // be a backward-flow algo OR that this algo should be run on a // dataflow graph / SSA graph. if (srcType == Float.class || srcType == Fixnum.class) { Operand unboxedSrc = src instanceof Variable ? getUnboxedVar(srcType, unboxMap, (Variable) src) : src; TemporaryLocalVariable unboxedDst = getUnboxedVar(srcType, unboxMap, dst); newInstrs.add(new CopyInstr(Operation.COPY, unboxedDst, unboxedSrc)); tmpState.unboxedVars.put(dst, srcType); dirtied = true; } } else if (i instanceof ClosureAcceptingInstr) { Operand o = ((ClosureAcceptingInstr) i).getClosureArg(); if (i instanceof CallBase && o == null) { CallBase c = (CallBase) i; MethAddr m = c.getMethodAddr(); Operand r = c.getReceiver(); Operand[] args = c.getCallArgs(); if (dst != null && args.length == 1 && m.resemblesALUOp()) { Operand a = args[0]; Class receiverType = getOperandType(tmpState, r); Class argType = getOperandType(tmpState, a); // Optimistically assume that call is an ALU op Operation unboxedOp; if ((receiverType == Float.class || (receiverType == Fixnum.class && argType == Float.class)) && (unboxedOp = m.getUnboxedOp(Float.class)) != null) { dirtied = true; Class dstType = m.getUnboxedResultType(Float.class); setOperandType(tmpState, dst, dstType); tmpState.unboxedVars.put(dst, dstType); TemporaryLocalVariable unboxedDst = getUnboxedVar(dstType, unboxMap, dst); r = unboxOperand(tmpState, Float.class, unboxMap, r, newInstrs); a = unboxOperand(tmpState, Float.class, unboxMap, a, newInstrs); newInstrs.add(new AluInstr(unboxedOp, unboxedDst, r, a)); } else if ((receiverType == Float.class || (receiverType == Fixnum.class && argType == Fixnum.class)) && (unboxedOp = m.getUnboxedOp(Fixnum.class)) != null) { dirtied = true; Class dstType = m.getUnboxedResultType(Fixnum.class); setOperandType(tmpState, dst, dstType); tmpState.unboxedVars.put(dst, dstType); TemporaryLocalVariable unboxedDst = getUnboxedVar(dstType, unboxMap, dst); r = unboxOperand(tmpState, Fixnum.class, unboxMap, r, newInstrs); a = unboxOperand(tmpState, Fixnum.class, unboxMap, a, newInstrs); newInstrs.add(new AluInstr(unboxedOp, unboxedDst, r, a)); } else { if (receiverType == Fixnum.class && argType == Fixnum.class) { setOperandType(tmpState, dst, Fixnum.class); } else { setOperandType(tmpState, dst, Object.class); } if (c.targetRequiresCallersBinding()) { hitDFBarrier = true; } } } else { setOperandType(tmpState, dst, Object.class); } } else { if (o instanceof WrappedIRClosure) { // Since binding can escape in arbitrary ways in the general case, // assume the worst for now. If we are guaranteed that the closure binding // is not used outside the closure itself, we can avoid worst-case behavior. hitDFBarrier = true; // Fetch the nested unboxing-analysis problem, creating one if necessary IRClosure cl = ((WrappedIRClosure) o).getClosure(); UnboxableOpsAnalysisProblem subProblem = (UnboxableOpsAnalysisProblem) cl.getDataFlowSolution(DataFlowConstants.UNBOXING); UnboxableOpsAnalysisNode exitNode = subProblem.getExitNode(); // Compute solution subProblem.unbox(); // Update types to MEET(new-state-on-exit, current-state) tmpState.computeMEETForTypes(exitNode.outState, true); // As for unboxed var state, since binding can escape in // arbitrary ways in the general case, assume the worst for now. // If we are guaranteed that the closure binding is not used // outside the closure itself, we can avoid worst-case behavior // and only clear vars that are modified in the closure. hitDFBarrier = true; } else { // Cannot analyze hitDFBarrier = true; } } } else { // We dont know how to optimize this instruction. // So, we assume we dont know type of the result. // TOP/class --> BOTTOM setOperandType(tmpState, dst, Object.class); } } if (dirtied) { tmpState.unboxedDirtyVars.add(dst); } else { // Since the instruction didn't run in unboxed form, // dirty unboxed vars will have to get boxed here. boxRequiredVars( i, tmpState, unboxMap, dst, hasExceptionsRescued(), hitDFBarrier, newInstrs); } } // Add unboxing instrs. if (!unboxedLiveVars) { for (Variable v : succUnboxedVars.keySet()) { if (liveVarsSet.get(lvp.getDFVar(v))) { unboxVar(tmpState, succUnboxedVars.get(v), unboxMap, v, newInstrs); } } } /* System.out.println("------"); for (Instr i : newInstrs) { System.out.println("NEW: " + i); } */ basicBlock.replaceInstrs(newInstrs); }
private boolean computeScopeFlags(boolean receivesClosureArg, List<Instr> instrs) { for (Instr i : instrs) { Operation op = i.getOperation(); if (op == Operation.RECV_CLOSURE) { receivesClosureArg = true; } else if (op == Operation.ZSUPER) { this.canCaptureCallersBinding = true; this.usesZSuper = true; } else if (i instanceof CallBase) { CallBase call = (CallBase) i; if (call.targetRequiresCallersBinding()) this.bindingHasEscaped = true; if (call.canBeEval()) { this.usesEval = 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.canCaptureCallersBinding = true; } } } else if (op == Operation.GET_GLOBAL_VAR) { 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.usesBackrefOrLastline = true; } } else if (op == Operation.PUT_GLOBAL_VAR) { GlobalVariable gv = (GlobalVariable) ((PutGlobalVarInstr) i).getTarget(); String gvName = gv.getName(); if (gvName.equals("$_") || gvName.equals("$~")) usesBackrefOrLastline = true; } else if (op == Operation.MATCH || op == Operation.MATCH2 || op == Operation.MATCH3) { this.usesBackrefOrLastline = true; } else if (op == Operation.BREAK) { this.hasBreakInstrs = true; } else if (i instanceof NonlocalReturnInstr) { this.hasNonlocalReturns = 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.canReceiveNonlocalReturns = true; } } return receivesClosureArg; }