|
|
|
@@ -45,6 +45,10 @@ public class BlockProcessor extends AbstractVisitor {
|
|
|
|
|
|
|
|
|
|
private static void processBlocksTree(MethodNode mth) {
|
|
|
|
|
computeDominators(mth);
|
|
|
|
|
if (independentBlockTreeMod(mth)) {
|
|
|
|
|
clearBlocksState(mth);
|
|
|
|
|
computeDominators(mth);
|
|
|
|
|
}
|
|
|
|
|
markReturnBlocks(mth);
|
|
|
|
|
|
|
|
|
|
int i = 0;
|
|
|
|
@@ -64,6 +68,109 @@ public class BlockProcessor extends AbstractVisitor {
|
|
|
|
|
processNestedLoops(mth);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static boolean removeEmptyBlock(MethodNode mth, BlockNode block) {
|
|
|
|
|
if (block.getInstructions().isEmpty()
|
|
|
|
|
&& !block.isSynthetic()
|
|
|
|
|
&& block.isAttrStorageEmpty()
|
|
|
|
|
&& block.getSuccessors().size() <= 1) {
|
|
|
|
|
LOG.debug("Removing empty block: {}", block);
|
|
|
|
|
if (block.getSuccessors().size() == 1) {
|
|
|
|
|
BlockNode successor = block.getSuccessors().get(0);
|
|
|
|
|
block.getPredecessors().forEach(pred -> {
|
|
|
|
|
pred.getSuccessors().remove(block);
|
|
|
|
|
BlockSplitter.connect(pred, successor);
|
|
|
|
|
BlockSplitter.replaceTarget(pred, block, successor);
|
|
|
|
|
pred.updateCleanSuccessors();
|
|
|
|
|
});
|
|
|
|
|
BlockSplitter.removeConnection(block, successor);
|
|
|
|
|
} else {
|
|
|
|
|
block.getPredecessors().forEach(pred -> {
|
|
|
|
|
pred.getSuccessors().remove(block);
|
|
|
|
|
pred.updateCleanSuccessors();
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
block.add(AFlag.REMOVE);
|
|
|
|
|
block.getSuccessors().clear();
|
|
|
|
|
block.getPredecessors().clear();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static boolean deduplicateBlockInsns(BlockNode block) {
|
|
|
|
|
if (block.contains(AFlag.LOOP_START) || block.contains(AFlag.LOOP_END)) {
|
|
|
|
|
// search for same instruction at end of all predecessors blocks
|
|
|
|
|
List<BlockNode> predecessors = block.getPredecessors();
|
|
|
|
|
int predsCount = predecessors.size();
|
|
|
|
|
if (predsCount > 1) {
|
|
|
|
|
InsnNode lastInsn = BlockUtils.getLastInsn(block);
|
|
|
|
|
if (lastInsn != null && lastInsn.getType() == InsnType.IF) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
int sameInsnCount = getSameLastInsnCount(predecessors);
|
|
|
|
|
if (sameInsnCount > 0) {
|
|
|
|
|
List<InsnNode> insns = getLastInsns(predecessors.get(0), sameInsnCount);
|
|
|
|
|
insertAtStart(block, insns);
|
|
|
|
|
predecessors.forEach(pred -> getLastInsns(pred, sameInsnCount).clear());
|
|
|
|
|
LOG.debug("Move duplicate insns, count: {} to block {}", sameInsnCount, block);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static List<InsnNode> getLastInsns(BlockNode blockNode, int sameInsnCount) {
|
|
|
|
|
List<InsnNode> instructions = blockNode.getInstructions();
|
|
|
|
|
int size = instructions.size();
|
|
|
|
|
return instructions.subList(size - sameInsnCount, size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void insertAtStart(BlockNode block, List<InsnNode> insns) {
|
|
|
|
|
List<InsnNode> blockInsns = block.getInstructions();
|
|
|
|
|
|
|
|
|
|
List<InsnNode> newInsnList = new ArrayList<>(insns.size() + blockInsns.size());
|
|
|
|
|
newInsnList.addAll(insns);
|
|
|
|
|
newInsnList.addAll(blockInsns);
|
|
|
|
|
|
|
|
|
|
blockInsns.clear();
|
|
|
|
|
blockInsns.addAll(newInsnList);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static int getSameLastInsnCount(List<BlockNode> predecessors) {
|
|
|
|
|
int sameInsnCount = 0;
|
|
|
|
|
while (true) {
|
|
|
|
|
InsnNode insn = null;
|
|
|
|
|
for (BlockNode pred : predecessors) {
|
|
|
|
|
InsnNode curInsn = getInsnsFromEnd(pred, sameInsnCount);
|
|
|
|
|
if (curInsn == null) {
|
|
|
|
|
return sameInsnCount;
|
|
|
|
|
}
|
|
|
|
|
if (insn == null) {
|
|
|
|
|
insn = curInsn;
|
|
|
|
|
} else {
|
|
|
|
|
if (!isSame(insn, curInsn)) {
|
|
|
|
|
return sameInsnCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sameInsnCount++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static boolean isSame(InsnNode insn, InsnNode curInsn) {
|
|
|
|
|
return /*insn.getType() == InsnType.MOVE &&*/ insn.isDeepEquals(curInsn) && insn.canReorder();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static InsnNode getInsnsFromEnd(BlockNode block, int number) {
|
|
|
|
|
List<InsnNode> instructions = block.getInstructions();
|
|
|
|
|
int insnCount = instructions.size();
|
|
|
|
|
if (insnCount <= number) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
return instructions.get(insnCount - number - 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void computeDominators(MethodNode mth) {
|
|
|
|
|
List<BlockNode> basicBlocks = mth.getBasicBlocks();
|
|
|
|
|
int nBlocks = basicBlocks.size();
|
|
|
|
@@ -104,9 +211,7 @@ public class BlockProcessor extends AbstractVisitor {
|
|
|
|
|
markLoops(mth);
|
|
|
|
|
|
|
|
|
|
// clear self dominance
|
|
|
|
|
for (BlockNode block : basicBlocks) {
|
|
|
|
|
block.getDoms().clear(block.getId());
|
|
|
|
|
}
|
|
|
|
|
basicBlocks.forEach(block -> block.getDoms().clear(block.getId()));
|
|
|
|
|
|
|
|
|
|
// calculate immediate dominators
|
|
|
|
|
for (BlockNode block : basicBlocks) {
|
|
|
|
@@ -148,9 +253,7 @@ public class BlockProcessor extends AbstractVisitor {
|
|
|
|
|
if (block.getDomFrontier() != null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
for (BlockNode c : block.getDominatesOn()) {
|
|
|
|
|
computeBlockDF(mth, c);
|
|
|
|
|
}
|
|
|
|
|
block.getDominatesOn().forEach(c -> computeBlockDF(mth, c));
|
|
|
|
|
List<BlockNode> blocks = mth.getBasicBlocks();
|
|
|
|
|
BitSet domFrontier = null;
|
|
|
|
|
for (BlockNode s : block.getSuccessors()) {
|
|
|
|
@@ -180,39 +283,37 @@ public class BlockProcessor extends AbstractVisitor {
|
|
|
|
|
|
|
|
|
|
private static void markReturnBlocks(MethodNode mth) {
|
|
|
|
|
mth.getExitBlocks().clear();
|
|
|
|
|
for (BlockNode block : mth.getBasicBlocks()) {
|
|
|
|
|
mth.getBasicBlocks().forEach(block -> {
|
|
|
|
|
if (BlockUtils.checkLastInsnType(block, InsnType.RETURN)) {
|
|
|
|
|
block.add(AFlag.RETURN);
|
|
|
|
|
mth.getExitBlocks().add(block);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void markLoops(MethodNode mth) {
|
|
|
|
|
for (BlockNode block : mth.getBasicBlocks()) {
|
|
|
|
|
for (BlockNode succ : block.getSuccessors()) {
|
|
|
|
|
// Every successor that dominates its predecessor is a header of a loop,
|
|
|
|
|
// block -> succ is a back edge.
|
|
|
|
|
if (block.getDoms().get(succ.getId())) {
|
|
|
|
|
succ.add(AFlag.LOOP_START);
|
|
|
|
|
mth.getBasicBlocks().forEach(block -> {
|
|
|
|
|
// Every successor that dominates its predecessor is a header of a loop,
|
|
|
|
|
// block -> succ is a back edge.
|
|
|
|
|
block.getSuccessors().forEach(successor -> {
|
|
|
|
|
if (block.getDoms().get(successor.getId())) {
|
|
|
|
|
successor.add(AFlag.LOOP_START);
|
|
|
|
|
block.add(AFlag.LOOP_END);
|
|
|
|
|
|
|
|
|
|
LoopInfo loop = new LoopInfo(succ, block);
|
|
|
|
|
succ.addAttr(AType.LOOP, loop);
|
|
|
|
|
LoopInfo loop = new LoopInfo(successor, block);
|
|
|
|
|
successor.addAttr(AType.LOOP, loop);
|
|
|
|
|
block.addAttr(AType.LOOP, loop);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void registerLoops(MethodNode mth) {
|
|
|
|
|
for (BlockNode block : mth.getBasicBlocks()) {
|
|
|
|
|
mth.getBasicBlocks().forEach(block -> {
|
|
|
|
|
if (block.contains(AFlag.LOOP_START)) {
|
|
|
|
|
for (LoopInfo loop : block.getAll(AType.LOOP)) {
|
|
|
|
|
mth.registerLoop(loop);
|
|
|
|
|
}
|
|
|
|
|
block.getAll(AType.LOOP).forEach(mth::registerLoop);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static void processNestedLoops(MethodNode mth) {
|
|
|
|
@@ -242,10 +343,14 @@ public class BlockProcessor extends AbstractVisitor {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static boolean modifyBlocksTree(MethodNode mth) {
|
|
|
|
|
for (BlockNode block : mth.getBasicBlocks()) {
|
|
|
|
|
List<BlockNode> basicBlocks = mth.getBasicBlocks();
|
|
|
|
|
for (BlockNode block : basicBlocks) {
|
|
|
|
|
if (block.getPredecessors().isEmpty() && block != mth.getEnterBlock()) {
|
|
|
|
|
throw new JadxRuntimeException("Unreachable block: " + block);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (BlockNode block : basicBlocks) {
|
|
|
|
|
if (checkLoops(mth, block)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
@@ -253,6 +358,25 @@ public class BlockProcessor extends AbstractVisitor {
|
|
|
|
|
return splitReturn(mth);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static boolean independentBlockTreeMod(MethodNode mth) {
|
|
|
|
|
List<BlockNode> basicBlocks = mth.getBasicBlocks();
|
|
|
|
|
boolean changed = false;
|
|
|
|
|
for (BlockNode basicBlock : basicBlocks) {
|
|
|
|
|
if (deduplicateBlockInsns(basicBlock)) {
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (BlockNode basicBlock : basicBlocks) {
|
|
|
|
|
if (removeEmptyBlock(mth, basicBlock)) {
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (BlockSplitter.removeEmptyDetachedBlocks(mth)) {
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
return changed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static boolean checkLoops(MethodNode mth, BlockNode block) {
|
|
|
|
|
// check loops
|
|
|
|
|
List<LoopInfo> loops = block.getAll(AType.LOOP);
|
|
|
|
@@ -303,9 +427,7 @@ public class BlockProcessor extends AbstractVisitor {
|
|
|
|
|
change = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (change) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return change;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|