@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; }
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); }