public IRClosure(IRScope lexicalParent, StaticScope staticScope, Arity arity, int argumentType) { super(lexicalParent, MetaObject.create(lexicalParent), null, staticScope); startLabel = getNewLabel("_CLOSURE_START"); endLabel = getNewLabel("_CLOSURE_END"); closureId = lexicalParent.getNextClosureId(); setName("_CLOSURE_" + closureId); this.body = new InterpretedIRBlockBody(this, arity, argumentType); }
/** Build the Control Flow Graph */ public DirectedGraph<BasicBlock> build(List<Instr> instrs) { // Map of label & basic blocks which are waiting for a bb with that label Map<Label, List<BasicBlock>> forwardRefs = new HashMap<Label, List<BasicBlock>>(); // Map of return address variable and all possible targets (required to connect up ensure blocks // with their targets) Map<Variable, Set<Label>> retAddrMap = new HashMap<Variable, Set<Label>>(); Map<Variable, BasicBlock> retAddrTargetMap = new HashMap<Variable, BasicBlock>(); // List of bbs that have a 'return' instruction List<BasicBlock> returnBBs = new ArrayList<BasicBlock>(); // List of bbs that have a 'throw' instruction List<BasicBlock> exceptionBBs = new ArrayList<BasicBlock>(); // Stack of nested rescue regions Stack<ExceptionRegion> nestedExceptionRegions = new Stack<ExceptionRegion>(); // List of all rescued regions List<ExceptionRegion> allExceptionRegions = new ArrayList<ExceptionRegion>(); // Dummy entry basic block (see note at end to see why) entryBB = createBB(nestedExceptionRegions); // First real bb BasicBlock firstBB = createBB(nestedExceptionRegions); // Build the rest! BasicBlock currBB = firstBB; BasicBlock newBB = null; boolean bbEnded = false; boolean nextBBIsFallThrough = true; for (Instr i : instrs) { Operation iop = i.getOperation(); if (iop == Operation.LABEL) { Label l = ((LabelInstr) i).label; newBB = createBB(l, nestedExceptionRegions); // Jump instruction bbs dont add an edge to the succeeding bb by default if (nextBBIsFallThrough) graph.addEdge(currBB, newBB, EdgeType.FALL_THROUGH); currBB = newBB; bbEnded = false; nextBBIsFallThrough = true; // Add forward reference edges List<BasicBlock> frefs = forwardRefs.get(l); if (frefs != null) { for (BasicBlock b : frefs) { graph.addEdge(b, newBB, EdgeType.REGULAR); } } } else if (bbEnded && (iop != Operation.EXC_REGION_END)) { newBB = createBB(nestedExceptionRegions); // Jump instruction bbs dont add an edge to the succeeding bb by default if (nextBBIsFallThrough) graph.addEdge(currBB, newBB, EdgeType.FALL_THROUGH); // currBB cannot be null! currBB = newBB; bbEnded = false; nextBBIsFallThrough = true; } if (i instanceof ExceptionRegionStartMarkerInstr) { // SSS: Do we need this anymore? // currBB.addInstr(i); ExceptionRegionStartMarkerInstr ersmi = (ExceptionRegionStartMarkerInstr) i; ExceptionRegion rr = new ExceptionRegion(ersmi.firstRescueBlockLabel, ersmi.ensureBlockLabel); rr.addBB(currBB); allExceptionRegions.add(rr); if (nestedExceptionRegions.empty()) { outermostERs.add(rr); } else { nestedExceptionRegions.peek().addNestedRegion(rr); } nestedExceptionRegions.push(rr); } else if (i instanceof ExceptionRegionEndMarkerInstr) { // SSS: Do we need this anymore? // currBB.addInstr(i); nestedExceptionRegions.pop().setEndBB(currBB); } else if (iop.endsBasicBlock()) { bbEnded = true; currBB.addInstr(i); Label tgt; nextBBIsFallThrough = false; if (i instanceof BranchInstr) { tgt = ((BranchInstr) i).getJumpTarget(); nextBBIsFallThrough = true; } else if (i instanceof JumpInstr) { tgt = ((JumpInstr) i).getJumpTarget(); } else if (iop.isReturn()) { // BREAK, RETURN, CLOSURE_RETURN tgt = null; returnBBs.add(currBB); } else if (i instanceof ThrowExceptionInstr) { tgt = null; exceptionBBs.add(currBB); } else if (i instanceof JumpIndirectInstr) { tgt = null; Set<Label> retAddrs = retAddrMap.get(((JumpIndirectInstr) i).getJumpTarget()); for (Label l : retAddrs) { addEdge(currBB, l, forwardRefs); } // Record the target bb for the retaddr var for any set_addr instrs that appear later and // use the same retaddr var retAddrTargetMap.put(((JumpIndirectInstr) i).getJumpTarget(), currBB); } else { throw new RuntimeException( "Unhandled case in CFG builder for basic block ending instr: " + i); } if (tgt != null) addEdge(currBB, tgt, forwardRefs); } else if (iop != Operation.LABEL) { currBB.addInstr(i); } if (i instanceof SetReturnAddressInstr) { Variable v = i.getResult(); Label tgtLbl = ((SetReturnAddressInstr) i).getReturnAddr(); BasicBlock tgtBB = retAddrTargetMap.get(v); // If we have the target bb, add the edge // If not, record it for fixup later if (tgtBB != null) { addEdge(tgtBB, tgtLbl, forwardRefs); } else { Set<Label> addrs = retAddrMap.get(v); if (addrs == null) { addrs = new HashSet<Label>(); retAddrMap.put(v, addrs); } addrs.add(tgtLbl); } } else if (i instanceof CallInstr) { // Build CFG for the closure if there exists one Operand closureArg = ((CallInstr) i).getClosureArg(); if (closureArg instanceof MetaObject) { ((IRClosure) ((MetaObject) closureArg).scope).buildCFG(); } } } // Process all rescued regions for (ExceptionRegion rr : allExceptionRegions) { BasicBlock firstRescueBB = bbMap.get(rr.getFirstRescueBlockLabel()); // 1. Tell the region that firstRescueBB is its protector! rr.setFirstRescueBB(firstRescueBB); // 2. Record a mapping from the region's exclusive basic blocks to the first bb that will // start exception handling for all their exceptions. // 3. Add an exception edge from every exclusive bb of the region to firstRescueBB BasicBlock ensureBlockBB = rr.getEnsureBlockLabel() == null ? null : bbMap.get(rr.getEnsureBlockLabel()); for (BasicBlock b : rr.getExclusiveBBs()) { rescuerMap.put(b, firstRescueBB); graph.addEdge(b, firstRescueBB, EdgeType.EXCEPTION); if (ensureBlockBB != null) { ensurerMap.put(b, ensureBlockBB); // SSS FIXME: This is a conservative edge because when a rescue block is present // that catches an exception, control never reaches the ensure block directly. // Only when we get an error or threadkill even, or when breaks propagate upward // do we need to hit an ensure directly. This edge is present to account for that // control-flow scneario. graph.addEdge(b, ensureBlockBB, EdgeType.EXCEPTION); } } } buildExitBasicBlock( nestedExceptionRegions, firstBB, returnBBs, exceptionBBs, nextBBIsFallThrough, currBB, entryBB); optimize(); // remove useless cfg edges & orphaned bbs return graph; }