@Override public Object execute(IRScope s, Object... data) { StoreLocalVarPlacementProblem slvp = new StoreLocalVarPlacementProblem(); // Only run if we are pushing a scope or we are reusing the parents scope. if (!s.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED) || s.getFlags().contains(IRFlags.REUSE_PARENT_DYNSCOPE)) { // Make sure flags are computed s.computeScopeFlags(); Map<Operand, Operand> varRenameMap = new HashMap<Operand, Operand>(); // 1. Figure out required stores // 2. Add stores // 3. Figure out required loads // 4. Add loads // // Order is important since loads in 3. depend on stores in 2. slvp.setup(s); slvp.compute_MOP_Solution(); // Add stores, assigning an equivalent tmp-var for each local var slvp.addStores(varRenameMap); // Once stores have been added, figure out required loads LoadLocalVarPlacementProblem llvp = new LoadLocalVarPlacementProblem(); llvp.setup(s); llvp.compute_MOP_Solution(); // Add loads llvp.addLoads(varRenameMap); // Rename all local var uses with their tmp-var stand-ins for (BasicBlock b : s.getCFG().getBasicBlocks()) { for (Instr i : b.getInstrs()) i.renameVars(varRenameMap); } // Run on all nested closures. // // In the current implementation, nested scopes are processed independently (unlike Live // Variable Analysis) for (IRClosure c : s.getClosures()) run(c, false, true); // LVA information is no longer valid after this pass // FIXME: Grrr ... this seems broken to have to create a new object to invalidate (new LiveVariableAnalysis()).invalidate(s); } s.setDataFlowSolution(StoreLocalVarPlacementProblem.NAME, slvp); return slvp; }
@Override public Object execute(IRScope scope, Object... data) { // IRScriptBody do not get explicit call protocol instructions right now. // They dont push/pop a frame and do other special things like run begin/end blocks. // So, for now, they go through the runtime stub in IRScriptBody. // // Add explicit frame and binding push/pop instrs ONLY for methods -- we cannot handle this in // closures and evals yet // If the scope uses $_ or $~ family of vars, has local load/stores, or if its binding has // escaped, we have // to allocate a dynamic scope for it and add binding push/pop instructions. if (explicitCallProtocolSupported(scope)) { StoreLocalVarPlacementProblem slvpp = (StoreLocalVarPlacementProblem) scope.getDataFlowSolution(StoreLocalVarPlacementProblem.NAME); boolean scopeHasLocalVarStores = false; boolean bindingHasEscaped = scope.bindingHasEscaped(); CFG cfg = scope.cfg(); if (slvpp != null && bindingHasEscaped) { scopeHasLocalVarStores = slvpp.scopeHasLocalVarStores(); } else { // We dont require local-var load/stores to have been run. // If it is not run, we go conservative and add push/pop binding instrs. everywhere scopeHasLocalVarStores = bindingHasEscaped; } boolean requireFrame = doesItRequireFrame(scope, bindingHasEscaped); boolean requireBinding = !scope.getFlags().contains(IRFlags.DYNSCOPE_ELIMINATED); if (requireBinding || requireFrame) { BasicBlock entryBB = cfg.getEntryBB(); // Push if (requireFrame) entryBB.addInstr(new PushFrameInstr(scope.getName())); if (requireBinding) entryBB.addInstr(new PushBindingInstr()); // SSS FIXME: We are doing this conservatively. // Only scopes that have unrescued exceptions need a GEB. // // Allocate GEB if necessary for popping BasicBlock geb = cfg.getGlobalEnsureBB(); if (geb == null) { Variable exc = scope.createTemporaryVariable(); geb = new BasicBlock(cfg, Label.getGlobalEnsureBlockLabel()); geb.addInstr( new ReceiveJRubyExceptionInstr(exc)); // JRuby Implementation exception handling geb.addInstr(new ThrowExceptionInstr(exc)); cfg.addGlobalEnsureBB(geb); } // Pop on all scope-exit paths for (BasicBlock bb : cfg.getBasicBlocks()) { Instr i = null; ListIterator<Instr> instrs = bb.getInstrs().listIterator(); while (instrs.hasNext()) { i = instrs.next(); // Right now, we only support explicit call protocol on methods. // So, non-local returns and breaks don't get here. // Non-local-returns and breaks are tricky since they almost always // throw an exception and we don't multiple pops (once before the // return/break, and once when the exception is caught). if (!bb.isExitBB() && i instanceof ReturnBase) { // Add before the break/return instrs.previous(); if (requireBinding) instrs.add(new PopBindingInstr()); if (requireFrame) instrs.add(new PopFrameInstr()); break; } } if (bb.isExitBB() && !bb.isEmpty()) { // Last instr could be a return -- so, move iterator one position back if (i != null && i instanceof ReturnBase) instrs.previous(); if (requireBinding) instrs.add(new PopBindingInstr()); if (requireFrame) instrs.add(new PopFrameInstr()); } if (bb == geb) { // Add before throw-exception-instr which would be the last instr if (i != null) { // Assumption: Last instr should always be a control-transfer instruction assert i.getOperation().transfersControl() : "Last instruction of GEB in scope: " + scope + " is " + i + ", not a control-xfer instruction"; instrs.previous(); } if (requireBinding) instrs.add(new PopBindingInstr()); if (requireFrame) instrs.add(new PopFrameInstr()); } } } // This scope has an explicit call protocol flag now scope.setExplicitCallProtocolFlag(); } // FIXME: Useless for now // Run on all nested closures. for (IRClosure c : scope.getClosures()) run(c, false, true); // LVA information is no longer valid after the pass // FIXME: Grrr ... this seems broken to have to create a new object to invalidate (new LiveVariableAnalysis()).invalidate(scope); return null; }