/** Recursively traverse all blocks from 'block' until block from 'exits' */ private BlockNode traverse(IRegion r, BlockNode block, RegionStack stack) { BlockNode next = null; boolean processed = false; List<LoopInfo> loops = block.getAll(AType.LOOP); int loopCount = loops.size(); if (loopCount != 0 && block.contains(AFlag.LOOP_START)) { if (loopCount == 1) { next = processLoop(r, loops.get(0), stack); processed = true; } else { for (LoopInfo loop : loops) { if (loop.getStart() == block) { next = processLoop(r, loop, stack); processed = true; break; } } } } if (!processed && block.getInstructions().size() == 1) { InsnNode insn = block.getInstructions().get(0); switch (insn.getType()) { case IF: next = processIf(r, block, (IfNode) insn, stack); processed = true; break; case SWITCH: next = processSwitch(r, block, (SwitchNode) insn, stack); processed = true; break; case MONITOR_ENTER: next = processMonitorEnter(r, block, insn, stack); processed = true; break; default: break; } } if (!processed) { r.getSubBlocks().add(block); next = getNextBlock(block); } if (next != null && !stack.containsExit(block) && !stack.containsExit(next)) { return next; } return null; }
private static void insertContinue(LoopInfo loop) { BlockNode loopEnd = loop.getEnd(); List<BlockNode> predecessors = loopEnd.getPredecessors(); if (predecessors.size() <= 1) { return; } Set<BlockNode> loopExitNodes = loop.getExitNodes(); for (BlockNode pred : predecessors) { if (canInsertContinue(pred, predecessors, loopEnd, loopExitNodes)) { InsnNode cont = new InsnNode(InsnType.CONTINUE, 0); pred.getInstructions().add(cont); } } }
private void addBreakLabel(Edge exitEdge, BlockNode exit, InsnNode breakInsn) { BlockNode outBlock = BlockUtils.getNextBlock(exitEdge.getTarget()); if (outBlock == null) { return; } List<LoopInfo> exitLoop = mth.getAllLoopsForBlock(outBlock); if (!exitLoop.isEmpty()) { return; } List<LoopInfo> inLoops = mth.getAllLoopsForBlock(exitEdge.getSource()); if (inLoops.size() < 2) { return; } // search for parent loop LoopInfo parentLoop = null; for (LoopInfo loop : inLoops) { if (loop.getParentLoop() == null) { parentLoop = loop; break; } } if (parentLoop == null) { return; } if (parentLoop.getEnd() != exit && !parentLoop.getExitNodes().contains(exit)) { LoopLabelAttr labelAttr = new LoopLabelAttr(parentLoop); breakInsn.addAttr(labelAttr); parentLoop.getStart().addAttr(labelAttr); } }
private BlockNode makeEndlessLoop( IRegion curRegion, RegionStack stack, LoopInfo loop, BlockNode loopStart) { LoopRegion loopRegion = new LoopRegion(curRegion, loop, null, false); curRegion.getSubBlocks().add(loopRegion); loopStart.remove(AType.LOOP); stack.push(loopRegion); BlockNode loopExit = null; // insert 'break' for exits List<Edge> exitEdges = loop.getExitEdges(); for (Edge exitEdge : exitEdges) { BlockNode exit = exitEdge.getTarget(); if (insertBreak(stack, exit, exitEdge)) { BlockNode nextBlock = getNextBlock(exit); if (nextBlock != null) { stack.addExit(nextBlock); loopExit = nextBlock; } } } Region body = makeRegion(loopStart, stack); BlockNode loopEnd = loop.getEnd(); if (!RegionUtils.isRegionContainsBlock(body, loopEnd) && !loopEnd.contains(AType.EXC_HANDLER) && !inExceptionHandlerBlocks(loopEnd)) { body.getSubBlocks().add(loopEnd); } loopRegion.setBody(body); if (loopExit == null) { BlockNode next = getNextBlock(loopEnd); loopExit = RegionUtils.isRegionContainsBlock(body, next) ? null : next; } stack.pop(); loopStart.addAttr(AType.LOOP, loop); return loopExit; }
/** Select loop exit and construct LoopRegion */ private LoopRegion makeLoopRegion(IRegion curRegion, LoopInfo loop, List<BlockNode> exitBlocks) { for (BlockNode block : exitBlocks) { if (block.contains(AType.EXC_HANDLER) || block.getInstructions().size() != 1 || block.getInstructions().get(0).getType() != InsnType.IF) { continue; } List<LoopInfo> loops = block.getAll(AType.LOOP); if (!loops.isEmpty() && loops.get(0) != loop) { // skip nested loop condition continue; } LoopRegion loopRegion = new LoopRegion(curRegion, loop, block, block == loop.getEnd()); boolean found; if (block == loop.getStart() || block == loop.getEnd() || BlockUtils.isEmptySimplePath(loop.getStart(), block)) { found = true; } else if (block.getPredecessors().contains(loop.getStart())) { loopRegion.setPreCondition(loop.getStart()); // if we can't merge pre-condition this is not correct header found = loopRegion.checkPreCondition(); } else { found = false; } if (found) { List<LoopInfo> list = mth.getAllLoopsForBlock(block); if (list.size() >= 2) { // bad condition if successors going out of all loops boolean allOuter = true; for (BlockNode outerBlock : block.getCleanSuccessors()) { List<LoopInfo> outLoopList = mth.getAllLoopsForBlock(outerBlock); outLoopList.remove(loop); if (!outLoopList.isEmpty()) { // goes to outer loop allOuter = false; break; } } if (allOuter) { found = false; } } } if (found) { return loopRegion; } } // no exit found => endless loop return null; }
private BlockNode processSwitch( IRegion currentRegion, BlockNode block, SwitchNode insn, RegionStack stack) { SwitchRegion sw = new SwitchRegion(currentRegion, block); currentRegion.getSubBlocks().add(sw); int len = insn.getTargets().length; // sort by target Map<Integer, List<Object>> casesMap = new LinkedHashMap<Integer, List<Object>>(len); for (int i = 0; i < len; i++) { Object key = insn.getKeys()[i]; int targ = insn.getTargets()[i]; List<Object> keys = casesMap.get(targ); if (keys == null) { keys = new ArrayList<Object>(2); casesMap.put(targ, keys); } keys.add(key); } Map<BlockNode, List<Object>> blocksMap = new LinkedHashMap<BlockNode, List<Object>>(len); for (Map.Entry<Integer, List<Object>> entry : casesMap.entrySet()) { BlockNode c = getBlockByOffset(entry.getKey(), block.getSuccessors()); assert c != null; blocksMap.put(c, entry.getValue()); } BlockNode defCase = getBlockByOffset(insn.getDefaultCaseOffset(), block.getSuccessors()); if (defCase != null) { blocksMap.remove(defCase); } LoopInfo loop = mth.getLoopForBlock(block); Map<BlockNode, BlockNode> fallThroughCases = new LinkedHashMap<BlockNode, BlockNode>(); List<BlockNode> basicBlocks = mth.getBasicBlocks(); BitSet outs = new BitSet(basicBlocks.size()); outs.or(block.getDomFrontier()); for (BlockNode s : block.getCleanSuccessors()) { BitSet df = s.getDomFrontier(); // fall through case block if (df.cardinality() > 1) { if (df.cardinality() > 2) { LOG.debug("Unexpected case pattern, block: {}, mth: {}", s, mth); } else { BlockNode first = basicBlocks.get(df.nextSetBit(0)); BlockNode second = basicBlocks.get(df.nextSetBit(first.getId() + 1)); if (second.getDomFrontier().get(first.getId())) { fallThroughCases.put(s, second); df = new BitSet(df.size()); df.set(first.getId()); } else if (first.getDomFrontier().get(second.getId())) { fallThroughCases.put(s, first); df = new BitSet(df.size()); df.set(second.getId()); } } } outs.or(df); } outs.clear(block.getId()); if (loop != null) { outs.clear(loop.getStart().getId()); } stack.push(sw); stack.addExits(BlockUtils.bitSetToBlocks(mth, outs)); // check cases order if fall through case exists if (!fallThroughCases.isEmpty()) { if (isBadCasesOrder(blocksMap, fallThroughCases)) { LOG.debug("Fixing incorrect switch cases order, method: {}", mth); blocksMap = reOrderSwitchCases(blocksMap, fallThroughCases); if (isBadCasesOrder(blocksMap, fallThroughCases)) { LOG.error("Can't fix incorrect switch cases order, method: {}", mth); mth.add(AFlag.INCONSISTENT_CODE); } } } // filter 'out' block if (outs.cardinality() > 1) { // remove exception handlers BlockUtils.cleanBitSet(mth, outs); } if (outs.cardinality() > 1) { // filter loop start and successors of other blocks for (int i = outs.nextSetBit(0); i >= 0; i = outs.nextSetBit(i + 1)) { BlockNode b = basicBlocks.get(i); outs.andNot(b.getDomFrontier()); if (b.contains(AFlag.LOOP_START)) { outs.clear(b.getId()); } else { for (BlockNode s : b.getCleanSuccessors()) { outs.clear(s.getId()); } } } } if (loop != null && outs.cardinality() > 1) { outs.clear(loop.getEnd().getId()); } if (outs.cardinality() == 0) { // one or several case blocks are empty, // run expensive algorithm for find 'out' block for (BlockNode maybeOut : block.getSuccessors()) { boolean allReached = true; for (BlockNode s : block.getSuccessors()) { if (!isPathExists(s, maybeOut)) { allReached = false; break; } } if (allReached) { outs.set(maybeOut.getId()); break; } } } BlockNode out = null; if (outs.cardinality() == 1) { out = basicBlocks.get(outs.nextSetBit(0)); stack.addExit(out); } else if (loop == null && outs.cardinality() > 1) { LOG.warn("Can't detect out node for switch block: {} in {}", block, mth); } if (loop != null) { // check if 'continue' must be inserted BlockNode end = loop.getEnd(); if (out != end && out != null) { insertContinueInSwitch(block, out, end); } } if (!stack.containsExit(defCase)) { sw.setDefaultCase(makeRegion(defCase, stack)); } for (Entry<BlockNode, List<Object>> entry : blocksMap.entrySet()) { BlockNode caseBlock = entry.getKey(); if (stack.containsExit(caseBlock)) { // empty case block sw.addCase(entry.getValue(), new Region(stack.peekRegion())); } else { BlockNode next = fallThroughCases.get(caseBlock); stack.addExit(next); Region caseRegion = makeRegion(caseBlock, stack); stack.removeExit(next); if (next != null) { next.add(AFlag.FALL_THROUGH); caseRegion.add(AFlag.FALL_THROUGH); } sw.addCase(entry.getValue(), caseRegion); // 'break' instruction will be inserted in RegionMakerVisitor.PostRegionVisitor } } stack.pop(); return out; }
private BlockNode processLoop(IRegion curRegion, LoopInfo loop, RegionStack stack) { BlockNode loopStart = loop.getStart(); Set<BlockNode> exitBlocksSet = loop.getExitNodes(); // set exit blocks scan order priority // this can help if loop have several exits (after using 'break' or 'return' in loop) List<BlockNode> exitBlocks = new ArrayList<BlockNode>(exitBlocksSet.size()); BlockNode nextStart = getNextBlock(loopStart); if (nextStart != null && exitBlocksSet.remove(nextStart)) { exitBlocks.add(nextStart); } if (exitBlocksSet.remove(loopStart)) { exitBlocks.add(loopStart); } if (exitBlocksSet.remove(loop.getEnd())) { exitBlocks.add(loop.getEnd()); } exitBlocks.addAll(exitBlocksSet); LoopRegion loopRegion = makeLoopRegion(curRegion, loop, exitBlocks); if (loopRegion == null) { BlockNode exit = makeEndlessLoop(curRegion, stack, loop, loopStart); insertContinue(loop); return exit; } curRegion.getSubBlocks().add(loopRegion); IRegion outerRegion = stack.peekRegion(); stack.push(loopRegion); IfInfo condInfo = makeIfInfo(loopRegion.getHeader()); condInfo = searchNestedIf(condInfo); confirmMerge(condInfo); if (!loop.getLoopBlocks().contains(condInfo.getThenBlock())) { // invert loop condition if 'then' points to exit condInfo = IfInfo.invert(condInfo); } loopRegion.setCondition(condInfo.getCondition()); exitBlocks.removeAll(condInfo.getMergedBlocks()); if (!exitBlocks.isEmpty()) { BlockNode loopExit = condInfo.getElseBlock(); if (loopExit != null) { // add 'break' instruction before path cross between main loop exit and sub-exit for (Edge exitEdge : loop.getExitEdges()) { if (!exitBlocks.contains(exitEdge.getSource())) { continue; } insertBreak(stack, loopExit, exitEdge); } } } BlockNode out; if (loopRegion.isConditionAtEnd()) { BlockNode thenBlock = condInfo.getThenBlock(); out = thenBlock == loopStart ? condInfo.getElseBlock() : thenBlock; loopStart.remove(AType.LOOP); loop.getEnd().add(AFlag.SKIP); stack.addExit(loop.getEnd()); loopRegion.setBody(makeRegion(loopStart, stack)); loopStart.addAttr(AType.LOOP, loop); loop.getEnd().remove(AFlag.SKIP); } else { out = condInfo.getElseBlock(); if (outerRegion != null && out.contains(AFlag.LOOP_START) && !out.getAll(AType.LOOP).contains(loop) && RegionUtils.isRegionContainsBlock(outerRegion, out)) { // exit to already processed outer loop out = null; } stack.addExit(out); BlockNode loopBody = condInfo.getThenBlock(); Region body = makeRegion(loopBody, stack); // add blocks from loop start to first condition block BlockNode conditionBlock = condInfo.getIfBlock(); if (loopStart != conditionBlock) { Set<BlockNode> blocks = BlockUtils.getAllPathsBlocks(loopStart, conditionBlock); blocks.remove(conditionBlock); for (BlockNode block : blocks) { if (block.getInstructions().isEmpty() && !block.contains(AFlag.SKIP) && !RegionUtils.isRegionContainsBlock(body, block)) { body.add(block); } } } loopRegion.setBody(body); } stack.pop(); insertContinue(loop); return out; }