System.out.println("source instrs:" + scope.getCFG().toStringInstrs());
*/
// Host method data init
InlinerInfo ii = new InlinerInfo(call, cfg);
Label splitBBLabel = hostScope.getNewLabel();
BasicBlock splitBB;
// Inlinee method data init
CFG methodCFG = scope.getCFG();
BasicBlock mEntry = methodCFG.getEntryBB();
BasicBlock mExit = methodCFG.getExitBB();
List<BasicBlock> methodBBs = new ArrayList<BasicBlock>();
for (BasicBlock b: methodCFG.getBasicBlocks()) methodBBs.add(b);
// Check if we are inlining a recursive method
if (hostScope.getNearestMethod() == scope) {
// 1. clone self
// SSS: FIXME: We need a clone-graph api method in cfg and graph
CFG selfClone = cloneSelf(ii);
// 2. add callee bbs and their edges
// SSS: FIXME: We need a swallow-graph api method in cfg and graph
for (BasicBlock b : selfClone.getBasicBlocks()) {
cfg.addBasicBlock(b);
for (Edge<BasicBlock> e : selfClone.getOutgoingEdges(b)) {
cfg.addEdge(b, e.getDestination().getData(), e.getType());
}
}
} else {
// 2. clone callee and add it to the host cfg
for (BasicBlock b : methodCFG.getBasicBlocks()) {
if (b != mEntry && b != mExit) {
cfg.addBasicBlock(b.cloneForInlinedMethod(ii));
}
}
for (BasicBlock x : methodCFG.getBasicBlocks()) {
if (x != mEntry && x != mExit) {
BasicBlock rx = ii.getRenamedBB(x);
for (Edge<BasicBlock> e : methodCFG.getOutgoingEdges(x)) {
BasicBlock b = e.getDestination().getData();
if (b != mExit) cfg.addEdge(rx, ii.getRenamedBB(b), e.getType());
}
}
}
}
// 3. split callsite bb, move outbound edges from callsite bb to split bb, and unhook call bb
splitBB = callBB.splitAtInstruction(call, splitBBLabel, false);
cfg.addBasicBlock(splitBB);
for (Edge<BasicBlock> e : cfg.getOutgoingEdges(callBB)) {
cfg.addEdge(splitBB, e.getDestination().getData(), e.getType());
}
cfg.removeAllOutgoingEdgesForBB(callBB);
// 4a. Hook up entry edges
assert methodCFG.outDegree(mEntry) == 2: "Entry BB of inlinee method does not have outdegree 2: " + methodCFG.toStringGraph();
for (Edge<BasicBlock> e : methodCFG.getOutgoingEdges(mEntry)) {
BasicBlock destination = e.getDestination().getData();
if (destination != mExit) {
BasicBlock dstBB = ii.getRenamedBB(destination);
if (!ii.canMapArgsStatically()) {
dstBB.addInstr(new ToAryInstr((Variable)ii.getArgs(), new Array(call.getCallArgs()), cfg.getScope().getManager().getTrue()));
}
cfg.addEdge(callBB, dstBB, CFG.EdgeType.FALL_THROUGH);
}
}
// 4b. Hook up exit edges
for (Edge<BasicBlock> e : methodCFG.getIncomingEdges(mExit)) {
BasicBlock source = e.getSource().getData();
if (source != mEntry) {
BasicBlock clonedSource = ii.getRenamedBB(source);
if (e.getType() == EdgeType.EXCEPTION) {
// e._src has an explicit throw that returns from the callee
// after inlining, if the caller instruction has a rescuer, then the
// throw has to be captured by the rescuer as well.
BasicBlock rescuerOfSplitBB = cfg.getRescuerBBFor(splitBB);
if (rescuerOfSplitBB != null) {
cfg.addEdge(clonedSource, rescuerOfSplitBB, EdgeType.EXCEPTION);
} else {
cfg.addEdge(clonedSource, cfg.getExitBB(), EdgeType.EXIT);
}
} else {
cfg.addEdge(clonedSource, splitBB, e.getType());
}
}
}
// SSS FIXME: Are these used anywhere post-CFG building?
// 5. Clone exception regions
List<ExceptionRegion> exceptionRegions = cfg.getOutermostExceptionRegions();
for (ExceptionRegion r : methodCFG.getOutermostExceptionRegions()) {
exceptionRegions.add(r.cloneForInlining(ii));
}
// 6. Update bb rescuer map
// 6a. splitBB will be protected by the same bb as callBB
BasicBlock callBBrescuer = cfg.getRescuerBBFor(callBB);
if (callBBrescuer != null) cfg.setRescuerBB(splitBB, callBBrescuer);
BasicBlock callBBensurer = cfg.getEnsurerBBFor(callBB);
if (callBBensurer != null) cfg.setEnsurerBB(splitBB, callBBensurer);
// 6b. remap existing protections for bbs in mcfg to their renamed bbs.
// 6c. bbs in mcfg that aren't protected by an existing bb will be protected by callBBrescuer.
for (BasicBlock x : methodBBs) {
if (x != mEntry && x != mExit) {
BasicBlock xRenamed = ii.getRenamedBB(x);
BasicBlock xProtector = methodCFG.getRescuerBBFor(x);
if (xProtector != null) {
cfg.setRescuerBB(xRenamed, ii.getRenamedBB(xProtector));
} else if (callBBrescuer != null) {
cfg.setRescuerBB(xRenamed, callBBrescuer);
}
BasicBlock xEnsurer = methodCFG.getEnsurerBBFor(x);
if (xEnsurer != null) {
cfg.setEnsurerBB(xRenamed, ii.getRenamedBB(xEnsurer));
} else if (callBBensurer != null) {
cfg.setEnsurerBB(xRenamed, callBBensurer);
}
}
}
// 7. Add inline guard that verifies that the method inlined is the same
// that gets called in future invocations. In addition to the guard, add
// a failure path code.
Label failurePathLabel = hostScope.getNewLabel();
callBB.addInstr(new ModuleVersionGuardInstr(implClass, classToken, call.getReceiver(), failurePathLabel));
BasicBlock failurePathBB = new BasicBlock(cfg, failurePathLabel);
cfg.addBasicBlock(failurePathBB);
failurePathBB.addInstr(call);