Fixes for inline fun finally block generation before lambda non-local returns

This commit is contained in:
Michael Bogdanov
2014-10-23 11:27:53 +04:00
parent 25d7c9f20a
commit dd8c3f0e49
10 changed files with 462 additions and 40 deletions
@@ -26,7 +26,11 @@ import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.codegen.AsmUtil;
import org.jetbrains.org.objectweb.asm.*;
import org.jetbrains.org.objectweb.asm.tree.*;
import org.jetbrains.org.objectweb.asm.util.Textifier;
import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
import static org.jetbrains.jet.codegen.inline.InlineCodegenUtil.*;
@@ -43,6 +47,18 @@ public class InternalFinallyBlockInliner {
startIns = inclusiveStart;
endInsExclusive = exclusiveEnd;
}
public boolean isEmpty() {
if (!(startIns instanceof LabelNode)) {
return false;
}
AbstractInsnNode end = endInsExclusive;
while (end != startIns && end instanceof LabelNode) {
end = end.getPrevious();
}
return startIns == end;
}
}
public static void processInlineFunFinallyBlocks(@NotNull MethodNode inlineFun, int lambdaTryCatchBlockNodes) {
@@ -99,7 +115,10 @@ public class InternalFinallyBlockInliner {
updateCoveringTryBlocks(coveringTryCatchBlocks, curIns);
//At this point only global return is possible, local one already substituted with: goto endLabel
if (!InlineCodegenUtil.isReturnOpcode(curIns.getOpcode()) || !InlineCodegenUtil.isMarkedReturn(curIns)) {
if (!InlineCodegenUtil.isReturnOpcode(curIns.getOpcode()) ||
!InlineCodegenUtil.isMarkedReturn(curIns) ||
coveringTryCatchBlocks.isEmpty() ||
coveringTryCatchBlocks.get(0).getOnlyCopyNotProcess()) {
curIns = curIns.getPrevious();
continue;
}
@@ -115,15 +134,26 @@ public class InternalFinallyBlockInliner {
// So we split all try blocks on current instructions to groups and process them independently
List<TryBlockCluster<TryCatchBlockNodeInfo>> clusters = InlinePackage.doClustering(coveringTryCatchBlocks);
ListIterator<TryBlockCluster<TryCatchBlockNodeInfo>> tryCatchBlockIterator = clusters.listIterator(clusters.size());
//Reverse visiting cause innermost tryCatchBlocks in the end
while (tryCatchBlockIterator.hasPrevious()) {
TryBlockCluster originalFinallyCluster = tryCatchBlockIterator.previous();
List<TryCatchBlockNodeInfo> clusterBlocks = originalFinallyCluster.getBlocks();
TryCatchBlockNodeInfo originalFinallyBlock = clusterBlocks.get(0);
FinallyBlockInfo finallyInfo = findFinallyBlockBody(originalFinallyBlock, inlineFunTryBlockInfo);
checkClusterInvariant(clusters);
//Reverse visiting cause innermost tryCatchBlocks in the end
List<TryCatchBlockNodeInfo> patched = new ArrayList<TryCatchBlockNodeInfo>();
while (tryCatchBlockIterator.hasPrevious()) {
TryBlockCluster clusterToFindFinally = tryCatchBlockIterator.previous();
List<TryCatchBlockNodeInfo> clusterBlocks = clusterToFindFinally.getBlocks();
TryCatchBlockNodeInfo originalTryCatchBlock = clusterBlocks.get(0);
FinallyBlockInfo finallyInfo = findFinallyBlockBody(originalTryCatchBlock, inlineFunTryBlockInfo);
if (finallyInfo == null) continue;
if (originalTryCatchBlock.getOnlyCopyNotProcess()) {
patched.addAll(clusterBlocks);
continue;
}
instructions.resetLabels();
List<TryCatchBlockNodePosition> tryCatchBlockInlinedInFinally = findTryCatchBlocksInlinedInFinally(finallyInfo);
@@ -138,7 +168,7 @@ public class InternalFinallyBlockInliner {
Label newFinallyEnd = new Label();
Label insertedBlockEnd = new Label();
if (nonLocalReturnType != Type.VOID_TYPE) {
if (nonLocalReturnType != Type.VOID_TYPE && !finallyInfo.isEmpty()) {
finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ISTORE), nextTempNonLocalVarIndex);
}
finallyBlockCopy.visitLabel(newFinallyStart);
@@ -175,7 +205,7 @@ public class InternalFinallyBlockInliner {
}
finallyBlockCopy.visitLabel(newFinallyEnd);
if (nonLocalReturnType != Type.VOID_TYPE) {
if (nonLocalReturnType != Type.VOID_TYPE && !finallyInfo.isEmpty()) {
finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ILOAD), nextTempNonLocalVarIndex);
nextTempNonLocalVarIndex += nonLocalReturnType.getSize(); //TODO: do more wise indexing
}
@@ -185,15 +215,44 @@ public class InternalFinallyBlockInliner {
//Copying finally body before non-local return instruction
InlineCodegenUtil.insertNodeBefore(finallyBlockCopy, inlineFun, instrInsertFinallyBefore);
nextPrev = updateExceptionTable(coveringTryCatchBlocks, nextPrev, clusterBlocks, newFinallyStart, newFinallyEnd,
tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info);
updateExceptionTable(clusterBlocks, newFinallyStart, newFinallyEnd,
tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info, patched);
}
curIns = nextPrev;
}
curIns = curIns.getPrevious();
while (curIns != null && curIns != nextPrev) {
updateCoveringTryBlocks(coveringTryCatchBlocks, curIns);
curIns = curIns.getPrevious();
}
//curIns = nextPrev;
}
inlineFun.tryCatchBlocks.clear();
for (TryCatchBlockNodeInfo info : inlineFunTryBlockInfo) {
inlineFun.tryCatchBlocks.add(info.getNode());
substitureTryBlockNodes();
}
private void checkCoveringBlocksInvariant(Stack<TryCatchBlockNodeInfo> coveringTryCatchBlocks) {
boolean isWasOnlyLocal = false;
for (TryCatchBlockNodeInfo info : coveringTryCatchBlocks) {
assert !isWasOnlyLocal || info.getOnlyCopyNotProcess();
if (info.getOnlyCopyNotProcess()) {
isWasOnlyLocal = true;
}
}
}
private void checkClusterInvariant(List<TryBlockCluster<TryCatchBlockNodeInfo>> clusters) {
boolean isWasOnlyLocal;
isWasOnlyLocal = false;
for (TryBlockCluster<TryCatchBlockNodeInfo> cluster : clusters) {
TryCatchBlockNodeInfo info = cluster.getBlocks().get(0);
assert !isWasOnlyLocal || info.getOnlyCopyNotProcess();
if (info.getOnlyCopyNotProcess()) {
isWasOnlyLocal = true;
}
}
}
@@ -209,16 +268,14 @@ public class InternalFinallyBlockInliner {
}
@Nullable
private AbstractInsnNode updateExceptionTable(
@NotNull Stack<TryCatchBlockNodeInfo> coveringTryBlocks,
@Nullable AbstractInsnNode nextPrev,
private void updateExceptionTable(
@NotNull List<TryCatchBlockNodeInfo> updatingClusterBlocks,
@NotNull Label newFinallyStart,
@NotNull Label newFinallyEnd,
@NotNull List<TryCatchBlockNodePosition> tryCatchBlockPresentInFinally,
@NotNull Set<LabelNode> labelsInsideFinally,
@NotNull LabelNode insertedBlockEnd
@NotNull LabelNode insertedBlockEnd,
@NotNull List<TryCatchBlockNodeInfo> patched
) {
//copy tryCatchFinallies that totally in finally block
@@ -272,7 +329,7 @@ public class InternalFinallyBlockInliner {
assert Objects.equal(startNode.getType(), endNode.getType()) : "Different handler types : " + startNode.getType() + " " + endNode.getType();
patchTryBlocks((LabelNode) startNode.getStartLabel().getLabel().info, endNode, false);
patchTryBlocks((LabelNode) startNode.getStartLabel().getLabel().info, endNode);
}
}
}
@@ -284,7 +341,7 @@ public class InternalFinallyBlockInliner {
//TODO rewrite to clusters
for (TryCatchBlockNodePosition endBlockPosition : singleCluster.getBlocks()) {
TryCatchBlockNodeInfo endNode = endBlockPosition.getNodeInfo();
patchTryBlocks((LabelNode) insertedBlockEnd.getLabel().info, endNode, true);
patchTryBlocks((LabelNode) insertedBlockEnd.getLabel().info, endNode);
//nextPrev = (AbstractInsnNode) insertedBlockEnd.getLabel().info;
}
@@ -293,33 +350,38 @@ public class InternalFinallyBlockInliner {
}
assert handler2Cluster.isEmpty() : "Unmatched clusters " + handler2Cluster.size();
List<TryCatchBlockNodeInfo > toProcess = new ArrayList<TryCatchBlockNodeInfo >();
toProcess.addAll(patched);
toProcess.addAll(updatingClusterBlocks);
patched.clear();
// Inserted finally shouldn't be handled by corresponding catches,
// so we should split original interval by inserted finally one
for (TryCatchBlockNodeInfo block : updatingClusterBlocks) {
for (TryCatchBlockNodeInfo block : toProcess) {
//update exception mapping
LabelNode oldStartNode = block.getNode().start;
tryBlockStarts.remove(oldStartNode, block);
block.getNode().start = (LabelNode) newFinallyEnd.info;
//tryBlockStarts.put(block.getStartLabel(), block);
tryBlockStarts.remove(oldStartNode, block);
tryBlockStarts.put(block.getStartLabel(), block);
//if (!block.getOnlyCopyNotProcess()) {
patched.add(block);
//}
TryCatchBlockNode additionalTryCatchBlock =
new TryCatchBlockNode(oldStartNode, (LabelNode) newFinallyStart.info, block.getNode().handler, block.getNode().type);
TryCatchBlockNodeInfo newInfo = new TryCatchBlockNodeInfo(additionalTryCatchBlock, false);
TryCatchBlockNodeInfo newInfo = new TryCatchBlockNodeInfo(additionalTryCatchBlock, block.getOnlyCopyNotProcess());
tryBlockStarts.put(additionalTryCatchBlock.start, newInfo);
tryBlockEnds.put(additionalTryCatchBlock.end, newInfo);
inlineFunTryBlockInfo.add(newInfo);
//TODO add assert
nextPrev = additionalTryCatchBlock.end;
coveringTryBlocks.pop();
}
sortTryCatchBlocks(inlineFunTryBlockInfo);
return nextPrev;
}
private void patchTryBlocks(@NotNull LabelNode newStartLabelNode, @NotNull TryCatchBlockNodeInfo endNode, boolean sort) {
private void patchTryBlocks(@NotNull LabelNode newStartLabelNode, @NotNull TryCatchBlockNodeInfo endNode) {
LabelNode oldStart = endNode.getStartLabel();
endNode.getNode().start = newStartLabelNode;
tryBlockStarts.remove(oldStart, endNode);
@@ -355,20 +417,18 @@ public class InternalFinallyBlockInliner {
List<TryCatchBlockNodeInfo> infos = tryBlockStarts.get((LabelNode) curIns);
for (TryCatchBlockNodeInfo startNode : infos) {
if (!startNode.getOnlyCopyNotProcess()) {
TryCatchBlockNodeInfo pop = coveringTryBlocks.pop();
//Temporary disabled cause during patched structure of exceptions changed
//assert startNode == pop : "Wrong try-catch structure " + startNode + " " + pop + " " + infos.size();
}
TryCatchBlockNodeInfo pop = coveringTryBlocks.pop();
//Temporary disabled cause during patched structure of exceptions changed
//assert startNode == pop : "Wrong try-catch structure " + startNode + " " + pop + " " + infos.size();
}
//Reversing list order cause we should pop external block before internal one
// (originally internal blocks goes before external one, such invariant preserved via sortTryCatchBlocks method)
for (TryCatchBlockNodeInfo info : Lists.reverse(tryBlockEnds.get((LabelNode) curIns))) {
if (!info.getOnlyCopyNotProcess()) {
coveringTryBlocks.add(info);
}
coveringTryBlocks.add(info);
}
checkCoveringBlocksInvariant(coveringTryBlocks);
}
private static boolean hasFinallyBlocks(List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo) {
@@ -396,6 +456,15 @@ public class InternalFinallyBlockInliner {
@NotNull TryCatchBlockNodeInfo tryCatchBlock,
@NotNull List<TryCatchBlockNodeInfo> tryCatchBlocks
) {
if (tryCatchBlock.getOnlyCopyNotProcess()) {
AbstractInsnNode start = new LabelNode();
AbstractInsnNode end = new LabelNode();
InsnList insnList = new InsnList();
insnList.add(start);
insnList.add(end);
return new FinallyBlockInfo(start, end);
}
List<TryCatchBlockNodeInfo> sameDefaultHandler = new ArrayList<TryCatchBlockNodeInfo>();
LabelNode defaultHandler = null;
boolean afterStartBlock = false;
@@ -427,7 +496,16 @@ public class InternalFinallyBlockInliner {
AbstractInsnNode endFinallyChainExclusive = skipLastGotoIfNeeded(nextIntervalWithSameDefaultHandler.getNode().handler,
nextIntervalWithSameDefaultHandler.getNode().start);
return new FinallyBlockInfo(startFinallyChain.getNext(), endFinallyChainExclusive);
FinallyBlockInfo finallyInfo = new FinallyBlockInfo(startFinallyChain.getNext(), endFinallyChainExclusive);
if (inlineFun.instructions.indexOf(finallyInfo.startIns) > inlineFun.instructions.indexOf(finallyInfo.endInsExclusive)) {
AbstractInsnNode startNode = finallyInfo.endInsExclusive;
AbstractInsnNode stopNode = finallyInfo.startIns;
writeNodes(startNode, stopNode);
throw new AssertionError();
}
return finallyInfo;
}
@NotNull
@@ -470,6 +548,7 @@ public class InternalFinallyBlockInliner {
private List<TryCatchBlockNodePosition> findTryCatchBlocksInlinedInFinally(@NotNull FinallyBlockInfo finallyInfo) {
List<TryCatchBlockNodePosition> result = new ArrayList<TryCatchBlockNodePosition>();
Map<TryCatchBlockNodeInfo, TryCatchBlockNodePosition> processedBlocks = new HashMap<TryCatchBlockNodeInfo, TryCatchBlockNodePosition>();
for (AbstractInsnNode curInstr = finallyInfo.startIns; curInstr != finallyInfo.endInsExclusive; curInstr = curInstr.getNext()) {
if (!(curInstr instanceof LabelNode)) continue;
@@ -503,6 +582,19 @@ public class InternalFinallyBlockInliner {
return result;
}
private void writeNodes(AbstractInsnNode startNode, AbstractInsnNode stopNode) {
Textifier p = new Textifier();
TraceMethodVisitor visitor = new TraceMethodVisitor(p);
while (startNode != stopNode) {
startNode.accept(visitor);
startNode = startNode.getNext();
}
startNode.accept(visitor);
StringWriter out = new StringWriter();
p.print(new PrintWriter(out));
System.out.println(out.toString());
}
private static void substituteReturnValueInFinally(
int nonLocalVarIndex,
@NotNull Type nonLocalReturnType,
@@ -543,5 +635,43 @@ public class InternalFinallyBlockInliner {
}
};
Collections.sort(inlineFunTryBlockInfo, comp);
for (TryCatchBlockNodeInfo info : inlineFunTryBlockInfo) {
TryCatchBlockNode node = info.getNode();
assertNotEmptyTryNode(node);
}
}
private void assertNotEmptyTryNode(TryCatchBlockNode node) {
LabelNode start = node.start;
AbstractInsnNode end = node.end;
while (end != start && end instanceof LabelNode) {
end = end.getPrevious();
}
assert start != end;
}
private static int counter = 0;
private void flushCurrentState(@NotNull AbstractInsnNode curNonLocal) {
substitureTryBlockNodes();
System.out.println( );
System.out.println();
System.out.println("Iteration: " + counter++);
System.out.println("Will process instruction at : " + inlineFun.instructions.indexOf(curNonLocal) + " " + curNonLocal.toString());
String text = getNodeText(inlineFun);
System.out.println(text);
}
private void substitureTryBlockNodes() {
inlineFun.tryCatchBlocks.clear();
for (TryCatchBlockNodeInfo info : inlineFunTryBlockInfo) {
inlineFun.tryCatchBlocks.add(info.getNode());
}
}
private int indexOf(AbstractInsnNode node) {
return inlineFun.instructions.indexOf(node) + 1;
}
}