Merge "CfBuilder: Fix missing label and stack frame for entry block"
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
index 5664f8d..83995bd 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/CfBuilder.java
@@ -201,6 +201,7 @@
CfLabel tryCatchStart = null;
CatchHandlers<BasicBlock> tryCatchHandlers = CatchHandlers.EMPTY_BASIC_BLOCK;
BasicBlock pendingFrame = null;
+ boolean previousFallthrough = false;
do {
CatchHandlers<BasicBlock> handlers = block.getCatchHandlers();
if (!tryCatchHandlers.equals(handlers)) {
@@ -218,6 +219,24 @@
tryCatchHandlers = handlers;
}
BasicBlock nextBlock = blockIterator.hasNext() ? blockIterator.next() : null;
+ // If previousBlock is fallthrough, then it is counted in getPredecessors().size(), but
+ // we only want to set a pendingFrame if we have a predecessor which is not previousBlock.
+ if (block.getPredecessors().size() > (previousFallthrough ? 1 : 0)) {
+ assert stack.isEmpty();
+ pendingFrame = block;
+ emitLabel(getLabel(block));
+ }
+ if (pendingFrame != null) {
+ boolean advancesPC = hasMaterializingInstructions(block, nextBlock);
+ // If block has no materializing instructions, then we postpone emitting the frame
+ // until the next block. In this case, nextBlock must be non-null
+ // (or we would fall off the edge of the method).
+ assert advancesPC || nextBlock != null;
+ if (advancesPC) {
+ addFrame(pendingFrame, Collections.emptyList());
+ pendingFrame = null;
+ }
+ }
JumpInstruction exit = block.exit();
boolean fallthrough =
(exit.isGoto() && exit.asGoto().getTarget() == nextBlock)
@@ -230,29 +249,8 @@
pendingLocalChanges = true;
}
buildCfInstructions(block, fallthrough, stack);
- if (nextBlock != null) {
- if (!fallthrough || nextBlock.getPredecessors().size() > 1) {
- assert stack.isEmpty();
- pendingFrame = nextBlock;
- emitLabel(getLabel(nextBlock));
- }
- if (pendingFrame != null) {
- BasicBlock nextNextBlock = null;
- if (blockIterator.hasNext()) {
- nextNextBlock = blockIterator.next();
- blockIterator.previous();
- }
- boolean advancesPC = hasMaterializingInstructions(nextBlock, nextNextBlock);
- // If nextBlock has no materializing instructions, then nextNextBlock must be non-null
- // (or we would fall off the edge of the method).
- assert advancesPC || nextNextBlock != null;
- if (advancesPC) {
- addFrame(pendingFrame, Collections.emptyList());
- pendingFrame = null;
- }
- }
- }
block = nextBlock;
+ previousFallthrough = fallthrough;
} while (block != null);
assert stack.isEmpty();
CfLabel endLabel = ensureLabel();
diff --git a/src/test/java/com/android/tools/r8/cf/CallLoopTest.java b/src/test/java/com/android/tools/r8/cf/CallLoopTest.java
new file mode 100644
index 0000000..6ccbdd1
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/CallLoopTest.java
@@ -0,0 +1,37 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.cf;
+
+public class CallLoopTest {
+ private static boolean doThrow = false;
+ private static boolean doLoop = true;
+
+ public static void main(String[] args) {
+ if (args.length % 2 == 0) doThrow = true;
+ if (args.length % 3 == 0) doLoop = false;
+ loop1();
+ try {
+ loop2();
+ } catch (RuntimeException e) {
+ // OK
+ }
+ }
+
+ private static void loop1() {
+ while (doLoop) {}
+ }
+
+ private static void loop2() {
+ while (true) {
+ maybeThrow();
+ }
+ }
+
+ private static void maybeThrow() {
+ if (doThrow) {
+ throw new RuntimeException();
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/cf/CallLoopTestRunner.java b/src/test/java/com/android/tools/r8/cf/CallLoopTestRunner.java
new file mode 100644
index 0000000..b4daa84
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/cf/CallLoopTestRunner.java
@@ -0,0 +1,33 @@
+// Copyright (c) 2018, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+package com.android.tools.r8.cf;
+
+import com.android.tools.r8.ClassFileConsumer.DirectoryConsumer;
+import com.android.tools.r8.CompilationMode;
+import com.android.tools.r8.R8;
+import com.android.tools.r8.R8Command;
+import com.android.tools.r8.ToolHelper;
+import com.android.tools.r8.origin.Origin;
+import java.nio.file.Path;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+public class CallLoopTestRunner {
+ static final Class CLASS = CallLoopTest.class;
+ @Rule public TemporaryFolder temp = ToolHelper.getTemporaryFolderForTest();
+
+ @Test
+ public void test() throws Exception {
+ Path out = temp.getRoot().toPath();
+ R8.run(
+ R8Command.builder()
+ .setMode(CompilationMode.DEBUG)
+ .addClassProgramData(ToolHelper.getClassAsBytes(CLASS), Origin.unknown())
+ .addLibraryFiles(ToolHelper.getAndroidJar(ToolHelper.getMinApiLevelForDexVm()))
+ .setProgramConsumer(new DirectoryConsumer(out))
+ .build());
+ assert ToolHelper.runJava(out, CLASS.getCanonicalName()).exitCode == 0;
+ }
+}