Merge remote-tracking branch 'aosp/ub-jack' into optim-dev
diff --git a/.gitignore b/.gitignore
index d50522a..e480c67 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,8 @@
# Code coverage
jack-coverage/testResults/
+# IDEA
+.idea
+JackCheckStyleSuppressions.xml
+JackStyleRules.xml
+**/*.iml
diff --git a/jack-tests/src/com/android/jack/test/dex/DexCodeItemPrinter.java b/jack-tests/src/com/android/jack/test/dex/DexCodeItemPrinter.java
index 07b61f8..93e3d46 100644
--- a/jack-tests/src/com/android/jack/test/dex/DexCodeItemPrinter.java
+++ b/jack-tests/src/com/android/jack/test/dex/DexCodeItemPrinter.java
@@ -18,6 +18,7 @@
import org.jf.dexlib.Code.FiveRegisterInstruction;
import org.jf.dexlib.Code.Format.Format;
+import org.jf.dexlib.Code.Format.PackedSwitchDataPseudoInstruction;
import org.jf.dexlib.Code.Instruction;
import org.jf.dexlib.Code.InstructionWithReference;
import org.jf.dexlib.Code.LiteralInstruction;
@@ -116,10 +117,13 @@
private void dump(
@Nonnull Instruction instr, int address,
- @Nonnull TreeMap<Integer, String> references) {
- write(instr.opcode.name);
-
+ @Nonnull TreeMap<Integer, String> references,
+ @Nonnull Map<Integer, Integer> crossAddressTable) {
Format format = instr.getFormat();
+ if (format != Format.PackedSwitchData) {
+ write(instr.opcode.name);
+ }
+
switch (format) {
case Format10x:
break;
@@ -131,6 +135,7 @@
break;
case Format21t:
+ case Format22t:
case Format31t:
write(" ").registers(instr).write(", ").address(instr, address, references);
break;
@@ -157,6 +162,19 @@
write(" ").registers(instr).write(", ").reference(instr);
break;
+ case PackedSwitchData:
+ PackedSwitchDataPseudoInstruction packed = (PackedSwitchDataPseudoInstruction) instr;
+ write("packed-switch: [").write(packed.getFirstKey())
+ .write("..").write(packed.getTargetCount() + packed.getFirstKey() - 1).write("] -> ");
+ String sep = "[";
+ int[] targets = packed.getTargets();
+ for (int i = 0; i < targets.length; i++) {
+ write(sep).write(references.get(targets[i] + crossAddressTable.get(address)));
+ sep = ", ";
+ }
+ write("]");
+ break;
+
default:
write(" // Details are not implemented yet for this format: " + format);
break;
@@ -168,20 +186,35 @@
private void dump(@Nonnull Instruction[] instructions) {
String indent = " | ";
- TreeMap<Integer, String> references = getReferencedAddresses(instructions);
+ TreeMap<Integer, Integer> crossAddressTable = getCrossAddressTable(instructions);
+ TreeMap<Integer, String> references = getReferencedAddresses(instructions, crossAddressTable);
int address = 0;
for (Instruction instr : instructions) {
// Indentation with optional label
String label = references.get(address);
write(label == null ? indent : (label + "-> "));
- dump(instr, address, references);
+ dump(instr, address, references, crossAddressTable);
address += instr.getSize(address);
}
}
@Nonnull
- private TreeMap<Integer, String> getReferencedAddresses(@Nonnull Instruction[] instructions) {
+ private TreeMap<Integer, Integer> getCrossAddressTable(@Nonnull Instruction[] instructions) {
+ int address = 0;
+ TreeMap<Integer, Integer> references = new TreeMap<>();
+ for (Instruction instr : instructions) {
+ if (instr instanceof OffsetInstruction) {
+ references.put(address + ((OffsetInstruction) instr).getTargetAddressOffset(), address);
+ }
+ address += instr.getSize(address);
+ }
+ return references;
+ }
+
+ @Nonnull
+ private TreeMap<Integer, String> getReferencedAddresses(
+ @Nonnull Instruction[] instructions, @Nonnull Map<Integer, Integer> crossAddressTable) {
int address = 0;
TreeMap<Integer, String> references = new TreeMap<Integer, String>();
for (Instruction instr : instructions) {
@@ -189,7 +222,7 @@
references.put(address + ((OffsetInstruction) instr).getTargetAddressOffset(), null);
} else if (instr instanceof MultiOffsetInstruction) {
for (int target : ((MultiOffsetInstruction) instr).getTargets()) {
- references.put(target, null);
+ references.put(target + crossAddressTable.get(address), null);
}
}
address += instr.getSize(address);
diff --git a/jack-tests/src/com/android/jack/test/dex/DexNoOpValidator.java b/jack-tests/src/com/android/jack/test/dex/DexNoOpValidator.java
new file mode 100644
index 0000000..449289c
--- /dev/null
+++ b/jack-tests/src/com/android/jack/test/dex/DexNoOpValidator.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.test.dex;
+
+import javax.annotation.Nonnull;
+
+/** DEX validator performing no validation */
+public final class DexNoOpValidator<T> extends DexValidator<T> {
+ protected void validateImpl(@Nonnull T element) {
+ }
+}
diff --git a/jack-tests/tests/com/android/jack/AllTests.java b/jack-tests/tests/com/android/jack/AllTests.java
index fa07f24..1e5ae93 100644
--- a/jack-tests/tests/com/android/jack/AllTests.java
+++ b/jack-tests/tests/com/android/jack/AllTests.java
@@ -50,6 +50,7 @@
import com.android.jack.newarray.NewarrayTests;
import com.android.jack.nopackage.NoPackageTests;
import com.android.jack.opcodes.OpcodesTests;
+import com.android.jack.optimizations.blockmerger.BlockMergerTests;
import com.android.jack.optimizations.defuse.DefUseTests;
import com.android.jack.optimizations.exprsimplifier.ExprsimplifierTests;
import com.android.jack.optimizations.ifwithconstantsimplifier.IfWithConstantSimplifierTests;
@@ -95,6 +96,7 @@
ArrayTests.class,
AssertionTests.class,
AssignTests.class,
+ BlockMergerTests.class,
BoostLockedRegionPriorityTests.class,
BoxTests.class,
BridgeTests.class,
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/BlockMergerTests.java b/jack-tests/tests/com/android/jack/optimizations/blockmerger/BlockMergerTests.java
new file mode 100644
index 0000000..b5c7935
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/BlockMergerTests.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.blockmerger;
+
+import com.android.jack.optimizations.Optimizations;
+import com.android.jack.optimizations.cfg.VariablesScope;
+import com.android.jack.test.dex.DexFileTypesValidator;
+import com.android.jack.test.dex.DexMethod;
+import com.android.jack.test.dex.DexMethodDalvikCodeValidator;
+import com.android.jack.test.dex.DexNoOpValidator;
+import com.android.jack.test.dex.DexOutputBasedTest;
+import com.android.jack.test.dex.DexTypeMethodsValidator;
+import com.android.jack.test.dex.DexValidator;
+import com.android.jack.test.junit.Runtime;
+
+import org.junit.Test;
+
+import javax.annotation.Nonnull;
+
+/** Set of unused local variables removal */
+public class BlockMergerTests extends DexOutputBasedTest {
+ @Nonnull
+ private CompilationProperties properties() {
+ return CompilationProperties.EMPTY
+ .with(Optimizations.SimpleBasicBlockMerging.ENABLE.getName(), Boolean.FALSE);
+ }
+
+ @Nonnull
+ private CompilationProperties properties(
+ boolean preserveSourceInfo, @Nonnull VariablesScope scope) {
+ return CompilationProperties.EMPTY
+ .with(Optimizations.SimpleBasicBlockMerging.ENABLE.getName(), Boolean.TRUE)
+ .with(Optimizations.SimpleBasicBlockMerging.PRESERVE_SOURCE_INFO.getName(),
+ Boolean.valueOf(preserveSourceInfo))
+ .with(Optimizations.SimpleBasicBlockMerging.MERGE_VARIABLES.getName(),
+ scope.toString());
+ }
+
+ @Nonnull
+ private DexValidator<DexMethod> dalvik(@Nonnull String test, @Nonnull String expected) {
+ return usingLegacyCompiler() ? new DexNoOpValidator<DexMethod>() :
+ new DexMethodDalvikCodeValidator(resource(test, expected));
+ }
+
+ @Test
+ @Runtime
+ public void test001() throws Exception {
+ String test = "com.android.jack.optimizations.blockmerger.test001";
+ String aType = "Lcom/android/jack/optimizations/blockmerger/test001/jack/A;";
+
+ compileAndValidate(test, properties(),
+ new DexFileTypesValidator()
+ .insert(aType,
+ new DexTypeMethodsValidator()
+ .insert("testA(IIIII)I", dalvik(test, "A.testA.no-opt.dalvik"))
+ .insert("testB(IIIII)I", dalvik(test, "A.testB.no-opt.dalvik"))
+ .insert("testC(IIIII)I", dalvik(test, "A.testC.no-opt.dalvik"))));
+
+ compileAndValidate(test, properties(false, VariablesScope.NONE),
+ new DexFileTypesValidator()
+ .insert(aType,
+ new DexTypeMethodsValidator()
+ .insert("testA(IIIII)I", dalvik(test, "A.testA.no-opt.dalvik" /* SAME */))
+ .insert("testB(IIIII)I", dalvik(test, "A.testB.no-opt.dalvik" /* SAME */))
+ .insert("testC(IIIII)I", dalvik(test, "A.testC.opt-none.dalvik"))));
+
+ compileAndValidate(test, properties(false, VariablesScope.SYNTHETIC),
+ new DexFileTypesValidator()
+ .insert(aType,
+ new DexTypeMethodsValidator()
+ .insert("testA(IIIII)I", dalvik(test, "A.testA.opt-syn.dalvik"))
+ .insert("testB(IIIII)I", dalvik(test, "A.testB.no-opt.dalvik" /* SAME */))
+ .insert("testC(IIIII)I", dalvik(test, "A.testC.opt-none.dalvik" /* SAME */))));
+
+ compileAndValidate(test, properties(false, VariablesScope.ALL),
+ new DexFileTypesValidator()
+ .insert(aType,
+ new DexTypeMethodsValidator()
+ .insert("testA(IIIII)I", dalvik(test, "A.testA.opt-syn.dalvik") /* SAME */)
+ .insert("testB(IIIII)I", dalvik(test, "A.testB.opt-all.dalvik"))
+ .insert("testC(IIIII)I", dalvik(test, "A.testC.opt-none.dalvik") /* SAME */)));
+ }
+
+ @Test
+ @Runtime
+ public void test002() throws Exception {
+ String test = "com.android.jack.optimizations.blockmerger.test002";
+ String aType = "Lcom/android/jack/optimizations/blockmerger/test002/jack/A;";
+
+ compileAndValidate(test, properties(),
+ new DexFileTypesValidator()
+ .insert(aType,
+ new DexTypeMethodsValidator()
+ .insert("testA(I)I", dalvik(test, "A.testA.no-opt.dalvik"))
+ .insert("testB(III)I", dalvik(test, "A.testB.no-opt.dalvik"))
+ .insert("testC(I)I", dalvik(test, "A.testC.no-opt.dalvik"))));
+
+ compileAndValidate(test, properties(true, VariablesScope.ALL),
+ new DexFileTypesValidator()
+ .insert(aType,
+ new DexTypeMethodsValidator()
+ .insert("testA(I)I", dalvik(test, "A.testA.no-opt.dalvik"))
+ .insert("testB(III)I", dalvik(test, "A.testB.no-opt.dalvik"))));
+
+ compileAndValidate(test, properties(false, VariablesScope.SYNTHETIC),
+ new DexFileTypesValidator()
+ .insert(aType,
+ new DexTypeMethodsValidator()
+ .insert("testC(I)I", dalvik(test, "A.testC.opt-syn.dalvik"))));
+
+ compileAndValidate(test, properties(false, VariablesScope.ALL),
+ new DexFileTypesValidator()
+ .insert(aType,
+ new DexTypeMethodsValidator()
+ .insert("testA(I)I", dalvik(test, "A.testA.opt-all.dalvik"))
+ .insert("testB(III)I", dalvik(test, "A.testB.opt-all.dalvik"))));
+ }
+}
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testA.no-opt.dalvik b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testA.no-opt.dalvik
new file mode 100644
index 0000000..f6c515c
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testA.no-opt.dalvik
@@ -0,0 +1,31 @@
+method: Lcom/android/jack/optimizations/blockmerger/test001/jack/A;->testA(IIIII)I
+registers: 8, in/out: 6/0
+instructions: 28
+ | add-int/lit8 v0, v3, 0
+ | packed-switch v3, #04
+ | mul-int/lit16 v1, v7, 10000
+ | add-int/2addr v0, v1
+#00-> return v0
+#01-> mul-int/lit8 v1, v4, 10
+ | add-int/2addr v0, v1
+ | mul-int/lit8 v1, v5, 100
+ | add-int/2addr v0, v1
+ | mul-int/lit16 v1, v6, 1000
+ | add-int/2addr v0, v1
+ | mul-int/lit16 v1, v7, 10000
+ | add-int/2addr v0, v1
+ | goto #00
+#02-> mul-int/lit8 v1, v5, 100
+ | add-int/2addr v0, v1
+ | mul-int/lit16 v1, v6, 1000
+ | add-int/2addr v0, v1
+ | mul-int/lit16 v1, v7, 10000
+ | add-int/2addr v0, v1
+ | goto #00
+#03-> mul-int/lit16 v1, v6, 1000
+ | add-int/2addr v0, v1
+ | mul-int/lit16 v1, v7, 10000
+ | add-int/2addr v0, v1
+ | goto #00
+ | nop
+#04-> packed-switch: [0..2] -> [#01, #02, #03]
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testA.opt-syn.dalvik b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testA.opt-syn.dalvik
new file mode 100644
index 0000000..9e71592
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testA.opt-syn.dalvik
@@ -0,0 +1,17 @@
+method: Lcom/android/jack/optimizations/blockmerger/test001/jack/A;->testA(IIIII)I
+registers: 8, in/out: 6/0
+instructions: 14
+ | add-int/lit8 v0, v3, 0
+ | packed-switch v3, #04
+#00-> mul-int/lit16 v1, v7, 10000
+ | add-int/2addr v0, v1
+ | return v0
+#01-> mul-int/lit8 v1, v4, 10
+ | add-int/2addr v0, v1
+#02-> mul-int/lit8 v1, v5, 100
+ | add-int/2addr v0, v1
+#03-> mul-int/lit16 v1, v6, 1000
+ | add-int/2addr v0, v1
+ | goto #00
+ | nop
+#04-> packed-switch: [0..2] -> [#01, #02, #03]
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testB.no-opt.dalvik b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testB.no-opt.dalvik
new file mode 100644
index 0000000..80b41c4
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testB.no-opt.dalvik
@@ -0,0 +1,53 @@
+method: Lcom/android/jack/optimizations/blockmerger/test001/jack/A;->testB(IIIII)I
+registers: 8, in/out: 6/0
+instructions: 50
+ | add-int/lit8 v0, v3, 0
+ | if-nez v3, #00
+ | mul-int/lit8 v1, v4, 2
+ | sub-int/2addr v1, v4
+ | mul-int/lit8 v1, v1, 10
+ | add-int/2addr v0, v1
+ | mul-int/lit8 v1, v5, 2
+ | sub-int/2addr v1, v5
+ | mul-int/lit8 v1, v1, 100
+ | add-int/2addr v0, v1
+ | mul-int/lit8 v1, v6, 2
+ | sub-int/2addr v1, v6
+ | mul-int/lit16 v1, v1, 1000
+ | add-int/2addr v0, v1
+ | mul-int/lit8 v1, v7, 2
+ | sub-int/2addr v1, v7
+ | mul-int/lit16 v1, v1, 10000
+ | add-int/2addr v0, v1
+ | return v0
+#00-> const/4 v1, 1
+ | if-ne v3, v1, #01
+ | mul-int/lit8 v1, v5, 2
+ | sub-int/2addr v1, v5
+ | mul-int/lit8 v1, v1, 100
+ | add-int/2addr v0, v1
+ | mul-int/lit8 v1, v6, 2
+ | sub-int/2addr v1, v6
+ | mul-int/lit16 v1, v1, 1000
+ | add-int/2addr v0, v1
+ | mul-int/lit8 v1, v7, 2
+ | sub-int/2addr v1, v7
+ | mul-int/lit16 v1, v1, 10000
+ | add-int/2addr v0, v1
+ | return v0
+#01-> const/4 v1, 2
+ | if-ne v3, v1, #02
+ | mul-int/lit8 v1, v6, 2
+ | sub-int/2addr v1, v6
+ | mul-int/lit16 v1, v1, 1000
+ | add-int/2addr v0, v1
+ | mul-int/lit8 v1, v7, 2
+ | sub-int/2addr v1, v7
+ | mul-int/lit16 v1, v1, 10000
+ | add-int/2addr v0, v1
+ | return v0
+#02-> mul-int/lit8 v1, v7, 2
+ | sub-int/2addr v1, v7
+ | mul-int/lit16 v1, v1, 10000
+ | add-int/2addr v0, v1
+ | return v0
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testB.opt-all.dalvik b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testB.opt-all.dalvik
new file mode 100644
index 0000000..864da92
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testB.opt-all.dalvik
@@ -0,0 +1,27 @@
+method: Lcom/android/jack/optimizations/blockmerger/test001/jack/A;->testB(IIIII)I
+registers: 8, in/out: 6/0
+instructions: 24
+ | add-int/lit8 v0, v3, 0
+ | if-nez v3, #03
+ | mul-int/lit8 v1, v4, 2
+ | sub-int/2addr v1, v4
+ | mul-int/lit8 v1, v1, 10
+ | add-int/2addr v0, v1
+#00-> mul-int/lit8 v1, v5, 2
+ | sub-int/2addr v1, v5
+ | mul-int/lit8 v1, v1, 100
+ | add-int/2addr v0, v1
+#01-> mul-int/lit8 v1, v6, 2
+ | sub-int/2addr v1, v6
+ | mul-int/lit16 v1, v1, 1000
+ | add-int/2addr v0, v1
+#02-> mul-int/lit8 v1, v7, 2
+ | sub-int/2addr v1, v7
+ | mul-int/lit16 v1, v1, 10000
+ | add-int/2addr v0, v1
+ | return v0
+#03-> const/4 v1, 1
+ | if-eq v3, v1, #00
+ | const/4 v1, 2
+ | if-ne v3, v1, #02
+ | goto #01
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testC.no-opt.dalvik b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testC.no-opt.dalvik
new file mode 100644
index 0000000..122ee87
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testC.no-opt.dalvik
@@ -0,0 +1,22 @@
+method: Lcom/android/jack/optimizations/blockmerger/test001/jack/A;->testC(IIIII)I
+registers: 7, in/out: 6/0
+instructions: 19
+ | if-nez v2, #00
+ | add-int v0, v2, v3
+ | add-int/2addr v0, v4
+ | add-int/2addr v0, v5
+ | add-int/2addr v0, v6
+ | return v0
+#00-> const/4 v0, 1
+ | if-ne v2, v0, #01
+ | add-int v0, v2, v4
+ | add-int/2addr v0, v5
+ | add-int/2addr v0, v6
+ | return v0
+#01-> const/4 v0, 2
+ | if-ne v2, v0, #02
+ | add-int v0, v2, v5
+ | add-int/2addr v0, v6
+ | return v0
+#02-> add-int v0, v2, v6
+ | return v0
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testC.opt-none.dalvik b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testC.opt-none.dalvik
new file mode 100644
index 0000000..a1808fd
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/A.testC.opt-none.dalvik
@@ -0,0 +1,19 @@
+method: Lcom/android/jack/optimizations/blockmerger/test001/jack/A;->testC(IIIII)I
+registers: 7, in/out: 6/0
+instructions: 16
+ | if-nez v2, #02
+ | add-int v0, v2, v3
+ | add-int/2addr v0, v4
+#00-> add-int/2addr v0, v5
+#01-> add-int/2addr v0, v6
+ | return v0
+#02-> const/4 v0, 1
+ | if-ne v2, v0, #03
+ | add-int v0, v2, v4
+ | goto #00
+#03-> const/4 v0, 2
+ | if-ne v2, v0, #04
+ | add-int v0, v2, v5
+ | goto #01
+#04-> add-int v0, v2, v6
+ | return v0
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/dx/Tests.java b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/dx/Tests.java
new file mode 100644
index 0000000..deadbd5
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/dx/Tests.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.blockmerger.test001.dx;
+
+import com.android.jack.optimizations.blockmerger.test001.jack.*;
+
+import junit.framework.Assert;
+import org.junit.Test;
+
+/** Just touch all the classes */
+public class Tests {
+ @Test
+ public void test001() {
+ A a = new A();
+ Assert.assertEquals("|43210|43201|43002|40003", a.testA());
+ Assert.assertEquals("|43210|43201|43002|40003", a.testB());
+ Assert.assertEquals("|43210|43201|43002|40003", a.testC());
+ }
+}
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/jack/A.java b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/jack/A.java
new file mode 100644
index 0000000..13e9f5c
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test001/jack/A.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.blockmerger.test001.jack;
+
+public class A {
+ private int testA(int s, int a, int b, int c, int d) {
+ int result = 0;
+ result += s;
+ switch (s) {
+ case 0: {
+ int A = a, B = b, C = c, D = d;
+ result += A * 10;
+ result += B * 100;
+ result += C * 1000;
+ result += D * 10000;
+ break;
+ }
+ case 1: {
+ int A = a, B = b, C = c, D = d;
+ result += B * 100;
+ result += C * 1000;
+ result += D * 10000;
+ break;
+ }
+ case 2: {
+ int A = a, B = b, C = c, D = d;
+ result += C * 1000;
+ result += D * 10000;
+ break;
+ }
+ default: {
+ int A = a, B = b, C = c, D = d;
+ result += D * 10000;
+ break;
+ }
+ }
+ return result;
+ }
+
+ private int testB(int s, int a, int b, int c, int d) {
+ int result = 0;
+ result += s;
+ if (s == 0) {
+ int A = a * 2 - a;
+ result += A * 10;
+ int B = b * 2 - b;
+ result += B * 100;
+ int C = c * 2 - c;
+ result += C * 1000;
+ int D = d * 2 - d;
+ result += D * 10000;
+ } else if (s == 1) {
+ int B = b * 2 - b;
+ result += B * 100;
+ int C = c * 2 - c;
+ result += C * 1000;
+ int D = d * 2 - d;
+ result += D * 10000;
+ } else if (s == 2) {
+ int C = c * 2 - c;
+ result += C * 1000;
+ int D = d * 2 - d;
+ result += D * 10000;
+ } else {
+ int D = d * 2 - d;
+ result += D * 10000;
+ }
+ return result;
+ }
+
+ private int testC(int s, int a, int b, int c, int d) {
+ int result = s;
+ if (s == 0) {
+ result += a;
+ result += b;
+ result += c;
+ result += d;
+ } else if (s == 1) {
+ result += b;
+ result += c;
+ result += d;
+ } else if (s == 2) {
+ result += c;
+ result += d;
+ } else {
+ result += d;
+ }
+ return result;
+ }
+
+ public String testA() {
+ return "" +
+ "|" + testA(0, 1, 2, 3, 4) +
+ "|" + testA(1, 9, 2, 3, 4) +
+ "|" + testA(2, 9, 9, 3, 4) +
+ "|" + testA(3, 9, 9, 9, 4);
+ }
+
+ public String testB() {
+ return "" +
+ "|" + testB(0, 1, 2, 3, 4) +
+ "|" + testB(1, 9, 2, 3, 4) +
+ "|" + testB(2, 9, 9, 3, 4) +
+ "|" + testB(3, 9, 9, 9, 4);
+ }
+
+ public String testC() {
+ return "" +
+ "|" + testC(0, 10, 200, 3000, 40000) +
+ "|" + testC(1, 99, 200, 3000, 40000) +
+ "|" + testC(2, 99, 999, 3000, 40000) +
+ "|" + testC(3, 99, 999, 9999, 40000);
+ }
+}
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testA.no-opt.dalvik b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testA.no-opt.dalvik
new file mode 100644
index 0000000..a2331cd
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testA.no-opt.dalvik
@@ -0,0 +1,13 @@
+method: Lcom/android/jack/optimizations/blockmerger/test002/jack/A;->testA(I)I
+registers: 2, in/out: 2/0
+instructions: 10
+ | if-lez v1, #03
+ | add-int/lit8 v1, v1, -1
+#00-> if-lez v1, #01
+ | return v1
+#01-> if-gez v1, #02
+ | return v1
+#02-> return v1
+#03-> if-gez v1, #00
+ | add-int/lit8 v1, v1, 1
+ | goto #00
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testA.opt-all.dalvik b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testA.opt-all.dalvik
new file mode 100644
index 0000000..bd32da0
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testA.opt-all.dalvik
@@ -0,0 +1,9 @@
+method: Lcom/android/jack/optimizations/blockmerger/test002/jack/A;->testA(I)I
+registers: 2, in/out: 2/0
+instructions: 6
+ | if-lez v1, #01
+ | add-int/lit8 v1, v1, -1
+#00-> return v1
+#01-> if-gez v1, #00
+ | add-int/lit8 v1, v1, 1
+ | goto #00
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testB.no-opt.dalvik b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testB.no-opt.dalvik
new file mode 100644
index 0000000..4d6abda
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testB.no-opt.dalvik
@@ -0,0 +1,22 @@
+method: Lcom/android/jack/optimizations/blockmerger/test002/jack/A;->testB(III)I
+registers: 5, in/out: 4/0
+instructions: 19
+ | if-lez v2, #03
+ | add-int/lit8 v2, v2, -1
+#00-> if-lez v2, #01
+ | mul-int v0, v3, v4
+ | mul-int/lit8 v0, v0, 10
+ | add-int/2addr v0, v2
+ | return v0
+#01-> if-gez v2, #02
+ | mul-int v0, v3, v4
+ | mul-int/lit8 v0, v0, 10
+ | add-int/2addr v0, v2
+ | return v0
+#02-> mul-int v0, v3, v4
+ | mul-int/lit8 v0, v0, 10
+ | add-int/2addr v0, v2
+ | return v0
+#03-> if-gez v2, #00
+ | add-int/lit8 v2, v2, 1
+ | goto #00
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testB.opt-all.dalvik b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testB.opt-all.dalvik
new file mode 100644
index 0000000..7073afe
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testB.opt-all.dalvik
@@ -0,0 +1,12 @@
+method: Lcom/android/jack/optimizations/blockmerger/test002/jack/A;->testB(III)I
+registers: 5, in/out: 4/0
+instructions: 9
+ | if-lez v2, #01
+ | add-int/lit8 v2, v2, -1
+#00-> mul-int v0, v3, v4
+ | mul-int/lit8 v0, v0, 10
+ | add-int/2addr v0, v2
+ | return v0
+#01-> if-gez v2, #00
+ | add-int/lit8 v2, v2, 1
+ | goto #00
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testC.no-opt.dalvik b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testC.no-opt.dalvik
new file mode 100644
index 0000000..4bb87e0
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testC.no-opt.dalvik
@@ -0,0 +1,11 @@
+method: Lcom/android/jack/optimizations/blockmerger/test002/jack/A;->testC(I)I
+registers: 3, in/out: 2/0
+instructions: 8
+ | if-lez v2, #00
+ | const-string/jumbo v0, string_id_item: a > 0
+ | return v2
+#00-> if-gez v2, #01
+ | const-string/jumbo v0, string_id_item: a < 0
+ | return v2
+#01-> const-string/jumbo v0, string_id_item: a == 0
+ | return v2
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testC.opt-syn.dalvik b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testC.opt-syn.dalvik
new file mode 100644
index 0000000..7d690ac
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/A.testC.opt-syn.dalvik
@@ -0,0 +1,11 @@
+method: Lcom/android/jack/optimizations/blockmerger/test002/jack/A;->testC(I)I
+registers: 3, in/out: 2/0
+instructions: 8
+ | if-lez v2, #01
+ | const-string/jumbo v0, string_id_item: a > 0
+#00-> return v2
+#01-> if-gez v2, #02
+ | const-string/jumbo v0, string_id_item: a < 0
+ | goto #00
+#02-> const-string/jumbo v0, string_id_item: a == 0
+ | goto #00
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/dx/Tests.java b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/dx/Tests.java
new file mode 100644
index 0000000..8cb874f
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/dx/Tests.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.blockmerger.test002.dx;
+
+import com.android.jack.optimizations.blockmerger.test002.jack.*;
+
+import junit.framework.Assert;
+import org.junit.Test;
+
+/** Just touch all the classes */
+public class Tests {
+ @Test
+ public void test001() {
+ A a = new A();
+ Assert.assertEquals("1|0|0|0|-1", a.testA());
+ Assert.assertEquals("11|20|30|-40|-51", a.testB());
+ Assert.assertEquals("1|0|-1", a.testC());
+ }
+}
diff --git a/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/jack/A.java b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/jack/A.java
new file mode 100644
index 0000000..7962626
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/blockmerger/test002/jack/A.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.blockmerger.test002.jack;
+
+public class A {
+ private int testA(int a) {
+ if (a > 0) {
+ a--;
+ } else if (a < 0) {
+ a++;
+ }
+ if (a > 0) {
+ return a;
+ } else if (a < 0) {
+ return a;
+ } else {
+ return a;
+ }
+ }
+
+ private int testB(int a, int b, int c) {
+ if (a > 0) {
+ a--;
+ } else if (a < 0) {
+ a++;
+ }
+ if (a > 0) {
+ return a + b * c * 10;
+ } else if (a < 0) {
+ return a + b * c * 10;
+ } else {
+ return a + b * c * 10;
+ }
+ }
+
+ private int testC(int a) {
+ if (a > 0) {
+ String x = "a > 0";
+ return a;
+ } else if (a < 0) {
+ String x = "a < 0";
+ return a;
+ } else {
+ String x = "a == 0";
+ return a;
+ }
+ }
+
+ public String testA() {
+ return testA(2) + "|" + testA(1) + "|" +
+ testA(0) + "|" + testA(-1) + "|" + testA(-2);
+ }
+
+ public String testB() {
+ return testB(2, 1, 1) + "|" + testB(1, 1, 2) + "|" +
+ testB(0, 1, 3) + "|" + testB(-1, -1, 4) + "|" + testB(-2, -1, 5);
+ }
+
+ public String testC() {
+ return testC(1) + "|" + testC(0) + "|" + testC(-1);
+ }
+}
diff --git a/jack-tests/tests/com/android/jack/optimizations/ssa/copypropagation/CopyPropagationTests.java b/jack-tests/tests/com/android/jack/optimizations/ssa/copypropagation/CopyPropagationTests.java
new file mode 100644
index 0000000..a5f49a5
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/ssa/copypropagation/CopyPropagationTests.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.ssa.copypropagation;
+
+import com.android.jack.Options.UseJackSsaIR;
+import com.android.jack.test.helper.RuntimeTestHelper;
+import com.android.jack.test.junit.Runtime;
+import com.android.jack.test.runtime.RuntimeTest;
+import com.android.jack.test.runtime.RuntimeTestInfo;
+import com.android.jack.test.toolchain.AbstractTestTools;
+
+import org.junit.Test;
+
+public class CopyPropagationTests extends RuntimeTest {
+
+ private RuntimeTestInfo TEST001 = new RuntimeTestInfo(
+ AbstractTestTools
+ .getTestRootDir("com.android.jack.optimizations.ssa.copypropagation.test001"),
+ "com.android.jack.optimizations.ssa.copypropagation.test001.dx.Tests");
+
+ @Test
+ @Runtime
+ public void test001() throws Exception {
+ new RuntimeTestHelper(TEST001).addProperty(UseJackSsaIR.ENABLE.getName(), "true")
+ .compileAndRunTest();
+ }
+
+ @Override
+ protected void fillRtTestInfos() {
+ rtTestInfos.add(TEST001);
+ }
+}
diff --git a/jack-tests/tests/com/android/jack/optimizations/ssa/copypropagation/test001/dx/Tests.java b/jack-tests/tests/com/android/jack/optimizations/ssa/copypropagation/test001/dx/Tests.java
new file mode 100644
index 0000000..645aca7
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/ssa/copypropagation/test001/dx/Tests.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.ssa.copypropagation.test001.dx;
+
+import com.android.jack.optimizations.ssa.copypropagation.test001.jack.CopyPropagation;
+import com.android.jack.optimizations.ssa.copypropagation.test001.jack.CopyPropagation.Testing;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+public class Tests {
+
+ @Test
+ public void test001() {
+ Testing a = new Testing(99);
+ Testing b = new Testing(3);
+ Testing c = new Testing(4);
+ Testing d = new Testing(5);
+
+ a.next = b;
+ b.next = c;
+ c.next = d;
+ d.next = a;
+
+ CopyPropagation cp = new CopyPropagation();
+ Assert.assertEquals(3 + 4 + 5, cp.sum(a));
+ Assert.assertEquals(4 + 5 + 99, cp.sum(b));
+ Assert.assertEquals(5 + 99 + 3, cp.sum(c));
+ Assert.assertEquals(99 + 3 + 4, cp.sum(d));
+ }
+}
diff --git a/jack-tests/tests/com/android/jack/optimizations/ssa/copypropagation/test001/jack/CopyPropagation.java b/jack-tests/tests/com/android/jack/optimizations/ssa/copypropagation/test001/jack/CopyPropagation.java
new file mode 100644
index 0000000..30fd4e8
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/optimizations/ssa/copypropagation/test001/jack/CopyPropagation.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.ssa.copypropagation.test001.jack;
+
+public class CopyPropagation {
+
+ /**
+ * Linked List like object
+ */
+ public static class Testing {
+ public final int value;
+ public Testing next = null;
+
+ public Testing(int value) {
+ this.value = value;
+ }
+
+ public Testing getNext() {
+ return next;
+ }
+ }
+
+ /**
+ * Sum the list, ignoring the head value.
+ */
+ public int sum(Testing head) {
+ Testing e = head.getNext();
+ int sum = 0;
+ while (e != head) {
+ sum += e.value;
+ Testing next = e.getNext();
+ e = next;
+ }
+ return sum;
+ }
+}
diff --git a/jack-tests/tests/com/android/jack/ssa/SsaTests.java b/jack-tests/tests/com/android/jack/ssa/SsaTests.java
new file mode 100644
index 0000000..541dffc
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/ssa/SsaTests.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ssa;
+
+import com.android.jack.Options;
+import com.android.jack.test.helper.RuntimeTestHelper;
+import com.android.jack.test.junit.Runtime;
+import com.android.jack.test.runtime.RuntimeTest;
+import com.android.jack.test.runtime.RuntimeTestInfo;
+import com.android.jack.test.toolchain.AbstractTestTools;
+
+import org.junit.Test;
+
+import java.security.cert.PKIXRevocationChecker.Option;
+
+/**
+ * A list of tests to prevent regression in certain corner cases when using Jack's SSA.
+ *
+ * Note that these tests are not meant to be comprehensive verification of the SSA pipeline. It is
+ * used mostly to cover certain cases that requires extra attention. It should be used with
+ * conjunction with the rest of the jack-tests.
+ */
+public class SsaTests extends RuntimeTest {
+
+ private RuntimeTestInfo TEST001 =
+ new RuntimeTestInfo(AbstractTestTools.getTestRootDir("com.android.jack.ssa.test001"),
+ "com.android.jack.ssa.test001.dx.Tests");
+
+ @Test
+ @Runtime
+ public void test001() throws Exception {
+ new RuntimeTestHelper(TEST001).addProperty(Options.UseJackSsaIR.ENABLE.getName(), "true")
+ .compileAndRunTest();
+ }
+
+ @Override
+ protected void fillRtTestInfos() {
+ rtTestInfos.add(TEST001);
+ }
+}
diff --git a/jack-tests/tests/com/android/jack/ssa/test001/dx/Tests.java b/jack-tests/tests/com/android/jack/ssa/test001/dx/Tests.java
new file mode 100644
index 0000000..a99a5d5
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/ssa/test001/dx/Tests.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ssa.test001.dx;
+
+import com.android.jack.ssa.test001.jack.Ssa;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class Tests {
+ @Test
+ public void testNestedCatch() {
+ Assert.assertEquals("message".length(), Ssa.doubleNestedCatch());
+ }
+
+ @Test
+ public void testMultipleUses() {
+ int x = 11;
+ int y = 51;
+ int z = 91;
+
+ int a = x + y;
+ int b = y + z;
+ int c = x + z;
+
+ int x_1 = x + 1;
+ int y_1 = y + 1;
+ int z_1 = z + 1;
+
+ int result = a + b + c + x_1 + y_1 + z_1;
+
+ Assert.assertEquals(result, Ssa.multipleUses(x, y, z));
+ }
+
+ @Test
+ public void testFallThroughCaseWithPhi() {
+ Assert.assertEquals(2, Ssa.fallThroughCaseWithPhi(2, -1));
+ }
+}
diff --git a/jack-tests/tests/com/android/jack/ssa/test001/jack/Ssa.java b/jack-tests/tests/com/android/jack/ssa/test001/jack/Ssa.java
new file mode 100644
index 0000000..27bdf39
--- /dev/null
+++ b/jack-tests/tests/com/android/jack/ssa/test001/jack/Ssa.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ssa.test001.jack;
+
+public class Ssa {
+
+ private static void sometimesThrow(boolean shouldThrow) {
+ if (shouldThrow) {
+ throw new RuntimeException("message");
+ }
+ }
+
+ /**
+ * A doublely nested catch case.
+ */
+ public static int doubleNestedCatch() {
+ try {
+ sometimesThrow(true);
+ return -1;
+ } catch (Throwable e0) {
+ try {
+ sometimesThrow("message".equals(e0.getMessage()));
+ } catch (Exception e1) {
+ return e0.getMessage().length();
+ }
+ }
+ return -1;
+ }
+
+ public static int multipleUses(int x, int y, int z) {
+ int a = x + y;
+ int b = y + z;
+ int c = x + z;
+
+ x = x + 1;
+ y = y + 1;
+ z = z + 1;
+
+ int result = a + b + c + x + y + z;
+ return result;
+ }
+
+ public static int fallThroughCaseWithPhi(int x, int y) {
+ switch(x) {
+ case 2:
+ y = 2;
+ case 1: {
+ return y;
+ }
+ }
+ return -1;
+ }
+}
diff --git a/jack/src/com/android/jack/Jack.java b/jack/src/com/android/jack/Jack.java
index 3ee7a0e..4015559 100644
--- a/jack/src/com/android/jack/Jack.java
+++ b/jack/src/com/android/jack/Jack.java
@@ -78,6 +78,7 @@
import com.android.jack.backend.dex.multidex.legacy.RuntimeAnnotationFinder;
import com.android.jack.backend.dex.rop.CodeItemBuilder;
import com.android.jack.backend.dex.rop.DexCodeMarkerRemover;
+import com.android.jack.backend.dex.rop.SsaCodeItemBuilder;
import com.android.jack.backend.jayce.JayceFileImporter;
import com.android.jack.backend.jayce.JayceInLibraryProduct;
import com.android.jack.backend.jayce.JayceInLibraryWriterAll;
@@ -105,10 +106,13 @@
import com.android.jack.ir.ast.JDefinedClassOrInterface;
import com.android.jack.ir.ast.JField;
import com.android.jack.ir.ast.JMethod;
+import com.android.jack.ir.ast.JMethodBodyCfg;
import com.android.jack.ir.ast.JPackage;
import com.android.jack.ir.ast.JSession;
import com.android.jack.ir.ast.JSwitchStatement;
import com.android.jack.ir.ast.Resource;
+import com.android.jack.ir.ast.cfg.CfgChecker;
+import com.android.jack.ir.ast.cfg.MethodBodyCfgBuilder;
import com.android.jack.ir.formatter.InternalFormatter;
import com.android.jack.ir.formatter.TypePackageAndMethodFormatter;
import com.android.jack.ir.formatter.UserFriendlyFormatter;
@@ -132,6 +136,14 @@
import com.android.jack.optimizations.Optimizations;
import com.android.jack.optimizations.UnusedDefinitionRemover;
import com.android.jack.optimizations.UseDefsChainsSimplifier;
+import com.android.jack.optimizations.blockmerger.CfgSimpleBasicBlockMerger;
+import com.android.jack.optimizations.cfg.OptimizeConditionalPrimarySuccessor;
+import com.android.jack.optimizations.cfg.RemoveEmptyBasicBlocks;
+import com.android.jack.optimizations.cfg.RemoveRedundantConditionalBlocks;
+import com.android.jack.optimizations.cfg.RemoveRedundantGotoReturnEdges;
+import com.android.jack.optimizations.cfg.RemoveUnreachableBasicBlocks;
+import com.android.jack.optimizations.cfg.SimplifyConditionalExpressions;
+import com.android.jack.optimizations.cfg.ToggleCfgSsaFlagProcessor;
import com.android.jack.optimizations.common.DirectlyDerivedClassesProvider;
import com.android.jack.optimizations.inlining.InlineAnnotatedMethods;
import com.android.jack.optimizations.inlining.InlineAnnotationSanityCheck;
@@ -139,6 +151,7 @@
import com.android.jack.optimizations.modifiers.ClassFinalizer;
import com.android.jack.optimizations.modifiers.FieldFinalizer;
import com.android.jack.optimizations.modifiers.MethodFinalizer;
+import com.android.jack.optimizations.ssa.CopyPropagation;
import com.android.jack.optimizations.tailrecursion.TailRecursionOptimization;
import com.android.jack.optimizations.tailrecursion.TailRecursionOptimizer;
import com.android.jack.optimizations.valuepropagation.argument.AvpCalculateTaintedMethods;
@@ -161,6 +174,7 @@
import com.android.jack.scheduling.adapter.JDefinedClassOrInterfaceAdapter;
import com.android.jack.scheduling.adapter.JFieldAdapter;
import com.android.jack.scheduling.adapter.JMethodAdapter;
+import com.android.jack.scheduling.adapter.JMethodBodyCfgAdapter;
import com.android.jack.scheduling.adapter.JPackageAdapter;
import com.android.jack.scheduling.feature.CompiledTypeStats;
import com.android.jack.scheduling.feature.DropMethodBody;
@@ -305,11 +319,19 @@
import com.android.jack.transformations.parent.TypeAstChecker;
import com.android.jack.transformations.renamepackage.PackageRenamer;
import com.android.jack.transformations.rop.cast.RopCastLegalizer;
+import com.android.jack.transformations.ssa.CfgNodeIdAssignment;
+import com.android.jack.transformations.ssa.CfgNodeIdRemoval;
+import com.android.jack.transformations.ssa.CfgNodeListAssignment;
+import com.android.jack.transformations.ssa.CfgNodeListRemoval;
+import com.android.jack.transformations.ssa.DominanceFrontierAssignment;
+import com.android.jack.transformations.ssa.DominanceFrontierRemoval;
+import com.android.jack.transformations.ssa.JPhiElementInsertion;
+import com.android.jack.transformations.ssa.SsaBasicBlockSplitter;
+import com.android.jack.transformations.ssa.SsaRenamer;
import com.android.jack.transformations.threeaddresscode.ThreeAddressCodeBuilder;
import com.android.jack.transformations.typedef.TypeDefRemover;
import com.android.jack.transformations.typedef.TypeDefRemover.RemoveTypeDef;
import com.android.jack.transformations.uselessif.UselessIfChecker;
-import com.android.jack.transformations.uselessif.UselessIfRemover;
import com.android.jack.util.collect.UnmodifiableCollections;
import com.android.sched.item.Component;
import com.android.sched.reflections.ReflectionFactory;
@@ -735,6 +757,13 @@
if (config.get(Optimizations.WriteOnlyFieldRemoval.ENABLE).booleanValue()) {
request.addFeature(Optimizations.WriteOnlyFieldRemoval.class);
}
+ if (config.get(Options.UseJackSsaIR.ENABLE).booleanValue()) {
+ request.addFeature(Options.UseJackSsaIR.class);
+ }
+ if (config.get(Optimizations.SimpleBasicBlockMerging.ENABLE).booleanValue()) {
+ request.addFeature(Optimizations.SimpleBasicBlockMerging.class);
+ }
+
if (config.get(Optimizations.InlineAnnotatedMethods.ENABLE).booleanValue()) {
request.addFeature(Optimizations.InlineAnnotatedMethods.class);
}
@@ -1660,82 +1689,112 @@
features.contains(Optimizations.FieldValuePropagation.class);
boolean enableWriteOnlyFieldRemoval =
features.contains(Optimizations.WriteOnlyFieldRemoval.class);
-
+ {
+ SubPlanBuilder<JDefinedClassOrInterface> typePlan5 =
+ planBuilder.appendSubPlan(JDefinedClassOrInterfaceAdapter.class);
{
- SubPlanBuilder<JDefinedClassOrInterface> typePlan5 =
- planBuilder.appendSubPlan(JDefinedClassOrInterfaceAdapter.class);
{
- {
- SubPlanBuilder<JMethod> methodPlan4 = typePlan5.appendSubPlan(JMethodAdapter.class);
- methodPlan4.append(RefAsStatementRemover.class);
- methodPlan4.append(CfgBuilder.class);
- methodPlan4.append(DefinitionMarkerAdder.class);
- methodPlan4.append(ReachingDefinitions.class);
- methodPlan4.append(UsedVariableAdder.class);
- methodPlan4.append(DefUsesAndUseDefsChainComputation.class);
- if (hasSanityChecks) {
- methodPlan4.append(UseDefsChecker.class);
- }
- methodPlan4.append(ConstantRefiner.class);
- if (features.contains(Optimizations.UseDefSimplifier.class)) {
- methodPlan4.append(UseDefsChainsSimplifier.class);
- }
- if (features.contains(Optimizations.DefUseSimplifier.class)) {
- methodPlan4.append(DefUsesChainsSimplifier.class);
- }
- methodPlan4.append(UnusedDefinitionRemover.class);
- methodPlan4.append(RefAsStatementRemover.class);
- methodPlan4.append(CfgMarkerRemover.class);
- methodPlan4.append(CfgBuilder.class);
- if (features.contains(Optimizations.IfSimplifier.class)) {
- methodPlan4.append(IfWithConstantSimplifier.class);
- }
- methodPlan4.append(UnusedLocalRemover.class);
- if (enableFieldValuePropagation) {
- methodPlan4.append(FvpCollectFieldAssignments.class);
- }
- if (enableArgumentValuePropagation) {
- methodPlan4.append(AvpCollectMethodCallArguments.class);
- }
- if (enableWriteOnlyFieldRemoval) {
- methodPlan4.append(WofrCollectFieldAccesses.class);
- }
- methodPlan4.append(DefUsesAndUseDefsChainRemover.class);
- methodPlan4.append(DefinitionMarkerRemover.class);
- methodPlan4.append(UsedVariableRemover.class);
- if (features.contains(Optimizations.ExpressionSimplifier.class)) {
- methodPlan4.append(ExpressionSimplifier.class);
- }
- methodPlan4.append(UselessIfRemover.class);
- methodPlan4.append(CfgMarkerRemover.class);
- methodPlan4.append(CfgBuilder.class);
- methodPlan4.append(ContainerAnnotationAdder.MethodContainerAnnotationAdder.class);
+ SubPlanBuilder<JMethod> methodPlan4 =
+ typePlan5.appendSubPlan(JMethodAdapter.class);
+ methodPlan4.append(RefAsStatementRemover.class);
+ methodPlan4.append(CfgBuilder.class);
+ methodPlan4.append(DefinitionMarkerAdder.class);
+ methodPlan4.append(ReachingDefinitions.class);
+ methodPlan4.append(UsedVariableAdder.class);
+ methodPlan4.append(DefUsesAndUseDefsChainComputation.class);
+ if (hasSanityChecks) {
+ methodPlan4.append(UseDefsChecker.class);
}
+ methodPlan4.append(ConstantRefiner.class);
+ if (features.contains(Optimizations.UseDefSimplifier.class)) {
+ methodPlan4.append(UseDefsChainsSimplifier.class);
+ }
+ if (features.contains(Optimizations.DefUseSimplifier.class)) {
+ methodPlan4.append(DefUsesChainsSimplifier.class);
+ }
+ methodPlan4.append(UnusedDefinitionRemover.class);
+ methodPlan4.append(RefAsStatementRemover.class);
+ methodPlan4.append(CfgMarkerRemover.class);
+ methodPlan4.append(CfgBuilder.class);
+ if (features.contains(Optimizations.IfSimplifier.class)) {
+ methodPlan4.append(IfWithConstantSimplifier.class);
+ }
+ methodPlan4.append(UnusedLocalRemover.class);
+ if (enableFieldValuePropagation) {
+ methodPlan4.append(FvpCollectFieldAssignments.class);
+ }
+ if (enableArgumentValuePropagation) {
+ methodPlan4.append(AvpCollectMethodCallArguments.class);
+ }
+ if (enableWriteOnlyFieldRemoval) {
+ methodPlan4.append(WofrCollectFieldAccesses.class);
+ }
+ methodPlan4.append(DefUsesAndUseDefsChainRemover.class);
+ methodPlan4.append(DefinitionMarkerRemover.class);
+ methodPlan4.append(UsedVariableRemover.class);
+ if (features.contains(Optimizations.ExpressionSimplifier.class)) {
+ methodPlan4.append(ExpressionSimplifier.class);
+ }
+ methodPlan4.append(CfgMarkerRemover.class);
+ methodPlan4.append(CfgBuilder.class);
+ methodPlan4.append(ContainerAnnotationAdder.MethodContainerAnnotationAdder.class);
}
}
+ }
+ if (enableArgumentValuePropagation) {
+ planBuilder.append(
+ AvpComputeMethodArgumentsValues.class);
+ }
+ {
+ // Build cfg-IR representation of body methods
+ SubPlanBuilder<JMethod> methodPlan = planBuilder
+ .appendSubPlan(JDefinedClassOrInterfaceAdapter.class)
+ .appendSubPlan(JMethodAdapter.class);
+ methodPlan.append(MethodBodyCfgBuilder.class);
+ methodPlan.append(CfgMarkerRemover.class);
+
+ // Cfg-IR base transformations
+ SubPlanBuilder<JMethodBodyCfg> cfgPlan = planBuilder
+ .appendSubPlan(JDefinedClassOrInterfaceAdapter.class)
+ .appendSubPlan(JMethodBodyCfgAdapter.class);
+
+ if (hasSanityChecks) {
+ cfgPlan.append(CfgChecker.class);
+ }
+ if (enableFieldValuePropagation) {
+ cfgPlan.append(FvpPropagateFieldValues.class);
+ }
if (enableArgumentValuePropagation) {
- planBuilder.append(AvpComputeMethodArgumentsValues.class);
+ cfgPlan.append(AvpPropagateArgumentValues.class);
+ }
+ if (enableWriteOnlyFieldRemoval) {
+ cfgPlan.append(WofrRemoveFieldWrites.class);
+ }
+ if (hasSanityChecks) {
+ cfgPlan.append(CfgChecker.class);
}
- if (enableFieldValuePropagation || enableArgumentValuePropagation
- || enableWriteOnlyFieldRemoval) {
- SubPlanBuilder<JDefinedClassOrInterface> typePlan =
- planBuilder.appendSubPlan(JDefinedClassOrInterfaceAdapter.class);
- SubPlanBuilder<JMethod> methodPlan = typePlan.appendSubPlan(JMethodAdapter.class);
- if (enableFieldValuePropagation) {
- methodPlan.append(FvpPropagateFieldValues.class);
- }
- if (enableArgumentValuePropagation) {
- methodPlan.append(AvpPropagateArgumentValues.class);
- }
- if (enableWriteOnlyFieldRemoval) {
- methodPlan.append(WofrRemoveFieldWrites.class);
- }
- methodPlan.append(CfgMarkerRemover.class);
- methodPlan.append(CfgBuilder.class);
+ if (features.contains(Optimizations.SimpleBasicBlockMerging.class)) {
+ cfgPlan.append(CfgSimpleBasicBlockMerger.class);
+ }
+ if (hasSanityChecks) {
+ cfgPlan.append(CfgChecker.class);
}
+ cfgPlan.append(RemoveRedundantConditionalBlocks.class);
+ cfgPlan.append(RemoveEmptyBasicBlocks.class);
+ cfgPlan.append(RemoveRedundantGotoReturnEdges.class);
+ cfgPlan.append(RemoveUnreachableBasicBlocks.class);
+ cfgPlan.append(OptimizeConditionalPrimarySuccessor.class);
+ cfgPlan.append(SimplifyConditionalExpressions.class);
+
+ if (hasSanityChecks) {
+ cfgPlan.append(CfgChecker.class);
+ }
+ }
+
+ {
SubPlanBuilder<JDefinedClassOrInterface> typePlan1 =
planBuilder.appendSubPlan(JDefinedClassOrInterfaceAdapter.class);
@@ -1744,49 +1803,74 @@
if (enableWriteOnlyFieldRemoval) {
fieldPlan.append(WofrRemoveFields.class);
}
+ }
- // The sub plan that write dex files representing type must not be split in order to remove dx
- // IR from memory when dex file is written.
+ // SSA Construction.
+ if (features.contains(Options.UseJackSsaIR.class)) {
+ SubPlanBuilder<JMethodBodyCfg> cfgPlan = planBuilder
+ .appendSubPlan(JDefinedClassOrInterfaceAdapter.class)
+ .appendSubPlan(JMethodBodyCfgAdapter.class);
+ cfgPlan.append(ToggleCfgSsaFlagProcessor.class);
+ cfgPlan.append(SsaBasicBlockSplitter.class);
+ cfgPlan.append(CfgNodeIdAssignment.class);
+ cfgPlan.append(CfgNodeListAssignment.class);
+ cfgPlan.append(DominanceFrontierAssignment.class);
+ cfgPlan.append(JPhiElementInsertion.class);
+ cfgPlan.append(SsaRenamer.class);
+ cfgPlan.append(CopyPropagation.class);
+ cfgPlan.append(CfgNodeIdRemoval.class);
+ cfgPlan.append(CfgNodeListRemoval.class);
+ cfgPlan.append(DominanceFrontierRemoval.class);
+
+ if (hasSanityChecks) {
+ cfgPlan.append(CfgChecker.class);
+ }
+ }
+
+ // The sub plan that write dex files representing type must not be split in order to remove dx
+ // IR from memory when dex file is written.
+ {
+ SubPlanBuilder<JDefinedClassOrInterface> typePlan6 =
+ planBuilder.appendSubPlan(JDefinedClassOrInterfaceAdapter.class);
+
{
- SubPlanBuilder<JDefinedClassOrInterface> typePlan6 =
- planBuilder.appendSubPlan(JDefinedClassOrInterfaceAdapter.class);
-
- {
- SubPlanBuilder<JMethod> methodPlan5 = typePlan6.appendSubPlan(JMethodAdapter.class);
- methodPlan5.append(CodeItemBuilder.class);
- methodPlan5.append(CfgMarkerRemover.class);
- methodPlan5.append(EncodedMethodBuilder.class);
- methodPlan5.append(DexCodeMarkerRemover.class);
- if (features.contains(ParameterMetadataFeature.class)) {
- methodPlan5.append(ParameterMetadataAnnotationsAdder.class);
- }
- methodPlan5.append(MethodAnnotationBuilder.class);
- if (features.contains(DropMethodBody.class)) {
- methodPlan5.append(MethodBodyRemover.class);
- }
- }
-
- {
- SubPlanBuilder<JField> fieldPlan2 = typePlan6.appendSubPlan(JFieldAdapter.class);
- fieldPlan2.append(EncodedFieldBuilder.class);
- fieldPlan2.append(FieldAnnotationBuilder.class);
- }
-
- if (hasSanityChecks) {
- typePlan6.append(TypeAstChecker.class);
- }
-
- // Jayce files must be copied into output library in incremental library mode or in non
- // incremental mode
- if (features.contains(GenerateLibraryFromIncrementalFolder.class)
- || !features.contains(Incremental.class)) {
- typePlan6.append(DexInLibraryWriterAll.class);
+ SubPlanBuilder<JMethod> methodPlan5 = typePlan6.appendSubPlan(JMethodAdapter.class);
+ if (features.contains(Options.UseJackSsaIR.class)) {
+ methodPlan5.append(SsaCodeItemBuilder.class);
} else {
- typePlan6.append(DexInLibraryWriterNoPrebuilt.class);
+ methodPlan5.append(CodeItemBuilder.class);
}
- typePlan6.append(ClassDefItemMarkerRemover.class);
+ methodPlan5.append(EncodedMethodBuilder.class);
+ methodPlan5.append(DexCodeMarkerRemover.class);
+ if (features.contains(ParameterMetadataFeature.class)) {
+ methodPlan5.append(ParameterMetadataAnnotationsAdder.class);
+ }
+ methodPlan5.append(MethodAnnotationBuilder.class);
+ if (features.contains(DropMethodBody.class)) {
+ methodPlan5.append(MethodBodyRemover.class);
+ }
}
+ {
+ SubPlanBuilder<JField> fieldPlan2 = typePlan6.appendSubPlan(JFieldAdapter.class);
+ fieldPlan2.append(EncodedFieldBuilder.class);
+ fieldPlan2.append(FieldAnnotationBuilder.class);
+ }
+
+ if (hasSanityChecks) {
+ typePlan6.append(TypeAstChecker.class);
+ }
+
+ // Jayce files must be copied into output library in incremental library mode or in non
+ // incremental mode
+ if (features.contains(GenerateLibraryFromIncrementalFolder.class)
+ || !features.contains(Incremental.class)) {
+ typePlan6.append(DexInLibraryWriterAll.class);
+ } else {
+ typePlan6.append(DexInLibraryWriterNoPrebuilt.class);
+ }
+ typePlan6.append(ClassDefItemMarkerRemover.class);
+ }
}
if (hasSanityChecks) {
diff --git a/jack/src/com/android/jack/Options.java b/jack/src/com/android/jack/Options.java
index b13ead8..1f29c83 100644
--- a/jack/src/com/android/jack/Options.java
+++ b/jack/src/com/android/jack/Options.java
@@ -56,6 +56,8 @@
import com.android.jack.util.ClassNameCodec;
import com.android.jack.util.args4j.JackEnumOptionHandler;
import com.android.jack.util.filter.Filter;
+import com.android.sched.item.Description;
+import com.android.sched.item.Feature;
import com.android.sched.reflections.ReflectionFactory;
import com.android.sched.util.RunnableHooks;
import com.android.sched.util.SubReleaseKind;
@@ -345,6 +347,21 @@
"jack.annotation-processor", "Enable annotation processors")
.addDefaultValue(true).addCategory(DumpInLibrary.class);
+ /**
+ * Enables Jack SSA IR.
+ */
+ @HasKeyId
+ @Description("Uses Jack SSA IR.")
+ public static class UseJackSsaIR implements Feature {
+ @Nonnull
+ public static final BooleanPropertyId ENABLE = BooleanPropertyId
+ .create("jack.optimization.use-jack-ssa-ir",
+ "Apply method argument value propagation optimization")
+ .addDefaultValue(Boolean.FALSE)
+ .addCategory(DumpInLibrary.class)
+ .addCategory(PrebuiltCompatibility.class)
+ .addCategory(Private.class);
+ }
@Option(name = "--version", usage = "display version")
private boolean version;
diff --git a/jack/src/com/android/jack/backend/dex/rop/CodeItemBuilder.java b/jack/src/com/android/jack/backend/dex/rop/CodeItemBuilder.java
index 7baa30a..6a128f3 100644
--- a/jack/src/com/android/jack/backend/dex/rop/CodeItemBuilder.java
+++ b/jack/src/com/android/jack/backend/dex/rop/CodeItemBuilder.java
@@ -17,15 +17,6 @@
import com.android.jack.JackEventType;
import com.android.jack.Options;
-import com.android.jack.cfg.BasicBlock;
-import com.android.jack.cfg.CatchBasicBlock;
-import com.android.jack.cfg.ConditionalBasicBlock;
-import com.android.jack.cfg.ControlFlowGraph;
-import com.android.jack.cfg.NormalBasicBlock;
-import com.android.jack.cfg.PeiBasicBlock;
-import com.android.jack.cfg.ReturnBasicBlock;
-import com.android.jack.cfg.SwitchBasicBlock;
-import com.android.jack.cfg.ThrowBasicBlock;
import com.android.jack.dx.dex.DexOptions;
import com.android.jack.dx.dex.code.DalvCode;
import com.android.jack.dx.dex.code.PositionList;
@@ -62,16 +53,34 @@
import com.android.jack.ir.ast.JFieldInitializer;
import com.android.jack.ir.ast.JLoop;
import com.android.jack.ir.ast.JMethod;
-import com.android.jack.ir.ast.JMethodBody;
+import com.android.jack.ir.ast.JMethodBodyCfg;
import com.android.jack.ir.ast.JMultiExpression;
import com.android.jack.ir.ast.JParameter;
import com.android.jack.ir.ast.JPrimitiveType.JPrimitiveTypeEnum;
-import com.android.jack.ir.ast.JStatement;
+import com.android.jack.ir.ast.JSsaVariableRef;
import com.android.jack.ir.ast.JSwitchStatement;
import com.android.jack.ir.ast.JThis;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.ast.cfg.ExceptionHandlingContext;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JCaseBasicBlock;
+import com.android.jack.ir.ast.cfg.JCatchBasicBlock;
+import com.android.jack.ir.ast.cfg.JConditionalBasicBlock;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JEntryBasicBlock;
+import com.android.jack.ir.ast.cfg.JExitBasicBlock;
+import com.android.jack.ir.ast.cfg.JRegularBasicBlock;
+import com.android.jack.ir.ast.cfg.JReturnBasicBlock;
+import com.android.jack.ir.ast.cfg.JSimpleBasicBlock;
+import com.android.jack.ir.ast.cfg.JSwitchBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowingExpressionBasicBlock;
import com.android.jack.ir.ast.marker.ThrownExceptionMarker;
+import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.jack.library.DumpInLibrary;
import com.android.jack.library.PrebuiltCompatibility;
+import com.android.jack.optimizations.cfg.CfgBasicBlockUtils;
import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
import com.android.jack.scheduling.marker.DexCodeMarker;
import com.android.jack.transformations.EmptyClinit;
@@ -87,7 +96,6 @@
import com.android.jack.transformations.ast.UnassignedValues;
import com.android.jack.transformations.ast.inner.InnerAccessor;
import com.android.jack.transformations.ast.switches.UselessSwitches;
-import com.android.jack.transformations.booleanoperators.FallThroughMarker;
import com.android.jack.transformations.cast.SourceCast;
import com.android.jack.transformations.rop.cast.RopLegalCast;
import com.android.jack.transformations.threeaddresscode.ThreeAddressCodeForm;
@@ -107,8 +115,9 @@
import com.android.sched.util.log.TracerFactory;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
-
+import java.util.Map;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
@@ -120,39 +129,40 @@
@Description("Builds CodeItem from JMethod")
@Name("CodeItemBuilder")
@Constraint(
- need = {
- ControlFlowGraph.class,
- JExceptionRuntimeValue.class,
- NewInstanceRemoved.class,
- ThreeAddressCodeForm.class,
- RopLegalCast.class,
- InnerAccessor.class,
- InvalidDefaultBridgeInInterfaceRemoved.class
- },
- no = {
- BooleanTestOutsideIf.class,
- InitInNewArray.class,
- JAsgOperation.class,
- JPrimitiveClassLiteral.class,
- JMultiExpression.class,
- JConditionalExpression.class,
- JFieldInitializer.class,
- JConcatOperation.class,
- JLoop.class,
- SideEffectOperation.class,
- UnassignedValues.class,
- RefAsStatement.class,
- MultiDimensionNewArray.class,
- JSwitchStatement.SwitchWithEnum.class,
- ImplicitBoxingAndUnboxing.class,
- ImplicitCast.class,
- JAssertStatement.class,
- JConditionalOperation.class,
- EmptyClinit.class,
- UselessSwitches.class,
- SourceCast.class,
- JCastOperation.WithIntersectionType.class
- }
+ need = {
+ JMethodBodyCfg.class,
+ JExceptionRuntimeValue.class,
+ NewInstanceRemoved.class,
+ ThreeAddressCodeForm.class,
+ RopLegalCast.class,
+ InnerAccessor.class,
+ InvalidDefaultBridgeInInterfaceRemoved.class
+ },
+ no = {
+ JSsaVariableRef.class,
+ BooleanTestOutsideIf.class,
+ InitInNewArray.class,
+ JAsgOperation.class,
+ JPrimitiveClassLiteral.class,
+ JMultiExpression.class,
+ JConditionalExpression.class,
+ JFieldInitializer.class,
+ JConcatOperation.class,
+ JLoop.class,
+ SideEffectOperation.class,
+ UnassignedValues.class,
+ RefAsStatement.class,
+ MultiDimensionNewArray.class,
+ JSwitchStatement.SwitchWithEnum.class,
+ ImplicitBoxingAndUnboxing.class,
+ ImplicitCast.class,
+ JAssertStatement.class,
+ JConditionalOperation.class,
+ EmptyClinit.class,
+ UselessSwitches.class,
+ SourceCast.class,
+ JCastOperation.WithIntersectionType.class
+ }
)
@Transform(add = DexCodeMarker.class)
@Use(RopHelper.class)
@@ -207,7 +217,7 @@
ThreadConfig.get(Options.EMIT_LINE_NUMBER_DEBUG_INFO).booleanValue();
@Nonnull
- private final Tracer tracer = TracerFactory.getTracer();
+ private final Tracer tracer = TracerFactory.getTracer();
@Override
public void run(@Nonnull JMethod method) {
@@ -221,171 +231,207 @@
RopRegisterManager ropReg =
new RopRegisterManager(emitLocalDebugInfo, emitSyntheticLocalDebugInfo);
- ControlFlowGraph cfg = method.getMarker(ControlFlowGraph.class);
- assert cfg != null;
-
- RopBasicBlockManager ropBb = new RopBasicBlockManager(getMaxLabel(cfg));
- assert cfg.getEntryNode().getSuccessors().size() == 1;
- BasicBlock firstBlockOfCode = cfg.getEntryNode().getSuccessors().get(0);
- assert firstBlockOfCode != null;
- addSetupBlocks(method, ropReg, ropBb, firstBlockOfCode.getId());
-
JAbstractMethodBody body = method.getBody();
- assert body instanceof JMethodBody;
+ assert body instanceof JMethodBodyCfg;
+ JControlFlowGraph cfg = ((JMethodBodyCfg) body).getCfg();
+
+ final JEntryBasicBlock entryBasicBlock = cfg.getEntryBlock();
+ final JExitBasicBlock exitBasicBlock = cfg.getExitBlock();
+
+ // Before building code item, we clean all exception handling
+ // context and all weakly referenced catch blocks.
+ removeExceptionHandlingContext(cfg);
+
+ final Map<JBasicBlock, Integer> basicBlocks = new LinkedHashMap<>();
+ int blockId = 1; // 0 is reserved for entry block
+ for (JBasicBlock block : cfg.getReachableBlocksDepthFirst()) {
+ if (block != entryBasicBlock && block != exitBasicBlock) {
+ basicBlocks.put(block, Integer.valueOf(blockId++));
+ }
+ }
+
+ // We assume that there are no *internal* blocks in cfg
+ // that are not reachable from entry block
+ assert basicBlocks.size() == cfg.getInternalBlocksUnordered().size();
+
+ final RopBasicBlockManager ropBb = new RopBasicBlockManager(blockId + 1);
+ JBasicBlock firstBlockOfCode = entryBasicBlock.getOnlySuccessor();
+ addSetupBlocks(method, ropReg, ropBb, basicBlocks.get(firstBlockOfCode).intValue());
if (method.getType() != JPrimitiveTypeEnum.VOID.getType()) {
ropReg.createReturnReg(method.getType());
}
- for (BasicBlock bb : cfg.getNodes()) {
- if (bb == cfg.getEntryNode()) {
- continue;
- }
- RopBuilderVisitor ropBuilder = new RopBuilderVisitor(ropReg, bb, apiLevel);
+ for (JBasicBlock bb : basicBlocks.keySet()) {
+ assert bb != entryBasicBlock && bb != exitBasicBlock;
+ final RopBuilderVisitor ropBuilder = new RopBuilderVisitor(ropReg, bb, apiLevel);
- assert !bb.getStatements().isEmpty();
-
- ropBuilder.accept(bb.getStatements());
- List<Insn> instructions = ropBuilder.getInstructions();
+ ropBuilder.processBasicBlockElements();
+ final List<Insn> instructions = ropBuilder.getInstructions();
assert instructions != null;
- JStatement lastStmt = bb.getLastInstruction();
- SourcePosition lastStmtsourcePosition = RopHelper.getSourcePosition(lastStmt);
- // TODO(mikaelpeltier) Think about a better solution to take into account control flow
- // (perhaps with meta on cfg).
- if (bb instanceof ReturnBasicBlock) {
- InsnList il = createInsnList(instructions, 0);
- il.setImmutable();
- ropBb.createBasicBlock(bb.getId(), il, IntList.EMPTY, -1);
- } else if (bb instanceof ConditionalBasicBlock) {
- InsnList il = createInsnList(instructions, 0);
- il.setImmutable();
- BasicBlock primary = ((ConditionalBasicBlock) bb).getThenBlock();
- BasicBlock secondary = ((ConditionalBasicBlock) bb).getElseBlock();
+ JVisitor visitor = new JVisitor() {
+ @Override
+ public boolean visit(@Nonnull JRegularBasicBlock bb) {
+ assert bb instanceof JSimpleBasicBlock
+ || bb instanceof JCatchBasicBlock
+ || bb instanceof JCaseBasicBlock;
+ assert bb.hasPrimarySuccessor();
- FallThroughMarker ftm = lastStmt.getMarker(FallThroughMarker.class);
- if (ftm != null) {
- switch (ftm.getFallThrough()) {
- case ELSE: {
- primary = ((ConditionalBasicBlock) bb).getElseBlock();
- secondary = ((ConditionalBasicBlock) bb).getThenBlock();
- break;
+ JBasicBlock primarySuccessor = bb.getPrimarySuccessor();
+ IntList successors = IntList.makeImmutable(getBlockId(primarySuccessor));
+ InsnList il = createInsnList(instructions, 1);
+ Insn gotoInstruction = new PlainInsn(
+ Rops.GOTO, getLastElementPosition(bb), null, RegisterSpecList.EMPTY);
+ il.set(instructions.size(), gotoInstruction);
+ il.setImmutable();
+ ropBb.createBasicBlock(getBlockId(bb), il, successors, getBlockId(primarySuccessor));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JReturnBasicBlock bb) {
+ InsnList il = createInsnList(instructions, 0);
+ il.setImmutable();
+ ropBb.createBasicBlock(getBlockId(bb), il, IntList.EMPTY, -1);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JThrowBasicBlock bb) {
+ InsnList il = createInsnList(instructions, 0);
+ il.setImmutable();
+
+ IntList successors = new IntList();
+ int primarySuccessor = -1;
+
+ if (!bb.getCatchBlocks().isEmpty()) {
+ addCatchBlockSuccessors(bb.getCatchBlocks(), successors);
+ }
+ successors.setImmutable();
+
+ ropBb.createBasicBlock(getBlockId(bb), il, successors, primarySuccessor);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JThrowingExpressionBasicBlock bb) {
+ Insn lastInstruction = instructions.get(instructions.size() - 1);
+ List<Insn> extraInstructions = ropBuilder.getExtraInstructions();
+ assert extraInstructions != null;
+
+ InsnList il = createInsnList(instructions, 0);
+ il.setImmutable();
+
+ int extraBlockLabel = ropBb.getAvailableLabel();
+
+ IntList successors = new IntList();
+ addCatchBlockSuccessors(bb.getCatchBlocks(), successors);
+
+ successors.add(extraBlockLabel);
+ successors.setImmutable();
+
+ ropBb.createBasicBlock(getBlockId(bb), il, successors, extraBlockLabel);
+
+ int indexInstruction = 0;
+ boolean needsGoto;
+ SourcePosition sourcePosition;
+ if (extraInstructions.isEmpty()) {
+ needsGoto = true;
+ sourcePosition = lastInstruction.getPosition();
+ il = new InsnList(1);
+ } else {
+ Insn extraInsn = extraInstructions.get(0);
+ needsGoto =
+ extraInstructions.get(extraInstructions.size() - 1)
+ .getOpcode().getBranchingness() == Rop.BRANCH_NONE;
+ il = new InsnList(extraInstructions.size() + (needsGoto ? 1 : 0));
+ for (Insn inst : extraInstructions) {
+ il.set(indexInstruction++, inst);
}
- case THEN: {
- primary = ((ConditionalBasicBlock) bb).getThenBlock();
- secondary = ((ConditionalBasicBlock) bb).getElseBlock();
- break;
- }
- default: {
- throw new AssertionError();
+ sourcePosition = extraInsn.getPosition();
+ }
+
+ if (needsGoto) {
+ il.set(indexInstruction,
+ new PlainInsn(Rops.GOTO, sourcePosition, null, RegisterSpecList.EMPTY));
+ }
+
+ il.setImmutable();
+
+ JBasicBlock primary = bb.getPrimarySuccessor();
+ successors = IntList.makeImmutable(getBlockId(primary));
+
+ ropBb.createBasicBlock(extraBlockLabel, il, successors, getBlockId(primary));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JConditionalBasicBlock bb) {
+ InsnList il = createInsnList(instructions, 0);
+ il.setImmutable();
+
+ JBasicBlock primary = bb.getPrimarySuccessor();
+ JBasicBlock secondary = bb.getAlternativeSuccessor();
+
+ int primarySuccessor = getBlockId(primary);
+ IntList successors = IntList.makeImmutable(primarySuccessor, getBlockId(secondary));
+
+ ropBb.createBasicBlock(getBlockId(bb), il, successors, primarySuccessor);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JSwitchBasicBlock bb) {
+ IntList successors = new IntList();
+ for (JBasicBlock caseBb : bb.getCases()) {
+ successors.add(getBlockId(caseBb));
+ }
+
+ successors.add(getBlockId(bb.getDefaultCase()));
+
+ successors.setImmutable();
+ InsnList il = createInsnList(instructions, 0);
+ il.setImmutable();
+ ropBb.createBasicBlock(
+ getBlockId(bb), il, successors, successors.get(successors.size() - 1));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JBasicBlock x) {
+ throw new AssertionError("Not implemented yet: " + x.toString());
+ }
+
+ private SourcePosition getLastElementPosition(@Nonnull JBasicBlock bb) {
+ return RopHelper.getSourcePosition(bb.hasElements() ?
+ bb.getLastElement().getSourceInfo() : SourceInfo.UNKNOWN);
+ }
+
+ private void addCatchBlockSuccessors(
+ @Nonnull List<JBasicBlock> catchBlocks,
+ @Nonnull IntList successors) {
+ for (JBasicBlock catchBlock : catchBlocks) {
+ int catchTypeCount = 0;
+ int catchTypesSize = ((JCatchBasicBlock) catchBlock).getCatchTypes().size();
+ while (catchTypeCount++ < catchTypesSize) {
+ successors.add(getBlockId(catchBlock));
}
}
- } else {
- primary = ((ConditionalBasicBlock) bb).getThenBlock();
- secondary = ((ConditionalBasicBlock) bb).getElseBlock();
}
- assert primary != null;
- assert secondary != null;
- int primarySuccessor = primary.getId();
- IntList successors = IntList.makeImmutable(primarySuccessor, secondary.getId());
-
- ropBb.createBasicBlock(bb.getId(), il, successors, primarySuccessor);
- } else if (bb instanceof ThrowBasicBlock) {
- assert bb.getSuccessors().size() >= 1;
- ThrowBasicBlock throwBlock = (ThrowBasicBlock) bb;
- InsnList il = createInsnList(instructions, 0);
- il.setImmutable();
-
- IntList successors = new IntList();
- int primarySuccessor = -1;
-
- if (!throwBlock.getExceptionBlocks().isEmpty()) {
- addCatchBlockSuccessors(throwBlock.getExceptionBlocks(), successors);
- }
- successors.setImmutable();
-
- ropBb.createBasicBlock(bb.getId(), il, successors, primarySuccessor);
-
- } else if (bb instanceof PeiBasicBlock) {
- assert bb.getSuccessors().size() >= 2;
- PeiBasicBlock peiBlock = (PeiBasicBlock) bb;
- Insn lastInstruction = instructions.get(instructions.size() - 1);
-
- List<Insn> extraInstructions = ropBuilder.getExtraInstructions();
- assert extraInstructions != null;
-
- InsnList il = createInsnList(instructions, 0);
- il.setImmutable();
-
- int extraBlockLabel = ropBb.getAvailableLabel();
-
- IntList successors = new IntList();
- addCatchBlockSuccessors(peiBlock.getExceptionBlocks(), successors);
-
- successors.add(extraBlockLabel);
- successors.setImmutable();
-
- ropBb.createBasicBlock(bb.getId(), il, successors, extraBlockLabel);
-
- int indexInstruction = 0;
- boolean needsGoto;
- SourcePosition sourcePosition;
- if (extraInstructions.isEmpty()) {
- needsGoto = true;
- sourcePosition = lastInstruction.getPosition();
- il = new InsnList(1);
- } else {
- Insn extraInsn = extraInstructions.get(0);
- needsGoto =
- extraInstructions.get(extraInstructions.size() - 1).getOpcode().getBranchingness()
- == Rop.BRANCH_NONE;
- il = new InsnList(extraInstructions.size() + (needsGoto ? 1 : 0));
- for (Insn inst : extraInstructions) {
- il.set(indexInstruction++, inst);
+ private int getBlockId(@Nonnull JBasicBlock block) {
+ if (block == entryBasicBlock) {
+ return 0;
}
- sourcePosition = extraInsn.getPosition();
+ if (block == exitBasicBlock) {
+ return Integer.MAX_VALUE;
+ }
+ return basicBlocks.get(block).intValue();
}
+ };
- if (needsGoto) {
- il.set(indexInstruction++, new PlainInsn(Rops.GOTO, sourcePosition, null,
- RegisterSpecList.EMPTY));
- }
-
- il.setImmutable();
- BasicBlock primarySuccessor = ((PeiBasicBlock) bb).getTarget();
- assert primarySuccessor != null;
-
- successors = IntList.makeImmutable(primarySuccessor.getId());
-
- ropBb.createBasicBlock(extraBlockLabel, il, successors, primarySuccessor.getId());
- } else if (bb instanceof SwitchBasicBlock) {
- IntList successors = new IntList();
- for (BasicBlock succ : ((SwitchBasicBlock) bb).getCasesBlock()) {
- successors.add(succ.getId());
- }
-
- int defaultIdBlock = ((SwitchBasicBlock) bb).getDefaultBlock().getId();
- successors.add(defaultIdBlock);
-
- successors.setImmutable();
- InsnList il = createInsnList(instructions, 0);
- il.setImmutable();
- ropBb.createBasicBlock(bb.getId(), il, successors, successors.get(successors.size() - 1));
- } else if (bb instanceof NormalBasicBlock) {
- List<BasicBlock> bbSuccessors = bb.getSuccessors();
- assert bbSuccessors.size() == 1;
- int primarySuccessor = bbSuccessors.get(0).getId();
- IntList successors = IntList.makeImmutable(primarySuccessor);
- InsnList il = createInsnList(instructions, 1);
- Insn gotoInstruction =
- new PlainInsn(Rops.GOTO, lastStmtsourcePosition, null, RegisterSpecList.EMPTY);
- il.set(instructions.size(), gotoInstruction);
- il.setImmutable();
- ropBb.createBasicBlock(bb.getId(), il, successors, primarySuccessor);
- } else {
- throw new AssertionError("Not yet supported");
- }
+ visitor.accept(bb);
}
RopMethod ropMethod = new RopMethod(ropBb.getBasicBlockList(),
@@ -415,15 +461,13 @@
}
}
- private void addCatchBlockSuccessors(@Nonnull List<CatchBasicBlock> catchBlocks,
- @Nonnull IntList successors) {
- for (CatchBasicBlock catchblock : catchBlocks) {
- int catchTypeCount = 0;
- int catchTypesSize = catchblock.getCatchTypes().size();
- while (catchTypeCount++ < catchTypesSize) {
- successors.add(catchblock.getId());
+ private void removeExceptionHandlingContext(@Nonnull JControlFlowGraph cfg) {
+ for (JBasicBlock block : cfg.getInternalBlocksUnordered()) {
+ for (JBasicBlockElement element : block.getElements(true)) {
+ element.resetEHContext(ExceptionHandlingContext.EMPTY);
}
}
+ new CfgBasicBlockUtils(cfg).removeUnreachableBlocks();
}
@Nonnull
@@ -436,21 +480,6 @@
}
}
- private int getMaxLabel(ControlFlowGraph cfg) {
- int maxLabel = -1;
-
- for (BasicBlock bb : cfg.getNodes()) {
- int bbId = bb.getId();
- if (bbId > maxLabel) {
- maxLabel = bbId;
- }
- }
-
- // maxLabel is exclusive, thus add +1
- maxLabel++;
- return maxLabel;
- }
-
@Nonnull
private InsnList createInsnList(@Nonnull List<Insn> instructions, @Nonnegative int extraSize) {
InsnList il = new InsnList(instructions.size() + extraSize);
diff --git a/jack/src/com/android/jack/backend/dex/rop/RopBuilderVisitor.java b/jack/src/com/android/jack/backend/dex/rop/RopBuilderVisitor.java
index ef831b9..ff7fdf3 100644
--- a/jack/src/com/android/jack/backend/dex/rop/RopBuilderVisitor.java
+++ b/jack/src/com/android/jack/backend/dex/rop/RopBuilderVisitor.java
@@ -16,11 +16,9 @@
package com.android.jack.backend.dex.rop;
+import com.google.common.collect.Lists;
+
import com.android.jack.backend.dex.invokecustom.InvokeCustomHelper;
-import com.android.jack.cfg.BasicBlock;
-import com.android.jack.cfg.CatchBasicBlock;
-import com.android.jack.cfg.PeiBasicBlock;
-import com.android.jack.cfg.SwitchBasicBlock;
import com.android.jack.dx.rop.code.FillArrayDataInsn;
import com.android.jack.dx.rop.code.Insn;
import com.android.jack.dx.rop.code.PlainCstInsn;
@@ -61,12 +59,11 @@
import com.android.jack.ir.ast.JAnnotation;
import com.android.jack.ir.ast.JArrayLength;
import com.android.jack.ir.ast.JArrayRef;
+import com.android.jack.ir.ast.JAsgOperation;
import com.android.jack.ir.ast.JBinaryOperation;
import com.android.jack.ir.ast.JBinaryOperator;
import com.android.jack.ir.ast.JBooleanLiteral;
import com.android.jack.ir.ast.JByteLiteral;
-import com.android.jack.ir.ast.JCaseStatement;
-import com.android.jack.ir.ast.JCatchBlock;
import com.android.jack.ir.ast.JCharLiteral;
import com.android.jack.ir.ast.JClass;
import com.android.jack.ir.ast.JClassLiteral;
@@ -74,18 +71,14 @@
import com.android.jack.ir.ast.JDynamicCastOperation;
import com.android.jack.ir.ast.JExceptionRuntimeValue;
import com.android.jack.ir.ast.JExpression;
-import com.android.jack.ir.ast.JExpressionStatement;
import com.android.jack.ir.ast.JFieldRef;
import com.android.jack.ir.ast.JFloatLiteral;
-import com.android.jack.ir.ast.JIfStatement;
import com.android.jack.ir.ast.JInstanceOf;
import com.android.jack.ir.ast.JIntLiteral;
import com.android.jack.ir.ast.JIntegralConstant32;
import com.android.jack.ir.ast.JInterface;
import com.android.jack.ir.ast.JLambda;
import com.android.jack.ir.ast.JLiteral;
-import com.android.jack.ir.ast.JLocalRef;
-import com.android.jack.ir.ast.JLock;
import com.android.jack.ir.ast.JLongLiteral;
import com.android.jack.ir.ast.JMethodCall;
import com.android.jack.ir.ast.JMethodCall.DispatchKind;
@@ -98,20 +91,32 @@
import com.android.jack.ir.ast.JPrimitiveType.JPrimitiveTypeEnum;
import com.android.jack.ir.ast.JReferenceType;
import com.android.jack.ir.ast.JReinterpretCastOperation;
-import com.android.jack.ir.ast.JReturnStatement;
import com.android.jack.ir.ast.JShortLiteral;
-import com.android.jack.ir.ast.JStatement;
-import com.android.jack.ir.ast.JSwitchStatement;
-import com.android.jack.ir.ast.JThrowStatement;
import com.android.jack.ir.ast.JType;
import com.android.jack.ir.ast.JUnaryOperation;
-import com.android.jack.ir.ast.JUnlock;
import com.android.jack.ir.ast.JValueLiteral;
import com.android.jack.ir.ast.JVariableRef;
import com.android.jack.ir.ast.JVisitor;
import com.android.jack.ir.ast.MethodKind;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JCaseBlockElement;
+import com.android.jack.ir.ast.cfg.JCatchBasicBlock;
+import com.android.jack.ir.ast.cfg.JConditionalBasicBlock;
+import com.android.jack.ir.ast.cfg.JConditionalBlockElement;
+import com.android.jack.ir.ast.cfg.JGotoBlockElement;
+import com.android.jack.ir.ast.cfg.JLockBlockElement;
+import com.android.jack.ir.ast.cfg.JMethodCallBlockElement;
+import com.android.jack.ir.ast.cfg.JPolymorphicMethodCallBlockElement;
+import com.android.jack.ir.ast.cfg.JReturnBlockElement;
+import com.android.jack.ir.ast.cfg.JStoreBlockElement;
+import com.android.jack.ir.ast.cfg.JSwitchBasicBlock;
+import com.android.jack.ir.ast.cfg.JSwitchBlockElement;
+import com.android.jack.ir.ast.cfg.JThrowBlockElement;
+import com.android.jack.ir.ast.cfg.JThrowingBasicBlock;
+import com.android.jack.ir.ast.cfg.JUnlockBlockElement;
+import com.android.jack.ir.ast.cfg.JVariableAsgBlockElement;
import com.android.jack.ir.types.JIntegralType32;
-import com.android.jack.transformations.booleanoperators.FallThroughMarker;
import com.android.jack.util.AndroidApiLevel;
import com.android.jack.util.AndroidApiLevel.ProvisionalLevel;
@@ -138,7 +143,7 @@
private List<Insn> extraInstructions;
@Nonnull
- private final BasicBlock currentBasicBlock;
+ private final JBasicBlock currentBasicBlock;
/**
* A guard for {@code instructions}. Does not protect {@code extraInstructions}.
@@ -146,23 +151,21 @@
private boolean noMoreInstruction = true;
private class AssignBuilderVisitor extends JVisitor {
-
- @Nonnull
- private final JStatement declaration;
@Nonnull
private final RegisterSpec destReg;
@Nonnull
- SourcePosition sourcePosition;
+ private final SourcePosition sourcePosition;
- public AssignBuilderVisitor(@Nonnull JStatement declaration, @Nonnull JVariableRef destRef) {
- this.declaration = declaration;
+ public AssignBuilderVisitor(
+ @Nonnull SourcePosition sourcePosition,
+ @Nonnull JVariableRef destRef) {
this.destReg = ropReg.getOrCreateRegisterSpec(destRef);
- this.sourcePosition = RopHelper.getSourcePosition(declaration);
+ this.sourcePosition = sourcePosition;
}
@Override
public boolean visit(@Nonnull JNode node) {
- throw new AssertionError(declaration.toSource() + " not yet supported.");
+ throw new AssertionError(node.toSource() + " not yet supported.");
}
@Override
@@ -273,7 +276,7 @@
List<JExpression> initializers = newArray.getInitializers();
if (!initializers.isEmpty() && initializers.size() <= 5 && newArray.getDims().size() == 1
&& elementType == JPrimitiveTypeEnum.INT.getType()) {
- return true;
+ return true;
}
return false;
}
@@ -390,7 +393,7 @@
}
}
- RopBuilderVisitor(@Nonnull RopRegisterManager ropReg, @Nonnull BasicBlock currentBasicBlock,
+ RopBuilderVisitor(@Nonnull RopRegisterManager ropReg, @Nonnull JBasicBlock currentBasicBlock,
@Nonnull AndroidApiLevel apiLevel) {
this.ropReg = ropReg;
this.currentBasicBlock = currentBasicBlock;
@@ -407,52 +410,127 @@
return extraInstructions;
}
- public void accept(@Nonnull List<JStatement> list) {
- instructions = new LinkedList<Insn>();
- extraInstructions = new LinkedList<Insn>();
+ public void processBasicBlockElements() {
+ instructions = new LinkedList<>();
+ extraInstructions = new LinkedList<>();
noMoreInstruction = false;
- super.accept(list);
+ ArrayList<JBasicBlockElement> elements =
+ Lists.newArrayList(this.currentBasicBlock.getElements(true));
+ super.accept(elements);
}
- @Override
- public boolean visit(@Nonnull JExpressionStatement exprStmt) {
- JExpression expr = exprStmt.getExpr();
-
- if (expr instanceof JPolymorphicMethodCall) {
- buildInvokePolymorphic(null, (JPolymorphicMethodCall) expr);
- } else if (expr instanceof JMethodCall) {
- buildCall(null, (JMethodCall) expr);
- } else if (expr instanceof JLocalRef) {
- // "lv;" This is a nop, do nothing
- } else if (expr instanceof JBinaryOperation) {
- JBinaryOperation binaryOperation = (JBinaryOperation) expr;
-
- // ensured by ThreeAddressCodeForm
- assert binaryOperation.getOp() == JBinaryOperator.ASG;
- JExpression lhs = binaryOperation.getLhs();
- assert (lhs instanceof JVariableRef)
- || (lhs instanceof JFieldRef)
- || (lhs instanceof JArrayRef);
-
- buildAssign(exprStmt, lhs, binaryOperation.getRhs());
- } else {
- throw new AssertionError(exprStmt.toSource() + " not yet supported.");
- }
-
+ public boolean visit(@Nonnull JGotoBlockElement element) {
return false;
}
@Override
- public boolean visit(@Nonnull JIfStatement ifStmt) {
- JExpression condExpr = ifStmt.getIfExpr();
- SourcePosition ifStmtSrcPos = RopHelper.getSourcePosition(ifStmt);
- Rop ifOp;
- RegisterSpecList sources;
- JBinaryOperator op = null;
+ public boolean visit(@Nonnull JCaseBlockElement element) {
+ return false;
+ }
- if (condExpr instanceof JBinaryOperation) {
- JBinaryOperation binCondExpr = (JBinaryOperation) condExpr;
+ @Override
+ public boolean visit(@Nonnull JThrowBlockElement element) {
+ addInstruction(new ThrowingInsn(
+ Rops.THROW, RopHelper.getSourcePosition(element.getSourceInfo()),
+ RegisterSpecList.make(getRegisterSpec(element.getExpression())), getCatchTypes()));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JStoreBlockElement element) {
+ JAsgOperation expression = element.getAssignment();
+ JExpression lhs = expression.getLhs();
+ JExpression rhs = expression.getRhs();
+
+ assert lhs instanceof JFieldRef || lhs instanceof JArrayRef;
+ assert !(rhs instanceof JExceptionRuntimeValue);
+
+ if (lhs instanceof JFieldRef) {
+ buildWriteField((JFieldRef) lhs, rhs, RopHelper.getSourcePosition(element.getSourceInfo()));
+ } else {
+ buildArrayWrite((JArrayRef) lhs, rhs, RopHelper.getSourcePosition(element.getSourceInfo()));
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JReturnBlockElement element) {
+ JExpression expression = element.getExpression();
+ RegisterSpecList sources = expression != null ?
+ RegisterSpecList.make(getRegisterSpec(expression)) :
+ RegisterSpecList.EMPTY;
+
+ JType type = expression != null ?
+ expression.getType() : JPrimitiveTypeEnum.VOID.getType();
+
+ addInstruction(new PlainInsn(
+ Rops.opReturn(RopHelper.convertTypeToDx(type)),
+ RopHelper.getSourcePosition(element.getSourceInfo()), null, sources));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JVariableAsgBlockElement element) {
+ JAsgOperation asg = element.getAssignment();
+ JVariableRef local = (JVariableRef) asg.getLhs();
+ JExpression value = asg.getRhs();
+
+ if (value instanceof JExceptionRuntimeValue) {
+ RegisterSpec exceptionReg =
+ ropReg.getOrCreateRegisterSpec(local);
+ addInstruction(new PlainInsn(
+ Rops.opMoveException(exceptionReg.getTypeBearer()),
+ RopHelper.getSourcePosition(local),
+ exceptionReg,
+ RegisterSpecList.EMPTY));
+
+ } else {
+ new AssignBuilderVisitor(
+ RopHelper.getSourcePosition(element.getSourceInfo()),
+ local).accept(value);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JMethodCallBlockElement element) {
+ buildCall(null, element.getCall());
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JPolymorphicMethodCallBlockElement element) {
+ buildInvokePolymorphic(null, element.getCall());
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JLockBlockElement element) {
+ addInstruction(new ThrowingInsn(
+ Rops.MONITOR_ENTER, RopHelper.getSourcePosition(element.getSourceInfo()),
+ RegisterSpecList.make(getRegisterSpec(element.getExpression())), getCatchTypes()));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JUnlockBlockElement element) {
+ addInstruction(new ThrowingInsn(
+ Rops.MONITOR_EXIT, RopHelper.getSourcePosition(element.getSourceInfo()),
+ RegisterSpecList.make(getRegisterSpec(element.getExpression())), getCatchTypes()));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JConditionalBlockElement element) {
+ SourcePosition ifStmtSrcPos = RopHelper.getSourcePosition(element.getSourceInfo());
+ RegisterSpecList sources;
+ JBinaryOperator op;
+
+ JExpression expr = element.getCondition();
+
+ if (expr instanceof JBinaryOperation) {
+ JBinaryOperation binCondExpr = (JBinaryOperation) expr;
JExpression right = binCondExpr.getRhs();
RegisterSpec rightReg = getRegisterSpec(right);
@@ -472,14 +550,10 @@
case FLOAT:
case DOUBLE: {
RegisterSpec dest = ropReg.createRegisterSpec(JPrimitiveTypeEnum.BOOLEAN.getType());
- Rop cmpOp = null;
Type dxType = RopHelper.convertTypeToDx(type);
- if (type == JPrimitiveTypeEnum.LONG.getType()) {
- cmpOp = Rops.opCmpl(dxType);
- } else {
- cmpOp = getCmpOperatorForFloatDouble(op, dxType);
- }
+ Rop cmpOp = (type == JPrimitiveTypeEnum.LONG.getType())
+ ? Rops.opCmpl(dxType) : getCmpOperatorForFloatDouble(op, dxType);
Insn ifInst = new PlainInsn(cmpOp, ifStmtSrcPos, dest, sources);
addInstruction(ifInst);
@@ -497,43 +571,69 @@
throw new AssertionError("Void type not supported.");
}
}
- } else if (condExpr instanceof JPrefixNotOperation) {
- RegisterSpec sourceReg = getRegisterSpec(((JPrefixNotOperation) condExpr).getArg());
+ } else if (expr instanceof JPrefixNotOperation) {
+ RegisterSpec sourceReg = getRegisterSpec(((JPrefixNotOperation) expr).getArg());
sources = RegisterSpecList.make(sourceReg);
op = JBinaryOperator.EQ;
} else {
- RegisterSpec sourceReg = getRegisterSpec(condExpr);
+ RegisterSpec sourceReg = getRegisterSpec(expr);
sources = RegisterSpecList.make(sourceReg);
op = JBinaryOperator.NEQ;
}
- assert op != null;
- FallThroughMarker ftm = ifStmt.getMarker(FallThroughMarker.class);
- if (ftm != null) {
- switch (ftm.getFallThrough()) {
- case ELSE: {
- ifOp = getOperatorForIf(op, sources);
- break;
- }
- case THEN: {
- ifOp = getReverseOperatorForIf(op, sources);
- break;
- }
- default: {
- throw new AssertionError();
- }
- }
- } else {
- ifOp = getReverseOperatorForIf(op, sources);
+ Rop ifOp = getReverseOperatorForIf(op, sources);
+ assert this.currentBasicBlock instanceof JConditionalBasicBlock;
+ if (((JConditionalBasicBlock) this.currentBasicBlock).isInverted()) {
+ ifOp = getOperatorForIf(op, sources);
}
- assert ifOp != null;
Insn ifInst = new PlainInsn(ifOp, ifStmtSrcPos, null, sources);
addInstruction(ifInst);
-
return false;
}
+ @Override
+ public boolean visit(@Nonnull JSwitchBlockElement element) {
+ assert currentBasicBlock instanceof JSwitchBasicBlock;
+
+ SourcePosition switchStmtSrcPos = RopHelper.getSourcePosition(element.getSourceInfo());
+ IntList cases = new IntList();
+ for (JBasicBlock caseBb : ((JSwitchBasicBlock) currentBasicBlock).getCases()) {
+ assert caseBb.hasElements();
+ JBasicBlockElement firstElement = caseBb.getFirstElement();
+ assert firstElement instanceof JCaseBlockElement;
+
+ JLiteral caseValue = ((JCaseBlockElement) firstElement).getLiteral();
+ if (caseValue instanceof JIntLiteral) {
+ cases.add(((JIntLiteral) caseValue).getValue());
+ } else if (caseValue instanceof JCharLiteral) {
+ cases.add(((JCharLiteral) caseValue).getValue());
+ } else if (caseValue instanceof JShortLiteral) {
+ cases.add(((JShortLiteral) caseValue).getValue());
+ } else if (caseValue instanceof JByteLiteral) {
+ cases.add(((JByteLiteral) caseValue).getValue());
+ } else {
+ throw new AssertionError("Unsupported value");
+ }
+ }
+
+ RegisterSpecList sources = RegisterSpecList.make(getRegisterSpec(element.getExpression()));
+
+ Insn switchInst = new SwitchInsn(Rops.SWITCH, switchStmtSrcPos, null, sources, cases);
+ addInstruction(switchInst);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JBasicBlockElement element) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public boolean visit(@Nonnull JNode node) {
+ throw new AssertionError("Not supported: " + node.toSource());
+ }
+
/**
* Get the @link{Rop} corresponding to the inverse of the @link{JBinaryOperator} provided for
* float or double type.
@@ -549,11 +649,11 @@
case LTE:
case LT:
return Rops.opCmpg(type);
- case GT:
- case GTE:
- case EQ:
- case NEQ:
- return Rops.opCmpl(type);
+ case GT:
+ case GTE:
+ case EQ:
+ case NEQ:
+ return Rops.opCmpl(type);
default:
throw new AssertionError("Operator " + op.toString() + " not yet supported into IfStmt.");
}
@@ -571,16 +671,16 @@
switch (op) {
case LT:
return Rops.opIfLt(sources);
- case GT:
- return Rops.opIfGt(sources);
- case LTE:
- return Rops.opIfLe(sources);
- case GTE:
- return Rops.opIfGe(sources);
- case EQ:
- return Rops.opIfEq(sources);
- case NEQ:
- return Rops.opIfNe(sources);
+ case GT:
+ return Rops.opIfGt(sources);
+ case LTE:
+ return Rops.opIfLe(sources);
+ case GTE:
+ return Rops.opIfGe(sources);
+ case EQ:
+ return Rops.opIfEq(sources);
+ case NEQ:
+ return Rops.opIfNe(sources);
default:
throw new AssertionError("Operator " + op.toString()
+ " not yet supported into IfStmt.");
@@ -599,107 +699,22 @@
switch (op) {
case LT:
return Rops.opIfGe(sources);
- case GT:
- return Rops.opIfLe(sources);
- case LTE:
- return Rops.opIfGt(sources);
- case GTE:
- return Rops.opIfLt(sources);
- case EQ:
- return Rops.opIfNe(sources);
- case NEQ:
- return Rops.opIfEq(sources);
+ case GT:
+ return Rops.opIfLe(sources);
+ case LTE:
+ return Rops.opIfGt(sources);
+ case GTE:
+ return Rops.opIfLt(sources);
+ case EQ:
+ return Rops.opIfNe(sources);
+ case NEQ:
+ return Rops.opIfEq(sources);
default:
throw new AssertionError("Operator " + op.toString()
+ " not yet supported into IfStmt.");
}
}
- @Override
- public boolean visit(@Nonnull JReturnStatement retStmt) {
- JExpression returnedExpr = retStmt.getExpr();
- RegisterSpecList sources;
-
- if (returnedExpr != null) {
- sources = RegisterSpecList.make(getRegisterSpec(returnedExpr));
- } else {
- sources = RegisterSpecList.EMPTY;
- }
-
- Insn retInst = new PlainInsn(Rops.opReturn(RopHelper.convertTypeToDx(
- returnedExpr == null ? JPrimitiveTypeEnum.VOID.getType() : returnedExpr.getType())),
- RopHelper.getSourcePosition(retStmt), null, sources);
- addInstruction(retInst);
-
- return false;
- }
-
- @Override
- public boolean visit(@Nonnull JSwitchStatement jswitch) {
- assert currentBasicBlock instanceof SwitchBasicBlock;
-
- SourcePosition switchStmtSrcPos = RopHelper.getSourcePosition(jswitch);
- IntList cases = new IntList();
- for (BasicBlock caseBb : ((SwitchBasicBlock) currentBasicBlock).getCasesBlock()) {
- JStatement firstStatement = caseBb.getStatements().get(0);
- assert firstStatement instanceof JCaseStatement;
- JLiteral caseValue = ((JCaseStatement) firstStatement).getExpr();
- if (caseValue instanceof JIntLiteral) {
- cases.add(((JIntLiteral) caseValue).getValue());
- } else if (caseValue instanceof JCharLiteral) {
- cases.add(((JCharLiteral) caseValue).getValue());
- } else if (caseValue instanceof JShortLiteral) {
- cases.add(((JShortLiteral) caseValue).getValue());
- } else if (caseValue instanceof JByteLiteral) {
- cases.add(((JByteLiteral) caseValue).getValue());
- } else {
- throw new AssertionError("Unsupported value");
- }
- }
-
- RegisterSpecList sources = RegisterSpecList.make(getRegisterSpec(jswitch.getExpr()));
-
- Insn switchInst = new SwitchInsn(Rops.SWITCH, switchStmtSrcPos, null, sources, cases);
- addInstruction(switchInst);
- return false;
- }
-
- @Override
- public boolean visit(@Nonnull JThrowStatement throwStmt) {
- Insn throwInsn = new ThrowingInsn(Rops.THROW, RopHelper.getSourcePosition(throwStmt),
- RegisterSpecList.make(getRegisterSpec(throwStmt.getExpr())),
- getCatchTypes());
-
- addInstruction(throwInsn);
-
- return false;
- }
-
- @Override
- public boolean visit(@Nonnull JLock lockStmt) {
- SourcePosition srcPosition = RopHelper.getSourcePosition(lockStmt);
- RegisterSpec lockReg = getRegisterSpec(lockStmt.getLockExpr());
-
- Insn lockInsn = new ThrowingInsn(Rops.MONITOR_ENTER, srcPosition,
- RegisterSpecList.make(lockReg), getCatchTypes());
-
- addInstruction(lockInsn);
-
- return false;
- }
-
- @Override
- public boolean visit(@Nonnull JUnlock unlockStmt) {
- RegisterSpec unlockReg = getRegisterSpec(unlockStmt.getLockExpr());
-
- Insn unlockInsn = new ThrowingInsn(Rops.MONITOR_EXIT, RopHelper.getSourcePosition(unlockStmt),
- RegisterSpecList.make(unlockReg), getCatchTypes());
-
- addInstruction(unlockInsn);
-
- return false;
- }
-
private void buildAlloc(@Nonnull RegisterSpec destReg, @Nonnull JAlloc alloc,
@Nonnull SourcePosition sourcePosition) {
addInstruction(new ThrowingCstInsn(Rops.NEW_INSTANCE, sourcePosition, RegisterSpecList.EMPTY,
@@ -707,36 +722,10 @@
addMoveResultPseudoAsExtraInstruction(destReg, sourcePosition);
}
- private void buildAssign(
- @Nonnull JStatement declaration,
- @Nonnull JExpression dest,
- @Nonnull JExpression value)
- throws AssertionError {
- if (value instanceof JExceptionRuntimeValue) {
- assert dest instanceof JVariableRef;
- assert declaration.getParent() instanceof JCatchBlock
- && ((JCatchBlock) declaration.getParent()).getStatements().get(0) == declaration;
- RegisterSpec exceptionReg = ropReg.getOrCreateRegisterSpec((JVariableRef) dest);
- addInstruction(new PlainInsn(
- Rops.opMoveException(exceptionReg.getTypeBearer()), RopHelper.getSourcePosition(dest),
- exceptionReg, RegisterSpecList.EMPTY));
- } else if (dest instanceof JFieldRef) {
- buildWriteField((JFieldRef) dest, value, RopHelper.getSourcePosition(declaration));
- } else if (dest instanceof JArrayRef) {
- buildArrayWrite((JArrayRef) dest, value, RopHelper.getSourcePosition(declaration));
- } else {
- assert dest instanceof JVariableRef;
- JVariableRef destRef = (JVariableRef) dest;
-
- JVisitor rhsHandler = new AssignBuilderVisitor(declaration, destRef);
- rhsHandler.accept(value);
- }
- }
-
private void buildArrayWrite(JArrayRef arrayRef, JExpression value,
SourcePosition sourcePosition) {
assert arrayRef.getInstance() instanceof JVariableRef
- || isNullOrReinterpretCastOfNull(arrayRef.getInstance());
+ || isNullOrReinterpretCastOfNull(arrayRef.getInstance());
RegisterSpec valueReg = getRegisterSpec(value);
RegisterSpec instanceReg = getRegisterSpec(arrayRef.getInstance());
RegisterSpec indexReg = getRegisterSpec(arrayRef.getIndexExpr());
@@ -937,7 +926,7 @@
JType type = literal.getType();
if (type instanceof JPrimitiveType) {
cst = buildPrimitiveConstant(literal);
- } else if (literal instanceof JAbstractStringLiteral){
+ } else if (literal instanceof JAbstractStringLiteral) {
cst = RopHelper.createString((JAbstractStringLiteral) literal);
} else if (literal instanceof JNullLiteral) {
cst = CstKnownNull.THE_ONE;
@@ -958,7 +947,7 @@
constOp, sourcePosition, destReg, RegisterSpecList.EMPTY,
getConstant(literal));
addInstruction(constInst);
- } else if (literal instanceof JAbstractStringLiteral){
+ } else if (literal instanceof JAbstractStringLiteral) {
constInst = new ThrowingCstInsn(constOp, sourcePosition,
RegisterSpecList.EMPTY, getCatchTypes(), getConstant(literal));
addInstruction(constInst);
@@ -982,7 +971,7 @@
Rop opcode = null;
- switch(unary.getOp()) {
+ switch (unary.getOp()) {
case NEG: {
assert unary.getType() == JPrimitiveTypeEnum.BYTE.getType()
|| unary.getType() == JPrimitiveTypeEnum.CHAR.getType()
@@ -1056,6 +1045,7 @@
&& lhs instanceof JIntegralConstant32 && ((JIntegralType32) JPrimitiveTypeEnum.SHORT
.getType()).isValidValue(((JIntegralConstant32) lhs).getIntValue())) {
sources = RegisterSpecList.make(ropReg.getOrCreateRegisterSpec((JVariableRef) rhs));
+ assert lhs instanceof JValueLiteral;
cst = getConstant((JValueLiteral) lhs);
} else {
sources = RegisterSpecList.make(getRegisterSpec(lhs),
@@ -1253,7 +1243,7 @@
sources = new RegisterSpecList(1 + methodCall.getArgs().size());
}
- switch(methodKind) {
+ switch (methodKind) {
case STATIC:
callOp = Rops.opInvokeStatic(prototype);
break;
@@ -1318,6 +1308,7 @@
return regSpec;
}
+
private void addMoveResultAsExtraInstruction(@Nonnull TypeBearer type,
@Nonnull RegisterSpec destReg, @Nonnull SourcePosition sourcePosition) {
Rop moveResultOp = Rops.opMoveResult(type);
@@ -1351,13 +1342,13 @@
* block.
*/
private TypeList getCatchTypes() {
- assert currentBasicBlock instanceof PeiBasicBlock;
- PeiBasicBlock peiBlock = (PeiBasicBlock) currentBasicBlock;
+ assert currentBasicBlock instanceof JThrowingBasicBlock;
+ JThrowingBasicBlock block = (JThrowingBasicBlock) currentBasicBlock;
List<JType> catchTypes = new ArrayList<JType>();
- for (CatchBasicBlock bb : peiBlock.getExceptionBlocks()) {
- for (JClass catchType : bb.getCatchTypes()) {
+ for (JBasicBlock bb : block.getCatchBlocks()) {
+ for (JClass catchType : ((JCatchBasicBlock) bb).getCatchTypes()) {
catchTypes.add(catchType);
}
}
@@ -1370,7 +1361,7 @@
}
@Override
- public void endVisit(@Nonnull JStatement x) {
+ public void endVisit(@Nonnull JBasicBlockElement x) {
ropReg.resetFreeTmpRegister();
}
diff --git a/jack/src/com/android/jack/backend/dex/rop/RopHelper.java b/jack/src/com/android/jack/backend/dex/rop/RopHelper.java
index d145c73..a3408c7 100644
--- a/jack/src/com/android/jack/backend/dex/rop/RopHelper.java
+++ b/jack/src/com/android/jack/backend/dex/rop/RopHelper.java
@@ -194,12 +194,24 @@
*/
@Nonnull
public static SourcePosition getSourcePosition(@Nonnull JNode stmt) {
- if (stmt.getSourceInfo() == SourceInfo.UNKNOWN) {
+ return getSourcePosition(stmt.getSourceInfo());
+ }
+
+ /**
+ * Builds a {@code SourcePosition} for a {@code SourceInfo}.
+ *
+ * @param sourceInfo
+ * The source info used to extract source position information.
+ * @return The built {@code SourcePosition}.
+ */
+ @Nonnull
+ public static SourcePosition getSourcePosition(@Nonnull SourceInfo sourceInfo) {
+ if (sourceInfo == SourceInfo.UNKNOWN) {
return SourcePosition.NO_INFO;
}
- int startLine = stmt.getSourceInfo().getStartLine();
+ int startLine = sourceInfo.getStartLine();
// Jack defines unknown line by UNKNOWN_LINE_NUMBER, but dx requires to use -1.
- return (new SourcePosition(new CstString(stmt.getSourceInfo().getFileName()), -1,
+ return (new SourcePosition(new CstString(sourceInfo.getFileName()), -1,
startLine == SourceInfo.UNKNOWN_LINE_NUMBER ? -1 : startLine));
}
diff --git a/jack/src/com/android/jack/backend/dex/rop/SsaCodeItemBuilder.java b/jack/src/com/android/jack/backend/dex/rop/SsaCodeItemBuilder.java
new file mode 100644
index 0000000..79827a3
--- /dev/null
+++ b/jack/src/com/android/jack/backend/dex/rop/SsaCodeItemBuilder.java
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.jack.backend.dex.rop;
+
+import com.google.common.collect.Lists;
+
+import com.android.jack.JackEventType;
+import com.android.jack.Options;
+import com.android.jack.dx.dex.DexOptions;
+import com.android.jack.dx.dex.code.DalvCode;
+import com.android.jack.dx.dex.code.PositionList;
+import com.android.jack.dx.dex.code.RopTranslator;
+import com.android.jack.dx.dex.file.CodeItem;
+import com.android.jack.dx.rop.code.DexTranslationAdvice;
+import com.android.jack.dx.rop.code.Insn;
+import com.android.jack.dx.rop.code.LocalVariableExtractor;
+import com.android.jack.dx.rop.code.LocalVariableInfo;
+import com.android.jack.dx.rop.code.PlainCstInsn;
+import com.android.jack.dx.rop.code.PlainInsn;
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.rop.code.Rop;
+import com.android.jack.dx.rop.code.RopMethod;
+import com.android.jack.dx.rop.code.Rops;
+import com.android.jack.dx.rop.code.SourcePosition;
+import com.android.jack.dx.rop.cst.CstInteger;
+import com.android.jack.dx.rop.type.StdTypeList;
+import com.android.jack.dx.rop.type.Type;
+import com.android.jack.dx.rop.type.TypeList;
+import com.android.jack.dx.ssa.NormalSsaInsn;
+import com.android.jack.dx.ssa.Optimizer;
+import com.android.jack.dx.ssa.Optimizer.OptionalStep;
+import com.android.jack.dx.ssa.PhiInsn;
+import com.android.jack.dx.ssa.SsaBasicBlock;
+import com.android.jack.dx.ssa.SsaInsn;
+import com.android.jack.dx.ssa.SsaMethod;
+import com.android.jack.dx.util.IntList;
+import com.android.jack.ir.SideEffectOperation;
+import com.android.jack.ir.ast.JAbstractMethodBody;
+import com.android.jack.ir.ast.JAsgOperation;
+import com.android.jack.ir.ast.JAssertStatement;
+import com.android.jack.ir.ast.JCastOperation;
+import com.android.jack.ir.ast.JConcatOperation;
+import com.android.jack.ir.ast.JConditionalExpression;
+import com.android.jack.ir.ast.JConditionalOperation;
+import com.android.jack.ir.ast.JExceptionRuntimeValue;
+import com.android.jack.ir.ast.JFieldInitializer;
+import com.android.jack.ir.ast.JLoop;
+import com.android.jack.ir.ast.JMethod;
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.JMultiExpression;
+import com.android.jack.ir.ast.JParameter;
+import com.android.jack.ir.ast.JPrimitiveType.JPrimitiveTypeEnum;
+import com.android.jack.ir.ast.JSsaVariableDefRef;
+import com.android.jack.ir.ast.JSsaVariableRef;
+import com.android.jack.ir.ast.JSwitchStatement;
+import com.android.jack.ir.ast.JThis;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JCaseBasicBlock;
+import com.android.jack.ir.ast.cfg.JCatchBasicBlock;
+import com.android.jack.ir.ast.cfg.JConditionalBasicBlock;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JEntryBasicBlock;
+import com.android.jack.ir.ast.cfg.JExitBasicBlock;
+import com.android.jack.ir.ast.cfg.JRegularBasicBlock;
+import com.android.jack.ir.ast.cfg.JReturnBasicBlock;
+import com.android.jack.ir.ast.cfg.JSimpleBasicBlock;
+import com.android.jack.ir.ast.cfg.JSwitchBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowingExpressionBasicBlock;
+import com.android.jack.ir.ast.marker.ThrownExceptionMarker;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.jack.scheduling.marker.DexCodeMarker;
+import com.android.jack.transformations.EmptyClinit;
+import com.android.jack.transformations.InvalidDefaultBridgeInInterfaceRemoved;
+import com.android.jack.transformations.ast.BooleanTestOutsideIf;
+import com.android.jack.transformations.ast.ImplicitBoxingAndUnboxing;
+import com.android.jack.transformations.ast.ImplicitCast;
+import com.android.jack.transformations.ast.InitInNewArray;
+import com.android.jack.transformations.ast.JPrimitiveClassLiteral;
+import com.android.jack.transformations.ast.MultiDimensionNewArray;
+import com.android.jack.transformations.ast.NewInstanceRemoved;
+import com.android.jack.transformations.ast.RefAsStatement;
+import com.android.jack.transformations.ast.UnassignedValues;
+import com.android.jack.transformations.ast.inner.InnerAccessor;
+import com.android.jack.transformations.ast.switches.UselessSwitches;
+import com.android.jack.transformations.cast.SourceCast;
+import com.android.jack.transformations.rop.cast.RopLegalCast;
+import com.android.jack.transformations.threeaddresscode.ThreeAddressCodeForm;
+import com.android.jack.util.AndroidApiLevel;
+import com.android.sched.item.Description;
+import com.android.sched.item.Name;
+import com.android.sched.schedulable.Constraint;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+import com.android.sched.schedulable.Use;
+import com.android.sched.util.config.ThreadConfig;
+import com.android.sched.util.log.Event;
+import com.android.sched.util.log.Tracer;
+import com.android.sched.util.log.TracerFactory;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.EnumSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * CodeItemBuilder is a schedulable that generates {@link CodeItem} from {@link JMethod}. The
+ * generated {@link CodeItem} is saved into the {@link DexCodeMarker}.
+ */
+@Description("Builds CodeItem from JMethod in SSA form")
+@Name("SsaCodeItemBuilder")
+@Constraint(need = {JSsaVariableRef.class, JMethodBodyCfg.class, JExceptionRuntimeValue.class,
+ NewInstanceRemoved.class, ThreeAddressCodeForm.class, RopLegalCast.class,
+ InnerAccessor.class, InvalidDefaultBridgeInInterfaceRemoved.class},
+ no = {BooleanTestOutsideIf.class, InitInNewArray.class, JAsgOperation.class,
+ JPrimitiveClassLiteral.class, JMultiExpression.class, JConditionalExpression.class,
+ JFieldInitializer.class, JConcatOperation.class, JLoop.class, SideEffectOperation.class,
+ UnassignedValues.class, RefAsStatement.class, MultiDimensionNewArray.class,
+ JSwitchStatement.SwitchWithEnum.class, ImplicitBoxingAndUnboxing.class,
+ ImplicitCast.class, JAssertStatement.class, JConditionalOperation.class,
+ EmptyClinit.class, UselessSwitches.class, SourceCast.class,
+ JCastOperation.WithIntersectionType.class})
+@Transform(add = DexCodeMarker.class)
+@Use(RopHelper.class)
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class SsaCodeItemBuilder implements RunnableSchedulable<JMethod> {
+ @Nonnull
+ private final com.android.jack.util.filter.Filter<JMethod> filter =
+ ThreadConfig.get(Options.METHOD_FILTER);
+ private final boolean emitSyntheticLocalDebugInfo =
+ ThreadConfig.get(CodeItemBuilder.EMIT_SYNTHETIC_LOCAL_DEBUG_INFO).booleanValue();
+ private final boolean emitLocalDebugInfo =
+ ThreadConfig.get(Options.EMIT_LOCAL_DEBUG_INFO).booleanValue();
+ private final boolean runDxOptimizations =
+ ThreadConfig.get(CodeItemBuilder.DEX_OPTIMIZE).booleanValue();
+ private final boolean forceJumbo = ThreadConfig.get(CodeItemBuilder.FORCE_JUMBO).booleanValue();
+ private final boolean removeRedundantConditionalBranch =
+ ThreadConfig.get(CodeItemBuilder.OPTIMIZE_BRANCHES).booleanValue();
+ private final AndroidApiLevel apiLevel = ThreadConfig.get(Options.ANDROID_MIN_API_LEVEL);
+ private final boolean emitLineNumberTable =
+ ThreadConfig.get(Options.EMIT_LINE_NUMBER_DEBUG_INFO).booleanValue();
+
+ @Nonnull
+ private final Tracer tracer = TracerFactory.getTracer();
+
+ @Override
+ public void run(@Nonnull JMethod method) {
+ if (method.isNative() || method.isAbstract() || !filter.accept(this.getClass(), method)) {
+ return;
+ }
+ buildSsaCodeItem(method, false);
+ }
+
+ @SuppressWarnings("boxing")
+ public void buildSsaCodeItem(@Nonnull JMethod method, boolean minimizeRegister) {
+ try (Event event = tracer.open(JackEventType.DX_BACKEND)) {
+ SsaRopRegisterManager ropReg =
+ new SsaRopRegisterManager(emitLocalDebugInfo, emitSyntheticLocalDebugInfo);
+
+ JAbstractMethodBody body = method.getBody();
+ assert body instanceof JMethodBodyCfg;
+ JControlFlowGraph cfg = ((JMethodBodyCfg) body).getCfg();
+
+ final JEntryBasicBlock entryBasicBlock = cfg.getEntryBlock();
+ final JExitBasicBlock exitBasicBlock = cfg.getExitBlock();
+
+ final Map<JBasicBlock, Integer> basicBlocks = new LinkedHashMap<>();
+ int blockId = 0; // 0 is reserved for entry block
+ // TODO(acleung): We don't need to generate blocks that is not reachable. We are only doing
+ // this for now because the Phi nodes will not have a valid prececessor otherwise. DX normally
+ // remove any unreachable nodes so this will not be a performance issue.
+ for (JBasicBlock block : cfg.getReachableBlocksDepthFirst()) {
+ if (block == exitBasicBlock) {
+ basicBlocks.put(block, Integer.MAX_VALUE);
+ } else {
+ basicBlocks.put(block, Integer.valueOf(blockId++));
+ }
+ }
+
+ int maxLabel = blockId + 2;
+ SsaMethod ssaMethod =
+ new SsaMethod(getParameterWordCount(method), method.isStatic(), maxLabel, 0);
+ final SsaRopBasicBlockManager ropBb = new SsaRopBasicBlockManager(ssaMethod, maxLabel);
+
+ JBasicBlock firstBlockOfCode = entryBasicBlock.getOnlySuccessor();
+
+ // Rop-SSA needs an extra entry block compare the ROP.
+ int firstJackBlockIndex = basicBlocks.get(firstBlockOfCode).intValue();
+ SsaBasicBlock initBb = ropBb.createBasicBlock();
+ initBb.setSuccessors(
+ IntList.makeImmutable(ropBb.getSpecialLabel(SsaRopBasicBlockManager.PARAM_ASSIGNMENT)),
+ ropBb.getSpecialLabel(SsaRopBasicBlockManager.PARAM_ASSIGNMENT));
+ List<SsaInsn> insns = Lists.newArrayList();
+ insns.add(new NormalSsaInsn(
+ new PlainInsn(Rops.GOTO, SourcePosition.NO_INFO, null, RegisterSpecList.EMPTY), initBb));
+ initBb.setInsns(insns);
+ initBb.setRopLabel(ropBb.getSpecialLabel(SsaRopBasicBlockManager.SSA_INIT));
+
+ addSetupBlocks(method, ropReg, ropBb, firstJackBlockIndex);
+
+ if (method.getType() != JPrimitiveTypeEnum.VOID.getType()) {
+ ropReg.createReturnReg(method.getType());
+ }
+
+ for (JBasicBlock b : basicBlocks.keySet()) {
+ if (b instanceof JExitBasicBlock || b instanceof JEntryBasicBlock) {
+ continue;
+ }
+ assert b != entryBasicBlock && b != exitBasicBlock;
+ JRegularBasicBlock bb = (JRegularBasicBlock) b;
+
+ final SsaBasicBlock ssaBb = ropBb.createBasicBlock();
+ final SsaRopBuilderVisitor ropBuilder =
+ new SsaRopBuilderVisitor(ropReg, bb, ssaBb, basicBlocks, apiLevel);
+
+ ropBuilder.processBasicBlockElements();
+ final List<SsaInsn> instructions = ropBuilder.getInstructions();
+ assert instructions != null;
+
+ if (bb instanceof JReturnBasicBlock) {
+ List<SsaInsn> il = instructions;
+ ssaBb.setRopLabel(basicBlocks.get(bb));
+ ssaBb.setInsns(il);
+ ssaBb.setSuccessors(IntList.EMPTY, -1);
+
+ } else if (bb instanceof JThrowBasicBlock) {
+ List<SsaInsn> il = instructions;
+
+ IntList successors = new IntList();
+ int primarySuccessor = -1;
+
+ if (!((JThrowBasicBlock) bb).getCatchBlocks().isEmpty()) {
+ addCatchBlockSuccessors(basicBlocks, ((JThrowBasicBlock) bb).getCatchBlocks(),
+ successors);
+ }
+ successors.setImmutable();
+ ssaBb.setRopLabel(basicBlocks.get(bb));
+ ssaBb.setInsns(il);
+ ssaBb.setSuccessors(successors, primarySuccessor);
+
+ } else if (bb instanceof JThrowingExpressionBasicBlock) {
+ SsaInsn lastInstruction = instructions.get(instructions.size() - 1);
+ List<Insn> extraInstructions = ropBuilder.getExtraInstructions();
+ assert extraInstructions != null;
+
+ List<SsaInsn> il = instructions;
+
+ int extraBlockLabel = ropBb.getAvailableLabel();
+
+ IntList successors = new IntList();
+ addCatchBlockSuccessors(basicBlocks,
+ ((JThrowingExpressionBasicBlock) bb).getCatchBlocks(), successors);
+
+ successors.add(extraBlockLabel);
+ successors.setImmutable();
+
+ ssaBb.setRopLabel(basicBlocks.get(bb));
+ ssaBb.setInsns(il);
+ ssaBb.setSuccessors(successors, extraBlockLabel);
+
+ boolean needsGoto;
+ SourcePosition sourcePosition;
+
+ SsaBasicBlock extraSsaBb = ropBb.createBasicBlock();
+
+ if (extraInstructions.isEmpty()) {
+ needsGoto = true;
+ sourcePosition = lastInstruction.getOriginalRopInsn().getPosition();
+ il = new ArrayList<SsaInsn>(1);
+ } else {
+ Insn extraInsn = extraInstructions.get(0);
+ needsGoto = extraInstructions.get(extraInstructions.size() - 1).getOpcode()
+ .getBranchingness() == Rop.BRANCH_NONE;
+ il = new ArrayList<SsaInsn>(extraInstructions.size() + (needsGoto ? 1 : 0));
+ for (Insn inst : extraInstructions) {
+ il.add(new NormalSsaInsn(inst, extraSsaBb));
+ }
+ sourcePosition = extraInsn.getPosition();
+ }
+
+ if (needsGoto) {
+ il.add(new NormalSsaInsn(
+ new PlainInsn(Rops.GOTO, sourcePosition, null, RegisterSpecList.EMPTY),
+ extraSsaBb));
+ }
+
+ JBasicBlock primary = bb.getPrimarySuccessor();
+ successors = IntList.makeImmutable(basicBlocks.get(primary));
+ extraSsaBb.setRopLabel(extraBlockLabel);
+ extraSsaBb.setInsns(il);
+ extraSsaBb.setSuccessors(successors, basicBlocks.get(primary));
+
+ } else if (bb instanceof JConditionalBasicBlock) {
+ List<SsaInsn> il = instructions;
+
+ JBasicBlock primary = bb.getPrimarySuccessor();
+ JBasicBlock secondary = ((JConditionalBasicBlock) bb).getAlternativeSuccessor();
+
+ int primarySuccessor = basicBlocks.get(primary);
+ IntList successors = IntList.makeImmutable(primarySuccessor, basicBlocks.get(secondary));
+
+ ssaBb.setRopLabel(basicBlocks.get(bb));
+ ssaBb.setInsns(il);
+ ssaBb.setSuccessors(successors, primarySuccessor);
+
+ } else if (bb instanceof JSwitchBasicBlock) {
+ IntList successors = new IntList();
+ for (JBasicBlock caseBb : ((JSwitchBasicBlock) bb).getCases()) {
+ successors.add(basicBlocks.get(caseBb));
+ }
+
+ successors.add(basicBlocks.get(((JSwitchBasicBlock) bb).getDefaultCase()));
+
+ successors.setImmutable();
+ List<SsaInsn> il = instructions;
+
+ ssaBb.setRopLabel(basicBlocks.get(bb));
+ ssaBb.setInsns(il);
+ ssaBb.setSuccessors(successors, successors.get(successors.size() - 1));
+ } else {
+ assert bb instanceof JSimpleBasicBlock || bb instanceof JCatchBasicBlock
+ || bb instanceof JCaseBasicBlock;
+ assert bb.hasPrimarySuccessor();
+
+ JBasicBlock primarySuccessor = bb.getPrimarySuccessor();
+ IntList successors = IntList.makeImmutable(basicBlocks.get(primarySuccessor));
+ List<SsaInsn> il = createInsnList(instructions, 1);
+ Insn gotoInstruction =
+ new PlainInsn(Rops.GOTO, getLastElementPosition(bb), null, RegisterSpecList.EMPTY);
+ il.add(new NormalSsaInsn(gotoInstruction, ssaBb));
+ ssaBb.setRopLabel(basicBlocks.get(bb));
+ ssaBb.setInsns(il);
+ ssaBb.setSuccessors(successors, basicBlocks.get(primarySuccessor));
+ }
+ }
+
+ ssaMethod.setBlocks(ropBb.computeSsaBasicBlockList());
+ ssaMethod.setEntryBlockIndex(initBb.getIndex());
+ ssaMethod.setRegisterCount(ropReg.getRegisterCount());
+ ssaMethod.makeExitBlock();
+
+ edgeSplitMoveExceptionsAndResults(ssaMethod);
+
+ RopMethod ropMethod = null;
+
+ if (runDxOptimizations) {
+ if (!minimizeRegister) {
+ ropMethod = Optimizer.optimize(ssaMethod, getParameterWordCount(method),
+ method.isStatic(), true /* inPreserveLocals */, removeRedundantConditionalBranch,
+ DexTranslationAdvice.THE_ONE);
+ if (ropMethod.getBlocks().getRegCount() > DexTranslationAdvice.THE_ONE
+ .getMaxOptimalRegisterCount()) {
+ // Try to see if we can squeeze it under the register count bar
+ buildSsaCodeItem(method, true);
+ // Abort the current SSA-ROP code generation, otherwise we end up with duplicates.
+ return;
+ }
+ } else {
+ EnumSet<OptionalStep> steps = EnumSet.allOf(OptionalStep.class);
+ steps.remove(OptionalStep.CONST_COLLECTOR);
+ ropMethod = Optimizer.optimizeMinimizeRegisters(ssaMethod, getParameterWordCount(method),
+ method.isStatic(), true /* inPreserveLocals */, removeRedundantConditionalBranch,
+ DexTranslationAdvice.THE_ONE);
+ }
+ } else {
+ ropMethod = Optimizer.optimize(ssaMethod, getParameterWordCount(method), method.isStatic(),
+ true /* inPreserveLocals */, removeRedundantConditionalBranch,
+ DexTranslationAdvice.THE_ONE, EnumSet.noneOf(OptionalStep.class));
+ }
+
+ DalvCode dalvCode;
+ try (Event dopEvent = tracer.open(JackEventType.DOP_CREATION)) {
+ dalvCode = createCode(method, ropMethod);
+ }
+
+ method.addMarker(new DexCodeMarker(new CodeItem(RopHelper.createMethodRef(method), dalvCode,
+ method.isStatic(), createThrows(method))));
+ }
+ }
+
+ private SourcePosition getLastElementPosition(@Nonnull JBasicBlock bb) {
+ return RopHelper.getSourcePosition(
+ bb.hasElements() ? bb.getLastElement().getSourceInfo() : SourceInfo.UNKNOWN);
+ }
+
+ private void addCatchBlockSuccessors(Map<JBasicBlock, Integer> basicBlocks,
+ @Nonnull List<JBasicBlock> catchBlocks, @Nonnull IntList successors) {
+ for (JBasicBlock catchBlock : catchBlocks) {
+ if (!(catchBlock instanceof JCatchBasicBlock)) {
+ // We must have either some Phi nodes here or just empty gotos.
+ assert catchBlock.getSuccessors().size() == 1;
+ catchBlock = catchBlock.getSuccessors().get(0);
+ }
+ int catchTypeCount = 0;
+ int catchTypesSize = ((JCatchBasicBlock) catchBlock).getCatchTypes().size();
+ while (catchTypeCount++ < catchTypesSize) {
+ successors.add(basicBlocks.get(catchBlock).intValue());
+ }
+ }
+ }
+
+ @Nonnull
+ private static TypeList createThrows(@Nonnull JMethod method) {
+ ThrownExceptionMarker marker = method.getMarker(ThrownExceptionMarker.class);
+ if (marker != null) {
+ return RopHelper.createTypeList(marker.getThrownExceptions());
+ } else {
+ return StdTypeList.EMPTY;
+ }
+ }
+
+ @Nonnull
+ private List<SsaInsn> createInsnList(@Nonnull List<SsaInsn> instructions,
+ @Nonnegative int extraSize) {
+ List<SsaInsn> il = Lists.newArrayListWithCapacity(instructions.size() + extraSize);
+ for (SsaInsn instruction : instructions) {
+ il.add(instruction);
+ }
+ return il;
+ }
+
+ /**
+ * Constructs and adds the blocks that perform setup for the rest of the method. This includes a
+ * first block which merely contains assignments from parameters to the same-numbered registers
+ * and a possible second block which deals with synchronization.
+ */
+ // TODO(mikaelpeltier) keep local variable information if required
+ private void addSetupBlocks(@Nonnull JMethod method, @Nonnull SsaRopRegisterManager ropReg,
+ @Nonnull SsaRopBasicBlockManager ropBb, @Nonnegative int entryNodeId) {
+ SsaBasicBlock ssaBb = ropBb.createBasicBlock();
+ SourcePosition pos = SourcePosition.NO_INFO;
+
+ List<JParameter> parameters = method.getParams();
+ int sz = parameters.size();
+ List<SsaInsn> insns;
+
+ if (method.isStatic()) {
+ // +1 is to reserve space for Goto instruction
+ insns = new ArrayList<SsaInsn>(sz + 1);
+ } else {
+ // +2 is to reserve space for Goto instruction, and parameter 'this'
+ insns = new ArrayList<SsaInsn>(sz + 2);
+ JThis jThis = method.getThis();
+ assert jThis != null;
+ RegisterSpec thisReg = ropReg.createThisReg(jThis);
+ Insn insn = new PlainCstInsn(Rops.opMoveParam(thisReg.getType()), pos, thisReg,
+ RegisterSpecList.EMPTY, CstInteger.make(thisReg.getReg()));
+ insns.add(new NormalSsaInsn(insn, ssaBb));
+ }
+
+ JMethodBodyCfg body = (JMethodBodyCfg) method.getBody();
+ assert body != null;
+ for (JSsaVariableDefRef def : body.getSsaParamsDefs()) {
+ RegisterSpec paramReg = ropReg.getOrCreateRegisterSpec(def);
+ Insn insn = new PlainCstInsn(Rops.opMoveParam(paramReg.getType()), pos, paramReg,
+ RegisterSpecList.EMPTY, CstInteger.make(paramReg.getReg()));
+ insns.add(new NormalSsaInsn(insn, ssaBb));
+ }
+
+ insns
+ .add(new NormalSsaInsn(new PlainInsn(Rops.GOTO, pos, null, RegisterSpecList.EMPTY), ssaBb));
+
+ ssaBb.setRopLabel(ropBb.getSpecialLabel(SsaRopBasicBlockManager.PARAM_ASSIGNMENT));
+ ssaBb.setInsns(insns);
+ ssaBb.setSuccessors(IntList.makeImmutable(entryNodeId), entryNodeId);
+ }
+
+ @Nonnull
+ private DalvCode createCode(@Nonnull JMethod method, @Nonnull RopMethod ropMethod) {
+ DexOptions options = new DexOptions(apiLevel, forceJumbo);
+ int paramSize = getParameterWordCount(method);
+ int positionListKind;
+ LocalVariableInfo lvInfo;
+ if (emitLocalDebugInfo) {
+ lvInfo = LocalVariableExtractor.extract(ropMethod);
+ } else {
+ lvInfo = null;
+ }
+ if (emitLineNumberTable) {
+ positionListKind = PositionList.LINES;
+ } else {
+ positionListKind = PositionList.NONE;
+ }
+
+ return RopTranslator.translate(ropMethod, positionListKind, lvInfo, paramSize, options);
+ }
+
+ @Nonnegative
+ private int getParameterWordCount(@Nonnull JMethod method) {
+ // Add size in word (1) to represent 'this' parameter if method is not static.
+ int wordCount = method.isStatic() ? 0 : Type.OBJECT.getWordCount();
+
+ for (JParameter param : method.getParams()) {
+ wordCount += RopHelper.convertTypeToDx(param.getType()).getWordCount();
+ }
+
+ return wordCount;
+ }
+
+ private static void edgeSplitMoveExceptionsAndResults(@Nonnull SsaMethod ssaMeth) {
+ ArrayList<SsaBasicBlock> blocks = ssaMeth.getBlocks();
+ /*
+ * New blocks are added to the end of the block list during this iteration.
+ */
+ for (int i = blocks.size() - 1; i >= 0; i--) {
+ SsaBasicBlock block = blocks.get(i);
+
+ /*
+ * Any block that starts with a move-exception and has more than one predecessor...
+ */
+ if (!block.isExitBlock() && block.getPredecessors().cardinality() > 1) {
+ int moveExceptionIndex = 0;
+ while (moveExceptionIndex < block.getInsns().size()
+ && block.getInsns().get(moveExceptionIndex) instanceof PhiInsn) {
+ moveExceptionIndex++;
+ }
+
+ if (moveExceptionIndex == block.getInsns().size()
+ || !block.getInsns().get(moveExceptionIndex).isMoveException()) {
+ continue;
+ }
+
+ // block.getPredecessors() is changed in the loop below.
+ BitSet preds = (BitSet) block.getPredecessors().clone();
+ for (int j = preds.nextSetBit(0); j >= 0; j = preds.nextSetBit(j + 1)) {
+ SsaBasicBlock predecessor = blocks.get(j);
+ SsaBasicBlock zNode = predecessor.insertNewSuccessor(block);
+
+ /*
+ * Make sure to place the move-exception as the first insn.
+ */
+ zNode.getInsns().add(0, block.getInsns().get(moveExceptionIndex).clone());
+
+ for (int p = 0; p < moveExceptionIndex; p++) {
+ // It must be a phi given how moveExceptionIndex is computed.
+ PhiInsn phi = (PhiInsn) block.getInsns().get(p);
+ phi.changeOperandPred(predecessor, zNode);
+ }
+ }
+
+ // Remove the move-exception from the original block.
+ block.getInsns().remove(moveExceptionIndex);
+
+ }
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/backend/dex/rop/SsaRopBasicBlockManager.java b/jack/src/com/android/jack/backend/dex/rop/SsaRopBasicBlockManager.java
new file mode 100644
index 0000000..f2b9064
--- /dev/null
+++ b/jack/src/com/android/jack/backend/dex/rop/SsaRopBasicBlockManager.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.backend.dex.rop;
+
+import com.android.jack.dx.ssa.PhiInsn;
+import com.android.jack.dx.ssa.PhiInsn.Visitor;
+import com.android.jack.dx.ssa.SsaBasicBlock;
+import com.android.jack.dx.ssa.SsaMethod;
+import com.android.jack.dx.util.IntList;
+
+import java.util.ArrayList;
+
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+class SsaRopBasicBlockManager {
+
+ /** label offset for the parameter assignment block */
+ static final int PARAM_ASSIGNMENT = -1;
+ /** label offset for the return block */
+ static final int RETURN = -2;
+ /** Reserved for SSA init */
+ static final int SSA_INIT = -3;
+ /** number of special label offsets */
+ static final int SPECIAL_LABEL_COUNT = 7;
+ /** max label (exclusive) of any original code block */
+ @Nonnegative
+ private final int maxLabel;
+ /** output block list in-progress */
+ @Nonnull
+ private final ArrayList<SsaBasicBlock> basicBlocks;
+ /** Result rop method */
+ @Nonnull
+ private final SsaMethod ssaMethod;
+
+ private boolean resolved = false;
+
+ SsaRopBasicBlockManager(SsaMethod ssaMethod, @Nonnegative int maxLabel) {
+ this.maxLabel = maxLabel;
+ this.ssaMethod = ssaMethod;
+
+ /*
+ * The "* 2 + 10" below is to conservatively believe that every block is an exception handler
+ * target and should also take care of enough other possible extra overhead such that the
+ * underlying array is unlikely to need resizing.
+ */
+ basicBlocks = new ArrayList<SsaBasicBlock>(maxLabel * 2 + 10);
+ }
+
+ @Nonnull
+ SsaBasicBlock createBasicBlock() {
+ assert !resolved;
+ SsaBasicBlock bb = new SsaBasicBlock(basicBlocks.size(), ssaMethod);
+ basicBlocks.add(bb);
+ return bb;
+ }
+
+ @Nonnull
+ ArrayList<SsaBasicBlock> computeSsaBasicBlockList() {
+ /*
+ * We start allocating index at maxlabel * 2 so, again, we can be believe that we only need
+ * maxLabel number of extra index.
+ */
+ int[] labelToIndex = new int[maxLabel * 3 + 10];
+ resolveBlockIndex(labelToIndex);
+ resolvePhiIndex(labelToIndex);
+ resolved = true;
+ return basicBlocks;
+ }
+
+ private void resolveBlockIndex(int[] labelToIndex) {
+ assert !resolved;
+
+ for (SsaBasicBlock bb : basicBlocks) {
+ labelToIndex[bb.getRopLabel()] = bb.getIndex();
+ }
+
+ for (SsaBasicBlock bb : basicBlocks) {
+ IntList oldSuccessors = bb.getSuccessorList();
+ IntList newSuccessorsList = new IntList(oldSuccessors.size());
+
+ for (int i = 0, size = oldSuccessors.size(); i < size; i++) {
+ int oldSuccessor = oldSuccessors.get(i);
+ int successorIdx = labelToIndex[oldSuccessor];
+ SsaBasicBlock successor = basicBlocks.get(successorIdx);
+ successor.addPredeccessors(bb.getIndex());
+ newSuccessorsList.add(successorIdx);
+ }
+ if (bb.getPrimarySuccessorIndex() == -1) {
+ bb.setSuccessors(newSuccessorsList, -1);
+ } else {
+ bb.setSuccessors(newSuccessorsList, labelToIndex[bb.getPrimarySuccessorIndex()]);
+ }
+ }
+ }
+
+ private void resolvePhiIndex(final int[] labelToIndex) {
+ assert !resolved;
+ for (final SsaBasicBlock bb : basicBlocks) {
+ bb.forEachPhiInsn(new Visitor() {
+ @Override
+ public void visitPhiInsn(PhiInsn phi) {
+ phi.resolveOperandBlockIndex(labelToIndex);
+ }
+ });
+ }
+ }
+
+ /**
+ * Gets the minimum label for unreserved use.
+ *
+ * @return the minimum label
+ */
+ @Nonnegative
+ private int getMinimumUnreservedLabel() {
+ /*
+ * The labels below ((maxLabel * 2) + SPECIAL_LABEL_COUNT) are reserved for particular uses.
+ */
+ return (maxLabel * 2) + SsaRopBasicBlockManager.SPECIAL_LABEL_COUNT;
+ }
+
+ /**
+ * Gets an arbitrary unreserved and available label.
+ *
+ * @return the label
+ */
+ @Nonnegative
+ int getAvailableLabel() {
+ int candidate = getMinimumUnreservedLabel();
+
+ for (SsaBasicBlock bb : basicBlocks) {
+ int label = bb.getRopLabel();
+ if (label >= candidate) {
+ candidate = label + 1;
+ }
+ }
+
+ return candidate;
+ }
+
+ /**
+ * Gets the label for the given special-purpose block. The given label should be one of the static
+ * constants defined by this class.
+ *
+ * @param label {@code < 0;} the special label constant
+ * @return the actual label value to use
+ */
+ @Nonnegative
+ int getSpecialLabel(int label) {
+ assert label < 0 : "label is supposed to be negative";
+ /*
+ * The label is bitwise-complemented so that mistakes where LABEL is used instead of
+ * getSpecialLabel(LABEL) cause a failure at block construction time, since negative labels are
+ * illegal. We multiply maxLabel by 2 since 0..maxLabel (exclusive) are the original blocks and
+ * maxLabel..(maxLabel*2) are reserved for exception handler setup blocks (see
+ * getExceptionSetupLabel(), above).
+ */
+ return (maxLabel * 2) + ~label;
+ }
+}
diff --git a/jack/src/com/android/jack/backend/dex/rop/SsaRopBuilderVisitor.java b/jack/src/com/android/jack/backend/dex/rop/SsaRopBuilderVisitor.java
new file mode 100644
index 0000000..1a99287
--- /dev/null
+++ b/jack/src/com/android/jack/backend/dex/rop/SsaRopBuilderVisitor.java
@@ -0,0 +1,1431 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.backend.dex.rop;
+
+import com.google.common.collect.Lists;
+
+import com.android.jack.backend.dex.invokecustom.InvokeCustomHelper;
+import com.android.jack.dx.rop.code.FillArrayDataInsn;
+import com.android.jack.dx.rop.code.Insn;
+import com.android.jack.dx.rop.code.PlainCstInsn;
+import com.android.jack.dx.rop.code.PlainInsn;
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.code.RegisterSpecList;
+import com.android.jack.dx.rop.code.Rop;
+import com.android.jack.dx.rop.code.Rops;
+import com.android.jack.dx.rop.code.SourcePosition;
+import com.android.jack.dx.rop.code.SwitchInsn;
+import com.android.jack.dx.rop.code.ThrowingCstInsn;
+import com.android.jack.dx.rop.code.ThrowingDualCstInsn;
+import com.android.jack.dx.rop.code.ThrowingInsn;
+import com.android.jack.dx.rop.cst.Constant;
+import com.android.jack.dx.rop.cst.CstBoolean;
+import com.android.jack.dx.rop.cst.CstCallSiteRef;
+import com.android.jack.dx.rop.cst.CstDouble;
+import com.android.jack.dx.rop.cst.CstFieldRef;
+import com.android.jack.dx.rop.cst.CstFloat;
+import com.android.jack.dx.rop.cst.CstInteger;
+import com.android.jack.dx.rop.cst.CstKnownNull;
+import com.android.jack.dx.rop.cst.CstLiteral32;
+import com.android.jack.dx.rop.cst.CstLiteral64;
+import com.android.jack.dx.rop.cst.CstLong;
+import com.android.jack.dx.rop.cst.CstMethodRef;
+import com.android.jack.dx.rop.cst.CstPrototypeRef;
+import com.android.jack.dx.rop.cst.CstString;
+import com.android.jack.dx.rop.type.Prototype;
+import com.android.jack.dx.rop.type.StdTypeList;
+import com.android.jack.dx.rop.type.Type;
+import com.android.jack.dx.rop.type.TypeBearer;
+import com.android.jack.dx.rop.type.TypeList;
+import com.android.jack.dx.ssa.NormalSsaInsn;
+import com.android.jack.dx.ssa.PhiInsn;
+import com.android.jack.dx.ssa.SsaBasicBlock;
+import com.android.jack.dx.ssa.SsaInsn;
+import com.android.jack.dx.util.IntList;
+import com.android.jack.ir.ast.FieldKind;
+import com.android.jack.ir.ast.JAbsentArrayDimension;
+import com.android.jack.ir.ast.JAbstractStringLiteral;
+import com.android.jack.ir.ast.JAlloc;
+import com.android.jack.ir.ast.JAnnotation;
+import com.android.jack.ir.ast.JArrayLength;
+import com.android.jack.ir.ast.JArrayRef;
+import com.android.jack.ir.ast.JAsgOperation;
+import com.android.jack.ir.ast.JBinaryOperation;
+import com.android.jack.ir.ast.JBinaryOperator;
+import com.android.jack.ir.ast.JBooleanLiteral;
+import com.android.jack.ir.ast.JByteLiteral;
+import com.android.jack.ir.ast.JCharLiteral;
+import com.android.jack.ir.ast.JClass;
+import com.android.jack.ir.ast.JClassLiteral;
+import com.android.jack.ir.ast.JDoubleLiteral;
+import com.android.jack.ir.ast.JDynamicCastOperation;
+import com.android.jack.ir.ast.JExceptionRuntimeValue;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JFieldRef;
+import com.android.jack.ir.ast.JFloatLiteral;
+import com.android.jack.ir.ast.JInstanceOf;
+import com.android.jack.ir.ast.JIntLiteral;
+import com.android.jack.ir.ast.JIntegralConstant32;
+import com.android.jack.ir.ast.JInterface;
+import com.android.jack.ir.ast.JLambda;
+import com.android.jack.ir.ast.JLiteral;
+import com.android.jack.ir.ast.JLongLiteral;
+import com.android.jack.ir.ast.JMethodCall;
+import com.android.jack.ir.ast.JMethodCall.DispatchKind;
+import com.android.jack.ir.ast.JNewArray;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JNullLiteral;
+import com.android.jack.ir.ast.JPolymorphicMethodCall;
+import com.android.jack.ir.ast.JPrefixNotOperation;
+import com.android.jack.ir.ast.JPrimitiveType;
+import com.android.jack.ir.ast.JPrimitiveType.JPrimitiveTypeEnum;
+import com.android.jack.ir.ast.JReferenceType;
+import com.android.jack.ir.ast.JReinterpretCastOperation;
+import com.android.jack.ir.ast.JShortLiteral;
+import com.android.jack.ir.ast.JSsaVariableRef;
+import com.android.jack.ir.ast.JThisRef;
+import com.android.jack.ir.ast.JType;
+import com.android.jack.ir.ast.JUnaryOperation;
+import com.android.jack.ir.ast.JValueLiteral;
+import com.android.jack.ir.ast.JVariableRef;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.ast.MethodKind;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JCaseBlockElement;
+import com.android.jack.ir.ast.cfg.JCatchBasicBlock;
+import com.android.jack.ir.ast.cfg.JConditionalBasicBlock;
+import com.android.jack.ir.ast.cfg.JConditionalBlockElement;
+import com.android.jack.ir.ast.cfg.JGotoBlockElement;
+import com.android.jack.ir.ast.cfg.JLockBlockElement;
+import com.android.jack.ir.ast.cfg.JMethodCallBlockElement;
+import com.android.jack.ir.ast.cfg.JPhiBlockElement;
+import com.android.jack.ir.ast.cfg.JPolymorphicMethodCallBlockElement;
+import com.android.jack.ir.ast.cfg.JReturnBlockElement;
+import com.android.jack.ir.ast.cfg.JStoreBlockElement;
+import com.android.jack.ir.ast.cfg.JSwitchBasicBlock;
+import com.android.jack.ir.ast.cfg.JSwitchBlockElement;
+import com.android.jack.ir.ast.cfg.JThrowBlockElement;
+import com.android.jack.ir.ast.cfg.JThrowingBasicBlock;
+import com.android.jack.ir.ast.cfg.JUnlockBlockElement;
+import com.android.jack.ir.ast.cfg.JVariableAsgBlockElement;
+import com.android.jack.ir.types.JIntegralType32;
+import com.android.jack.util.AndroidApiLevel;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+class SsaRopBuilderVisitor extends JVisitor {
+
+ @Nonnull
+ private final AndroidApiLevel apiLevel;
+
+ @Nonnull
+ private final SsaRopRegisterManager ropReg;
+
+ @CheckForNull
+ private List<SsaInsn> instructions;
+
+ @CheckForNull
+ private List<Insn> extraInstructions;
+
+ @Nonnull
+ private final JBasicBlock currentBasicBlock;
+
+ @Nonnull
+ private final SsaBasicBlock ssaBb;
+
+ @Nonnull
+ private final Map<JBasicBlock, Integer> labelMap;
+
+ /**
+ * A guard for {@code instructions}. Does not protect {@code extraInstructions}.
+ */
+ private boolean noMoreInstruction = true;
+
+ private class AssignBuilderVisitor extends JVisitor {
+ @Nonnull
+ private final JSsaVariableRef destRef;
+ @Nonnull
+ private final RegisterSpec destReg;
+ @Nonnull
+ private final SourcePosition sourcePosition;
+
+ public AssignBuilderVisitor(
+ @Nonnull SourcePosition sourcePosition,
+ @Nonnull JSsaVariableRef destRef) {
+ this.destRef = destRef;
+ this.destReg = ropReg.getOrCreateRegisterSpec(destRef);
+ this.sourcePosition = sourcePosition;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JNode node) {
+ throw new AssertionError(node.toSource() + " not yet supported.");
+ }
+
+ @Override
+ public boolean visit(@Nonnull JAlloc alloc) {
+ buildAlloc(destReg, alloc, sourcePosition);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JArrayLength arrayLength) {
+ buildArrayLength(destReg, arrayLength);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JArrayRef arrayRef) {
+ buildArrayRead(destReg, arrayRef, sourcePosition);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JBinaryOperation binOp) {
+ buildBinaryOperation(destReg, binOp);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JReinterpretCastOperation cast) {
+ // Nothing to do it is a nop operation, generate it by a mov instruction.
+ SourcePosition sourcePosition = RopHelper.getSourcePosition(cast);
+ RegisterSpec fromReg = getRegisterSpec(cast.getExpr());
+ RegisterSpecList sources = RegisterSpecList.make(fromReg);
+ addInstruction(new PlainInsn(
+ Rops.opMove(fromReg.getTypeBearer()), sourcePosition,
+ destReg, sources));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JDynamicCastOperation cast) {
+ buildCast(destReg, cast);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JFieldRef fieldRef) {
+ buildReadField(destReg, fieldRef, sourcePosition);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JInstanceOf instanceOf) {
+ buildInstanceOf(destReg, instanceOf);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JLambda lambda) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public boolean visit(@Nonnull JPolymorphicMethodCall methodCall) {
+ buildInvokePolymorphic(destReg, methodCall);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JMethodCall call) {
+ buildCall(destReg, call);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JVariableRef varRef) {
+ throw new RuntimeException("Code is not in SSA form.");
+ }
+
+ @Override
+ public boolean visit(@Nonnull JThisRef thisRef) {
+ RegisterSpec valueReg = ropReg.getThisReg();
+ RegisterSpecList sources = RegisterSpecList.make(valueReg);
+ addInstruction(
+ new PlainInsn(Rops.opMove(valueReg.getTypeBearer()), sourcePosition, destReg, sources));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JSsaVariableRef varRef) {
+ RegisterSpec valueReg = ropReg.getOrCreateRegisterSpec(varRef);
+ RegisterSpecList sources = RegisterSpecList.make(valueReg);
+ RegisterSpec copyDestReg;
+ if (destReg.getLocalItem() == null && valueReg.getLocalItem() != null) {
+ copyDestReg = ropReg.getOrCreateRegisterSpec(destRef, valueReg.getLocalItem());
+ } else {
+ copyDestReg = destReg;
+ }
+ addInstruction(new PlainInsn(Rops.opMove(valueReg.getTypeBearer()), sourcePosition,
+ copyDestReg, sources));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JUnaryOperation unaryOp) {
+ buildUnaryOperation(destReg, unaryOp);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JValueLiteral valueLit) {
+ buildConstant(destReg, valueLit);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JClassLiteral literal) {
+ Rop constOp = Rops.opConst(RopHelper.convertTypeToDx(literal.getType()));
+ SourcePosition literalSrcPos = RopHelper.getSourcePosition(literal);
+ Insn constInst = new ThrowingCstInsn(constOp, literalSrcPos,
+ RegisterSpecList.EMPTY, getCatchTypes(), RopHelper.convertTypeToDx(literal.getRefType()));
+ addInstruction(constInst);
+ addMoveResultPseudoAsExtraInstruction(destReg, literalSrcPos);
+ return false;
+ }
+
+ private boolean isDexFilledNewArrayCompatible(@Nonnull JNewArray newArray) {
+ JType elementType = newArray.getArrayType().getElementType();
+ List<JExpression> initializers = newArray.getInitializers();
+ if (!initializers.isEmpty() && initializers.size() <= 5 && newArray.getDims().size() == 1
+ && elementType == JPrimitiveTypeEnum.INT.getType()) {
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JNewArray newArray) {
+ Type dxType = RopHelper.convertTypeToDx(newArray.getType());
+ SourcePosition newArraySourcePosition = RopHelper.getSourcePosition(newArray);
+ List<JExpression> valuesSize = newArray.getInitializers();
+
+ if (isDexFilledNewArrayCompatible(newArray)) {
+ // Array with few initializer uses filled-new-array instructions
+ int i = 0;
+ RegisterSpecList sources = new RegisterSpecList(valuesSize.size());
+ for (JExpression expr : valuesSize) {
+ sources.set(i++, getRegisterSpec(expr));
+ }
+
+ Type arrayType = RopHelper.convertTypeToDx(newArray.getType());
+ Rop op = Rops.opFilledNewArray(arrayType, valuesSize.size());
+
+ Insn insn =
+ new ThrowingCstInsn(op, newArraySourcePosition, sources, getCatchTypes(), dxType);
+ addInstruction(insn);
+ addMoveResultAsExtraInstruction(arrayType, destReg, newArraySourcePosition);
+ } else {
+ // Uses instructions new-array and fill-array-data
+
+ List<JExpression> dims = newArray.getDims();
+ assert dims.size() >= 1;
+ assert isDexNewArrayCompatible(newArray);
+ RegisterSpecList sources =
+ RegisterSpecList.make(getRegisterSpec(dims.get(0)));
+
+ Rop op = Rops.opNewArray(dxType);
+
+ Insn insn =
+ new ThrowingCstInsn(op, newArraySourcePosition, sources, getCatchTypes(), dxType);
+ addInstruction(insn);
+ addMoveResultPseudoAsExtraInstruction(destReg, newArraySourcePosition);
+
+ if (!newArray.getInitializers().isEmpty()) {
+ assert newArray.hasConstantInitializer();
+ ArrayList<Constant> initValues = new ArrayList<Constant>();
+ for (JExpression initializer : newArray.getInitializers()) {
+ initValues.add(buildPrimitiveConstant((JValueLiteral) initializer));
+ }
+ insn = new FillArrayDataInsn(
+ Rops.FILL_ARRAY_DATA, newArraySourcePosition, RegisterSpecList.make(destReg),
+ initValues, dxType);
+ addExtraInstruction(insn);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Return true if the given JNewArray is compatible with a translation to dex opcode new-array.
+ */
+ private boolean isDexNewArrayCompatible(JNewArray newArray) {
+ List<JExpression> dims = newArray.getDims();
+ if (dims.size() < 1) {
+ return false;
+ }
+ Iterator<JExpression> iter = dims.iterator();
+ if (iter.next() instanceof JAbsentArrayDimension) {
+ return false;
+ }
+
+ while (iter.hasNext()) {
+ if (!(iter.next() instanceof JAbsentArrayDimension)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ private void buildArrayRead(@Nonnull RegisterSpec destReg, @Nonnull JArrayRef arrayRef,
+ @Nonnull SourcePosition sourcePosition) {
+ assert arrayRef.getInstance() instanceof JVariableRef
+ || arrayRef.getInstance() instanceof JNullLiteral;
+ RegisterSpec instanceReg = getRegisterSpec(arrayRef.getInstance());
+ RegisterSpec indexReg = getRegisterSpec(arrayRef.getIndexExpr());
+ RegisterSpecList sources = RegisterSpecList.make(instanceReg, indexReg);
+
+ Rop rop = Rops.opAget(getComponentType(instanceReg));
+ addInstruction(new ThrowingInsn(rop, sourcePosition, sources, getCatchTypes()));
+ addMoveResultPseudoAsExtraInstruction(destReg, sourcePosition);
+ }
+
+ private void buildReadField(@Nonnull RegisterSpec destReg, @Nonnull JFieldRef fieldRef,
+ @Nonnull SourcePosition sourcePosition) {
+ CstFieldRef cstField =
+ RopHelper.createFieldRef(fieldRef.getFieldId(), fieldRef.getReceiverType());
+ Type ropFieldType = RopHelper.convertTypeToDx(fieldRef.getType());
+ if (fieldRef.getFieldId().getKind() == FieldKind.STATIC) {
+ Rop rop = Rops.opGetStatic(ropFieldType);
+ addInstruction(new ThrowingCstInsn(rop, sourcePosition, RegisterSpecList.EMPTY,
+ getCatchTypes(), cstField));
+ } else {
+ JExpression instance = fieldRef.getInstance();
+ assert instance != null;
+ assert instance instanceof JVariableRef || instance instanceof JNullLiteral;
+ RegisterSpec instanceReg = getRegisterSpec(instance);
+ RegisterSpecList sources = RegisterSpecList.make(instanceReg);
+
+ Rop rop = Rops.opGetField(ropFieldType);
+ addInstruction(
+ new ThrowingCstInsn(rop, sourcePosition, sources, getCatchTypes(), cstField));
+ }
+ addMoveResultPseudoAsExtraInstruction(destReg, sourcePosition);
+ }
+ }
+
+ SsaRopBuilderVisitor(@Nonnull SsaRopRegisterManager ropReg,
+ @Nonnull JBasicBlock currentBasicBlock, @Nonnull SsaBasicBlock ssaBb,
+ @Nonnull Map<JBasicBlock, Integer> labelMap,
+ @Nonnull AndroidApiLevel apiLevel) {
+ this.ropReg = ropReg;
+ this.currentBasicBlock = currentBasicBlock;
+ this.ssaBb = ssaBb;
+ this.labelMap = labelMap;
+ this.apiLevel = apiLevel;
+ }
+
+ @CheckForNull
+ List<SsaInsn> getInstructions() {
+ return instructions;
+ }
+
+ @CheckForNull
+ List<Insn> getExtraInstructions() {
+ return extraInstructions;
+ }
+
+ public void processBasicBlockElements() {
+ instructions = new LinkedList<>();
+ extraInstructions = new LinkedList<>();
+ noMoreInstruction = false;
+
+ ArrayList<JBasicBlockElement> elements =
+ Lists.newArrayList(this.currentBasicBlock.getElements(true));
+ super.accept(elements);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JPhiBlockElement phi) {
+ RegisterSpec result = ropReg.getOrCreateRegisterSpec(phi.getLhs());
+
+ PhiInsn phiInsn = new PhiInsn(result, ssaBb);
+ for (JBasicBlock pred : currentBasicBlock.getPredecessors()) {
+ Integer predLabel = labelMap.get(pred);
+ assert predLabel != null;
+
+ // Because we don't know the predIndex until the whole CFG is traversed, we are going to
+ // set the predIndex at the very end instead.
+ phiInsn.addPhiOperand(ropReg.getOrCreateRegisterSpec(phi.getRhs(pred)),
+ predLabel.intValue(), predLabel.intValue());
+ }
+ addInstruction(phiInsn);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JGotoBlockElement element) {
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JCaseBlockElement element) {
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JThrowBlockElement element) {
+ addInstruction(new ThrowingInsn(
+ Rops.THROW, RopHelper.getSourcePosition(element.getSourceInfo()),
+ RegisterSpecList.make(getRegisterSpec(element.getExpression())), getCatchTypes()));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JStoreBlockElement element) {
+ JAsgOperation expression = element.getAssignment();
+ JExpression lhs = expression.getLhs();
+ JExpression rhs = expression.getRhs();
+
+ assert lhs instanceof JFieldRef || lhs instanceof JArrayRef;
+ assert !(rhs instanceof JExceptionRuntimeValue);
+
+ if (lhs instanceof JFieldRef) {
+ buildWriteField((JFieldRef) lhs, rhs, RopHelper.getSourcePosition(element.getSourceInfo()));
+ } else {
+ buildArrayWrite((JArrayRef) lhs, rhs, RopHelper.getSourcePosition(element.getSourceInfo()));
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JReturnBlockElement element) {
+ JExpression expression = element.getExpression();
+ RegisterSpecList sources = expression != null ?
+ RegisterSpecList.make(getRegisterSpec(expression)) :
+ RegisterSpecList.EMPTY;
+
+ JType type = expression != null ?
+ expression.getType() : JPrimitiveTypeEnum.VOID.getType();
+
+ addInstruction(new PlainInsn(
+ Rops.opReturn(RopHelper.convertTypeToDx(type)),
+ RopHelper.getSourcePosition(element.getSourceInfo()), null, sources));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JVariableAsgBlockElement element) {
+ JAsgOperation asg = element.getAssignment();
+ JSsaVariableRef local = (JSsaVariableRef) asg.getLhs();
+ JExpression value = asg.getRhs();
+
+ if (value instanceof JExceptionRuntimeValue) {
+ RegisterSpec exceptionReg =
+ ropReg.getOrCreateRegisterSpec(local);
+ addInstruction(new PlainInsn(
+ Rops.opMoveException(exceptionReg.getTypeBearer()),
+ RopHelper.getSourcePosition(local),
+ exceptionReg,
+ RegisterSpecList.EMPTY));
+
+ } else {
+ new AssignBuilderVisitor(
+ RopHelper.getSourcePosition(element.getSourceInfo()),
+ local).accept(value);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JMethodCallBlockElement element) {
+ buildCall(null, element.getCall());
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JPolymorphicMethodCallBlockElement element) {
+ buildInvokePolymorphic(null, element.getCall());
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JLockBlockElement element) {
+ addInstruction(new ThrowingInsn(
+ Rops.MONITOR_ENTER, RopHelper.getSourcePosition(element.getSourceInfo()),
+ RegisterSpecList.make(getRegisterSpec(element.getExpression())), getCatchTypes()));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JUnlockBlockElement element) {
+ addInstruction(new ThrowingInsn(
+ Rops.MONITOR_EXIT, RopHelper.getSourcePosition(element.getSourceInfo()),
+ RegisterSpecList.make(getRegisterSpec(element.getExpression())), getCatchTypes()));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JConditionalBlockElement element) {
+ SourcePosition ifStmtSrcPos = RopHelper.getSourcePosition(element.getSourceInfo());
+ RegisterSpecList sources;
+ JBinaryOperator op;
+
+ JExpression expr = element.getCondition();
+
+ if (expr instanceof JBinaryOperation) {
+ JBinaryOperation binCondExpr = (JBinaryOperation) expr;
+ JExpression right = binCondExpr.getRhs();
+ RegisterSpec rightReg = getRegisterSpec(right);
+
+ JExpression left = binCondExpr.getLhs();
+ JType type = right.getType();
+ JType leftType = left.getType();
+ assert leftType.isSameType(type)
+ || (leftType instanceof JIntegralType32 && type instanceof JIntegralType32)
+ || (leftType instanceof JReferenceType && type instanceof JReferenceType);
+
+ op = binCondExpr.getOp();
+ RegisterSpec leftReg = getRegisterSpec(left);
+ sources = RegisterSpecList.make(leftReg, rightReg);
+ if (type instanceof JPrimitiveType) {
+ switch (((JPrimitiveType) type).getPrimitiveTypeEnum()) {
+ case LONG:
+ case FLOAT:
+ case DOUBLE: {
+ RegisterSpec dest = ropReg.createRegisterSpec(JPrimitiveTypeEnum.BOOLEAN.getType());
+ Type dxType = RopHelper.convertTypeToDx(type);
+
+ Rop cmpOp = (type == JPrimitiveTypeEnum.LONG.getType())
+ ? Rops.opCmpl(dxType) : getCmpOperatorForFloatDouble(op, dxType);
+
+ Insn ifInst = new PlainInsn(cmpOp, ifStmtSrcPos, dest, sources);
+ addInstruction(ifInst);
+ sources = RegisterSpecList.make(dest);
+ break;
+ }
+ case BOOLEAN:
+ case BYTE:
+ case CHAR:
+ case SHORT:
+ case INT:
+ // Nothing to do.
+ break;
+ case VOID:
+ throw new AssertionError("Void type not supported.");
+ }
+ }
+ } else if (expr instanceof JPrefixNotOperation) {
+ RegisterSpec sourceReg = getRegisterSpec(((JPrefixNotOperation) expr).getArg());
+ sources = RegisterSpecList.make(sourceReg);
+ op = JBinaryOperator.EQ;
+ } else {
+ RegisterSpec sourceReg = getRegisterSpec(expr);
+ sources = RegisterSpecList.make(sourceReg);
+ op = JBinaryOperator.NEQ;
+ }
+
+ Rop ifOp = getReverseOperatorForIf(op, sources);
+ assert this.currentBasicBlock instanceof JConditionalBasicBlock;
+ if (((JConditionalBasicBlock) this.currentBasicBlock).isInverted()) {
+ ifOp = getOperatorForIf(op, sources);
+ }
+
+ Insn ifInst = new PlainInsn(ifOp, ifStmtSrcPos, null, sources);
+ addInstruction(ifInst);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JSwitchBlockElement element) {
+ assert currentBasicBlock instanceof JSwitchBasicBlock;
+
+ SourcePosition switchStmtSrcPos = RopHelper.getSourcePosition(element.getSourceInfo());
+ IntList cases = new IntList();
+ for (JBasicBlock caseBb : ((JSwitchBasicBlock) currentBasicBlock).getCases()) {
+ assert caseBb.hasElements();
+ JBasicBlockElement caseElement = null;
+ for (JBasicBlockElement e : caseBb.getElements()) {
+ if (!(e instanceof JPhiBlockElement)) {
+ caseElement = e;
+ break;
+ }
+ }
+
+ assert caseElement instanceof JCaseBlockElement;
+ JLiteral caseValue = ((JCaseBlockElement) caseElement).getLiteral();
+ if (caseValue instanceof JIntLiteral) {
+ cases.add(((JIntLiteral) caseValue).getValue());
+ } else if (caseValue instanceof JCharLiteral) {
+ cases.add(((JCharLiteral) caseValue).getValue());
+ } else if (caseValue instanceof JShortLiteral) {
+ cases.add(((JShortLiteral) caseValue).getValue());
+ } else if (caseValue instanceof JByteLiteral) {
+ cases.add(((JByteLiteral) caseValue).getValue());
+ } else {
+ throw new AssertionError("Unsupported value");
+ }
+ }
+
+ RegisterSpecList sources = RegisterSpecList.make(getRegisterSpec(element.getExpression()));
+
+ Insn switchInst = new SwitchInsn(Rops.SWITCH, switchStmtSrcPos, null, sources, cases);
+ addInstruction(switchInst);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JBasicBlockElement element) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public boolean visit(@Nonnull JNode node) {
+ throw new AssertionError("Not supported: " + node.toSource());
+ }
+
+ /**
+ * Get the @link{Rop} corresponding to the inverse of the @link{JBinaryOperator} provided for
+ * float or double type.
+ * @param op the operator to convert
+ * @param type dx type of operator
+ * @return the reverse @code{Rop}
+ */
+ @Nonnull
+ public Rop getCmpOperatorForFloatDouble(@Nonnull JBinaryOperator op,
+ @Nonnull Type type) {
+ assert type == Type.FLOAT || type == Type.DOUBLE;
+ switch (op) {
+ case LTE:
+ case LT:
+ return Rops.opCmpg(type);
+ case GT:
+ case GTE:
+ case EQ:
+ case NEQ:
+ return Rops.opCmpl(type);
+ default:
+ throw new AssertionError("Operator " + op.toString() + " not yet supported into IfStmt.");
+ }
+ }
+
+ /**
+ * Get the @link{Rop} corresponding of the @link{JBinaryOperator} provided.
+ * @param op the operator to convert
+ * @param sources the sources that will be used with the @code{Rop}
+ * @return the reverse @code{Rop}
+ */
+ @Nonnull
+ public Rop getOperatorForIf(@Nonnull JBinaryOperator op,
+ @Nonnull RegisterSpecList sources) {
+ switch (op) {
+ case LT:
+ return Rops.opIfLt(sources);
+ case GT:
+ return Rops.opIfGt(sources);
+ case LTE:
+ return Rops.opIfLe(sources);
+ case GTE:
+ return Rops.opIfGe(sources);
+ case EQ:
+ return Rops.opIfEq(sources);
+ case NEQ:
+ return Rops.opIfNe(sources);
+ default:
+ throw new AssertionError("Operator " + op.toString()
+ + " not yet supported into IfStmt.");
+ }
+ }
+
+ /**
+ * Get the @link{Rop} corresponding to the inverse of the @link{JBinaryOperator} provided.
+ * @param op the operator to convert
+ * @param sources the sources that will be used with the @code{Rop}
+ * @return the reverse @code{Rop}
+ */
+ @Nonnull
+ public Rop getReverseOperatorForIf(@Nonnull JBinaryOperator op,
+ @Nonnull RegisterSpecList sources) {
+ switch (op) {
+ case LT:
+ return Rops.opIfGe(sources);
+ case GT:
+ return Rops.opIfLe(sources);
+ case LTE:
+ return Rops.opIfGt(sources);
+ case GTE:
+ return Rops.opIfLt(sources);
+ case EQ:
+ return Rops.opIfNe(sources);
+ case NEQ:
+ return Rops.opIfEq(sources);
+ default:
+ throw new AssertionError("Operator " + op.toString()
+ + " not yet supported into IfStmt.");
+ }
+ }
+
+ private void buildAlloc(@Nonnull RegisterSpec destReg, @Nonnull JAlloc alloc,
+ @Nonnull SourcePosition sourcePosition) {
+ addInstruction(new ThrowingCstInsn(Rops.NEW_INSTANCE, sourcePosition, RegisterSpecList.EMPTY,
+ getCatchTypes(), RopHelper.convertTypeToDx(alloc.getInstanceType())));
+ addMoveResultPseudoAsExtraInstruction(destReg, sourcePosition);
+ }
+
+ private void buildArrayWrite(JArrayRef arrayRef, JExpression value,
+ SourcePosition sourcePosition) {
+ assert arrayRef.getInstance() instanceof JVariableRef
+ || arrayRef.getInstance() instanceof JNullLiteral;
+ RegisterSpec valueReg = getRegisterSpec(value);
+ RegisterSpec instanceReg = getRegisterSpec(arrayRef.getInstance());
+ RegisterSpec indexReg = getRegisterSpec(arrayRef.getIndexExpr());
+ RegisterSpecList sources = RegisterSpecList.make(valueReg, instanceReg, indexReg);
+
+ Rop rop = Rops.opAput(getComponentType(instanceReg));
+ addInstruction(new ThrowingInsn(rop, sourcePosition, sources, getCatchTypes()));
+ }
+
+ private void buildInstanceOf(RegisterSpec destReg, JInstanceOf instanceOf) {
+ SourcePosition srcPos = RopHelper.getSourcePosition(instanceOf);
+ addInstruction(new ThrowingCstInsn(Rops.INSTANCE_OF, srcPos,
+ RegisterSpecList.make(getRegisterSpec(instanceOf.getExpr())), getCatchTypes(),
+ RopHelper.convertTypeToDx(instanceOf.getTestType())));
+ addMoveResultPseudoAsExtraInstruction(destReg, srcPos);
+ }
+
+ @Nonnull
+ private static Type getComponentType(@Nonnull TypeBearer arrayTypeBearer) {
+ Type arrayType = arrayTypeBearer.getType();
+
+ if (arrayType.isArray()) {
+ return arrayType.getComponentType();
+ }
+
+ assert arrayType.equals(Type.KNOWN_NULL);
+ return arrayType.getType();
+ }
+
+ private void buildArrayLength(RegisterSpec destReg, JArrayLength value) {
+ RegisterSpec reg = getRegisterSpec(value.getInstance());
+ SourcePosition srcPos = RopHelper.getSourcePosition(value);
+ addInstruction(new ThrowingInsn(Rops.ARRAY_LENGTH, srcPos, RegisterSpecList.make(reg),
+ getCatchTypes()));
+ addMoveResultPseudoAsExtraInstruction(destReg, srcPos);
+ }
+
+ private void buildWriteField(@Nonnull JFieldRef fieldRef, @Nonnull JExpression value,
+ @Nonnull SourcePosition sourcePosition) {
+
+ RegisterSpec valueReg = getRegisterSpec(value);
+
+ CstFieldRef cstField = RopHelper.createFieldRef(fieldRef.getFieldId(),
+ fieldRef.getReceiverType());
+
+ if (fieldRef.getFieldId().getKind() == FieldKind.STATIC) {
+ Rop rop = Rops.opPutStatic(RopHelper.convertTypeToDx(fieldRef.getType()));
+ addInstruction(new ThrowingCstInsn(rop, sourcePosition, RegisterSpecList.make(valueReg),
+ getCatchTypes(), cstField));
+ } else {
+ JExpression instance = fieldRef.getInstance();
+ assert instance != null;
+ assert instance instanceof JVariableRef || instance instanceof JNullLiteral;
+ RegisterSpec instanceReg = getRegisterSpec(instance);
+ RegisterSpecList sources = RegisterSpecList.make(valueReg, instanceReg);
+
+ Rop rop = Rops.opPutField(RopHelper.convertTypeToDx(fieldRef.getType()));
+ addInstruction(new ThrowingCstInsn(rop, sourcePosition, sources, getCatchTypes(), cstField));
+ }
+ }
+
+
+ private void buildCast(@Nonnull RegisterSpec destReg, @Nonnull JDynamicCastOperation cast) {
+ JExpression from = cast.getExpr();
+ SourcePosition sourcePosition = RopHelper.getSourcePosition(cast);
+ RegisterSpec fromReg = getRegisterSpec(from);
+
+ JType castTo = cast.getType();
+ JType castedFrom = from.getType();
+
+ if (castTo instanceof JPrimitiveType) {
+
+ assert castedFrom instanceof JPrimitiveType;
+
+ if (castTo == castedFrom) {
+ RegisterSpecList sources = RegisterSpecList.make(fromReg);
+ addInstruction(new PlainInsn(
+ Rops.opMove(fromReg.getTypeBearer()), sourcePosition,
+ destReg, sources));
+ return;
+ }
+
+ /* Rop has 2 groups of cast instructions:
+ * - Casts form int to byte, char and short.
+ * - Casts between int, long, float and double.
+ * Casts from larger values than int to smaller values must be done with 2 instructions, one
+ * from each group. These two instructions are created by RopCastLegalier.
+ */
+
+ if (((castTo == JPrimitiveTypeEnum.BYTE.getType())
+ || (castTo == JPrimitiveTypeEnum.SHORT.getType())
+ || (castTo == JPrimitiveTypeEnum.CHAR.getType())
+ || (castTo == JPrimitiveTypeEnum.INT.getType())
+ || (castTo == JPrimitiveTypeEnum.BOOLEAN.getType())
+ )
+ &&
+ ((castedFrom == JPrimitiveTypeEnum.INT.getType())
+ || (castedFrom == JPrimitiveTypeEnum.BYTE.getType())
+ || (castedFrom == JPrimitiveTypeEnum.CHAR.getType())
+ || (castedFrom == JPrimitiveTypeEnum.SHORT.getType())
+ || (castedFrom == JPrimitiveTypeEnum.BOOLEAN.getType())
+ )) {
+ addTruncateIntOrMoveInstruction(sourcePosition,
+ ((JPrimitiveType) castTo).getPrimitiveTypeEnum(), fromReg, destReg);
+ } else {
+
+ Insn inst =
+ new PlainInsn(Rops.opConv(destReg, fromReg), sourcePosition, destReg,
+ RegisterSpecList.make(fromReg));
+ addInstruction(inst);
+
+ }
+ } else {
+ RegisterSpecList sources = RegisterSpecList.make(fromReg);
+
+ Insn insn =
+ new ThrowingCstInsn(Rops.CHECK_CAST, sourcePosition, sources, getCatchTypes(),
+ RopHelper.convertTypeToDx(castTo));
+ addInstruction(insn);
+
+ addMoveResultPseudoAsExtraInstruction(destReg, sourcePosition);
+ }
+ }
+
+ private void addTruncateIntOrMoveInstruction(@Nonnull SourcePosition sourcePosition,
+ @Nonnull JPrimitiveTypeEnum castTo, @Nonnull RegisterSpec fromReg,
+ @CheckForNull RegisterSpec destReg) throws AssertionError {
+ Rop rop;
+ switch (castTo) {
+ case BYTE:
+ rop = Rops.TO_BYTE;
+ break;
+ case CHAR:
+ rop = Rops.TO_CHAR;
+ break;
+ case SHORT:
+ rop = Rops.TO_SHORT;
+ break;
+ case BOOLEAN:
+ case INT:
+ rop = Rops.MOVE_INT;
+ break;
+ default:
+ throw new AssertionError(castTo + " not supported");
+ }
+
+ RegisterSpecList sources = RegisterSpecList.make(fromReg);
+ Insn inst = new PlainInsn(
+ rop, sourcePosition, destReg, sources);
+ addInstruction(inst);
+ }
+
+ @Nonnull
+ private Constant buildPrimitiveConstant(@Nonnull JValueLiteral literal) {
+ Constant cst = null;
+
+ assert literal.getType() instanceof JPrimitiveType;
+
+ JPrimitiveTypeEnum primitiveType = ((JPrimitiveType) literal.getType()).getPrimitiveTypeEnum();
+
+ switch (primitiveType) {
+ case BOOLEAN:
+ cst = CstInteger.make(((JBooleanLiteral) literal).getValue() ? 1 : 0);
+ break;
+ case BYTE:
+ cst = CstInteger.make(((JByteLiteral) literal).getValue());
+ break;
+ case CHAR:
+ cst = CstInteger.make(((JCharLiteral) literal).getValue());
+ break;
+ case DOUBLE:
+ cst = CstDouble.make(Double.doubleToLongBits(((JDoubleLiteral) literal).getValue()));
+ break;
+ case FLOAT:
+ cst = CstFloat.make(Float.floatToIntBits(((JFloatLiteral) literal).getValue()));
+ break;
+ case INT:
+ cst = CstInteger.make(((JIntLiteral) literal).getValue());
+ break;
+ case LONG:
+ cst = CstLong.make(((JLongLiteral) literal).getValue());
+ break;
+ case SHORT:
+ cst = CstInteger.make(((JShortLiteral) literal).getValue());
+ break;
+ case VOID:
+ throw new AssertionError(literal.toSource() + " not supported.");
+ }
+
+ assert cst != null;
+ return cst;
+ }
+
+ @Nonnull
+ private Constant getConstant(@Nonnull JValueLiteral literal) {
+ Constant cst = null;
+
+ JType type = literal.getType();
+ if (type instanceof JPrimitiveType) {
+ cst = buildPrimitiveConstant(literal);
+ } else if (literal instanceof JAbstractStringLiteral) {
+ cst = RopHelper.createString((JAbstractStringLiteral) literal);
+ } else if (literal instanceof JNullLiteral) {
+ cst = CstKnownNull.THE_ONE;
+ } else {
+ throw new AssertionError(literal.toSource() + " not supported.");
+ }
+
+ return cst;
+ }
+
+ private void buildConstant(@Nonnull RegisterSpec destReg, @Nonnull JValueLiteral literal) {
+ JType type = literal.getType();
+ Rop constOp = Rops.opConst(RopHelper.convertTypeToDx(type));
+ Insn constInst;
+ SourcePosition sourcePosition = RopHelper.getSourcePosition(literal);
+ if (type instanceof JPrimitiveType) {
+ constInst = new PlainCstInsn(
+ constOp, sourcePosition, destReg, RegisterSpecList.EMPTY,
+ getConstant(literal));
+ addInstruction(constInst);
+ } else if (literal instanceof JAbstractStringLiteral) {
+ constInst = new ThrowingCstInsn(constOp, sourcePosition,
+ RegisterSpecList.EMPTY, getCatchTypes(), getConstant(literal));
+ addInstruction(constInst);
+ addMoveResultPseudoAsExtraInstruction(destReg, sourcePosition);
+ } else if (literal instanceof JNullLiteral) {
+ constInst = new PlainCstInsn(
+ constOp, sourcePosition, destReg,
+ RegisterSpecList.EMPTY, getConstant(literal));
+ addInstruction(constInst);
+ } else {
+ throw new AssertionError(literal.toSource() + " not supported.");
+ }
+ }
+
+ private void buildUnaryOperation(@Nonnull RegisterSpec destReg,
+ @Nonnull JUnaryOperation unary) {
+ SourcePosition unarySrcPos = RopHelper.getSourcePosition(unary);
+
+ RegisterSpec srcRegisterSpec = getRegisterSpec(unary.getArg());
+ RegisterSpecList sources = RegisterSpecList.make(srcRegisterSpec);
+
+ Rop opcode = null;
+
+ switch (unary.getOp()) {
+ case NEG: {
+ assert unary.getType() == JPrimitiveTypeEnum.BYTE.getType()
+ || unary.getType() == JPrimitiveTypeEnum.CHAR.getType()
+ || unary.getType() == JPrimitiveTypeEnum.SHORT.getType()
+ || unary.getType() == JPrimitiveTypeEnum.INT.getType()
+ || unary.getType() == JPrimitiveTypeEnum.LONG.getType()
+ || unary.getType() == JPrimitiveTypeEnum.FLOAT.getType()
+ || unary.getType() == JPrimitiveTypeEnum.DOUBLE.getType();
+ opcode = Rops.opNeg(srcRegisterSpec);
+ break;
+ }
+ case BIT_NOT: {
+ assert unary.getType() == JPrimitiveTypeEnum.BYTE.getType()
+ || unary.getType() == JPrimitiveTypeEnum.CHAR.getType()
+ || unary.getType() == JPrimitiveTypeEnum.SHORT.getType()
+ || unary.getType() == JPrimitiveTypeEnum.INT.getType()
+ || unary.getType() == JPrimitiveTypeEnum.LONG.getType();
+ opcode = Rops.opNot(srcRegisterSpec);
+ break;
+ }
+ case NOT: {
+ // Since Dalvik code does not have NOT operator, we will use
+ // x = y ^ true to represent x = !y
+ assert unary.getType() == JPrimitiveTypeEnum.BOOLEAN.getType();
+ addInstruction(
+ new PlainCstInsn(
+ Rops.opXor(sources), unarySrcPos, destReg, sources, CstBoolean.make(true)));
+ return;
+ }
+ default: {
+ throw new AssertionError("Unary operation not supported.");
+ }
+ }
+
+ addInstruction(new PlainInsn(opcode, unarySrcPos, destReg, sources));
+ }
+
+ private void buildBinaryOperation(
+ @Nonnull RegisterSpec destReg, @Nonnull JBinaryOperation binary) {
+
+ RegisterSpecList sources;
+ SourcePosition declarationSrcPos = RopHelper.getSourcePosition(binary);
+ Constant cst = null;
+ JBinaryOperator binOp = binary.getOp();
+ JExpression rhs = binary.getRhs();
+ JExpression lhs = binary.getLhs();
+
+ if (lhs instanceof JSsaVariableRef && binary.getType() instanceof JIntegralType32
+ && rhs instanceof JIntegralConstant32 && ((JIntegralType32) JPrimitiveTypeEnum.SHORT
+ .getType()).isValidValue(((JIntegralConstant32) rhs).getIntValue())) {
+ assert rhs instanceof JValueLiteral;
+
+ // Sub with constant does not exist, check if it can be replace by an add
+ if (binOp == JBinaryOperator.SUB) {
+ int newCst = -((JIntegralConstant32) rhs).getIntValue();
+ if (((JIntegralType32) JPrimitiveTypeEnum.SHORT.getType()).isValidValue(newCst)) {
+ binOp = JBinaryOperator.ADD;
+ sources = RegisterSpecList.make(ropReg.getOrCreateRegisterSpec((JSsaVariableRef) lhs));
+ cst = CstInteger.make(newCst);
+ } else {
+ sources = RegisterSpecList.make(getRegisterSpec(lhs), getRegisterSpec(rhs));
+ }
+ } else {
+ sources = RegisterSpecList.make(ropReg.getOrCreateRegisterSpec((JSsaVariableRef) lhs));
+ cst = getConstant((JValueLiteral) rhs);
+ }
+ } else {
+ if (rhs instanceof JSsaVariableRef) {
+ // Check if rsub can be generated
+ if (binOp == JBinaryOperator.SUB
+ && lhs instanceof JIntegralConstant32 && ((JIntegralType32) JPrimitiveTypeEnum.SHORT
+ .getType()).isValidValue(((JIntegralConstant32) lhs).getIntValue())) {
+ sources = RegisterSpecList.make(ropReg.getOrCreateRegisterSpec((JSsaVariableRef) rhs));
+ assert lhs instanceof JValueLiteral;
+ cst = getConstant((JValueLiteral) lhs);
+ } else {
+ sources = RegisterSpecList.make(getRegisterSpec(lhs),
+ ropReg.getOrCreateRegisterSpec((JSsaVariableRef) rhs));
+ }
+ } else {
+ assert rhs instanceof JValueLiteral;
+ sources = RegisterSpecList.make(
+ getRegisterSpec(lhs), getRegisterSpec(rhs));
+ }
+ }
+
+ Rop opcode;
+
+ switch (binOp) {
+ case ADD:
+ opcode = Rops.opAdd(sources);
+ break;
+ case SUB:
+ opcode = Rops.opSub(sources);
+ break;
+ case ASG: // all assign not removed in ThreeAddressCodeForm are to be handled by buildAssign
+ case ASG_ADD: // assigns with operation are removed in ThreeAddressCodeForm
+ case ASG_BIT_AND:
+ case ASG_BIT_OR:
+ case ASG_BIT_XOR:
+ case ASG_CONCAT:
+ case ASG_DIV:
+ case ASG_MOD:
+ case ASG_MUL:
+ case ASG_SHL:
+ case ASG_SHR:
+ case ASG_SHRU:
+ case ASG_SUB:
+ case EQ: // TODO(yroussel) add constraint on Ropper to ensure no boolean BinaryOperation
+ case GT:
+ case GTE:
+ case LT:
+ case LTE:
+ case NEQ:
+ case OR:
+ case AND:
+ throw new AssertionError();
+ case BIT_AND:
+ opcode = Rops.opAnd(sources);
+ break;
+ case BIT_OR:
+ opcode = Rops.opOr(sources);
+ break;
+ case BIT_XOR:
+ opcode = Rops.opXor(sources);
+ break;
+ case DIV:
+ opcode = Rops.opDiv(sources);
+ break;
+ case MOD:
+ opcode = Rops.opRem(sources);
+ break;
+ case MUL:
+ opcode = Rops.opMul(sources);
+ break;
+ case SHL:
+ opcode = Rops.opShl(sources);
+ if (opcode.equals(Rops.SHL_CONST_INT)) {
+ assert cst != null;
+ CstLiteral32 lit = (CstLiteral32) cst;
+ cst = CstInteger.make(lit.getIntBits() & 0b11111);
+ } else if (opcode.equals(Rops.SHL_CONST_LONG)) {
+ assert cst != null;
+ CstLiteral64 lit = (CstLiteral64) cst;
+ cst = CstInteger.make(lit.getIntBits() & 0b111111);
+ }
+ break;
+ case SHR:
+ opcode = Rops.opShr(sources);
+ if (opcode.equals(Rops.SHR_CONST_INT)) {
+ assert cst != null;
+ CstLiteral32 lit = (CstLiteral32) cst;
+ cst = CstInteger.make(lit.getIntBits() & 0b11111);
+ } else if (opcode.equals(Rops.SHR_CONST_LONG)) {
+ assert cst != null;
+ CstLiteral64 lit = (CstLiteral64) cst;
+ cst = CstInteger.make(lit.getIntBits() & 0b111111);
+ }
+ break;
+ case SHRU:
+ opcode = Rops.opUshr(sources);
+ if (opcode.equals(Rops.USHR_CONST_INT)) {
+ assert cst != null;
+ CstLiteral32 lit = (CstLiteral32) cst;
+ cst = CstInteger.make(lit.getIntBits() & 0b11111);
+ } else if (opcode.equals(Rops.USHR_CONST_LONG)) {
+ assert cst != null;
+ CstLiteral64 lit = (CstLiteral64) cst;
+ cst = CstInteger.make(lit.getIntBits() & 0b111111);
+ }
+ break;
+ default:
+ throw new AssertionError();
+ }
+ if (opcode.canThrow()) {
+ if (cst == null) {
+ addInstruction(new ThrowingInsn(opcode, declarationSrcPos, sources, getCatchTypes()));
+ } else {
+ addInstruction(
+ new ThrowingCstInsn(opcode, declarationSrcPos, sources, getCatchTypes(), cst));
+ }
+ addMoveResultPseudoAsExtraInstruction(destReg, declarationSrcPos);
+ } else {
+ if (cst == null) {
+ addInstruction(new PlainInsn(opcode, declarationSrcPos, destReg, sources));
+ } else {
+ addInstruction(new PlainCstInsn(opcode, declarationSrcPos, destReg, sources, cst));
+ }
+ }
+ }
+
+ private void buildInvokePolymorphic(@CheckForNull RegisterSpec result,
+ @Nonnull JPolymorphicMethodCall methodCall) {
+ CstMethodRef methodRef =
+ new CstMethodRef(RopHelper.convertTypeToDx(methodCall.getReceiverType()),
+ new CstString(methodCall.getMethodName()),
+ RopHelper.getPrototypeFromPolymorphicCall(methodCall));
+
+ SourcePosition methodCallSrcPos = RopHelper.getSourcePosition(methodCall);
+ Prototype prototype =
+ Prototype.intern(RopHelper.getPolymorphicCallSiteSymbolicDescriptor(methodCall));
+ Rop callOp = Rops.opInvokePolymorphic(prototype);
+
+ /* 1 means that first register is always an instance of MethodHandle. */
+ RegisterSpecList sources = new RegisterSpecList(1 + methodCall.getArgs().size());
+
+ /* Set MethodHandle as first parameter */
+ JExpression instance = methodCall.getInstance();
+ assert instance != null;
+ int paramIndex = 0;
+ sources.set(paramIndex++, getRegisterSpec(instance));
+
+ for (JExpression exprArg : methodCall.getArgs()) {
+ sources.set(paramIndex++, getRegisterSpec(exprArg));
+ }
+
+ Insn callInst = new ThrowingDualCstInsn(callOp, methodCallSrcPos, sources, getCatchTypes(),
+ methodRef, new CstPrototypeRef(prototype));
+ addInstruction(callInst);
+
+ if (result != null) {
+ addMoveResultAsExtraInstruction(prototype.getReturnType(), result, methodCallSrcPos);
+ }
+ }
+
+ private void buildCall(@CheckForNull RegisterSpec result, @Nonnull JMethodCall methodCall) {
+ if (InvokeCustomHelper.isInvokeCustom(methodCall)
+ && apiLevel.getProvisionalLevel() == AndroidApiLevel.ProvisionalLevel.O_BETA2) {
+ JAnnotation invokeCustomCallSite = InvokeCustomHelper
+ .getInvokeCustomCallsite(methodCall.getMethodId().getMethods().iterator().next());
+ assert invokeCustomCallSite != null;
+ CstCallSiteRef callSiteRef =
+ InvokeCustomHelper.readInvokeCustomCallSite(invokeCustomCallSite);
+
+ SourcePosition methodCallSrcPos = RopHelper.getSourcePosition(methodCall);
+
+ RegisterSpecList sources = new RegisterSpecList(methodCall.getArgs().size());
+ int paramIndex = 0;
+ for (JExpression exprArg : methodCall.getArgs()) {
+ sources.set(paramIndex++, getRegisterSpec(exprArg));
+ }
+
+ Insn invokeCustom = new ThrowingCstInsn(
+ Rops.opInvokeCustom(callSiteRef.getCallSitePrototype().getPrototype()), methodCallSrcPos,
+ sources, getCatchTypes(), callSiteRef);
+ addInstruction(invokeCustom);
+
+ if (result != null) {
+ addMoveResultAsExtraInstruction(callSiteRef.getType(), result, methodCallSrcPos);
+ }
+
+ return;
+ }
+
+ SourcePosition methodCallSrcPos = RopHelper.getSourcePosition(methodCall);
+
+ Prototype prototype = RopHelper.getPrototype(methodCall.getMethodId());
+
+ RegisterSpecList sources;
+ int paramIndex = 0;
+
+ Rop callOp;
+ MethodKind methodKind = methodCall.getMethodIdWide().getKind();
+ if (methodKind == MethodKind.STATIC) {
+ // Reserve space for the method arguments
+ sources = new RegisterSpecList(methodCall.getArgs().size());
+ } else {
+ // Reserve space for the instance and the method arguments
+ sources = new RegisterSpecList(1 + methodCall.getArgs().size());
+ }
+
+ switch (methodKind) {
+ case STATIC:
+ callOp = Rops.opInvokeStatic(prototype);
+ break;
+ case INSTANCE_NON_VIRTUAL: {
+ callOp = Rops.opInvokeDirect(prototype);
+ // Add the instance as first parameter
+ JExpression instance = methodCall.getInstance();
+ assert instance != null;
+ sources.set(paramIndex++, getRegisterSpec(instance));
+ break;
+ }
+ case INSTANCE_VIRTUAL: {
+ JExpression instance = methodCall.getInstance();
+ assert instance != null;
+ RegisterSpec instanceReg = getRegisterSpec(instance);
+ if (methodCall.getDispatchKind() == DispatchKind.DIRECT) {
+ callOp = Rops.opInvokeSuper(prototype);
+ } else {
+ if (methodCall.getReceiverType() instanceof JInterface) {
+ callOp = Rops.opInvokeInterface(prototype);
+ } else {
+ callOp = Rops.opInvokeVirtual(prototype);
+ }
+ }
+ sources.set(paramIndex++, instanceReg);
+ break;
+ }
+ default:
+ throw new AssertionError(methodCall.toSource() + " not yet supported.");
+ }
+
+ assert prototype.getParameterTypes().size() == methodCall.getArgs().size();
+ for (JExpression exprArg : methodCall.getArgs()) {
+ sources.set(paramIndex++, getRegisterSpec(exprArg));
+ }
+
+ CstMethodRef methodRef = RopHelper.createMethodRef(methodCall);
+ Insn callInst =
+ new ThrowingCstInsn(callOp, methodCallSrcPos, sources, getCatchTypes(), methodRef);
+ addInstruction(callInst);
+
+ if (result != null) {
+ addMoveResultAsExtraInstruction(prototype.getReturnType(), result, methodCallSrcPos);
+ }
+ }
+
+ @Nonnull
+ private RegisterSpec getRegisterSpec(@Nonnull JExpression expr) {
+ RegisterSpec regSpec;
+ if (expr instanceof JSsaVariableRef) {
+ regSpec = ropReg.getOrCreateRegisterSpec((JSsaVariableRef) expr);
+ } else if (expr instanceof JThisRef) {
+ return ropReg.getThisReg();
+ } else {
+ assert expr instanceof JValueLiteral;
+ regSpec =
+ ropReg.getOrCreateTmpRegister(RopHelper.convertTypeToDx(expr.getType()));
+ buildConstant(regSpec, (JValueLiteral) expr);
+ }
+
+ return regSpec;
+ }
+
+ private void addMoveResultAsExtraInstruction(@Nonnull TypeBearer type,
+ @Nonnull RegisterSpec destReg, @Nonnull SourcePosition sourcePosition) {
+ Rop moveResultOp = Rops.opMoveResult(type);
+ Insn moveResultInst =
+ new PlainInsn(moveResultOp, sourcePosition, destReg, RegisterSpecList.EMPTY);
+ addExtraInstruction(moveResultInst);
+ }
+
+ private void addMoveResultPseudoAsExtraInstruction(
+ @Nonnull RegisterSpec destReg, @Nonnull SourcePosition sourcePosition) {
+ PlainInsn moveResult = new PlainInsn(
+ Rops.opMoveResultPseudo(destReg.getTypeBearer()),
+ sourcePosition, destReg, RegisterSpecList.EMPTY);
+ addExtraInstruction(moveResult);
+ }
+
+ private void addExtraInstruction(@Nonnull Insn insn) {
+ assert extraInstructions != null;
+ extraInstructions.add(insn);
+ noMoreInstruction = true;
+ }
+
+ private boolean addInstruction(@Nonnull Insn insn) {
+ assert instructions != null;
+ assert !noMoreInstruction;
+ return instructions.add(new NormalSsaInsn(insn, ssaBb));
+ }
+
+ private boolean addInstruction(@Nonnull SsaInsn insn) {
+ assert instructions != null;
+ assert !noMoreInstruction;
+ return instructions.add(insn);
+ }
+
+ /**
+ * Get the catch types list containing the types of every catch block accessible from the given
+ * block.
+ */
+ private TypeList getCatchTypes() {
+ assert currentBasicBlock instanceof JThrowingBasicBlock;
+ JThrowingBasicBlock block = (JThrowingBasicBlock) currentBasicBlock;
+
+ List<JType> catchTypes = new ArrayList<JType>();
+
+ for (JBasicBlock bb : block.getCatchBlocks()) {
+ for (JClass catchType : ((JCatchBasicBlock) bb).getCatchTypes()) {
+ catchTypes.add(catchType);
+ }
+ }
+
+ if (catchTypes.isEmpty()) {
+ return (StdTypeList.EMPTY);
+ } else {
+ return (RopHelper.createTypeList(catchTypes));
+ }
+ }
+}
\ No newline at end of file
diff --git a/jack/src/com/android/jack/backend/dex/rop/SsaRopRegisterManager.java b/jack/src/com/android/jack/backend/dex/rop/SsaRopRegisterManager.java
new file mode 100644
index 0000000..8ba07df
--- /dev/null
+++ b/jack/src/com/android/jack/backend/dex/rop/SsaRopRegisterManager.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.backend.dex.rop;
+
+import com.android.jack.debug.DebugVariableInfoMarker;
+import com.android.jack.dx.rop.code.LocalItem;
+import com.android.jack.dx.rop.code.RegisterSpec;
+import com.android.jack.dx.rop.cst.CstString;
+import com.android.jack.dx.rop.type.Type;
+import com.android.jack.ir.ast.JDefinedClassOrInterface;
+import com.android.jack.ir.ast.JParameter;
+import com.android.jack.ir.ast.JSsaVariableDefRef;
+import com.android.jack.ir.ast.JSsaVariableRef;
+import com.android.jack.ir.ast.JSsaVariableUseRef;
+import com.android.jack.ir.ast.JThis;
+import com.android.jack.ir.ast.JType;
+import com.android.jack.ir.ast.JVariable;
+import com.android.jack.ir.ast.marker.GenericSignature;
+import com.android.jack.ir.ast.marker.ThisRefTypeInfo;
+
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+class SsaRopRegisterManager {
+
+ private int nextFreeReg = 0;
+
+ /**
+ * Keep a list of temporary register for each dex type.
+ */
+ @Nonnull
+ private final Map<Type, List<RegisterSpec>> typeToTmpRegister =
+ new Hashtable<Type, List<RegisterSpec>>();
+
+ /**
+ * Keep position of the next free register into {@code typeToTmpRegister}.
+ */
+ @Nonnull
+ private final Map<Type, Integer> typeToNextPosFreeRegister = new Hashtable<Type, Integer>();
+
+ @CheckForNull
+ private RegisterSpec returnReg = null;
+ @CheckForNull
+ private RegisterSpec thisReg = null;
+
+ private final boolean emitSyntheticDebugInfo;
+
+ private final boolean emitDebugInfo;
+
+ public SsaRopRegisterManager(boolean emitDebugInfo, boolean emitSyntheticDebugInfo) {
+ this.emitDebugInfo = emitDebugInfo;
+ this.emitSyntheticDebugInfo = emitSyntheticDebugInfo;
+ }
+
+ public int getRegisterCount() {
+ return nextFreeReg;
+ }
+
+ /**
+ * Create a {@link RegisterSpec} representing the variable {@code this}.
+ * @param jThis The {@link JThis} we want the {@link RegisterSpec} for.
+ * @return The built {@link RegisterSpec}.
+ */
+ @Nonnull
+ RegisterSpec createThisReg(@Nonnull JThis jThis) {
+ assert thisReg == null : "This register was already created.";
+ JDefinedClassOrInterface type = (JDefinedClassOrInterface) jThis.getType();
+
+ Type dexRegType = RopHelper.convertTypeToDx(type);
+ String name = jThis.getName();
+ if (emitDebugInfo && name != null) {
+ assert jThis.getMarker(GenericSignature.class) == null;
+ CstString cstSignature = null;
+ ThisRefTypeInfo thisMarker = type.getMarker(ThisRefTypeInfo.class);
+ if (thisMarker != null && !thisMarker.getGenericSignature().isEmpty()) {
+ cstSignature = new CstString(thisMarker.getGenericSignature());
+ }
+ LocalItem localItem =
+ LocalItem.make(new CstString(name), RopHelper.convertTypeToDx(type), cstSignature);
+ thisReg = RegisterSpec.make(nextFreeReg, dexRegType, localItem);
+ } else {
+ thisReg = RegisterSpec.make(nextFreeReg, dexRegType);
+ }
+ nextFreeReg += dexRegType.getCategory();
+
+ assert thisReg != null;
+ return (thisReg);
+ }
+
+ /**
+ * Create a {@code RegisterSpec} from a {@code JType}.
+ *
+ * @param type The {@code JType} we want the {@code RegisterSpec} of.
+ * @return The built {@code RegisterSpec}.
+ */
+ @Nonnull
+ RegisterSpec createRegisterSpec(@Nonnull JType type) {
+ Type dexRegType = RopHelper.convertTypeToDx(type);
+ RegisterSpec reg = RegisterSpec.make(nextFreeReg, dexRegType);
+ nextFreeReg += dexRegType.getCategory();
+ return (reg);
+ }
+
+ /**
+ * Return the register number associated with a {@link JVariable}
+ * @return The register number of a {@link JVariable}.
+ */
+ @Nonnegative
+ int getRegisterNumber(@Nonnull JSsaVariableRef ref) {
+ JVariable variable = ref.getTarget();
+ Type dexRegType = RopHelper.convertTypeToDx(variable.getType());
+ JSsaVariableDefRef def = ref instanceof JSsaVariableUseRef ? ((JSsaVariableUseRef) ref).getDef()
+ : (JSsaVariableDefRef) ref;
+ int regNum = def.getRopRegister();
+ if (regNum != -1) {
+ if (nextFreeReg <= regNum) {
+ nextFreeReg = regNum + dexRegType.getCategory();
+ }
+ return regNum;
+ } else {
+ def.setRopRegister(nextFreeReg);
+ nextFreeReg += dexRegType.getCategory();
+ return def.getRopRegister();
+ }
+ }
+
+ @Nonnull
+ RegisterSpec getOrCreateRegisterSpec(@Nonnull JParameter param) {
+ return getOrCreateRegisterSpec(
+ new JSsaVariableDefRef(param.getSourceInfo(), param, 0));
+ }
+
+ /**
+ * Get a {@code RegisterSpec} from a {@code JVariableRef}.
+ *
+ * @param varRef The {@code JVariableRef} we want the {@code RegisterSpec} of.
+ * @return The previously built {@code RegisterSpec}.
+ */
+ @Nonnull
+ RegisterSpec getOrCreateRegisterSpec(@Nonnull JSsaVariableRef varRef) {
+ if (varRef.getTarget() instanceof JThis) {
+ assert thisReg != null : "This register was not created.";
+ return (thisReg);
+ }
+
+ JVariable variable = varRef.getTarget();
+ RegisterSpec register = getRegisterSpec(getRegisterNumber(varRef), variable,
+ varRef.getMarker(DebugVariableInfoMarker.class));
+
+ assert RopHelper.areTypeCompatible(
+ RopHelper.convertTypeToDx(varRef.getType()),
+ register.getType());
+
+ return register;
+ }
+
+ @Nonnull
+ RegisterSpec getOrCreateRegisterSpec(@Nonnull JSsaVariableRef varRef,
+ @CheckForNull LocalItem localItem) {
+ if (varRef.getTarget() instanceof JThis) {
+ assert thisReg != null : "This register was not created.";
+ return (thisReg);
+ }
+
+ JVariable variable = varRef.getTarget();
+ RegisterSpec register = getRegisterSpec(getRegisterNumber(varRef), variable, localItem);
+
+ assert RopHelper.areTypeCompatible(
+ RopHelper.convertTypeToDx(varRef.getType()),
+ register.getType());
+
+ return register;
+ }
+
+ @Nonnull
+ private RegisterSpec getRegisterSpec(@Nonnegative int regNum, @Nonnull JVariable variable,
+ @CheckForNull DebugVariableInfoMarker debugInfo) {
+ RegisterSpec reg;
+ JType variableType = variable.getType();
+ Type regType = RopHelper.convertTypeToDx(variableType);
+
+ String name = variable.getName();
+ if (emitDebugInfo && name != null
+ && (emitSyntheticDebugInfo || !variable.isSynthetic())) {
+ if (debugInfo != null) {
+ // Debug info marker exists, uses debug information from it
+ if (debugInfo == DebugVariableInfoMarker.NO_DEBUG_INFO) {
+ // There is no debug information when coming from Jill, do not get name from JVariable
+ reg = RegisterSpec.make(regNum, regType);
+ } else {
+ CstString cstSignature = null;
+ String genericSignature = debugInfo.getGenericSignature();
+ if (genericSignature != null) {
+ cstSignature = new CstString(genericSignature);
+ }
+ String debugName = debugInfo.getName();
+ assert debugName != null;
+ JType debugType = debugInfo.getType();
+ assert debugType != null;
+ LocalItem localItem = LocalItem.make(new CstString(debugName),
+ RopHelper.convertTypeToDx(debugType), cstSignature);
+ reg = RegisterSpec.make(regNum, regType, localItem);
+ }
+ } else {
+ CstString cstSignature = null;
+ GenericSignature infoMarker = variable.getMarker(GenericSignature.class);
+ if (infoMarker != null) {
+ cstSignature = new CstString(infoMarker.getGenericSignature());
+ }
+ LocalItem localItem = LocalItem.make(new CstString(name), regType, cstSignature);
+ reg = RegisterSpec.make(regNum, regType, localItem);
+ }
+ } else {
+ reg = RegisterSpec.make(regNum, regType);
+ }
+
+ return (reg);
+ }
+
+ @Nonnull
+ private RegisterSpec getRegisterSpec(@Nonnegative int regNum, @Nonnull JVariable variable,
+ @Nonnull LocalItem localItem) {
+ RegisterSpec reg;
+ JType variableType = variable.getType();
+ Type regType = RopHelper.convertTypeToDx(variableType);
+ reg = RegisterSpec.make(regNum, regType, localItem);
+ return reg;
+ }
+
+ /**
+ * Get the {@code RegisterSpec} with the type {@code type} to return value from method.
+ *
+ * @param returnType The return type of the method.
+ * @return The {@code RegisterSpec} used to return result.
+ */
+ @Nonnull
+ RegisterSpec getReturnReg(@Nonnull JType returnType) {
+ RegisterSpec localReturnReg = returnReg;
+ assert localReturnReg != null : "Return reg must be firstly created.";
+ assert RopHelper.areTypeCompatible(
+ RopHelper.convertTypeToDx(returnType), localReturnReg.getType());
+ return (localReturnReg);
+ }
+
+ /**
+ * Create a {@code RegisterSpec} with the type {@code type} to return value from method. The
+ * register number for this register must be 0.
+ *
+ * @param returnType The return type of the method.
+ * @return The {@code RegisterSpec} used to return result.
+ */
+ @Nonnull
+ RegisterSpec createReturnReg(@Nonnull JType returnType) {
+ assert returnReg == null;
+ Type dexRegType = RopHelper.convertTypeToDx(returnType);
+ returnReg = RegisterSpec.make(0, dexRegType);
+ assert returnReg != null;
+ return (returnReg);
+ }
+
+ @Nonnull
+ RegisterSpec getOrCreateTmpRegister(@Nonnull Type dexRegType) {
+ RegisterSpec regSpec = RegisterSpec.make(nextFreeReg, dexRegType);
+ nextFreeReg += dexRegType.getCategory();
+ return regSpec;
+ }
+
+ void resetFreeTmpRegister() {
+ for (Type type : typeToNextPosFreeRegister.keySet()) {
+ typeToNextPosFreeRegister.put(type, Integer.valueOf(0));
+ }
+ }
+
+ @Nonnull
+ RegisterSpec getThisReg() {
+ assert thisReg != null;
+ return thisReg;
+ }
+}
\ No newline at end of file
diff --git a/jack/src/com/android/jack/debug/DebugVariableInfoMarker.java b/jack/src/com/android/jack/debug/DebugVariableInfoMarker.java
index 5687bf0..0821212 100644
--- a/jack/src/com/android/jack/debug/DebugVariableInfoMarker.java
+++ b/jack/src/com/android/jack/debug/DebugVariableInfoMarker.java
@@ -18,6 +18,8 @@
import com.android.jack.ir.ast.JLocalRef;
import com.android.jack.ir.ast.JParameterRef;
+import com.android.jack.ir.ast.JSsaVariableDefRef;
+import com.android.jack.ir.ast.JSsaVariableUseRef;
import com.android.jack.ir.ast.JType;
import com.android.sched.item.Description;
import com.android.sched.marker.Marker;
@@ -30,7 +32,7 @@
/**
* This {@link Marker} contains debug information related to variable.
*/
-@ValidOn({JLocalRef.class, JParameterRef.class})
+@ValidOn({JLocalRef.class, JSsaVariableDefRef.class, JSsaVariableUseRef.class, JParameterRef.class})
@Description("This marker contains debug information related to variable.")
public class DebugVariableInfoMarker implements SerializableMarker {
diff --git a/jack/src/com/android/jack/dx/ssa/DomFront.java b/jack/src/com/android/jack/dx/ssa/DomFront.java
index 2de1687..648599d 100644
--- a/jack/src/com/android/jack/dx/ssa/DomFront.java
+++ b/jack/src/com/android/jack/dx/ssa/DomFront.java
@@ -18,8 +18,8 @@
import com.android.jack.dx.util.IntSet;
-import java.util.ArrayList;
import java.util.BitSet;
+import java.util.List;
/**
* Calculates the dominance-frontiers of a methot's basic blocks.
@@ -33,7 +33,7 @@
/** {@code non-null;} method being processed */
private final SsaMethod meth;
- private final ArrayList<SsaBasicBlock> nodes;
+ private final List<SsaBasicBlock> nodes;
private final DomInfo[] domInfos;
diff --git a/jack/src/com/android/jack/dx/ssa/NormalSsaInsn.java b/jack/src/com/android/jack/dx/ssa/NormalSsaInsn.java
index 026617f..e5940db 100644
--- a/jack/src/com/android/jack/dx/ssa/NormalSsaInsn.java
+++ b/jack/src/com/android/jack/dx/ssa/NormalSsaInsn.java
@@ -36,7 +36,7 @@
* @param insn Rop insn to wrap
* @param block block that contains this insn
*/
- NormalSsaInsn(final Insn insn, final SsaBasicBlock block) {
+ public NormalSsaInsn(final Insn insn, final SsaBasicBlock block) {
super(insn.getResult(), block);
this.insn = insn;
}
diff --git a/jack/src/com/android/jack/dx/ssa/Optimizer.java b/jack/src/com/android/jack/dx/ssa/Optimizer.java
index 8502ae2..6371971 100644
--- a/jack/src/com/android/jack/dx/ssa/Optimizer.java
+++ b/jack/src/com/android/jack/dx/ssa/Optimizer.java
@@ -18,6 +18,7 @@
import com.android.jack.dx.rop.code.RopMethod;
import com.android.jack.dx.rop.code.TranslationAdvice;
+import com.android.jack.dx.ssa.Optimizer.OptionalStep;
import com.android.jack.dx.ssa.back.LivenessAnalyzer;
import com.android.jack.dx.ssa.back.SsaToRop;
@@ -126,6 +127,85 @@
}
/**
+ * Runs optimization algorthims over this SSA method, and returns a new
+ * instance of RopMethod with the changes.
+ *
+ * @param ssaMethod method to process
+ * @param paramWidth the total width, in register-units, of this method's
+ * parameters
+ * @param isStatic true if this method has no 'this' pointer argument.
+ * @param inPreserveLocals true if local variable info should be preserved,
+ * at the cost of some registers and insns
+ * @param removeRedundantConditionalBranch true if we should optimize unneccesary conditional
+ * branches.
+ * @param inAdvice {@code non-null;} translation advice
+ * @return optimized method
+ */
+ public static RopMethod optimize(
+ SsaMethod ssaMethod,
+ int paramWidth,
+ boolean isStatic,
+ boolean inPreserveLocals,
+ boolean removeRedundantConditionalBranch,
+ TranslationAdvice inAdvice) {
+ EnumSet<OptionalStep> steps = EnumSet.allOf(OptionalStep.class);
+ return optimize(ssaMethod, paramWidth, isStatic, inPreserveLocals,
+ removeRedundantConditionalBranch, inAdvice, steps);
+ }
+ /**
+ * Dex bytecode does not have instruction forms that take register numbers larger than 15 for all
+ * instructions. If we've produced a method that uses more than 16 registers, try again by
+ * removing the CONST_COLLECTOR step to see if we can get under the bar. The end result will be
+ * much more efficient.
+ *
+ * @param ssaMethod method to process
+ * @param paramWidth the total width, in register-units, of this method's parameters
+ * @param isStatic true if this method has no 'this' pointer argument.
+ * @param removeRedundantConditionalBranch true if we should optimize unneccesary conditional
+ * branches.
+ * @return optimized method
+ */
+ public static RopMethod optimizeMinimizeRegisters(SsaMethod ssaMethod,
+ int paramWidth,
+ boolean isStatic,
+ boolean inPreserveLocals,
+ boolean removeRedundantConditionalBranch,
+ TranslationAdvice inAdvice) {
+ EnumSet<OptionalStep> steps = EnumSet.allOf(OptionalStep.class);
+ steps.remove(OptionalStep.CONST_COLLECTOR);
+ return optimize(ssaMethod, paramWidth, isStatic, inPreserveLocals,
+ removeRedundantConditionalBranch, inAdvice, steps);
+ }
+ /**
+ * Runs optimization algorthims over this SSA method, and returns a new
+ * instance of RopMethod with the changes.
+ *
+ * @param ssaMethod method to process
+ * @param paramWidth the total width, in register-units, of this method's
+ * parameters
+ * @param isStatic true if this method has no 'this' pointer argument.
+ * @param inPreserveLocals true if local variable info should be preserved,
+ * at the cost of some registers and insns
+ * @param removeRedundantConditionalBranch true if we should optimize unneccesary conditional
+ * branches.
+ * @param inAdvice {@code non-null;} translation advice
+ * @param steps set of optional optimization steps to run
+ * @return optimized method
+ */
+ public static RopMethod optimize(SsaMethod ssaMethod,
+ int paramWidth,
+ boolean isStatic,
+ boolean inPreserveLocals,
+ boolean removeRedundantConditionalBranch,
+ TranslationAdvice inAdvice,
+ EnumSet<OptionalStep> steps) {
+ preserveLocals = inPreserveLocals;
+ advice = inAdvice;
+ runSsaFormSteps(ssaMethod, steps);
+ return SsaToRop.convertToRopMethod(ssaMethod, removeRedundantConditionalBranch);
+ }
+
+ /**
* Dex bytecode does not have instruction forms that take register numbers larger than 15 for all
* instructions. If we've produced a method that uses more than 16 registers, try again by
* removing the CONST_COLLECTOR step to see if we can get under the bar. The end result will be
diff --git a/jack/src/com/android/jack/dx/ssa/PhiInsn.java b/jack/src/com/android/jack/dx/ssa/PhiInsn.java
index d7419d6..f5f8014 100644
--- a/jack/src/com/android/jack/dx/ssa/PhiInsn.java
+++ b/jack/src/com/android/jack/dx/ssa/PhiInsn.java
@@ -128,13 +128,49 @@
* @param predBlock predecessor block to be associated with this operand
*/
public void addPhiOperand(RegisterSpec registerSpec, SsaBasicBlock predBlock) {
- operands.add(new Operand(registerSpec, predBlock.getIndex(), predBlock.getRopLabel()));
+ addPhiOperand(registerSpec, predBlock.getIndex(), predBlock.getRopLabel());
+ }
+ /**
+ * Adds an operand to this phi instruction.
+ *
+ * @param registerSpec register spec, including type and reg of operand
+ * @param predIndex predecessor index.
+ * @param predLabel predecessor ROP label.
+ */
+ public void addPhiOperand(RegisterSpec registerSpec, int predIndex, int predLabel) {
+ operands.add(new Operand(registerSpec, predIndex, predLabel));
// Un-cache sources, in case someone has already called getSources().
sources = null;
}
/**
+ * Switches the operand ID from ROP block label to SSA block index.
+ *
+ * @param labelToIndex A map that proves ROP block label to SSA block index.
+ */
+ public void resolveOperandBlockIndex(int[] labelToIndex) {
+ for (Operand o : operands) {
+ o.blockIndex = labelToIndex[o.ropLabel];
+ }
+ }
+
+ public void changeOperandPred(SsaBasicBlock oldPred, SsaBasicBlock newPred) {
+ boolean found = false;
+ for (Operand o : operands) {
+ if (o.blockIndex == oldPred.getIndex()) {
+ assert o.ropLabel == oldPred.getRopLabel();
+ o.blockIndex = newPred.getIndex();
+ o.ropLabel = newPred.getRopLabel();
+ found = true;
+ }
+ }
+ if (!found) {
+ throw new RuntimeException("Predecessor does not exist in phi node!");
+ }
+ }
+
+ /**
* Removes all operand uses of a register from this phi instruction.
*
* @param registerSpec register spec, including type and reg of operand
@@ -377,8 +413,8 @@
*/
private static class Operand {
public RegisterSpec regSpec;
- public final int blockIndex;
- public final int ropLabel; // only used for debugging
+ public int blockIndex;
+ public int ropLabel; // only used for debugging
public Operand(RegisterSpec regSpec, int blockIndex, int ropLabel) {
this.regSpec = regSpec;
diff --git a/jack/src/com/android/jack/dx/ssa/SsaBasicBlock.java b/jack/src/com/android/jack/dx/ssa/SsaBasicBlock.java
index c6b97c2..202c614 100644
--- a/jack/src/com/android/jack/dx/ssa/SsaBasicBlock.java
+++ b/jack/src/com/android/jack/dx/ssa/SsaBasicBlock.java
@@ -135,6 +135,14 @@
domChildren = new ArrayList<SsaBasicBlock>();
}
+ public SsaBasicBlock(final int basicBlockIndex, final SsaMethod parent) {
+ this.parent = parent;
+ this.index = basicBlockIndex;
+ this.predecessors = new BitSet();
+ this.successors = new BitSet();
+ domChildren = new ArrayList<SsaBasicBlock>();
+ }
+
/**
* Creates a new SSA basic block from a ROP form basic block.
*
@@ -864,6 +872,39 @@
}
/**
+ * Sets the instructions in the block all at once.
+ *
+ * @param insns new list of instructions.
+ */
+ public void setInsns(List<SsaInsn> insns) {
+ this.insns = insns;
+ }
+
+ /**
+ * Sets the label for branching.
+ *
+ * @param ropLabel Rop branching label.
+ */
+ public void setRopLabel(int ropLabel) {
+ this.ropLabel = ropLabel;
+ }
+
+ public void setSuccessors(final IntList successorList, final int primarySuccessor) {
+ this.successorList = successorList;
+ this.primarySuccessor = primarySuccessor;
+ this.primarySuccessor = primarySuccessor;
+ this.successorList = successorList;
+ successors = new BitSet();
+ for (int i = 0; i < successorList.size(); i++) {
+ successors.set(successorList.get(i));
+ }
+ }
+
+ public void addPredeccessors(int index) {
+ predecessors.set(index);
+ }
+
+ /**
* Sorts move instructions added via {@code addMoveToEnd} during
* phi removal so that results don't overwrite sources that are used.
* For use after all phis have been removed and all calls to
diff --git a/jack/src/com/android/jack/dx/ssa/SsaMethod.java b/jack/src/com/android/jack/dx/ssa/SsaMethod.java
index 0ed0ed0..b16f2bb 100644
--- a/jack/src/com/android/jack/dx/ssa/SsaMethod.java
+++ b/jack/src/com/android/jack/dx/ssa/SsaMethod.java
@@ -124,6 +124,16 @@
this.spareRegisterBase = registerCount;
}
+ public SsaMethod(int paramWidth, boolean isStatic, int maxLabel, int registerCount) {
+ this.paramWidth = paramWidth;
+ this.isStatic = isStatic;
+ this.backMode = false;
+ this.maxLabel = maxLabel;
+ this.registerCount = registerCount;
+ this.spareRegisterBase = registerCount;
+ this.exitBlockIndex = -1; // This gets made later.
+ }
+
/**
* Builds a BitSet of block indices from a basic block list and a list
* of labels taken from Rop form.
@@ -187,7 +197,7 @@
* is called after edge-splitting and phi insertion, since the edges
* going into the exit block should not be considered in those steps.
*/
- /*package*/void makeExitBlock() {
+ public void makeExitBlock() {
if (exitBlockIndex >= 0) {
throw new RuntimeException("must be called at most once");
}
@@ -866,6 +876,37 @@
}
/**
+ * Sets the blocks.
+ *
+ * @param blocks list of all the SSA blocks.
+ */
+ public void setBlocks(ArrayList<SsaBasicBlock> blocks) {
+ this.blocks = blocks;
+ for (SsaBasicBlock b : blocks) {
+ if (b.getRopLabel() >= maxLabel) {
+ maxLabel = b.getRopLabel() + 1;
+ }
+ }
+ }
+
+ /**
+ * Sets number of SSA register.
+ *
+ * @param count total number of SSA register.
+ */
+ public void setRegisterCount(int count) {
+ this.registerCount = count;
+ }
+
+ public void setEntryBlockIndex(int entryBlockIndex) {
+ this.entryBlockIndex = entryBlockIndex;
+ }
+
+ public void setMaxLabel(int maxLabel) {
+ this.maxLabel = maxLabel;
+ }
+
+ /**
* Sets "back-convert mode". Set during back-conversion when registers
* are about to be mapped into a non-SSA namespace. When true,
* use and def lists are unavailable.
@@ -875,4 +916,4 @@
useList = null;
definitionList = null;
}
-}
+}
\ No newline at end of file
diff --git a/jack/src/com/android/jack/ir/ast/JAbstractStringLiteral.java b/jack/src/com/android/jack/ir/ast/JAbstractStringLiteral.java
index 8f911fc..17350c2 100644
--- a/jack/src/com/android/jack/ir/ast/JAbstractStringLiteral.java
+++ b/jack/src/com/android/jack/ir/ast/JAbstractStringLiteral.java
@@ -17,6 +17,9 @@
import com.android.jack.Jack;
import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.cfg.JCaseBlockElement;
+import com.android.jack.ir.ast.cfg.JReturnBlockElement;
+import com.android.jack.ir.ast.cfg.JSwitchBlockElement;
import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.jack.lookup.CommonTypes;
import com.android.sched.item.Description;
@@ -57,8 +60,11 @@
|| parent instanceof JNameValuePair
|| parent instanceof JAssertStatement
|| parent instanceof JCaseStatement
+ || parent instanceof JCaseBlockElement
|| parent instanceof JReturnStatement
+ || parent instanceof JReturnBlockElement
|| parent instanceof JSwitchStatement
+ || parent instanceof JSwitchBlockElement
|| parent instanceof JAnnotationMethod
|| parent instanceof JFieldInitializer
|| parent instanceof JSynchronizedBlock)) {
diff --git a/jack/src/com/android/jack/ir/ast/JBooleanLiteral.java b/jack/src/com/android/jack/ir/ast/JBooleanLiteral.java
index abc5ff3..9a3e878 100644
--- a/jack/src/com/android/jack/ir/ast/JBooleanLiteral.java
+++ b/jack/src/com/android/jack/ir/ast/JBooleanLiteral.java
@@ -17,6 +17,8 @@
import com.android.jack.ir.JNodeInternalError;
import com.android.jack.ir.ast.JPrimitiveType.JPrimitiveTypeEnum;
+import com.android.jack.ir.ast.cfg.JConditionalBlockElement;
+import com.android.jack.ir.ast.cfg.JReturnBlockElement;
import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.sched.item.Component;
import com.android.sched.item.Description;
@@ -74,7 +76,9 @@
|| parent instanceof JDoStatement
|| parent instanceof JForStatement
|| parent instanceof JIfStatement
+ || parent instanceof JConditionalBlockElement
|| parent instanceof JReturnStatement
+ || parent instanceof JReturnBlockElement
|| parent instanceof JFieldInitializer
|| parent instanceof JWhileStatement)) {
throw new JNodeInternalError(this, "Invalid parent");
diff --git a/jack/src/com/android/jack/ir/ast/JByteLiteral.java b/jack/src/com/android/jack/ir/ast/JByteLiteral.java
index 20347a3..a0aef56 100644
--- a/jack/src/com/android/jack/ir/ast/JByteLiteral.java
+++ b/jack/src/com/android/jack/ir/ast/JByteLiteral.java
@@ -16,6 +16,8 @@
package com.android.jack.ir.ast;
import com.android.jack.ir.ast.JPrimitiveType.JPrimitiveTypeEnum;
+import com.android.jack.ir.ast.cfg.JCaseBlockElement;
+import com.android.jack.ir.ast.cfg.JSwitchBlockElement;
import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.sched.item.Component;
import com.android.sched.item.Description;
@@ -80,7 +82,10 @@
@Override
public void checkValidity() {
- if (!(parent instanceof JSwitchStatement || parent instanceof JCaseStatement)) {
+ if (!(parent instanceof JSwitchStatement
+ || parent instanceof JSwitchBlockElement
+ || parent instanceof JCaseStatement
+ || parent instanceof JCaseBlockElement)) {
super.checkValidity();
}
}
diff --git a/jack/src/com/android/jack/ir/ast/JCharLiteral.java b/jack/src/com/android/jack/ir/ast/JCharLiteral.java
index 0e0446b..abded1e 100644
--- a/jack/src/com/android/jack/ir/ast/JCharLiteral.java
+++ b/jack/src/com/android/jack/ir/ast/JCharLiteral.java
@@ -16,6 +16,8 @@
package com.android.jack.ir.ast;
import com.android.jack.ir.ast.JPrimitiveType.JPrimitiveTypeEnum;
+import com.android.jack.ir.ast.cfg.JCaseBlockElement;
+import com.android.jack.ir.ast.cfg.JSwitchBlockElement;
import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.sched.item.Component;
import com.android.sched.item.Description;
@@ -78,7 +80,10 @@
@Override
public void checkValidity() {
- if (!(parent instanceof JSwitchStatement || parent instanceof JCaseStatement)) {
+ if (!(parent instanceof JSwitchStatement
+ || parent instanceof JSwitchBlockElement
+ || parent instanceof JCaseStatement
+ || parent instanceof JCaseBlockElement)) {
super.checkValidity();
}
}
diff --git a/jack/src/com/android/jack/ir/ast/JClassLiteral.java b/jack/src/com/android/jack/ir/ast/JClassLiteral.java
index ec04608..24177e7 100644
--- a/jack/src/com/android/jack/ir/ast/JClassLiteral.java
+++ b/jack/src/com/android/jack/ir/ast/JClassLiteral.java
@@ -17,6 +17,7 @@
import com.android.jack.Jack;
import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.cfg.JReturnBlockElement;
import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.jack.lookup.CommonTypes;
import com.android.sched.item.Component;
@@ -91,6 +92,7 @@
|| parent instanceof JAnnotationMethod
|| parent instanceof JFieldInitializer
|| parent instanceof JReturnStatement
+ || parent instanceof JReturnBlockElement
|| parent instanceof JSynchronizedBlock
|| parent instanceof JLock
|| parent instanceof JUnlock)) {
diff --git a/jack/src/com/android/jack/ir/ast/JConcreteMethodBody.java b/jack/src/com/android/jack/ir/ast/JConcreteMethodBody.java
new file mode 100644
index 0000000..16092b1
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/JConcreteMethodBody.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast;
+
+import com.android.jack.Jack;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * Base implementation for regular and cfg method body,
+ * represents a the body of a method. Can be Java or JSNI.
+ */
+public abstract class JConcreteMethodBody extends JAbstractMethodBody {
+ @Nonnull
+ protected final List<JLocal> locals = new LinkedList<>();
+
+ public JConcreteMethodBody(@Nonnull SourceInfo info) {
+ this(info, Collections.<JLocal>emptyList());
+ }
+
+ public JConcreteMethodBody(@Nonnull SourceInfo info, @Nonnull List<JLocal> locals) {
+ super(info);
+ this.locals.addAll(locals);
+ }
+
+ /**
+ * Adds a local to this method body.
+ */
+ public void addLocal(@Nonnull JLocal local) {
+ locals.add(local);
+ }
+
+ /**
+ * Returns this method's local variables.
+ */
+ @Nonnull
+ public List<JLocal> getLocals() {
+ return Jack.getUnmodifiableCollections().getUnmodifiableList(locals);
+ }
+
+ @Override
+ public boolean isNative() {
+ return false;
+ }
+
+ /**
+ * Removes a local from this method body.
+ */
+ public void removeLocal(@Nonnull JLocal localToDelete) {
+ locals.remove(localToDelete);
+ }
+
+ /**
+ * Removes locals from this method body.
+ */
+ public void removeLocals(@Nonnull List<JLocal> localsToDelete) {
+ locals.removeAll(localsToDelete);
+ }
+
+ final void acceptLocals(@Nonnull JVisitor visitor) {
+ visitor.accept(locals);
+ }
+
+ final void traverseLocals(
+ @Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ for (JLocal local : locals) {
+ local.traverse(schedule);
+ }
+ }
+
+ @Override
+ protected void transform(@Nonnull JNode existingNode, @CheckForNull JNode newNode,
+ @Nonnull Transformation transformation) throws UnsupportedOperationException {
+ if (!transform(locals, existingNode, (JLocal) newNode, transformation)) {
+ super.transform(existingNode, newNode, transformation);
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/JConstructor.java b/jack/src/com/android/jack/ir/ast/JConstructor.java
index 1cfa1bd..9fc3cd8 100644
--- a/jack/src/com/android/jack/ir/ast/JConstructor.java
+++ b/jack/src/com/android/jack/ir/ast/JConstructor.java
@@ -69,11 +69,6 @@
return false;
}
- @Override
- public JMethodBody getBody() {
- return (JMethodBody) super.getBody();
- }
-
@Nonnull
@Override
public JDefinedClass getEnclosingType() {
diff --git a/jack/src/com/android/jack/ir/ast/JEnumLiteral.java b/jack/src/com/android/jack/ir/ast/JEnumLiteral.java
index f605d0d..4926198 100644
--- a/jack/src/com/android/jack/ir/ast/JEnumLiteral.java
+++ b/jack/src/com/android/jack/ir/ast/JEnumLiteral.java
@@ -16,6 +16,9 @@
package com.android.jack.ir.ast;
+import com.android.jack.ir.ast.cfg.JCaseBlockElement;
+import com.android.jack.ir.ast.cfg.JReturnBlockElement;
+import com.android.jack.ir.ast.cfg.JSwitchBlockElement;
import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.sched.item.Component;
import com.android.sched.item.Description;
@@ -79,8 +82,11 @@
|| parent instanceof JNameValuePair
|| parent instanceof JAnnotationMethod
|| parent instanceof JCaseStatement
+ || parent instanceof JCaseBlockElement
|| parent instanceof JSwitchStatement
+ || parent instanceof JSwitchBlockElement
|| parent instanceof JReturnStatement
+ || parent instanceof JReturnBlockElement
|| parent instanceof JFieldInitializer
|| parent instanceof JSynchronizedBlock)) {
super.checkValidity();
diff --git a/jack/src/com/android/jack/ir/ast/JExpression.java b/jack/src/com/android/jack/ir/ast/JExpression.java
index ab285b2..c2d8716 100644
--- a/jack/src/com/android/jack/ir/ast/JExpression.java
+++ b/jack/src/com/android/jack/ir/ast/JExpression.java
@@ -16,6 +16,7 @@
package com.android.jack.ir.ast;
import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.sched.item.Description;
@@ -46,7 +47,9 @@
@Override
public void checkValidity() {
- if (!(parent instanceof JExpression || parent instanceof JStatement)) {
+ if (!(parent instanceof JExpression
+ || parent instanceof JStatement
+ || parent instanceof JBasicBlockElement)) {
throw new JNodeInternalError(this, "Invalid parent");
}
}
diff --git a/jack/src/com/android/jack/ir/ast/JIntLiteral.java b/jack/src/com/android/jack/ir/ast/JIntLiteral.java
index 854d52d..5dbce2c 100644
--- a/jack/src/com/android/jack/ir/ast/JIntLiteral.java
+++ b/jack/src/com/android/jack/ir/ast/JIntLiteral.java
@@ -16,6 +16,8 @@
package com.android.jack.ir.ast;
import com.android.jack.ir.ast.JPrimitiveType.JPrimitiveTypeEnum;
+import com.android.jack.ir.ast.cfg.JCaseBlockElement;
+import com.android.jack.ir.ast.cfg.JSwitchBlockElement;
import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.sched.item.Component;
import com.android.sched.item.Description;
@@ -78,7 +80,10 @@
@Override
public void checkValidity() {
- if (!(parent instanceof JSwitchStatement || parent instanceof JCaseStatement)) {
+ if (!(parent instanceof JSwitchStatement
+ || parent instanceof JSwitchBlockElement
+ || parent instanceof JCaseStatement
+ || parent instanceof JCaseBlockElement)) {
super.checkValidity();
}
}
diff --git a/jack/src/com/android/jack/ir/ast/JLocal.java b/jack/src/com/android/jack/ir/ast/JLocal.java
index 86ccd79..e12c414 100644
--- a/jack/src/com/android/jack/ir/ast/JLocal.java
+++ b/jack/src/com/android/jack/ir/ast/JLocal.java
@@ -16,6 +16,7 @@
package com.android.jack.ir.ast;
import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.cfg.JCatchBasicBlock;
import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.sched.item.Component;
import com.android.sched.item.Description;
@@ -32,10 +33,10 @@
public class JLocal extends JVariable implements HasEnclosingMethod {
@CheckForNull
- private JMethodBody enclosingMethodBody;
+ private JConcreteMethodBody enclosingMethodBody;
public JLocal(SourceInfo info, String name, JType type, int modifier,
- @CheckForNull JMethodBody enclosingMethodBody) {
+ @CheckForNull JConcreteMethodBody enclosingMethodBody) {
super(info, name, type, modifier);
assert JModifier.isLocalModifier(modifier);
this.enclosingMethodBody = enclosingMethodBody;
@@ -53,7 +54,7 @@
return null;
}
- public void setEnclosingMethodBody(@Nonnull JMethodBody enclosingMethodBody) {
+ public void setEnclosingMethodBody(@Nonnull JConcreteMethodBody enclosingMethodBody) {
this.enclosingMethodBody = enclosingMethodBody;
}
@@ -81,7 +82,8 @@
@Override
public void checkValidity() {
- if (!(parent instanceof JMethodBody || parent instanceof JCatchBlock)) {
+ if (!(parent instanceof JConcreteMethodBody ||
+ parent instanceof JCatchBlock || parent instanceof JCatchBasicBlock)) {
throw new JNodeInternalError(this, "Invalid parent");
}
}
diff --git a/jack/src/com/android/jack/ir/ast/JMethodBody.java b/jack/src/com/android/jack/ir/ast/JMethodBody.java
index 148e4d3..ccf518f 100644
--- a/jack/src/com/android/jack/ir/ast/JMethodBody.java
+++ b/jack/src/com/android/jack/ir/ast/JMethodBody.java
@@ -16,83 +16,43 @@
package com.android.jack.ir.ast;
-import com.android.jack.Jack;
import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.sched.item.Component;
import com.android.sched.item.Description;
import com.android.sched.scheduler.ScheduleInstance;
import com.android.sched.transform.TransformRequest;
-import java.util.LinkedList;
import java.util.List;
-
-import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
* Represents a the body of a method. Can be Java or JSNI.
*/
-@Description("Represents a the body of a Java method")
-public class JMethodBody extends JAbstractMethodBody {
+@Description("Represents a the body of a Java method as regular IR")
+public class JMethodBody extends JConcreteMethodBody {
@Nonnull
private JBlock block;
- @Nonnull
- private final List<JLocal> locals = new LinkedList<JLocal>();
public JMethodBody(@Nonnull SourceInfo info, @Nonnull JBlock block) {
super(info);
this.block = block;
}
- /**
- * Adds a local to this method body.
- */
- public void addLocal(@Nonnull JLocal local) {
- locals.add(local);
- }
-
@Nonnull
public JBlock getBlock() {
return block;
}
- /**
- * Returns this method's local variables.
- */
- @Nonnull
- public List<JLocal> getLocals() {
- return Jack.getUnmodifiableCollections().getUnmodifiableList(locals);
- }
-
@Nonnull
public List<JStatement> getStatements() {
return block.getStatements();
}
@Override
- public boolean isNative() {
- return false;
- }
-
- /**
- * Removes a local from this method body.
- */
- public void removeLocal(@Nonnull JLocal localToDelete) {
- locals.remove(localToDelete);
- }
-
- /**
- * Removes locals from this method body.
- */
- public void removeLocals(@Nonnull List<JLocal> localsToDelete) {
- locals.removeAll(localsToDelete);
- }
-
- @Override
public void traverse(@Nonnull JVisitor visitor) {
if (visitor.visit(this)) {
- visitor.accept(locals);
+ acceptLocals(visitor);
visitor.accept(block);
}
visitor.endVisit(this);
@@ -101,25 +61,13 @@
@Override
public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
schedule.process(this);
- for (JLocal local : locals) {
- local.traverse(schedule);
- }
+ traverseLocals(schedule);
block.traverse(schedule);
}
@Override
- protected void transform(@Nonnull JNode existingNode, @CheckForNull JNode newNode,
- @Nonnull Transformation transformation) throws UnsupportedOperationException {
- if (!transform(locals, existingNode, (JLocal) newNode, transformation)) {
- super.transform(existingNode, newNode, transformation);
- }
- }
-
- @Override
protected void replaceImpl(@Nonnull JNode existingNode, @Nonnull JNode newNode)
throws UnsupportedOperationException {
- assert newNode != null;
-
if (block == existingNode) {
block = (JBlock) newNode;
} else {
@@ -128,8 +76,8 @@
}
@Override
- public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest transformRequest)
- throws Exception {
+ public void visit(
+ @Nonnull JVisitor visitor, @Nonnull TransformRequest transformRequest) throws Exception {
visitor.visit(this, transformRequest);
}
}
diff --git a/jack/src/com/android/jack/ir/ast/JMethodBodyCfg.java b/jack/src/com/android/jack/ir/ast/JMethodBodyCfg.java
new file mode 100644
index 0000000..43fbc42
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/JMethodBodyCfg.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2008 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package com.android.jack.ir.ast;
+
+import com.google.common.collect.Lists;
+
+import com.android.jack.Jack;
+import com.android.jack.ir.ast.cfg.JCatchBasicBlock;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.item.Description;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * Represents a the body of a method as a CFG. Can be Java or JSNI.
+ */
+@Description("Represents a the body of a Java method as CFG")
+public class JMethodBodyCfg extends JConcreteMethodBody {
+ @Nonnull
+ private JControlFlowGraph cfg;
+
+ @Nonnull
+ private final List<JLocal> catchLocals = Lists.newLinkedList();
+
+ @Nonnull
+ private final List<JSsaVariableDefRef> params = new ArrayList<>();
+
+ public JMethodBodyCfg(@Nonnull SourceInfo info, @Nonnull List<JLocal> locals) {
+ super(info, locals);
+ cfg = new JControlFlowGraph(this.getSourceInfo());
+ cfg.updateParents(this);
+ }
+
+ @Override
+ @Nonnull
+ public JMethod getMethod() {
+ JNode parent = getParent();
+ assert parent instanceof JMethod;
+ return (JMethod) parent;
+ }
+
+ @Nonnull
+ public JControlFlowGraph getCfg() {
+ return cfg;
+ }
+
+ public void addCatchLocal(@Nonnull JLocal catchLocal) {
+ assert catchLocal.getParent() instanceof JCatchBasicBlock;
+ assert !catchLocals.contains(catchLocal);
+ catchLocals.add(catchLocal);
+ }
+
+ public void removeCatchLocal(@Nonnull JLocal catchLocal) {
+ assert catchLocals.contains(catchLocal);
+ catchLocals.remove(catchLocal);
+ }
+
+ @Nonnegative
+ public int getNumCatchLocals() {
+ return catchLocals.size();
+ }
+
+ @Nonnull
+ public List<JLocal> getCatchLocals() {
+ return Jack.getUnmodifiableCollections().getUnmodifiableList(catchLocals);
+ }
+
+ public void addSsaParamDef(@Nonnull JSsaVariableDefRef param) {
+ params.add(param);
+ }
+
+ @Nonnull
+ public List<JSsaVariableDefRef> getSsaParamsDefs() {
+ return params;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ acceptLocals(visitor);
+ visitor.accept(cfg);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ traverseLocals(schedule);
+ cfg.traverse(schedule);
+ }
+
+ @Override
+ protected void replaceImpl(@Nonnull JNode existingNode, @Nonnull JNode newNode) {
+ if (cfg == existingNode) {
+ cfg = (JControlFlowGraph) newNode;
+ } else {
+ super.replaceImpl(existingNode, newNode);
+ }
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/JNullLiteral.java b/jack/src/com/android/jack/ir/ast/JNullLiteral.java
index 5c37305..5688236 100644
--- a/jack/src/com/android/jack/ir/ast/JNullLiteral.java
+++ b/jack/src/com/android/jack/ir/ast/JNullLiteral.java
@@ -15,6 +15,7 @@
*/
package com.android.jack.ir.ast;
+import com.android.jack.ir.ast.cfg.JReturnBlockElement;
import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.sched.item.Component;
import com.android.sched.item.Description;
@@ -62,6 +63,7 @@
|| parent instanceof JThrowStatement
|| parent instanceof JNameValuePair
|| parent instanceof JReturnStatement
+ || parent instanceof JReturnBlockElement
|| parent instanceof JFieldInitializer)) {
super.checkValidity();
}
diff --git a/jack/src/com/android/jack/ir/ast/JNumberValueLiteral.java b/jack/src/com/android/jack/ir/ast/JNumberValueLiteral.java
index 28b6eca..a99c871 100644
--- a/jack/src/com/android/jack/ir/ast/JNumberValueLiteral.java
+++ b/jack/src/com/android/jack/ir/ast/JNumberValueLiteral.java
@@ -17,6 +17,7 @@
package com.android.jack.ir.ast;
import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.cfg.JReturnBlockElement;
import com.android.jack.ir.sourceinfo.SourceInfo;
/**
@@ -34,6 +35,7 @@
|| parent instanceof JNameValuePair
|| parent instanceof JAnnotationMethod
|| parent instanceof JReturnStatement
+ || parent instanceof JReturnBlockElement
|| parent instanceof JFieldInitializer)) {
throw new JNodeInternalError(this, "Invalid parent");
}
diff --git a/jack/src/com/android/jack/ir/ast/JShortLiteral.java b/jack/src/com/android/jack/ir/ast/JShortLiteral.java
index d7889db..a64bd06 100644
--- a/jack/src/com/android/jack/ir/ast/JShortLiteral.java
+++ b/jack/src/com/android/jack/ir/ast/JShortLiteral.java
@@ -16,6 +16,8 @@
package com.android.jack.ir.ast;
import com.android.jack.ir.ast.JPrimitiveType.JPrimitiveTypeEnum;
+import com.android.jack.ir.ast.cfg.JCaseBlockElement;
+import com.android.jack.ir.ast.cfg.JSwitchBlockElement;
import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.sched.item.Component;
import com.android.sched.item.Description;
@@ -80,7 +82,10 @@
@Override
public void checkValidity() {
- if (!(parent instanceof JSwitchStatement || parent instanceof JCaseStatement)) {
+ if (!(parent instanceof JSwitchStatement
+ || parent instanceof JSwitchBlockElement
+ || parent instanceof JCaseStatement
+ || parent instanceof JCaseBlockElement)) {
super.checkValidity();
}
}
diff --git a/jack/src/com/android/jack/ir/ast/JSsaVariableDefRef.java b/jack/src/com/android/jack/ir/ast/JSsaVariableDefRef.java
new file mode 100644
index 0000000..e5537cb
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/JSsaVariableDefRef.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast;
+
+import com.google.common.collect.Lists;
+
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import java.util.List;
+
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * This version of the SSA variable reference only appears on the left hand side of an assignment
+ * except for a few exceptions.
+ *
+ */
+public class JSsaVariableDefRef extends JSsaVariableRef {
+
+ @Nonnull
+ private final List<JSsaVariableUseRef> uses;
+
+ private int regNum = -1;
+
+ public JSsaVariableDefRef(@Nonnull SourceInfo info, @Nonnull JVariable target,
+ @Nonnegative int version) {
+ super(info, target, version);
+ this.uses = Lists.newLinkedList();
+ }
+
+ public JSsaVariableUseRef makeRef(@Nonnull SourceInfo info) {
+ JSsaVariableUseRef use = new JSsaVariableUseRef(info, target, this.getVersion(), this);
+ uses.add(use);
+ return use;
+ }
+
+ @Nonnull
+ public List<JSsaVariableUseRef> getUses() {
+ return uses;
+ }
+
+ public boolean removeUse(JSsaVariableUseRef use) {
+ int index = uses.indexOf(use);
+ if (index == -1) {
+ return false;
+ }
+ uses.remove(index);
+ return true;
+ }
+
+ /**
+ * Removes all referenced {@link JSsaVariableUseRef} objects.
+ */
+ public void removeUses() {
+ uses.clear();
+ }
+
+ public boolean hasUses() {
+ return !uses.isEmpty();
+ }
+
+ public boolean hasUsesOutsideOfPhis() {
+ for (JSsaVariableUseRef use : uses) {
+ if (use.isPhiUse()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void setRopRegister(@Nonnegative int reg) {
+ assert this.regNum == -1;
+ this.regNum = reg;
+ }
+
+ public int getRopRegister() {
+ return this.regNum;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest transformRequest)
+ throws Exception {
+ visitor.visit(this, transformRequest);
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/JSsaVariableDefRefPlaceHolder.java b/jack/src/com/android/jack/ir/ast/JSsaVariableDefRefPlaceHolder.java
new file mode 100644
index 0000000..38bbbb5
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/JSsaVariableDefRefPlaceHolder.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast;
+
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This version of the SSA variable reference only appears on the left hand side of an assignment
+ * except for a few exceptions.
+ *
+ */
+public class JSsaVariableDefRefPlaceHolder extends JSsaVariableDefRef {
+
+ public JSsaVariableDefRefPlaceHolder(@Nonnull SourceInfo info, @Nonnull JVariable target) {
+ super(info, target, 0);
+ }
+
+ @Override
+ public JSsaVariableUseRef makeRef(@Nonnull SourceInfo info) {
+ return new JSsaVariableUseRefPlaceHolder(info, this.getTarget(), this);
+ }
+
+ @Override
+ @Nonnull
+ public List<JSsaVariableUseRef> getUses() {
+ throw new UnsupportedOperationException("Should not be called on place holder variables");
+ }
+
+ @Override
+ public boolean removeUse(JSsaVariableUseRef use) {
+ throw new UnsupportedOperationException("Should not be called on place holder variables");
+ }
+
+ @Override
+ public boolean hasUses() {
+ throw new UnsupportedOperationException("Should not be called on place holder variables");
+ }
+
+ @Override
+ public boolean hasUsesOutsideOfPhis() {
+ throw new UnsupportedOperationException("Should not be called on place holder variables");
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest transformRequest)
+ throws Exception {
+ visitor.visit(this, transformRequest);
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/JSsaVariableRef.java b/jack/src/com/android/jack/ir/ast/JSsaVariableRef.java
new file mode 100644
index 0000000..fa41544
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/JSsaVariableRef.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast;
+
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.item.Description;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * Presents a variable reference that is in SSA form.
+ *
+ * For all variables X in the AST, a renamed variable X_1 will be a {@link JVariableRef} with a
+ * version number 1.
+ *
+ * JVariableRef of different version is considered to be behave like completely different variables
+ * in SSA form.
+ */
+@Description("Represents a reference to an SSA variable.")
+public abstract class JSsaVariableRef extends JVariableRef {
+
+ @Nonnegative
+ private final int version;
+
+ /**
+ * Constructs a JSsaVariableRef.
+ *
+ * @param target The original variable without renaming / versioning.
+ * @Param version The version number of the variable if it is renamed.
+ */
+ public JSsaVariableRef(@Nonnull SourceInfo info, @Nonnull JVariable target,
+ @Nonnegative int version) {
+ super(info, target);
+ this.version = version;
+ }
+
+ /**
+ * @return The version number of the variable it is referencing.
+ */
+ @Nonnegative
+ public int getVersion() {
+ return version;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest transformRequest)
+ throws Exception {
+ visitor.visit(this, transformRequest);
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/JSsaVariableUseRef.java b/jack/src/com/android/jack/ir/ast/JSsaVariableUseRef.java
new file mode 100644
index 0000000..85151f3
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/JSsaVariableUseRef.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast;
+
+import com.android.jack.ir.ast.cfg.JPhiBlockElement;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * This version of the SSA variable reference only appears on the right hand side of any
+ * assignments.
+ */
+public class JSsaVariableUseRef extends JSsaVariableRef {
+
+ @Nonnull
+ private final JSsaVariableDefRef def;
+
+ /* package */ JSsaVariableUseRef(@Nonnull SourceInfo info, @Nonnull JVariable target,
+ @Nonnegative int version, JSsaVariableDefRef def) {
+ super(info, target, version);
+ this.def = def;
+ }
+
+ /**
+ * @return true if it is used in a Phi element.
+ */
+ public boolean isPhiUse() {
+ JNode parent = getParent();
+ return parent instanceof JPhiBlockElement;
+ }
+
+ @Nonnull
+ public JSsaVariableDefRef getDef() {
+ return def;
+ }
+
+ public void deleteUseFromDef() {
+ boolean result = def.removeUse(this);
+ assert result;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest transformRequest)
+ throws Exception {
+ visitor.visit(this, transformRequest);
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/JSsaVariableUseRefPlaceHolder.java b/jack/src/com/android/jack/ir/ast/JSsaVariableUseRefPlaceHolder.java
new file mode 100644
index 0000000..2245374
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/JSsaVariableUseRefPlaceHolder.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast;
+
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This version of the SSA variable reference only appears on the right hand side of any
+ * assignments.
+ */
+public class JSsaVariableUseRefPlaceHolder extends JSsaVariableUseRef {
+
+ @Nonnull
+ private final JSsaVariableDefRef def;
+
+ /* package */ JSsaVariableUseRefPlaceHolder(@Nonnull SourceInfo info, @Nonnull JVariable target,
+ JSsaVariableDefRef def) {
+ super(info, target, 0, def);
+ this.def = def;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest transformRequest)
+ throws Exception {
+ visitor.visit(this, transformRequest);
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/JVisitor.java b/jack/src/com/android/jack/ir/ast/JVisitor.java
index f4bb893..91a2b86 100644
--- a/jack/src/com/android/jack/ir/ast/JVisitor.java
+++ b/jack/src/com/android/jack/ir/ast/JVisitor.java
@@ -19,6 +19,35 @@
import com.android.jack.JackAbortException;
import com.android.jack.ir.HasSourceInfo;
import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JCaseBasicBlock;
+import com.android.jack.ir.ast.cfg.JCaseBlockElement;
+import com.android.jack.ir.ast.cfg.JCatchBasicBlock;
+import com.android.jack.ir.ast.cfg.JConditionalBasicBlock;
+import com.android.jack.ir.ast.cfg.JConditionalBlockElement;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JEntryBasicBlock;
+import com.android.jack.ir.ast.cfg.JExitBasicBlock;
+import com.android.jack.ir.ast.cfg.JGotoBlockElement;
+import com.android.jack.ir.ast.cfg.JLockBlockElement;
+import com.android.jack.ir.ast.cfg.JMethodCallBlockElement;
+import com.android.jack.ir.ast.cfg.JPhiBlockElement;
+import com.android.jack.ir.ast.cfg.JPlaceholderBasicBlock;
+import com.android.jack.ir.ast.cfg.JPolymorphicMethodCallBlockElement;
+import com.android.jack.ir.ast.cfg.JRegularBasicBlock;
+import com.android.jack.ir.ast.cfg.JReturnBasicBlock;
+import com.android.jack.ir.ast.cfg.JReturnBlockElement;
+import com.android.jack.ir.ast.cfg.JSimpleBasicBlock;
+import com.android.jack.ir.ast.cfg.JStoreBlockElement;
+import com.android.jack.ir.ast.cfg.JSwitchBasicBlock;
+import com.android.jack.ir.ast.cfg.JSwitchBlockElement;
+import com.android.jack.ir.ast.cfg.JThrowBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowBlockElement;
+import com.android.jack.ir.ast.cfg.JThrowingBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowingExpressionBasicBlock;
+import com.android.jack.ir.ast.cfg.JUnlockBlockElement;
+import com.android.jack.ir.ast.cfg.JVariableAsgBlockElement;
import com.android.jack.load.JackLoadingException;
import com.android.sched.transform.TransformRequest;
@@ -149,6 +178,126 @@
endVisit((JStatement) assertStatement);
}
+ public void endVisit(@Nonnull JConcreteMethodBody concreteMethodBody) {
+ endVisit((JAbstractMethodBody) concreteMethodBody);
+ }
+
+ public void endVisit(@Nonnull JMethodBodyCfg methodBodyCfg) {
+ endVisit((JConcreteMethodBody) methodBodyCfg);
+ }
+
+ public void endVisit(@Nonnull JControlFlowGraph controlFlowGraph) {
+ endVisit((JNode) controlFlowGraph);
+ }
+
+ public void endVisit(@Nonnull JBasicBlock basicBlock) {
+ endVisit((JNode) basicBlock);
+ }
+
+ public void endVisit(@Nonnull JCaseBasicBlock caseBasicBlock) {
+ endVisit((JRegularBasicBlock) caseBasicBlock);
+ }
+
+ public void endVisit(@Nonnull JCatchBasicBlock catchBasicBlock) {
+ endVisit((JRegularBasicBlock) catchBasicBlock);
+ }
+
+ public void endVisit(@Nonnull JConditionalBasicBlock conditionalBasicBlock) {
+ endVisit((JRegularBasicBlock) conditionalBasicBlock);
+ }
+
+ public void endVisit(@Nonnull JEntryBasicBlock entryBasicBlock) {
+ endVisit((JBasicBlock) entryBasicBlock);
+ }
+
+ public void endVisit(@Nonnull JExitBasicBlock exitBasicBlock) {
+ endVisit((JBasicBlock) exitBasicBlock);
+ }
+
+ public void endVisit(@Nonnull JRegularBasicBlock regularBasicBlock) {
+ endVisit((JBasicBlock) regularBasicBlock);
+ }
+
+ public void endVisit(@Nonnull JPlaceholderBasicBlock blockUnderConstruction) {
+ endVisit((JBasicBlock) blockUnderConstruction);
+ }
+
+ public void endVisit(@Nonnull JReturnBasicBlock returnBasicBlock) {
+ endVisit((JRegularBasicBlock) returnBasicBlock);
+ }
+
+ public void endVisit(@Nonnull JSimpleBasicBlock simpleBasicBlock) {
+ endVisit((JRegularBasicBlock) simpleBasicBlock);
+ }
+
+ public void endVisit(@Nonnull JSwitchBasicBlock switchBasicBlock) {
+ endVisit((JRegularBasicBlock) switchBasicBlock);
+ }
+
+ public void endVisit(@Nonnull JThrowingBasicBlock throwingBasicBlock) {
+ endVisit((JRegularBasicBlock) throwingBasicBlock);
+ }
+
+ public void endVisit(@Nonnull JThrowingExpressionBasicBlock throwingExpressionBasicBlock) {
+ endVisit((JThrowingBasicBlock) throwingExpressionBasicBlock);
+ }
+
+ public void endVisit(@Nonnull JThrowBasicBlock throwBasicBlock) {
+ endVisit((JThrowingBasicBlock) throwBasicBlock);
+ }
+
+ public void endVisit(@Nonnull JBasicBlockElement basicBlockElement) {
+ endVisit((JNode) basicBlockElement);
+ }
+
+ public void endVisit(@Nonnull JGotoBlockElement gotoBlockElement) {
+ endVisit((JBasicBlockElement) gotoBlockElement);
+ }
+
+ public void endVisit(@Nonnull JThrowBlockElement throwBlockElement) {
+ endVisit((JBasicBlockElement) throwBlockElement);
+ }
+
+ public void endVisit(@Nonnull JStoreBlockElement storeBlockElement) {
+ endVisit((JBasicBlockElement) storeBlockElement);
+ }
+
+ public void endVisit(@Nonnull JVariableAsgBlockElement variableAsgBlockElement) {
+ endVisit((JBasicBlockElement) variableAsgBlockElement);
+ }
+
+ public void endVisit(@Nonnull JReturnBlockElement returnBlockElement) {
+ endVisit((JBasicBlockElement) returnBlockElement);
+ }
+
+ public void endVisit(@Nonnull JCaseBlockElement caseBlockElement) {
+ endVisit((JBasicBlockElement) caseBlockElement);
+ }
+
+ public void endVisit(@Nonnull JMethodCallBlockElement methodCallBlockElement) {
+ endVisit((JBasicBlockElement) methodCallBlockElement);
+ }
+
+ public void endVisit(@Nonnull JPolymorphicMethodCallBlockElement methodCallBlockElement) {
+ endVisit((JBasicBlockElement) methodCallBlockElement);
+ }
+
+ public void endVisit(@Nonnull JLockBlockElement lockBlockElement) {
+ endVisit((JBasicBlockElement) lockBlockElement);
+ }
+
+ public void endVisit(@Nonnull JUnlockBlockElement unlockBlockElement) {
+ endVisit((JBasicBlockElement) unlockBlockElement);
+ }
+
+ public void endVisit(@Nonnull JSwitchBlockElement switchBlockElement) {
+ endVisit((JBasicBlockElement) switchBlockElement);
+ }
+
+ public void endVisit(@Nonnull JConditionalBlockElement conditionalBlockElement) {
+ endVisit((JBasicBlockElement) conditionalBlockElement);
+ }
+
public void endVisit(@Nonnull JBinaryOperation binaryOperation) {
endVisit((JExpression) binaryOperation);
}
@@ -326,7 +475,7 @@
}
public void endVisit(@Nonnull JMethodBody methodBody) {
- endVisit((JAbstractMethodBody) methodBody);
+ endVisit((JConcreteMethodBody) methodBody);
}
public void endVisit(@Nonnull JPolymorphicMethodCall polymorphicMethodCall) {
@@ -367,7 +516,9 @@
/**
* End visit of a {@link JNode}
- * @param jnode visited {@link JNode}
+ *
+ * @param jnode
+ * visited {@link JNode}
*/
public void endVisit(@Nonnull JNode jnode) {
// empty block
@@ -413,6 +564,10 @@
endVisit((JPhantomClassOrInterface) phantomInterface);
}
+ public void endVisit(@Nonnull JPhiBlockElement phi) {
+ endVisit((JBasicBlockElement) phi);
+ }
+
public void endVisit(@Nonnull JPostfixOperation postfixOperation) {
endVisit((JUnaryOperation) postfixOperation);
}
@@ -441,6 +596,18 @@
endVisit((JNumberValueLiteral) shortLiteral);
}
+ public void endVisit(@Nonnull JSsaVariableRef ref) {
+ endVisit((JVariableRef) ref);
+ }
+
+ public void endVisit(@Nonnull JSsaVariableDefRef ref) {
+ endVisit((JSsaVariableRef) ref);
+ }
+
+ public void endVisit(@Nonnull JSsaVariableUseRef ref) {
+ endVisit((JSsaVariableRef) ref);
+ }
+
public void endVisit(@Nonnull JStatement statement) {
endVisit((JNode) statement);
}
@@ -549,6 +716,126 @@
return visit((JExpression) binaryOperation);
}
+ public boolean visit(@Nonnull JConcreteMethodBody concreteMethodBody) {
+ return visit((JAbstractMethodBody) concreteMethodBody);
+ }
+
+ public boolean visit(@Nonnull JMethodBodyCfg methodBodyCfg) {
+ return visit((JConcreteMethodBody) methodBodyCfg);
+ }
+
+ public boolean visit(@Nonnull JControlFlowGraph controlFlowGraph) {
+ return visit((JNode) controlFlowGraph);
+ }
+
+ public boolean visit(@Nonnull JBasicBlock basicBlock) {
+ return visit((JNode) basicBlock);
+ }
+
+ public boolean visit(@Nonnull JCatchBasicBlock catchBasicBlock) {
+ return visit((JRegularBasicBlock) catchBasicBlock);
+ }
+
+ public boolean visit(@Nonnull JCaseBasicBlock caseBasicBlock) {
+ return visit((JRegularBasicBlock) caseBasicBlock);
+ }
+
+ public boolean visit(@Nonnull JConditionalBasicBlock conditionalBasicBlock) {
+ return visit((JRegularBasicBlock) conditionalBasicBlock);
+ }
+
+ public boolean visit(@Nonnull JEntryBasicBlock entryBasicBlock) {
+ return visit((JBasicBlock) entryBasicBlock);
+ }
+
+ public boolean visit(@Nonnull JExitBasicBlock exitBasicBlock) {
+ return visit((JBasicBlock) exitBasicBlock);
+ }
+
+ public boolean visit(@Nonnull JRegularBasicBlock regularBasicBlock) {
+ return visit((JBasicBlock) regularBasicBlock);
+ }
+
+ public boolean visit(@Nonnull JPlaceholderBasicBlock blockUnderConstruction) {
+ return visit((JBasicBlock) blockUnderConstruction);
+ }
+
+ public boolean visit(@Nonnull JReturnBasicBlock returnBasicBlock) {
+ return visit((JRegularBasicBlock) returnBasicBlock);
+ }
+
+ public boolean visit(@Nonnull JSimpleBasicBlock simpleBasicBlock) {
+ return visit((JRegularBasicBlock) simpleBasicBlock);
+ }
+
+ public boolean visit(@Nonnull JSwitchBasicBlock switchBasicBlock) {
+ return visit((JRegularBasicBlock) switchBasicBlock);
+ }
+
+ public boolean visit(@Nonnull JThrowingBasicBlock throwingBasicBlock) {
+ return visit((JRegularBasicBlock) throwingBasicBlock);
+ }
+
+ public boolean visit(@Nonnull JThrowingExpressionBasicBlock throwingExpressionBasicBlock) {
+ return visit((JThrowingBasicBlock) throwingExpressionBasicBlock);
+ }
+
+ public boolean visit(@Nonnull JThrowBasicBlock throwBasicBlock) {
+ return visit((JThrowingBasicBlock) throwBasicBlock);
+ }
+
+ public boolean visit(@Nonnull JBasicBlockElement basicBlockElement) {
+ return visit((JNode) basicBlockElement);
+ }
+
+ public boolean visit(@Nonnull JGotoBlockElement gotoBlockElement) {
+ return visit((JBasicBlockElement) gotoBlockElement);
+ }
+
+ public boolean visit(@Nonnull JThrowBlockElement throwBlockElement) {
+ return visit((JBasicBlockElement) throwBlockElement);
+ }
+
+ public boolean visit(@Nonnull JStoreBlockElement storeBlockElement) {
+ return visit((JBasicBlockElement) storeBlockElement);
+ }
+
+ public boolean visit(@Nonnull JVariableAsgBlockElement variableAsgBlockElement) {
+ return visit((JBasicBlockElement) variableAsgBlockElement);
+ }
+
+ public boolean visit(@Nonnull JReturnBlockElement returnBlockElement) {
+ return visit((JBasicBlockElement) returnBlockElement);
+ }
+
+ public boolean visit(@Nonnull JCaseBlockElement caseBlockElement) {
+ return visit((JBasicBlockElement) caseBlockElement);
+ }
+
+ public boolean visit(@Nonnull JMethodCallBlockElement methodCallBlockElement) {
+ return visit((JBasicBlockElement) methodCallBlockElement);
+ }
+
+ public boolean visit(@Nonnull JPolymorphicMethodCallBlockElement methodCallBlockElement) {
+ return visit((JBasicBlockElement) methodCallBlockElement);
+ }
+
+ public boolean visit(@Nonnull JLockBlockElement lockBlockElement) {
+ return visit((JBasicBlockElement) lockBlockElement);
+ }
+
+ public boolean visit(@Nonnull JUnlockBlockElement unlockBlockElement) {
+ return visit((JBasicBlockElement) unlockBlockElement);
+ }
+
+ public boolean visit(@Nonnull JSwitchBlockElement switchBlockElement) {
+ return visit((JBasicBlockElement) switchBlockElement);
+ }
+
+ public boolean visit(@Nonnull JConditionalBlockElement conditionalBlockElement) {
+ return visit((JBasicBlockElement) conditionalBlockElement);
+ }
+
public boolean visit(@Nonnull JReinterpretCastOperation reinterpretCastOperation) {
return visit((JCastOperation) reinterpretCastOperation);
}
@@ -722,7 +1009,7 @@
}
public boolean visit(@Nonnull JMethodBody methodBody) {
- return visit((JAbstractMethodBody) methodBody);
+ return visit((JConcreteMethodBody) methodBody);
}
public boolean visit(@Nonnull JPolymorphicMethodCall polymorphicMethodCall) {
@@ -763,7 +1050,9 @@
/**
* Visit of a {@link JNode}
- * @param jnode visited {@link JNode}
+ *
+ * @param jnode
+ * visited {@link JNode}
*/
public boolean visit(@Nonnull JNode jnode) {
return true;
@@ -809,6 +1098,10 @@
return visit((JPhantomClassOrInterface) phantomInterface);
}
+ public boolean visit(@Nonnull JPhiBlockElement phi) {
+ return visit((JBasicBlockElement) phi);
+ }
+
public boolean visit(@Nonnull JPostfixOperation postfixOperation) {
return visit((JUnaryOperation) postfixOperation);
}
@@ -841,6 +1134,18 @@
return visit((JNode) statement);
}
+ public boolean visit(@Nonnull JSsaVariableRef ref) {
+ return visit((JVariableRef) ref);
+ }
+
+ public boolean visit(@Nonnull JSsaVariableDefRef ref) {
+ return visit((JSsaVariableRef) ref);
+ }
+
+ public boolean visit(@Nonnull JSsaVariableUseRef ref) {
+ return visit((JSsaVariableRef) ref);
+ }
+
public boolean visit(@Nonnull JStringLiteral stringLiteral) {
return visit((JAbstractStringLiteral) stringLiteral);
}
@@ -960,6 +1265,157 @@
visit((JExpression) binaryOperation, transformRequest);
}
+ public void visit(@Nonnull JConcreteMethodBody concreteMethodBody,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JAbstractMethodBody) concreteMethodBody, transformRequest);
+ }
+
+ public void visit(@Nonnull JMethodBodyCfg x, @Nonnull TransformRequest transformRequest)
+ throws Exception {
+ visit((JConcreteMethodBody) x, transformRequest);
+ }
+
+ public void visit(@Nonnull JControlFlowGraph controlFlowGraph,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JNode) controlFlowGraph, transformRequest);
+ }
+
+ public void visit(@Nonnull JBasicBlock basicBlock,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JNode) basicBlock, transformRequest);
+ }
+
+ public void visit(@Nonnull JCatchBasicBlock catchBasicBlock,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JRegularBasicBlock) catchBasicBlock, transformRequest);
+ }
+
+ public void visit(@Nonnull JCaseBasicBlock caseBasicBlock,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JRegularBasicBlock) caseBasicBlock, transformRequest);
+ }
+
+ public void visit(@Nonnull JConditionalBasicBlock conditionalBasicBlock,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JRegularBasicBlock) conditionalBasicBlock, transformRequest);
+ }
+
+ public void visit(@Nonnull JEntryBasicBlock entryBasicBlock,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlock) entryBasicBlock, transformRequest);
+ }
+
+ public void visit(@Nonnull JExitBasicBlock exitBasicBlock,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlock) exitBasicBlock, transformRequest);
+ }
+
+ public void visit(@Nonnull JRegularBasicBlock regularBasicBlock,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlock) regularBasicBlock, transformRequest);
+ }
+
+ public void visit(@Nonnull JPlaceholderBasicBlock blockUnderConstruction,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlock) blockUnderConstruction, transformRequest);
+ }
+
+ public void visit(@Nonnull JReturnBasicBlock returnBasicBlock,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JRegularBasicBlock) returnBasicBlock, transformRequest);
+ }
+
+ public void visit(@Nonnull JSimpleBasicBlock simpleBasicBlock,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JRegularBasicBlock) simpleBasicBlock, transformRequest);
+ }
+
+ public void visit(@Nonnull JSwitchBasicBlock switchBasicBlock,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JRegularBasicBlock) switchBasicBlock, transformRequest);
+ }
+
+ public void visit(@Nonnull JThrowingBasicBlock throwingBasicBlock,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JRegularBasicBlock) throwingBasicBlock, transformRequest);
+ }
+
+ public void visit(
+ @Nonnull JThrowingExpressionBasicBlock throwingExpressionBasicBlock,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JThrowingBasicBlock) throwingExpressionBasicBlock, transformRequest);
+ }
+
+ public void visit(@Nonnull JThrowBasicBlock throwBasicBlock,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JThrowingBasicBlock) throwBasicBlock, transformRequest);
+ }
+
+ public void visit(@Nonnull JBasicBlockElement basicBlockElement,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JNode) basicBlockElement, transformRequest);
+ }
+
+ public void visit(@Nonnull JGotoBlockElement gotoBlockElement,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlockElement) gotoBlockElement, transformRequest);
+ }
+
+ public void visit(@Nonnull JThrowBlockElement throwBlockElement,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlockElement) throwBlockElement, transformRequest);
+ }
+
+ public void visit(@Nonnull JStoreBlockElement storeBlockElement,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlockElement) storeBlockElement, transformRequest);
+ }
+
+ public void visit(@Nonnull JVariableAsgBlockElement variableAsgBlockElement,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlockElement) variableAsgBlockElement, transformRequest);
+ }
+
+ public void visit(@Nonnull JReturnBlockElement returnBlockElement,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlockElement) returnBlockElement, transformRequest);
+ }
+
+ public void visit(@Nonnull JCaseBlockElement caseBlockElement,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlockElement) caseBlockElement, transformRequest);
+ }
+
+ public void visit(@Nonnull JMethodCallBlockElement methodCallBlockElement,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlockElement) methodCallBlockElement, transformRequest);
+ }
+
+ public void visit(@Nonnull JPolymorphicMethodCallBlockElement methodCallBlockElement,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlockElement) methodCallBlockElement, transformRequest);
+ }
+
+ public void visit(@Nonnull JLockBlockElement lockBlockElement,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlockElement) lockBlockElement, transformRequest);
+ }
+
+ public void visit(@Nonnull JUnlockBlockElement unlockBlockElement,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlockElement) unlockBlockElement, transformRequest);
+ }
+
+ public void visit(@Nonnull JSwitchBlockElement switchBlockElement,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlockElement) switchBlockElement, transformRequest);
+ }
+
+ public void visit(@Nonnull JConditionalBlockElement conditionalBlockElement,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlockElement) conditionalBlockElement, transformRequest);
+ }
+
public void visit(
@Nonnull JReinterpretCastOperation reinterpretCastOperation,
@Nonnull TransformRequest transformRequest) throws Exception {
@@ -1174,7 +1630,7 @@
public void visit(@Nonnull JMethodBody methodBody, @Nonnull TransformRequest transformRequest)
throws Exception {
- visit((JAbstractMethodBody) methodBody, transformRequest);
+ visit((JConcreteMethodBody) methodBody, transformRequest);
}
public void visit(@Nonnull JPolymorphicMethodCall polymorphicMethodCall,
@@ -1228,9 +1684,9 @@
* Visit of a {@link JNode} with a {@link TransformRequest} to apply on.
* @param jnode visited {@link JNode}
* @param transformRequest {@link TransformRequest} to apply on.
- */
+ **/
public void visit(@Nonnull JNode jnode, @Nonnull TransformRequest transformRequest)
- throws Exception {}
+ throws Exception { }
public void visit(@Nonnull JNullLiteral nullLiteral, @Nonnull TransformRequest transformRequest)
throws Exception {
@@ -1278,6 +1734,11 @@
visit((JPhantomClassOrInterface) phantomInterface, transformRequest);
}
+ public void visit(@Nonnull JPhiBlockElement phi,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JBasicBlockElement) phi, transformRequest);
+ }
+
public void visit(@Nonnull JPostfixOperation postfixOperation,
@Nonnull TransformRequest transformRequest) throws Exception {
visit((JUnaryOperation) postfixOperation, transformRequest);
@@ -1313,6 +1774,21 @@
visit((JNode) statement, transformRequest);
}
+ public void visit(@Nonnull JSsaVariableRef ref,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JVariableRef) ref, transformRequest);
+ }
+
+ public void visit(@Nonnull JSsaVariableDefRef ref,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JSsaVariableRef) ref, transformRequest);
+ }
+
+ public void visit(@Nonnull JSsaVariableUseRef ref,
+ @Nonnull TransformRequest transformRequest) throws Exception {
+ visit((JSsaVariableRef) ref, transformRequest);
+ }
+
public void visit(@Nonnull JStringLiteral stringLiteral,
@Nonnull TransformRequest transformRequest) throws Exception {
visit((JAbstractStringLiteral) stringLiteral, transformRequest);
diff --git a/jack/src/com/android/jack/ir/ast/cfg/BasicBlockComparator.java b/jack/src/com/android/jack/ir/ast/cfg/BasicBlockComparator.java
new file mode 100644
index 0000000..2365f28
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/BasicBlockComparator.java
@@ -0,0 +1,405 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JAbstractStringLiteral;
+import com.android.jack.ir.ast.JAlloc;
+import com.android.jack.ir.ast.JArrayLength;
+import com.android.jack.ir.ast.JArrayRef;
+import com.android.jack.ir.ast.JBinaryOperation;
+import com.android.jack.ir.ast.JBooleanLiteral;
+import com.android.jack.ir.ast.JByteLiteral;
+import com.android.jack.ir.ast.JCharLiteral;
+import com.android.jack.ir.ast.JClassLiteral;
+import com.android.jack.ir.ast.JDoubleLiteral;
+import com.android.jack.ir.ast.JDynamicCastOperation;
+import com.android.jack.ir.ast.JEnumLiteral;
+import com.android.jack.ir.ast.JExceptionRuntimeValue;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JFieldRef;
+import com.android.jack.ir.ast.JFloatLiteral;
+import com.android.jack.ir.ast.JInstanceOf;
+import com.android.jack.ir.ast.JIntLiteral;
+import com.android.jack.ir.ast.JLiteral;
+import com.android.jack.ir.ast.JLongLiteral;
+import com.android.jack.ir.ast.JMethodCall;
+import com.android.jack.ir.ast.JMethodLiteral;
+import com.android.jack.ir.ast.JNewArray;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JNullLiteral;
+import com.android.jack.ir.ast.JPolymorphicMethodCall;
+import com.android.jack.ir.ast.JReinterpretCastOperation;
+import com.android.jack.ir.ast.JShortLiteral;
+import com.android.jack.ir.ast.JType;
+import com.android.jack.ir.ast.JUnaryOperation;
+import com.android.jack.ir.ast.JValueLiteral;
+import com.android.jack.ir.ast.JVariable;
+import com.android.jack.ir.ast.JVariableRef;
+import com.android.jack.ir.ast.JVisitor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/** Basic implementation of the comparator for basic blocks */
+public class BasicBlockComparator {
+ /** Comparator visitor implements 'shallow' comparison of two nodes */
+ protected static class Comparator extends JVisitor {
+ private boolean differenceFound = false;
+
+ @CheckForNull
+ private JNode other;
+
+ /**
+ * Get the node representing comparison target, ensures the exact node type.
+ * If node types don't match, marks the difference and returns the original node.
+ */
+ @Nonnull
+ @SuppressWarnings(value = "unchecked")
+ protected <T extends JNode> T otherOrMe(@Nonnull T expr) {
+ assert other != null;
+ if (expr.getClass() != other.getClass()) {
+ differenceFound = true;
+ return expr;
+ }
+ return (T) other;
+ }
+
+ /** Check if the value if true, marks the difference otherwise */
+ protected boolean ensure(boolean expectedToBeTrue) {
+ if (!expectedToBeTrue) {
+ differenceFound = true;
+ }
+ return expectedToBeTrue;
+ }
+
+ /** Compare two nodes w/o children */
+ protected boolean areShallowlyEqual(@Nonnull JNode a, @Nonnull JNode b) {
+ assert !differenceFound;
+ other = b;
+ accept(a);
+ return !differenceFound;
+ }
+
+ /** Performs basic checks valid for all kinds of the nodes */
+ protected void performCommonChecks(@Nonnull JNode node) {
+ }
+
+ /** Performs basic checks valid for all kinds of the block elements */
+ protected void performCommonChecks(@Nonnull JBasicBlockElement element) {
+ performCommonChecks((JNode) element);
+
+ List<JCatchBasicBlock> thisContext = element.getEHContext().getCatchBlocks();
+ List<JCatchBasicBlock> otherContext = otherOrMe(element).getEHContext().getCatchBlocks();
+
+ int size = thisContext.size();
+ if (ensure(size == otherContext.size())) {
+ for (int i = 0; i < size; i++) {
+ ensure(thisContext.get(i) == otherContext.get(i));
+ }
+ }
+ }
+
+ /** Performs basic checks valid for all kinds of the basic blocks */
+ protected void performCommonChecks(@Nonnull JBasicBlock block) {
+ performCommonChecks((JNode) block);
+ }
+
+ /** Performs basic checks valid for all kinds of the expressions */
+ protected void performCommonChecks(@Nonnull JExpression expr) {
+ performCommonChecks((JNode) expr);
+ ensure(equal(expr.getType(), otherOrMe(expr).getType()));
+ }
+
+ protected <T extends JType> boolean equal(@Nonnull List<T> a, @Nonnull List<T> b) {
+ if (a.size() != b.size()) {
+ return false;
+ }
+ for (int i = 0; i < a.size(); i++) {
+ if (!equal(a.get(i), b.get(i))) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ protected boolean equal(@Nonnull JType a, @Nonnull JType b) {
+ return a.isSameType(b);
+ }
+
+ protected boolean equal(@Nonnull JVariable a, @Nonnull JVariable b) {
+ return a == b;
+ }
+
+ @Override public boolean visit(@Nonnull JBasicBlock block) {
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JBasicBlockElement element) {
+ return false;
+ }
+
+ @Override public void endVisit(@Nonnull JBasicBlock block) {
+ performCommonChecks(block);
+ otherOrMe(block);
+ }
+
+ @Override public void endVisit(@Nonnull JBasicBlockElement element) {
+ performCommonChecks(element);
+ otherOrMe(element);
+ }
+
+ @Override public void endVisit(@Nonnull JConditionalBasicBlock block) {
+ super.endVisit(block);
+ JConditionalBasicBlock other = otherOrMe(block);
+ ensure(other.isInverted() == block.isInverted());
+ }
+
+ @Override public void endVisit(@Nonnull JCatchBasicBlock block) {
+ super.endVisit(block);
+ JCatchBasicBlock other = otherOrMe(block);
+ ensure(equal(other.getCatchTypes(), block.getCatchTypes()));
+ }
+
+ @Override public boolean visit(@Nonnull JExpression expr) {
+ return false;
+ }
+
+ @Override public void endVisit(@Nonnull JExpression expr) {
+ throw new JNodeInternalError(expr,
+ "Unexpected expression in CFG: " + expr.toSource() +
+ " (" + expr.getClass().getSimpleName() + ")");
+ }
+
+ @Override public void endVisit(@Nonnull JMethodCall expr) {
+ performCommonChecks(expr);
+ JMethodCall other = otherOrMe(expr);
+ ensure((expr.getInstance() == null) == (other.getInstance() == null));
+ ensure(equal(expr.getReceiverType(), other.getReceiverType()));
+ ensure(expr.getMethodId().equals(other.getMethodId()));
+ ensure(expr.getDispatchKind() == other.getDispatchKind());
+ ensure(expr.getArgs().size() == other.getArgs().size());
+ }
+
+ @Override public void endVisit(@Nonnull JPolymorphicMethodCall expr) {
+ performCommonChecks(expr);
+ JPolymorphicMethodCall other = otherOrMe(expr);
+ ensure((expr.getInstance() == null) == (other.getInstance() == null));
+ ensure(equal(expr.getReceiverType(), other.getReceiverType()));
+ ensure(expr.getMethodId().equals(other.getMethodId()));
+ ensure(equal(expr.getCallSiteParameterTypes(), other.getCallSiteParameterTypes()));
+ ensure(expr.getArgs().size() == other.getArgs().size());
+ }
+
+ @Override public void endVisit(@Nonnull JFieldRef expr) {
+ performCommonChecks(expr);
+ JFieldRef other = otherOrMe(expr);
+ ensure((expr.getInstance() == null) == (other.getInstance() == null));
+ ensure(equal(expr.getReceiverType(), other.getReceiverType()));
+ ensure(expr.getFieldId().equals(other.getFieldId()));
+ }
+
+ @Override public void endVisit(@Nonnull JArrayRef expr) {
+ performCommonChecks(expr);
+ otherOrMe(expr);
+ }
+
+ @Override public void endVisit(@Nonnull JBinaryOperation expr) {
+ performCommonChecks(expr);
+ JBinaryOperation other = otherOrMe(expr);
+ ensure(expr.getOp() == other.getOp());
+ }
+
+ @Override public void endVisit(@Nonnull JUnaryOperation expr) {
+ performCommonChecks(expr);
+ JUnaryOperation other = otherOrMe(expr);
+ ensure(expr.getOp() == other.getOp());
+ }
+
+ @Override public void endVisit(@Nonnull JExceptionRuntimeValue expr) {
+ performCommonChecks(expr);
+ otherOrMe(expr);
+ }
+
+ @Override public void endVisit(@Nonnull JVariableRef expr) {
+ performCommonChecks(expr);
+ JVariableRef other = otherOrMe(expr);
+ ensure(equal(expr.getTarget(), other.getTarget()));
+ }
+
+ @Override public void endVisit(@Nonnull JLiteral expr) {
+ throw new AssertionError(expr.getClass()); // should be implemented in derived classes
+ }
+
+ @Override public void endVisit(@Nonnull JValueLiteral expr) {
+ throw new AssertionError(expr.getClass()); // should be implemented in derived classes
+ }
+
+ @Override public void endVisit(@Nonnull JClassLiteral expr) {
+ performCommonChecks(expr);
+ JClassLiteral other = otherOrMe(expr);
+ ensure(equal(expr.getRefType(), other.getRefType()));
+ }
+
+ @Override public void endVisit(@Nonnull JMethodLiteral expr) {
+ performCommonChecks(expr);
+ JMethodLiteral other = otherOrMe(expr);
+ ensure(expr.getMethod() == other.getMethod());
+ }
+
+ @Override public void endVisit(@Nonnull JEnumLiteral expr) {
+ performCommonChecks(expr);
+ JEnumLiteral other = otherOrMe(expr);
+ ensure(expr.getFieldId().equals(other.getFieldId()));
+ }
+
+ @Override public void endVisit(@Nonnull JNullLiteral expr) {
+ performCommonChecks(expr);
+ otherOrMe(expr);
+ }
+
+ @Override public void endVisit(@Nonnull JBooleanLiteral expr) {
+ performCommonChecks(expr);
+ JBooleanLiteral other = otherOrMe(expr);
+ ensure(expr.getValue() == other.getValue());
+ }
+
+ @Override public void endVisit(@Nonnull JAbstractStringLiteral expr) {
+ performCommonChecks(expr);
+ JAbstractStringLiteral other = otherOrMe(expr);
+ ensure(expr.getValue().equals(other.getValue()));
+ }
+
+ @Override public void endVisit(@Nonnull JShortLiteral expr) {
+ performCommonChecks(expr);
+ JShortLiteral other = otherOrMe(expr);
+ ensure(expr.getValue() == other.getValue());
+ }
+
+ @Override public void endVisit(@Nonnull JByteLiteral expr) {
+ performCommonChecks(expr);
+ JByteLiteral other = otherOrMe(expr);
+ ensure(expr.getValue() == other.getValue());
+ }
+
+ @Override public void endVisit(@Nonnull JLongLiteral expr) {
+ performCommonChecks(expr);
+ JLongLiteral other = otherOrMe(expr);
+ ensure(expr.getValue() == other.getValue());
+ }
+
+ @Override public void endVisit(@Nonnull JIntLiteral expr) {
+ performCommonChecks(expr);
+ JIntLiteral other = otherOrMe(expr);
+ ensure(expr.getValue() == other.getValue());
+ }
+
+ @Override public void endVisit(@Nonnull JCharLiteral expr) {
+ performCommonChecks(expr);
+ JCharLiteral other = otherOrMe(expr);
+ ensure(expr.getValue() == other.getValue());
+ }
+
+ @Override public void endVisit(@Nonnull JFloatLiteral expr) {
+ performCommonChecks(expr);
+ JFloatLiteral other = otherOrMe(expr);
+ ensure(expr.getValue() == other.getValue());
+ }
+
+ @Override public void endVisit(@Nonnull JDoubleLiteral expr) {
+ performCommonChecks(expr);
+ JDoubleLiteral other = otherOrMe(expr);
+ ensure(expr.getValue() == other.getValue());
+ }
+
+ @Override public void endVisit(@Nonnull JAlloc expr) {
+ performCommonChecks(expr);
+ otherOrMe(expr);
+ }
+
+ @Override public void endVisit(@Nonnull JDynamicCastOperation expr) {
+ performCommonChecks(expr);
+ JDynamicCastOperation other = otherOrMe(expr);
+ ensure(equal(expr.getTypes(), other.getTypes()));
+ }
+
+ @Override public void endVisit(@Nonnull JReinterpretCastOperation expr) {
+ performCommonChecks(expr);
+ JReinterpretCastOperation other = otherOrMe(expr);
+ ensure(equal(expr.getType(), other.getType()));
+ }
+
+ @Override public void endVisit(@Nonnull JInstanceOf expr) {
+ performCommonChecks(expr);
+ JInstanceOf other = otherOrMe(expr);
+ ensure(equal(expr.getTestType(), other.getTestType()));
+ }
+
+ @Override public void endVisit(@Nonnull JArrayLength expr) {
+ performCommonChecks(expr);
+ otherOrMe(expr);
+ }
+
+ @Override public void endVisit(@Nonnull JNewArray expr) {
+ performCommonChecks(expr);
+ JNewArray other = otherOrMe(expr);
+ ensure(equal(expr.getArrayType(), other.getArrayType()));
+ ensure(expr.getDims().size() == other.getDims().size());
+ ensure(expr.getInitializers().size() == other.getInitializers().size());
+ }
+ }
+
+ /** Compares `a` to `b`, returns `true` if they are considered to be equal */
+ public boolean compare(@Nonnull JBasicBlock a, @Nonnull JBasicBlock b) {
+ List<JNode> aNodes = getAllNodesInPostOrder(a);
+ List<JNode> bNodes = getAllNodesInPostOrder(b);
+
+ int size = aNodes.size();
+ if (size != bNodes.size()) {
+ return false;
+ }
+
+ Comparator comparator = getComparator();
+ for (int i = 0; i < size; i++) {
+ if (!comparator.areShallowlyEqual(aNodes.get(i), bNodes.get(i))) {
+ return false;
+ }
+ }
+
+ return !comparator.differenceFound;
+ }
+
+ @Nonnull
+ protected Comparator getComparator() {
+ return new Comparator();
+ }
+
+ @Nonnull
+ private List<JNode> getAllNodesInPostOrder(@Nonnull JBasicBlock block) {
+ final List<JNode> result = new ArrayList<>();
+ new JVisitor() {
+ @Override
+ public void endVisit(@Nonnull JNode e) {
+ result.add(e);
+ }
+ }.accept(block);
+ return result;
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/BasicBlockIterator.java b/jack/src/com/android/jack/ir/ast/cfg/BasicBlockIterator.java
new file mode 100644
index 0000000..a4f892c
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/BasicBlockIterator.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.google.common.collect.Sets;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+import javax.annotation.Nonnull;
+
+/** Simplifies iterations through basic blocks */
+public abstract class BasicBlockIterator {
+ @Nonnull
+ private final JControlFlowGraph cfg;
+
+ protected BasicBlockIterator(@Nonnull JControlFlowGraph cfg) {
+ this.cfg = cfg;
+ }
+
+ /** Processes basic block, returns `false` if the iterations should be stopped */
+ public abstract boolean process(@Nonnull JBasicBlock block);
+
+ /**
+ * Iterates basic blocks depth first with weak references. Weakly referenced
+ * blocks are iterated after regular referenced blocks.
+ */
+ public final void iterateDepthFirst() {
+ Stack<JBasicBlock> blocksStack = new Stack<>();
+ Set<JBasicBlock> blocksEverStacked = new HashSet<>();
+
+ Stack<ExceptionHandlingContext> ehcStack = new Stack<>();
+ Set<ExceptionHandlingContext> ehcEverStacked = Sets.newIdentityHashSet();
+
+ JBasicBlock start = cfg.getEntryBlock();
+ blocksStack.push(start);
+ blocksEverStacked.add(start);
+
+ while (true) {
+ if (!blocksStack.isEmpty()) {
+ // Block queue is not empty, process the next block
+ JBasicBlock block = blocksStack.pop();
+ assert blocksEverStacked.contains(block);
+ enqueueBlocks(block.getSuccessors(), blocksStack, blocksEverStacked);
+
+ // Compute the list of exception handling contexts
+ for (JBasicBlockElement element : block.getElements(/* forward: */ false)) {
+ ExceptionHandlingContext ehc = element.getEHContext();
+ if (!ehcEverStacked.contains(ehc)) {
+ ehcStack.push(ehc);
+ ehcEverStacked.add(ehc);
+ }
+ }
+
+ if (!process(block)) {
+ break;
+ }
+
+ } else if (!ehcStack.isEmpty()) {
+ // Otherwise, if the block queue is empty fetch check blocks from
+ // the next exception handling context
+ ExceptionHandlingContext ehc = ehcStack.pop();
+ assert ehcEverStacked.contains(ehc);
+ enqueueBlocks(ehc.getCatchBlocks(), blocksStack, blocksEverStacked);
+
+ } else {
+ // Both stacks are empty
+ break;
+ }
+ }
+ }
+
+ private void enqueueBlocks(@Nonnull List<? extends JBasicBlock> blocks,
+ @Nonnull Stack<JBasicBlock> blocksStack, @Nonnull Set<JBasicBlock> blocksEverStacked) {
+ // Since we use stack to hold the list of the blocks to be processed, we
+ // need to reverse successors lists to visit them not in reversed order
+ for (int i = blocks.size() - 1; i >= 0; i--) {
+ JBasicBlock next = blocks.get(i);
+ if (!blocksEverStacked.contains(next)) {
+ blocksStack.push(next);
+ blocksEverStacked.add(next);
+ }
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/BasicBlockLiveProcessor.java b/jack/src/com/android/jack/ir/ast/cfg/BasicBlockLiveProcessor.java
new file mode 100644
index 0000000..bc78acc
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/BasicBlockLiveProcessor.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.ast.JVisitor;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.Set;
+import javax.annotation.Nonnull;
+
+/**
+ * Simplifies iterations through basic blocks of possibly *mutating* CFG. Since the CFG
+ * may be mutated during iteration, does not guarantee any particular iteration order.
+ *
+ * If new basic blocks are added while mutating CFG, it is possible to add these
+ * newly created blocks with `enqueue(...)`.
+ */
+public abstract class BasicBlockLiveProcessor extends JVisitor {
+ @Nonnull
+ private final Queue<JBasicBlock> queue = new LinkedList<>();
+ @Nonnull
+ private final Set<JBasicBlock> everQueued = new HashSet<>();
+ private final boolean stepIntoElements;
+
+ public BasicBlockLiveProcessor(@Nonnull JControlFlowGraph cfg, boolean stepIntoElements) {
+ this.stepIntoElements = stepIntoElements;
+ new BasicBlockIterator(cfg) {
+ @Override
+ public boolean process(@Nonnull JBasicBlock block) {
+ enqueue(block);
+ return true;
+ }
+ }.iterateDepthFirst();
+ }
+
+ /** Enqueues the element if it was not queued before */
+ protected void enqueue(@Nonnull JBasicBlock block) {
+ if (!everQueued.contains(block)) {
+ everQueued.add(block);
+ queue.add(block);
+ }
+ }
+
+ @Override
+ public boolean visit(@Nonnull JBasicBlock basicBlock) {
+ return stepIntoElements;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JBasicBlockElement element) {
+ return false;
+ }
+
+ /** Process the blocks */
+ public final void process() {
+ while (!queue.isEmpty()) {
+ this.accept(queue.remove());
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/CfgBasicBlockTracker.java b/jack/src/com/android/jack/ir/ast/cfg/CfgBasicBlockTracker.java
new file mode 100644
index 0000000..8fa3b73
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/CfgBasicBlockTracker.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.sched.item.Description;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.util.log.Tracer;
+import com.android.sched.util.log.TracerFactory;
+import com.android.sched.util.log.stats.ExtendedSample;
+import com.android.sched.util.log.stats.ExtendedSampleImpl;
+import com.android.sched.util.log.stats.StatisticId;
+
+import javax.annotation.Nonnull;
+
+/** Counts and reports the statistics of different basic block kinds. */
+@Description("Counts and reports the statistics of different basic block kinds.")
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class CfgBasicBlockTracker implements RunnableSchedulable<JBasicBlock> {
+ @Nonnull
+ private final Tracer tracer = TracerFactory.getTracer();
+
+ @Nonnull
+ private static final StatisticId<ExtendedSample> TOTAL_COUNT = new StatisticId<>(
+ "jack.cfg.bb.total", "Total basic block statistics",
+ ExtendedSampleImpl.class, ExtendedSample.class);
+ @Nonnull
+ private static final StatisticId<ExtendedSample> RETURN_COUNT = new StatisticId<>(
+ "jack.cfg.bb.return", "Return basic block statistics",
+ ExtendedSampleImpl.class, ExtendedSample.class);
+ @Nonnull
+ private static final StatisticId<ExtendedSample> ENTRY_COUNT = new StatisticId<>(
+ "jack.cfg.bb.entry", "Entry basic block statistics",
+ ExtendedSampleImpl.class, ExtendedSample.class);
+ @Nonnull
+ private static final StatisticId<ExtendedSample> EXIT_COUNT = new StatisticId<>(
+ "jack.cfg.bb.exit", "Exit basic block statistics",
+ ExtendedSampleImpl.class, ExtendedSample.class);
+ @Nonnull
+ private static final StatisticId<ExtendedSample> THROWING_EXPR_COUNT = new StatisticId<>(
+ "jack.cfg.bb.throwing-expr", "Throwing expression basic block statistics",
+ ExtendedSampleImpl.class, ExtendedSample.class);
+ @Nonnull
+ private static final StatisticId<ExtendedSample> THROW_COUNT = new StatisticId<>(
+ "jack.cfg.bb.throw", "Throw basic block statistics",
+ ExtendedSampleImpl.class, ExtendedSample.class);
+ @Nonnull
+ private static final StatisticId<ExtendedSample> SWITCH_COUNT = new StatisticId<>(
+ "jack.cfg.bb.switch", "Switch basic block statistics",
+ ExtendedSampleImpl.class, ExtendedSample.class);
+ @Nonnull
+ private static final StatisticId<ExtendedSample> CASE_COUNT = new StatisticId<>(
+ "jack.cfg.bb.catch", "Case basic block statistics",
+ ExtendedSampleImpl.class, ExtendedSample.class);
+ @Nonnull
+ private static final StatisticId<ExtendedSample> CATCH_COUNT = new StatisticId<>(
+ "jack.cfg.bb.catch", "Catch basic block statistics",
+ ExtendedSampleImpl.class, ExtendedSample.class);
+ @Nonnull
+ private static final StatisticId<ExtendedSample> SIMPLE_COUNT = new StatisticId<>(
+ "jack.cfg.bb.simple", "Simple basic block statistics",
+ ExtendedSampleImpl.class, ExtendedSample.class);
+ @Nonnull
+ private static final StatisticId<ExtendedSample> CONDITIONAL_COUNT = new StatisticId<>(
+ "jack.cfg.bb.conditional", "Conditional basic block statistics",
+ ExtendedSampleImpl.class, ExtendedSample.class);
+
+ private final JVisitor processor = new JVisitor() {
+ @Override public boolean visit(@Nonnull JBasicBlock block) {
+ throw new AssertionError(block.getClass().getSimpleName());
+ }
+
+ @Override public boolean visit(@Nonnull JPlaceholderBasicBlock block) {
+ throw new AssertionError();
+ }
+
+ @Override public boolean visit(@Nonnull JEntryBasicBlock block) {
+ // Don't count in total
+ tracer.getStatistic(ENTRY_COUNT).add(block.getElementCount());
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JExitBasicBlock block) {
+ // Don't count in total
+ tracer.getStatistic(EXIT_COUNT).add(block.getElementCount());
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JCaseBasicBlock block) {
+ tracer.getStatistic(TOTAL_COUNT).add(block.getElementCount());
+ tracer.getStatistic(CASE_COUNT).add(block.getElementCount());
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JCatchBasicBlock block) {
+ tracer.getStatistic(TOTAL_COUNT).add(block.getElementCount());
+ tracer.getStatistic(CATCH_COUNT).add(block.getElementCount());
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JSimpleBasicBlock block) {
+ tracer.getStatistic(TOTAL_COUNT).add(block.getElementCount());
+ tracer.getStatistic(SIMPLE_COUNT).add(block.getElementCount());
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JConditionalBasicBlock block) {
+ tracer.getStatistic(TOTAL_COUNT).add(block.getElementCount());
+ tracer.getStatistic(CONDITIONAL_COUNT).add(block.getElementCount());
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JReturnBasicBlock block) {
+ tracer.getStatistic(TOTAL_COUNT).add(block.getElementCount());
+ tracer.getStatistic(RETURN_COUNT).add(block.getElementCount());
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JSwitchBasicBlock block) {
+ tracer.getStatistic(TOTAL_COUNT).add(block.getElementCount());
+ tracer.getStatistic(SWITCH_COUNT).add(block.getElementCount());
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JThrowingExpressionBasicBlock block) {
+ tracer.getStatistic(TOTAL_COUNT).add(block.getElementCount());
+ tracer.getStatistic(THROWING_EXPR_COUNT).add(block.getElementCount());
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JThrowBasicBlock block) {
+ tracer.getStatistic(TOTAL_COUNT).add(block.getElementCount());
+ tracer.getStatistic(THROW_COUNT).add(block.getElementCount());
+ return false;
+ }
+ };
+
+ @Override
+ public void run(@Nonnull JBasicBlock block) {
+ processor.accept(block);
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/CfgChecker.java b/jack/src/com/android/jack/ir/ast/cfg/CfgChecker.java
new file mode 100644
index 0000000..980cc24
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/CfgChecker.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.jack.transformations.SanityChecks;
+import com.android.sched.item.Description;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Support;
+
+import java.util.Stack;
+import javax.annotation.Nonnull;
+
+/** Check that AST of JControlFlowGraph nodes is correct. */
+@Description("Check that AST of JControlFlowGraph is correct.")
+@Support(SanityChecks.class)
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class CfgChecker implements RunnableSchedulable<JMethodBodyCfg> {
+ @Override
+ public void run(@Nonnull JMethodBodyCfg body) {
+ new JVisitor(/* needLoading = */ false) {
+ @Nonnull
+ private final Stack<JNode> nodes = new Stack<JNode>();
+
+ @Override
+ public boolean visit(@Nonnull JNode node) {
+ node.checkValidity();
+
+ if (node instanceof JControlFlowGraph) {
+ if (node.getParent() == null) {
+ throw new AssertionError(
+ "Parent of " + JControlFlowGraph.class.getName() + " must not be null.");
+ }
+ } else {
+ if (node.getParent() != nodes.peek()) {
+ throw new AssertionError("Node with wrong parent.");
+ }
+ }
+
+ nodes.push(node);
+ return super.visit(node);
+ }
+
+ @Override
+ public void endVisit(@Nonnull JNode node) {
+ nodes.pop();
+ super.endVisit(node);
+ }
+ }.accept(body.getCfg());
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/CfgExpressionValidator.java b/jack/src/com/android/jack/ir/ast/cfg/CfgExpressionValidator.java
new file mode 100644
index 0000000..ecf4685
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/CfgExpressionValidator.java
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JAbstractMethodCall;
+import com.android.jack.ir.ast.JAlloc;
+import com.android.jack.ir.ast.JArrayLength;
+import com.android.jack.ir.ast.JArrayRef;
+import com.android.jack.ir.ast.JAsgOperation;
+import com.android.jack.ir.ast.JBinaryOperation;
+import com.android.jack.ir.ast.JConditionalOperation;
+import com.android.jack.ir.ast.JDynamicCastOperation;
+import com.android.jack.ir.ast.JExceptionRuntimeValue;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JFieldRef;
+import com.android.jack.ir.ast.JInstanceOf;
+import com.android.jack.ir.ast.JLiteral;
+import com.android.jack.ir.ast.JLocalRef;
+import com.android.jack.ir.ast.JMethodCall;
+import com.android.jack.ir.ast.JNewArray;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JNullLiteral;
+import com.android.jack.ir.ast.JParameterRef;
+import com.android.jack.ir.ast.JPolymorphicMethodCall;
+import com.android.jack.ir.ast.JReinterpretCastOperation;
+import com.android.jack.ir.ast.JSsaVariableRef;
+import com.android.jack.ir.ast.JThisRef;
+import com.android.jack.ir.ast.JUnaryOperation;
+import com.android.jack.ir.ast.JVisitor;
+
+import java.util.NoSuchElementException;
+import javax.annotation.Nonnull;
+
+/** Validates expressions inside the CFG basic block elements */
+class CfgExpressionValidator extends JVisitor {
+ @Nonnull
+ private final JBasicBlockElement blockElement;
+ private final boolean isThrowingExpected;
+
+ private boolean seenThrowingExpression = false;
+
+ private CfgExpressionValidator(@Nonnull JBasicBlockElement element) {
+ this.blockElement = element;
+
+ JBasicBlock basicBlock = element.getBasicBlock();
+ this.isThrowingExpected =
+ basicBlock instanceof JThrowingBasicBlock &&
+ basicBlock.getLastElement() == element;
+ }
+
+ public static void validate(@Nonnull JBasicBlockElement element) {
+ new CfgExpressionValidator(element).accept(element);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JBasicBlockElement element) {
+ if (this.blockElement != element) {
+ throw new JNodeInternalError(element, "Nested block element");
+ }
+ return true;
+ }
+
+ @Override
+ public void endVisit(@Nonnull JBasicBlockElement element) {
+ if (isThrowingExpected) {
+ assert this.blockElement.getBasicBlock() instanceof JThrowingBasicBlock;
+ boolean isImplicitThrow =
+ this.blockElement instanceof JUnlockBlockElement
+ || this.blockElement instanceof JLockBlockElement
+ || this.blockElement instanceof JThrowBlockElement;
+
+ if (isImplicitThrow) {
+ if (seenThrowingExpression) {
+ throw new JNodeInternalError(element,
+ "An unexpected exception is thrown in lock/unlock/throw block element");
+ }
+ } else {
+ if (!seenThrowingExpression) {
+ throw new JNodeInternalError(element,
+ "An exception is expected to be thrown in throwing block element");
+ }
+ }
+ } else {
+ if (seenThrowingExpression) {
+ throw new JNodeInternalError(element,
+ "An unexpected exception is thrown in block element");
+ }
+ }
+ super.endVisit(element);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JExpression expr) {
+ throw new JNodeInternalError(expr,
+ "Unexpected expression in CFG: " + expr.toSource() +
+ " (" + expr.getClass().getSimpleName() + ")");
+ }
+
+ @Override
+ public boolean visit(@Nonnull JMethodCall expr) {
+ return visitMethodCall(expr, JMethodCallBlockElement.class);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JPolymorphicMethodCall expr) {
+ return visitMethodCall(expr, JPolymorphicMethodCallBlockElement.class);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JFieldRef expr) {
+ return visitFieldOrArrayRef(expr);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JArrayRef expr) {
+ return visitFieldOrArrayRef(expr);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JBinaryOperation expr) {
+ if (expr instanceof JConditionalOperation) {
+ throw new JNodeInternalError(expr,
+ "JConditionalOperation is not allowed in cfg-IR");
+ }
+ if (expr instanceof JAsgOperation) {
+ confirmBlockElement(expr);
+ confirmParent(expr, JVariableAsgBlockElement.class, JStoreBlockElement.class);
+
+ } else {
+ // Some of binary operations can throw
+ if (expr.canThrow()) {
+ visitThrowingRValue(expr);
+ } else {
+ visitNonThrowingOperation(expr);
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JUnaryOperation expr) {
+ return visitNonThrowingOperation(expr);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JExceptionRuntimeValue expr) {
+ assert !expr.canThrow();
+ confirmBlockElement(expr);
+ confirmVariableAssignmentRhs(expr, JVariableAsgBlockElement.class);
+ confirmParent(expr.getParent().getParent(), JCatchBasicBlock.class);
+ return true;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JLocalRef expr) {
+ return visitLocalOrParameter(expr);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JSsaVariableRef ref) {
+ return visitLocalOrParameter(ref);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JParameterRef expr) {
+ return visitLocalOrParameter(expr);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JLiteral expr) {
+ return expr.canThrow() ? visitThrowingRValue(expr) : visitNonThrowingTriviaRValue(expr);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JNullLiteral expr) {
+ return visitNonThrowingTriviaRValue(expr);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JThisRef expr) {
+ return visitNonThrowingTriviaRValue(expr);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JAlloc expr) {
+ return visitThrowingRValue(expr);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JDynamicCastOperation expr) {
+ if (expr.getTypes().size() > 1) {
+ throw new JNodeInternalError(expr, "Intersection types are invalid on this level");
+ }
+ return expr.canThrow() ? visitThrowingRValue(expr) : visitNonThrowingOperation(expr);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JReinterpretCastOperation expr) {
+ assert !expr.canThrow();
+ return visitNonThrowingOperation(expr);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JInstanceOf expr) {
+ assert expr.canThrow();
+ return visitThrowingRValue(expr);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JArrayLength expr) {
+ return visitThrowingRValue(expr);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JNewArray expr) {
+ return visitThrowingRValue(expr);
+ }
+
+ private void confirmParent(@Nonnull JNode expr, @Nonnull Class... parents) {
+ JNode parent = expr.getParent();
+ assert parent != null;
+ Class<? extends JNode> actual = parent.getClass();
+ for (Class<?> clazz : parents) {
+ if (clazz.isAssignableFrom(actual)) {
+ return;
+ }
+ }
+
+ StringBuilder builder = new StringBuilder();
+ String sep = "{";
+ for (Class clazz : parents) {
+ builder.append(sep).append(clazz.getSimpleName());
+ sep = " or ";
+ }
+ builder.append("}");
+ throw new JNodeInternalError(expr,
+ "Node must be a child of " + builder.toString() +
+ ", but real parent is: " + parent.getClass().getSimpleName() +
+ ", cfg: " + expr.getParent(JControlFlowGraph.class).toSource());
+ }
+
+ private void confirmBlockElement(@Nonnull JExpression expr) {
+ try {
+ JBasicBlockElement element = expr.getParent(JBasicBlockElement.class);
+ if (element == blockElement) {
+ return;
+ }
+ } catch (NoSuchElementException e) {
+ /* Ignore the exception */
+ }
+
+ throw new JNodeInternalError(expr,
+ "Expression must be in basic block element: " + this.blockElement.toSource());
+ }
+
+ private void confirmThrowingIsAllowed(@Nonnull JExpression expr) {
+ assert expr.canThrow();
+ if (!isThrowingExpected) {
+ throw new JNodeInternalError(expr,
+ "Expression must be in the last element of the trowing basic block: "
+ + this.blockElement.toSource());
+ }
+
+ if (this.seenThrowingExpression) {
+ throw new JNodeInternalError(expr,
+ "Multiple throwing exceptions in block element");
+ }
+
+ this.seenThrowingExpression = true;
+ }
+
+ private void confirmVariableAssignmentRhs(
+ @Nonnull JExpression expr, @Nonnull Class expectedParent) {
+ JNode parent = expr.getParent();
+ if (!(parent instanceof JAsgOperation) ||
+ ((JAsgOperation) parent).getRhs() != expr) {
+ throw new JNodeInternalError(expr,
+ "Expression must be the value in the assignment inside variable "
+ + "assignment block element: " + this.blockElement.toSource());
+ }
+ confirmParent(parent, expectedParent);
+ }
+
+ private void confirmNotAssignmentTarget(@Nonnull JExpression expr) {
+ JNode parent = expr.getParent();
+ if (parent instanceof JAsgOperation &&
+ ((JAsgOperation) parent).getLhs() == expr) {
+ throw new JNodeInternalError(expr,
+ "Expression must NOT be assignment target, "
+ + "block element: " + this.blockElement.toSource());
+ }
+ }
+
+ private void confirmParentForTrivia(@Nonnull JExpression expr) {
+ assert !expr.canThrow();
+ confirmParent(expr,
+ // Methods: both arguments and receiver
+ JMethodCall.class, JPolymorphicMethodCall.class,
+ // Operators
+ JUnaryOperation.class, JBinaryOperation.class,
+ // Expression holding block elements
+ JCaseBlockElement.class, JSwitchBlockElement.class,
+ JConditionalBlockElement.class, JReturnBlockElement.class,
+ JLockBlockElement.class, JUnlockBlockElement.class,
+ JPhiBlockElement.class,
+ // Misc expressions
+ JFieldRef.class, JArrayRef.class, JArrayLength.class, JNewArray.class,
+ JDynamicCastOperation.class, JReinterpretCastOperation.class,
+ JThrowBlockElement.class, JInstanceOf.class);
+ }
+
+ private boolean visitMethodCall(@Nonnull JAbstractMethodCall expr, @Nonnull Class parentClass) {
+ assert expr.canThrow();
+ confirmBlockElement(expr);
+ confirmThrowingIsAllowed(expr);
+
+ // Should be either in standalone method call block element
+ // or in variable assignment block element
+ confirmParent(expr, JAsgOperation.class, parentClass);
+ confirmNotAssignmentTarget(expr);
+
+ return true;
+ }
+
+ private boolean visitFieldOrArrayRef(@Nonnull JExpression expr) {
+ assert expr.canThrow();
+ confirmBlockElement(expr);
+ confirmThrowingIsAllowed(expr);
+
+ // Array/field ref may be referenced either in assignment or load context
+ confirmParent(expr, JAsgOperation.class);
+ JAsgOperation asgExpr = (JAsgOperation) expr.getParent();
+ if (asgExpr.getLhs() == expr) {
+ // Assignment context, assignment must be a child of store block element
+ confirmParent(asgExpr, JStoreBlockElement.class);
+
+ } else {
+ // Load context, value in variable assignment block element
+ confirmVariableAssignmentRhs(expr, JVariableAsgBlockElement.class);
+ }
+ return true;
+ }
+
+ private boolean visitLocalOrParameter(@Nonnull JExpression expr) {
+ confirmBlockElement(expr);
+ confirmParentForTrivia(expr);
+
+ JNode parent = expr.getParent();
+ if (parent instanceof JAsgOperation) {
+ if (((JAsgOperation) parent).getLhs() == expr) {
+ // Assignment context, assignment must be part of variable assignment block element
+ confirmParent(parent, JVariableAsgBlockElement.class);
+ }
+ }
+ return true;
+ }
+
+ private boolean visitThrowingRValue(@Nonnull JExpression expr) {
+ assert expr.canThrow();
+ confirmBlockElement(expr);
+ confirmThrowingIsAllowed(expr);
+ confirmVariableAssignmentRhs(expr, JVariableAsgBlockElement.class);
+ return true;
+ }
+
+ private boolean visitNonThrowingTriviaRValue(@Nonnull JExpression expr) {
+ assert !expr.canThrow();
+ confirmBlockElement(expr);
+ confirmParentForTrivia(expr);
+ confirmNotAssignmentTarget(expr);
+ return true;
+ }
+
+ private boolean visitNonThrowingOperation(@Nonnull JExpression expr) {
+ assert !expr.canThrow();
+ confirmBlockElement(expr);
+ if (expr instanceof JReinterpretCastOperation) {
+ // It is probably not the right thing to allow reinterpret cast
+ // as an instance of array/field access or the receiver of the
+ // method call, keeping it this way until we discuss it
+ confirmParent(expr, JAsgOperation.class,
+ JArrayRef.class, JFieldRef.class, JMethodCall.class);
+
+ } else if (expr instanceof JDynamicCastOperation) {
+ confirmParent(expr, JAsgOperation.class);
+
+ } else {
+ confirmParent(expr, JConditionalBlockElement.class, JAsgOperation.class);
+ }
+ confirmNotAssignmentTarget(expr);
+ return true;
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/ControlFlowGraphSizeTracker.java b/jack/src/com/android/jack/ir/ast/cfg/ControlFlowGraphSizeTracker.java
new file mode 100644
index 0000000..fde2381
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/ControlFlowGraphSizeTracker.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.Jack;
+import com.android.jack.ir.ast.JMethod;
+import com.android.jack.ir.formatter.TypePackageAndMethodFormatter;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.sched.item.Description;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.util.log.Tracer;
+import com.android.sched.util.log.TracerFactory;
+import com.android.sched.util.log.stats.ExtendedSample;
+import com.android.sched.util.log.stats.ExtendedSampleImpl;
+import com.android.sched.util.log.stats.StatisticId;
+
+import javax.annotation.Nonnull;
+
+/** Counts and reports the number of control flow graphs. */
+@Description("Counts and reports the number of control flow graphs.")
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class ControlFlowGraphSizeTracker implements RunnableSchedulable<JControlFlowGraph> {
+ @Nonnull
+ public static final StatisticId<ExtendedSample> STATISTICS = new StatisticId<>(
+ "jack.cfg.statistics", "Control flow graphs statistics",
+ ExtendedSampleImpl.class, ExtendedSample.class);
+
+ @Nonnull
+ private final Tracer tracer = TracerFactory.getTracer();
+ @Nonnull
+ private final TypePackageAndMethodFormatter formatter = Jack.getUserFriendlyFormatter();
+
+ @Override
+ public void run(@Nonnull final JControlFlowGraph cfg) {
+ JMethod method = cfg.getMethod();
+ tracer.getStatistic(STATISTICS).add(cfg.getAllBlocksUnordered().size(),
+ formatter.getName(method) + " [" + formatter.getName(method.getEnclosingType()) + "]");
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/ExceptionHandlingContext.java b/jack/src/com/android/jack/ir/ast/cfg/ExceptionHandlingContext.java
new file mode 100644
index 0000000..b0287bd
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/ExceptionHandlingContext.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
+import com.android.jack.Jack;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nonnull;
+
+/** Specifies the ordered set of catch blocks handling exceptions */
+public final class ExceptionHandlingContext {
+ @Nonnull
+ public static final ExceptionHandlingContext EMPTY =
+ new ExceptionHandlingContext(Collections.<JCatchBasicBlock>emptyList());
+
+ @Nonnull
+ private final List<JCatchBasicBlock> catchBlocks;
+
+ private ExceptionHandlingContext(@Nonnull List<JCatchBasicBlock> catchBlocks) {
+ this.catchBlocks = catchBlocks.isEmpty()
+ ? Collections.<JCatchBasicBlock>emptyList() : ImmutableList.copyOf(catchBlocks);
+ }
+
+ @Nonnull
+ public static ExceptionHandlingContext create(@Nonnull List<JCatchBasicBlock> catchBlocks) {
+ return catchBlocks.isEmpty() ? EMPTY
+ : new ExceptionHandlingContext(Jack.getUnmodifiableCollections()
+ .getUnmodifiableList(Lists.newArrayList(catchBlocks)));
+ }
+
+ @Nonnull
+ public List<JCatchBasicBlock> getCatchBlocks() {
+ return catchBlocks;
+ }
+
+ /** Creates a pool of unique exception handler contexts */
+ static class Pool {
+ @Nonnull
+ private final Map<List<JCatchBasicBlock>, ExceptionHandlingContext> pool = new HashMap<>();
+
+ @Nonnull
+ ExceptionHandlingContext getOrCreate(@Nonnull List<JCatchBasicBlock> catchBlocks) {
+ ExceptionHandlingContext ehc = pool.get(catchBlocks);
+ if (ehc == null) {
+ ehc = ExceptionHandlingContext.create(catchBlocks);
+ pool.put(ehc.getCatchBlocks(), ehc);
+ }
+ return ehc;
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JBasicBlock.java b/jack/src/com/android/jack/ir/ast/cfg/JBasicBlock.java
new file mode 100644
index 0000000..016c38c
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JBasicBlock.java
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.google.common.collect.Lists;
+
+import com.android.jack.Jack;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.jack.util.graph.IGraphNode;
+import com.android.sched.item.Description;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/** Represents an abstract CFG basic block implementation */
+@Description("CFG Basic Block")
+public abstract class JBasicBlock extends JNode implements IGraphNode<JBasicBlock> {
+ @Nonnull
+ private final ArrayList<JBasicBlock> predecessors = new ArrayList<>();
+
+ JBasicBlock() {
+ super(SourceInfo.UNKNOWN);
+ // NOTE: we cannot pass cfg to set up the parent at this phase,
+ // because call updateParents() will try to visit the basic block
+ // being constructed and it is not constructed yet.
+ //
+ // The CFG parameter must be a part of LEAF basic block constructor
+ // signature and must be set at the end of basic block construction
+ }
+
+ @Nonnull
+ public JControlFlowGraph getCfg() {
+ JNode parent = this.getParent();
+ assert parent instanceof JControlFlowGraph;
+ return (JControlFlowGraph) parent;
+ }
+
+ /** Immutable successors snapshot */
+ @Nonnull
+ public abstract List<JBasicBlock> getSuccessors();
+
+ /** Immutable list of all block elements */
+ @Nonnull
+ public final List<JBasicBlockElement> getElements() {
+ return getElements(true);
+ }
+
+ /** Immutable list of all block elements, can be forward or backward */
+ @Nonnull
+ public abstract List<JBasicBlockElement> getElements(boolean forward);
+
+ /** Elements count */
+ @Nonnegative
+ public abstract int getElementCount();
+
+ /** Returns the first element, element must exist */
+ @Nonnull
+ public abstract JBasicBlockElement getFirstElement();
+
+ /** Returns the last element, element must exist */
+ @Nonnull
+ public abstract JBasicBlockElement getLastElement();
+
+ /** Is the element list empty? */
+ public abstract boolean hasElements();
+
+ /** Appends a new basic block element at the end of the basic block */
+ public abstract void appendElement(@Nonnull JBasicBlockElement element);
+
+ /**
+ * Inserts a new basic block element into the list of this basic block's elements.
+ * <p>
+ * Index may be positive or negative:
+ * <li> <b>at == 0</b>: `element` is inserted in the beginning </li>
+ * <li> <b>at == elementCount()</b>: equal to appendElement(element) </li>
+ * <li> <b>at == 1</b>: `element` is inserted after the first element
+ * (there must be at least one element already) </li>
+ * <li> <b>at == -1</b>: `element` is inserted before the last element
+ * (there must be at least one element already) </li>
+ */
+ public abstract void insertElement(int at, @Nonnull JBasicBlockElement element);
+
+ /** Removes the specified element from the basic block */
+ public abstract void removeElement(@Nonnull JBasicBlockElement element);
+
+ /** Replace all the successors equal to 'what' with 'with' */
+ public abstract void replaceAllSuccessors(@Nonnull JBasicBlock what, @Nonnull JBasicBlock with);
+
+ /** The number of predecessors */
+ @Nonnegative
+ public final int getPredecessorCount() {
+ return predecessors.size();
+ }
+
+ /** Immutable predecessors' list */
+ public final List<JBasicBlock> getPredecessors() {
+ return Jack.getUnmodifiableCollections().getUnmodifiableList(predecessors);
+ }
+
+ /** Snapshot of predecessors' list */
+ public final List<JBasicBlock> getPredecessorsSnapshot() {
+ return Lists.newArrayList(predecessors);
+ }
+
+ @Override
+ @Nonnull
+ public Iterable<JBasicBlock> getSuccessorsIterable() {
+ return getSuccessors();
+ }
+
+ @Override
+ @Nonnull
+ public Iterable<JBasicBlock> getPredecessorsIterable() {
+ return getPredecessors();
+ }
+
+ /**
+ * Splits the block at the location `at` into two blocks:
+ * <ul>
+ * <li> a new {@link JSimpleBasicBlock} with the elements [0..at) of the original
+ * basic block and goto block element at the end</li>
+ * <li> the original block with remaining elements </li>
+ * </ul>
+ * After splitting, all the predecessors of the original block re-pint to
+ * the first block, which becomes the only predecessor of the original block.
+ * <p/>
+ * Note that `at` may be negative, with semantics same as in `insertElement(...)`.
+ * <p/>
+ * Value of `at` must not be pointing after the last element of
+ * the block, since the last element must stay in the original block.
+ *
+ * <pre>
+ * Original:
+ * block {e0, e1, ... eAt, ... eN}
+ *
+ * Split at '0':
+ * simple-block {goto-element} --->
+ * block {e0, e1, ... eAt, ... eN}
+ *
+ * Split at I:
+ * simple-block {e0, e1, ... e(I-1), goto-element} --->
+ * block {eI, ... eN}
+ * </pre>
+ *
+ * Note also that some of the block kinds don't support splitting.
+ */
+ @Nonnull
+ public abstract JSimpleBasicBlock split(int at);
+
+ /** Return the index of the block element, element must exist */
+ @Nonnegative
+ public abstract int indexOf(@Nonnull JBasicBlockElement element);
+
+ /**
+ * Detaches `this` from CFG by de-referencing all successors, the block
+ * must not have any predecessors.
+ */
+ public void detach() {
+ if (!this.predecessors.isEmpty()) {
+ throw new IllegalStateException("The basic block must not have predecessors");
+ }
+
+ // De-reference all successors
+ for (JBasicBlock successor : getSuccessors()) {
+ replaceAllSuccessors(successor, this);
+ }
+ }
+
+ /**
+ * Detaches `this` from CFG by replacing it with the `newBlock` in all
+ * predecessors of `this` block and also de-referencing all successors.
+ * Returns `newBlock`.
+ */
+ @Nonnull
+ public JBasicBlock detach(@Nonnull JBasicBlock newBlock) {
+ // Redirect all the predecessors to point `newBlock`
+ if (!this.predecessors.isEmpty()) {
+ for (JBasicBlock pre : this.getPredecessorsSnapshot()) {
+ pre.replaceAllSuccessors(this, newBlock);
+ }
+ }
+ // De-reference all successors
+ for (JBasicBlock successor : getSuccessors()) {
+ replaceAllSuccessors(successor, this);
+ }
+ return newBlock;
+ }
+
+ /**
+ * Resets successor/predecessor references for replacing
+ * `current` with `candidate`, returns `candidate`.
+ */
+ @Nonnull
+ JBasicBlock resetSuccessor(@Nonnull JBasicBlock current, @Nonnull JBasicBlock candidate) {
+ if (candidate != current) {
+ current.removePredecessor(this);
+ candidate.addPredecessor(this);
+ }
+ return candidate;
+ }
+
+ /** Removes the reference of a successor */
+ void removeSuccessor(@Nonnull JBasicBlock current) {
+ current.removePredecessor(this);
+ }
+
+ /** Remove predecessor */
+ private void removePredecessor(@Nonnull JBasicBlock predecessor) {
+ assert predecessors.contains(predecessor);
+ int sizeM1 = predecessors.size() - 1;
+ for (int idx = 0; idx < sizeM1; idx++) {
+ if (predecessors.get(idx) == predecessor) {
+ predecessors.set(idx, predecessors.get(sizeM1));
+ break;
+ }
+ }
+ predecessors.remove(sizeM1);
+ }
+
+ /** Add predecessor */
+ void addPredecessor(@Nonnull JBasicBlock predecessor) {
+ predecessors.add(predecessor);
+ }
+
+ public void checkValidity() {
+ // NOTE: only check basic-block validity, each block element will be validated later
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JBasicBlockElement.java b/jack/src/com/android/jack/ir/ast/cfg/JBasicBlockElement.java
new file mode 100644
index 0000000..4ee540e
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JBasicBlockElement.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.google.common.collect.Lists;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JAsgOperation;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JVariableRef;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/** Represents an abstract CFG basic block element. */
+public abstract class JBasicBlockElement extends JNode {
+ @Nonnull
+ private ExceptionHandlingContext ehc;
+
+ JBasicBlockElement(@Nonnull SourceInfo info, @Nonnull ExceptionHandlingContext ehc) {
+ super(info);
+ this.ehc = ehc;
+ }
+
+ @Nonnull
+ public JBasicBlock getBasicBlock() {
+ JNode parent = getParent();
+ assert parent instanceof JBasicBlock;
+ return (JBasicBlock) parent;
+ }
+
+ /** Is this a terminal basic block element */
+ public abstract boolean isTerminal();
+
+ /** Get exception handling context */
+ @Nonnull
+ public ExceptionHandlingContext getEHContext() {
+ return ehc;
+ }
+
+ /** Reset exception handling context, update basic block */
+ public void resetEHContext(@Nonnull ExceptionHandlingContext ehc) {
+ this.ehc = ehc;
+ }
+
+ @Nonnull
+ public List<JVariableRef> getUsedVariables() {
+ final List<JVariableRef> result = Lists.newArrayList();
+ (new JVisitor() {
+ @Override
+ public boolean visit(@Nonnull JVariableRef varRef) {
+ JNode parent = varRef.getParent();
+ if (!(parent instanceof JAsgOperation) || ((JAsgOperation) parent).getLhs() != varRef) {
+ result.add(varRef);
+ }
+ return super.visit(varRef);
+ }
+ }).accept(this);
+ return result;
+ }
+
+ @CheckForNull
+ public JVariableRef getDefinedVariable() {
+ return null;
+ }
+
+ @Override
+ public abstract void traverse(@Nonnull JVisitor visitor);
+
+ @Override
+ public abstract void traverse(@Nonnull ScheduleInstance<? super Component> schedule)
+ throws Exception;
+
+ @Override
+ public abstract void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request)
+ throws Exception;
+
+ @Override
+ public void checkValidity() {
+ CfgExpressionValidator.validate(this);
+
+ if (this.isTerminal()) {
+ if (this != getBasicBlock().getLastElement()) {
+ throw new JNodeInternalError(this,
+ "Terminal block element must be the last element of the block");
+ }
+ } else {
+ if (this == getBasicBlock().getLastElement()) {
+ throw new JNodeInternalError(this,
+ "Non-terminal block element must NOT be the last element of the block");
+ }
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JCaseBasicBlock.java b/jack/src/com/android/jack/ir/ast/cfg/JCaseBasicBlock.java
new file mode 100644
index 0000000..08e27fc
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JCaseBasicBlock.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.Nonnull;
+
+/** Represents case basic block. */
+public final class JCaseBasicBlock extends JRegularBasicBlock {
+ public JCaseBasicBlock(@Nonnull JControlFlowGraph cfg, @Nonnull JBasicBlock primary) {
+ super(primary);
+ updateParents(cfg);
+ }
+
+ @Override
+ public boolean hasPrimarySuccessor() {
+ return true;
+ }
+
+ @Nonnull
+ @Override
+ public JSimpleBasicBlock split(int at) {
+ // Case blocks are referenced directly by JSwitchBasicBlock and cannot
+ // be split so that there is another block in between switch block and
+ // case block. Consider splitting the only successor of the case block.
+ // NOTE: this is also true in SSA form
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ acceptElements(visitor);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ traverseElements(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!getCfg().isInSsaForm()) {
+ if (getElementCount() > 1) {
+ throw new JNodeInternalError(this,
+ "JCaseBasicBlock must always have one single element");
+ }
+ }
+
+ for (JBasicBlock predecessor : this.getPredecessors()) {
+ if (!(predecessor instanceof JSwitchBasicBlock)) {
+ throw new JNodeInternalError(this,
+ "JCaseBasicBlock must only have JSwitchBasicBlock predecessors");
+ }
+ }
+
+ if (!(getLastElement() instanceof JCaseBlockElement)) {
+ throw new JNodeInternalError(this,
+ "The last element of the block must be case element");
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JCaseBlockElement.java b/jack/src/com/android/jack/ir/ast/cfg/JCaseBlockElement.java
new file mode 100644
index 0000000..1099893
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JCaseBlockElement.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JLiteral;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/** Represents case branch value basic block element */
+public final class JCaseBlockElement extends JBasicBlockElement {
+ /** The value is null in case the block represents the default case of the switch */
+ @CheckForNull
+ private JLiteral literal;
+
+ JCaseBlockElement(@Nonnull SourceInfo info,
+ @Nonnull ExceptionHandlingContext ehc, @CheckForNull JLiteral literal) {
+ super(info, ehc);
+ this.literal = literal;
+ }
+
+ @CheckForNull
+ public JLiteral getLiteral() {
+ return literal;
+ }
+
+ @Override
+ @Nonnull
+ public JCaseBasicBlock getBasicBlock() {
+ JBasicBlock block = super.getBasicBlock();
+ assert block instanceof JCaseBasicBlock;
+ return (JCaseBasicBlock) block;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ if (literal != null) {
+ visitor.accept(literal);
+ }
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ if (literal != null) {
+ literal.traverse(schedule);
+ }
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return true;
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(super.getBasicBlock() instanceof JCaseBasicBlock)) {
+ throw new JNodeInternalError(this, "The parent node must be JCaseBasicBlock");
+ }
+ }
+
+ @Override
+ protected void replaceImpl(
+ @Nonnull JNode existingNode, @Nonnull JNode newNode) throws UnsupportedOperationException {
+ if (literal == existingNode) {
+ literal = (JLiteral) newNode;
+ } else {
+ super.replaceImpl(existingNode, newNode);
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JCatchBasicBlock.java b/jack/src/com/android/jack/ir/ast/cfg/JCatchBasicBlock.java
new file mode 100644
index 0000000..1187ad5
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JCatchBasicBlock.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.Jack;
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JClass;
+import com.android.jack.ir.ast.JLocal;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import java.util.List;
+import javax.annotation.Nonnull;
+
+/**
+ * Represents catch basic block.
+ *
+ * Catch basic block is intended to carry catch specific information and
+ * always has one catch variable assignment element.
+ */
+public final class JCatchBasicBlock extends JRegularBasicBlock {
+ @Nonnull
+ private final List<JClass> catchTypes;
+ @Nonnull
+ private final JLocal catchLocal;
+
+ public JCatchBasicBlock(@Nonnull JControlFlowGraph cfg, @Nonnull JBasicBlock primary,
+ @Nonnull List<JClass> catchTypes, @Nonnull JLocal catchLocal) {
+ super(primary);
+ this.catchTypes = catchTypes;
+ this.catchLocal = catchLocal;
+ updateParents(cfg);
+ catchLocal.updateParents(this);
+ catchLocal.setEnclosingMethodBody(cfg.getMethodBody());
+ cfg.getMethodBody().addCatchLocal(catchLocal);
+ }
+
+ @Override
+ public boolean hasPrimarySuccessor() {
+ return true;
+ }
+
+ @Nonnull
+ @Override
+ public JSimpleBasicBlock split(int at) {
+ // Catch blocks are referenced directly by JThrowingBasicBlock and cannot
+ // be split so that there is another block in between throwing block and
+ // catch block. Consider splitting the only successor of the catch block.
+ // NOTE: this is also true in SSA form
+ throw new UnsupportedOperationException();
+ }
+
+ @Nonnull
+ public List<JClass> getCatchTypes() {
+ return Jack.getUnmodifiableCollections().getUnmodifiableList(catchTypes);
+ }
+
+ @Nonnull
+ public JLocal getCatchLocal() {
+ return catchLocal;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ visitor.accept(catchLocal);
+ acceptElements(visitor);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ catchLocal.traverse(schedule);
+ traverseElements(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!getCfg().isInSsaForm()) {
+ if (getElementCount() > 1) {
+ throw new JNodeInternalError(this,
+ "JCatchBasicBlock must always have one single element");
+ }
+ }
+
+ for (JBasicBlock predecessor : this.getPredecessors()) {
+ if (!(predecessor instanceof JThrowingBasicBlock)) {
+ throw new JNodeInternalError(this,
+ "JCatchBasicBlock must only have JThrowingBasicBlock predecessors");
+ }
+ }
+
+ if (!(getLastElement() instanceof JVariableAsgBlockElement) ||
+ !((JVariableAsgBlockElement) getLastElement()).isCatchVariableAssignment()) {
+ throw new JNodeInternalError(this,
+ "The last element of the block must be catch variable assignment element");
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JConditionalBasicBlock.java b/jack/src/com/android/jack/ir/ast/cfg/JConditionalBasicBlock.java
new file mode 100644
index 0000000..91c1b65
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JConditionalBasicBlock.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Nonnull;
+
+/** Represents blocks ending with conditional branching. */
+public final class JConditionalBasicBlock extends JRegularBasicBlock {
+ @Nonnull
+ private JBasicBlock ifFalse;
+
+ /**
+ * If true, ensures that the primary successor is the one specified as isFalse,
+ * and the alternative successor if the one specified as ifTrue.
+ */
+ private boolean inverted = false;
+
+ public JConditionalBasicBlock(@Nonnull JControlFlowGraph cfg,
+ @Nonnull JBasicBlock ifTrue, @Nonnull JBasicBlock ifFalse) {
+ super(ifTrue);
+ this.ifFalse = ifFalse;
+ this.ifFalse.addPredecessor(this);
+ updateParents(cfg);
+ }
+
+ @Override
+ public boolean hasPrimarySuccessor() {
+ return true;
+ }
+
+ /** Primary successor is getIfTrue() unless the block marked as inverted */
+ @Override
+ @Nonnull
+ public JBasicBlock getPrimarySuccessor() {
+ return inverted ? getIfFalse() : getIfTrue();
+ }
+
+ /** The other, not-primary successor */
+ @Nonnull
+ public JBasicBlock getAlternativeSuccessor() {
+ return inverted ? getIfTrue() : getIfFalse();
+ }
+
+ /** Returns the branch taken if the condition is `true` */
+ @Nonnull
+ public final JBasicBlock getIfTrue() {
+ return super.getPrimarySuccessor();
+ }
+
+ /** Returns the branch taken if the condition is `false` */
+ @Nonnull
+ public final JBasicBlock getIfFalse() {
+ return ifFalse;
+ }
+
+ /**
+ * Is `true` if getIfFalse() successor should be considered a primary successor,
+ * is used in codegen to indicate if ifTrue or ifFalse branch is default.
+ */
+ public boolean isInverted() {
+ return inverted;
+ }
+
+ public void setInverted(boolean inverted) {
+ this.inverted = inverted;
+ }
+
+ @Nonnull
+ @Override
+ public List<JBasicBlock> getSuccessors() {
+ ArrayList<JBasicBlock> successors = new ArrayList<>();
+ successors.add(getIfTrue());
+ successors.add(ifFalse);
+ return successors;
+ }
+
+ @Override
+ public void replaceAllSuccessors(@Nonnull JBasicBlock what, @Nonnull JBasicBlock with) {
+ super.replaceAllSuccessors(what, with);
+ if (this.ifFalse == what) {
+ this.ifFalse = resetSuccessor(what, with);
+ }
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ acceptElements(visitor);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ traverseElements(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(getLastElement() instanceof JConditionalBlockElement)) {
+ throw new JNodeInternalError(this,
+ "The last element of the block must be conditional element");
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JConditionalBlockElement.java b/jack/src/com/android/jack/ir/ast/cfg/JConditionalBlockElement.java
new file mode 100644
index 0000000..0c889af
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JConditionalBlockElement.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JPrimitiveType;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.Nonnull;
+
+/** Represents conditional basic block element */
+public final class JConditionalBlockElement extends JBasicBlockElement {
+ @Nonnull
+ private JExpression cond;
+
+ public JConditionalBlockElement(@Nonnull SourceInfo info,
+ @Nonnull ExceptionHandlingContext ehc, @Nonnull JExpression cond) {
+ super(info, ehc);
+ assert !cond.canThrow();
+ assert JPrimitiveType.JPrimitiveTypeEnum.BOOLEAN.getType().isSameType(cond.getType());
+ this.cond = cond;
+ }
+
+ @Nonnull
+ public JExpression getCondition() {
+ return cond;
+ }
+
+ @Override
+ @Nonnull
+ public JConditionalBasicBlock getBasicBlock() {
+ JBasicBlock block = super.getBasicBlock();
+ assert block instanceof JConditionalBasicBlock;
+ return (JConditionalBasicBlock) block;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ visitor.accept(cond);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ cond.traverse(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return true;
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(super.getBasicBlock() instanceof JConditionalBasicBlock)) {
+ throw new JNodeInternalError(this, "The parent node must be JConditionalBasicBlock");
+ }
+ }
+
+ @Override
+ protected void replaceImpl(
+ @Nonnull JNode existingNode, @Nonnull JNode newNode) throws UnsupportedOperationException {
+ if (cond == existingNode) {
+ cond = (JExpression) newNode;
+ } else {
+ super.replaceImpl(existingNode, newNode);
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JControlFlowGraph.java b/jack/src/com/android/jack/ir/ast/cfg/JControlFlowGraph.java
new file mode 100644
index 0000000..d486fbb
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JControlFlowGraph.java
@@ -0,0 +1,235 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.google.common.collect.Lists;
+
+import com.android.jack.ir.ast.JMethod;
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.jack.util.graph.IGraph;
+import com.android.sched.item.Component;
+import com.android.sched.item.Description;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.Stack;
+import javax.annotation.Nonnull;
+
+/** Represents method control flow graph */
+@Description("Control Flow Graph")
+public final class JControlFlowGraph extends JNode implements IGraph<JBasicBlock> {
+ @Nonnull
+ private final JEntryBasicBlock entry;
+ @Nonnull
+ private final JExitBasicBlock exit;
+
+ /** Specifies if the CFG is in SSA form or not, used for enforcing constraints */
+ private boolean inSsaForm = false;
+
+ public JControlFlowGraph(@Nonnull SourceInfo info) {
+ super(info);
+ this.exit = new JExitBasicBlock(this);
+ this.entry = new JEntryBasicBlock(this, exit);
+ }
+
+ @Nonnull
+ public final JEntryBasicBlock getEntryBlock() {
+ return entry;
+ }
+
+ @Nonnull
+ public final JExitBasicBlock getExitBlock() {
+ return exit;
+ }
+
+ @Nonnull
+ public JMethod getMethod() {
+ return getMethodBody().getMethod();
+ }
+
+ /** Is the CFG is in SSA form or not */
+ public boolean isInSsaForm() {
+ return inSsaForm;
+ }
+
+ /** Sets or clears CFG SSA form status */
+ public void setInSsaForm(boolean inSsaForm) {
+ assert this.inSsaForm != inSsaForm;
+ this.inSsaForm = inSsaForm;
+ }
+
+ @Nonnull
+ public JMethodBodyCfg getMethodBody() {
+ JNode parent = getParent();
+ assert parent instanceof JMethodBodyCfg;
+ return (JMethodBodyCfg) parent;
+ }
+
+ /**
+ * Returns all basic blocks reachable from entry block in depth-first order,
+ * ALSO returns basic blocks 'weakly referenced' via exception handling context.
+ *
+ * Note that the entry block is always the first block of the list, and the
+ * exist block may not be present in this list.
+ */
+ @Nonnull
+ public List<JBasicBlock> getReachableBlocksDepthFirst() {
+ final List<JBasicBlock> blocks = new ArrayList<>();
+ new BasicBlockIterator(this) {
+ @Override public boolean process(@Nonnull JBasicBlock block) {
+ blocks.add(block);
+ return true;
+ }
+ }.iterateDepthFirst();
+ return blocks;
+ }
+
+ /**
+ * Returns all basic blocks reachable from entry or exit blocks via
+ * successor/predecessor edges OR 'weakly referenced' via exception
+ * handling context.
+ *
+ * Note that basic blocks returned by this method represent a superset
+ * of the blocks returned by getReachableBlocksDepthFirst().
+ *
+ * The blocks are returned in stable order, i.e. two calls to this method
+ * will return the same sequence of the blocks.
+ */
+ @Nonnull
+ public List<JBasicBlock> getAllBlocksUnordered() {
+ return Lists.newArrayList(getInternalBasicBlocks(false));
+ }
+
+ /**
+ * Returns all basic blocks reachable from entry or exit blocks via
+ * successor/predecessor edges OR 'weakly referenced' via exception
+ * handling context EXCEPT entry and exit nodes themselves.
+ *
+ * The blocks are returned in stable order, i.e. two calls to this method
+ * will return the same sequence of the blocks.
+ */
+ @Nonnull
+ public List<JBasicBlock> getInternalBlocksUnordered() {
+ return Lists.newArrayList(getInternalBasicBlocks(true));
+ }
+
+ @Nonnull
+ private Set<JBasicBlock> getInternalBasicBlocks(boolean internalOnly) {
+ Set<JBasicBlock> blocks = new LinkedHashSet<>();
+ Stack<JBasicBlock> queue = new Stack<>();
+
+ JEntryBasicBlock entry = this.getEntryBlock();
+ JExitBasicBlock exit = this.getExitBlock();
+
+ blocks.add(entry);
+ queue.push(entry);
+ blocks.add(exit);
+ queue.push(exit);
+
+ while (!queue.isEmpty()) {
+ JBasicBlock block = queue.pop();
+
+ // Successors, then predecessors
+ for (JBasicBlock successor : block.getSuccessors()) {
+ if (!blocks.contains(successor)) {
+ blocks.add(successor);
+ queue.push(successor);
+ }
+ }
+ for (JBasicBlock predecessor : block.getPredecessors()) {
+ if (!blocks.contains(predecessor)) {
+ blocks.add(predecessor);
+ queue.push(predecessor);
+ }
+ }
+
+ // Catch blocks referenced from EH contexts
+ for (JBasicBlockElement element : block.getElements(true)) {
+ ExceptionHandlingContext context = element.getEHContext();
+ for (JCatchBasicBlock catchBlock : context.getCatchBlocks()) {
+ if (!blocks.contains(catchBlock)) {
+ blocks.add(catchBlock);
+ queue.push(catchBlock);
+ }
+ }
+ }
+ }
+
+ if (internalOnly) {
+ blocks.remove(entry);
+ blocks.remove(exit);
+ }
+
+ return blocks;
+ }
+
+ /** Traverses the the blocks in the order provided by `getAllBlocksUnordered()` */
+ @Override
+ public void traverse(@Nonnull final JVisitor visitor) {
+ if (visitor.visit(this)) {
+ for (JBasicBlock block : getAllBlocksUnordered()) {
+ visitor.accept(block);
+ }
+ }
+ visitor.endVisit(this);
+ }
+
+ /** Traverses the the blocks in the order provided by `getAllBlocksUnordered()` */
+ @Override
+ public void traverse(
+ @Nonnull final ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ for (JBasicBlock block : getAllBlocksUnordered()) {
+ block.traverse(schedule);
+ }
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public void checkValidity() {
+ // NOTE: only check cfg-level validity, each basic block will be validated later
+ }
+
+ @Override
+ @Nonnull
+ public List<JBasicBlock> getNodes() {
+ return getAllBlocksUnordered();
+ }
+
+ @Override
+ @Nonnull
+ public JBasicBlock getEntryNode() {
+ return getEntryBlock();
+ }
+
+ @Override
+ @Nonnull
+ public JBasicBlock getExitNode() {
+ return getExitBlock();
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JEntryBasicBlock.java b/jack/src/com/android/jack/ir/ast/cfg/JEntryBasicBlock.java
new file mode 100644
index 0000000..4d4e0dc
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JEntryBasicBlock.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.ast.JVisitor;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/** Represents entry CFG basic block, has only one successor and no predecessors */
+public final class JEntryBasicBlock extends JBasicBlock {
+ @Nonnull
+ private JBasicBlock next;
+
+ JEntryBasicBlock(@Nonnull JControlFlowGraph cfg, @Nonnull JBasicBlock next) {
+ this.next = next;
+ next.addPredecessor(this);
+ updateParents(cfg);
+ }
+
+ @Override
+ @Nonnull
+ public List<JBasicBlock> getSuccessors() {
+ return Collections.singletonList(next);
+ }
+
+ @Override
+ @Nonnull
+ public List<JBasicBlockElement> getElements(boolean forward) {
+ return Collections.emptyList();
+ }
+
+ @Nonnegative
+ @Override
+ public int getElementCount() {
+ return 0;
+ }
+
+ @Override
+ @Nonnull
+ public JBasicBlockElement getLastElement() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ @Nonnull
+ public JBasicBlockElement getFirstElement() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasElements() {
+ return false;
+ }
+
+ @Nonnull
+ public JBasicBlock getOnlySuccessor() {
+ return next;
+ }
+
+ @Override
+ public void appendElement(@Nonnull JBasicBlockElement element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ @Nonnegative
+ public int indexOf(@Nonnull JBasicBlockElement element) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public void insertElement(int at, @Nonnull JBasicBlockElement element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeElement(@Nonnull JBasicBlockElement element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void replaceAllSuccessors(@Nonnull JBasicBlock what, @Nonnull JBasicBlock with) {
+ assert next == what;
+ this.next = resetSuccessor(what, with);
+ }
+
+ @Nonnull
+ @Override
+ public JSimpleBasicBlock split(int at) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ @Nonnull
+ public JBasicBlock detach(@Nonnull JBasicBlock newBlock) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void detach() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ void addPredecessor(@Nonnull JBasicBlock predecessor) {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JExitBasicBlock.java b/jack/src/com/android/jack/ir/ast/cfg/JExitBasicBlock.java
new file mode 100644
index 0000000..89de192
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JExitBasicBlock.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.ast.JVisitor;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/** Represents exit CFG basic block */
+public final class JExitBasicBlock extends JBasicBlock {
+ JExitBasicBlock(@Nonnull JControlFlowGraph cfg) {
+ updateParents(cfg);
+ }
+
+ @Override
+ @Nonnull
+ public List<JBasicBlock> getSuccessors() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ @Nonnull
+ public List<JBasicBlockElement> getElements(boolean forward) {
+ return Collections.emptyList();
+ }
+
+ @Nonnegative
+ @Override
+ public int getElementCount() {
+ return 0;
+ }
+
+ @Override
+ @Nonnull
+ public JBasicBlockElement getLastElement() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ @Nonnull
+ public JBasicBlockElement getFirstElement() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasElements() {
+ return false;
+ }
+
+ @Override
+ public void appendElement(@Nonnull JBasicBlockElement element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nonnegative
+ public int indexOf(@Nonnull JBasicBlockElement element) {
+ throw new AssertionError();
+ }
+
+ @Override
+ public void insertElement(int at, @Nonnull JBasicBlockElement element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeElement(@Nonnull JBasicBlockElement element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void replaceAllSuccessors(@Nonnull JBasicBlock what, @Nonnull JBasicBlock with) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Nonnull
+ @Override
+ public JSimpleBasicBlock split(int at) {
+ // Splitting the exit basic block is not good at all, even if possible,
+ // since exception throwing blocks must reference it as their uncaught
+ // exception block and split(...) will break this invariant
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ @Nonnull
+ public JBasicBlock detach(@Nonnull JBasicBlock newBlock) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void detach() {
+ throw new UnsupportedOperationException();
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JGotoBlockElement.java b/jack/src/com/android/jack/ir/ast/cfg/JGotoBlockElement.java
new file mode 100644
index 0000000..21ab0b9
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JGotoBlockElement.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.Nonnull;
+
+/** Represents goto basic block element */
+public final class JGotoBlockElement extends JBasicBlockElement {
+ public JGotoBlockElement(@Nonnull SourceInfo info, @Nonnull ExceptionHandlingContext ehc) {
+ super(info, ehc);
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ visitor.visit(this);
+ visitor.endVisit(this);
+ }
+
+ @Nonnull
+ @Override
+ public JSimpleBasicBlock getBasicBlock() {
+ JBasicBlock block = super.getBasicBlock();
+ assert block instanceof JSimpleBasicBlock;
+ return (JSimpleBasicBlock) block;
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return true;
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(super.getBasicBlock() instanceof JSimpleBasicBlock)) {
+ throw new JNodeInternalError(this, "The parent node must be JSimpleBasicBlock");
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JLockBlockElement.java b/jack/src/com/android/jack/ir/ast/cfg/JLockBlockElement.java
new file mode 100644
index 0000000..da075fb
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JLockBlockElement.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JReferenceType;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.Nonnull;
+
+/** Represents lock expression basic block element */
+public final class JLockBlockElement extends JBasicBlockElement {
+ @Nonnull
+ private JExpression expr;
+
+ JLockBlockElement(@Nonnull SourceInfo info,
+ @Nonnull ExceptionHandlingContext ehc, @Nonnull JExpression expr) {
+ super(info, ehc);
+ assert !expr.canThrow();
+ assert expr.getType() instanceof JReferenceType;
+ this.expr = expr;
+ }
+
+ @Nonnull
+ public JExpression getExpression() {
+ return expr;
+ }
+
+ @Override
+ @Nonnull
+ public JThrowingExpressionBasicBlock getBasicBlock() {
+ JBasicBlock block = super.getBasicBlock();
+ assert block instanceof JThrowingExpressionBasicBlock;
+ return (JThrowingExpressionBasicBlock) block;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ visitor.accept(expr);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ expr.traverse(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return true; // Can throw
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(super.getBasicBlock() instanceof JThrowingExpressionBasicBlock)) {
+ throw new JNodeInternalError(this, "The parent node must be JThrowingExpressionBasicBlock");
+ }
+ }
+
+ @Override
+ protected void replaceImpl(
+ @Nonnull JNode existingNode, @Nonnull JNode newNode) throws UnsupportedOperationException {
+ if (expr == existingNode) {
+ expr = (JExpression) newNode;
+ } else {
+ super.replaceImpl(existingNode, newNode);
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JMethodCallBlockElement.java b/jack/src/com/android/jack/ir/ast/cfg/JMethodCallBlockElement.java
new file mode 100644
index 0000000..65ee568
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JMethodCallBlockElement.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JMethodCall;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.Nonnull;
+
+/** Represents method call basic block element */
+public final class JMethodCallBlockElement extends JBasicBlockElement {
+ @Nonnull
+ private JMethodCall call;
+
+ public JMethodCallBlockElement(@Nonnull SourceInfo info,
+ @Nonnull ExceptionHandlingContext ehc, @Nonnull JMethodCall call) {
+ super(info, ehc);
+ this.call = call;
+ }
+
+ @Nonnull
+ public JMethodCall getCall() {
+ return call;
+ }
+
+ @Nonnull
+ @Override
+ public JThrowingExpressionBasicBlock getBasicBlock() {
+ JBasicBlock block = super.getBasicBlock();
+ assert block instanceof JThrowingExpressionBasicBlock;
+ return (JThrowingExpressionBasicBlock) block;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ visitor.accept(call);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ call.traverse(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return true;
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(super.getBasicBlock() instanceof JThrowingExpressionBasicBlock)) {
+ throw new JNodeInternalError(this, "The parent node must be JThrowingExpressionBasicBlock");
+ }
+ }
+
+ @Override
+ protected void replaceImpl(
+ @Nonnull JNode existingNode, @Nonnull JNode newNode) throws UnsupportedOperationException {
+ if (call == existingNode) {
+ call = (JMethodCall) newNode;
+ } else {
+ super.replaceImpl(existingNode, newNode);
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JPhiBlockElement.java b/jack/src/com/android/jack/ir/ast/cfg/JPhiBlockElement.java
new file mode 100644
index 0000000..a3414c3
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JPhiBlockElement.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JSsaVariableDefRef;
+import com.android.jack.ir.ast.JSsaVariableDefRefPlaceHolder;
+import com.android.jack.ir.ast.JSsaVariableRef;
+import com.android.jack.ir.ast.JSsaVariableUseRef;
+import com.android.jack.ir.ast.JVariable;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.item.Description;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Represents a Phi statement in the SSA form.
+ */
+@Description("Phi elements for SSA.")
+public class JPhiBlockElement extends JBasicBlockElement {
+ @Nonnull
+ private final JVariable var;
+
+ @Nonnull
+ private JSsaVariableDefRef lhs;
+
+ @Nonnull
+ private final JBasicBlock[] preds;
+
+ @Nonnull
+ private final JSsaVariableUseRef[] rhs;
+ /**
+ * Creates a new Phi statement.
+ *
+ * @param target Non-ssa variable this phi node is to denote to.
+ * @param predsList Predecessors block list of this phi node.
+ * @param info SourceInfo
+ */
+ public JPhiBlockElement(JVariable target, List<JBasicBlock> predsList, SourceInfo info) {
+ // Phi instructions are not real instructions and should not throw any exception.
+ super(info, ExceptionHandlingContext.EMPTY);
+ this.lhs = new JSsaVariableDefRefPlaceHolder(info, target);
+ lhs.updateParents(this);
+ rhs = new JSsaVariableUseRef[predsList.size()];
+ preds = new JBasicBlock[predsList.size()];
+ int index = 0;
+ for (JBasicBlock pred : predsList) {
+ // We are going to insert a place holder first. The SSA renamer will replace the var ref
+ // with a proper version.
+ JSsaVariableUseRef use = lhs.makeRef(info);
+ rhs[index] = use;
+ preds[index] = pred;
+ use.updateParents(this);
+ index++;
+ }
+ this.var = lhs.getTarget();
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ visitor.accept(lhs);
+ for (int i = 0; i < rhs.length; i++) {
+ visitor.accept(rhs[i]);
+ }
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ lhs.traverse(schedule);
+ for (JSsaVariableRef var : rhs) {
+ var.traverse(schedule);
+ }
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest transformRequest)
+ throws Exception {
+ visitor.visit(this, transformRequest);
+ }
+
+ @Override
+ protected void replaceImpl(@Nonnull JNode existingNode, @Nonnull JNode newNode)
+ throws UnsupportedOperationException {
+ if (lhs == existingNode) {
+ lhs = (JSsaVariableDefRef) newNode;
+ return;
+ }
+
+ for (int i = 0; i < rhs.length; i++) {
+ if (rhs[i] == existingNode) {
+ rhs[i] = (JSsaVariableUseRef) newNode;
+ return;
+ }
+ }
+ super.replaceImpl(existingNode, newNode);
+ }
+
+ @Nonnull
+ public JVariable getTarget() {
+ return var;
+ }
+
+ @Nonnull
+ public JSsaVariableDefRef getLhs() {
+ return lhs;
+ }
+
+ @Nonnull
+ public JSsaVariableUseRef[] getRhs() {
+ return Arrays.copyOf(rhs, rhs.length);
+ }
+
+ @Nonnull
+ public JSsaVariableUseRef getRhs(@Nonnull JBasicBlock pred) {
+ for (int i = 0; i < preds.length; i++) {
+ if (preds[i] == pred) {
+ return rhs[i];
+ }
+ }
+ throw new RuntimeException();
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return false;
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!super.getBasicBlock().getCfg().isInSsaForm()) {
+ throw new JNodeInternalError(this,
+ "The node must not be used in non-SSA form of CFG");
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JPlaceholderBasicBlock.java b/jack/src/com/android/jack/ir/ast/cfg/JPlaceholderBasicBlock.java
new file mode 100644
index 0000000..d6bacdf
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JPlaceholderBasicBlock.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * Represents a special basic block to be used as a temporary block
+ * representation during CFG construction.
+ *
+ * NOTE: the block can have any number of predecessors, but no successors
+ * or elements.
+ */
+public final class JPlaceholderBasicBlock extends JBasicBlock {
+ public JPlaceholderBasicBlock(@Nonnull JControlFlowGraph cfg) {
+ updateParents(cfg);
+ }
+
+ @Override
+ @Nonnull
+ public List<JBasicBlock> getSuccessors() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ @Nonnull
+ public List<JBasicBlockElement> getElements(boolean forward) {
+ return Collections.emptyList();
+ }
+
+ @Nonnegative
+ @Override
+ public int getElementCount() {
+ return 0;
+ }
+
+ @Override
+ @Nonnull
+ public JBasicBlockElement getLastElement() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ @Nonnull
+ public JBasicBlockElement getFirstElement() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean hasElements() {
+ return false;
+ }
+
+ @Override
+ public void appendElement(@Nonnull JBasicBlockElement element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nonnegative
+ public int indexOf(@Nonnull JBasicBlockElement element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void insertElement(int at, @Nonnull JBasicBlockElement element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void removeElement(@Nonnull JBasicBlockElement element) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void replaceAllSuccessors(@Nonnull JBasicBlock what, @Nonnull JBasicBlock with) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Nonnull
+ @Override
+ public JSimpleBasicBlock split(int at) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public void checkValidity() {
+ throw new JNodeInternalError(this,
+ "Placeholder basic block is transient and should not exist during validation");
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JPolymorphicMethodCallBlockElement.java b/jack/src/com/android/jack/ir/ast/cfg/JPolymorphicMethodCallBlockElement.java
new file mode 100644
index 0000000..55556cb
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JPolymorphicMethodCallBlockElement.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JPolymorphicMethodCall;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.Nonnull;
+
+/** Represents polymorphic method call basic block element */
+public final class JPolymorphicMethodCallBlockElement extends JBasicBlockElement {
+ @Nonnull
+ private JPolymorphicMethodCall call;
+
+ JPolymorphicMethodCallBlockElement(@Nonnull SourceInfo info,
+ @Nonnull ExceptionHandlingContext ehc, @Nonnull JPolymorphicMethodCall call) {
+ super(info, ehc);
+ this.call = call;
+ }
+
+ @Nonnull
+ public JPolymorphicMethodCall getCall() {
+ return call;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ visitor.accept(call);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ call.traverse(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return true;
+ }
+
+ @Override
+ protected void replaceImpl(
+ @Nonnull JNode existingNode, @Nonnull JNode newNode) throws UnsupportedOperationException {
+ if (call == existingNode) {
+ call = (JPolymorphicMethodCall) newNode;
+ } else {
+ super.replaceImpl(existingNode, newNode);
+ }
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(super.getBasicBlock() instanceof JThrowingExpressionBasicBlock)) {
+ throw new JNodeInternalError(this, "The parent node must be JThrowingExpressionBasicBlock");
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JRegularBasicBlock.java b/jack/src/com/android/jack/ir/ast/cfg/JRegularBasicBlock.java
new file mode 100644
index 0000000..d41db8d
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JRegularBasicBlock.java
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.google.common.collect.ImmutableList;
+
+import com.android.jack.Jack;
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * Represents a regular implementation of CFG basic block containing any
+ * number of basic block elements.
+ */
+public abstract class JRegularBasicBlock extends JBasicBlock {
+ @CheckForNull
+ private JBasicBlock primarySuccessor;
+
+ private final List<JBasicBlockElement> elements = new ArrayList<>();
+
+ JRegularBasicBlock(@CheckForNull JBasicBlock primarySuccessor) {
+ if ((primarySuccessor == null) == hasPrimarySuccessor()) {
+ throw new AssertionError();
+ }
+ this.primarySuccessor = primarySuccessor;
+ if (this.primarySuccessor != null) {
+ this.primarySuccessor.addPredecessor(this);
+ }
+ }
+
+ @Override
+ @Nonnull
+ public List<JBasicBlockElement> getElements(boolean forward) {
+ return forward
+ ? Jack.getUnmodifiableCollections().getUnmodifiableList(elements)
+ : ImmutableList.copyOf(elements).reverse();
+ }
+
+ @Override
+ @Nonnull
+ public List<JBasicBlock> getSuccessors() {
+ return hasPrimarySuccessor()
+ ? Collections.singletonList(primarySuccessor)
+ : Collections.<JBasicBlock>emptyList();
+ }
+
+ /** If the block has primary successor */
+ public abstract boolean hasPrimarySuccessor();
+
+ /**
+ * Returns block's primary successor. Used in codegen.
+ * Valid only if the primary successor exists, see hasPrimarySuccessor().
+ */
+ @Nonnull
+ public JBasicBlock getPrimarySuccessor() {
+ assert hasPrimarySuccessor();
+ assert primarySuccessor != null;
+ return primarySuccessor;
+ }
+
+ @Override
+ public void replaceAllSuccessors(@Nonnull JBasicBlock what, @Nonnull JBasicBlock with) {
+ if (this.primarySuccessor == what) {
+ this.primarySuccessor = resetSuccessor(what, with);
+ }
+ }
+
+ @Override
+ @Nonnull
+ public JBasicBlockElement getLastElement() {
+ assert elements.size() > 0;
+ return elements.get(elements.size() - 1);
+ }
+
+ @Override
+ @Nonnull
+ public JBasicBlockElement getFirstElement() {
+ assert elements.size() > 0;
+ return elements.get(0);
+ }
+
+ @Override
+ public boolean hasElements() {
+ return !elements.isEmpty();
+ }
+
+ @Nonnegative
+ @Override
+ public int getElementCount() {
+ return elements.size();
+ }
+
+ @Override
+ public void appendElement(@Nonnull JBasicBlockElement element) {
+ elements.add(element);
+ element.updateParents(this);
+ }
+
+ @Override
+ public void insertElement(int at, @Nonnull JBasicBlockElement element) {
+ int size = elements.size();
+ if (at < 0) {
+ at = size + at;
+ }
+ assert at >= 0 && at <= size;
+
+ if (at == size) {
+ appendElement(element);
+ } else {
+ elements.add(at, element);
+ element.updateParents(this);
+ }
+ }
+
+ @Override
+ public void removeElement(@Nonnull JBasicBlockElement element) {
+ int index = elements.indexOf(element);
+ assert index != -1;
+ elements.remove(index);
+ }
+
+ @Override
+ @Nonnegative
+ public int indexOf(@Nonnull JBasicBlockElement element) {
+ assert element.getBasicBlock() == this;
+ int index = elements.indexOf(element);
+ assert index >= 0;
+ return index;
+ }
+
+ final void acceptElements(@Nonnull JVisitor visitor) {
+ visitor.accept(elements);
+ }
+
+ final void traverseElements(
+ @Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ for (JBasicBlockElement element : elements) {
+ element.traverse(schedule);
+ }
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!hasElements()) {
+ throw new JNodeInternalError(this, "Regular block must not be empty");
+ }
+
+ if (!getLastElement().isTerminal()) {
+ throw new JNodeInternalError(this,
+ "The last element of the basic block must be terminal element: " + this.toSource());
+ }
+
+ // In SSA any regular block may start with arbitrary number of Phi block
+ // elements. I.e. if phi block elements are present, they must not have any
+ // non-phi block elements on the left.
+ if (getCfg().isInSsaForm()) {
+ boolean seenNonPhi = false;
+ for (JBasicBlockElement element : elements) {
+ if (element instanceof JPhiBlockElement) {
+ if (seenNonPhi) {
+ throw new JNodeInternalError(this,
+ "Phi block element must not have non-phi element before it: " + this.toSource());
+ }
+ } else {
+ seenNonPhi = true;
+ }
+ }
+ } else {
+ for (JBasicBlockElement element : elements) {
+ if (element instanceof JPhiBlockElement) {
+ throw new JNodeInternalError(this,
+ "Phi block element must NOT be present in non-SSA CFG: " + this.toSource());
+ }
+ }
+ }
+ }
+
+ @Nonnull
+ @Override
+ public JSimpleBasicBlock split(int at) {
+ List<JBasicBlockElement> elements = this.elements;
+
+ int size = elements.size();
+ if (at < 0) {
+ at = size + at;
+ }
+ assert at >= 0 && at < size;
+
+ JSimpleBasicBlock block = new JSimpleBasicBlock(getCfg(), this);
+
+ // Move elements to a new block and append goto element at the end
+ for (int i = 0; i < at; i++) {
+ block.appendElement(elements.get(i));
+ }
+ block.appendElement(new JGotoBlockElement(SourceInfo.UNKNOWN,
+ elements.get(at).getEHContext() /* The first element of the next block */));
+
+ // Remove elements from this block
+ if (at > 0) {
+ int dest = 0;
+ for (int src = at; src < size; src++) {
+ elements.set(dest++, elements.get(src));
+ }
+ while (dest < size) {
+ elements.remove(--size);
+ }
+ }
+
+ // Re-point all the predecessors to a newly created simple block
+ for (JBasicBlock pre : this.getPredecessorsSnapshot()) {
+ if (pre != block) {
+ pre.replaceAllSuccessors(this, block);
+ }
+ }
+
+ return block;
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JReturnBasicBlock.java b/jack/src/com/android/jack/ir/ast/cfg/JReturnBasicBlock.java
new file mode 100644
index 0000000..716c9e3
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JReturnBasicBlock.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.Nonnull;
+
+/** Represents blocks ending with return. */
+public final class JReturnBasicBlock extends JRegularBasicBlock {
+ public JReturnBasicBlock(@Nonnull JControlFlowGraph cfg) {
+ super(cfg.getExitBlock());
+ updateParents(cfg);
+ }
+
+ @Override
+ public boolean hasPrimarySuccessor() {
+ return true;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ acceptElements(visitor);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ traverseElements(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(getLastElement() instanceof JReturnBlockElement)) {
+ throw new JNodeInternalError(this,
+ "JReturnBasicBlock's last element must be JReturnBlockElement");
+ }
+
+ if (!(getPrimarySuccessor() instanceof JExitBasicBlock)) {
+ throw new JNodeInternalError(this,
+ "JReturnBasicBlock's primary successor must be JExitBasicBlock");
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JReturnBlockElement.java b/jack/src/com/android/jack/ir/ast/cfg/JReturnBlockElement.java
new file mode 100644
index 0000000..20a3dbb
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JReturnBlockElement.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/** Represents return operation basic block element */
+public final class JReturnBlockElement extends JBasicBlockElement {
+ @CheckForNull
+ private JExpression expr;
+
+ public JReturnBlockElement(@Nonnull SourceInfo info,
+ @Nonnull ExceptionHandlingContext ehc, @CheckForNull JExpression expr) {
+ super(info, ehc);
+ this.expr = expr;
+ }
+
+ @CheckForNull
+ public JExpression getExpression() {
+ return expr;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ if (expr != null) {
+ visitor.accept(expr);
+ }
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ if (expr != null) {
+ expr.traverse(schedule);
+ }
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(super.getBasicBlock() instanceof JReturnBasicBlock)) {
+ throw new JNodeInternalError(this, "The parent node must be JReturnBasicBlock");
+ }
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return true;
+ }
+
+ @Override
+ protected void replaceImpl(
+ @Nonnull JNode existingNode, @Nonnull JNode newNode) throws UnsupportedOperationException {
+ if (expr == existingNode) {
+ expr = (JExpression) newNode;
+ } else {
+ super.replaceImpl(existingNode, newNode);
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JSimpleBasicBlock.java b/jack/src/com/android/jack/ir/ast/cfg/JSimpleBasicBlock.java
new file mode 100644
index 0000000..b8671c2
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JSimpleBasicBlock.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import java.util.List;
+import javax.annotation.Nonnull;
+
+/** Represents a simple blocks with just one successor ending with GOTO. */
+public final class JSimpleBasicBlock extends JRegularBasicBlock {
+ public JSimpleBasicBlock(@Nonnull JControlFlowGraph cfg, @Nonnull JBasicBlock primary) {
+ super(primary);
+ updateParents(cfg);
+ }
+
+ @Override
+ public boolean hasPrimarySuccessor() {
+ return true;
+ }
+
+ /** Merges the block into its primary successor, returns the successor */
+ @Nonnull
+ public JBasicBlock mergeIntoSuccessor() {
+ JBasicBlock successor = getPrimarySuccessor();
+ if (successor.getPredecessorCount() != 1) {
+ // Make sure we never merge into the block with multiple predecessors
+ throw new AssertionError();
+ }
+
+ // Move children
+ List<JBasicBlockElement> elements = this.getElements(true);
+ int count = elements.size() - 1; // Ignore trailing GOTO element
+ for (int i = 0; i < count; i++) {
+ successor.insertElement(i, elements.get(i));
+ }
+
+ this.detach(successor);
+ return successor;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ acceptElements(visitor);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ traverseElements(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ /**
+ * Removes the basic block by redirecting all the predecessors to point to the
+ * primary successor of this block.
+ */
+ public void delete() {
+ this.detach(getPrimarySuccessor());
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(getLastElement() instanceof JGotoBlockElement)) {
+ throw new JNodeInternalError(this, "The last element of the block must be goto element");
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JStoreBlockElement.java b/jack/src/com/android/jack/ir/ast/cfg/JStoreBlockElement.java
new file mode 100644
index 0000000..99c9e66
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JStoreBlockElement.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JArrayRef;
+import com.android.jack.ir.ast.JAsgOperation;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JFieldRef;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/** Represents field or array element store basic block element */
+public final class JStoreBlockElement extends JBasicBlockElement {
+ @Nonnull
+ private JAsgOperation asg;
+
+ public JStoreBlockElement(@Nonnull SourceInfo info,
+ @Nonnull ExceptionHandlingContext ehc, @Nonnull JAsgOperation asg) {
+ super(info, ehc);
+ assert asg.getLhs().canThrow();
+ assert asg.getLhs() instanceof JFieldRef || asg.getLhs() instanceof JArrayRef;
+ this.asg = asg;
+ }
+
+ @Nonnull
+ public JAsgOperation getAssignment() {
+ return asg;
+ }
+
+ @CheckForNull
+ public JFieldRef getLhsAsFieldRef() {
+ JExpression lhs = asg.getLhs();
+ return (lhs instanceof JFieldRef) ? (JFieldRef) lhs : null;
+ }
+
+ @CheckForNull
+ public JArrayRef getLhsAsArrayRef() {
+ JExpression lhs = asg.getLhs();
+ return (lhs instanceof JArrayRef) ? (JArrayRef) lhs : null;
+ }
+
+ @Nonnull
+ public JExpression getValueExpression() {
+ return asg.getRhs();
+ }
+
+ @Override
+ @Nonnull
+ public JThrowingExpressionBasicBlock getBasicBlock() {
+ JBasicBlock block = super.getBasicBlock();
+ assert block instanceof JThrowingExpressionBasicBlock;
+ return (JThrowingExpressionBasicBlock) block;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ visitor.accept(asg);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ asg.traverse(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return true;
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(super.getBasicBlock() instanceof JThrowingExpressionBasicBlock)) {
+ throw new JNodeInternalError(this, "The parent node must be JThrowingExpressionBasicBlock");
+ }
+ }
+
+ @Override
+ protected void replaceImpl(
+ @Nonnull JNode existingNode, @Nonnull JNode newNode) throws UnsupportedOperationException {
+ if (asg == existingNode) {
+ asg = (JAsgOperation) newNode;
+ } else {
+ super.replaceImpl(existingNode, newNode);
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JSwitchBasicBlock.java b/jack/src/com/android/jack/ir/ast/cfg/JSwitchBasicBlock.java
new file mode 100644
index 0000000..c80efcb
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JSwitchBasicBlock.java
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.Jack;
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Nonnull;
+
+/** Represents switch block. */
+public final class JSwitchBasicBlock extends JRegularBasicBlock {
+ @Nonnull
+ private final List<JBasicBlock> cases = new ArrayList<>();
+
+ public JSwitchBasicBlock(@Nonnull JControlFlowGraph cfg, @Nonnull JBasicBlock defaultCase) {
+ super(defaultCase);
+ defaultCase.addPredecessor(this);
+ updateParents(cfg);
+ }
+
+ @Override
+ public boolean hasPrimarySuccessor() {
+ return true;
+ }
+
+ @Nonnull
+ @Override
+ public List<JBasicBlock> getSuccessors() {
+ ArrayList<JBasicBlock> successors = new ArrayList<>();
+ successors.add(getPrimarySuccessor());
+ successors.addAll(cases);
+ return successors;
+ }
+
+ /** Add a new case block */
+ public void addCase(@Nonnull JBasicBlock block) {
+ cases.add(block);
+ block.addPredecessor(this);
+ }
+
+ @Nonnull
+ public List<JBasicBlock> getCases() {
+ return Jack.getUnmodifiableCollections().getUnmodifiableList(cases);
+ }
+
+ @Nonnull
+ public JBasicBlock getDefaultCase() {
+ return getPrimarySuccessor();
+ }
+
+ @Override
+ public void replaceAllSuccessors(@Nonnull JBasicBlock what, @Nonnull JBasicBlock with) {
+ super.replaceAllSuccessors(what, with);
+ for (int i = 0; i < cases.size(); i++) {
+ if (cases.get(i) == what) {
+ cases.set(i, resetSuccessor(what, with));
+ }
+ }
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ acceptElements(visitor);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ traverseElements(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(getLastElement() instanceof JSwitchBlockElement)) {
+ throw new JNodeInternalError(this,
+ "The last element of the block must be switch element");
+ }
+
+ for (JBasicBlock successor : cases) {
+ if (!(successor instanceof JCaseBasicBlock)) {
+ throw new JNodeInternalError(this,
+ "JSwitchBasicBlock must only have JCaseBasicBlock "
+ + "case successors: " + this.toSource());
+ }
+ JBasicBlockElement lastElement = successor.getLastElement();
+ if (lastElement instanceof JCaseBlockElement) {
+ // This will be asserted case block, checking to avoid invalid cast
+ if (((JCaseBlockElement) lastElement).getLiteral() == null) {
+ throw new JNodeInternalError(this, "JSwitchBasicBlock's case "
+ + "successor JCaseBasicBlock must NOT be default case");
+ }
+ }
+ }
+
+ // Primary successor may be non-case block if there is no
+ // catch-all case in the statement
+ JBasicBlock primarySuccessor = getPrimarySuccessor();
+ if (primarySuccessor instanceof JCaseBasicBlock) {
+ JBasicBlockElement lastElement = primarySuccessor.getLastElement();
+ if (lastElement instanceof JCaseBlockElement) {
+ // This will be asserted case block, checking to avoid invalid cast
+ if (((JCaseBlockElement) lastElement).getLiteral() != null) {
+ throw new JNodeInternalError(this, "If JSwitchBasicBlock's "
+ + "primary successor is JCaseBasicBlock it must be default case");
+ }
+ }
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JSwitchBlockElement.java b/jack/src/com/android/jack/ir/ast/cfg/JSwitchBlockElement.java
new file mode 100644
index 0000000..f87c921
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JSwitchBlockElement.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JPrimitiveType;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.Nonnull;
+
+/** Represents switch basic block element */
+public final class JSwitchBlockElement extends JBasicBlockElement {
+ @Nonnull
+ private JExpression expr;
+
+ JSwitchBlockElement(@Nonnull SourceInfo info,
+ @Nonnull ExceptionHandlingContext ehc, @Nonnull JExpression expr) {
+ super(info, ehc);
+ assert !expr.canThrow();
+ assert !JPrimitiveType.JPrimitiveTypeEnum.VOID.getType().isSameType(expr.getType());
+ assert expr.getType() instanceof JPrimitiveType;
+ this.expr = expr;
+ }
+
+ @Nonnull
+ public JExpression getExpression() {
+ return expr;
+ }
+
+ @Nonnull
+ @Override
+ public JSwitchBasicBlock getBasicBlock() {
+ JBasicBlock block = super.getBasicBlock();
+ assert block instanceof JSwitchBasicBlock;
+ return (JSwitchBasicBlock) block;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ visitor.accept(expr);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ expr.traverse(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return true;
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(super.getBasicBlock() instanceof JSwitchBasicBlock)) {
+ throw new JNodeInternalError(this, "The parent node must be JSwitchBasicBlock");
+ }
+ }
+
+ @Override
+ protected void replaceImpl(
+ @Nonnull JNode existingNode, @Nonnull JNode newNode) throws UnsupportedOperationException {
+ if (expr == existingNode) {
+ expr = (JExpression) newNode;
+ } else {
+ super.replaceImpl(existingNode, newNode);
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JThrowBasicBlock.java b/jack/src/com/android/jack/ir/ast/cfg/JThrowBasicBlock.java
new file mode 100644
index 0000000..26a401e
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JThrowBasicBlock.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.Nonnull;
+
+/** Represents blocks ended by throw. */
+public final class JThrowBasicBlock extends JThrowingBasicBlock {
+ public JThrowBasicBlock(@Nonnull JControlFlowGraph cfg) {
+ super(null, cfg.getExitBlock());
+ updateParents(cfg);
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ acceptElements(visitor);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ traverseElements(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public boolean hasPrimarySuccessor() {
+ return false;
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(getLastElement() instanceof JThrowBlockElement)) {
+ throw new JNodeInternalError(this,
+ "The last element of JThrowBasicBlock must be JThrowBlockElement");
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JThrowBlockElement.java b/jack/src/com/android/jack/ir/ast/cfg/JThrowBlockElement.java
new file mode 100644
index 0000000..32ce02f
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JThrowBlockElement.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.Nonnull;
+
+/** Represents throw expression basic block element */
+public final class JThrowBlockElement extends JBasicBlockElement {
+ @Nonnull
+ private JExpression expr;
+
+ public JThrowBlockElement(@Nonnull SourceInfo info,
+ @Nonnull ExceptionHandlingContext ehc, @Nonnull JExpression expr) {
+ super(info, ehc);
+ assert !expr.canThrow();
+ this.expr = expr;
+ }
+
+ @Nonnull
+ public JExpression getExpression() {
+ return expr;
+ }
+
+ @Nonnull
+ @Override
+ public JThrowBasicBlock getBasicBlock() {
+ JBasicBlock block = super.getBasicBlock();
+ assert block instanceof JThrowBasicBlock;
+ return (JThrowBasicBlock) block;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ visitor.accept(expr);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ expr.traverse(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return true;
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(super.getBasicBlock() instanceof JThrowBasicBlock)) {
+ throw new JNodeInternalError(this, "The parent node must be JThrowBasicBlock");
+ }
+ }
+
+ @Override
+ protected void replaceImpl(
+ @Nonnull JNode existingNode, @Nonnull JNode newNode) throws UnsupportedOperationException {
+
+ if (expr == existingNode) {
+ expr = (JExpression) newNode;
+ } else {
+ super.replaceImpl(existingNode, newNode);
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JThrowingBasicBlock.java b/jack/src/com/android/jack/ir/ast/cfg/JThrowingBasicBlock.java
new file mode 100644
index 0000000..70be19a
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JThrowingBasicBlock.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.Jack;
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JVisitor;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/** Represents blocks which potentially may trigger exceptions. */
+public abstract class JThrowingBasicBlock extends JRegularBasicBlock {
+ /** Successor for unhandled exception */
+ @Nonnull
+ private JBasicBlock unhandledBlock;
+ @Nonnull
+ private final List<JBasicBlock> catchBlocks = new ArrayList<>();
+
+ JThrowingBasicBlock(@CheckForNull JBasicBlock primary, @Nonnull JBasicBlock unhandledBlock) {
+ super(primary);
+ this.unhandledBlock = unhandledBlock;
+ this.unhandledBlock.addPredecessor(this);
+ }
+
+ @Nonnull
+ @Override
+ public List<JBasicBlock> getSuccessors() {
+ ArrayList<JBasicBlock> successors = new ArrayList<>();
+ if (hasPrimarySuccessor()) {
+ successors.add(getPrimarySuccessor());
+ }
+ successors.add(unhandledBlock);
+ successors.addAll(catchBlocks);
+ return successors;
+ }
+
+ /** Resets exception catch blocks from the EH context of the last element */
+ public void resetCatchBlocks() {
+ // Remove old catch blocks
+ for (JBasicBlock block : catchBlocks) {
+ this.removeSuccessor(block);
+ }
+ catchBlocks.clear();
+
+ // Add new catch blocks
+ catchBlocks.addAll(getLastElement().getEHContext().getCatchBlocks());
+ for (JBasicBlock block : catchBlocks) {
+ block.addPredecessor(this);
+ }
+ }
+
+ @Nonnull
+ public List<JBasicBlock> getCatchBlocks() {
+ return Jack.getUnmodifiableCollections().getUnmodifiableList(catchBlocks);
+ }
+
+ @Nonnull
+ public JBasicBlock getUnhandledBlock() {
+ return unhandledBlock;
+ }
+
+ @Override
+ public void replaceAllSuccessors(@Nonnull JBasicBlock what, @Nonnull JBasicBlock with) {
+ super.replaceAllSuccessors(what, with);
+
+ if (this.unhandledBlock == what) {
+ this.unhandledBlock = resetSuccessor(what, with);
+ }
+ for (int i = 0; i < catchBlocks.size(); i++) {
+ if (catchBlocks.get(i) == what) {
+ catchBlocks.set(i, resetSuccessor(what, with));
+ }
+ }
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ new JVisitor() {
+ @Override public boolean visit(@Nonnull JMethodCallBlockElement node) {
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JPolymorphicMethodCallBlockElement node) {
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JStoreBlockElement node) {
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JLockBlockElement node) {
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JUnlockBlockElement node) {
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JThrowBlockElement node) {
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JGotoBlockElement node) {
+ throw new JNodeInternalError(JThrowingBasicBlock.this,
+ "JThrowingBasicBlock must NOT end with JGotoBlockElement");
+ }
+
+ @Override public boolean visit(@Nonnull JSwitchBlockElement node) {
+ throw new JNodeInternalError(JThrowingBasicBlock.this,
+ "JThrowingBasicBlock must NOT end with JSwitchBlockElement");
+ }
+
+ @Override public boolean visit(@Nonnull JConditionalBlockElement node) {
+ throw new JNodeInternalError(JThrowingBasicBlock.this,
+ "JThrowingBasicBlock must NOT end with JConditionalBlockElement");
+ }
+
+ @Override public boolean visit(@Nonnull JCaseBlockElement node) {
+ throw new JNodeInternalError(JThrowingBasicBlock.this,
+ "JThrowingBasicBlock must NOT end with JCaseBlockElement");
+ }
+
+ @Override public boolean visit(@Nonnull JVariableAsgBlockElement node) {
+ if (!node.getAssignment().getRhs().canThrow()) {
+ throw new JNodeInternalError(JThrowingBasicBlock.this,
+ "JThrowingBasicBlock must NOT end with JVariableAsgBlockElement "
+ + "which does not throw");
+ }
+ return false;
+ }
+
+ @Override public boolean visit(@Nonnull JReturnBlockElement node) {
+ throw new JNodeInternalError(JThrowingBasicBlock.this,
+ "JThrowingBasicBlock must NOT end with JReturnBlockElement");
+ }
+
+ @Override public boolean visit(@Nonnull JPhiBlockElement node) {
+ throw new JNodeInternalError(JThrowingBasicBlock.this,
+ "JThrowingBasicBlock must NOT end with JPhiBlockElement");
+ }
+ }.accept(getLastElement());
+
+ if (!(getUnhandledBlock() instanceof JExitBasicBlock)) {
+ throw new JNodeInternalError(this, "Unhandled exception block of "
+ + "JThrowingBasicBlock must always point to the exit block");
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JThrowingExpressionBasicBlock.java b/jack/src/com/android/jack/ir/ast/cfg/JThrowingExpressionBasicBlock.java
new file mode 100644
index 0000000..757e5af
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JThrowingExpressionBasicBlock.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.Nonnull;
+
+/** Represents blocks which potentially may trigger exceptions. */
+public final class JThrowingExpressionBasicBlock extends JThrowingBasicBlock {
+ public JThrowingExpressionBasicBlock(
+ @Nonnull JControlFlowGraph cfg, @Nonnull JBasicBlock primary) {
+ super(primary, cfg.getExitBlock());
+ updateParents(cfg);
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ acceptElements(visitor);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ traverseElements(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public boolean hasPrimarySuccessor() {
+ return true;
+ }
+
+ /**
+ * Removes the basic block by redirecting all the predecessors to point to the
+ * primary successor of this block.
+ */
+ public void delete() {
+ this.detach(getPrimarySuccessor());
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (getLastElement() instanceof JThrowBlockElement) {
+ throw new JNodeInternalError(this,
+ "The last element of JThrowingExpressionBasicBlock must NOT be JThrowBlockElement");
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JUnlockBlockElement.java b/jack/src/com/android/jack/ir/ast/cfg/JUnlockBlockElement.java
new file mode 100644
index 0000000..3d2ff8a
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JUnlockBlockElement.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JReferenceType;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.Nonnull;
+
+/** Represents unlock expression basic block element */
+public final class JUnlockBlockElement extends JBasicBlockElement {
+ @Nonnull
+ private JExpression expr;
+
+ JUnlockBlockElement(@Nonnull SourceInfo info,
+ @Nonnull ExceptionHandlingContext ehc, @Nonnull JExpression expr) {
+ super(info, ehc);
+ assert !expr.canThrow();
+ assert expr.getType() instanceof JReferenceType;
+ this.expr = expr;
+ }
+
+ @Nonnull
+ public JExpression getExpression() {
+ return expr;
+ }
+
+ @Override
+ @Nonnull
+ public JThrowingExpressionBasicBlock getBasicBlock() {
+ JBasicBlock block = super.getBasicBlock();
+ assert block instanceof JThrowingExpressionBasicBlock;
+ return (JThrowingExpressionBasicBlock) block;
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ visitor.accept(expr);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ expr.traverse(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return true; // Can throw
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (!(super.getBasicBlock() instanceof JThrowingExpressionBasicBlock)) {
+ throw new JNodeInternalError(this, "The parent node must be JThrowingExpressionBasicBlock");
+ }
+ }
+
+ @Override
+ protected void replaceImpl(
+ @Nonnull JNode existingNode, @Nonnull JNode newNode) throws UnsupportedOperationException {
+ if (expr == existingNode) {
+ expr = (JExpression) newNode;
+ } else {
+ super.replaceImpl(existingNode, newNode);
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/JVariableAsgBlockElement.java b/jack/src/com/android/jack/ir/ast/cfg/JVariableAsgBlockElement.java
new file mode 100644
index 0000000..8a5dba6
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/JVariableAsgBlockElement.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.ir.JNodeInternalError;
+import com.android.jack.ir.ast.JArrayRef;
+import com.android.jack.ir.ast.JAsgOperation;
+import com.android.jack.ir.ast.JExceptionRuntimeValue;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JFieldRef;
+import com.android.jack.ir.ast.JMethodCall;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JPolymorphicMethodCall;
+import com.android.jack.ir.ast.JVariable;
+import com.android.jack.ir.ast.JVariableRef;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.sched.item.Component;
+import com.android.sched.scheduler.ScheduleInstance;
+import com.android.sched.transform.TransformRequest;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/** Represents a variable (local, this, parameter) assignment basic block element */
+public final class JVariableAsgBlockElement extends JBasicBlockElement {
+ @Nonnull
+ private JAsgOperation asg;
+
+ public JVariableAsgBlockElement(@Nonnull SourceInfo info,
+ @Nonnull ExceptionHandlingContext ehc, @Nonnull JAsgOperation asg) {
+ super(info, ehc);
+ assert !asg.getLhs().canThrow();
+ assert asg.getLhs() instanceof JVariableRef;
+ this.asg = asg;
+ }
+
+ @Nonnull
+ public JAsgOperation getAssignment() {
+ return asg;
+ }
+
+ @Nonnull
+ public JVariable getVariable() {
+ JExpression lhs = asg.getLhs();
+ assert lhs instanceof JVariableRef;
+ return ((JVariableRef) lhs).getTarget();
+ }
+
+ @Nonnull
+ public JExpression getValue() {
+ return asg.getRhs();
+ }
+
+ public boolean isCatchVariableAssignment() {
+ return asg.getRhs() instanceof JExceptionRuntimeValue;
+ }
+
+ public boolean isFieldLoad() {
+ return asg.getRhs() instanceof JFieldRef;
+ }
+
+ public boolean isArrayElementLoad() {
+ return asg.getRhs() instanceof JArrayRef;
+ }
+
+ public boolean isLoad() {
+ return isFieldLoad() || isArrayElementLoad();
+ }
+
+ public boolean isMethodCall() {
+ return asg.getRhs() instanceof JMethodCall;
+ }
+
+ public boolean isPolymorphicMethodCall() {
+ return asg.getRhs() instanceof JPolymorphicMethodCall;
+ }
+
+ @Override
+ @CheckForNull
+ public JVariableRef getDefinedVariable() {
+ JExpression lhs = getAssignment().getLhs();
+ if (!(lhs instanceof JVariableRef)) {
+ return null;
+ } else {
+ return (JVariableRef) lhs;
+ }
+ }
+
+ @Override
+ public void traverse(@Nonnull JVisitor visitor) {
+ if (visitor.visit(this)) {
+ visitor.accept(asg);
+ }
+ visitor.endVisit(this);
+ }
+
+ @Override
+ public void traverse(@Nonnull ScheduleInstance<? super Component> schedule) throws Exception {
+ schedule.process(this);
+ asg.traverse(schedule);
+ }
+
+ @Override
+ public void visit(@Nonnull JVisitor visitor, @Nonnull TransformRequest request) throws Exception {
+ visitor.visit(this, request);
+ }
+
+ @Override
+ public boolean isTerminal() {
+ return asg.getRhs().canThrow() || isCatchVariableAssignment();
+ }
+
+ @Override
+ public void checkValidity() {
+ super.checkValidity();
+
+ if (isCatchVariableAssignment()) {
+ if (!(getBasicBlock() instanceof JCatchBasicBlock)) {
+ throw new JNodeInternalError(this, "Parent block must be JCatchBasicBlock");
+ }
+
+ } else if (asg.getRhs().canThrow()) {
+ if (!(getBasicBlock() instanceof JThrowingExpressionBasicBlock)) {
+ throw new JNodeInternalError(this, "Parent block must be JThrowingExpressionBasicBlock");
+ }
+ }
+ }
+
+ @Override
+ protected void replaceImpl(
+ @Nonnull JNode existingNode, @Nonnull JNode newNode) throws UnsupportedOperationException {
+ if (asg == existingNode) {
+ asg = (JAsgOperation) newNode;
+ } else {
+ super.replaceImpl(existingNode, newNode);
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/MethodBodyCfgBuilder.java b/jack/src/com/android/jack/ir/ast/cfg/MethodBodyCfgBuilder.java
new file mode 100644
index 0000000..538a6f9
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/MethodBodyCfgBuilder.java
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg;
+
+import com.android.jack.Options;
+import com.android.jack.cfg.BasicBlock;
+import com.android.jack.cfg.BasicBlockMarker;
+import com.android.jack.cfg.CatchBasicBlock;
+import com.android.jack.cfg.ConditionalBasicBlock;
+import com.android.jack.cfg.ControlFlowGraph;
+import com.android.jack.cfg.EntryBlock;
+import com.android.jack.cfg.ExitBlock;
+import com.android.jack.cfg.NormalBasicBlock;
+import com.android.jack.cfg.PeiBasicBlock;
+import com.android.jack.cfg.ReturnBasicBlock;
+import com.android.jack.cfg.SwitchBasicBlock;
+import com.android.jack.cfg.ThrowBasicBlock;
+import com.android.jack.ir.SideEffectOperation;
+import com.android.jack.ir.ast.JAbstractMethodBody;
+import com.android.jack.ir.ast.JArrayRef;
+import com.android.jack.ir.ast.JAsgOperation;
+import com.android.jack.ir.ast.JAssertStatement;
+import com.android.jack.ir.ast.JCaseStatement;
+import com.android.jack.ir.ast.JCastOperation;
+import com.android.jack.ir.ast.JCatchBlock;
+import com.android.jack.ir.ast.JConcatOperation;
+import com.android.jack.ir.ast.JConditionalExpression;
+import com.android.jack.ir.ast.JConditionalOperation;
+import com.android.jack.ir.ast.JExceptionRuntimeValue;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JExpressionStatement;
+import com.android.jack.ir.ast.JFieldInitializer;
+import com.android.jack.ir.ast.JFieldRef;
+import com.android.jack.ir.ast.JGoto;
+import com.android.jack.ir.ast.JIfStatement;
+import com.android.jack.ir.ast.JLocal;
+import com.android.jack.ir.ast.JLock;
+import com.android.jack.ir.ast.JLoop;
+import com.android.jack.ir.ast.JMethod;
+import com.android.jack.ir.ast.JMethodBody;
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.JMethodCall;
+import com.android.jack.ir.ast.JMultiExpression;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JPolymorphicMethodCall;
+import com.android.jack.ir.ast.JReturnStatement;
+import com.android.jack.ir.ast.JStatement;
+import com.android.jack.ir.ast.JSwitchStatement;
+import com.android.jack.ir.ast.JThrowStatement;
+import com.android.jack.ir.ast.JUnlock;
+import com.android.jack.ir.ast.JVariableRef;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.ast.cfg.mutations.BasicBlockBuilder;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.jack.transformations.EmptyClinit;
+import com.android.jack.transformations.InvalidDefaultBridgeInInterfaceRemoved;
+import com.android.jack.transformations.ast.BooleanTestOutsideIf;
+import com.android.jack.transformations.ast.ImplicitBoxingAndUnboxing;
+import com.android.jack.transformations.ast.ImplicitCast;
+import com.android.jack.transformations.ast.InitInNewArray;
+import com.android.jack.transformations.ast.JPrimitiveClassLiteral;
+import com.android.jack.transformations.ast.MultiDimensionNewArray;
+import com.android.jack.transformations.ast.NewInstanceRemoved;
+import com.android.jack.transformations.ast.RefAsStatement;
+import com.android.jack.transformations.ast.UnassignedValues;
+import com.android.jack.transformations.ast.inner.InnerAccessor;
+import com.android.jack.transformations.ast.switches.UselessSwitches;
+import com.android.jack.transformations.booleanoperators.FallThroughMarker;
+import com.android.jack.transformations.cast.SourceCast;
+import com.android.jack.transformations.request.Replace;
+import com.android.jack.transformations.request.TransformationRequest;
+import com.android.jack.transformations.rop.cast.RopLegalCast;
+import com.android.jack.transformations.threeaddresscode.ThreeAddressCodeForm;
+import com.android.sched.item.Description;
+import com.android.sched.schedulable.Constraint;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+import com.android.sched.util.config.ThreadConfig;
+
+import java.util.ArrayList;
+import java.util.IdentityHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Stack;
+import javax.annotation.Nonnull;
+
+/** Builds a ControlFlowGraph body representation for all methods. */
+@Description("Builds a ControlFlowGraph body representation for all methods.")
+@Constraint(
+ need = {
+ ControlFlowGraph.class,
+ JExceptionRuntimeValue.class,
+ NewInstanceRemoved.class,
+ ThreeAddressCodeForm.class,
+ RopLegalCast.class,
+ InnerAccessor.class,
+ InvalidDefaultBridgeInInterfaceRemoved.class },
+ no = {
+ BooleanTestOutsideIf.class,
+ InitInNewArray.class,
+ JAsgOperation.class,
+ JPrimitiveClassLiteral.class,
+ JMultiExpression.class,
+ JConditionalExpression.class,
+ JFieldInitializer.class,
+ JConcatOperation.class,
+ JLoop.class,
+ SideEffectOperation.class,
+ UnassignedValues.class,
+ RefAsStatement.class,
+ MultiDimensionNewArray.class,
+ JSwitchStatement.SwitchWithEnum.class,
+ ImplicitBoxingAndUnboxing.class,
+ ImplicitCast.class,
+ JAssertStatement.class,
+ JConditionalOperation.class,
+ EmptyClinit.class,
+ UselessSwitches.class,
+ SourceCast.class,
+ JCastOperation.WithIntersectionType.class
+ })
+@Transform(remove = JMethodBody.class,
+ add = { JMethodBodyCfg.class, JControlFlowGraph.class })
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class MethodBodyCfgBuilder implements RunnableSchedulable<JMethod> {
+ @Nonnull
+ private final com.android.jack.util.filter.Filter<JMethod> filter =
+ ThreadConfig.get(Options.METHOD_FILTER);
+
+ @Override
+ public void run(@Nonnull JMethod method) {
+ if (method.isAbstract() ||
+ method.isNative() ||
+ !filter.accept(this.getClass(), method)) {
+ return;
+ }
+
+ ControlFlowGraph cfgMarker = method.getMarker(ControlFlowGraph.class);
+ assert cfgMarker != null;
+
+ JAbstractMethodBody body = method.getBody();
+ assert body instanceof JMethodBody;
+
+ List<JLocal> locals = ((JMethodBody) body).getLocals();
+ JMethodBodyCfg cfgBody = new JMethodBodyCfg(body.getSourceInfo(), locals);
+
+ new Builder(
+ cfgMarker.getEntryNode(),
+ (ExitBlock) cfgMarker.getExitNode(),
+ cfgBody).build();
+
+ TransformationRequest request = new TransformationRequest(method);
+ request.append(new Replace(body, cfgBody));
+ request.commit();
+
+ // Update locals to reflect new parent
+ for (JLocal local : locals) {
+ local.updateParents(cfgBody);
+ local.setEnclosingMethodBody(cfgBody);
+ }
+ }
+
+ /** Cfg builder */
+ private static class Builder {
+ @Nonnull
+ private final EntryBlock entry;
+ @Nonnull
+ private final ExitBlock exit;
+ @Nonnull
+ private final JControlFlowGraph cfg;
+ /** Maps CFG marker blocks into a created basic blocks or a placeholder */
+ @Nonnull
+ private final Map<BasicBlock, JBasicBlock> processed = new IdentityHashMap<>();
+ /** Processing queue */
+ @Nonnull
+ private final Stack<BasicBlock> queue = new Stack<>();
+ /** Maps all basic block elements into original statements */
+ @Nonnull
+ private final Map<JBasicBlockElement, JStatement> bbElements = new IdentityHashMap<>();
+
+ Builder(@Nonnull EntryBlock entry, @Nonnull ExitBlock exit, @Nonnull JMethodBodyCfg cfgBody) {
+ this.entry = entry;
+ this.exit = exit;
+ this.cfg = cfgBody.getCfg();
+ }
+
+ void build() {
+ assert entry.getSuccessors().size() == 1;
+ BasicBlock firstBlock = entry.getSuccessors().get(0);
+
+ getBlockOrEnqueue(firstBlock);
+ while (!queue.isEmpty()) {
+ buildBlock(queue.pop());
+ }
+
+ JBasicBlock block = getBlockOrEnqueue(firstBlock);
+ assert !(block instanceof JPlaceholderBasicBlock);
+ cfg.getEntryBlock().replaceAllSuccessors(cfg.getExitBlock(), block);
+
+ // Post-build steps
+ setUpCatchBlockReferences();
+ fixUpReferencesToCaseBlocks();
+ }
+
+ private void fixUpReferencesToCaseBlocks() {
+ // If the original switch statement had a fall-through case, we may
+ // end up with one case clause directly referencing another catch block,
+ // we fix this case to enforce switch block <--> case block invariants
+ // by changing the referencing block to point to the case block
+ // primary successor instead.
+ for (JBasicBlock block : processed.values()) {
+ if (block instanceof JCaseBasicBlock) {
+ for (JBasicBlock predecessor : block.getPredecessorsSnapshot()) {
+ if (!(predecessor instanceof JSwitchBasicBlock)) {
+ predecessor.replaceAllSuccessors(
+ block, ((JCaseBasicBlock) block).getPrimarySuccessor());
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets the block created to represent the cfg marker block, or enqueue it to
+ * process and return a placeholder.
+ */
+ private JBasicBlock getBlockOrEnqueue(@Nonnull BasicBlock block) {
+ if (block == exit) {
+ return cfg.getExitBlock();
+ }
+
+ JBasicBlock newBlock = processed.get(block);
+ if (newBlock != null) {
+ return newBlock;
+ }
+
+ // Create a placeholder pseudo-block
+ JPlaceholderBasicBlock placeholder = new JPlaceholderBasicBlock(cfg);
+ processed.put(block, placeholder);
+ queue.push(block);
+ return placeholder;
+ }
+
+ private void setUpCatchBlockReferences() {
+ // Use exception handling context pool to reduce allocations
+ ExceptionHandlingContext.Pool pool = new ExceptionHandlingContext.Pool();
+
+ // Assign exception handling context for all created basic block elements
+ for (Map.Entry<JBasicBlockElement, JStatement> entry : bbElements.entrySet()) {
+ List<JCatchBlock> origCatchBlocks = entry.getValue().getJCatchBlocks();
+
+ if (!origCatchBlocks.isEmpty()) {
+ // Create an ordered list of just created basic blocks to match the list of
+ // catch blocks returned by getJCatchBlocks() of the original statement.
+
+ List<JCatchBasicBlock> newCatchBlocks = new ArrayList<>(origCatchBlocks.size());
+ for (JCatchBlock origCatchBlock : origCatchBlocks) {
+ BasicBlockMarker marker = origCatchBlock.getMarker(BasicBlockMarker.class);
+ assert marker != null;
+ JBasicBlock newCatchBlock = processed.get(marker.getBasicBlock());
+ assert newCatchBlock != null;
+ assert newCatchBlock instanceof JCatchBasicBlock;
+ newCatchBlocks.add((JCatchBasicBlock) newCatchBlock);
+ }
+
+ // Reset exception handling context of the block element
+ entry.getKey().resetEHContext(pool.getOrCreate(newCatchBlocks));
+ }
+ }
+
+ // Refresh throwing basic block's catch block lists
+ for (JBasicBlock block : processed.values()) {
+ if (block instanceof JThrowingBasicBlock) {
+ ((JThrowingBasicBlock) block).resetCatchBlocks();
+ }
+ }
+ }
+
+ private void buildBlock(@Nonnull BasicBlock block) {
+ assert block != exit;
+ assert block != entry;
+
+ JBasicBlock placeholder = processed.get(block);
+ assert placeholder instanceof JPlaceholderBasicBlock;
+
+ JBasicBlock newBlock;
+
+ // Process each kind of CFG marker blocks
+ if (block instanceof PeiBasicBlock) {
+ // NOTE: this handles both PeiBasicBlock and ThrowBasicBlock
+ List<BasicBlock> successors = block.getSuccessors();
+ int index = 0;
+
+ JThrowingBasicBlock throwingBlock =
+ (block instanceof ThrowBasicBlock)
+ ? new JThrowBasicBlock(cfg)
+ : new JThrowingExpressionBasicBlock(cfg,
+ getBlockOrEnqueue(successors.get(index++)));
+
+ // Build uncaught exception block (which should be CFGs exit block)
+ JBasicBlock uncaughtExceptionBlock = getBlockOrEnqueue(successors.get(index++));
+ assert uncaughtExceptionBlock == cfg.getExitBlock();
+
+ for (; index < successors.size(); index++) {
+ // Build the catch block, note that catch blocks will be assigned later
+ // when block elements are initialized with their exception handling context
+ getBlockOrEnqueue(successors.get(index));
+ }
+ newBlock = throwingBlock;
+
+ } else if (block instanceof SwitchBasicBlock) {
+ SwitchBasicBlock switchBasicBlock = (SwitchBasicBlock) block;
+
+ JSwitchBasicBlock switchBlock =
+ new JSwitchBasicBlock(cfg, getBlockOrEnqueue(switchBasicBlock.getDefaultBlock()));
+ for (BasicBlock caseBlock : switchBasicBlock.getCasesBlock()) {
+ switchBlock.addCase(getBlockOrEnqueue(caseBlock));
+ }
+ newBlock = switchBlock;
+
+ } else if (block instanceof ConditionalBasicBlock) {
+ List<BasicBlock> successors = block.getSuccessors();
+ assert successors.size() == 2;
+ newBlock = new JConditionalBasicBlock(cfg,
+ getBlockOrEnqueue(successors.get(0)), getBlockOrEnqueue(successors.get(1)));
+
+ } else if (block instanceof CatchBasicBlock) {
+ List<BasicBlock> successors = block.getSuccessors();
+ assert successors.size() == 1;
+ CatchBasicBlock catchBlock = (CatchBasicBlock) block;
+ JLocal catchVar = catchBlock.getCatchVar();
+ newBlock = new JCatchBasicBlock(cfg, getBlockOrEnqueue(successors.get(0)),
+ catchBlock.getCatchTypes(), catchVar);
+
+ } else if (block instanceof ReturnBasicBlock) {
+ List<BasicBlock> successors = block.getSuccessors();
+ assert successors.size() == 1;
+ newBlock = new JReturnBasicBlock(cfg);
+
+ // Must always point to the exit block
+ JBasicBlock exitBlock = getBlockOrEnqueue(successors.get(0));
+ assert exitBlock == cfg.getExitBlock();
+
+ } else if (block instanceof NormalBasicBlock) {
+ List<BasicBlock> successors = block.getSuccessors();
+ assert successors.size() == 1;
+ newBlock = new JSimpleBasicBlock(cfg, getBlockOrEnqueue(successors.get(0)));
+
+ } else {
+ throw new AssertionError();
+ }
+
+ // Move statements into newly created block
+ BasicBlockEntryBuilder builder = new BasicBlockEntryBuilder(bbElements, newBlock);
+ for (JStatement stmt : block.getStatements()) {
+ builder.accept(stmt);
+ }
+
+ // Make sure Simple block element always has a trailing goto element
+ if (newBlock instanceof JSimpleBasicBlock) {
+ assert newBlock.hasElements();
+ if (!(newBlock.getLastElement() instanceof JGotoBlockElement)) {
+ newBlock.appendElement(new JGotoBlockElement(SourceInfo.UNKNOWN,
+ newBlock.getLastElement().getEHContext()));
+ }
+ }
+
+ // We have to special-case creation of JCaseBasicBlock with does not exist in CFG marker.
+ // If the first element of the created basic block is JCaseBlockElement, we will
+ // split the block we just created into two blocks and make a new JCaseBasicBlock
+ // of the first one.
+ assert newBlock.hasElements();
+ if (newBlock.getFirstElement() instanceof JCaseBlockElement) {
+ JSimpleBasicBlock firstBlock = newBlock.split(1);
+ assert firstBlock.getElementCount() == 2;
+ assert firstBlock.getPredecessors().isEmpty();
+
+ // Note that the created basic block is going to
+ // represent the original CFG marker block.
+ newBlock = new BasicBlockBuilder(cfg)
+ .append(firstBlock).removeLast()
+ .createCaseBlock(firstBlock.getPrimarySuccessor());
+
+ firstBlock.detach(newBlock);
+ }
+
+ // Replace temp block with real one and fix-up references
+ placeholder.detach(newBlock);
+ processed.put(block, newBlock);
+ }
+
+ /** Builds all the elements of the basic block */
+ private static class BasicBlockEntryBuilder extends JVisitor {
+ /** Maps *all* created basic block elements into original statements */
+ @Nonnull
+ private final Map<JBasicBlockElement, JStatement> elements;
+ /** Basic block being built */
+ private final JBasicBlock block;
+ /**
+ * Indicates the block is sealed, i.e. already saw potentially
+ * throwing expression, for assertion purpose only.
+ */
+ private boolean sealed = false;
+
+ private BasicBlockEntryBuilder(
+ @Nonnull Map<JBasicBlockElement, JStatement> elements, @Nonnull JBasicBlock block) {
+ this.elements = elements;
+ this.block = block;
+ }
+
+ private void addElement(@Nonnull JStatement statement, @Nonnull JBasicBlockElement element) {
+ assert !sealed;
+ this.elements.put(element, statement);
+ this.block.appendElement(element);
+ // NOTE: don't consider case elements as terminal, the block will be split
+ // later into a case basic block and rest representing the current block
+ this.sealed = element.isTerminal() &&
+ !(element instanceof JCaseBlockElement);
+ }
+
+ @Override
+ public boolean visit(@Nonnull JGoto x) {
+ assert block instanceof JSimpleBasicBlock;
+ addElement(x, new JGotoBlockElement(
+ x.getSourceInfo(), ExceptionHandlingContext.EMPTY));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JReturnStatement x) {
+ assert block instanceof JReturnBasicBlock;
+ addElement(x, new JReturnBlockElement(
+ x.getSourceInfo(), ExceptionHandlingContext.EMPTY, x.getExpr()));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JExpressionStatement x) {
+ JExpression expr = x.getExpr();
+ // Depending on the expression structure we create different basic block elements
+ if (expr instanceof JAsgOperation) {
+ JAsgOperation asg = (JAsgOperation) expr;
+ JExpression lhs = asg.getLhs();
+ if (lhs instanceof JArrayRef || lhs instanceof JFieldRef) {
+ addElement(x, new JStoreBlockElement(
+ x.getSourceInfo(), ExceptionHandlingContext.EMPTY, asg));
+ } else if (lhs instanceof JVariableRef) {
+ addElement(x, new JVariableAsgBlockElement(
+ x.getSourceInfo(), ExceptionHandlingContext.EMPTY, asg));
+ } else {
+ throw new AssertionError();
+ }
+
+ } else if (expr instanceof JMethodCall) {
+ addElement(x, new JMethodCallBlockElement(
+ x.getSourceInfo(), ExceptionHandlingContext.EMPTY, (JMethodCall) x.getExpr()));
+
+ } else if (expr instanceof JPolymorphicMethodCall) {
+ addElement(x, new JPolymorphicMethodCallBlockElement(x.getSourceInfo(),
+ ExceptionHandlingContext.EMPTY, (JPolymorphicMethodCall) x.getExpr()));
+
+ } else {
+ throw new AssertionError();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JThrowStatement x) {
+ assert block instanceof JThrowingBasicBlock;
+ addElement(x, new JThrowBlockElement(
+ x.getSourceInfo(), ExceptionHandlingContext.EMPTY, x.getExpr()));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JIfStatement x) {
+ // If statement must end JConditionalBasicBlock
+ assert block instanceof JConditionalBasicBlock;
+ addElement(x, new JConditionalBlockElement(
+ x.getSourceInfo(), ExceptionHandlingContext.EMPTY, x.getIfExpr()));
+
+ FallThroughMarker marker = x.getMarker(FallThroughMarker.class);
+ if (marker != null && marker.getFallThrough() == FallThroughMarker.FallThroughEnum.ELSE) {
+ ((JConditionalBasicBlock) this.block).setInverted(true);
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JCaseStatement x) {
+ addElement(x, new JCaseBlockElement(
+ x.getSourceInfo(), ExceptionHandlingContext.EMPTY, x.getExpr()));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JUnlock x) {
+ assert block instanceof JThrowingBasicBlock;
+ addElement(x, new JUnlockBlockElement(
+ x.getSourceInfo(), ExceptionHandlingContext.EMPTY, x.getLockExpr()));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JLock x) {
+ assert block instanceof JThrowingBasicBlock;
+ addElement(x, new JLockBlockElement(
+ x.getSourceInfo(), ExceptionHandlingContext.EMPTY, x.getLockExpr()));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JSwitchStatement x) {
+ assert block instanceof JSwitchBasicBlock;
+ addElement(x, new JSwitchBlockElement(
+ x.getSourceInfo(), ExceptionHandlingContext.EMPTY, x.getExpr()));
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JNode jnode) {
+ throw new AssertionError(
+ "Unsupported JNode in JBasicBlock builder: " + jnode.getClass().getCanonicalName());
+ }
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/mutations/BasicBlockBuilder.java b/jack/src/com/android/jack/ir/ast/cfg/mutations/BasicBlockBuilder.java
new file mode 100644
index 0000000..cb9c795
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/mutations/BasicBlockBuilder.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg.mutations;
+
+import com.android.jack.ir.ast.JClass;
+import com.android.jack.ir.ast.JLocal;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JCaseBasicBlock;
+import com.android.jack.ir.ast.cfg.JCatchBasicBlock;
+import com.android.jack.ir.ast.cfg.JConditionalBasicBlock;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JReturnBasicBlock;
+import com.android.jack.ir.ast.cfg.JSimpleBasicBlock;
+import com.android.jack.ir.ast.cfg.JSwitchBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowingExpressionBasicBlock;
+
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * A basic block builder, simplifies building the list of block
+ * elements and initializing the basic block.
+ */
+public class BasicBlockBuilder {
+ @Nonnull
+ private final JControlFlowGraph cfg;
+ @CheckForNull
+ private List<JBasicBlockElement> elements = new ArrayList<>();
+
+ public BasicBlockBuilder(@Nonnull JControlFlowGraph cfg) {
+ this.cfg = cfg;
+ }
+
+ @Nonnull
+ private List<JBasicBlockElement> elements() {
+ List<JBasicBlockElement> elements = this.elements;
+ if (elements == null) {
+ throw new IllegalStateException("The builder has already been closed");
+ }
+ return elements;
+ }
+
+ /** Append element */
+ @Nonnull
+ public BasicBlockBuilder append(@Nonnull JBasicBlockElement element) {
+ elements().add(element);
+ return this;
+ }
+
+ /** Append elements */
+ @Nonnull
+ public BasicBlockBuilder append(@Nonnull List<JBasicBlockElement> elements) {
+ for (JBasicBlockElement element : elements) {
+ elements().add(element);
+ }
+ return this;
+ }
+
+ /** Append elements from basic block */
+ @Nonnull
+ public BasicBlockBuilder append(@Nonnull JBasicBlock block) {
+ return append(block.getElements(true));
+ }
+
+ /** Remove the last element */
+ @Nonnull
+ public BasicBlockBuilder removeLast() {
+ List<JBasicBlockElement> elements = elements();
+ assert elements.size() > 0;
+ elements.remove(elements.size() - 1);
+ return this;
+ }
+
+ @Nonnull
+ public JSimpleBasicBlock createSimpleBlock(@Nonnull JBasicBlock primary) {
+ return commit(new JSimpleBasicBlock(cfg, primary));
+ }
+
+ @Nonnull
+ public JConditionalBasicBlock createConditionalBlock(
+ @Nonnull JBasicBlock ifTrue, @Nonnull JBasicBlock ifFalse) {
+ return commit(new JConditionalBasicBlock(cfg, ifTrue, ifFalse));
+ }
+
+ @Nonnull
+ public JCaseBasicBlock createCaseBlock(@Nonnull JBasicBlock primary) {
+ return commit(new JCaseBasicBlock(cfg, primary));
+ }
+
+ @Nonnull
+ public JSwitchBasicBlock createSwitchBlock(@Nonnull JBasicBlock defaultCase) {
+ return commit(new JSwitchBasicBlock(cfg, defaultCase));
+ }
+
+ @Nonnull
+ public JCatchBasicBlock createCatchBlock(
+ @Nonnull JBasicBlock primary, @Nonnull List<JClass> catchTypes, @Nonnull JLocal catchLocal) {
+ return commit(new JCatchBasicBlock(cfg, primary, catchTypes, catchLocal));
+ }
+
+ @Nonnull
+ public JReturnBasicBlock createReturnBlock() {
+ return commit(new JReturnBasicBlock(cfg));
+ }
+
+ @Nonnull
+ public JThrowBasicBlock createThrowBlock() {
+ JThrowBasicBlock block = commit(new JThrowBasicBlock(cfg));
+ block.resetCatchBlocks();
+ return block;
+ }
+
+ @Nonnull
+ public JThrowingExpressionBasicBlock createThrowingExprBlock(@Nonnull JBasicBlock primary) {
+ JThrowingExpressionBasicBlock block = commit(new JThrowingExpressionBasicBlock(cfg, primary));
+ block.resetCatchBlocks();
+ return block;
+ }
+
+ /** Initializes the block and closes the builder */
+ @Nonnull
+ public <T extends JBasicBlock> T commit(@Nonnull T block) {
+ for (JBasicBlockElement element : elements()) {
+ block.appendElement(element);
+ }
+ this.elements = null;
+ return block;
+ }
+}
diff --git a/jack/src/com/android/jack/ir/ast/cfg/mutations/CfgFragment.java b/jack/src/com/android/jack/ir/ast/cfg/mutations/CfgFragment.java
new file mode 100644
index 0000000..9a9361e
--- /dev/null
+++ b/jack/src/com/android/jack/ir/ast/cfg/mutations/CfgFragment.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.ir.ast.cfg.mutations;
+
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JPlaceholderBasicBlock;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * Represents a CFG fragment with one entry block and zero, one or many exit
+ * blocks. All exit blocks must converge on one single JPlaceholderBasicBlock
+ * block representing one single 'sink' point.
+ *
+ * It is not enforced that the basic blocks of the fragment represent an isolated
+ * fragment of the CFG, thus some of the basic blocks may reference outside blocks.
+ *
+ * This may be important in some cases, for example when the fragment includes
+ * JThrowingBasicBlock blocks with already properly assigned unhandled exception
+ * handler block.
+ *
+ * In case there a no edges coming out of the block, the exit block may be `null`.
+ */
+public class CfgFragment {
+ @Nonnull
+ public final JBasicBlock entry;
+ @CheckForNull
+ public final JPlaceholderBasicBlock exit;
+
+ public CfgFragment(@Nonnull JBasicBlock entry, @CheckForNull JPlaceholderBasicBlock exit) {
+ this.entry = entry;
+ this.exit = exit;
+ }
+
+ /**
+ * Replaces all the successors of `source` pointing to `target` with the entry
+ * block of the fragment, and replaces the exit block of the fragment with `target`.
+ */
+ public void insert(@Nonnull JBasicBlock source, @Nonnull JBasicBlock target) {
+ // Replace all the successor-references pointing to `target` with
+ // CFG fragment entry block.
+ source.replaceAllSuccessors(target, this.entry);
+
+ // If there is an exit block, replace it with the target
+ if (this.exit != null) {
+ this.exit.detach(target);
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/ir/impl/BaseGenerationVisitor.java b/jack/src/com/android/jack/ir/impl/BaseGenerationVisitor.java
index 52090e9..1056276 100644
--- a/jack/src/com/android/jack/ir/impl/BaseGenerationVisitor.java
+++ b/jack/src/com/android/jack/ir/impl/BaseGenerationVisitor.java
@@ -77,6 +77,7 @@
import com.android.jack.ir.ast.JLongLiteral;
import com.android.jack.ir.ast.JMethod;
import com.android.jack.ir.ast.JMethodBody;
+import com.android.jack.ir.ast.JMethodBodyCfg;
import com.android.jack.ir.ast.JMethodCall;
import com.android.jack.ir.ast.JMethodId;
import com.android.jack.ir.ast.JMethodIdRef;
@@ -102,6 +103,9 @@
import com.android.jack.ir.ast.JReturnStatement;
import com.android.jack.ir.ast.JSession;
import com.android.jack.ir.ast.JShortLiteral;
+import com.android.jack.ir.ast.JSsaVariableDefRef;
+import com.android.jack.ir.ast.JSsaVariableRef;
+import com.android.jack.ir.ast.JSsaVariableUseRef;
import com.android.jack.ir.ast.JStatement;
import com.android.jack.ir.ast.JSwitchStatement;
import com.android.jack.ir.ast.JSynchronizedBlock;
@@ -112,6 +116,33 @@
import com.android.jack.ir.ast.JType;
import com.android.jack.ir.ast.JUnlock;
import com.android.jack.ir.ast.JWhileStatement;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JCaseBasicBlock;
+import com.android.jack.ir.ast.cfg.JCaseBlockElement;
+import com.android.jack.ir.ast.cfg.JCatchBasicBlock;
+import com.android.jack.ir.ast.cfg.JConditionalBasicBlock;
+import com.android.jack.ir.ast.cfg.JConditionalBlockElement;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JEntryBasicBlock;
+import com.android.jack.ir.ast.cfg.JExitBasicBlock;
+import com.android.jack.ir.ast.cfg.JGotoBlockElement;
+import com.android.jack.ir.ast.cfg.JLockBlockElement;
+import com.android.jack.ir.ast.cfg.JMethodCallBlockElement;
+import com.android.jack.ir.ast.cfg.JPhiBlockElement;
+import com.android.jack.ir.ast.cfg.JPlaceholderBasicBlock;
+import com.android.jack.ir.ast.cfg.JPolymorphicMethodCallBlockElement;
+import com.android.jack.ir.ast.cfg.JRegularBasicBlock;
+import com.android.jack.ir.ast.cfg.JReturnBlockElement;
+import com.android.jack.ir.ast.cfg.JSimpleBasicBlock;
+import com.android.jack.ir.ast.cfg.JStoreBlockElement;
+import com.android.jack.ir.ast.cfg.JSwitchBasicBlock;
+import com.android.jack.ir.ast.cfg.JSwitchBlockElement;
+import com.android.jack.ir.ast.cfg.JThrowBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowBlockElement;
+import com.android.jack.ir.ast.cfg.JThrowingExpressionBasicBlock;
+import com.android.jack.ir.ast.cfg.JUnlockBlockElement;
+import com.android.jack.ir.ast.cfg.JVariableAsgBlockElement;
import com.android.jack.ir.formatter.SourceFormatter;
import com.android.jack.lookup.CommonTypes;
import com.android.jack.util.TextOutput;
@@ -121,8 +152,12 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
+import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
/**
@@ -181,6 +216,9 @@
protected boolean suppressType = false;
+ @CheckForNull
+ protected Map<JBasicBlock, String> cfgBlocks = null;
+
public BaseGenerationVisitor(TextOutput textOutput) {
super(textOutput);
}
@@ -517,7 +555,7 @@
semi();
newlineOpt();
} else {
- JMethodBody body = x.getBody();
+ JAbstractMethodBody body = x.getBody();
assert body != null;
accept(body);
}
@@ -888,6 +926,271 @@
}
@Override
+ public boolean visit(@Nonnull JMethodBodyCfg x) {
+ accept(x.getCfg());
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JControlFlowGraph x) {
+ openBlock();
+ needSemi = false;
+ assert this.cfgBlocks == null;
+
+ Map<JBasicBlock, String> cfgBlocks = new LinkedHashMap<>();
+ this.cfgBlocks = cfgBlocks;
+
+ // Collect basic blocks backwards to include unreachable ones
+ cfgBlocks.put(x.getEntryBlock(), getStringId(0));
+ List<JBasicBlock> blocks = x.getReachableBlocksDepthFirst();
+ blocks.addAll(x.getInternalBlocksUnordered()); // Add unreachable blocks
+ for (JBasicBlock block : blocks) {
+ if (block != x.getExitBlock() && !cfgBlocks.containsKey(block)) {
+ cfgBlocks.put(block, getStringId(cfgBlocks.size()));
+ }
+ }
+ cfgBlocks.put(x.getExitBlock(), getStringId(cfgBlocks.size()));
+
+ for (Map.Entry<JBasicBlock, String> entry : cfgBlocks.entrySet()) {
+ print(entry.getValue());
+ print(": ");
+ accept(entry.getKey());
+ }
+ closeBlock();
+
+ this.cfgBlocks = null;
+ return false;
+ }
+
+ private String getStringId(@Nonnegative int id) {
+ return String.format("bb#%1$03d", Integer.valueOf(id));
+ }
+
+ private void printBlockIds(
+ @Nonnull Iterable<JBasicBlock> blocks, @CheckForNull JBasicBlock primary) {
+ print("{");
+ String sep = " ";
+ for (JBasicBlock block : blocks) {
+ print(sep);
+ if (cfgBlocks != null && cfgBlocks.containsKey(block)) {
+ print(cfgBlocks.get(block));
+ } else {
+ print("???");
+ }
+ if (block == primary) {
+ print("**");
+ }
+ sep = ", ";
+ }
+ print(" }");
+ }
+
+ private void printBlockCommon(@Nonnull JRegularBasicBlock block) {
+ printBlockCommon(block,
+ block.hasPrimarySuccessor() ? block.getPrimarySuccessor() : null);
+ }
+
+ private void printBlockCommon(@Nonnull JBasicBlock block, @CheckForNull JBasicBlock primary) {
+ print("; suc: ");
+ printBlockIds(block.getSuccessors(), primary);
+ print("; pre: ");
+ printBlockIds(block.getPredecessors(), null);
+ newline();
+
+ List<JBasicBlockElement> elements = block.getElements(true);
+ for (int i = 0; i < elements.size(); i++) {
+ print(String.format(" e%1$03d: ", Integer.valueOf(i)));
+ accept(elements.get(i));
+ newline();
+ }
+ }
+
+ @Override
+ public boolean visit(@Nonnull JBasicBlock x) {
+ throw new AssertionError(x.getClass());
+ }
+
+ @Override
+ public boolean visit(@Nonnull JPlaceholderBasicBlock x) {
+ print("placeholder");
+ printBlockCommon(x, null);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JEntryBasicBlock x) {
+ print("entry");
+ printBlockCommon(x, null);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JExitBasicBlock x) {
+ print("exit");
+ printBlockCommon(x, null);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JPhiBlockElement x) {
+ JSsaVariableDefRef lhs = x.getLhs();
+ visit(lhs);
+ print(" = phi (");
+
+ for (JSsaVariableUseRef rhs : x.getRhs()) {
+ if (rhs == null) {
+ print("?");
+ } else {
+ visit(rhs);
+ }
+ space();
+ }
+ print(")");
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JRegularBasicBlock x) {
+ print("return");
+ printBlockCommon(x);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JThrowBasicBlock x) {
+ print("throw");
+ printBlockCommon(x);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JThrowingExpressionBasicBlock x) {
+ print("throwing expr");
+ printBlockCommon(x);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JSwitchBasicBlock x) {
+ print("switch");
+ printBlockCommon(x);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JCatchBasicBlock x) {
+ print("catch");
+ printBlockCommon(x);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JCaseBasicBlock x) {
+ print("case");
+ printBlockCommon(x);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JSimpleBasicBlock x) {
+ print("simple");
+ printBlockCommon(x);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JConditionalBasicBlock x) {
+ print("conditional");
+ printBlockCommon(x);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JBasicBlockElement x) {
+ throw new AssertionError(x.getClass());
+ }
+
+ private void printElementCommon(@Nonnull String name, @CheckForNull JExpression expr) {
+ print(name);
+ if (expr != null) {
+ print("; expr: ");
+ accept(expr);
+ }
+ }
+
+ @Override
+ public boolean visit(@Nonnull JMethodCallBlockElement x) {
+ printElementCommon("method call", x.getCall());
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JStoreBlockElement x) {
+ printElementCommon("store", x.getAssignment());
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JVariableAsgBlockElement x) {
+ printElementCommon("var", x.getAssignment());
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JLockBlockElement x) {
+ printElementCommon("lock", x.getExpression());
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JUnlockBlockElement x) {
+ printElementCommon("unlock", x.getExpression());
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JSwitchBlockElement x) {
+ printElementCommon("switch", x.getExpression());
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JReturnBlockElement x) {
+ printElementCommon("return", x.getExpression());
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JConditionalBlockElement x) {
+ printElementCommon("condition", x.getCondition());
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JThrowBlockElement x) {
+ printElementCommon("throw", x.getExpression());
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JPolymorphicMethodCallBlockElement x) {
+ printElementCommon("poly-call", x.getCall());
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JGotoBlockElement x) {
+ printElementCommon("goto", null);
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JCaseBlockElement x) {
+ printElementCommon("case", x.getLiteral());
+ return false;
+ }
+
+ @Override
public boolean visit(@Nonnull JMethodIdRef x) {
printTypeName(x.getEnclosingType());
print('.');
@@ -1101,6 +1404,35 @@
}
@Override
+ public boolean visit(@Nonnull JSsaVariableRef x) {
+ print(x.getTarget().getName());
+ print("{");
+ print("" + x.getVersion());
+ print("}");
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JSsaVariableDefRef x) {
+ print(x.getTarget().getName());
+ print("{");
+ print("" + x.getVersion());
+ int reg = x.getRopRegister();
+ if (reg != -1) {
+ print("(v");
+ print("" + reg);
+ print(")");
+ }
+ print("}");
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JSsaVariableUseRef x) {
+ return visit(x.getDef());
+ }
+
+ @Override
public boolean visit(@Nonnull JSwitchStatement x) {
print(CHARS_SWITCH);
lparen();
diff --git a/jack/src/com/android/jack/ir/impl/JavaPrecedenceVisitor.java b/jack/src/com/android/jack/ir/impl/JavaPrecedenceVisitor.java
index c40bb9e..c84c32a 100644
--- a/jack/src/com/android/jack/ir/impl/JavaPrecedenceVisitor.java
+++ b/jack/src/com/android/jack/ir/impl/JavaPrecedenceVisitor.java
@@ -48,6 +48,7 @@
import com.android.jack.ir.ast.JPostfixOperation;
import com.android.jack.ir.ast.JPrefixOperation;
import com.android.jack.ir.ast.JShortLiteral;
+import com.android.jack.ir.ast.JSsaVariableRef;
import com.android.jack.ir.ast.JThisRef;
import com.android.jack.ir.ast.JVisitor;
@@ -184,6 +185,12 @@
}
@Override
+ public boolean visit(@Nonnull JSsaVariableRef x) {
+ answer = 0;
+ return false;
+ }
+
+ @Override
public boolean visit(@Nonnull JLongLiteral x) {
answer = 0;
return false;
diff --git a/jack/src/com/android/jack/optimizations/Optimizations.java b/jack/src/com/android/jack/optimizations/Optimizations.java
index 7063f17..e146b3b 100644
--- a/jack/src/com/android/jack/optimizations/Optimizations.java
+++ b/jack/src/com/android/jack/optimizations/Optimizations.java
@@ -18,11 +18,13 @@
import com.android.jack.library.DumpInLibrary;
import com.android.jack.library.PrebuiltCompatibility;
+import com.android.jack.optimizations.cfg.VariablesScope;
import com.android.sched.item.Description;
import com.android.sched.item.Feature;
import com.android.sched.util.config.HasKeyId;
import com.android.sched.util.config.category.Private;
import com.android.sched.util.config.id.BooleanPropertyId;
+import com.android.sched.util.config.id.EnumPropertyId;
import com.android.sched.util.config.id.PropertyId;
import javax.annotation.Nonnull;
@@ -326,6 +328,43 @@
.addCategory(Private.class);
}
+ /**
+ * A {@link Feature} that represents simple block merging optimization.
+ */
+ @HasKeyId
+ @Description("Apply simple block merging optimization")
+ public static class SimpleBasicBlockMerging implements Feature {
+ @Nonnull
+ public static final BooleanPropertyId ENABLE = BooleanPropertyId
+ .create("jack.optimization.simple-block-merging",
+ "Apply simple block merging optimization")
+ .addDefaultValue(Boolean.FALSE)
+ .addCategory(DumpInLibrary.class)
+ .addCategory(PrebuiltCompatibility.class)
+ .addCategory(Private.class);
+
+ @Nonnull
+ public static final BooleanPropertyId PRESERVE_SOURCE_INFO = BooleanPropertyId
+ .create("jack.optimization.simple-block-merging.preserve-source-info",
+ "Preserves source info while merging blocks")
+ .addDefaultValue(Boolean.TRUE)
+ .requiredIf(ENABLE.getValue().isTrue())
+ .addCategory(DumpInLibrary.class)
+ .addCategory(PrebuiltCompatibility.class)
+ .addCategory(Private.class);
+
+ @Nonnull
+ public static final EnumPropertyId<VariablesScope> MERGE_VARIABLES = EnumPropertyId
+ .create("jack.optimization.simple-block-merging.merge-vars",
+ "Merge variables before merging blocks", VariablesScope.class)
+ .ignoreCase()
+ .addDefaultValue(VariablesScope.SYNTHETIC)
+ .requiredIf(ENABLE.getValue().isTrue())
+ .addCategory(DumpInLibrary.class)
+ .addCategory(PrebuiltCompatibility.class)
+ .addCategory(Private.class);
+ }
+
@Nonnull
public static final BooleanPropertyId ENABLE_NULL_INSTANCEOF =
BooleanPropertyId.create(
diff --git a/jack/src/com/android/jack/optimizations/blockmerger/CfgSimpleBasicBlockMerger.java b/jack/src/com/android/jack/optimizations/blockmerger/CfgSimpleBasicBlockMerger.java
new file mode 100644
index 0000000..3edce1e
--- /dev/null
+++ b/jack/src/com/android/jack/optimizations/blockmerger/CfgSimpleBasicBlockMerger.java
@@ -0,0 +1,495 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.blockmerger;
+
+import com.android.jack.ir.ast.JAsgOperation;
+import com.android.jack.ir.ast.JLocal;
+import com.android.jack.ir.ast.JLocalRef;
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JVariable;
+import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.ast.cfg.BasicBlockComparator;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JEntryBasicBlock;
+import com.android.jack.ir.ast.cfg.JRegularBasicBlock;
+import com.android.jack.ir.ast.cfg.JSimpleBasicBlock;
+import com.android.jack.ir.ast.cfg.JVariableAsgBlockElement;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.jack.optimizations.Optimizations;
+import com.android.jack.optimizations.cfg.CfgBasicBlockUtils;
+import com.android.jack.optimizations.cfg.VariablesScope;
+import com.android.sched.item.Description;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+import com.android.sched.schedulable.Use;
+import com.android.sched.util.config.ThreadConfig;
+import com.android.sched.util.log.Tracer;
+import com.android.sched.util.log.TracerFactory;
+import com.android.sched.util.log.stats.Counter;
+import com.android.sched.util.log.stats.CounterImpl;
+import com.android.sched.util.log.stats.StatisticId;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/** Simple CFG basic block merger */
+@Description("Simple CFG basic block merger")
+@Transform(modify = JControlFlowGraph.class)
+@Use(CfgBasicBlockUtils.class)
+public class CfgSimpleBasicBlockMerger
+ implements RunnableSchedulable<JMethodBodyCfg> {
+
+ @Nonnull
+ public static final StatisticId<Counter> BLOCKS_MERGED = new StatisticId<>(
+ "jack.optimization.simple-block-merging.blocks-merged", "Blocks merged",
+ CounterImpl.class, Counter.class);
+
+ private final boolean preserveSourceInfo =
+ ThreadConfig.get(Optimizations.SimpleBasicBlockMerging.PRESERVE_SOURCE_INFO).booleanValue();
+ @Nonnull
+ private final VariablesScope mergeVarsScope =
+ ThreadConfig.get(Optimizations.SimpleBasicBlockMerging.MERGE_VARIABLES);
+
+ @Override
+ public void run(@Nonnull final JMethodBodyCfg cfg) {
+ CfgBasicBlockUtils basicBlockUtils = new CfgBasicBlockUtils(cfg.getCfg());
+
+ // #1 Maximally split basic blocks
+ basicBlockUtils.maximallySplitAllBasicBlocks();
+
+ // #2 Merge basic blocks
+ mergeBlocks(cfg.getCfg());
+
+ // #3 Maximally merge simple basic blocks
+ basicBlockUtils.mergeSimpleBlocks(preserveSourceInfo);
+ }
+
+ private void mergeBlocks(@Nonnull final JControlFlowGraph cfg) {
+ new Processor(cfg).process();
+ }
+
+ /** Processing class */
+ private class Processor {
+ @Nonnull
+ private final Tracer tracer = TracerFactory.getTracer();
+ @Nonnull
+ private final JControlFlowGraph cfg;
+
+ /** Maps list of successors into a (stable) set of blocks having such successors */
+ @Nonnull
+ private final
+ Map<List<JBasicBlock>, Set<JBasicBlock>> groupsBySuccessors = new LinkedHashMap<>();
+ /**
+ * Maps basic blocks into the cached list of their successors, this cache is
+ * also used to access successors list after the block's successors are updated.
+ */
+ @Nonnull
+ private final Map<JBasicBlock, List<JBasicBlock>> successorsCache = new LinkedHashMap<>();
+ /** Processing queue */
+ @Nonnull
+ private final Set<List<JBasicBlock>> queue = new LinkedHashSet<>();
+
+ /** Caches last used independent variables set */
+ @CheckForNull
+ private IndependentVariables independentVariablesCache = null;
+
+ Processor(@Nonnull JControlFlowGraph cfg) {
+ this.cfg = cfg;
+
+ for (JBasicBlock block : cfg.getInternalBlocksUnordered()) {
+ assert block instanceof JRegularBasicBlock;
+ successorsCache.put(block, block.getSuccessors());
+ addBlockToSuccessorsGroup(block);
+ }
+
+ // We process all groups of the basic blocks sharing the same list of
+ // successors in stable order. In case we merged some of the blocks
+ // we might need to update `groupsBySuccessors` map, and re-process
+ // groups we have already processed.
+ queue.addAll(groupsBySuccessors.keySet());
+ }
+
+ private void addBlockToSuccessorsGroup(@Nonnull JBasicBlock block) {
+ List<JBasicBlock> successors = successorsCache.get(block);
+ assert successors != null;
+ Set<JBasicBlock> blocks = groupsBySuccessors.get(successors);
+ if (blocks == null) {
+ blocks = new LinkedHashSet<>();
+ groupsBySuccessors.put(successors, blocks);
+ }
+ blocks.add(block);
+ }
+
+ void process() {
+ while (!queue.isEmpty()) {
+ Iterator<List<JBasicBlock>> iterator = queue.iterator();
+ assert iterator.hasNext();
+ List<JBasicBlock> group = iterator.next();
+ iterator.remove();
+ processGroup(group);
+ }
+ }
+
+ private void processGroup(@Nonnull List<JBasicBlock> group) {
+ Set<JBasicBlock> blocks = groupsBySuccessors.get(group);
+ assert blocks != null;
+
+ // Only process groups with 2 or more blocks
+ if (blocks.size() < 2) {
+ return;
+ }
+
+ List<JBasicBlock> blocksOfLength = new ArrayList<>();
+
+ // We can only merge blocks of the same kind and of the same size.
+ // Iterate blocks with sizes from 1 to max.
+ int length = 1;
+ doneWithGroup:
+ while (true) {
+ // Process the blocks of the specified length only, if we find two
+ // blocks that can be be merged, do so.
+ Iterator<JBasicBlock> iterator = blocks.iterator();
+
+ sameLength:
+ while (iterator.hasNext()) {
+ JBasicBlock candidate = iterator.next();
+ if (candidate.getElementCount() == length) {
+
+ // Check if we can merge with any of the existing blocks
+ for (JBasicBlock existing : blocksOfLength) {
+
+ if (shouldReplaceCandidateWithReplacement(candidate, existing)) {
+ // Merge the blocks
+ replaceOriginalBlockWithReplacement(candidate, existing);
+
+ // Merging blocks (actually replacing `candidate` block with `existing`
+ // block) may change the current group in following ways:
+ //
+ // 1. If `candidate` is part of the group 'signature' (`group` list), *all*
+ // the blocks should be removed from this group since their successors
+ // have changed.
+ //
+ // NOTE: deleting `candidate` basic block may only result in removing basic
+ // blocks other than `candidate` from `blocks` set if these blocks
+ // are predecessors of `candidate`, meaning `candidate` is part of
+ // group signature.
+ if (blocks.isEmpty()) {
+ break doneWithGroup;
+ }
+
+ // 2. If removing `candidate` caused adding *new* blocks to `blocks` list it
+ // will also lead to this group being re-scheduled for processing, so we
+ // just stop here and let the whole group be re-processed later.
+ if (queue.contains(group)) {
+ break doneWithGroup;
+ }
+
+ // 3. Otherwise we can just remove `candidate` from `blocks` list and continue
+ // processing blocks with the same length.
+ iterator.remove();
+ continue sameLength;
+ }
+ }
+
+ // We didn't merge `candidate`
+ blocksOfLength.add(candidate);
+ }
+ }
+
+ // Try to grow the blocks in the group if possible
+ if (!addTrivialPredecessors(blocks, length)) {
+ break;
+ }
+
+ length++;
+ blocksOfLength.clear();
+ }
+ }
+
+ private void resetIndependentVariables() {
+ independentVariablesCache = null;
+ }
+
+ /**
+ * Returns true if `a` and `b` can substitute each other.
+ * They can if they are considered to be independent and appropriate
+ * options are set.
+ */
+ private boolean canSubstituteVariables(@Nonnull JLocal a, @Nonnull JLocal b) {
+ if (mergeVarsScope == VariablesScope.NONE) {
+ return false;
+ }
+ if (mergeVarsScope == VariablesScope.SYNTHETIC && (!a.isSynthetic() || !b.isSynthetic())) {
+ return false;
+ }
+
+ // Get variables info
+ if (independentVariablesCache == null) {
+ independentVariablesCache = new IndependentVariables(cfg);
+ }
+ return independentVariablesCache.areIsolatedAndIndependent(a, b);
+ }
+
+ private boolean shouldReplaceCandidateWithReplacement(
+ @Nonnull JBasicBlock candidate, @Nonnull JBasicBlock replacement) {
+ // `candidate` and be replaced with `replacement` if these two blocks are
+ // considered equal, except for allowed local variable differences.
+ BasicBlockComparator comparator = new BasicBlockComparator() {
+ @Nonnull @Override protected Comparator getComparator() {
+ @Nonnull final Map<JLocal, JLocal> substitutions = new HashMap<>();
+
+ return new Comparator() {
+ @Override protected void performCommonChecks(@Nonnull JNode node) {
+ super.performCommonChecks(node);
+ if (preserveSourceInfo) {
+ // Make sure the two nodes have same source info
+ ensure(node.getSourceInfo().equals(otherOrMe(node).getSourceInfo()));
+ }
+ }
+
+ @Override protected boolean equal(@Nonnull JVariable a, @Nonnull JVariable b) {
+ if (a == b) {
+ return true;
+ }
+ if (a instanceof JLocal && b instanceof JLocal) {
+ if (!a.getType().isSameType(b.getType())) {
+ return false; // Should be of different types
+ }
+ JLocal local = substitutions.get(a);
+ if (local != null) {
+ return b == local; // Should not be mapped into different `b`
+ }
+ local = substitutions.get(b);
+ if (local != null) {
+ return a == local; // Should not be mapped into different `a`
+ }
+ if (canSubstituteVariables((JLocal) a, (JLocal) b)) {
+ substitutions.put((JLocal) a, (JLocal) b);
+ substitutions.put((JLocal) b, (JLocal) a);
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+ }
+ };
+
+ return comparator.compare(candidate, replacement);
+ }
+
+ /**
+ * For all blocks of given length checks if their have one simple predecessor of
+ * kind JSimpleBasicBlock. Such a predecessor can me merged into their successors.
+ */
+ private boolean addTrivialPredecessors(
+ @Nonnull Set<JBasicBlock> blocks, @Nonnegative int length) {
+
+ boolean seenLonger = false;
+ for (JBasicBlock block : blocks) {
+ if (block.getElementCount() == length && block.getPredecessorCount() == 1) {
+ JBasicBlock predecessor = block.getPredecessors().get(0);
+ assert predecessor != block;
+ assert !blocks.contains(predecessor);
+
+ if (predecessor instanceof JSimpleBasicBlock) {
+ // We increase the length of the block by merging its
+ // simple predecessor into the block
+ JSimpleBasicBlock simple = (JSimpleBasicBlock) predecessor;
+ if (!preserveSourceInfo
+ || simple.getLastElement().getSourceInfo() == SourceInfo.UNKNOWN) {
+
+ // Clean up the singleton group `simple` belongs to (should be
+ // single-basic-block group with successors being { block }).
+ Set<JBasicBlock> groupBlocks = groupsBySuccessors.get(successorsCache.get(simple));
+ assert groupBlocks != null && groupBlocks.size() == 1;
+ groupBlocks.remove(simple);
+
+ // Merge `simple` into its only successor. Note that this operation
+ // should not change the blocks in the current group.
+ List<JBasicBlock> predecessors = simple.getPredecessorsSnapshot();
+ simple.mergeIntoSuccessor();
+ handleRemovedBlock(simple, predecessors);
+
+ // Merging blocks may result in more independent variables, clear the cache
+ resetIndependentVariables();
+ }
+ }
+ }
+ if (block.getElementCount() > length) {
+ seenLonger = true;
+ }
+ }
+ return seenLonger;
+ }
+
+ /**
+ * Merge `original` and `replacement` blocks, such that the original block is deleted
+ * and replaced with `replacement` block. Properly updates all maps and processing queue.
+ *
+ * After replacing with `replacement` block, `original` is removed from the CFG and all
+ * its predecessors are remapped to point into `replacement`, their successor maps are
+ * updated accordingly.
+ */
+ private void replaceOriginalBlockWithReplacement(
+ @Nonnull JBasicBlock original, @Nonnull JBasicBlock replacement) {
+
+ // Replace the block, but don't remove it from
+ // `groupsBySuccessors` (it'll be done by the caller)
+ List<JBasicBlock> originalPredecessors = original.getPredecessorsSnapshot();
+ original.detach(replacement);
+ handleRemovedBlock(original, originalPredecessors);
+ tracer.getStatistic(BLOCKS_MERGED).incValue();
+ }
+
+ private void handleRemovedBlock(
+ @Nonnull JBasicBlock block, @Nonnull List<JBasicBlock> predecessors) {
+ successorsCache.remove(block);
+
+ // Update predecessors
+ for (JBasicBlock predecessor : predecessors) {
+ List<JBasicBlock> successors = successorsCache.get(predecessor);
+ if (successors == null) {
+ assert predecessor instanceof JEntryBasicBlock || predecessor == block;
+ continue;
+ }
+
+ // Remove the predecessor from the old group. Don't need to reprocess
+ // `successors` group it was part of, since we only remove its element
+ // and if it was processed before it should not change the merging result
+ Set<JBasicBlock> blocks = groupsBySuccessors.get(successors);
+ assert blocks != null;
+ blocks.remove(predecessor);
+
+ // Put the predecessor into a new group, schedule it for reprocessing
+ successorsCache.put(predecessor, predecessor.getSuccessors());
+ addBlockToSuccessorsGroup(predecessor);
+ queue.add(successorsCache.get(predecessor));
+ }
+ }
+ }
+
+ /**
+ * Represents a set of isolated variables which can be replaced with each other
+ * during block merge (substitution).
+ *
+ * Isolated variable is a variable which value does not flow between basic blocks,
+ * i.e. if the variable is used in the basic block it is assigned in this basic
+ * block before any read.
+ */
+ private static class IndependentVariables {
+ @Nonnull
+ private final Map<JLocal, Set<JBasicBlock>> isolatedVariables = new HashMap<>();
+
+ IndependentVariables(@Nonnull JControlFlowGraph cfg) {
+ new JVisitor() {
+ @Nonnull
+ final Set<JLocal> assignedInsideBlock = new HashSet<>();
+ @Nonnull
+ final Set<JLocal> notIsolated = new HashSet<>();
+ @CheckForNull
+ JBasicBlock currentBlock = null;
+
+ @Override public boolean visit(@Nonnull JBasicBlock block) {
+ assert currentBlock == null;
+ currentBlock = block;
+ assignedInsideBlock.clear();
+ return super.visit(block);
+ }
+
+ @Override public boolean visit(@Nonnull JLocalRef ref) {
+ assert currentBlock != null;
+ JLocal local = ref.getLocal();
+
+ // Read before assignment?
+ JNode parent = ref.getParent();
+ boolean isAssignmentTarget =
+ parent instanceof JAsgOperation && ((JAsgOperation) parent).getLhs() == ref;
+ if (!isAssignmentTarget) {
+ // Local is a read ...
+ if (!assignedInsideBlock.contains(local)) {
+ // ... and is not assigned yet in this block
+ notIsolated.add(local);
+ }
+ }
+
+ // Mark referencing block
+ Set<JBasicBlock> blocks = isolatedVariables.get(local);
+ if (blocks == null) {
+ blocks = new HashSet<>();
+ isolatedVariables.put(local, blocks);
+ }
+ blocks.add(currentBlock);
+
+ return super.visit(ref);
+ }
+
+ @Override public void endVisit(@Nonnull JVariableAsgBlockElement element) {
+ assert currentBlock != null;
+ JVariable variable = element.getVariable();
+ if (variable instanceof JLocal) {
+ assignedInsideBlock.add((JLocal) variable);
+ }
+ super.endVisit(element);
+ }
+
+ @Override public void endVisit(@Nonnull JBasicBlock block) {
+ assert currentBlock != null;
+ currentBlock = null;
+ super.endVisit(block);
+ }
+
+ @Override public void endVisit(@Nonnull JControlFlowGraph cfg) {
+ // Remove non isolated locals
+ for (JLocal local : notIsolated) {
+ isolatedVariables.remove(local);
+ }
+ super.endVisit(cfg);
+ }
+ }.accept(cfg);
+ }
+
+ boolean areIsolatedAndIndependent(@Nonnull JLocal what, @Nonnull JLocal with) {
+ Set<JBasicBlock> whatBlocks = isolatedVariables.get(what);
+ if (whatBlocks == null) {
+ return false;
+ }
+ Set<JBasicBlock> withBlocks = isolatedVariables.get(with);
+ if (withBlocks == null) {
+ return false;
+ }
+ for (JBasicBlock block : whatBlocks) {
+ if (withBlocks.contains(block)) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/optimizations/cfg/CfgBasicBlockUtils.java b/jack/src/com/android/jack/optimizations/cfg/CfgBasicBlockUtils.java
new file mode 100644
index 0000000..db8a346
--- /dev/null
+++ b/jack/src/com/android/jack/optimizations/cfg/CfgBasicBlockUtils.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.cfg;
+
+import com.google.common.collect.Sets;
+
+import com.android.jack.ir.ast.cfg.BasicBlockLiveProcessor;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JPlaceholderBasicBlock;
+import com.android.jack.ir.ast.cfg.JRegularBasicBlock;
+import com.android.jack.ir.ast.cfg.JSimpleBasicBlock;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.jack.transformations.LocalVarCreator;
+import com.android.sched.schedulable.Transform;
+import com.android.sched.schedulable.Use;
+
+import java.util.Set;
+import javax.annotation.Nonnull;
+
+/** Implements series of basic block related utilities. */
+@Transform(modify = JControlFlowGraph.class)
+@Use(LocalVarCreator.class)
+public final class CfgBasicBlockUtils {
+ @Nonnull
+ private final JControlFlowGraph cfg;
+
+ public CfgBasicBlockUtils(@Nonnull JControlFlowGraph cfg) {
+ this.cfg = cfg;
+ }
+
+ /** Mergers all simple blocks into their successors when possible */
+ public void mergeSimpleBlocks(final boolean preserveSourceInfo) {
+ new BasicBlockLiveProcessor(cfg, false) {
+ @Override
+ public boolean visit(@Nonnull JSimpleBasicBlock simple) {
+ JBasicBlock primary = simple.getPrimarySuccessor();
+ if (primary.getPredecessorCount() == 1) {
+ if (!preserveSourceInfo
+ || simple.getLastElement().getSourceInfo() == SourceInfo.UNKNOWN) {
+ simple.mergeIntoSuccessor();
+ }
+ }
+ return false;
+ }
+ }.process();
+ }
+
+ /** Maximally split all basic blocks */
+ public void maximallySplitAllBasicBlocks() {
+ new BasicBlockLiveProcessor(cfg, false) {
+ @Override
+ public boolean visit(@Nonnull JSimpleBasicBlock simple) {
+ while (simple.getElementCount() > 2) {
+ simple.split(1);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean visit(@Nonnull JRegularBasicBlock regular) {
+ while (regular.getElementCount() > 1) {
+ regular.split(1);
+ }
+ return false;
+ }
+ }.process();
+ }
+
+ /**
+ * Removes basic blocks unreachable from the entry node, treats catch basic
+ * blocks referenced only from exception handling contexts as still referenced.
+ */
+ public void removeUnreachableBlocks() {
+ Set<JBasicBlock> reachable = Sets.newHashSet(cfg.getReachableBlocksDepthFirst());
+ JPlaceholderBasicBlock placeholder = new JPlaceholderBasicBlock(cfg);
+ for (JBasicBlock block : cfg.getInternalBlocksUnordered()) {
+ if (!reachable.contains(block)) {
+ block.detach(placeholder);
+ }
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/optimizations/cfg/CfgJlsNullabilityChecker.java b/jack/src/com/android/jack/optimizations/cfg/CfgJlsNullabilityChecker.java
new file mode 100644
index 0000000..e6f2743
--- /dev/null
+++ b/jack/src/com/android/jack/optimizations/cfg/CfgJlsNullabilityChecker.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.cfg;
+
+import com.android.jack.ir.ast.JAlloc;
+import com.android.jack.ir.ast.JAsgOperation;
+import com.android.jack.ir.ast.JClass;
+import com.android.jack.ir.ast.JEqOperation;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JLocal;
+import com.android.jack.ir.ast.JMethodCall;
+import com.android.jack.ir.ast.JNullLiteral;
+import com.android.jack.ir.ast.JPrimitiveType;
+import com.android.jack.ir.ast.JType;
+import com.android.jack.ir.ast.MethodKind;
+import com.android.jack.ir.ast.cfg.ExceptionHandlingContext;
+import com.android.jack.ir.ast.cfg.JConditionalBasicBlock;
+import com.android.jack.ir.ast.cfg.JConditionalBlockElement;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JMethodCallBlockElement;
+import com.android.jack.ir.ast.cfg.JPlaceholderBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowBlockElement;
+import com.android.jack.ir.ast.cfg.JThrowingExpressionBasicBlock;
+import com.android.jack.ir.ast.cfg.JVariableAsgBlockElement;
+import com.android.jack.ir.ast.cfg.mutations.CfgFragment;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.jack.lookup.CommonTypes;
+import com.android.jack.lookup.JPhantomLookup;
+import com.android.jack.transformations.LocalVarCreator;
+import com.android.jack.transformations.request.TransformationRequest;
+import com.android.jack.util.NamingTools;
+import com.android.sched.schedulable.Transform;
+import com.android.sched.schedulable.Use;
+
+import java.util.Collections;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Helps create nullability checks for preserving JLS-compliance needed
+ * in some optimizations. Works on cfg-IR, rather than regular-IR.
+ */
+@Transform(add = { JAlloc.class, JAsgOperation.NonReusedAsg.class,
+ JEqOperation.class, JMethodCall.class, JNullLiteral.class })
+@Use({ LocalVarCreator.class })
+public final class CfgJlsNullabilityChecker {
+ @Nonnull
+ private final JControlFlowGraph cfg;
+ @Nonnull
+ private final LocalVarCreator varCreator;
+ @Nonnull
+ private final JPhantomLookup getPhantomLookup;
+
+ public CfgJlsNullabilityChecker(
+ @Nonnull JControlFlowGraph cfg,
+ @Nonnull LocalVarCreator varCreator,
+ @Nonnull JPhantomLookup getPhantomLookup) {
+ this.cfg = cfg;
+ this.varCreator = varCreator;
+ this.getPhantomLookup = getPhantomLookup;
+ }
+
+ /**
+ * Creates a CFG fragment representing the following 'if' statement:
+ * <pre>
+ * if ([expr] == null) {
+ * throw new NullPointerException();
+ * }
+ * </pre>
+ */
+ @Nonnull
+ public CfgFragment createNullCheck(@Nonnull ExceptionHandlingContext ehContext,
+ @Nonnull JExpression expr, @Nonnull TransformationRequest request) {
+ assert !expr.canThrow();
+
+ // We build the following CFG fragment:
+ //
+ // bbEntry (cond: expr == null)
+ // (true) (false)
+ // / |
+ // / |
+ // bbT1 |
+ // (-t = alloc <NPE>) |
+ // | |
+ // bbT2 |
+ // (-t.<init>()) |
+ // | |
+ // bbT3 |
+ // (throw -t) |
+ // X |
+ // |
+ // bbDone <-----o
+ //
+ JClass exceptionType = getPhantomLookup
+ .getClass(CommonTypes.JAVA_LANG_NULL_POINTER_EXCEPTION);
+
+ JLocal tmp = varCreator.createTempLocal(exceptionType, SourceInfo.UNKNOWN, request);
+
+ // throw -tmp
+ JThrowBasicBlock bbT3 = new JThrowBasicBlock(cfg);
+ JThrowBlockElement throwExpr = new JThrowBlockElement(
+ SourceInfo.UNKNOWN, ehContext, tmp.makeRef(SourceInfo.UNKNOWN));
+ bbT3.appendElement(throwExpr);
+ bbT3.resetCatchBlocks();
+
+ // -tmp.<init>()
+ JMethodCallBlockElement constructorCall =
+ new JMethodCallBlockElement(SourceInfo.UNKNOWN, ehContext,
+ new JMethodCall(SourceInfo.UNKNOWN,
+ tmp.makeRef(SourceInfo.UNKNOWN),
+ exceptionType,
+ exceptionType.getOrCreateMethodId(
+ NamingTools.INIT_NAME,
+ Collections.<JType>emptyList(),
+ MethodKind.INSTANCE_NON_VIRTUAL,
+ JPrimitiveType.JPrimitiveTypeEnum.VOID.getType()),
+ false));
+
+ JThrowingExpressionBasicBlock bbT2 =
+ new JThrowingExpressionBasicBlock(cfg, bbT3);
+ bbT2.appendElement(constructorCall);
+ bbT2.resetCatchBlocks();
+
+ // -tmp = alloc <NPE>
+ JVariableAsgBlockElement alloc =
+ new JVariableAsgBlockElement(SourceInfo.UNKNOWN, ehContext,
+ new JAsgOperation(SourceInfo.UNKNOWN, tmp.makeRef(SourceInfo.UNKNOWN),
+ new JAlloc(SourceInfo.UNKNOWN, exceptionType)));
+
+ JThrowingExpressionBasicBlock bbT1 =
+ new JThrowingExpressionBasicBlock(cfg, bbT2);
+ bbT1.appendElement(alloc);
+ bbT1.resetCatchBlocks();
+
+ // Exit block
+ JPlaceholderBasicBlock exit = new JPlaceholderBasicBlock(cfg);
+
+ // Conditional block
+ JConditionalBasicBlock cond =
+ new JConditionalBasicBlock(cfg, bbT1, exit);
+ cond.appendElement(
+ new JConditionalBlockElement(SourceInfo.UNKNOWN, ehContext,
+ new JEqOperation(SourceInfo.UNKNOWN, expr, new JNullLiteral(SourceInfo.UNKNOWN))));
+
+ return new CfgFragment(cond, exit);
+ }
+}
diff --git a/jack/src/com/android/jack/optimizations/cfg/CfgVarUtils.java b/jack/src/com/android/jack/optimizations/cfg/CfgVarUtils.java
new file mode 100644
index 0000000..8e6956d
--- /dev/null
+++ b/jack/src/com/android/jack/optimizations/cfg/CfgVarUtils.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.cfg;
+
+import com.android.jack.ir.ast.JAsgOperation;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JLocal;
+import com.android.jack.ir.ast.JLocalRef;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JVariableAsgBlockElement;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.jack.transformations.LocalVarCreator;
+import com.android.jack.transformations.request.Replace;
+import com.android.jack.transformations.request.TransformationRequest;
+import com.android.sched.schedulable.Transform;
+import com.android.sched.schedulable.Use;
+
+import javax.annotation.Nonnull;
+
+/** Implements series of variable related utilities. */
+public final class CfgVarUtils {
+ /** Defines sets of transformations for method replaceWithLocal(...) */
+ @Transform(add = { JAsgOperation.NonReusedAsg.class, JLocalRef.class },
+ modify = JControlFlowGraph.class)
+ @Use(LocalVarCreator.class)
+ public static class ReplaceWithLocal {
+ }
+
+ /**
+ * Replaces the non-throwing subexpression with a newly created local.
+ *
+ * Reuses EH context from the current block element.
+ */
+ @Nonnull
+ public JLocalRef replaceWithLocal(
+ @Nonnull LocalVarCreator varCreator, @Nonnull JExpression expr) {
+ assert !expr.canThrow(); // Non-throwing expression only
+ SourceInfo srcInfo = expr.getSourceInfo();
+
+ // Get outer block element and basic block
+ JBasicBlockElement element = expr.getParent(JBasicBlockElement.class);
+ JBasicBlock block = element.getBasicBlock();
+ int index = block.indexOf(element);
+
+ // Create a new local
+ TransformationRequest request = new TransformationRequest(block.getCfg().getMethod());
+ JLocal tmp = varCreator.createTempLocal(expr.getType(), SourceInfo.UNKNOWN, request);
+
+ // Replace the expression with local reference
+ JLocalRef result = tmp.makeRef(srcInfo);
+ request.append(new Replace(expr, result));
+
+ // Commit the operation to apply changes, not that the following operation
+ // resets `expr`s parent
+ request.commit();
+
+ // Create and insert the local initialization
+ block.insertElement(index,
+ new JVariableAsgBlockElement(srcInfo,
+ element.getEHContext(), new JAsgOperation(srcInfo, tmp.makeRef(srcInfo), expr)));
+
+ return result;
+ }
+}
diff --git a/jack/src/com/android/jack/optimizations/cfg/ClearAllExceptionHandlingContexts.java b/jack/src/com/android/jack/optimizations/cfg/ClearAllExceptionHandlingContexts.java
new file mode 100644
index 0000000..e04e225
--- /dev/null
+++ b/jack/src/com/android/jack/optimizations/cfg/ClearAllExceptionHandlingContexts.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.cfg;
+
+import com.android.jack.ir.ast.cfg.ExceptionHandlingContext;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.sched.item.Description;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+
+import javax.annotation.Nonnull;
+
+/** Clear all exception handling contexts */
+@Description("Clear all exception handling contexts")
+@Transform(modify = JControlFlowGraph.class)
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class ClearAllExceptionHandlingContexts
+ implements RunnableSchedulable<JControlFlowGraph> {
+
+ @Override
+ public void run(@Nonnull final JControlFlowGraph cfg) {
+ for (JBasicBlock block : cfg.getInternalBlocksUnordered()) {
+ for (JBasicBlockElement element : block.getElements(true)) {
+ element.resetEHContext(ExceptionHandlingContext.EMPTY);
+ }
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/optimizations/cfg/OptimizeConditionalPrimarySuccessor.java b/jack/src/com/android/jack/optimizations/cfg/OptimizeConditionalPrimarySuccessor.java
new file mode 100644
index 0000000..ce1c9d5
--- /dev/null
+++ b/jack/src/com/android/jack/optimizations/cfg/OptimizeConditionalPrimarySuccessor.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.cfg;
+
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.cfg.BasicBlockLiveProcessor;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JConditionalBasicBlock;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JReturnBasicBlock;
+import com.android.jack.ir.ast.cfg.JSimpleBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowingExpressionBasicBlock;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.jack.transformations.request.TransformationRequest;
+import com.android.sched.item.Description;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+import com.android.sched.util.log.Tracer;
+import com.android.sched.util.log.TracerFactory;
+import com.android.sched.util.log.stats.Counter;
+import com.android.sched.util.log.stats.CounterImpl;
+import com.android.sched.util.log.stats.StatisticId;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/** Optimizes primary successor of the conditional block */
+@Description("Optimizes primary successor of the conditional block")
+@Transform(modify = JControlFlowGraph.class)
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class OptimizeConditionalPrimarySuccessor
+ implements RunnableSchedulable<JMethodBodyCfg> {
+
+ @Nonnull
+ public static final StatisticId<Counter> CONDITIONAL_BLOCKS_OPTIMIZED = new StatisticId<>(
+ "jack.cfg.conditional-block-optimized", "Conditional blocks optimized",
+ CounterImpl.class, Counter.class);
+
+ @Nonnull
+ private final Tracer tracer = TracerFactory.getTracer();
+
+ @Override
+ public void run(@Nonnull JMethodBodyCfg body) {
+ final TransformationRequest request = new TransformationRequest(body);
+ new BasicBlockLiveProcessor(body.getCfg(), /* stepIntoElements = */ false) {
+ @Override
+ public boolean visit(@Nonnull JConditionalBasicBlock block) {
+ JBasicBlock primary = block.getPrimarySuccessor();
+ JBasicBlock alternative = block.getAlternativeSuccessor();
+
+ // If primary block successor is also a primary successor of the
+ // alternative successor or its own primary successor, we can
+ // invert primary/secondary successors, since it usually result in
+ // better code being generated.
+ Set<JBasicBlock> blocksInChain = new HashSet<>();
+ JBasicBlock pointer = alternative;
+ while (pointer != null) {
+ if (pointer == primary) {
+ if (pointer != alternative) {
+ tracer.getStatistic(CONDITIONAL_BLOCKS_OPTIMIZED).incValue();
+ block.setInverted(!block.isInverted());
+ }
+ break;
+ } else if (blocksInChain.contains(pointer)) {
+ break;
+ }
+
+ blocksInChain.add(pointer);
+ pointer = getNextPrimary(pointer);
+ }
+
+ return false;
+ }
+ }.process();
+ request.commit();
+ }
+
+ @CheckForNull
+ private JBasicBlock getNextPrimary(@Nonnull JBasicBlock block) {
+ if (block instanceof JSimpleBasicBlock) {
+ return ((JSimpleBasicBlock) block).getPrimarySuccessor();
+ } else if (block instanceof JThrowingExpressionBasicBlock) {
+ return ((JThrowingExpressionBasicBlock) block).getPrimarySuccessor();
+ } else if (block instanceof JReturnBasicBlock) {
+ return ((JReturnBasicBlock) block).getPrimarySuccessor();
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/optimizations/cfg/RemoveEmptyBasicBlocks.java b/jack/src/com/android/jack/optimizations/cfg/RemoveEmptyBasicBlocks.java
new file mode 100644
index 0000000..a9e3c0b
--- /dev/null
+++ b/jack/src/com/android/jack/optimizations/cfg/RemoveEmptyBasicBlocks.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.cfg;
+
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.cfg.BasicBlockLiveProcessor;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JSimpleBasicBlock;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.sched.item.Description;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+
+import javax.annotation.Nonnull;
+
+/** Remove all empty basic blocks */
+@Description("Remove all empty basic blocks")
+@Transform(modify = JControlFlowGraph.class)
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class RemoveEmptyBasicBlocks
+ implements RunnableSchedulable<JMethodBodyCfg> {
+
+ @Override
+ public void run(@Nonnull final JMethodBodyCfg body) {
+ new BasicBlockLiveProcessor(body.getCfg(), /* stepIntoElements = */ false) {
+ @Override
+ public boolean visit(@Nonnull JSimpleBasicBlock block) {
+ if (block.getElementCount() == 1 &&
+ block.getLastElement().getSourceInfo() == SourceInfo.UNKNOWN) {
+ // Delete empty basic blocks without source information
+ block.delete();
+ }
+ return false;
+ }
+ }.process();
+ }
+}
diff --git a/jack/src/com/android/jack/optimizations/cfg/RemoveRedundantConditionalBlocks.java b/jack/src/com/android/jack/optimizations/cfg/RemoveRedundantConditionalBlocks.java
new file mode 100644
index 0000000..d1d85f7
--- /dev/null
+++ b/jack/src/com/android/jack/optimizations/cfg/RemoveRedundantConditionalBlocks.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.cfg;
+
+import com.android.jack.ir.ast.JBooleanLiteral;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.cfg.BasicBlockLiveProcessor;
+import com.android.jack.ir.ast.cfg.JConditionalBasicBlock;
+import com.android.jack.ir.ast.cfg.JConditionalBlockElement;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JGotoBlockElement;
+import com.android.jack.ir.ast.cfg.JSimpleBasicBlock;
+import com.android.jack.ir.ast.cfg.mutations.BasicBlockBuilder;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.sched.item.Description;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+import com.android.sched.util.log.Tracer;
+import com.android.sched.util.log.TracerFactory;
+import com.android.sched.util.log.stats.Counter;
+import com.android.sched.util.log.stats.CounterImpl;
+import com.android.sched.util.log.stats.StatisticId;
+
+import javax.annotation.Nonnull;
+
+/** Remove all conditional basic with constant condition */
+@Description("Remove all conditional basic with constant condition")
+@Transform(modify = JControlFlowGraph.class)
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class RemoveRedundantConditionalBlocks
+ implements RunnableSchedulable<JMethodBodyCfg> {
+
+ @Nonnull
+ public static final StatisticId<Counter> REMOVED_CONST_BRANCHES = new StatisticId<>(
+ "jack.cfg.const-branches-removed", "Removed branches with constant condition",
+ CounterImpl.class, Counter.class);
+
+ @Nonnull
+ public static final StatisticId<Counter> REMOVED_REDUNDANT_CONDITIONS = new StatisticId<>(
+ "jack.cfg.redundant-conditions-removed", "Removed redundant conditional blocks",
+ CounterImpl.class, Counter.class);
+
+ @Nonnull
+ private final Tracer tracer = TracerFactory.getTracer();
+
+ @Override
+ public void run(@Nonnull final JMethodBodyCfg body) {
+ new BasicBlockLiveProcessor(body.getCfg(), /* stepIntoElements = */ false) {
+ @Override
+ public boolean visit(@Nonnull JConditionalBasicBlock block) {
+ JConditionalBlockElement element =
+ (JConditionalBlockElement) block.getLastElement();
+ JExpression condition = element.getCondition();
+
+ if (block.getIfFalse() == block.getIfTrue()) {
+ // Conditional block is redundant, replace it with a simple basic block
+
+ // Note that goto block element created reuses source
+ // info of the original conditional block element
+ JSimpleBasicBlock simple = new BasicBlockBuilder(body.getCfg())
+ .append(block).removeLast()
+ .append(new JGotoBlockElement(
+ element.getSourceInfo(), element.getEHContext()))
+ .createSimpleBlock(block.getIfTrue());
+
+ // Replace conditional block with newly created simple block
+ block.detach(simple);
+
+ tracer.getStatistic(REMOVED_REDUNDANT_CONDITIONS).incValue();
+
+ } else if (condition instanceof JBooleanLiteral) {
+ // Split conditional block: pre-block --> cond-block
+ // with pre-block containing all the elements except for
+ // the last (conditional) one
+ block.split(-1);
+
+ // Detach the conditional block and replace it with either
+ // if-true or if-false successor depending on the constant value
+ block.detach(((JBooleanLiteral) condition).getValue()
+ ? block.getIfTrue() : block.getIfFalse());
+
+ tracer.getStatistic(REMOVED_CONST_BRANCHES).incValue();
+ }
+ return false;
+ }
+ }.process();
+ }
+}
diff --git a/jack/src/com/android/jack/optimizations/cfg/RemoveRedundantGotoReturnEdges.java b/jack/src/com/android/jack/optimizations/cfg/RemoveRedundantGotoReturnEdges.java
new file mode 100644
index 0000000..9a1997a
--- /dev/null
+++ b/jack/src/com/android/jack/optimizations/cfg/RemoveRedundantGotoReturnEdges.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.cfg;
+
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.cfg.BasicBlockLiveProcessor;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JReturnBasicBlock;
+import com.android.jack.ir.ast.cfg.JReturnBlockElement;
+import com.android.jack.ir.ast.cfg.JSimpleBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowBlockElement;
+import com.android.jack.ir.ast.cfg.JThrowingBasicBlock;
+import com.android.jack.ir.ast.cfg.mutations.BasicBlockBuilder;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.jack.util.CloneExpressionVisitor;
+import com.android.sched.item.Description;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+
+import javax.annotation.Nonnull;
+
+/** Remove redundant goto edges to a simple return blocks */
+@Description("Remove redundant goto edges to a simple return blocks")
+@Transform(modify = JControlFlowGraph.class)
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class RemoveRedundantGotoReturnEdges
+ implements RunnableSchedulable<JMethodBodyCfg> {
+
+ @Override
+ public void run(@Nonnull final JMethodBodyCfg body) {
+ new BasicBlockLiveProcessor(body.getCfg(), /* stepIntoElements = */ false) {
+ @Nonnull
+ private final CloneExpressionVisitor copier = new CloneExpressionVisitor();
+
+ @Override
+ public boolean visit(@Nonnull JSimpleBasicBlock block) {
+ if (block.getLastElement().getSourceInfo() != SourceInfo.UNKNOWN) {
+ // Don't handle blocks with GOTO element carrying source info
+ return false;
+ }
+
+ if (block.getElementCount() < 2) {
+ // Empty simple blocks will be optimized away by `RemoveEmptyBasicBlocks`
+ return false;
+ }
+
+ // We detect the cases when the current simple block points to a
+ // single-element return ot throw blocks, in which case we can avoid
+ // unnecessary jump instruction by replacing this block's end element
+ // with appropriate return or throw instruction.
+ JBasicBlock primary = block.getPrimarySuccessor();
+ boolean isReturnBlock = primary instanceof JReturnBasicBlock;
+ boolean isThrowBlock = primary instanceof JThrowBasicBlock;
+ if (!(isReturnBlock || isThrowBlock) || primary.getElementCount() != 1) {
+ return false;
+ }
+
+ // Move all block elements except for the trailing goto element to a new block
+ BasicBlockBuilder builder =
+ new BasicBlockBuilder(body.getCfg()).append(block).removeLast();
+ JBasicBlock newBlock;
+
+ if (isThrowBlock) {
+ // Create a new throw basic block
+ JBasicBlockElement throwElement = primary.getLastElement();
+ assert throwElement instanceof JThrowBlockElement;
+
+ JExpression expression = ((JThrowBlockElement) throwElement).getExpression();
+ builder.append(new JThrowBlockElement(
+ throwElement.getSourceInfo(), throwElement.getEHContext(),
+ copier.cloneExpression(expression)));
+
+ newBlock = builder.createThrowBlock();
+ ((JThrowingBasicBlock) newBlock).resetCatchBlocks();
+
+ } else {
+ // Create a new throw return block
+ JBasicBlockElement retElement = primary.getLastElement();
+ assert retElement instanceof JReturnBlockElement;
+
+ // Note that the expression is optional in return block element
+ JExpression expression = ((JReturnBlockElement) retElement).getExpression();
+ builder.append(new JReturnBlockElement(
+ retElement.getSourceInfo(), retElement.getEHContext(),
+ expression == null ? null : copier.cloneExpression(expression)));
+
+ newBlock = builder.createReturnBlock();
+ }
+
+ // Replace this block with a new one
+ block.detach(newBlock);
+ return false;
+ }
+ }.process();
+ }
+}
diff --git a/jack/src/com/android/jack/optimizations/cfg/RemoveUnreachableBasicBlocks.java b/jack/src/com/android/jack/optimizations/cfg/RemoveUnreachableBasicBlocks.java
new file mode 100644
index 0000000..fa6eca7
--- /dev/null
+++ b/jack/src/com/android/jack/optimizations/cfg/RemoveUnreachableBasicBlocks.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.cfg;
+
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.sched.item.Description;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+
+import javax.annotation.Nonnull;
+
+/** Remove all unreachable basic blocks, except catch blocks */
+@Description("Remove all unreachable basic blocks, except catch blocks")
+@Transform(modify = JControlFlowGraph.class)
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class RemoveUnreachableBasicBlocks
+ implements RunnableSchedulable<JMethodBodyCfg> {
+
+ @Override
+ public void run(@Nonnull final JMethodBodyCfg body) {
+ new CfgBasicBlockUtils(body.getCfg()).removeUnreachableBlocks();
+ }
+}
diff --git a/jack/src/com/android/jack/optimizations/cfg/SimplifyConditionalExpressions.java b/jack/src/com/android/jack/optimizations/cfg/SimplifyConditionalExpressions.java
new file mode 100644
index 0000000..161ea84
--- /dev/null
+++ b/jack/src/com/android/jack/optimizations/cfg/SimplifyConditionalExpressions.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.cfg;
+
+import com.android.jack.ir.ast.JBinaryOperation;
+import com.android.jack.ir.ast.JBinaryOperator;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JIntegralConstant32;
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.JRelationalOperation;
+import com.android.jack.ir.ast.JValueLiteral;
+import com.android.jack.ir.ast.UnsupportedOperatorException;
+import com.android.jack.ir.ast.cfg.BasicBlockLiveProcessor;
+import com.android.jack.ir.ast.cfg.JConditionalBasicBlock;
+import com.android.jack.ir.ast.cfg.JConditionalBlockElement;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.sourceinfo.SourceInfo;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.jack.transformations.request.Replace;
+import com.android.jack.transformations.request.TransformationRequest;
+import com.android.sched.item.Description;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+import com.android.sched.util.log.Tracer;
+import com.android.sched.util.log.TracerFactory;
+import com.android.sched.util.log.stats.Counter;
+import com.android.sched.util.log.stats.CounterImpl;
+import com.android.sched.util.log.stats.StatisticId;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/** Simplifies conditional expressions */
+@Description("Simplifies conditional expressions")
+@Transform(modify = JControlFlowGraph.class)
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class SimplifyConditionalExpressions
+ implements RunnableSchedulable<JMethodBodyCfg> {
+
+ @Nonnull
+ public static final StatisticId<Counter> CONDITIONS_SIMPLIFIED = new StatisticId<>(
+ "jack.cfg.conditions-simplified", "Simplified condition expressions",
+ CounterImpl.class, Counter.class);
+
+ @Nonnull
+ private final Tracer tracer = TracerFactory.getTracer();
+
+ @Nonnull
+ private JExpression createZeroLiteral(@Nonnull JValueLiteral original) {
+ return original.getType().createDefaultValue(original.getSourceInfo());
+ }
+
+ @CheckForNull
+ private JBinaryOperation optimizeOperation(
+ @Nonnull SourceInfo si, @Nonnull JBinaryOperator op,
+ @Nonnull JExpression lhs, @Nonnull JIntegralConstant32 rhs) {
+
+ int value = rhs.getIntValue();
+ if (value == 1) {
+ if (op == JBinaryOperator.GTE) {
+ // expr >= 1 ---> expr > 0
+ return JBinaryOperation.create(si,
+ JBinaryOperator.GT, lhs, createZeroLiteral((JValueLiteral) rhs));
+ }
+ if (op == JBinaryOperator.LT) {
+ // expr < 1 ---> expr <= 0
+ return JBinaryOperation.create(si,
+ JBinaryOperator.LTE, lhs, createZeroLiteral((JValueLiteral) rhs));
+ }
+
+ } else if (value == -1) {
+ if (op == JBinaryOperator.LTE) {
+ // expr <= -1 ---> expr < 0
+ return JBinaryOperation.create(si,
+ JBinaryOperator.LT, lhs, createZeroLiteral((JValueLiteral) rhs));
+ }
+ if (op == JBinaryOperator.GT) {
+ // expr > -1 ---> expr >= 0
+ return JBinaryOperation.create(si,
+ JBinaryOperator.GTE, lhs, createZeroLiteral((JValueLiteral) rhs));
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void run(@Nonnull final JMethodBodyCfg body) {
+ final TransformationRequest request = new TransformationRequest(body);
+
+ new BasicBlockLiveProcessor(body.getCfg(), /* stepIntoElements = */ false) {
+ @Override
+ public boolean visit(@Nonnull JConditionalBasicBlock block) {
+ JConditionalBlockElement element =
+ (JConditionalBlockElement) block.getLastElement();
+ JExpression condition = element.getCondition();
+
+ if (condition instanceof JRelationalOperation) {
+ JBinaryOperation relation = (JRelationalOperation) condition;
+ JBinaryOperator op = relation.getOp();
+ assert op == JBinaryOperator.LT || op == JBinaryOperator.LTE ||
+ op == JBinaryOperator.GT || op == JBinaryOperator.GTE;
+
+ JExpression lhs = relation.getLhs();
+ JExpression rhs = relation.getRhs();
+
+ JBinaryOperation newRelation = null;
+
+ if (rhs instanceof JIntegralConstant32) {
+ newRelation = optimizeOperation(
+ relation.getSourceInfo(), op, lhs, (JIntegralConstant32) rhs);
+ } else if (lhs instanceof JIntegralConstant32) {
+ try {
+ newRelation = optimizeOperation(relation.getSourceInfo(),
+ op.getReverseOperator(), rhs, (JIntegralConstant32) lhs);
+ } catch (UnsupportedOperatorException e) {
+ throw new AssertionError();
+ }
+ }
+
+ if (newRelation != null) {
+ tracer.getStatistic(CONDITIONS_SIMPLIFIED).incValue();
+ request.append(new Replace(relation, newRelation));
+ }
+ }
+ return false;
+ }
+ }.process();
+ request.commit();
+ }
+
+}
diff --git a/jack/src/com/android/jack/optimizations/cfg/ToggleCfgSsaFlagProcessor.java b/jack/src/com/android/jack/optimizations/cfg/ToggleCfgSsaFlagProcessor.java
new file mode 100644
index 0000000..5327c9a
--- /dev/null
+++ b/jack/src/com/android/jack/optimizations/cfg/ToggleCfgSsaFlagProcessor.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.cfg;
+
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.sched.item.Description;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+
+import javax.annotation.Nonnull;
+
+/** Toggle SSA flag on CFG */
+@Description("Toggle SSA flag on CFG")
+@Transform(modify = JControlFlowGraph.class)
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class ToggleCfgSsaFlagProcessor
+ implements RunnableSchedulable<JMethodBodyCfg> {
+
+ @Override
+ public void run(@Nonnull final JMethodBodyCfg body) {
+ body.getCfg().setInSsaForm(!body.getCfg().isInSsaForm());
+ }
+}
diff --git a/jack/src/com/android/jack/optimizations/cfg/VariablesScope.java b/jack/src/com/android/jack/optimizations/cfg/VariablesScope.java
new file mode 100644
index 0000000..c87ea0b
--- /dev/null
+++ b/jack/src/com/android/jack/optimizations/cfg/VariablesScope.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.cfg;
+
+import com.android.sched.util.codec.EnumName;
+import com.android.sched.util.codec.VariableName;
+
+/** Defines the variable scope (i.e. a set of the variables) relevant to a context */
+@VariableName("scope")
+public enum VariablesScope {
+ @EnumName(name = "none", description = "does not apply to any variables")
+ NONE,
+ @EnumName(name = "synthetic", description = "only applies to synthetic variables")
+ SYNTHETIC,
+ @EnumName(name = "all", description = "applies to all variables")
+ ALL
+}
diff --git a/jack/src/com/android/jack/optimizations/common/ExpressionReplaceHelper.java b/jack/src/com/android/jack/optimizations/common/ExpressionReplaceHelper.java
deleted file mode 100644
index 2f6181d..0000000
--- a/jack/src/com/android/jack/optimizations/common/ExpressionReplaceHelper.java
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.jack.optimizations.common;
-
-import com.android.jack.ir.ast.JAsgOperation;
-import com.android.jack.ir.ast.JExpression;
-import com.android.jack.ir.ast.JExpressionStatement;
-import com.android.jack.ir.ast.JLocal;
-import com.android.jack.ir.ast.JLocalRef;
-import com.android.jack.ir.ast.JStatement;
-import com.android.jack.ir.ast.JValueLiteral;
-import com.android.jack.ir.sourceinfo.SourceInfo;
-import com.android.jack.transformations.LocalVarCreator;
-import com.android.jack.transformations.request.AppendBefore;
-import com.android.jack.transformations.request.Replace;
-import com.android.jack.transformations.request.TransformationRequest;
-import com.android.sched.schedulable.Transform;
-import com.android.sched.schedulable.Use;
-
-import java.util.ArrayList;
-import javax.annotation.Nonnull;
-
-/** Helps replace expression with another expression. */
-@Transform(add = {
- JAsgOperation.NonReusedAsg.class,
- JExpressionStatement.class,
- JLocalRef.class })
-@Use(LocalVarCreator.class)
-public class ExpressionReplaceHelper {
- @Nonnull
- private final LocalVarCreator varCreator;
-
- public ExpressionReplaceHelper(@Nonnull LocalVarCreator varCreator) {
- this.varCreator = varCreator;
- }
-
- /** Replaces the expression with literal value, adds a temporary if needed. */
- public void replace(
- @Nonnull JExpression expr, @Nonnull JValueLiteral value,
- @Nonnull TransformationRequest request) {
-
- if (expr.canThrow() || !value.canThrow()) {
- // Simple case, don't need a local
- request.append(new Replace(expr, value));
- return;
- }
-
- // When the expression being replaced was not throwing, but the
- // expression being inserted is throwing we introduce a temporary
- // to ensure we don't have two throwing expressions in the same
- // basic block.
-
- SourceInfo si = value.getSourceInfo();
-
- // Step outside the expression
- JStatement stmt = expr.getParent(JStatement.class);
-
- // Create a local with the value
- JLocal tmp = varCreator.createTempLocal(value.getType(), si, request);
- JAsgOperation assign = new JAsgOperation(si, tmp.makeRef(si), value);
- JExpressionStatement stmtAssignment = new JExpressionStatement(si, assign);
- stmtAssignment.setCatchBlocks(new ArrayList<>(stmt.getJCatchBlocks()));
-
- request.append(new Replace(expr, tmp.makeRef(si)));
- request.append(new AppendBefore(stmt, stmtAssignment));
- }
-}
diff --git a/jack/src/com/android/jack/optimizations/common/JlsNullabilityChecker.java b/jack/src/com/android/jack/optimizations/common/JlsNullabilityChecker.java
deleted file mode 100644
index 00c357e..0000000
--- a/jack/src/com/android/jack/optimizations/common/JlsNullabilityChecker.java
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.jack.optimizations.common;
-
-import com.android.jack.ir.ast.JBlock;
-import com.android.jack.ir.ast.JClass;
-import com.android.jack.ir.ast.JEqOperation;
-import com.android.jack.ir.ast.JExpression;
-import com.android.jack.ir.ast.JExpressionStatement;
-import com.android.jack.ir.ast.JIfStatement;
-import com.android.jack.ir.ast.JNewInstance;
-import com.android.jack.ir.ast.JNullLiteral;
-import com.android.jack.ir.ast.JPrimitiveType.JPrimitiveTypeEnum;
-import com.android.jack.ir.ast.JStatement;
-import com.android.jack.ir.ast.JThisRef;
-import com.android.jack.ir.ast.JThrowStatement;
-import com.android.jack.ir.ast.JType;
-import com.android.jack.ir.ast.MethodKind;
-import com.android.jack.ir.sourceinfo.SourceInfo;
-import com.android.jack.lookup.CommonTypes;
-import com.android.jack.lookup.JPhantomLookup;
-import com.android.jack.transformations.LocalVarCreator;
-import com.android.jack.transformations.ast.splitnew.SplitNewInstance;
-import com.android.jack.transformations.request.TransformationRequest;
-import com.android.jack.util.NamingTools;
-import com.android.sched.schedulable.Transform;
-import com.android.sched.schedulable.Use;
-
-import java.util.Collections;
-
-import javax.annotation.CheckForNull;
-import javax.annotation.Nonnull;
-
-/**
- * Helps create nullability checks for preserving JLS-compliance
- * needed in some optimizations.
- */
-@Transform(add = { JBlock.class,
- JEqOperation.class,
- JExpressionStatement.class,
- JIfStatement.class,
- JNullLiteral.class,
- JThrowStatement.class },
- remove = JNewInstance.class)
-@Use({ LocalVarCreator.class,
- SplitNewInstance.NewExpressionSplitter.class })
-public final class JlsNullabilityChecker {
- @Nonnull
- private final LocalVarCreator varCreator;
- @Nonnull
- private final JPhantomLookup getPhantomLookup;
-
- public JlsNullabilityChecker(
- @Nonnull LocalVarCreator varCreator,
- @Nonnull JPhantomLookup getPhantomLookup) {
- this.varCreator = varCreator;
- this.getPhantomLookup = getPhantomLookup;
- }
-
- /** If the expression can be null, creates null-checking 'if' statement */
- @CheckForNull
- public JStatement createNullCheckIfNeeded(
- @Nonnull JExpression expr, @Nonnull TransformationRequest request) {
- return expr instanceof JThisRef ? null : createNullCheck(expr, request);
- }
-
- /**
- * Creates an 'if' statement of the following form and
- * inserts it before the statement including the expression:
- * <pre>
- * if ([expr] == null) {
- * throw new NullPointerException();
- * }
- * </pre>
- */
- @Nonnull
- public JStatement createNullCheck(
- @Nonnull JExpression expr, @Nonnull TransformationRequest request) {
- SourceInfo srcInfo = expr.getSourceInfo();
- return new JIfStatement(
- srcInfo,
- new JEqOperation(srcInfo, expr, new JNullLiteral(srcInfo)),
- createThenBlock(srcInfo, request),
- null);
- }
-
- @Nonnull
- private JBlock createThenBlock(
- @Nonnull SourceInfo srcInfo,
- @Nonnull TransformationRequest request) {
-
- JClass exceptionType =
- getPhantomLookup
- .getClass(CommonTypes.JAVA_LANG_NULL_POINTER_EXCEPTION);
- JNewInstance newInstance = new JNewInstance(srcInfo, exceptionType,
- exceptionType.getOrCreateMethodId(
- NamingTools.INIT_NAME,
- Collections.<JType>emptyList(),
- MethodKind.INSTANCE_NON_VIRTUAL,
- JPrimitiveTypeEnum.VOID.getType()));
- JExpression[] expressions = SplitNewInstance.NewExpressionSplitter
- .splitNewInstance(newInstance, request, varCreator);
-
- // NOTE: the code below relies on exact structure of the returned
- // array, it should consist of three expressions, the last of
- // which is the resulting exception instance we are throwing
- assert expressions.length == 3;
- JBlock block = new JBlock(srcInfo);
- block.addStmt(new JExpressionStatement(srcInfo, expressions[0]));
- block.addStmt(new JExpressionStatement(srcInfo, expressions[1]));
- block.addStmt(new JThrowStatement(srcInfo, expressions[2]));
- return block;
- }
-}
diff --git a/jack/src/com/android/jack/optimizations/ssa/CopyPropagation.java b/jack/src/com/android/jack/optimizations/ssa/CopyPropagation.java
new file mode 100644
index 0000000..6b5bc75
--- /dev/null
+++ b/jack/src/com/android/jack/optimizations/ssa/CopyPropagation.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.optimizations.ssa;
+
+import com.google.common.collect.Lists;
+
+import com.android.jack.Options;
+import com.android.jack.backend.dex.rop.CodeItemBuilder;
+import com.android.jack.ir.ast.JExpression;
+import com.android.jack.ir.ast.JMethod;
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.JSsaVariableDefRef;
+import com.android.jack.ir.ast.JSsaVariableRef;
+import com.android.jack.ir.ast.JSsaVariableUseRef;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JPhiBlockElement;
+import com.android.jack.ir.ast.cfg.JRegularBasicBlock;
+import com.android.jack.ir.ast.cfg.JVariableAsgBlockElement;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.jack.transformations.request.Replace;
+import com.android.jack.transformations.request.TransformationRequest;
+import com.android.sched.item.Description;
+import com.android.sched.item.Name;
+import com.android.sched.schedulable.Constraint;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+import com.android.sched.util.config.ThreadConfig;
+
+import javax.annotation.Nonnull;
+
+/**
+ * This is a pass that performs copy propagation based on SSA values. The final output should
+ * reminds SSA valid.
+ *
+ * Based somewhat around DX's SSA rename algorithm, which performs copy propagation during
+ * construction, this pass only a sparse analysis on the Phi nodes and SSA variable references.
+ */
+@Description("Copy Propagation of Locals")
+@Name("CopyPropagation")
+@Constraint(need = {JPhiBlockElement.class, JSsaVariableRef.class})
+@Transform(modify = {JPhiBlockElement.class, JSsaVariableRef.class})
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class CopyPropagation implements RunnableSchedulable<JMethodBodyCfg> {
+
+ private final boolean emitSyntheticLocalDebugInfo =
+ ThreadConfig.get(CodeItemBuilder.EMIT_SYNTHETIC_LOCAL_DEBUG_INFO).booleanValue();
+ private final boolean emitLocalDebugInfo =
+ ThreadConfig.get(Options.EMIT_LOCAL_DEBUG_INFO).booleanValue();
+
+ @Nonnull
+ private final com.android.jack.util.filter.Filter<JMethod> filter =
+ ThreadConfig.get(Options.METHOD_FILTER);
+
+ @Override
+ public void run(JMethodBodyCfg body) {
+ JMethod method = body.getMethod();
+ if (method.isNative() || method.isAbstract() || !filter.accept(this.getClass(), method)) {
+ return;
+ }
+
+ boolean changed;
+ do {
+ changed = false;
+ for (JBasicBlock bb : body.getCfg().getAllBlocksUnordered()) {
+ for (JBasicBlockElement e : Lists.newArrayList(bb.getElements(true))) {
+ if (e instanceof JVariableAsgBlockElement) {
+ changed = tryPropagateAssignment((JVariableAsgBlockElement) e, body.getCfg());
+ } else if (e instanceof JPhiBlockElement) {
+ JPhiBlockElement phi = (JPhiBlockElement) e;
+ changed = tryRemoveUselessPhi(phi);
+ if (!changed) {
+ changed = tryPropagatePhi(phi, body.getCfg());
+ }
+ }
+ }
+ }
+ } while (changed);
+ }
+
+ private boolean shouldKeepVariable(JSsaVariableRef varRef) {
+ if (varRef.getTarget().isSynthetic()) {
+ return emitSyntheticLocalDebugInfo;
+ } else {
+ return emitLocalDebugInfo;
+ }
+ }
+
+ /**
+ * If we have an assignment a = b, we try to replace all access of 'a' to 'b' when possible.
+ *
+ * @return true if an optimizations was performed.
+ */
+ private boolean tryPropagateAssignment(JVariableAsgBlockElement assign, JControlFlowGraph cfg) {
+ JExpression lhs = assign.getAssignment().getLhs();
+ JExpression rhs = assign.getAssignment().getRhs();
+ if (!(rhs instanceof JSsaVariableRef)) {
+ return false;
+ }
+
+ JSsaVariableUseRef rhsVarRef = (JSsaVariableUseRef) rhs;
+ JSsaVariableDefRef lhsVarRef = (JSsaVariableDefRef) lhs;
+
+ // Check for debug build. Make sure we are keeping locals if that's the case.
+ if (shouldKeepVariable(lhsVarRef)) {
+ return false;
+ }
+
+ // We don't propagate unreachable values.
+ if (rhsVarRef.getVersion() == 0) {
+ return false;
+ }
+
+ TransformationRequest tr = new TransformationRequest(cfg);
+ propagateVarRef(lhsVarRef, rhsVarRef, tr);
+ ((JRegularBasicBlock) assign.getBasicBlock()).removeElement(assign);
+ tr.commit();
+ return true;
+ }
+
+ private boolean tryPropagatePhi(JPhiBlockElement phi, JControlFlowGraph cfg) {
+ JSsaVariableUseRef rhsVarRef = canPropagatePhi(phi);
+ JSsaVariableDefRef lhsVarRef = phi.getLhs();
+
+ // Check for debug build. Make sure we are keeping locals if that's the case.
+ if (shouldKeepVariable(lhsVarRef)) {
+ return false;
+ }
+
+ // Not every path is the same variable.
+ if (rhsVarRef == null) {
+ return false;
+ }
+
+ TransformationRequest tr = new TransformationRequest(cfg);
+ propagateVarRef(lhsVarRef, rhsVarRef, tr);
+ tr.commit();
+ ((JRegularBasicBlock) phi.getBasicBlock()).removeElement(phi);
+ return true;
+ }
+
+ private boolean tryRemoveUselessPhi(JPhiBlockElement phi) {
+ if (!phi.getLhs().hasUses()) {
+ ((JRegularBasicBlock) phi.getBasicBlock()).removeElement(phi);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Replace all the references to the lhs JSsaVariableRef with a new rhs JSsaVariableRef.
+ */
+ private void propagateVarRef(JSsaVariableDefRef lhs, JSsaVariableUseRef rhs,
+ TransformationRequest tr) {
+ JSsaVariableDefRef def = rhs.getDef();
+ for (JSsaVariableRef oldUse : Lists.newArrayList(lhs.getUses())) {
+ JSsaVariableRef newUse = def.makeRef(oldUse.getSourceInfo());
+ newUse.addAllMarkers(oldUse.getAllMarkers());
+ tr.append(new Replace(oldUse, newUse));
+ }
+ lhs.removeUses();
+ }
+
+ /**
+ * If we have a = phi(b,b,b,b), it is ok with replace a with b.
+ *
+ * @return The right hand side values that the left hand side should be replaced with. Otherwise
+ * null.
+ */
+ public JSsaVariableUseRef canPropagatePhi(JPhiBlockElement e) {
+ JSsaVariableUseRef first = e.getRhs(e.getBasicBlock().getPredecessors().get(0));
+ assert first != null;
+ for (JSsaVariableUseRef operand : e.getRhs()) {
+ if (first.getDef() != operand.getDef()) {
+ return null;
+ }
+ }
+ return first;
+ }
+}
diff --git a/jack/src/com/android/jack/optimizations/valuepropagation/argument/AvpPropagateArgumentValues.java b/jack/src/com/android/jack/optimizations/valuepropagation/argument/AvpPropagateArgumentValues.java
index e5c0f5d..d6e0279 100644
--- a/jack/src/com/android/jack/optimizations/valuepropagation/argument/AvpPropagateArgumentValues.java
+++ b/jack/src/com/android/jack/optimizations/valuepropagation/argument/AvpPropagateArgumentValues.java
@@ -19,17 +19,25 @@
import com.android.jack.Jack;
import com.android.jack.annotations.DisableArgumentValuePropagationOptimization;
import com.android.jack.ir.ast.JAnnotationType;
-import com.android.jack.ir.ast.JAsgOperation;
+import com.android.jack.ir.ast.JLocalRef;
import com.android.jack.ir.ast.JMethod;
-import com.android.jack.ir.ast.JNode;
+import com.android.jack.ir.ast.JMethodBodyCfg;
import com.android.jack.ir.ast.JParameter;
import com.android.jack.ir.ast.JParameterRef;
import com.android.jack.ir.ast.JValueLiteral;
+import com.android.jack.ir.ast.JVariable;
import com.android.jack.ir.ast.JVisitor;
-import com.android.jack.optimizations.common.ExpressionReplaceHelper;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JSimpleBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowingExpressionBasicBlock;
+import com.android.jack.ir.ast.cfg.JVariableAsgBlockElement;
+import com.android.jack.ir.ast.cfg.mutations.BasicBlockBuilder;
+import com.android.jack.optimizations.cfg.CfgVarUtils;
import com.android.jack.optimizations.common.LiteralValueListTracker;
import com.android.jack.optimizations.common.OptimizerUtils;
import com.android.jack.transformations.LocalVarCreator;
+import com.android.jack.transformations.request.Replace;
import com.android.jack.transformations.request.TransformationRequest;
import com.android.jack.util.NamingTools;
import com.android.sched.item.Description;
@@ -52,10 +60,10 @@
AvpSchedulable.TaintedMethodMarker.class })
@Transform(remove = { MethodCallArgumentsMarker.class,
AvpSchedulable.TaintedMethodMarker.class })
-@Use(ExpressionReplaceHelper.class)
+@Use(CfgVarUtils.ReplaceWithLocal.class)
@Name("ArgumentValuePropagation: PropagateArgumentValues")
public class AvpPropagateArgumentValues extends AvpSchedulable
- implements RunnableSchedulable<JMethod> {
+ implements RunnableSchedulable<JMethodBodyCfg> {
@Nonnull
public final JAnnotationType disablingAnnotationType =
@@ -67,7 +75,9 @@
private final Tracer tracer = TracerFactory.getTracer();
@Override
- public void run(@Nonnull final JMethod method) {
+ public void run(@Nonnull final JMethodBodyCfg body) {
+ final JMethod method = body.getMethod();
+
LiteralValueListTracker tracker =
MethodCallArgumentsMarker.getTrackerAndRemoveMarker(method);
boolean isTainted = TaintedMethodMarker.checkIfTaintedAndRemoveMarker(method);
@@ -93,17 +103,16 @@
// Detect parameters not being assigned to
JVisitor asgAnalyzer = new JVisitor() {
- @Override public void endVisit(@Nonnull JParameterRef x) {
- JNode parent = x.getParent();
- if (parent instanceof JAsgOperation &&
- ((JAsgOperation) parent).getLhs() == x) {
- if (paramValues.remove(x.getParameter()) != null) {
+ @Override public void endVisit(@Nonnull JVariableAsgBlockElement x) {
+ JVariable variable = x.getVariable();
+ if (variable instanceof JParameter) {
+ if (paramValues.remove(variable) != null) {
tracer.getStatistic(PARAMETER_IS_WRITTEN_TO).incValue();
}
}
}
};
- asgAnalyzer.accept(method);
+ asgAnalyzer.accept(body.getCfg());
if (paramValues.size() == 0) {
// All eligible parameters are mutated in the method
@@ -113,19 +122,47 @@
// Substitute parameter with value
class Processor extends JVisitor {
@Nonnull
- private TransformationRequest request =
- new TransformationRequest(method);
+ private final TransformationRequest request = new TransformationRequest(method);
@Nonnull
- private ExpressionReplaceHelper helper =
- new ExpressionReplaceHelper(new LocalVarCreator(method, "avp"));
+ private final CfgVarUtils helper = new CfgVarUtils();
+ @Nonnull
+ private final LocalVarCreator varCreator = new LocalVarCreator(method, "avp");
- @Override public void endVisit(@Nonnull JParameterRef x) {
+ @Override
+ public void endVisit(@Nonnull JParameterRef x) {
+ assert !x.canThrow();
JParameter parameter = x.getParameter();
JValueLiteral literal = paramValues.get(parameter);
+
if (literal != null && parameter.getAnnotations(disablingAnnotationType).isEmpty()) {
literal = OptimizerUtils.cloneExpression(literal);
literal.setSourceInfo(x.getSourceInfo());
- helper.replace(x, literal, request);
+
+ if (literal.canThrow()) {
+ // If the literal can throw, we are replacing non-throwing expression
+ // with throwing one. Thus we need to split the basic block and make it
+ // throwing
+
+ // First we replace the param reference with a temp local
+ JLocalRef tmpRef = helper.replaceWithLocal(varCreator, x);
+
+ // The local initialization is inserted before the element where
+ // the local is being used. We split the basic block such that
+ // The beginning of the block up to the initialization becomes a
+ // new simple basic block
+ JBasicBlockElement element = tmpRef.getParent(JBasicBlockElement.class);
+ JBasicBlock basicBlock = element.getBasicBlock();
+ JSimpleBasicBlock preBlock = basicBlock.split(basicBlock.indexOf(element));
+
+ // Create a new throwing expression basic block and new JThrowingEx
+ JThrowingExpressionBasicBlock newBlock =
+ new BasicBlockBuilder(body.getCfg()).append(preBlock).removeLast()
+ .createThrowingExprBlock(preBlock.getPrimarySuccessor());
+ preBlock.detach(newBlock);
+ }
+
+ // Finally, replace the parameter reference with the literal
+ request.append(new Replace(x, literal));
tracer.getStatistic(ARGUMENT_VALUES_PROPAGATED).incValue();
}
}
diff --git a/jack/src/com/android/jack/optimizations/valuepropagation/field/FvpPropagateFieldValues.java b/jack/src/com/android/jack/optimizations/valuepropagation/field/FvpPropagateFieldValues.java
index 2f51787..8e01b10 100644
--- a/jack/src/com/android/jack/optimizations/valuepropagation/field/FvpPropagateFieldValues.java
+++ b/jack/src/com/android/jack/optimizations/valuepropagation/field/FvpPropagateFieldValues.java
@@ -24,17 +24,24 @@
import com.android.jack.ir.ast.JField;
import com.android.jack.ir.ast.JFieldRef;
import com.android.jack.ir.ast.JMethod;
-import com.android.jack.ir.ast.JStatement;
+import com.android.jack.ir.ast.JMethodBodyCfg;
import com.android.jack.ir.ast.JThisRef;
import com.android.jack.ir.ast.JValueLiteral;
-import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.ast.cfg.BasicBlockLiveProcessor;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JGotoBlockElement;
+import com.android.jack.ir.ast.cfg.JSimpleBasicBlock;
+import com.android.jack.ir.ast.cfg.JThrowingExpressionBasicBlock;
+import com.android.jack.ir.ast.cfg.JVariableAsgBlockElement;
+import com.android.jack.ir.ast.cfg.mutations.BasicBlockBuilder;
+import com.android.jack.ir.ast.cfg.mutations.CfgFragment;
+import com.android.jack.ir.sourceinfo.SourceInfo;
import com.android.jack.lookup.JPhantomLookup;
import com.android.jack.optimizations.Optimizations;
-import com.android.jack.optimizations.common.ExpressionReplaceHelper;
-import com.android.jack.optimizations.common.JlsNullabilityChecker;
+import com.android.jack.optimizations.cfg.CfgJlsNullabilityChecker;
import com.android.jack.optimizations.common.OptimizerUtils;
import com.android.jack.transformations.LocalVarCreator;
-import com.android.jack.transformations.request.AppendBefore;
+import com.android.jack.transformations.request.Replace;
import com.android.jack.transformations.request.TransformationRequest;
import com.android.jack.transformations.threeaddresscode.ThreeAddressCodeForm;
import com.android.jack.util.NamingTools;
@@ -48,7 +55,6 @@
import com.android.sched.util.log.Tracer;
import com.android.sched.util.log.TracerFactory;
-import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/**
@@ -59,11 +65,10 @@
@Constraint(need = { FieldSingleValueMarker.class,
ThreeAddressCodeForm.class })
@Transform(add = JValueLiteral.class)
-@Use({ ExpressionReplaceHelper.class,
- JlsNullabilityChecker.class })
+@Use(CfgJlsNullabilityChecker.class)
@Name("FieldValuePropagation: PropagateFieldValues")
public class FvpPropagateFieldValues extends FvpSchedulable
- implements RunnableSchedulable<JMethod> {
+ implements RunnableSchedulable<JMethodBodyCfg> {
@Nonnull
public final JAnnotationType disablingAnnotationType =
@@ -78,118 +83,122 @@
private final boolean preserveNullChecks = ThreadConfig.get(
Optimizations.FieldValuePropagation.PRESERVE_NULL_CHECKS).booleanValue();
-
private final boolean ensureTypeInitializers = ThreadConfig.get(
Optimizations.FieldValuePropagation.ENSURE_TYPE_INITIALIZERS).booleanValue();
- private class Visitor extends JVisitor {
- @Nonnull
- private final JMethod method;
- private final boolean insideConstructor;
+ @Override
+ public void run(@Nonnull final JMethodBodyCfg body) {
+ final JMethod method = body.getMethod();
+ final boolean insideConstructor = OptimizerUtils.isConstructor(method);
+ final TransformationRequest request = new TransformationRequest(method);
+ final CfgJlsNullabilityChecker nullChecker = preserveNullChecks ?
+ new CfgJlsNullabilityChecker(body.getCfg(),
+ new LocalVarCreator(method, "fvp"), phantomLookup) : null;
- @CheckForNull
- private final JlsNullabilityChecker jlsNullabilityHelper;
- @Nonnull
- private final ExpressionReplaceHelper replaceHelper;
- @Nonnull
- public final TransformationRequest request;
-
- Visitor(@Nonnull JMethod method, boolean addNullChecks) {
- this.method = method;
- this.insideConstructor = OptimizerUtils.isConstructor(method);
- this.request = new TransformationRequest(method);
-
- LocalVarCreator fvp = new LocalVarCreator(method, "fvp");
- this.replaceHelper = new ExpressionReplaceHelper(fvp);
- this.jlsNullabilityHelper = addNullChecks ?
- new JlsNullabilityChecker(fvp, phantomLookup) : null;
- }
-
- @Override
- public void endVisit(@Nonnull JFieldRef ref) {
- replaceFieldWithValue(ref);
- super.endVisit(ref);
- }
-
- private void replaceFieldWithValue(@Nonnull JFieldRef ref) {
- if (OptimizerUtils.isAssigned(ref)) {
- return;
+ new BasicBlockLiveProcessor(body.getCfg(), /* stepIntoElements: */ false) {
+ @Override
+ public boolean visit(@Nonnull JThrowingExpressionBasicBlock block) {
+ JBasicBlockElement element = block.getLastElement();
+ if (element instanceof JVariableAsgBlockElement &&
+ ((JVariableAsgBlockElement) element).isFieldLoad()) {
+ handle((JVariableAsgBlockElement) element);
+ }
+ return false;
}
- JField field = ref.getFieldId().getField();
- if (field == null ||
- !field.getAnnotations(disablingAnnotationType).isEmpty() ||
- !field.getEnclosingType().getAnnotations(disablingAnnotationType).isEmpty()) {
- return;
- }
-
- // Only process fields of types to be emitted
- JDefinedClassOrInterface type = field.getEnclosingType();
- if (!type.isToEmit()) {
- return;
- }
-
- // Only process tracked fields. Note: there may be valid reasons why a tracked field
- // might not have a marker yet, for example not initialized static fields if there
- // is no any assignment to this field and type static initializer is also missing.
- FieldSingleValueMarker marker = FieldSingleValueMarker.getOrCreate(field);
- if (marker == null || marker.isMultipleOrNonLiteralValue()) {
- return;
- }
-
- // Do not substitute field reads inside correspondent type
- // initializers where this field is being initialized
- if (insideConstructor &&
- type == method.getEnclosingType() &&
- method.isStatic() == field.isStatic()) {
- if (method.isStatic() || ref.getInstance() instanceof JThisRef) {
- // Field either is static or is instance and implicitly
- // or explicitly referenced via 'this' reference
+ private void handle(@Nonnull JVariableAsgBlockElement element) {
+ JFieldRef ref = (JFieldRef) element.getValue();
+ if (OptimizerUtils.isAssigned(ref)) {
return;
}
- }
- // In case this is a static field and the field is accessed not from the same
- // type, we don't propagate value if 'ensure-type-initializers' is true.
- if (field.isStatic() && ensureTypeInitializers &&
- field.getEnclosingType() != method.getEnclosingType()) {
- return;
- }
+ JField field = ref.getFieldId().getField();
+ if (field == null ||
+ !field.getAnnotations(disablingAnnotationType).isEmpty() ||
+ !field.getEnclosingType().getAnnotations(disablingAnnotationType).isEmpty()) {
+ return;
+ }
- JValueLiteral value = marker.getConsolidatedValue();
- if (value == null) {
- value = createDefaultValue(field); // Assume default value.
- } else {
- value = OptimizerUtils.cloneExpression(value);
- value.setSourceInfo(ref.getSourceInfo());
- }
+ // Only process fields of types to be emitted
+ JDefinedClassOrInterface type = field.getEnclosingType();
+ if (!type.isToEmit()) {
+ return;
+ }
- replaceHelper.replace(ref, value, request);
- tracer.getStatistic(FIELD_VALUES_PROPAGATED).incValue();
+ // Only process tracked fields. Note: there may be valid reasons why a tracked field
+ // might not have a marker yet, for example not initialized static fields if there
+ // is no any assignment to this field and type static initializer is also missing.
+ FieldSingleValueMarker marker = FieldSingleValueMarker.getOrCreate(field);
+ if (marker == null || marker.isMultipleOrNonLiteralValue()) {
+ return;
+ }
- if (jlsNullabilityHelper != null) {
- if (!field.isStatic()) {
- JExpression instance = ref.getInstance();
- assert instance != null;
- JStatement nullCheck = jlsNullabilityHelper
- .createNullCheckIfNeeded(instance, request);
- if (nullCheck != null) {
- JStatement stmt = instance.getParent(JStatement.class);
- request.append(new AppendBefore(stmt, nullCheck));
+ // Do not substitute field reads inside correspondent type
+ // initializers where this field is being initialized
+ if (insideConstructor &&
+ type == method.getEnclosingType() &&
+ method.isStatic() == field.isStatic()) {
+ if (method.isStatic() || ref.getInstance() instanceof JThisRef) {
+ // Field either is static or is instance and implicitly
+ // or explicitly referenced via 'this' reference
+ return;
}
}
+
+ // In case this is a static field and the field is accessed not from the same
+ // type, we don't propagate value if 'ensure-type-initializers' is true.
+ if (field.isStatic() && ensureTypeInitializers &&
+ field.getEnclosingType() != method.getEnclosingType()) {
+ return;
+ }
+
+ JValueLiteral value = marker.getConsolidatedValue();
+ if (value == null) {
+ value = createDefaultValue(field); // Assume default value.
+ } else {
+ value = OptimizerUtils.cloneExpression(value);
+ value.setSourceInfo(ref.getSourceInfo());
+ }
+
+ // #1: Split the block to move all but the last element
+ // into a separate simple block.
+ JThrowingExpressionBasicBlock secondBlock =
+ (JThrowingExpressionBasicBlock) element.getBasicBlock();
+ JSimpleBasicBlock firstBlock = secondBlock.split(-1);
+
+ // #2: Insert null-checks if needed.
+ if (nullChecker != null && !field.isStatic()) {
+ // The null-check is needed, we insert it in between the two blocks
+ JExpression instance = ref.getInstance();
+ assert instance != null;
+ assert !instance.canThrow();
+
+ CfgFragment nullCheckFragment =
+ nullChecker.createNullCheck(element.getEHContext(), instance, request);
+
+ nullCheckFragment.insert(firstBlock, secondBlock);
+ }
+
+ // #3: Schedule value replace request
+ request.append(new Replace(ref, value));
+ tracer.getStatistic(FIELD_VALUES_PROPAGATED).incValue();
+
+ // #4: Turn the second block into a simple block in case it does not throw
+ if (!value.canThrow()) {
+ JSimpleBasicBlock newSecondBlock =
+ new BasicBlockBuilder(body.getCfg())
+ .append(secondBlock)
+ .append(new JGotoBlockElement(
+ SourceInfo.UNKNOWN, secondBlock.getLastElement().getEHContext()))
+ .createSimpleBlock(secondBlock.getPrimarySuccessor());
+ secondBlock.detach(newSecondBlock);
+ }
+
+ // #5: Merge the first block into its primary successor
+ firstBlock.mergeIntoSuccessor();
}
- }
- }
+ }.process();
- @Override
- public void run(@Nonnull JMethod method) {
- if (method.isNative() || method.isAbstract()) {
- return;
- }
-
- Visitor visitor = new Visitor(method, preserveNullChecks);
- visitor.accept(method);
- visitor.request.commit();
+ request.commit();
}
}
diff --git a/jack/src/com/android/jack/optimizations/wofr/WofrRemoveFieldWrites.java b/jack/src/com/android/jack/optimizations/wofr/WofrRemoveFieldWrites.java
index 186ef94..3ab472a 100644
--- a/jack/src/com/android/jack/optimizations/wofr/WofrRemoveFieldWrites.java
+++ b/jack/src/com/android/jack/optimizations/wofr/WofrRemoveFieldWrites.java
@@ -17,27 +17,24 @@
package com.android.jack.optimizations.wofr;
import com.android.jack.Jack;
-import com.android.jack.cfg.BasicBlock;
-import com.android.jack.cfg.ControlFlowGraph;
-import com.android.jack.ir.ast.JAsgOperation;
import com.android.jack.ir.ast.JDefinedClassOrInterface;
import com.android.jack.ir.ast.JExpression;
-import com.android.jack.ir.ast.JExpressionStatement;
import com.android.jack.ir.ast.JField;
import com.android.jack.ir.ast.JFieldRef;
-import com.android.jack.ir.ast.JLocal;
import com.android.jack.ir.ast.JMethod;
-import com.android.jack.ir.ast.JMethodCall;
+import com.android.jack.ir.ast.JMethodBodyCfg;
import com.android.jack.ir.ast.JPrimitiveType;
-import com.android.jack.ir.ast.JStatement;
import com.android.jack.ir.ast.JThisRef;
+import com.android.jack.ir.ast.cfg.BasicBlockLiveProcessor;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JSimpleBasicBlock;
+import com.android.jack.ir.ast.cfg.JStoreBlockElement;
+import com.android.jack.ir.ast.cfg.JThrowingExpressionBasicBlock;
+import com.android.jack.ir.ast.cfg.mutations.CfgFragment;
import com.android.jack.lookup.JPhantomLookup;
import com.android.jack.optimizations.Optimizations;
-import com.android.jack.optimizations.common.JlsNullabilityChecker;
+import com.android.jack.optimizations.cfg.CfgJlsNullabilityChecker;
import com.android.jack.transformations.LocalVarCreator;
-import com.android.jack.transformations.request.AppendBefore;
-import com.android.jack.transformations.request.PrependAfter;
-import com.android.jack.transformations.request.Replace;
import com.android.jack.transformations.request.TransformationRequest;
import com.android.sched.item.Description;
import com.android.sched.item.Name;
@@ -49,21 +46,16 @@
import com.android.sched.util.log.Tracer;
import com.android.sched.util.log.TracerFactory;
-import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
/** Write-only field removal, second phase: remove field assignments */
@Description("Write-only field removal, field writes removal")
-@Constraint(need = { ControlFlowGraph.class,
- JFieldRef.class,
- FieldReadWriteCountsMarker.class })
-@Transform(modify = FieldReadWriteCountsMarker.class,
- add = JExpressionStatement.class)
+@Constraint(need = { FieldReadWriteCountsMarker.class, JFieldRef.class })
+@Transform(modify = { FieldReadWriteCountsMarker.class, JControlFlowGraph.class })
@Name("WriteOnlyFieldRemoval: RemoveFieldWrites")
-@Use({ JlsNullabilityChecker.class,
- LocalVarCreator.class })
+@Use({ CfgJlsNullabilityChecker.class, LocalVarCreator.class })
public class WofrRemoveFieldWrites extends WofrSchedulable
- implements RunnableSchedulable<JMethod> {
+ implements RunnableSchedulable<JMethodBodyCfg> {
private final boolean preserveObjectLifetime =
ThreadConfig.get(Optimizations.WriteOnlyFieldRemoval.PRESERVE_OBJECT_LIFETIME).booleanValue();
@@ -84,17 +76,14 @@
private enum Action {
/** Field is either not eligible for optimization or write cannot be removed */
None,
- /** Field write can be replaced with just expression */
- Expression,
- /** Field write can be replaced with receiver and expression */
- ReceiverAndExpression,
- /** Field write can be replaced with receiver, expression and null-check */
- ReceiverExpressionAndNullCheck
+ /** Field write can be removed */
+ Remove,
+ /** Field write can be removed, but a null-check need to be added */
+ RemoveAndAddNullCheck
}
/** Classify field assignment */
private Action classify(@Nonnull JMethod method, @Nonnull JFieldRef ref) {
-
JField field = ref.getFieldId().getField();
if (field == null ||
!field.getAnnotations(disablingAnnotationType).isEmpty() ||
@@ -107,7 +96,7 @@
}
if (FieldReadWriteCountsMarker.hasReads(field)) {
- return Action.None; // The field is read
+ return Action.None; // The field has reads
}
JDefinedClassOrInterface fieldOwningType = field.getEnclosingType();
@@ -135,120 +124,81 @@
if (field.isStatic() || ref.getInstance() instanceof JThisRef) {
// The field is static OR the field is an instance field accessed
// via this reference: no side-effects while calculating receiver
- return Action.Expression;
+ return Action.Remove;
}
assert !field.isStatic();
-
- return preserveNullChecks
- ? Action.ReceiverExpressionAndNullCheck
- : Action.ReceiverAndExpression;
+ return preserveNullChecks ? Action.RemoveAndAddNullCheck : Action.Remove;
}
@Override
- public void run(@Nonnull final JMethod method) {
- if (method.isAbstract() || method.isNative()) {
- return;
- }
+ public void run(@Nonnull final JMethodBodyCfg body) {
+ final TransformationRequest request = new TransformationRequest(body);
+ final CfgJlsNullabilityChecker nullChecker =
+ new CfgJlsNullabilityChecker(body.getCfg(),
+ new LocalVarCreator(body.getMethod(), "wofr"), phantomLookup);
- final TransformationRequest request = new TransformationRequest(method);
- final LocalVarCreator varCreator = new LocalVarCreator(method, "wofr");
- final JlsNullabilityChecker nullChecker = new JlsNullabilityChecker(varCreator, phantomLookup);
+ // Create a processor to walk the cfg and remove the writes, note that
+ // even though we insert the new basic blocks, we don't need to update
+ // the processor to know about these newly created blocks, since they
+ // don't have any field writes we might want to process.
- class Processor {
- private void handleExprStmt(@Nonnull JExpressionStatement stmt) {
- JExpression expr = stmt.getExpr();
- if (expr instanceof JAsgOperation) {
- JAsgOperation asg = (JAsgOperation) expr;
- JExpression lhs = asg.getLhs();
- if (lhs instanceof JFieldRef) {
- JFieldRef ref = (JFieldRef) lhs;
-
- switch (classify(method, ref)) {
- case None:
- // Cannot remove field assignment
- return;
-
- case ReceiverExpressionAndNullCheck:
- // Replace received with temp local
- JLocal local = handleFieldReceiver(asg, true);
- assert local != null;
- // Replace assignment expression with rhs
- handleAssignmentRhs(asg);
- // Append null-check
- JStatement nullCheck =
- nullChecker.createNullCheck(
- local.makeRef(local.getSourceInfo()), request);
- request.append(new PrependAfter(stmt, nullCheck));
- break;
-
- case ReceiverAndExpression:
- // Prepend statement with expression statement representing <receiver>
- handleFieldReceiver(asg, false);
- // Replace assignment expression with rhs
- handleAssignmentRhs(asg);
- break;
-
- case Expression:
- // Replace assignment expression with rhs
- handleAssignmentRhs(asg);
- break;
- }
-
- JField field = ref.getFieldId().getField();
- assert field != null;
- FieldReadWriteCountsMarker.unmarkWrite(field);
- tracer.getStatistic(FIELD_WRITES_REMOVED).incValue();
- }
+ new BasicBlockLiveProcessor(body.getCfg(), /* stepIntoElements = */ true) {
+ @Override
+ public boolean visit(@Nonnull JStoreBlockElement element) {
+ JFieldRef ref = element.getLhsAsFieldRef();
+ if (ref == null) {
+ return false;
}
- }
+ JExpression value = element.getValueExpression();
+ assert !value.canThrow();
- private void handleAssignmentRhs(@Nonnull JAsgOperation asg) {
- JExpression rhs = asg.getRhs();
- if (rhs instanceof JMethodCall) {
- request.append(new Replace(asg, rhs));
- } else {
- JExpression lhs = asg.getLhs();
- JLocal local =
- varCreator.createTempLocal(
- lhs.getType(), lhs.getSourceInfo(), request);
- request.append(new Replace(lhs, local.makeRef(local.getSourceInfo())));
- }
- }
-
- @CheckForNull
- private JLocal handleFieldReceiver(@Nonnull JAsgOperation asg, boolean forceLocal) {
- JExpression receiver = ((JFieldRef) asg.getLhs()).getInstance();
- assert receiver != null;
-
- JLocal local = null;
-
- if (!(receiver instanceof JMethodCall) || forceLocal) {
- // Need a temp local
- local = varCreator.createTempLocal(
- receiver.getType(), receiver.getSourceInfo(), request);
- receiver = new JAsgOperation(
- receiver.getSourceInfo(), local.makeRef(receiver.getSourceInfo()), receiver);
+ Action action = classify(body.getMethod(), ref);
+ if (action == Action.None) {
+ // Cannot remove field assignment
+ return false;
}
- JExpressionStatement stmt =
- new JExpressionStatement(asg.getSourceInfo(), receiver);
- request.append(new AppendBefore(asg.getParent(), stmt));
- return local;
- }
- }
+ // Field store operation must be the LAST element of
+ // the throwing expression basic block.
+ JThrowingExpressionBasicBlock block = element.getBasicBlock();
+ assert block.getLastElement() == element;
- Processor processor = new Processor();
- ControlFlowGraph cfg = method.getMarker(ControlFlowGraph.class);
- assert cfg != null;
+ // We first split the basic block into two parts:
+ //
+ // block { e0, e1, ..., eLast (== element) }
+ // | | |
+ // V V V
+ // simple { e0, e1, ..., goto } --> block { eLast }
+ //
+ JSimpleBasicBlock simple = block.split(-1);
- for (BasicBlock bb : cfg.getNodes()) {
- for (JStatement stmt : bb.getStatements()) {
- if (stmt instanceof JExpressionStatement) {
- processor.handleExprStmt((JExpressionStatement) stmt);
+ if (action == Action.RemoveAndAddNullCheck) {
+ // The null-check is needed, we insert it in between the two blocks
+ JExpression instance = ref.getInstance();
+ assert instance != null;
+ assert !instance.canThrow();
+
+ CfgFragment nullCheckFragment =
+ nullChecker.createNullCheck(element.getEHContext(), instance, request);
+
+ nullCheckFragment.insert(simple, block);
}
+
+ // Delete the block, since we remove the write
+ assert block.getElementCount() == 1;
+ assert block.getLastElement() == element;
+ block.delete();
+
+ // Unmark the write
+ JField field = ref.getFieldId().getField();
+ assert field != null;
+ FieldReadWriteCountsMarker.unmarkWrite(field);
+ tracer.getStatistic(FIELD_WRITES_REMOVED).incValue();
+
+ return false;
}
- }
+ }.process();
request.commit();
}
diff --git a/jack/src/com/android/jack/scheduling/adapter/JAllBasicBlockAdapter.java b/jack/src/com/android/jack/scheduling/adapter/JAllBasicBlockAdapter.java
new file mode 100644
index 0000000..e1b853b
--- /dev/null
+++ b/jack/src/com/android/jack/scheduling/adapter/JAllBasicBlockAdapter.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.scheduling.adapter;
+
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.sched.item.Description;
+import com.android.sched.schedulable.AdapterSchedulable;
+
+import java.util.Iterator;
+import javax.annotation.Nonnull;
+
+/**
+ * Adapts a process on {@code JControlFlowGraph} onto one or several processes
+ * on each {@code JBasicBlock} of this type control flow graph.
+ */
+@Description("Adapts process on JControlFlowGraph to one "
+ + "or several processes on each of its basic blocks")
+public class JAllBasicBlockAdapter
+ implements AdapterSchedulable<JControlFlowGraph, JBasicBlock> {
+
+ /** Returns every {@code JBasicBlock} of the given {@code JControlFlowGraph}. */
+ @Override
+ @Nonnull
+ public Iterator<JBasicBlock> adapt(@Nonnull JControlFlowGraph cfg) {
+ return cfg.getAllBlocksUnordered().iterator();
+ }
+}
diff --git a/jack/src/com/android/jack/scheduling/adapter/JMethodBodyCfgAdapter.java b/jack/src/com/android/jack/scheduling/adapter/JMethodBodyCfgAdapter.java
new file mode 100644
index 0000000..4c3d7d1
--- /dev/null
+++ b/jack/src/com/android/jack/scheduling/adapter/JMethodBodyCfgAdapter.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.scheduling.adapter;
+
+import com.android.jack.ir.ast.JAbstractMethodBody;
+import com.android.jack.ir.ast.JDefinedClassOrInterface;
+import com.android.jack.ir.ast.JMethod;
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.sched.item.Description;
+import com.android.sched.schedulable.AdapterSchedulable;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import javax.annotation.Nonnull;
+
+/**
+ * Adapts a process on {@code JDefinedClassOrInterface} onto one or several processes on
+ * each {@code JMethodBodyCfg} of all the methods declared by this type.
+ */
+@Description("Adapts process on JDefinedClassOrInterface to one or several processes "
+ + "on each cfg method body of all the methods declared by this type")
+public class JMethodBodyCfgAdapter
+ implements AdapterSchedulable<JDefinedClassOrInterface, JMethodBodyCfg> {
+
+ /**
+ * Returns every {@code JMethodBodyCfg} of the methods declared
+ * in the given {@code JDefinedClassOrInterface}.
+ */
+ @Override
+ @Nonnull
+ public Iterator<JMethodBodyCfg> adapt(@Nonnull JDefinedClassOrInterface declaredType) {
+ ArrayList<JMethodBodyCfg> cfgList = new ArrayList<>();
+ for (JMethod method : declaredType.getMethods()) {
+ JAbstractMethodBody body = method.getBody();
+ if (body instanceof JMethodBodyCfg) {
+ cfgList.add((JMethodBodyCfg) body);
+ }
+ }
+ return cfgList.iterator();
+ }
+}
diff --git a/jack/src/com/android/jack/transformations/LocalVarCreator.java b/jack/src/com/android/jack/transformations/LocalVarCreator.java
index 42f9859..3ec3822f 100644
--- a/jack/src/com/android/jack/transformations/LocalVarCreator.java
+++ b/jack/src/com/android/jack/transformations/LocalVarCreator.java
@@ -17,9 +17,9 @@
package com.android.jack.transformations;
import com.android.jack.ir.ast.JAbstractMethodBody;
+import com.android.jack.ir.ast.JConcreteMethodBody;
import com.android.jack.ir.ast.JLocal;
import com.android.jack.ir.ast.JMethod;
-import com.android.jack.ir.ast.JMethodBody;
import com.android.jack.ir.ast.JModifier;
import com.android.jack.ir.ast.JType;
import com.android.jack.ir.sourceinfo.SourceInfo;
@@ -38,7 +38,7 @@
public class LocalVarCreator {
@Nonnull
- private final JMethodBody currentMethodBody;
+ private final JConcreteMethodBody currentMethodBody;
@Nonnull
private final String tmpLocalVarPrefix;
@Nonnegative
@@ -52,8 +52,8 @@
public LocalVarCreator(@Nonnull JMethod method, @Nonnull String prefix) {
JAbstractMethodBody body = method.getBody();
assert body != null;
- assert body instanceof JMethodBody;
- currentMethodBody = (JMethodBody) body;
+ assert body instanceof JConcreteMethodBody;
+ currentMethodBody = (JConcreteMethodBody) body;
tmpLocalVarPrefix = prefix;
}
@@ -72,4 +72,4 @@
return local;
}
-}
\ No newline at end of file
+}
diff --git a/jack/src/com/android/jack/transformations/request/AddJLocalInMethodBody.java b/jack/src/com/android/jack/transformations/request/AddJLocalInMethodBody.java
index df63820..ca3e00f 100644
--- a/jack/src/com/android/jack/transformations/request/AddJLocalInMethodBody.java
+++ b/jack/src/com/android/jack/transformations/request/AddJLocalInMethodBody.java
@@ -16,6 +16,7 @@
package com.android.jack.transformations.request;
+import com.android.jack.ir.ast.JConcreteMethodBody;
import com.android.jack.ir.ast.JLocal;
import com.android.jack.ir.ast.JMethodBody;
import com.android.sched.transform.TransformStep;
@@ -31,7 +32,7 @@
@Nonnull
private final JLocal local;
@Nonnull
- private final JMethodBody methodBody;
+ private final JConcreteMethodBody methodBody;
/**
* Constructor specifying the {@link JLocal} to add into the {@link JMethodBody}.
@@ -39,7 +40,7 @@
* @param local the local variable to add to the method body
* @param methodBody the body of the method to update
*/
- public AddJLocalInMethodBody(@Nonnull JLocal local, @Nonnull JMethodBody methodBody) {
+ public AddJLocalInMethodBody(@Nonnull JLocal local, @Nonnull JConcreteMethodBody methodBody) {
this.local = local;
this.methodBody = methodBody;
}
diff --git a/jack/src/com/android/jack/transformations/ssa/CfgNodeIdAssignment.java b/jack/src/com/android/jack/transformations/ssa/CfgNodeIdAssignment.java
new file mode 100644
index 0000000..cd248af
--- /dev/null
+++ b/jack/src/com/android/jack/transformations/ssa/CfgNodeIdAssignment.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.transformations.ssa;
+
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.jack.util.graph.NodeIdMarker;
+import com.android.sched.item.Description;
+import com.android.sched.item.Name;
+import com.android.sched.schedulable.Constraint;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+
+/**
+ * Assign Node ID from the control flow graph.
+ */
+@Description("Insert Node ID to the CFG for SSA conversion")
+@Name("CfgNodeIdAssignment")
+@Constraint(need = {SsaBasicBlockSplitterMarker.class})
+@Transform(add = {NodeIdMarker.class})
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class CfgNodeIdAssignment implements RunnableSchedulable<JMethodBodyCfg> {
+ @Override
+ public void run(JMethodBodyCfg t) {
+ NodeIdMarker.assignIds(t.getCfg());
+ }
+}
diff --git a/jack/src/com/android/jack/transformations/ssa/CfgNodeIdRemoval.java b/jack/src/com/android/jack/transformations/ssa/CfgNodeIdRemoval.java
new file mode 100644
index 0000000..d030387
--- /dev/null
+++ b/jack/src/com/android/jack/transformations/ssa/CfgNodeIdRemoval.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.transformations.ssa;
+
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.jack.util.graph.NodeIdMarker;
+import com.android.sched.item.Description;
+import com.android.sched.item.Name;
+import com.android.sched.schedulable.Constraint;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+
+/**
+ * Removes Node ID to the control flow graph.
+ */
+@Description("Removes Node ID to the CFG for SSA conversion")
+@Name("CfgNodeIdRemoval")
+@Constraint(need = {NodeIdMarker.class})
+@Transform(remove = {NodeIdMarker.class})
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class CfgNodeIdRemoval implements RunnableSchedulable<JMethodBodyCfg> {
+ @Override
+ public void run(JMethodBodyCfg body) {
+ NodeIdMarker.removeIds(body.getCfg());
+ }
+}
diff --git a/jack/src/com/android/jack/transformations/ssa/CfgNodeListAssignment.java b/jack/src/com/android/jack/transformations/ssa/CfgNodeListAssignment.java
new file mode 100644
index 0000000..b85f04a
--- /dev/null
+++ b/jack/src/com/android/jack/transformations/ssa/CfgNodeListAssignment.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.transformations.ssa;
+
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.jack.util.graph.NodeListMarker;
+import com.android.sched.item.Description;
+import com.android.sched.item.Name;
+import com.android.sched.schedulable.Constraint;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+
+
+/**
+ * Removes Node ID from the control flow graph.
+ */
+@Description("Insert Node list to the CFG for SSA conversion")
+@Name("CfgNodeListAssignment")
+@Constraint(need = {SsaBasicBlockSplitterMarker.class})
+@Transform(add = {NodeListMarker.class})
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class CfgNodeListAssignment implements RunnableSchedulable<JMethodBodyCfg> {
+ @Override
+ public void run(JMethodBodyCfg body) {
+ NodeListMarker.assignNodeList(body.getCfg());
+ }
+}
diff --git a/jack/src/com/android/jack/transformations/ssa/CfgNodeListRemoval.java b/jack/src/com/android/jack/transformations/ssa/CfgNodeListRemoval.java
new file mode 100644
index 0000000..94eb169
--- /dev/null
+++ b/jack/src/com/android/jack/transformations/ssa/CfgNodeListRemoval.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.transformations.ssa;
+
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.jack.util.graph.NodeListMarker;
+import com.android.sched.item.Description;
+import com.android.sched.item.Name;
+import com.android.sched.schedulable.Constraint;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+
+/**
+ * Removes Node List from the CFG for SSA conversion.
+ */
+@Description("Removes Node List from the CFG for SSA conversion")
+@Name("CfgNodeListRemoval")
+@Constraint(need = {NodeListMarker.class})
+@Transform(remove = {NodeListMarker.class})
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class CfgNodeListRemoval implements RunnableSchedulable<JMethodBodyCfg> {
+ @Override
+ public void run(JMethodBodyCfg body) {
+ NodeListMarker.removeNodeList(body.getCfg());
+ }
+}
diff --git a/jack/src/com/android/jack/transformations/ssa/DominanceFrontierAssignment.java b/jack/src/com/android/jack/transformations/ssa/DominanceFrontierAssignment.java
new file mode 100644
index 0000000..05f0f2a
--- /dev/null
+++ b/jack/src/com/android/jack/transformations/ssa/DominanceFrontierAssignment.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.transformations.ssa;
+
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.jack.util.graph.DominanceFrontier;
+import com.android.sched.item.Description;
+import com.android.sched.item.Name;
+import com.android.sched.schedulable.Constraint;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Use;
+
+/**
+ *
+ */
+@Description("Computes dominance frontier in the CFG.")
+@Name("DominanceFrontierAssignment")
+@Constraint(need = {SsaBasicBlockSplitterMarker.class})
+@Use(DominanceFrontier.class)
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class DominanceFrontierAssignment implements RunnableSchedulable<JMethodBodyCfg> {
+ @Override
+ public void run(JMethodBodyCfg body) {
+ new DominanceFrontier<>(body.getCfg()).run();
+ }
+}
diff --git a/jack/src/com/android/jack/transformations/ssa/DominanceFrontierRemoval.java b/jack/src/com/android/jack/transformations/ssa/DominanceFrontierRemoval.java
new file mode 100644
index 0000000..97495d3
--- /dev/null
+++ b/jack/src/com/android/jack/transformations/ssa/DominanceFrontierRemoval.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.transformations.ssa;
+
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.jack.util.graph.DominanceFrontierInfoMarker;
+import com.android.jack.util.graph.DominatorTreeMarker;
+import com.android.sched.item.Description;
+import com.android.sched.item.Name;
+import com.android.sched.schedulable.Constraint;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+
+/**
+ * Removes dominance frontier related marker from control flow graph.
+ */
+@Description("Removes dominance frontier related marker from control flow graph")
+@Name("DominanceFrontierRemoval")
+@Constraint(need = {DominanceFrontierInfoMarker.class})
+@Transform(remove = {DominanceFrontierInfoMarker.class})
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class DominanceFrontierRemoval implements RunnableSchedulable<JMethodBodyCfg> {
+ @Override
+ public void run(JMethodBodyCfg body) {
+ DominanceFrontierInfoMarker.clearMarkers(body.getCfg());
+ DominatorTreeMarker.clearMarkers(body.getCfg());
+ }
+}
diff --git a/jack/src/com/android/jack/transformations/ssa/JPhiElementInsertion.java b/jack/src/com/android/jack/transformations/ssa/JPhiElementInsertion.java
new file mode 100644
index 0000000..ac74326
--- /dev/null
+++ b/jack/src/com/android/jack/transformations/ssa/JPhiElementInsertion.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.transformations.ssa;
+
+import com.android.jack.dx.util.IntIterator;
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.JVariable;
+import com.android.jack.ir.ast.JVariableRef;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JPhiBlockElement;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.jack.util.graph.DominanceFrontierInfoMarker;
+import com.android.jack.util.graph.NodeIdMarker;
+import com.android.jack.util.graph.NodeListMarker;
+import com.android.sched.item.Description;
+import com.android.sched.item.Name;
+import com.android.sched.schedulable.Constraint;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * Split basic blocks for Phi element insertion.
+ */
+@Description("Insert Phi elements into the CFG.")
+@Name("JPhiElementInsertion")
+@Transform(add = {JPhiBlockElement.class},
+ remove = {SsaBasicBlockSplitterMarker.class})
+@Constraint(need = {DominanceFrontierInfoMarker.class})
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class JPhiElementInsertion implements RunnableSchedulable<JMethodBodyCfg> {
+
+ @Override
+ public void run(JMethodBodyCfg body) {
+ // Invalidates the block split marker.
+ SsaBasicBlockSplitterMarker marker =
+ body.getCfg().removeMarker(SsaBasicBlockSplitterMarker.class);
+ assert marker != null;
+ placePhiFunctions(body.getCfg());
+ }
+
+ /**
+ * See Appel algorithm 19.6:
+ *
+ * Place Phi functions in appropriate locations.
+ *
+ */
+ private void placePhiFunctions(JControlFlowGraph cfg) {
+ final int numLocals = SsaUtil.getTotalNumberOfLocals(cfg);
+ int regCount;
+ int blockCount;
+
+ final List<JBasicBlock> bbMap = NodeListMarker.getNodeList(cfg);
+
+ blockCount = bbMap.size();
+ regCount = numLocals;
+
+
+ // Bit set of registers vs block index "definition sites"
+ BitSet[] defsites = new BitSet[regCount];
+
+ // Bit set of registers vs block index "phi placement sites"
+ BitSet[] phisites = new BitSet[regCount];
+
+ for (int i = 0; i < regCount; i++) {
+ defsites[i] = new BitSet(blockCount);
+ phisites[i] = new BitSet(blockCount);
+ }
+
+ /*
+ * For each register, build a set of all basic blocks where containing an assignment to that
+ * register.
+ */
+ for (JBasicBlock b : bbMap) {
+ for (JBasicBlockElement stmt : b.getElements(true)) {
+ JVariableRef dv = stmt.getDefinedVariable();
+ if (dv != null) {
+ JVariable rs = dv.getTarget();
+ // We don't need to check for JThis because JThis will never be defined.
+ int index = SsaUtil.getLocalIndex(cfg, rs);
+ defsites[index].set(NodeIdMarker.getId(b));
+ }
+ }
+ }
+
+ BitSet worklist;
+ /*
+ * For each register, compute all locations for phi placement based on dominance-frontier
+ * algorithm.
+ */
+ for (int reg = 0, s = regCount; reg < s; reg++) {
+ int workBlockIndex;
+
+ /* Worklist set starts out with each node where reg is assigned. */
+ worklist = (BitSet) (defsites[reg].clone());
+
+ while (0 <= (workBlockIndex = worklist.nextSetBit(0))) {
+ worklist.clear(workBlockIndex);
+ DominanceFrontierInfoMarker domInfo =
+ DominanceFrontierInfoMarker.getDomInfo(bbMap.get(workBlockIndex));
+ IntIterator dfIterator = domInfo.dominanceFrontiers.iterator();
+
+ while (dfIterator.hasNext()) {
+ int dfBlockIndex = dfIterator.next();
+ JBasicBlock dfBlock = bbMap.get(dfBlockIndex);
+
+ if (!phisites[reg].get(dfBlockIndex) && dfBlock != cfg.getExitBlock()) {
+ phisites[reg].set(dfBlockIndex);
+
+ JVariable target = SsaUtil.getVariableByIndex(cfg, reg);
+
+ JPhiBlockElement phi =
+ new JPhiBlockElement(target, dfBlock.getPredecessors(), dfBlock.getSourceInfo());
+ dfBlock.insertElement(0, phi);
+
+ if (!defsites[reg].get(dfBlockIndex)) {
+ worklist.set(dfBlockIndex);
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/transformations/ssa/SsaBasicBlockSplitter.java b/jack/src/com/android/jack/transformations/ssa/SsaBasicBlockSplitter.java
new file mode 100644
index 0000000..32c6c5d
--- /dev/null
+++ b/jack/src/com/android/jack/transformations/ssa/SsaBasicBlockSplitter.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.transformations.ssa;
+
+import com.google.common.collect.Lists;
+
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.cfg.ExceptionHandlingContext;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JCaseBasicBlock;
+import com.android.jack.ir.ast.cfg.JCatchBasicBlock;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JEntryBasicBlock;
+import com.android.jack.ir.ast.cfg.JExitBasicBlock;
+import com.android.jack.ir.ast.cfg.JGotoBlockElement;
+import com.android.jack.ir.ast.cfg.JSimpleBasicBlock;
+import com.android.jack.optimizations.cfg.CfgBasicBlockUtils;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.sched.item.Description;
+import com.android.sched.item.Name;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Split basic blocks for Phi element insertion.
+ */
+@Description("Split basic blocks for Phi element insertion.")
+@Name("SsaBasicBlockSplitter")
+@Transform(add = {SsaBasicBlockSplitterMarker.class},
+ modify = {JControlFlowGraph.class})
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class SsaBasicBlockSplitter implements RunnableSchedulable<JMethodBodyCfg> {
+
+ // This appears to never be an issue in Jack nor DX as far as we can tell.
+ public static final boolean NEED_EDGE_SPLIT_PREDECESSOR = false;
+
+ // This *IS* an issue in code gen. However, we have "delayed" it and do the splitting right
+ // before ROP is built.
+ public static final boolean NEED_EDGE_SPLIT_MV_EXCEPTION = false;
+
+ // This is like NEED_EDGE_SPLIT_MV_EXCEPTION and can be handled later.
+ public static final boolean NEED_EDGE_SPLIT_CATCH_BASIC_BLOCK = false;
+
+ public static final boolean NEED_EDGE_SPLIT_CASE_BASIC_BLOCK = false;
+
+ @Override
+ public void run(@Nonnull JMethodBodyCfg body) {
+ assert body.getCfg().getMarker(SsaBasicBlockSplitterMarker.class) == null;
+ removeExceptionHandlingContext(body.getCfg());
+ edgeSplit(body.getCfg());
+ body.getCfg().addMarker(SsaBasicBlockSplitterMarker.INSTANCE);
+ }
+
+ /**
+ * Extracts control flow graph from method and perform edge split in neccessary.
+ */
+ private void edgeSplit(@Nonnull JControlFlowGraph cfg) {
+ if (NEED_EDGE_SPLIT_PREDECESSOR) {
+ edgeSplitPredecessors(cfg);
+ }
+ if (NEED_EDGE_SPLIT_MV_EXCEPTION) {
+ edgeSplitMoveExceptionsAndResults(cfg);
+ }
+ edgeSplitSuccessors(cfg);
+ }
+
+ /**
+ * Inserts Z nodes as new predecessors for every node that has multiple successors and multiple
+ * predecessors.
+ */
+ private void edgeSplitPredecessors(JControlFlowGraph cfg) {
+ for (JBasicBlock block : cfg.getReachableBlocksDepthFirst()) {
+ if (nodeNeedsUniquePredecessor(block)) {
+ block.split(0);
+ }
+ }
+ }
+
+ /**
+ * @param block {@code non-null;} block in question
+ * @return {@code true} if this node needs to have a unique predecessor created for it
+ */
+ private static boolean nodeNeedsUniquePredecessor(JBasicBlock block) {
+ if (block instanceof JExitBasicBlock) {
+ return false;
+ }
+ /*
+ * Any block with that has both multiple successors and multiple predecessors needs a new
+ * predecessor node.
+ */
+ int countPredecessors = block.getPredecessorCount();
+ int countSuccessors = block.getSuccessors().size();
+ boolean needsUniquePredecessor = countPredecessors > 1 && countSuccessors > 1;
+ assert !needsUniquePredecessor
+ || !(block instanceof JCaseBasicBlock || block instanceof JCatchBasicBlock);
+ return needsUniquePredecessor;
+ }
+
+ private static void edgeSplitMoveExceptionsAndResults(JControlFlowGraph cfg) {
+ /*
+ * New blocks are added to the end of the block list during this iteration.
+ */
+ for (JBasicBlock block : cfg.getReachableBlocksDepthFirst()) {
+ /*
+ * Any block that starts with a move-exception and has more than one predecessor...
+ */
+ if (!(block instanceof JExitBasicBlock) && block.getPredecessorCount() > 1
+ && (block instanceof JCatchBasicBlock)) {
+ for (JBasicBlock predecessor : Lists.newArrayList(block.getPredecessors())) {
+ insertNewSimpleSuccessor(predecessor, block);
+ }
+ }
+ }
+ }
+
+ /**
+ * Inserts Z nodes for every node that needs a new successor.
+ *
+ */
+ private static void edgeSplitSuccessors(JControlFlowGraph cfg) {
+ /*
+ * New blocks are added to the end of the block list during this iteration.
+ */
+ for (JBasicBlock block : cfg.getReachableBlocksDepthFirst()) {
+ // Successors list is modified in loop below.
+ for (JBasicBlock succ : block.getSuccessors()) {
+ if (needsNewSuccessor(block, succ)) {
+ // These two type of basic block requires special case handling. Otherwise, we might
+ // up with an IR that is very difficult to understand. Please refer to the design for
+ // detail information.
+ if (succ instanceof JCatchBasicBlock) {
+ if (NEED_EDGE_SPLIT_CATCH_BASIC_BLOCK) {
+ block.split(-1);
+ }
+ } else if (succ instanceof JCaseBasicBlock) {
+ if (NEED_EDGE_SPLIT_CASE_BASIC_BLOCK) {
+ block.split(-1);
+ }
+ } else {
+ insertNewSimpleSuccessor(block, succ);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns {@code true} if block and successor need a Z-node between them. Presently, this is
+ * {@code true} if the final instruction has any sources or results and the current successor
+ * block has more than one predecessor.
+ *
+ * @param block predecessor node
+ * @param succ successor node
+ * @return {@code true} if a Z node is needed
+ */
+ private static boolean needsNewSuccessor(JBasicBlock block, JBasicBlock succ) {
+ if (block instanceof JEntryBasicBlock) {
+ return false;
+ }
+ if (block.getElementCount() == 0) {
+ return false;
+ }
+ if (succ instanceof JExitBasicBlock) {
+ return false;
+ }
+ JBasicBlockElement lastInsn = block.getLastElement();
+ int uvCount = lastInsn.getUsedVariables().size();
+ int dvCount = lastInsn.getDefinedVariable() == null ? 0 : 1;
+ return (uvCount + dvCount > 0) && succ.getPredecessorCount() > 1;
+ }
+
+ /**
+ * Insert a new successor between a block and one of its successor.
+ */
+ private static void insertNewSimpleSuccessor(JBasicBlock block, JBasicBlock other) {
+ JSimpleBasicBlock newSucc = new JSimpleBasicBlock(block.getCfg(), other);
+ // The goto itself shouldn't throw, therefore, it is ok with empty EHC.
+ JGotoBlockElement jGoto =
+ new JGotoBlockElement(other.getSourceInfo(), ExceptionHandlingContext.EMPTY);
+ newSucc.insertElement(0, jGoto);
+ block.replaceAllSuccessors(other, newSucc);
+ }
+
+ private void removeExceptionHandlingContext(@Nonnull JControlFlowGraph cfg) {
+ for (JBasicBlock block : cfg.getInternalBlocksUnordered()) {
+ for (JBasicBlockElement element : block.getElements(true)) {
+ element.resetEHContext(ExceptionHandlingContext.EMPTY);
+ }
+ }
+ new CfgBasicBlockUtils(cfg).removeUnreachableBlocks();
+ }
+}
diff --git a/jack/src/com/android/jack/transformations/ssa/SsaBasicBlockSplitterMarker.java b/jack/src/com/android/jack/transformations/ssa/SsaBasicBlockSplitterMarker.java
new file mode 100644
index 0000000..45b29ca
--- /dev/null
+++ b/jack/src/com/android/jack/transformations/ssa/SsaBasicBlockSplitterMarker.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.transformations.ssa;
+
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.sched.item.Description;
+import com.android.sched.marker.Marker;
+import com.android.sched.marker.ValidOn;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Marks a Method as block splitted and ready for Phi instructino insertion.
+ */
+@Description("Marks a CFG that has been block splitted and ready for SSA construction.")
+@ValidOn(JControlFlowGraph.class)
+public final class SsaBasicBlockSplitterMarker implements Marker {
+ @Nonnull
+ public static final SsaBasicBlockSplitterMarker INSTANCE = new SsaBasicBlockSplitterMarker();
+
+ private SsaBasicBlockSplitterMarker() {}
+
+ @Override
+ @Nonnull
+ public Marker cloneIfNeeded() {
+ return this;
+ }
+}
diff --git a/jack/src/com/android/jack/transformations/ssa/SsaMethodMarker.java b/jack/src/com/android/jack/transformations/ssa/SsaMethodMarker.java
new file mode 100644
index 0000000..2f6c0dd
--- /dev/null
+++ b/jack/src/com/android/jack/transformations/ssa/SsaMethodMarker.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.transformations.ssa;
+
+import com.android.jack.ir.ast.JMethod;
+import com.android.sched.item.Description;
+import com.android.sched.marker.Marker;
+import com.android.sched.marker.ValidOn;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Implies this method is SSA compatible.
+ */
+@Description("SsaMethodMarker")
+@ValidOn(value = {JMethod.class})
+public class SsaMethodMarker implements Marker {
+ @Override
+ @Nonnull
+ public Marker cloneIfNeeded() {
+ return this;
+ }
+}
diff --git a/jack/src/com/android/jack/transformations/ssa/SsaRenamer.java b/jack/src/com/android/jack/transformations/ssa/SsaRenamer.java
new file mode 100644
index 0000000..58b1380
--- /dev/null
+++ b/jack/src/com/android/jack/transformations/ssa/SsaRenamer.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.transformations.ssa;
+
+import com.android.jack.ir.ast.JMethod;
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.JParameter;
+import com.android.jack.ir.ast.JSsaVariableDefRef;
+import com.android.jack.ir.ast.JSsaVariableDefRefPlaceHolder;
+import com.android.jack.ir.ast.JSsaVariableRef;
+import com.android.jack.ir.ast.JSsaVariableUseRef;
+import com.android.jack.ir.ast.JSsaVariableUseRefPlaceHolder;
+import com.android.jack.ir.ast.JThis;
+import com.android.jack.ir.ast.JThisRef;
+import com.android.jack.ir.ast.JVariable;
+import com.android.jack.ir.ast.JVariableRef;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.jack.ir.ast.cfg.JPhiBlockElement;
+import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
+import com.android.jack.transformations.request.Replace;
+import com.android.jack.transformations.request.TransformationRequest;
+import com.android.jack.util.graph.DominatorTreeMarker;
+import com.android.jack.util.graph.NodeIdMarker;
+import com.android.jack.util.graph.NodeListMarker;
+import com.android.sched.item.Description;
+import com.android.sched.item.Name;
+import com.android.sched.schedulable.Constraint;
+import com.android.sched.schedulable.Filter;
+import com.android.sched.schedulable.RunnableSchedulable;
+import com.android.sched.schedulable.Transform;
+
+import java.util.BitSet;
+import java.util.List;
+import java.util.Stack;
+
+/**
+ * Complete transformation to SSA form by renaming all registers accessed.
+ * <p>
+ *
+ * See Appel algorithm 19.7
+ * <p>
+ *
+ * Unlike the original algorithm presented in Appel, this renamer converts to a new flat
+ * (versionless) register space. The "version 0" registers, which represent the initial state of the
+ * Rop registers and should never actually be meaningfully accessed in a legal program, are
+ * represented as the first N registers in the SSA namespace. Subsequent assignments are assigned
+ * new unique names. Note that the incoming Rop representation has a concept of register widths,
+ * where 64-bit values are stored into two adjoining Rop registers. This adjoining register
+ * representation is ignored in SSA form conversion and while in SSA form, each register can be e
+ * either 32 or 64 bits wide depending on use. The adjoining-register represention is re-created
+ * later when converting back to Rop form.
+ * <p>
+ *
+ * But, please note, the SSA Renamer's ignoring of the adjoining-register ROP representation means
+ * that unaligned accesses to 64-bit registers are not supported. For example, you cannot do a
+ * 32-bit operation on a portion of a 64-bit register. This will never be observed to happen when
+ * coming from Java code, of course.
+ * <p>
+ *
+ * The implementation here, rather than keeping a single register version stack for the entire
+ * method as the dom tree is walked, instead keeps a mapping table for the current block being
+ * processed. Once the current block has been processed, this mapping table is then copied and used
+ * as the initial state for child blocks.
+ * <p>
+ */
+@Description("Rename variables in the CFG for SSA properties.")
+@Name("SsaRenamer")
+@Constraint(need = {JPhiBlockElement.class})
+@Transform(add = JSsaVariableRef.class)
+@Filter(TypeWithoutPrebuiltFilter.class)
+public class SsaRenamer implements RunnableSchedulable<JMethodBodyCfg> {
+
+ private static class GraphRenamer {
+ private final JControlFlowGraph cfg;
+ private final JMethod method;
+
+ /** the number of original rop registers */
+ private final int ropRegCount;
+ /** next available SSA register */
+ private final int[] nextSsaReg;
+
+ private final List<JBasicBlock> bbMap;
+
+ /**
+ * indexed by block index; register version state for each block start. This list is updated by
+ * each dom parent for its children. The only sub-arrays that exist at any one time are the
+ * start states for blocks yet to be processed by a {@code BlockRenamer} instance.
+ */
+ private final JSsaVariableDefRef[][] startsForBlocks;
+
+ private GraphRenamer(JControlFlowGraph cfg) {
+ this.cfg = cfg;
+ this.method = cfg.getMethod();
+ bbMap = NodeListMarker.getNodeList(cfg);
+ ropRegCount = SsaUtil.getTotalNumberOfLocals(cfg);
+
+ /*
+ * Reserve the first N registers in the SSA register space for "version 0" registers.
+ */
+ nextSsaReg = new int[ropRegCount];
+ startsForBlocks = new JSsaVariableDefRef[bbMap.size()][];
+
+ /*
+ * Appel 19.7
+ *
+ * Initialization: for each variable a // register i Count[a] <- 0 // nextSsaReg, flattened
+ * Stack[a] <- 0 // versionStack push 0 onto Stack[a]
+ *
+ */
+
+ // top entry for the version stack is version 0
+ JSsaVariableDefRef[] initialRegMapping = new JSsaVariableDefRef[ropRegCount];
+ for (int i = 0; i < ropRegCount; i++) {
+ JVariable target = SsaUtil.getVariableByIndex(cfg, i);
+ initialRegMapping[i] = new JSsaVariableDefRef(method.getSourceInfo(), target, 0);
+ if (target instanceof JParameter) {
+ cfg.getMethodBody().addSsaParamDef(initialRegMapping[i]);
+ }
+ }
+ // Initial state for entry block
+ int entryId = NodeIdMarker.getId(cfg.getEntryBlock());
+ startsForBlocks[entryId] = initialRegMapping;
+ }
+
+ private void performRename() {
+ // Rename each block in dom-tree DFS order.
+ forEachBlockDepthFirstDom(cfg, new Visitor() {
+ @Override
+ public void visitBlock(JBasicBlock block, JBasicBlock unused) {
+ new BlockRenamer(block).process();
+ }
+ });
+ }
+
+ public void forEachBlockDepthFirstDom(JControlFlowGraph cfg, Visitor v) {
+ BitSet visited = new BitSet(bbMap.size() - 1);
+ Stack<JBasicBlock> stack = new Stack<JBasicBlock>();
+
+ stack.add(cfg.getEntryNode());
+
+ while (stack.size() > 0) {
+ JBasicBlock cur = stack.pop();
+ List<JBasicBlock> curDomChildren = DominatorTreeMarker.getDomChild(cur);
+
+ if (!visited.get(NodeIdMarker.getId(cur))) {
+ // We walk the tree this way for historical reasons...
+ for (int i = curDomChildren.size() - 1; i >= 0; i--) {
+ JBasicBlock child = curDomChildren.get(i);
+ stack.add(child);
+ }
+ visited.set(NodeIdMarker.getId(cur));
+ v.visitBlock(cur, null);
+ }
+ }
+ }
+
+ /**
+ * Processes all insns in a block and renames their registers as appropriate.
+ */
+ private class BlockRenamer {
+ /** {@code non-null;} block we're processing. */
+ private final JBasicBlock block;
+
+ /**
+ * {@code non-null;} indexed by old register name. The current top of the version stack as
+ * seen by this block. It's initialized from the ending state of its dom parent, updated as
+ * the block's instructions are processed, and then copied to each one of its dom children.
+ */
+ private final JSsaVariableDefRef[] currentMapping;
+
+ /**
+ * Constructs a block renamer instance. Call {@code process} to process.
+ *
+ * @param block {@code non-null;} block to process
+ */
+ BlockRenamer(final JBasicBlock block) {
+ this.block = block;
+ currentMapping = startsForBlocks[NodeIdMarker.getId(block)];
+ // We don't need our own start state anymore
+ startsForBlocks[NodeIdMarker.getId(block)] = null;
+ }
+
+ /**
+ * Renames all the variables in this block and inserts appriopriate phis in successor blocks.
+ */
+ public void process() {
+ /*
+ * From Appel:
+ *
+ * Rename(n) = for each statement S in block n // 'statement' in 'block'
+ */
+ for (JBasicBlockElement stmt : block.getElements(true)) {
+ if (stmt instanceof JPhiBlockElement) {
+ processPhiStmt((JPhiBlockElement) stmt);
+ } else {
+ processSourceReg(stmt);
+ processResultReg(stmt);
+ }
+ }
+
+ updateSuccessorPhis();
+
+ // Store the start states for our dom children.
+ boolean first = true;
+ List<JBasicBlock> domChildren = DominatorTreeMarker.getDomChild(block);
+ for (JBasicBlock child : domChildren) {
+ if (child != block) {
+ // Don't bother duplicating the array for the first child.
+ JSsaVariableDefRef[] childStart = first ? currentMapping : dupArray(currentMapping);
+ startsForBlocks[NodeIdMarker.getId(child)] = childStart;
+ first = false;
+ }
+ }
+
+ // currentMapping is owned by a child now.
+ }
+
+ /**
+ * Enforces a few contraints when a register mapping is added.
+ *
+ * <ol>
+ * <li>Ensures that all new SSA registers specs in the mapping table with the same register
+ * number are identical. In effect, once an SSA register spec has received or lost a local
+ * variable name, then every old-namespace register that maps to it should gain or lose its
+ * local variable name as well.
+ * <li>Records the local name associated with the register so that a register is never
+ * associated with more than one local.
+ * <li>ensures that only one SSA register at a time is considered to be associated with a
+ * local variable. When {@code currentMapping} is updated and the newly added element is
+ * named, strip that name from any other SSA registers.
+ * </ol>
+ *
+ * @param ropReg {@code >= 0;} rop register number
+ * @param ssaReg {@code non-null;} an SSA register that has just been added to
+ * {@code currentMapping}
+ */
+ private void addMapping(int ropReg, JSsaVariableDefRef ssaReg) {
+ currentMapping[ropReg] = ssaReg;
+ }
+
+ /**
+ *
+ * Phi insns have their result registers renamed.
+ */
+ public void processPhiStmt(JPhiBlockElement phi) {
+ JVariable target = phi.getTarget();
+ if (target instanceof JThis) {
+ return; // I don't think we ever run into this.
+ }
+ int index = SsaUtil.getLocalIndex(cfg, target);
+
+ /* don't process sources for phi's except to replace place holders. */
+ for (JSsaVariableUseRef use : phi.getRhs()) {
+ if (isVersionZeroRegister(use)) {
+ TransformationRequest tr2 = new TransformationRequest(phi);
+ JSsaVariableUseRef ref = currentMapping[index].makeRef(use.getSourceInfo());
+ ref.addAllMarkers(use.getAllMarkers());
+ tr2.append(new Replace(use, ref));
+ tr2.commit();
+ }
+ }
+
+ nextSsaReg[index]++;
+ // It is probably ok to not have any debug marker here.
+ JSsaVariableDefRef lhs =
+ new JSsaVariableDefRef(phi.getSourceInfo(), target, nextSsaReg[index]);
+ TransformationRequest tr = new TransformationRequest(phi);
+ tr.append(new Replace(phi.getLhs(), lhs));
+ addMapping(index, lhs);
+ tr.commit();
+ }
+
+ /**
+ * Renames the result register of this insn and updates the current register mapping. Does
+ * nothing if this insn has no result. Applied to all non-move insns.
+ *
+ * @param insn insn to process.
+ */
+ void processResultReg(JBasicBlockElement insn) {
+ JVariableRef dv = insn.getDefinedVariable();
+ if (dv == null) {
+ return;
+ }
+
+ JVariable ropReg = dv.getTarget();
+ int index = SsaUtil.getLocalIndex(cfg, ropReg);
+
+ nextSsaReg[index]++;
+ JSsaVariableDefRef ref =
+ new JSsaVariableDefRef(dv.getSourceInfo(), ropReg, nextSsaReg[index]);
+ ref.addAllMarkers(dv.getAllMarkers());
+ TransformationRequest tr = new TransformationRequest(method);
+ tr.append(new Replace(dv, ref));
+ tr.commit();
+ addMapping(index, ref);
+ }
+
+ void processSourceReg(JBasicBlockElement insn) {
+ List<JVariableRef> uv = insn.getUsedVariables();
+ if (uv.isEmpty()) {
+ return;
+ }
+
+ TransformationRequest tr = new TransformationRequest(method);
+ for (JVariableRef varRef : uv) {
+ if (varRef instanceof JThisRef) {
+ continue;
+ }
+ int index = SsaUtil.getLocalIndex(cfg, varRef.getTarget());
+ assert index != -1;
+ JSsaVariableUseRef ref = currentMapping[index].makeRef(varRef.getSourceInfo());
+ ref.addAllMarkers(varRef.getAllMarkers());
+ tr.append(new Replace(varRef, ref));
+ }
+ tr.commit();
+ }
+
+ /**
+ * Updates the phi insns in successor blocks with operands based on the current mapping of the
+ * rop register the phis represent.
+ */
+ private void updateSuccessorPhis() {
+ for (JBasicBlock successor : block.getSuccessors()) {
+ for (JBasicBlockElement stmt : successor.getElements(true)) {
+ if (stmt instanceof JPhiBlockElement) {
+ JPhiBlockElement phi = (JPhiBlockElement) stmt;
+ int ropReg = -1;
+ JVariable target = phi.getTarget();
+ if (target instanceof JThis) {
+ continue;
+ }
+ ropReg = SsaUtil.getLocalIndex(cfg, target);
+ /*
+ * Never add a version 0 register as a phi operand. Version 0 registers represent the
+ * initial register state, and thus are never significant. Furthermore, the register
+ * liveness algorithm doesn't properly count them as "live in" at the beginning of the
+ * method.
+ */
+ JSsaVariableDefRef stackTop = currentMapping[ropReg];
+ if (!isVersionZeroRegister(stackTop)) {
+ TransformationRequest tr = new TransformationRequest(phi);
+ JSsaVariableUseRef rhs = stackTop.makeRef(phi.getSourceInfo());
+ rhs.addAllMarkers(phi.getRhs(block).getAllMarkers());
+ JSsaVariableUseRef oldRhs = phi.getRhs(block);
+ tr.append(new Replace(oldRhs, rhs));
+ if (!(oldRhs instanceof JSsaVariableUseRefPlaceHolder)) {
+ oldRhs.deleteUseFromDef();
+ }
+ tr.commit();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns true if this SSA register is a "version 0" register. All version 0 registers are
+ * assigned the first N register numbers, where N is the count of original rop registers.
+ *
+ * @param ssaReg the SSA register in question
+ * @return true if it is a version 0 register.
+ */
+ private static boolean isVersionZeroRegister(JSsaVariableDefRef ssaReg) {
+ return ssaReg.getVersion() == 0 && ssaReg instanceof JSsaVariableDefRefPlaceHolder;
+ }
+
+ private static boolean isVersionZeroRegister(JSsaVariableUseRef ssaReg) {
+ return ssaReg.getVersion() == 0 && ssaReg instanceof JSsaVariableUseRefPlaceHolder;
+ }
+ }
+
+ @Override
+ public void run(JMethodBodyCfg body) {
+ new GraphRenamer(body.getCfg()).performRename();
+ }
+
+ private static JSsaVariableDefRef[] dupArray(JSsaVariableDefRef[] orig) {
+ JSsaVariableDefRef[] copy = new JSsaVariableDefRef[orig.length];
+ System.arraycopy(orig, 0, copy, 0, orig.length);
+ return copy;
+ }
+
+ /**
+ * Visitor interface for basic blocks.
+ */
+ public static interface Visitor {
+ /**
+ * Indicates a block has been visited by an iterator method.
+ *
+ * @param v {@code non-null;} block visited
+ * @param parent {@code null-ok;} parent node if applicable
+ */
+ void visitBlock(JBasicBlock v, JBasicBlock parent);
+ }
+}
diff --git a/jack/src/com/android/jack/transformations/ssa/SsaUtil.java b/jack/src/com/android/jack/transformations/ssa/SsaUtil.java
new file mode 100644
index 0000000..707f700
--- /dev/null
+++ b/jack/src/com/android/jack/transformations/ssa/SsaUtil.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.transformations.ssa;
+
+import com.android.jack.ir.ast.JLocal;
+import com.android.jack.ir.ast.JMethodBodyCfg;
+import com.android.jack.ir.ast.JParameter;
+import com.android.jack.ir.ast.JVariable;
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Not exactly a utilities but more of a set of things that are missing in the current API and we
+ * have to brute force it here.
+ */
+public class SsaUtil {
+
+ public static int getLocalIndex(@Nonnull JControlFlowGraph cfg, JVariable var) {
+ JMethodBodyCfg body = cfg.getMethodBody();
+ int numParam = body.getMethod().getParams().size();
+ if (var instanceof JParameter) {
+ int paramIdx = body.getMethod().getParams().indexOf(var);
+ assert paramIdx != -1;
+ return paramIdx;
+ }
+
+ int numLocal = body.getLocals().size();
+ if (var instanceof JLocal) {
+ // We either have a defined local or catch param.
+ List<JLocal> catchParams = body.getCatchLocals();
+ int index = catchParams.indexOf(var);
+ if (index != -1) {
+ return numLocal + numParam + index;
+ } else {
+ int localIdx = body.getLocals().indexOf(var);
+ assert localIdx != -1;
+ return numParam + localIdx;
+ }
+ }
+
+ return -1;
+ }
+
+ /**
+ * TODO(acleung): Investigate if it is worth caching this.
+ */
+ public static JVariable getVariableByIndex(@Nonnull JControlFlowGraph cfg, int index) {
+ JMethodBodyCfg body = cfg.getMethodBody();
+ assert body != null;
+ int numLocal = body.getLocals().size();
+ int numParam = body.getMethod().getParams().size();
+
+ if (index < numParam) {
+ return body.getMethod().getParams().get(index);
+ }
+
+ if (index < numParam + numLocal) {
+ return body.getLocals().get(index - numParam);
+ }
+
+ List<JLocal> catchParams = body.getCatchLocals();
+ return catchParams.get(index - numParam - numLocal);
+ }
+
+ public static int getTotalNumberOfLocals(@Nonnull JControlFlowGraph cfg) {
+ JMethodBodyCfg body = cfg.getMethodBody();
+ assert body != null;
+ int numParam = body.getMethod().getParams().size();
+ assert body.getLocals() != null;
+ int numLocal = body.getLocals().size();
+ int numCatch = body.getNumCatchLocals();
+ return numLocal + numParam + numCatch;
+ }
+}
diff --git a/jack/src/com/android/jack/transformations/uselessif/UselessIfRemover.java b/jack/src/com/android/jack/transformations/uselessif/UselessIfRemover.java
deleted file mode 100644
index 7a4baed..0000000
--- a/jack/src/com/android/jack/transformations/uselessif/UselessIfRemover.java
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.jack.transformations.uselessif;
-
-import com.android.jack.Options;
-import com.android.jack.ir.ast.JBooleanLiteral;
-import com.android.jack.ir.ast.JIfStatement;
-import com.android.jack.ir.ast.JMethod;
-import com.android.jack.ir.ast.JStatement;
-import com.android.jack.ir.ast.JVisitor;
-import com.android.jack.scheduling.filter.TypeWithoutPrebuiltFilter;
-import com.android.jack.transformations.request.Remove;
-import com.android.jack.transformations.request.Replace;
-import com.android.jack.transformations.request.TransformationRequest;
-import com.android.sched.item.Description;
-import com.android.sched.schedulable.Constraint;
-import com.android.sched.schedulable.Filter;
-import com.android.sched.schedulable.RunnableSchedulable;
-import com.android.sched.util.config.ThreadConfig;
-import com.android.sched.util.log.Tracer;
-import com.android.sched.util.log.TracerFactory;
-import com.android.sched.util.log.stats.Counter;
-import com.android.sched.util.log.stats.CounterImpl;
-import com.android.sched.util.log.stats.StatisticId;
-
-import javax.annotation.Nonnull;
-
-/**
- * This visitor removes the if statement when the condition is a boolean literal
- */
-@Description("Removes useless if statement")
-@Constraint(need = {JIfStatement.class})
-@Filter(TypeWithoutPrebuiltFilter.class)
-public class UselessIfRemover implements RunnableSchedulable<JMethod> {
-
- @Nonnull
- public static final StatisticId<Counter> REMOVED_IF = new StatisticId<Counter>(
- "jack.statement.if.removed", "Removed 'if' statement",
- CounterImpl.class, Counter.class);
-
- @Nonnull
- private final com.android.jack.util.filter.Filter<JMethod> filter =
- ThreadConfig.get(Options.METHOD_FILTER);
-
- @Nonnull
- private final Tracer tracer = TracerFactory.getTracer();
-
- private class UselessIfRemoverVisitor extends JVisitor {
-
- @Nonnull
- private final TransformationRequest request;
-
- private UselessIfRemoverVisitor(@Nonnull TransformationRequest request) {
- this.request = request;
- }
-
- @Override
- public boolean visit(@Nonnull JIfStatement ifStmt) {
- if (ifStmt.getIfExpr() instanceof JBooleanLiteral) {
- JBooleanLiteral cond = (JBooleanLiteral) ifStmt.getIfExpr();
- tracer.getStatistic(REMOVED_IF).incValue();
- if (cond.getValue()) {
- JStatement thenStmt = ifStmt.getThenStmt();
- // if (true) A else B => A
- request.append(new Replace(ifStmt, thenStmt));
- } else {
- JStatement elseStmt = ifStmt.getElseStmt();
- if (elseStmt != null) {
- // if (false) A else B => B
- request.append(new Replace(ifStmt, elseStmt));
- } else {
- // if (false) then B => []
- request.append(new Remove(ifStmt));
- }
- }
- }
-
- return super.visit(ifStmt);
- }
- }
-
- @Override
- public void run(@Nonnull JMethod method) {
- if (method.isNative() || method.isAbstract() || !filter.accept(this.getClass(), method)) {
- return;
- }
-
- TransformationRequest request = new TransformationRequest(method);
- UselessIfRemoverVisitor visitor = new UselessIfRemoverVisitor(request);
- visitor.accept(method);
- request.commit();
- }
-
-}
\ No newline at end of file
diff --git a/jack/src/com/android/jack/util/graph/DepthFirstTraversal.java b/jack/src/com/android/jack/util/graph/DepthFirstTraversal.java
new file mode 100644
index 0000000..8dca8d8
--- /dev/null
+++ b/jack/src/com/android/jack/util/graph/DepthFirstTraversal.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.util.graph;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.BitSet;
+import java.util.Stack;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A basic depth first traversal of a directed graph.
+ */
+public class DepthFirstTraversal<N extends IGraphNode<N>> {
+ @Nonnull
+ private final IGraph<N> graph;
+
+ @Nonnull
+ private final GraphNodeVisitor<N> visitor;
+
+ private final boolean reverse;
+
+ @Nonnull
+ private final ImmutableList<N> nodes;
+
+ /**
+ * Performance a Depth First Traversal on a graph using a provided visitor starting from the root
+ * node.
+ *
+ * @param graph Give Graph.
+ * @param reverse If true, reverse the direction of all the edges and start from the end node.
+ * @param visitor
+ */
+ public static <N extends IGraphNode<N>> void run(@Nonnull IGraph<N> graph, boolean reverse,
+ @Nonnull GraphNodeVisitor<N> visitor) {
+ DepthFirstTraversal<N> dft = new DepthFirstTraversal<N>(graph, reverse, visitor);
+ dft.run();
+ }
+
+ private DepthFirstTraversal(@Nonnull IGraph<N> graph, boolean reverse,
+ @Nonnull GraphNodeVisitor<N> visitor) {
+ this.graph = graph;
+ this.reverse = reverse;
+ this.visitor = visitor;
+ this.nodes = NodeListMarker.getNodeList(graph);
+ }
+
+ private void run() {
+ BitSet visited = new BitSet(nodes.size());
+
+ // We push the parent first, then the child on the stack.
+ Stack<N> stack = new Stack<N>();
+
+ N rootBlock = reverse ? graph.getExitNode() : graph.getEntryNode();
+
+ stack.add(null); // Start with null parent.
+ stack.add(rootBlock);
+
+ while (stack.size() > 0) {
+ N cur = stack.pop();
+ N parent = stack.pop();
+
+ if (!visited.get(NodeIdMarker.getId(cur))) {
+ for (N child : reverse ? cur.getPredecessorsIterable() : cur.getSuccessorsIterable()) {
+ stack.add(cur);
+ stack.add(child);
+ }
+ visited.set(NodeIdMarker.getId(cur));
+ visitor.visitNode(cur, parent);
+ }
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/util/graph/DominanceFrontier.java b/jack/src/com/android/jack/util/graph/DominanceFrontier.java
new file mode 100644
index 0000000..433f6ba
--- /dev/null
+++ b/jack/src/com/android/jack/util/graph/DominanceFrontier.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.util.graph;
+
+import com.google.common.collect.ImmutableList;
+
+import com.android.sched.schedulable.Constraint;
+import com.android.sched.schedulable.Transform;
+import com.android.sched.schedulable.Use;
+
+import javax.annotation.Nonnull;
+
+/**
+ * Calculates the dominance-frontiers of a method's basic blocks. Algorithm from "A Simple, Fast
+ * Dominance Algorithm" by Cooper, Harvey, and Kennedy; transliterated to Java.
+ */
+@Transform(add = {DominanceFrontierInfoMarker.class, DominatorTreeMarker.class})
+@Constraint(need = {NodeListMarker.class})
+@Use(Dominators.class)
+public class DominanceFrontier<N extends IGraphNode<N>> {
+
+ @Nonnull
+ private final IGraph<N> graph;
+
+ @Nonnull
+ private final ImmutableList<N> nodes;
+
+ /**
+ * Constructs instance. Call {@link DominanceFrontier#run} to process.
+ *
+ * @param graph {@code non-null;} Control flow graph to process
+ */
+ public DominanceFrontier(@Nonnull IGraph<N> graph) {
+ this.graph = graph;
+ this.nodes = NodeListMarker.getNodeList(graph);
+ int szNodes = nodes.size();
+ for (N bb : nodes) {
+ DominanceFrontierInfoMarker.setDomInfo(bb, szNodes);
+ }
+ }
+
+ /**
+ * Calculates the dominance frontier information for the method.
+ */
+ @Nonnull
+ public void run() {
+ Dominators.make(graph, false);
+ buildDomTree();
+ calcDomFronts();
+ }
+
+ /**
+ * The dominators algorithm leaves us knowing who the immediate dominator is for each node. This
+ * sweeps the node list and builds the proper dominance tree.
+ */
+ private void buildDomTree() {
+ for (N node : nodes) {
+ N idom = DominanceFrontierInfoMarker.getIDom(node);
+ if (idom == null) {
+ continue;
+ }
+ DominatorTreeMarker.addDomChild(idom, node);
+ }
+ }
+
+ /**
+ * Calculates the dominance-frontier set. from "A Simple, Fast Dominance Algorithm" by Cooper,
+ * Harvey, and Kennedy; transliterated to Java.
+ */
+ private void calcDomFronts() {
+ for (N nb : nodes) {
+ //final DomInfo nbInfo = domInfos[getIdByNode(nb)];
+ final N nbIdom = DominanceFrontierInfoMarker.getIDom(nb);
+ int numPreds = 0;
+ for (@SuppressWarnings("unused") N pred : nb.getPredecessorsIterable()) {
+ numPreds++;
+ }
+
+ if (numPreds > 1) {
+ for (N pred : nb.getPredecessorsIterable()) {
+ for (N runner = pred; runner != nbIdom; /* empty */) {
+ /*
+ * We can stop if we hit a block we already added label to, since we must be at a part
+ * of the dom tree we have seen before.
+ */
+ if (runner == null) {
+ break;
+ }
+
+ if (DominanceFrontierInfoMarker.isInDominanceFrontier(runner, nb)) {
+ break;
+ }
+
+ // Add b to runner's dominance frontier set.
+ DominanceFrontierInfoMarker.addDominanceFrontier(runner, nb);
+ runner = DominanceFrontierInfoMarker.getIDom(runner);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/util/graph/DominanceFrontierInfoMarker.java b/jack/src/com/android/jack/util/graph/DominanceFrontierInfoMarker.java
new file mode 100644
index 0000000..5ead823
--- /dev/null
+++ b/jack/src/com/android/jack/util/graph/DominanceFrontierInfoMarker.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.util.graph;
+
+import com.android.jack.dx.util.BitIntSet;
+import com.android.jack.dx.util.IntSet;
+import com.android.jack.dx.util.ListIntSet;
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.sched.item.Description;
+import com.android.sched.marker.Marker;
+import com.android.sched.marker.ValidOn;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * A marker to store information computed by {@Link DominanceFrontier}.
+ */
+@Description("Dominance Frontier information for a node in a CFG.")
+@ValidOn(JBasicBlock.class)
+public class DominanceFrontierInfoMarker implements Marker {
+ /**
+ * BitIntSet/ListIntSet threshold for dominance frontier sets. These sets are kept per basic block
+ * until phi placement and tend to be, like the CFG itself, very sparse at large sizes.
+ *
+ * A value of 3072 here is somewhere around 1.125mb of total bitset size.
+ */
+ private static final int DOMFRONT_SET_THRESHOLD_SIZE = 3072;
+
+ @Nonnull
+ public final IntSet dominanceFrontiers;
+
+ /** {@code >= 0 after run();} the index of the immediate dominator */
+ @CheckForNull
+ public IGraphNode<?> idom = null;
+
+ private DominanceFrontierInfoMarker(@Nonnull int szBlocks) {
+ dominanceFrontiers =
+ szBlocks <= DOMFRONT_SET_THRESHOLD_SIZE ? new BitIntSet(szBlocks) : new ListIntSet();
+ }
+
+ @Nonnull
+ public static DominanceFrontierInfoMarker getDomInfo(JBasicBlock bb) {
+ DominanceFrontierInfoMarker marker = bb.getMarker(DominanceFrontierInfoMarker.class);
+ assert marker != null;
+ return marker;
+ }
+
+ public static <N extends IGraphNode<N>> void setDomInfo(N node, int size) {
+ DominanceFrontierInfoMarker marker = new DominanceFrontierInfoMarker(size);
+ node.addMarker(marker);
+ }
+
+ public static <N extends IGraphNode<N>> void setIDom(N parent, N idom) {
+ DominanceFrontierInfoMarker marker = parent.getMarker(DominanceFrontierInfoMarker.class);
+ assert marker != null;
+ marker.idom = idom;
+ }
+
+ @SuppressWarnings("unchecked")
+ public static <N extends IGraphNode<N>> N getIDom(N parent) {
+ DominanceFrontierInfoMarker marker = parent.getMarker(DominanceFrontierInfoMarker.class);
+ assert marker != null;
+ return (N) marker.idom;
+ }
+
+ public static <N extends IGraphNode<N>> boolean isInDominanceFrontier(N parent, N target) {
+ DominanceFrontierInfoMarker df = parent.getMarker(DominanceFrontierInfoMarker.class);
+ assert df != null;
+ int targetIdx = NodeIdMarker.getId(target);
+ assert df.dominanceFrontiers != null;
+ return df.dominanceFrontiers.has(targetIdx);
+ }
+
+ public static <N extends IGraphNode<N>> void addDominanceFrontier(N parent, N target) {
+ DominanceFrontierInfoMarker df = parent.getMarker(DominanceFrontierInfoMarker.class);
+ assert df != null;
+ int targetIdx = NodeIdMarker.getId(target);
+ assert df.dominanceFrontiers != null;
+ df.dominanceFrontiers.add(targetIdx);
+ }
+
+ @Override
+ public Marker cloneIfNeeded() {
+ throw new AssertionError("It is not valid to use cloneIfNeeded, create a new marker.");
+ }
+
+ /**
+ * Removes all dominance frontier info marker from a graph.
+ */
+ public static void clearMarkers(@Nonnull IGraph<?> graph) {
+ for (IGraphNode<?> n : graph.getNodes()) {
+ n.removeMarker(DominanceFrontierInfoMarker.class);
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/util/graph/DominatorDepthFirstTraversal.java b/jack/src/com/android/jack/util/graph/DominatorDepthFirstTraversal.java
new file mode 100644
index 0000000..848126d
--- /dev/null
+++ b/jack/src/com/android/jack/util/graph/DominatorDepthFirstTraversal.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.util.graph;
+
+import com.google.common.collect.ImmutableList;
+
+import java.util.BitSet;
+import java.util.List;
+import java.util.Stack;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A traversal that visits a directed graph by dominator order in a depth first manner.
+ */
+public class DominatorDepthFirstTraversal<N extends IGraphNode<N>> {
+
+ @Nonnull
+ private final IGraph<N> graph;
+
+ @Nonnull
+ private final GraphNodeVisitor<N> visitor;
+
+ @Nonnull
+ private final ImmutableList<N> nodes;
+
+ /**
+ * Visits each reachable node of the graph in a dominator order and invoke the visitor's visit
+ * method on it. Each visit call will have null as parent.
+ */
+ public static <N extends IGraphNode<N>> void run(@Nonnull IGraph<N> graph,
+ @Nonnull GraphNodeVisitor<N> visitor) {
+ DominatorDepthFirstTraversal<N> ddft = new DominatorDepthFirstTraversal<N>(graph, visitor);
+ ddft.run();
+ }
+
+ private DominatorDepthFirstTraversal(@Nonnull IGraph<N> graph,
+ @Nonnull GraphNodeVisitor<N> visitor) {
+ this.graph = graph;
+ this.visitor = visitor;
+ this.nodes = NodeListMarker.getNodeList(graph);
+ }
+
+ private void run() {
+ BitSet visited = new BitSet(nodes.size());
+ Stack<N> stack = new Stack<N>();
+
+ stack.add(graph.getEntryNode());
+
+ while (stack.size() > 0) {
+ N cur = stack.pop();
+
+ List<N> curDomChildren = DominatorTreeMarker.getDomChild(cur);
+
+ int curId = NodeIdMarker.getId(cur);
+ if (!visited.get(curId)) {
+ // We walk the tree this way for historical reasons...
+ for (int i = curDomChildren.size() - 1; i >= 0; i--) {
+ N child = curDomChildren.get(i);
+ stack.add(child);
+ }
+ visited.set(curId);
+ visitor.visitNode(cur, /* parent = */ null);
+ }
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/util/graph/DominatorTreeMarker.java b/jack/src/com/android/jack/util/graph/DominatorTreeMarker.java
new file mode 100644
index 0000000..5416039
--- /dev/null
+++ b/jack/src/com/android/jack/util/graph/DominatorTreeMarker.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.util.graph;
+
+import com.android.sched.item.Description;
+import com.android.sched.marker.Marker;
+import com.android.sched.marker.ValidOn;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A marker that represent a dominator tree in a graph.
+ */
+@Description("Marks the dominator parent / child relation of a CFG.")
+@ValidOn(IGraphNode.class)
+public class DominatorTreeMarker<N extends IGraphNode<N>> implements Marker {
+ private final List<N> domChildren = new ArrayList<>();
+
+ public static <N extends IGraphNode<N>> void addDomChild(N node, N domChild) {
+ @SuppressWarnings({"cast", "unchecked"})
+ DominatorTreeMarker<N> marker =
+ (DominatorTreeMarker<N>) node.getMarker(DominatorTreeMarker.class);
+ if (marker == null) {
+ marker = new DominatorTreeMarker<N>();
+ node.addMarker(marker);
+ }
+ marker.domChildren.add(domChild);
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ public static <N extends IGraphNode<N>> List<N> getDomChild(IGraphNode node) {
+ DominatorTreeMarker marker = node.getMarker(DominatorTreeMarker.class);
+ if (marker == null) {
+ return Collections.EMPTY_LIST;
+ } else {
+ return marker.domChildren;
+ }
+ }
+
+ @Override
+ public Marker cloneIfNeeded() {
+ throw new AssertionError("It is not valid to use cloneIfNeeded, create a new marker.");
+ }
+
+ /**
+ * Removes all dominance tree marker from a graph.
+ */
+ public static <N extends IGraphNode<N>> void clearMarkers(@Nonnull IGraph<N> graph) {
+ for (IGraphNode<?> n : graph.getNodes()) {
+ n.removeMarker(DominatorTreeMarker.class);
+ }
+ }
+}
diff --git a/jack/src/com/android/jack/util/graph/Dominators.java b/jack/src/com/android/jack/util/graph/Dominators.java
new file mode 100644
index 0000000..00bb88e
--- /dev/null
+++ b/jack/src/com/android/jack/util/graph/Dominators.java
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.util.graph;
+
+import com.google.common.collect.ImmutableList;
+
+import com.android.sched.schedulable.Constraint;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * This class computes dominator and post-dominator information using the Lengauer-Tarjan method.
+ *
+ * See A Fast Algorithm for Finding Dominators in a Flowgraph T. Lengauer & R. Tarjan, ACM TOPLAS
+ * July 1979, pgs 121-141.
+ *
+ * This implementation runs in time O(n log n). The time bound could be changed to O(n * ack(n))
+ * with a small change to the link and eval, and an addition of a child field to the DFS info. In
+ * reality, the constant overheads are high enough that the current method is faster in all but the
+ * strangest artificially constructed examples.
+ *
+ * The basic idea behind this algorithm is to perform a DFS walk, keeping track of various info
+ * about parents. We then use this info to calculate the dominators, using union-find structures to
+ * link together the DFS info, then finally evaluate the union-find results to get the dominators.
+ * This implementation is m log n because it does not perform union by rank to keep the union-find
+ * tree balanced.
+ */
+@Constraint(need = {NodeIdMarker.class, NodeListMarker.class})
+public final class Dominators<N extends IGraphNode<N>> {
+ /* postdom is true if we want post dominators */
+ private final boolean postdom;
+
+ private final IGraph<N> graph;
+
+ /* Method's basic blocks. */
+ @Nonnull
+ private final ImmutableList<N> nodes;
+
+ /** indexed by basic block index */
+ @Nonnull
+ private final DFSInfo<N>[] info;
+
+ @Nonnull
+ private final List<N> vertex;
+
+ /**
+ * Constructs an instance.
+ *
+ * @param graph {@code non-null;} Graph to process
+ * @param postdom true for postdom information, false for normal dom info
+ */
+ @SuppressWarnings("unchecked")
+ private Dominators(@Nonnull IGraph<N> graph, boolean postdom) {
+ this.graph = graph;
+ this.postdom = postdom;
+ this.nodes = NodeListMarker.getNodeList(graph);
+ this.info = new DFSInfo[nodes.size()]; // Plus implicit entry + exit.
+ this.vertex = new ArrayList<N>();
+ }
+
+ /**
+ * Constructs a fully-initialized instance. (This method exists so as to avoid calling a large
+ * amount of code in the constructor.)
+ *
+ * @param graph {@code non-null;} Control flow graph to process
+ * @param postdom true for postdom information, false for normal dom info
+ */
+ public static <N extends IGraphNode<N>> Dominators<N> make(@Nonnull IGraph<N> graph,
+ boolean postdom) {
+ Dominators<N> result = new Dominators<N>(graph, postdom);
+ result.run();
+ return result;
+ }
+
+ @Nonnull
+ private Iterable<N> getPreds(@Nonnull N block) {
+ if (postdom) {
+ return block.getSuccessorsIterable();
+ } else {
+ return block.getPredecessorsIterable();
+ }
+ }
+
+ @Nonnegative
+ private int getIdByNode(@Nonnull N n) {
+ return NodeIdMarker.getId(n);
+ }
+
+ /**
+ * Performs path compress on the DFS info.
+ *
+ * @param in Basic block whose DFS info we are path compressing.
+ */
+ private void compress(@Nonnull N in) {
+ DFSInfo<N> bbInfo = info[getIdByNode(in)];
+
+ assert bbInfo.ancestor != null;
+ DFSInfo<N> ancestorbbInfo = info[getIdByNode(bbInfo.ancestor)];
+
+ if (ancestorbbInfo.ancestor != null) {
+ ArrayList<N> worklist = new ArrayList<N>();
+ HashSet<N> visited = new HashSet<N>();
+ worklist.add(in);
+
+ while (!worklist.isEmpty()) {
+ int wsize = worklist.size();
+ N v = worklist.get(wsize - 1);
+ DFSInfo<N> vbbInfo = info[getIdByNode(v)];
+ N vAncestor = vbbInfo.ancestor;
+ assert vbbInfo.ancestor != null;
+ DFSInfo<N> vabbInfo = info[getIdByNode(vAncestor)];
+
+ // Make sure we process our ancestor before ourselves.
+ if (visited.add(vAncestor) && vabbInfo.ancestor != null) {
+ worklist.add(vAncestor);
+ continue;
+ }
+ worklist.remove(wsize - 1);
+
+ // Update based on ancestor info.
+ if (vabbInfo.ancestor == null) {
+ continue;
+ }
+ N vAncestorRep = vabbInfo.rep;
+ N vRep = vbbInfo.rep;
+ assert vRep != null;
+ if (info[getIdByNode(vAncestorRep)].semidom < info[getIdByNode(vRep)].semidom) {
+ vbbInfo.rep = vAncestorRep;
+ }
+ vbbInfo.ancestor = vabbInfo.ancestor;
+ }
+ }
+ }
+
+ @Nonnull
+ private N eval(@Nonnull N v) {
+ DFSInfo<N> bbInfo = info[getIdByNode(v)];
+
+ if (bbInfo.ancestor == null) {
+ return v;
+ }
+
+ compress(v);
+ assert bbInfo.rep != null;
+ return bbInfo.rep;
+ }
+
+ /**
+ * Performs dominator/post-dominator calculation for the control flow graph.
+ */
+ private void run() {
+ N root = postdom ? graph.getExitNode() : graph.getEntryNode();
+
+ vertex.add(root);
+ DominanceFrontierInfoMarker.setIDom(root, root);
+
+ /*
+ * First we perform a DFS numbering of the blocks, by numbering the dfs tree roots.
+ */
+ DepthFirstTraversal.<N>run(graph, postdom, new DfsWalker());
+
+ // the largest semidom number assigned
+ int dfsMax = vertex.size() - 1;
+
+ // Now calculate semidominators.
+ for (int i = dfsMax; i >= 2; --i) {
+ N w = vertex.get(i);
+ DFSInfo<N> wInfo = info[NodeIdMarker.getId(w)];
+
+ for (N predBlock : getPreds(w)) {
+ DFSInfo<N> predInfo = info[NodeIdMarker.getId(predBlock)];
+
+ /*
+ * PredInfo may not exist in case the predecessor is not reachable.
+ */
+ if (predInfo != null) {
+ int predSemidom = info[NodeIdMarker.getId(eval(predBlock))].semidom;
+ if (predSemidom < wInfo.semidom) {
+ wInfo.semidom = predSemidom;
+ }
+ }
+ }
+ info[NodeIdMarker.getId(vertex.get(wInfo.semidom))].bucket.add(w);
+
+ /*
+ * Normally we would call link here, but in our O(m log n) implementation this is equivalent
+ * to the following single line.
+ */
+ wInfo.ancestor = wInfo.parent;
+
+ // Implicity define idom for each vertex.
+ ArrayList<N> wParentBucket;
+ assert wInfo.parent != null;
+ wParentBucket = info[NodeIdMarker.getId(wInfo.parent)].bucket;
+
+ while (!wParentBucket.isEmpty()) {
+ int lastItem = wParentBucket.size() - 1;
+ N last = wParentBucket.remove(lastItem);
+ N u = eval(last);
+ DFSInfo<N> uDomInfo = info[NodeIdMarker.getId(u)];
+ DFSInfo<N> lastDomInfo = info[NodeIdMarker.getId(last)];
+ if (uDomInfo.semidom < lastDomInfo.semidom) {
+ DominanceFrontierInfoMarker.setIDom(last, u);
+ } else {
+ DominanceFrontierInfoMarker.setIDom(last, wInfo.parent);
+ }
+ }
+ }
+
+ // Now explicitly define the immediate dominator of each vertex
+ for (int i = 2; i <= dfsMax; ++i) {
+ N w = vertex.get(i);
+ if (DominanceFrontierInfoMarker.getIDom(w) != vertex
+ .get(info[NodeIdMarker.getId(w)].semidom)) {
+ DominanceFrontierInfoMarker.setIDom(w,
+ DominanceFrontierInfoMarker.getIDom(DominanceFrontierInfoMarker.getIDom(w)));
+ }
+ }
+ }
+
+ /**
+ * Callback for depth-first walk through control flow graph (either from the entry block or the
+ * exit block). Records the traversal order in the {@code info}list.
+ */
+ private class DfsWalker extends GraphNodeVisitor<N> {
+ @Nonnegative
+ private int dfsNum = 0;
+
+ @Override
+ public void visitNode(@Nonnull N v, @CheckForNull N parent) {
+ if (NodeIdMarker.getId(v) == Integer.MAX_VALUE) {
+ return;
+ }
+ DFSInfo<N> bbInfo = new DFSInfo<N>();
+ bbInfo.semidom = ++dfsNum;
+ bbInfo.rep = v;
+ bbInfo.parent = parent;
+ vertex.add(v);
+ info[NodeIdMarker.getId(v)] = bbInfo;
+ }
+ }
+
+ private static final class DFSInfo<N extends IGraphNode<N>> {
+ @Nonnegative
+ public int semidom;
+ @CheckForNull
+ public N parent;
+
+ /**
+ * rep(resentative) is known as "label" in the paper. It is the node that our block's DFS info
+ * has been unioned to.
+ */
+ @CheckForNull
+ public N rep;
+
+ @CheckForNull
+ public N ancestor;
+
+ @Nonnull
+ public final ArrayList<N> bucket = new ArrayList<N>();
+ }
+}
diff --git a/jack/src/com/android/jack/util/graph/Graph.java b/jack/src/com/android/jack/util/graph/Graph.java
index c1dc2d7..b3ef512 100644
--- a/jack/src/com/android/jack/util/graph/Graph.java
+++ b/jack/src/com/android/jack/util/graph/Graph.java
@@ -17,6 +17,7 @@
package com.android.jack.util.graph;
import com.android.jack.Jack;
+import com.android.sched.marker.LocalMarkerManager;
import java.util.ArrayList;
import java.util.List;
@@ -28,7 +29,7 @@
*
* @param <N> The type of node contained by the graph.
*/
-public class Graph<N extends GraphNode<N>> {
+public abstract class Graph<N extends GraphNode<N>> extends LocalMarkerManager implements IGraph<N>{
@Nonnull
private final List<N> nodes;
@@ -49,6 +50,7 @@
}
@Nonnull
+ @Override
public List<N> getNodes() {
return nodes;
}
@@ -57,6 +59,7 @@
* @return the entry
*/
@Nonnull
+ @Override
public N getEntryNode() {
return entry;
}
@@ -65,6 +68,7 @@
* @return the exit
*/
@Nonnull
+ @Override
public N getExitNode() {
return exit;
}
diff --git a/jack/src/com/android/jack/util/graph/GraphNode.java b/jack/src/com/android/jack/util/graph/GraphNode.java
index fe0f094..0a928c6 100644
--- a/jack/src/com/android/jack/util/graph/GraphNode.java
+++ b/jack/src/com/android/jack/util/graph/GraphNode.java
@@ -30,7 +30,8 @@
*
* @param <N> The type of graph node.
*/
-public abstract class GraphNode<N extends GraphNode<N>> extends LocalMarkerManager {
+public abstract class GraphNode<N extends GraphNode<N>> extends LocalMarkerManager
+ implements IGraphNode<N> {
// TODO(mikaelpeltier) Think about new implementation of sparse list due to index usage and
// append usage to fill successors.
@@ -89,4 +90,16 @@
public boolean removePredecessor(@Nonnull N predecessor) {
return (predecessors.remove(predecessor));
}
+
+ @Override
+ @Nonnull
+ public Iterable<N> getSuccessorsIterable() {
+ return getSuccessors();
+ }
+
+ @Override
+ @Nonnull
+ public Iterable<N> getPredecessorsIterable() {
+ return getPredecessors();
+ }
}
diff --git a/jack/src/com/android/jack/util/graph/GraphNodeVisitor.java b/jack/src/com/android/jack/util/graph/GraphNodeVisitor.java
new file mode 100644
index 0000000..95fcc92
--- /dev/null
+++ b/jack/src/com/android/jack/util/graph/GraphNodeVisitor.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.util.graph;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * Visitor abstract class for graph nodes.
+ */
+public abstract class GraphNodeVisitor<N extends IGraphNode<N>> {
+
+ /**
+ * Visits the current node with a possible parent node.
+ *
+ * @param v {@code non-null;} graph node visited
+ * @param parent {@code null-ok;} parent node if applicable
+ */
+ public abstract void visitNode(@Nonnull N v, @CheckForNull N parent);
+}
diff --git a/jack/src/com/android/jack/util/graph/IGraph.java b/jack/src/com/android/jack/util/graph/IGraph.java
new file mode 100644
index 0000000..8dcad49
--- /dev/null
+++ b/jack/src/com/android/jack/util/graph/IGraph.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.util.graph;
+
+import com.android.sched.marker.MarkerManager;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ * A interface for all abstract graph representation.
+ */
+public interface IGraph<N extends IGraphNode<N>> extends MarkerManager {
+
+ @Nonnull
+ public List<N> getNodes();
+
+ /**
+ * @return the entry
+ */
+ @Nonnull
+ public N getEntryNode();
+
+ /**
+ * @return the exit
+ */
+ @Nonnull
+ public N getExitNode();
+}
diff --git a/jack/src/com/android/jack/util/graph/IGraphNode.java b/jack/src/com/android/jack/util/graph/IGraphNode.java
new file mode 100644
index 0000000..77e3545
--- /dev/null
+++ b/jack/src/com/android/jack/util/graph/IGraphNode.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.util.graph;
+
+import com.android.sched.marker.MarkerManager;
+
+import javax.annotation.Nonnull;
+
+/**
+ *
+ */
+public interface IGraphNode<N extends IGraphNode<N>> extends MarkerManager {
+
+ @Nonnull
+ public abstract Iterable<N> getSuccessorsIterable();
+
+ @Nonnull
+ public abstract Iterable<N> getPredecessorsIterable();
+}
diff --git a/jack/src/com/android/jack/util/graph/NodeIdMarker.java b/jack/src/com/android/jack/util/graph/NodeIdMarker.java
new file mode 100644
index 0000000..61946c6
--- /dev/null
+++ b/jack/src/com/android/jack/util/graph/NodeIdMarker.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.util.graph;
+
+import com.android.jack.ir.ast.cfg.JBasicBlock;
+import com.android.sched.item.Description;
+import com.android.sched.marker.Marker;
+import com.android.sched.marker.MarkerManager;
+import com.android.sched.marker.ValidOn;
+
+import java.util.BitSet;
+
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * A {@link Marker} to represent a numeric ID for a basic block.
+ *
+ * The goal is to allow sets of blocks to be represented by {@link BitSet} by assigning a number to
+ * each basic block.
+ */
+@Description("A numeric ID for a node in a graph.")
+@ValidOn(JBasicBlock.class)
+public class NodeIdMarker implements Marker {
+ @Nonnegative
+ private final int id;
+
+ public NodeIdMarker(@Nonnegative int id) {
+ this.id = id;
+ }
+
+ @Nonnegative
+ public static int getId(@Nonnull MarkerManager node) {
+ NodeIdMarker marker = node.getMarker(NodeIdMarker.class);
+ assert marker != null;
+ return marker.id;
+ }
+
+ public static void setId(@Nonnull MarkerManager node, @Nonnegative int id) {
+ assert node.getMarker(NodeIdMarker.class) == null;
+ node.addMarker(new NodeIdMarker(id));
+ }
+
+ @Override
+ @Nonnull
+ public Marker cloneIfNeeded() {
+ return this;
+ }
+
+ /**
+ * Assign numberic IDs to a graph.
+ */
+ public static void assignIds(@Nonnull IGraph<?> graph) {
+ int index = 0;
+ for (IGraphNode<?> n : graph.getNodes()) {
+ NodeIdMarker.setId(n, index++);
+ }
+ }
+
+ /**
+ * Removes all numberic IDs from a graph.
+ */
+ public static void removeIds(@Nonnull IGraph<?> graph) {
+ for (IGraphNode<?> n : graph.getNodes()) {
+ n.removeMarker(NodeIdMarker.class);
+ }
+ }
+}
\ No newline at end of file
diff --git a/jack/src/com/android/jack/util/graph/NodeListMarker.java b/jack/src/com/android/jack/util/graph/NodeListMarker.java
new file mode 100644
index 0000000..3770b67
--- /dev/null
+++ b/jack/src/com/android/jack/util/graph/NodeListMarker.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.util.graph;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableList.Builder;
+
+import com.android.jack.ir.ast.cfg.JControlFlowGraph;
+import com.android.sched.item.Description;
+import com.android.sched.marker.Marker;
+import com.android.sched.marker.ValidOn;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+
+/**
+ *
+ */
+@Description("Assign a number to block map to a CFG.")
+@ValidOn(JControlFlowGraph.class)
+public class NodeListMarker implements Marker {
+
+ @SuppressWarnings("rawtypes")
+ private final ImmutableList<IGraphNode> orderedList;
+
+ public NodeListMarker(@SuppressWarnings("rawtypes") List<IGraphNode> orderedList) {
+ this.orderedList = ImmutableList.copyOf(orderedList);
+ }
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ @Nonnull
+ public static <N extends IGraphNode<N>> ImmutableList<N> getNodeList(@Nonnull IGraph<N> graph) {
+ NodeListMarker marker = graph.getMarker(NodeListMarker.class);
+ assert marker != null;
+ Builder<N> builder = ImmutableList.<N>builder();
+ for (IGraphNode node : marker.orderedList) {
+ builder.add((N) node);
+ }
+ return builder.build();
+ }
+
+ @Override
+ public Marker cloneIfNeeded() {
+ throw new AssertionError("It is not valid to use cloneIfNeeded, create a new marker.");
+ }
+
+ @SuppressWarnings("unchecked")
+ public static void assignNodeList(@SuppressWarnings("rawtypes") @Nonnull IGraph graph) {
+ assert graph.getMarker(NodeListMarker.class) == null;
+ graph.addMarker(new NodeListMarker(graph.getNodes()));
+ }
+
+ public static void removeNodeList(@SuppressWarnings("rawtypes") @Nonnull IGraph graph) {
+ graph.removeMarker(NodeListMarker.class);
+ }
+}
diff --git a/jack/tests/com/android/jack/UnaryTest.java b/jack/tests/com/android/jack/UnaryTest.java
index a70e61f..669648a 100644
--- a/jack/tests/com/android/jack/UnaryTest.java
+++ b/jack/tests/com/android/jack/UnaryTest.java
@@ -16,9 +16,9 @@
package com.android.jack;
-import com.android.jack.ir.ast.JIfStatement;
import com.android.jack.ir.ast.JMethod;
import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.ast.cfg.JConditionalBlockElement;
import com.android.jack.optimizations.Optimizations;
import com.android.sched.util.config.ThreadConfig;
@@ -84,7 +84,7 @@
private int countIf = 0;
@Override
- public boolean visit(@Nonnull JIfStatement x) {
+ public boolean visit(@Nonnull JConditionalBlockElement x) {
countIf++;
return super.visit(x);
}
diff --git a/jack/tests/com/android/jack/cfg/BaseGraphTestCase.java b/jack/tests/com/android/jack/cfg/BaseGraphTestCase.java
new file mode 100644
index 0000000..c4aed15
--- /dev/null
+++ b/jack/tests/com/android/jack/cfg/BaseGraphTestCase.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.cfg;
+
+import com.google.common.collect.Lists;
+
+import com.android.jack.Options;
+import com.android.jack.util.graph.IGraph;
+import com.android.jack.util.graph.IGraphNode;
+import com.android.jack.util.graph.NodeIdMarker;
+import com.android.jack.util.graph.NodeListMarker;
+import com.android.sched.marker.LocalMarkerManager;
+import com.android.sched.marker.Marker;
+import com.android.sched.util.RunnableHooks;
+import com.android.sched.util.config.Config;
+import com.android.sched.util.config.ThreadConfig;
+
+import junit.framework.Assert;
+
+import org.junit.After;
+import org.junit.Before;
+
+import java.util.List;
+
+import javax.annotation.Nonnegative;
+import javax.annotation.Nonnull;
+
+/**
+ * Utilities to conduct some unit tests on a simple graph.
+ *
+ */
+public abstract class BaseGraphTestCase {
+
+ /**
+ * Creates a graph for testing purposes.
+ */
+ protected static TestGraph makeGraph(@Nonnegative int nNodes) {
+ List<TestNode> nodes = Lists.newArrayList();
+ for (int i = 0; i < nNodes; i++) {
+ nodes.add(new TestNode(i));
+ }
+ TestNode entry = new TestNode(nNodes);
+ TestNode exit = new TestNode(nNodes + 1);
+ nodes.add(entry);
+ nodes.add(exit);
+ TestGraph graph = new TestGraph(entry, exit, nodes);
+ NodeIdMarker.assignIds(graph);
+ NodeListMarker.assignNodeList(graph);
+ return graph;
+ }
+
+ /**
+ * Simple node type that represents a number which is same as the node's label.
+ *
+ * Note that this label is purely for testing purpose and it is totally independent on any sort
+ * of "ID" a graph or graph algorithm use for traversal and other computations.
+ */
+ protected static class TestNode extends LocalMarkerManager implements IGraphNode<TestNode> {
+ private final int label;
+ private final List<TestNode> predecessors = Lists.newArrayList();
+ private final List<TestNode> successors = Lists.newArrayList();
+
+ public TestNode(@Nonnegative int label) {
+ this.label = label;
+ }
+
+ /**
+ * For testing purposing, everything is a valid marker.
+ */
+ @Override
+ protected boolean isValidMarker(@Nonnull Marker marker) {
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "" + label;
+ }
+
+ @Override
+ public Iterable<TestNode> getSuccessorsIterable() {
+ return successors;
+ }
+
+ @Override
+ @Nonnull
+ public Iterable<TestNode> getPredecessorsIterable() {
+ return predecessors;
+ }
+ }
+
+ /**
+ * Simple graph data structure for testing.
+ */
+ protected static class TestGraph extends LocalMarkerManager implements IGraph<TestNode> {
+ private final List<TestNode> nodes;
+ private final @Nonnull TestNode entry;
+ private final @Nonnull TestNode exit;
+ protected TestGraph(@Nonnull TestNode entry, @Nonnull TestNode exit,
+ @Nonnull List<TestNode> nodes) {
+ this.nodes = nodes;
+ this.entry = entry;
+ this.exit = exit;
+ }
+
+ /**
+ * For testing purposing, everything is a valid marker.
+ */
+ @Override
+ protected boolean isValidMarker(@Nonnull Marker marker) {
+ return true;
+ }
+
+ @Nonnull
+ protected TestNode getNode(@Nonnegative int index) {
+ return nodes.get(index);
+ }
+
+ protected void connect(@Nonnegative int from, @Nonnegative int to) {
+ TestNode fromNode = getNode(from);
+ TestNode toNode = getNode(to);
+ connect(fromNode, toNode);
+ }
+
+ protected void connect(@Nonnegative TestNode fromNode, @Nonnegative TestNode toNode) {
+ fromNode.successors.add(toNode);
+ toNode.predecessors.add(fromNode);
+ }
+
+ protected void assertConnected(@Nonnegative int from, @Nonnegative int to) {
+ TestNode fromNode = getNode(from);
+ TestNode toNode = getNode(to);
+ Assert.assertTrue(fromNode.successors.contains(toNode));
+ Assert.assertTrue(toNode.predecessors.contains(fromNode));
+ }
+
+ protected void assertConnected(@Nonnull TestNode fromNode, @Nonnull TestNode toNode) {
+ Assert.assertTrue(fromNode.successors.contains(toNode));
+ Assert.assertTrue(toNode.predecessors.contains(fromNode));
+ }
+
+ protected void assertNotConnected(@Nonnegative int from, @Nonnegative int to) {
+ TestNode fromNode = getNode(from);
+ TestNode toNode = getNode(to);
+ Assert.assertFalse(fromNode.successors.contains(toNode));
+ Assert.assertFalse(toNode.predecessors.contains(fromNode));
+ }
+
+ @Override
+ @Nonnull
+ public List<TestNode> getNodes() {
+ return nodes;
+ }
+
+ @Override
+ @Nonnull
+ public TestNode getEntryNode() {
+ return entry;
+ }
+
+ @Override
+ @Nonnull
+ public TestNode getExitNode() {
+ return exit;
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ Options options = new Options();
+ RunnableHooks hooks = new RunnableHooks();
+ options.checkValidity(hooks);
+ Config config = options.getConfig();
+ ThreadConfig.setConfig(config);
+ }
+
+ @After
+ public void tearDown() {
+ ThreadConfig.unsetConfig();
+ }
+}
\ No newline at end of file
diff --git a/jack/tests/com/android/jack/cfg/DepthFirstTraversalTest.java b/jack/tests/com/android/jack/cfg/DepthFirstTraversalTest.java
new file mode 100644
index 0000000..9d00071
--- /dev/null
+++ b/jack/tests/com/android/jack/cfg/DepthFirstTraversalTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.cfg;
+
+import com.android.jack.util.graph.DepthFirstTraversal;
+import com.android.jack.util.graph.GraphNodeVisitor;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * Tests for basic depth first traversal.
+ */
+public class DepthFirstTraversalTest extends BaseGraphTestCase {
+
+ @Test
+ public void dft1() throws Exception {
+ // 0
+ // / \
+ // 1 2
+ // | |
+ // 3 /
+ // | /
+ // 4
+ //
+ // 1 is the idom of 3
+ // 4 is in the dominance frontier of 4
+ TestGraph graph = makeGraph(6);
+ graph.connect(graph.getEntryNode(), graph.getNodes().get(0));
+ graph.connect(0, 1);
+ graph.connect(0, 2);
+ graph.connect(1, 3);
+ graph.connect(3, 4);
+ graph.connect(2, 4);
+ graph.connect(graph.getNodes().get(4), graph.getExitNode());
+ Assert.assertEquals("02413", doDepthFirstTraversal(graph, false));
+ }
+
+ @Test
+ public void dftReverse1() throws Exception {
+ // 0
+ // / \
+ // 1 2
+ // | |
+ // 3 /
+ // | /
+ // 4
+ //
+ TestGraph graph = makeGraph(5);
+ graph.connect(graph.getEntryNode(), graph.getNodes().get(0));
+ graph.connect(0, 1);
+ graph.connect(0, 2);
+ graph.connect(1, 3);
+ graph.connect(3, 4);
+ graph.connect(2, 4);
+ graph.connect(graph.getNodes().get(4), graph.getExitNode());
+
+ Assert.assertEquals("42031", doDepthFirstTraversal(graph, true));
+ }
+
+ @Test
+ public void dftReverse2() throws Exception {
+ // 0
+ // / \
+ // 1 2
+ // |
+ // 3
+ // |
+ // 4
+ TestGraph graph = makeGraph(5);
+ graph.connect(graph.getEntryNode(), graph.getNodes().get(0));
+ graph.connect(0, 1);
+ graph.connect(0, 2);
+ graph.connect(1, 3);
+ graph.connect(3, 4);
+ graph.connect(graph.getNodes().get(4), graph.getExitNode());
+ graph.connect(graph.getNodes().get(2), graph.getExitNode());
+ Assert.assertEquals("20431", doDepthFirstTraversal(graph, true));
+ }
+
+ @Nonnull
+ private static String doDepthFirstTraversal(final TestGraph graph, boolean reverse) {
+ final StringBuilder sb = new StringBuilder();
+ DepthFirstTraversal.run(graph, reverse, new GraphNodeVisitor<TestNode>() {
+ @Override
+ public void visitNode(@Nonnull TestNode v, @CheckForNull TestNode parent) {
+ if (v != graph.getEntryNode() && v != graph.getExitNode()) {
+ sb.append(v);
+ }
+ }
+ });
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/jack/tests/com/android/jack/cfg/DominaceFrontierTest.java b/jack/tests/com/android/jack/cfg/DominaceFrontierTest.java
new file mode 100644
index 0000000..7820003
--- /dev/null
+++ b/jack/tests/com/android/jack/cfg/DominaceFrontierTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.cfg;
+
+import com.android.jack.util.graph.DominanceFrontier;
+import com.android.jack.util.graph.DominanceFrontierInfoMarker;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+/**
+ * Some basic tests for {@link DominanceFrontier}
+ */
+public class DominaceFrontierTest extends BaseGraphTestCase {
+ private TestGraph graph = null;
+
+ @Test
+ public void df1()throws Exception {
+ // 0
+ // / \
+ // 1 2
+ // | |
+ // 3 /
+ // | /
+ // 4
+ //
+ // 1 is the idom of 3
+ // 4 is in the dominance frontier of 4
+ graph = makeGraph(5);
+ graph.connect(graph.getEntryNode(), graph.getNodes().get(0));
+ graph.connect(0, 1);
+ graph.connect(0, 2);
+ graph.connect(1, 3);
+ graph.connect(3, 4);
+ graph.connect(2, 4);
+ graph.connect(graph.getNodes().get(4), graph.getExitNode());
+ new DominanceFrontier<TestNode>(graph).run();
+
+ // The only DF set that exists. Everything else should be empty.
+ Assert.assertTrue(isInDominanceFrontier(1, 4));
+ for (int i = 0; i < 5; i++) {
+ for (int j = 0; j < 5; j++) {
+ if (i != 1 && j != 4) {
+ Assert.assertFalse(isInDominanceFrontier(i, j));
+ }
+ }
+ }
+ }
+
+ @Test
+ public void df2() throws Exception {
+ // __0__
+ // / | \
+ // 1 2 3
+ // | \ |
+ // 4 5 6
+ // | | /
+ // | / | /
+ // 7 8
+ //
+ graph = makeGraph(9);
+ graph.connect(graph.getEntryNode(), graph.getNodes().get(0));
+
+ // level 1
+ graph.connect(0, 1);
+ graph.connect(0, 2);
+ graph.connect(0, 3);
+
+ // level 2
+ graph.connect(1, 4);
+ graph.connect(1, 5);
+ graph.connect(3, 6);
+
+ // level 3
+ graph.connect(4, 7);
+ graph.connect(5, 7);
+ graph.connect(5, 8);
+ graph.connect(6, 8);
+
+ graph.connect(graph.getNodes().get(7), graph.getExitNode());
+ graph.connect(graph.getNodes().get(8), graph.getExitNode());
+
+ new DominanceFrontier<TestNode>(graph).run();
+
+ // 8 is in the DF of 1 but not 7 because 1 is still dominating 7.
+ Assert.assertTrue(isInDominanceFrontier(1, 8));
+ Assert.assertFalse(isInDominanceFrontier(1, 7));
+ }
+
+ private boolean isInDominanceFrontier(int parent, int target) {
+ TestNode parentNode = graph.getNodes().get(parent);
+ TestNode targetNode = graph.getNodes().get(target);
+ return DominanceFrontierInfoMarker.isInDominanceFrontier(parentNode, targetNode);
+ }
+}
diff --git a/jack/tests/com/android/jack/cfg/DominatorDepthFirstTraversalTest.java b/jack/tests/com/android/jack/cfg/DominatorDepthFirstTraversalTest.java
new file mode 100644
index 0000000..9cf7811
--- /dev/null
+++ b/jack/tests/com/android/jack/cfg/DominatorDepthFirstTraversalTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.jack.cfg;
+
+import com.android.jack.util.graph.DominanceFrontier;
+import com.android.jack.util.graph.DominatorDepthFirstTraversal;
+import com.android.jack.util.graph.GraphNodeVisitor;
+
+import junit.framework.Assert;
+
+import org.junit.Test;
+
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+/**
+ * Tests for {@link DominatorDepthFirstTraversal}.
+ */
+public class DominatorDepthFirstTraversalTest extends BaseGraphTestCase {
+ @Test
+ public void ddft1() throws Exception {
+ // 0
+ // / \
+ // 1 2
+ // | |
+ // 3 /
+ // | /
+ // 4
+ //
+ TestGraph graph = makeGraph(5);
+ graph.connect(graph.getEntryNode(), graph.getNodes().get(0));
+ graph.connect(0, 1);
+ graph.connect(0, 2);
+ graph.connect(1, 3);
+ graph.connect(3, 4);
+ graph.connect(2, 4);
+ graph.connect(graph.getNodes().get(4), graph.getExitNode());
+ Assert.assertEquals("01324", doDominatorDepthFirstTraversal(graph));
+ }
+
+ @Test
+ public void ddft2() throws Exception {
+ // 0
+ // /
+ // 1
+ // | \
+ // 2 3
+ // | \
+ // 4 5
+ // | /
+ // | /
+ // 6
+ TestGraph graph = makeGraph(7);
+ graph.connect(graph.getEntryNode(), graph.getNodes().get(0));
+ graph.connect(0, 1);
+ graph.connect(1, 2);
+ graph.connect(1, 3);
+ graph.connect(2, 4);
+ graph.connect(3, 5);
+ graph.connect(4, 6);
+ graph.connect(5, 6);
+ graph.connect(graph.getNodes().get(6), graph.getExitNode());
+ Assert.assertEquals("0124356", doDominatorDepthFirstTraversal(graph));
+ }
+
+ @Test
+ public void ddft3() throws Exception {
+ // This creates a loop.
+ // 0 -> 1 -> 2 -> 1.
+ TestGraph graph = makeGraph(7);
+ graph.connect(graph.getEntryNode(), graph.getNodes().get(0));
+ graph.connect(0, 1);
+ graph.connect(1, 2);
+ graph.connect(2, 1);
+ Assert.assertEquals("012", doDominatorDepthFirstTraversal(graph));
+ }
+
+ @Nonnull
+ private static String doDominatorDepthFirstTraversal(final TestGraph graph) {
+ new DominanceFrontier<>(graph).run();
+ final StringBuilder sb = new StringBuilder();
+ DominatorDepthFirstTraversal.run(graph, new GraphNodeVisitor<TestNode>() {
+ @Override
+ public void visitNode(@Nonnull TestNode v, @CheckForNull TestNode parent) {
+ // Parent is always null since it is meaning less in this context.
+ Assert.assertNull(parent);
+ if (v != graph.getEntryNode() && v != graph.getExitNode()) {
+ sb.append(v);
+ }
+ }
+ });
+ return sb.toString();
+ }
+}
\ No newline at end of file
diff --git a/jack/tests/com/android/jack/optimizations/tailrecursion/TailRecursionTest.java b/jack/tests/com/android/jack/optimizations/tailrecursion/TailRecursionTest.java
index 04061e9..0b05576 100644
--- a/jack/tests/com/android/jack/optimizations/tailrecursion/TailRecursionTest.java
+++ b/jack/tests/com/android/jack/optimizations/tailrecursion/TailRecursionTest.java
@@ -18,11 +18,11 @@
import com.android.jack.Options;
import com.android.jack.TestTools;
-import com.android.jack.ir.ast.JGoto;
import com.android.jack.ir.ast.JMethod;
-import com.android.jack.ir.ast.JReturnStatement;
-import com.android.jack.ir.ast.JStatement;
import com.android.jack.ir.ast.JVisitor;
+import com.android.jack.ir.ast.cfg.JBasicBlockElement;
+import com.android.jack.ir.ast.cfg.JGotoBlockElement;
+import com.android.jack.ir.ast.cfg.JReturnBlockElement;
import com.android.jack.util.filter.SignatureMethodFilter;
import junit.framework.Assert;
@@ -55,31 +55,31 @@
JMethod m =
getJMethodAfterTailRecursionOptimization(TestTools.getJackTestFromBinaryName(classBinaryName), "L"
+ classBinaryName + ";", methodSignature);
- StatementsCounter counter1 = new StatementsCounter(JReturnStatement.class);
+ Counter counter1 = new Counter(JReturnBlockElement.class);
counter1.accept(m);
Assert.assertEquals(1, counter1.getCounter());
- StatementsCounter counter2 = new StatementsCounter(JGoto.class);
+ Counter counter2 = new Counter(JGotoBlockElement.class);
counter2.accept(m);
Assert.assertEquals(1, counter2.getCounter());
}
- private static class StatementsCounter extends JVisitor {
+ private static class Counter extends JVisitor {
private int counter = 0;
@Nonnull
- private final Class<? extends JStatement> clazz;
+ private final Class<? extends JBasicBlockElement> clazz;
- public StatementsCounter(Class<? extends JStatement> clazz) {
+ public Counter(@Nonnull Class<? extends JBasicBlockElement> clazz) {
this.clazz = clazz;
}
@Override
- public boolean visit(@Nonnull JStatement stmt) {
- if (clazz.isInstance(stmt)) {
+ public boolean visit(@Nonnull JBasicBlockElement element) {
+ if (clazz.isInstance(element)) {
counter++;
}
- return super.visit(stmt);
+ return super.visit(element);
}
public int getCounter() {
diff --git a/jack/tests/com/android/jack/util/GraphUtilsTests.java b/jack/tests/com/android/jack/util/GraphUtilsTests.java
index 3ee7e11..fb2e238 100644
--- a/jack/tests/com/android/jack/util/GraphUtilsTests.java
+++ b/jack/tests/com/android/jack/util/GraphUtilsTests.java
@@ -339,7 +339,7 @@
assert exitNode != null;
return new Graph<GraphUtilsTests.TestNode>(entryNode, exitNode,
- new ArrayList<>(idToNode.values()));
+ new ArrayList<>(idToNode.values())) { /* anonymous */ };
}
private static class TestNode extends GraphNode<TestNode> {
diff --git a/server/jack-server/etc/jack b/server/jack-server/etc/jack
index 75aed94..99f6c18 100755
--- a/server/jack-server/etc/jack
+++ b/server/jack-server/etc/jack
@@ -22,7 +22,7 @@
#
# Settings
#
-JACK_VERSION=${JACK_VERSION:=4.0.ENGINEERING}
+JACK_VERSION=${JACK_VERSION:=-2.0.ENGINEERING}
JACK_HOME="${JACK_HOME:=$HOME/.jack-server}"
JACK_CLIENT_SETTING="${JACK_CLIENT_SETTING:=$HOME/.jack-settings}"
TMPDIR=${TMPDIR:=/tmp}
diff --git a/version.properties b/version.properties
index 12380b0..13a3c2a 100644
--- a/version.properties
+++ b/version.properties
@@ -15,9 +15,9 @@
#
version-file.version.code=2
-version=1.3
-version.release.name=Douarn
-version.release.code=4
+version=-2.0
+version.release.name=Douarn 1.3-b6 + OptimDev
+version.release.code=-2
version.sub-release.kind=ENGINEERING
version.sub-release.code=0