Merge "Rewrite null check elimination to work on dalvik regs."
diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h
index fce23bc..3b1be51 100644
--- a/compiler/dex/bb_optimizations.h
+++ b/compiler/dex/bb_optimizations.h
@@ -143,7 +143,7 @@
class NullCheckElimination : public PassME {
public:
NullCheckElimination()
- : PassME("NCE", kRepeatingTopologicalSortTraversal, "3_post_nce_cfg") {
+ : PassME("NCE", kRepeatingPreOrderDFSTraversal, "3_post_nce_cfg") {
}
bool Gate(const PassDataHolder* data) const {
@@ -195,7 +195,7 @@
class ClassInitCheckElimination : public PassME {
public:
ClassInitCheckElimination()
- : PassME("ClInitCheckElimination", kLoopRepeatingTopologicalSortTraversal) {
+ : PassME("ClInitCheckElimination", kRepeatingPreOrderDFSTraversal) {
}
bool Gate(const PassDataHolder* data) const {
diff --git a/compiler/dex/mir_dataflow.cc b/compiler/dex/mir_dataflow.cc
index 51b6709..6d3f229 100644
--- a/compiler/dex/mir_dataflow.cc
+++ b/compiler/dex/mir_dataflow.cc
@@ -118,10 +118,10 @@
DF_DA | DF_REF_A | DF_NON_NULL_DST,
// 1D MONITOR_ENTER vAA
- DF_UA | DF_NULL_CHK_0 | DF_REF_A,
+ DF_UA | DF_NULL_CHK_A | DF_REF_A,
// 1E MONITOR_EXIT vAA
- DF_UA | DF_NULL_CHK_0 | DF_REF_A,
+ DF_UA | DF_NULL_CHK_A | DF_REF_A,
// 1F CHK_CAST vAA, type@BBBB
DF_UA | DF_REF_A | DF_UMS,
@@ -130,7 +130,7 @@
DF_DA | DF_UB | DF_CORE_A | DF_REF_B | DF_UMS,
// 21 ARRAY_LENGTH vA, vB
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_CORE_A | DF_REF_B,
+ DF_DA | DF_UB | DF_NULL_CHK_B | DF_CORE_A | DF_REF_B,
// 22 NEW_INSTANCE vAA, type@BBBB
DF_DA | DF_NON_NULL_DST | DF_REF_A | DF_UMS,
@@ -235,88 +235,88 @@
DF_NOP,
// 44 AGET vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
// 45 AGET_WIDE vAA, vBB, vCC
- DF_DA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_DA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
// 46 AGET_OBJECT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN,
// 47 AGET_BOOLEAN vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
// 48 AGET_BYTE vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
// 49 AGET_CHAR vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
// 4A AGET_SHORT vAA, vBB, vCC
- DF_DA | DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_DA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
// 4B APUT vAA, vBB, vCC
- DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
// 4C APUT_WIDE vAA, vBB, vCC
- DF_UA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_2 | DF_RANGE_CHK_3 | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_UA | DF_A_WIDE | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
// 4D APUT_OBJECT vAA, vBB, vCC
- DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_A | DF_REF_B | DF_CORE_C | DF_LVN,
// 4E APUT_BOOLEAN vAA, vBB, vCC
- DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
// 4F APUT_BYTE vAA, vBB, vCC
- DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
// 50 APUT_CHAR vAA, vBB, vCC
- DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
// 51 APUT_SHORT vAA, vBB, vCC
- DF_UA | DF_UB | DF_UC | DF_NULL_CHK_1 | DF_RANGE_CHK_2 | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_UA | DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
// 52 IGET vA, vB, field@CCCC
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// 53 IGET_WIDE vA, vB, field@CCCC
- DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// 54 IGET_OBJECT vA, vB, field@CCCC
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
// 55 IGET_BOOLEAN vA, vB, field@CCCC
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// 56 IGET_BYTE vA, vB, field@CCCC
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// 57 IGET_CHAR vA, vB, field@CCCC
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// 58 IGET_SHORT vA, vB, field@CCCC
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// 59 IPUT vA, vB, field@CCCC
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// 5A IPUT_WIDE vA, vB, field@CCCC
- DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// 5B IPUT_OBJECT vA, vB, field@CCCC
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
// 5C IPUT_BOOLEAN vA, vB, field@CCCC
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// 5D IPUT_BYTE vA, vB, field@CCCC
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// 5E IPUT_CHAR vA, vB, field@CCCC
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// 5F IPUT_SHORT vA, vB, field@CCCC
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// 60 SGET vAA, field@BBBB
DF_DA | DF_SFIELD | DF_UMS,
@@ -712,10 +712,10 @@
DF_DA | DF_UB | DF_CORE_A | DF_CORE_B,
// E3 IGET_VOLATILE
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// E4 IPUT_VOLATILE
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// E5 SGET_VOLATILE
DF_DA | DF_SFIELD | DF_UMS,
@@ -724,13 +724,13 @@
DF_UA | DF_SFIELD | DF_UMS,
// E7 IGET_OBJECT_VOLATILE
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_DA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
// E8 IGET_WIDE_VOLATILE
- DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// E9 IPUT_WIDE_VOLATILE
- DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_REF_B | DF_IFIELD | DF_LVN,
// EA SGET_WIDE_VOLATILE
DF_DA | DF_A_WIDE | DF_SFIELD | DF_UMS,
@@ -751,28 +751,28 @@
DF_FORMAT_3RC,
// F0 INVOKE_OBJECT_INIT_RANGE
- DF_NOP | DF_NULL_CHK_0,
+ DF_NOP,
// F1 RETURN_VOID_BARRIER
DF_NOP,
// F2 IGET_QUICK
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_IFIELD | DF_LVN,
+ DF_DA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
// F3 IGET_WIDE_QUICK
- DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_0 | DF_IFIELD | DF_LVN,
+ DF_DA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
// F4 IGET_OBJECT_QUICK
- DF_DA | DF_UB | DF_NULL_CHK_0 | DF_IFIELD | DF_LVN,
+ DF_DA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
// F5 IPUT_QUICK
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_IFIELD | DF_LVN,
+ DF_UA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
// F6 IPUT_WIDE_QUICK
- DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_2 | DF_IFIELD | DF_LVN,
+ DF_UA | DF_A_WIDE | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
// F7 IPUT_OBJECT_QUICK
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_IFIELD | DF_LVN,
+ DF_UA | DF_UB | DF_NULL_CHK_B | DF_IFIELD | DF_LVN,
// F8 INVOKE_VIRTUAL_QUICK
DF_FORMAT_35C | DF_NULL_CHK_OUT0 | DF_UMS,
@@ -787,7 +787,7 @@
DF_FORMAT_3RC | DF_NULL_CHK_OUT0 | DF_UMS,
// FC IPUT_OBJECT_VOLATILE
- DF_UA | DF_UB | DF_NULL_CHK_1 | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
+ DF_UA | DF_UB | DF_NULL_CHK_B | DF_REF_A | DF_REF_B | DF_IFIELD | DF_LVN,
// FD SGET_OBJECT_VOLATILE
DF_DA | DF_REF_A | DF_SFIELD | DF_UMS,
@@ -824,7 +824,7 @@
DF_NOP,
// 108 MIR_NULL_CHECK
- DF_UA | DF_REF_A | DF_NULL_CHK_0 | DF_LVN,
+ DF_UA | DF_REF_A | DF_NULL_CHK_A | DF_LVN,
// 109 MIR_RANGE_CHECK
0,
@@ -893,10 +893,10 @@
0,
// 11F MirOpPackedArrayGet
- DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
// 120 MirOpPackedArrayPut
- DF_UB | DF_UC | DF_NULL_CHK_0 | DF_RANGE_CHK_1 | DF_REF_B | DF_CORE_C | DF_LVN,
+ DF_UB | DF_UC | DF_NULL_CHK_B | DF_RANGE_CHK_C | DF_REF_B | DF_CORE_C | DF_LVN,
};
/* Return the base virtual register for a SSA name */
diff --git a/compiler/dex/mir_field_info.h b/compiler/dex/mir_field_info.h
index 9745c41..1842a16 100644
--- a/compiler/dex/mir_field_info.h
+++ b/compiler/dex/mir_field_info.h
@@ -137,6 +137,7 @@
// The member offset of the field, 0u if unresolved.
MemberOffset field_offset_;
+ friend class NullCheckEliminationTest;
friend class GlobalValueNumberingTest;
friend class LocalValueNumberingTest;
};
diff --git a/compiler/dex/mir_graph.h b/compiler/dex/mir_graph.h
index 90cdded..a7069b0 100644
--- a/compiler/dex/mir_graph.h
+++ b/compiler/dex/mir_graph.h
@@ -49,17 +49,14 @@
kFormat35c,
kFormat3rc,
kFormatExtended, // Extended format for extended MIRs.
- kNullCheckSrc0, // Null check of uses[0].
- kNullCheckSrc1, // Null check of uses[1].
- kNullCheckSrc2, // Null check of uses[2].
+ kNullCheckA, // Null check of A.
+ kNullCheckB, // Null check of B.
kNullCheckOut0, // Null check out outgoing arg0.
kDstNonNull, // May assume dst is non-null.
kRetNonNull, // May assume retval is non-null.
kNullTransferSrc0, // Object copy src[0] -> dst.
kNullTransferSrcN, // Phi null check state transfer.
- kRangeCheckSrc1, // Range check of uses[1].
- kRangeCheckSrc2, // Range check of uses[2].
- kRangeCheckSrc3, // Range check of uses[3].
+ kRangeCheckC, // Range check of C.
kFPA,
kFPB,
kFPC,
@@ -88,17 +85,14 @@
#define DF_FORMAT_35C (UINT64_C(1) << kFormat35c)
#define DF_FORMAT_3RC (UINT64_C(1) << kFormat3rc)
#define DF_FORMAT_EXTENDED (UINT64_C(1) << kFormatExtended)
-#define DF_NULL_CHK_0 (UINT64_C(1) << kNullCheckSrc0)
-#define DF_NULL_CHK_1 (UINT64_C(1) << kNullCheckSrc1)
-#define DF_NULL_CHK_2 (UINT64_C(1) << kNullCheckSrc2)
+#define DF_NULL_CHK_A (UINT64_C(1) << kNullCheckA)
+#define DF_NULL_CHK_B (UINT64_C(1) << kNullCheckB)
#define DF_NULL_CHK_OUT0 (UINT64_C(1) << kNullCheckOut0)
#define DF_NON_NULL_DST (UINT64_C(1) << kDstNonNull)
#define DF_NON_NULL_RET (UINT64_C(1) << kRetNonNull)
#define DF_NULL_TRANSFER_0 (UINT64_C(1) << kNullTransferSrc0)
#define DF_NULL_TRANSFER_N (UINT64_C(1) << kNullTransferSrcN)
-#define DF_RANGE_CHK_1 (UINT64_C(1) << kRangeCheckSrc1)
-#define DF_RANGE_CHK_2 (UINT64_C(1) << kRangeCheckSrc2)
-#define DF_RANGE_CHK_3 (UINT64_C(1) << kRangeCheckSrc3)
+#define DF_RANGE_CHK_C (UINT64_C(1) << kRangeCheckC)
#define DF_FP_A (UINT64_C(1) << kFPA)
#define DF_FP_B (UINT64_C(1) << kFPB)
#define DF_FP_C (UINT64_C(1) << kFPC)
@@ -117,14 +111,11 @@
#define DF_HAS_DEFS (DF_DA)
-#define DF_HAS_NULL_CHKS (DF_NULL_CHK_0 | \
- DF_NULL_CHK_1 | \
- DF_NULL_CHK_2 | \
+#define DF_HAS_NULL_CHKS (DF_NULL_CHK_A | \
+ DF_NULL_CHK_B | \
DF_NULL_CHK_OUT0)
-#define DF_HAS_RANGE_CHKS (DF_RANGE_CHK_1 | \
- DF_RANGE_CHK_2 | \
- DF_RANGE_CHK_3)
+#define DF_HAS_RANGE_CHKS (DF_RANGE_CHK_C)
#define DF_HAS_NR_CHKS (DF_HAS_NULL_CHKS | \
DF_HAS_RANGE_CHKS)
@@ -132,9 +123,10 @@
#define DF_A_IS_REG (DF_UA | DF_DA)
#define DF_B_IS_REG (DF_UB)
#define DF_C_IS_REG (DF_UC)
-#define DF_IS_GETTER_OR_SETTER (DF_IS_GETTER | DF_IS_SETTER)
#define DF_USES_FP (DF_FP_A | DF_FP_B | DF_FP_C)
#define DF_NULL_TRANSFER (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)
+#define DF_IS_INVOKE (DF_FORMAT_35C | DF_FORMAT_3RC)
+
enum OatMethodAttributes {
kIsLeaf, // Method is leaf.
kHasLoop, // Method contains simple loop.
@@ -1307,7 +1299,9 @@
static const uint64_t oat_data_flow_attributes_[kMirOpLast];
ArenaVector<BasicBlock*> gen_suspend_test_list_; // List of blocks containing suspend tests
+ friend class MirOptimizationTest;
friend class ClassInitCheckEliminationTest;
+ friend class NullCheckEliminationTest;
friend class GlobalValueNumberingTest;
friend class LocalValueNumberingTest;
friend class TopologicalSortOrderTest;
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 01f55b9..2a95853 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -827,7 +827,7 @@
DCHECK(temp_scoped_alloc_.get() == nullptr);
temp_scoped_alloc_.reset(ScopedArenaAllocator::Create(&cu_->arena_stack));
- temp_bit_vector_size_ = GetNumSSARegs();
+ temp_bit_vector_size_ = GetNumOfCodeVRs();
temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapNullCheck);
temp_bit_matrix_ = static_cast<ArenaBitVector**>(
@@ -849,98 +849,96 @@
* Eliminate unnecessary null checks for a basic block.
*/
bool MIRGraph::EliminateNullChecks(BasicBlock* bb) {
- if (bb->data_flow_info == nullptr) return false;
+ if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) {
+ // Ignore the kExitBlock as well.
+ DCHECK(bb->first_mir_insn == nullptr);
+ return false;
+ }
- ArenaBitVector* ssa_regs_to_check = temp_bit_vector_;
+ ArenaBitVector* vregs_to_check = temp_bit_vector_;
/*
* Set initial state. Catch blocks don't need any special treatment.
*/
if (bb->block_type == kEntryBlock) {
- ssa_regs_to_check->ClearAllBits();
+ vregs_to_check->ClearAllBits();
// Assume all ins are objects.
for (uint16_t in_reg = GetFirstInVR();
in_reg < GetNumOfCodeVRs(); in_reg++) {
- ssa_regs_to_check->SetBit(in_reg);
+ vregs_to_check->SetBit(in_reg);
}
if ((cu_->access_flags & kAccStatic) == 0) {
- // If non-static method, mark "this" as non-null
+ // If non-static method, mark "this" as non-null.
int this_reg = GetFirstInVR();
- ssa_regs_to_check->ClearBit(this_reg);
+ vregs_to_check->ClearBit(this_reg);
}
- } else if (bb->predecessors.size() == 1) {
- BasicBlock* pred_bb = GetBasicBlock(bb->predecessors[0]);
- // pred_bb must have already been processed at least once.
- DCHECK(temp_bit_matrix_[pred_bb->id] != nullptr);
- ssa_regs_to_check->Copy(temp_bit_matrix_[pred_bb->id]);
- if (pred_bb->block_type == kDalvikByteCode) {
- // Check to see if predecessor had an explicit null-check.
- MIR* last_insn = pred_bb->last_mir_insn;
- if (last_insn != nullptr) {
- Instruction::Code last_opcode = last_insn->dalvikInsn.opcode;
- if (last_opcode == Instruction::IF_EQZ) {
- if (pred_bb->fall_through == bb->id) {
- // The fall-through of a block following a IF_EQZ, set the vA of the IF_EQZ to show that
- // it can't be null.
- ssa_regs_to_check->ClearBit(last_insn->ssa_rep->uses[0]);
- }
- } else if (last_opcode == Instruction::IF_NEZ) {
- if (pred_bb->taken == bb->id) {
- // The taken block following a IF_NEZ, set the vA of the IF_NEZ to show that it can't be
- // null.
- ssa_regs_to_check->ClearBit(last_insn->ssa_rep->uses[0]);
+ } else {
+ DCHECK_EQ(bb->block_type, kDalvikByteCode);
+ // Starting state is union of all incoming arcs.
+ bool copied_first = false;
+ for (BasicBlockId pred_id : bb->predecessors) {
+ if (temp_bit_matrix_[pred_id] == nullptr) {
+ continue;
+ }
+ BasicBlock* pred_bb = GetBasicBlock(pred_id);
+ DCHECK(pred_bb != nullptr);
+ MIR* null_check_insn = nullptr;
+ if (pred_bb->block_type == kDalvikByteCode) {
+ // Check to see if predecessor had an explicit null-check.
+ MIR* last_insn = pred_bb->last_mir_insn;
+ if (last_insn != nullptr) {
+ Instruction::Code last_opcode = last_insn->dalvikInsn.opcode;
+ if ((last_opcode == Instruction::IF_EQZ && pred_bb->fall_through == bb->id) ||
+ (last_opcode == Instruction::IF_NEZ && pred_bb->taken == bb->id)) {
+ // Remember the null check insn if there's no other predecessor requiring null check.
+ if (!copied_first || !vregs_to_check->IsBitSet(last_insn->dalvikInsn.vA)) {
+ null_check_insn = last_insn;
+ }
}
}
}
- }
- } else {
- // Starting state is union of all incoming arcs
- bool copied_first = false;
- for (BasicBlockId pred_id : bb->predecessors) {
- BasicBlock* pred_bb = GetBasicBlock(pred_id);
- DCHECK(pred_bb != nullptr);
- DCHECK(pred_bb->data_flow_info != nullptr);
- if (temp_bit_matrix_[pred_bb->id] == nullptr) {
- continue;
- }
if (!copied_first) {
copied_first = true;
- ssa_regs_to_check->Copy(temp_bit_matrix_[pred_bb->id]);
+ vregs_to_check->Copy(temp_bit_matrix_[pred_id]);
} else {
- ssa_regs_to_check->Union(temp_bit_matrix_[pred_bb->id]);
+ vregs_to_check->Union(temp_bit_matrix_[pred_id]);
+ }
+ if (null_check_insn != nullptr) {
+ vregs_to_check->ClearBit(null_check_insn->dalvikInsn.vA);
}
}
DCHECK(copied_first); // At least one predecessor must have been processed before this bb.
}
- // At this point, ssa_regs_to_check shows which sregs have an object definition with
+ // At this point, vregs_to_check shows which sregs have an object definition with
// no intervening uses.
// Walk through the instruction in the block, updating as necessary
for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
- if (mir->ssa_rep == NULL) {
- continue;
- }
-
uint64_t df_attributes = GetDataFlowAttributes(mir);
+ DCHECK_EQ(df_attributes & DF_NULL_TRANSFER_N, 0u); // No Phis yet.
+
// Might need a null check?
if (df_attributes & DF_HAS_NULL_CHKS) {
- int src_idx;
- if (df_attributes & DF_NULL_CHK_1) {
- src_idx = 1;
- } else if (df_attributes & DF_NULL_CHK_2) {
- src_idx = 2;
+ int src_vreg;
+ if (df_attributes & DF_NULL_CHK_OUT0) {
+ DCHECK_NE(df_attributes & DF_IS_INVOKE, 0u);
+ src_vreg = mir->dalvikInsn.vC;
+ } else if (df_attributes & DF_NULL_CHK_B) {
+ DCHECK_NE(df_attributes & DF_REF_B, 0u);
+ src_vreg = mir->dalvikInsn.vB;
} else {
- src_idx = 0;
+ DCHECK_NE(df_attributes & DF_NULL_CHK_A, 0u);
+ DCHECK_NE(df_attributes & DF_REF_A, 0u);
+ src_vreg = mir->dalvikInsn.vA;
}
- int src_sreg = mir->ssa_rep->uses[src_idx];
- if (!ssa_regs_to_check->IsBitSet(src_sreg)) {
+ if (!vregs_to_check->IsBitSet(src_vreg)) {
// Eliminate the null check.
mir->optimization_flags |= MIR_MARK;
} else {
// Do the null check.
mir->optimization_flags &= ~MIR_MARK;
- // Mark s_reg as null-checked
- ssa_regs_to_check->ClearBit(src_sreg);
+ // Mark src_vreg as null-checked.
+ vregs_to_check->ClearBit(src_vreg);
}
}
@@ -954,66 +952,41 @@
* Note: we can't tell if a CONST definition might be used as an object, so treat
* them all as object definitions.
*/
- if (((df_attributes & (DF_DA | DF_REF_A)) == (DF_DA | DF_REF_A)) ||
+ if ((df_attributes & (DF_DA | DF_REF_A)) == (DF_DA | DF_REF_A) ||
(df_attributes & DF_SETS_CONST)) {
- ssa_regs_to_check->SetBit(mir->ssa_rep->defs[0]);
+ vregs_to_check->SetBit(mir->dalvikInsn.vA);
}
- // Now, remove mark from all object definitions we know are non-null.
+ // Then, remove mark from all object definitions we know are non-null.
if (df_attributes & DF_NON_NULL_DST) {
// Mark target of NEW* as non-null
- ssa_regs_to_check->ClearBit(mir->ssa_rep->defs[0]);
+ DCHECK_NE(df_attributes & DF_REF_A, 0u);
+ vregs_to_check->ClearBit(mir->dalvikInsn.vA);
}
// Mark non-null returns from invoke-style NEW*
if (df_attributes & DF_NON_NULL_RET) {
MIR* next_mir = mir->next;
// Next should be an MOVE_RESULT_OBJECT
- if (next_mir &&
- next_mir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
- // Mark as null checked
- ssa_regs_to_check->ClearBit(next_mir->ssa_rep->defs[0]);
+ if (UNLIKELY(next_mir == nullptr)) {
+ // The MethodVerifier makes sure there's no MOVE_RESULT at the catch entry or branch
+ // target, so the MOVE_RESULT cannot be broken away into another block.
+ LOG(WARNING) << "Unexpected end of block following new";
+ } else if (UNLIKELY(next_mir->dalvikInsn.opcode != Instruction::MOVE_RESULT_OBJECT)) {
+ LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode;
} else {
- if (next_mir) {
- LOG(WARNING) << "Unexpected opcode following new: " << next_mir->dalvikInsn.opcode;
- } else if (bb->fall_through != NullBasicBlockId) {
- // Look in next basic block
- struct BasicBlock* next_bb = GetBasicBlock(bb->fall_through);
- for (MIR* tmir = next_bb->first_mir_insn; tmir != NULL;
- tmir =tmir->next) {
- if (MIR::DecodedInstruction::IsPseudoMirOp(tmir->dalvikInsn.opcode)) {
- continue;
- }
- // First non-pseudo should be MOVE_RESULT_OBJECT
- if (tmir->dalvikInsn.opcode == Instruction::MOVE_RESULT_OBJECT) {
- // Mark as null checked
- ssa_regs_to_check->ClearBit(tmir->ssa_rep->defs[0]);
- } else {
- LOG(WARNING) << "Unexpected op after new: " << tmir->dalvikInsn.opcode;
- }
- break;
- }
- }
+ // Mark as null checked.
+ vregs_to_check->ClearBit(next_mir->dalvikInsn.vA);
}
}
- /*
- * Propagate nullcheck state on register copies (including
- * Phi pseudo copies. For the latter, nullcheck state is
- * the "or" of all the Phi's operands.
- */
- if (df_attributes & (DF_NULL_TRANSFER_0 | DF_NULL_TRANSFER_N)) {
- int tgt_sreg = mir->ssa_rep->defs[0];
- int operands = (df_attributes & DF_NULL_TRANSFER_0) ? 1 :
- mir->ssa_rep->num_uses;
- bool needs_null_check = false;
- for (int i = 0; i < operands; i++) {
- needs_null_check |= ssa_regs_to_check->IsBitSet(mir->ssa_rep->uses[i]);
- }
- if (needs_null_check) {
- ssa_regs_to_check->SetBit(tgt_sreg);
+ // Propagate null check state on register copies.
+ if (df_attributes & DF_NULL_TRANSFER_0) {
+ DCHECK_EQ(df_attributes | ~(DF_DA | DF_REF_A | DF_UB | DF_REF_B), static_cast<uint64_t>(-1));
+ if (vregs_to_check->IsBitSet(mir->dalvikInsn.vB)) {
+ vregs_to_check->SetBit(mir->dalvikInsn.vA);
} else {
- ssa_regs_to_check->ClearBit(tgt_sreg);
+ vregs_to_check->ClearBit(mir->dalvikInsn.vA);
}
}
}
@@ -1023,15 +996,15 @@
ArenaBitVector* old_ending_ssa_regs_to_check = temp_bit_matrix_[bb->id];
if (old_ending_ssa_regs_to_check == nullptr) {
DCHECK(temp_scoped_alloc_.get() != nullptr);
- nce_changed = ssa_regs_to_check->GetHighestBitSet() != -1;
- temp_bit_matrix_[bb->id] = ssa_regs_to_check;
- // Create a new ssa_regs_to_check for next BB.
+ nce_changed = vregs_to_check->GetHighestBitSet() != -1;
+ temp_bit_matrix_[bb->id] = vregs_to_check;
+ // Create a new vregs_to_check for next BB.
temp_bit_vector_ = new (temp_scoped_alloc_.get()) ArenaBitVector(
temp_scoped_alloc_.get(), temp_bit_vector_size_, false, kBitMapNullCheck);
- } else if (!ssa_regs_to_check->SameBitsSet(old_ending_ssa_regs_to_check)) {
+ } else if (!vregs_to_check->SameBitsSet(old_ending_ssa_regs_to_check)) {
nce_changed = true;
- temp_bit_matrix_[bb->id] = ssa_regs_to_check;
- temp_bit_vector_ = old_ending_ssa_regs_to_check; // Reuse for ssa_regs_to_check for next BB.
+ temp_bit_matrix_[bb->id] = vregs_to_check;
+ temp_bit_vector_ = old_ending_ssa_regs_to_check; // Reuse for vregs_to_check for next BB.
}
return nce_changed;
}
@@ -1045,13 +1018,13 @@
temp_scoped_alloc_.reset();
// converge MIR_MARK with MIR_IGNORE_NULL_CHECK
- const int MARK_TO_IGNORE_NULL_CHECK_SHIFT = kMIRMark - kMIRIgnoreNullCheck;
- DCHECK(MARK_TO_IGNORE_NULL_CHECK_SHIFT > 0);
AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
for (MIR* mir = bb->first_mir_insn; mir != NULL; mir = mir->next) {
+ constexpr int kMarkToIgnoreNullCheckShift = kMIRMark - kMIRIgnoreNullCheck;
+ COMPILE_ASSERT(kMarkToIgnoreNullCheckShift > 0, check_valid_shift_right);
uint16_t mirMarkAdjustedToIgnoreNullCheck =
- (mir->optimization_flags & MIR_MARK) >> MARK_TO_IGNORE_NULL_CHECK_SHIFT;
+ (mir->optimization_flags & MIR_MARK) >> kMarkToIgnoreNullCheckShift;
mir->optimization_flags |= mirMarkAdjustedToIgnoreNullCheck;
}
}
@@ -1119,26 +1092,27 @@
// First, find all SGET/SPUTs that may need class initialization checks, record INVOKE_STATICs.
AllNodesIterator iter(this);
for (BasicBlock* bb = iter.Next(); bb != nullptr; bb = iter.Next()) {
- for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
- DCHECK(bb->data_flow_info != nullptr);
- if (mir->dalvikInsn.opcode >= Instruction::SGET &&
- mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) {
- const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(mir);
- uint16_t index = 0xffffu;
- if (!field_info.IsInitialized()) {
- DCHECK_LT(class_to_index_map.size(), 0xffffu);
- MapEntry entry = {
- // Treat unresolved fields as if each had its own class.
- field_info.IsResolved() ? field_info.DeclaringDexFile()
- : nullptr,
- field_info.IsResolved() ? field_info.DeclaringClassIndex()
- : field_info.FieldIndex(),
- static_cast<uint16_t>(class_to_index_map.size())
- };
- index = class_to_index_map.insert(entry).first->index;
+ if (bb->block_type == kDalvikByteCode) {
+ for (MIR* mir = bb->first_mir_insn; mir != nullptr; mir = mir->next) {
+ if (mir->dalvikInsn.opcode >= Instruction::SGET &&
+ mir->dalvikInsn.opcode <= Instruction::SPUT_SHORT) {
+ const MirSFieldLoweringInfo& field_info = GetSFieldLoweringInfo(mir);
+ uint16_t index = 0xffffu;
+ if (!field_info.IsInitialized()) {
+ DCHECK_LT(class_to_index_map.size(), 0xffffu);
+ MapEntry entry = {
+ // Treat unresolved fields as if each had its own class.
+ field_info.IsResolved() ? field_info.DeclaringDexFile()
+ : nullptr,
+ field_info.IsResolved() ? field_info.DeclaringClassIndex()
+ : field_info.FieldIndex(),
+ static_cast<uint16_t>(class_to_index_map.size())
+ };
+ index = class_to_index_map.insert(entry).first->index;
+ }
+ // Using offset/2 for index into temp_insn_data_.
+ temp_insn_data_[mir->offset / 2u] = index;
}
- // Using offset/2 for index into temp_insn_data_.
- temp_insn_data_[mir->offset / 2u] = index;
}
}
}
@@ -1167,7 +1141,9 @@
*/
bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) {
DCHECK_EQ((cu_->disable_opt & (1 << kClassInitCheckElimination)), 0u);
- if (bb->data_flow_info == nullptr) {
+ if (bb->block_type != kDalvikByteCode && bb->block_type != kEntryBlock) {
+ // Ignore the kExitBlock as well.
+ DCHECK(bb->first_mir_insn == nullptr);
return false;
}
@@ -1182,7 +1158,6 @@
BasicBlock* pred_bb = GetBasicBlock(bb->predecessors[0]);
// pred_bb must have already been processed at least once.
DCHECK(pred_bb != nullptr);
- DCHECK(pred_bb->data_flow_info != nullptr);
DCHECK(temp_bit_matrix_[pred_bb->id] != nullptr);
classes_to_check->Copy(temp_bit_matrix_[pred_bb->id]);
} else {
@@ -1191,7 +1166,6 @@
for (BasicBlockId pred_id : bb->predecessors) {
BasicBlock* pred_bb = GetBasicBlock(pred_id);
DCHECK(pred_bb != nullptr);
- DCHECK(pred_bb->data_flow_info != nullptr);
if (temp_bit_matrix_[pred_bb->id] == nullptr) {
continue;
}
diff --git a/compiler/dex/mir_optimization_test.cc b/compiler/dex/mir_optimization_test.cc
index 55e547e..337d4ef 100644
--- a/compiler/dex/mir_optimization_test.cc
+++ b/compiler/dex/mir_optimization_test.cc
@@ -23,15 +23,8 @@
namespace art {
-class ClassInitCheckEliminationTest : public testing::Test {
+class MirOptimizationTest : public testing::Test {
protected:
- struct SFieldDef {
- uint16_t field_idx;
- uintptr_t declaring_dex_file;
- uint16_t declaring_class_idx;
- uint16_t declaring_field_idx;
- };
-
struct BBDef {
static constexpr size_t kMaxSuccessors = 4;
static constexpr size_t kMaxPredecessors = 4;
@@ -44,9 +37,12 @@
};
struct MIRDef {
- Instruction::Code opcode;
BasicBlockId bbid;
- uint32_t field_or_method_info;
+ Instruction::Code opcode;
+ uint32_t field_info;
+ uint32_t vA;
+ uint32_t vB;
+ uint32_t vC;
};
#define DEF_SUCC0() \
@@ -72,32 +68,6 @@
#define DEF_BB(type, succ, pred) \
{ type, succ, pred }
-#define DEF_MIR(opcode, bb, field_info) \
- { opcode, bb, field_info }
-
- void DoPrepareSFields(const SFieldDef* defs, size_t count) {
- cu_.mir_graph->sfield_lowering_infos_.clear();
- cu_.mir_graph->sfield_lowering_infos_.reserve(count);
- for (size_t i = 0u; i != count; ++i) {
- const SFieldDef* def = &defs[i];
- MirSFieldLoweringInfo field_info(def->field_idx);
- if (def->declaring_dex_file != 0u) {
- field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
- field_info.declaring_class_idx_ = def->declaring_class_idx;
- field_info.declaring_field_idx_ = def->declaring_field_idx;
- field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic;
- }
- ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
- ASSERT_FALSE(field_info.IsInitialized());
- cu_.mir_graph->sfield_lowering_infos_.push_back(field_info);
- }
- }
-
- template <size_t count>
- void PrepareSFields(const SFieldDef (&defs)[count]) {
- DoPrepareSFields(defs, count);
- }
-
void DoPrepareBasicBlocks(const BBDef* defs, size_t count) {
cu_.mir_graph->block_id_map_.clear();
cu_.mir_graph->block_list_.clear();
@@ -145,6 +115,63 @@
DoPrepareBasicBlocks(defs, count);
}
+ void PrepareSingleBlock() {
+ static const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(3)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(1)),
+ };
+ PrepareBasicBlocks(bbs);
+ }
+
+ void PrepareDiamond() {
+ static const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)),
+ };
+ PrepareBasicBlocks(bbs);
+ }
+
+ void PrepareLoop() {
+ static const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)), // "taken" loops to self.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
+ };
+ PrepareBasicBlocks(bbs);
+ }
+
+ void PrepareCatch() {
+ static const BBDef bbs[] = {
+ DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
+ DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
+ DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), // The top.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // The throwing insn.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Catch handler.
+ DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), // The merged block.
+ };
+ PrepareBasicBlocks(bbs);
+ BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u);
+ catch_handler->catch_entry = true;
+ // Add successor block info to the check block.
+ BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
+ check_bb->successor_block_list_type = kCatch;
+ SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
+ (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
+ successor_block_info->block = catch_handler->id;
+ check_bb->successor_blocks.push_back(successor_block_info);
+ }
+
void DoPrepareMIRs(const MIRDef* defs, size_t count) {
mir_count_ = count;
mirs_ = reinterpret_cast<MIR*>(cu_.arena.Alloc(sizeof(MIR) * count, kArenaAllocMIR));
@@ -157,9 +184,15 @@
BasicBlock* bb = cu_.mir_graph->block_list_[def->bbid];
bb->AppendMIR(mir);
if (def->opcode >= Instruction::SGET && def->opcode <= Instruction::SPUT_SHORT) {
- ASSERT_LT(def->field_or_method_info, cu_.mir_graph->sfield_lowering_infos_.size());
- mir->meta.sfield_lowering_info = def->field_or_method_info;
+ ASSERT_LT(def->field_info, cu_.mir_graph->sfield_lowering_infos_.size());
+ mir->meta.sfield_lowering_info = def->field_info;
+ } else if (def->opcode >= Instruction::IGET && def->opcode <= Instruction::IPUT_SHORT) {
+ ASSERT_LT(def->field_info, cu_.mir_graph->ifield_lowering_infos_.size());
+ mir->meta.ifield_lowering_info = def->field_info;
}
+ mir->dalvikInsn.vA = def->vA;
+ mir->dalvikInsn.vB = def->vB;
+ mir->dalvikInsn.vC = def->vC;
mir->ssa_rep = nullptr;
mir->offset = 2 * i; // All insns need to be at least 2 code units long.
mir->optimization_flags = 0u;
@@ -179,29 +212,14 @@
DoPrepareMIRs(defs, count);
}
- void PerformClassInitCheckElimination() {
- cu_.mir_graph->SSATransformationStart();
- cu_.mir_graph->ComputeDFSOrders();
- cu_.mir_graph->ComputeDominators();
- cu_.mir_graph->ComputeTopologicalSortOrder();
- cu_.mir_graph->SSATransformationEnd();
- bool gate_result = cu_.mir_graph->EliminateClassInitChecksGate();
- ASSERT_TRUE(gate_result);
- LoopRepeatingTopologicalSortIterator iterator(cu_.mir_graph.get());
- bool change = false;
- for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
- change = cu_.mir_graph->EliminateClassInitChecks(bb);
- }
- cu_.mir_graph->EliminateClassInitChecksEnd();
- }
-
- ClassInitCheckEliminationTest()
+ MirOptimizationTest()
: pool_(),
cu_(&pool_),
mir_count_(0u),
mirs_(nullptr),
code_item_(nullptr) {
cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena));
+ cu_.access_flags = kAccStatic; // Don't let "this" interfere with this test.
}
ArenaPool pool_;
@@ -211,6 +229,109 @@
DexFile::CodeItem* code_item_;
};
+class ClassInitCheckEliminationTest : public MirOptimizationTest {
+ protected:
+ struct SFieldDef {
+ uint16_t field_idx;
+ uintptr_t declaring_dex_file;
+ uint16_t declaring_class_idx;
+ uint16_t declaring_field_idx;
+ };
+
+ void DoPrepareSFields(const SFieldDef* defs, size_t count) {
+ cu_.mir_graph->sfield_lowering_infos_.clear();
+ cu_.mir_graph->sfield_lowering_infos_.reserve(count);
+ for (size_t i = 0u; i != count; ++i) {
+ const SFieldDef* def = &defs[i];
+ MirSFieldLoweringInfo field_info(def->field_idx);
+ if (def->declaring_dex_file != 0u) {
+ field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
+ field_info.declaring_class_idx_ = def->declaring_class_idx;
+ field_info.declaring_field_idx_ = def->declaring_field_idx;
+ field_info.flags_ = MirSFieldLoweringInfo::kFlagIsStatic;
+ }
+ ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
+ ASSERT_FALSE(field_info.IsInitialized());
+ cu_.mir_graph->sfield_lowering_infos_.push_back(field_info);
+ }
+ }
+
+ template <size_t count>
+ void PrepareSFields(const SFieldDef (&defs)[count]) {
+ DoPrepareSFields(defs, count);
+ }
+
+ void PerformClassInitCheckElimination() {
+ cu_.mir_graph->ComputeDFSOrders();
+ bool gate_result = cu_.mir_graph->EliminateClassInitChecksGate();
+ ASSERT_TRUE(gate_result);
+ RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get());
+ bool change = false;
+ for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
+ change = cu_.mir_graph->EliminateClassInitChecks(bb);
+ }
+ cu_.mir_graph->EliminateClassInitChecksEnd();
+ }
+
+ ClassInitCheckEliminationTest()
+ : MirOptimizationTest() {
+ }
+};
+
+class NullCheckEliminationTest : public MirOptimizationTest {
+ protected:
+ struct IFieldDef {
+ uint16_t field_idx;
+ uintptr_t declaring_dex_file;
+ uint16_t declaring_class_idx;
+ uint16_t declaring_field_idx;
+ };
+
+ void DoPrepareIFields(const IFieldDef* defs, size_t count) {
+ cu_.mir_graph->ifield_lowering_infos_.clear();
+ cu_.mir_graph->ifield_lowering_infos_.reserve(count);
+ for (size_t i = 0u; i != count; ++i) {
+ const IFieldDef* def = &defs[i];
+ MirIFieldLoweringInfo field_info(def->field_idx);
+ if (def->declaring_dex_file != 0u) {
+ field_info.declaring_dex_file_ = reinterpret_cast<const DexFile*>(def->declaring_dex_file);
+ field_info.declaring_class_idx_ = def->declaring_class_idx;
+ field_info.declaring_field_idx_ = def->declaring_field_idx;
+ }
+ ASSERT_EQ(def->declaring_dex_file != 0u, field_info.IsResolved());
+ cu_.mir_graph->ifield_lowering_infos_.push_back(field_info);
+ }
+ }
+
+ template <size_t count>
+ void PrepareIFields(const IFieldDef (&defs)[count]) {
+ DoPrepareIFields(defs, count);
+ }
+
+ void PerformNullCheckElimination() {
+ // Make vregs in range [100, 1000) input registers, i.e. requiring a null check.
+ code_item_->registers_size_ = 1000;
+ code_item_->ins_size_ = 900;
+
+ cu_.mir_graph->ComputeDFSOrders();
+ bool gate_result = cu_.mir_graph->EliminateNullChecksGate();
+ ASSERT_TRUE(gate_result);
+ RepeatingPreOrderDfsIterator iterator(cu_.mir_graph.get());
+ bool change = false;
+ for (BasicBlock* bb = iterator.Next(change); bb != nullptr; bb = iterator.Next(change)) {
+ change = cu_.mir_graph->EliminateNullChecks(bb);
+ }
+ cu_.mir_graph->EliminateNullChecksEnd();
+ }
+
+ NullCheckEliminationTest()
+ : MirOptimizationTest() {
+ }
+};
+
+#define DEF_SGET_SPUT_V0(bb, opcode, field_info) \
+ { bb, opcode, field_info, 0u, 0u, 0u }
+
TEST_F(ClassInitCheckEliminationTest, SingleBlock) {
static const SFieldDef sfields[] = {
{ 0u, 1u, 0u, 0u },
@@ -220,31 +341,25 @@
{ 4u, 1u, 3u, 4u }, // Same declaring class as sfield[3].
{ 5u, 0u, 0u, 0u }, // Unresolved.
};
- static const BBDef bbs[] = {
- DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
- DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
- DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(3)),
- DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(1)),
- };
static const MIRDef mirs[] = {
- DEF_MIR(Instruction::SPUT, 3u, 5u), // Unresolved.
- DEF_MIR(Instruction::SPUT, 3u, 0u),
- DEF_MIR(Instruction::SGET, 3u, 1u),
- DEF_MIR(Instruction::SGET, 3u, 2u),
- DEF_MIR(Instruction::SGET, 3u, 5u), // Unresolved.
- DEF_MIR(Instruction::SGET, 3u, 0u),
- DEF_MIR(Instruction::SGET, 3u, 1u),
- DEF_MIR(Instruction::SGET, 3u, 2u),
- DEF_MIR(Instruction::SGET, 3u, 5u), // Unresolved.
- DEF_MIR(Instruction::SGET, 3u, 3u),
- DEF_MIR(Instruction::SGET, 3u, 4u),
+ DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 5u), // Unresolved.
+ DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 0u),
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 1u),
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 2u),
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 5u), // Unresolved.
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 0u),
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 1u),
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 2u),
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 5u), // Unresolved.
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 3u),
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 4u),
};
static const bool expected_ignore_clinit_check[] = {
false, false, false, false, true, true, true, true, true, false, true
};
PrepareSFields(sfields);
- PrepareBasicBlocks(bbs);
+ PrepareSingleBlock();
PrepareMIRs(mirs);
PerformClassInitCheckElimination();
ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
@@ -268,40 +383,31 @@
{ 9u, 1u, 8u, 9u }, // Same declaring class as sfield[8].
{ 10u, 0u, 0u, 0u }, // Unresolved.
};
- static const BBDef bbs[] = {
- DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
- DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
- DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
- DEF_BB(kDalvikByteCode, DEF_SUCC2(4, 5), DEF_PRED1(1)),
- DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
- DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)),
- DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)),
- };
static const MIRDef mirs[] = {
// NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks.
- DEF_MIR(Instruction::SGET, 3u, 10u), // Unresolved.
- DEF_MIR(Instruction::SPUT, 3u, 10u), // Unresolved.
- DEF_MIR(Instruction::SPUT, 3u, 0u),
- DEF_MIR(Instruction::SGET, 6u, 0u), // Eliminated (block #3 dominates #6).
- DEF_MIR(Instruction::SPUT, 4u, 1u),
- DEF_MIR(Instruction::SGET, 6u, 1u), // Not eliminated (block #4 doesn't dominate #6).
- DEF_MIR(Instruction::SGET, 3u, 2u),
- DEF_MIR(Instruction::SGET, 4u, 2u), // Eliminated (block #3 dominates #4).
- DEF_MIR(Instruction::SGET, 3u, 3u),
- DEF_MIR(Instruction::SGET, 5u, 3u), // Eliminated (block #3 dominates #5).
- DEF_MIR(Instruction::SGET, 3u, 4u),
- DEF_MIR(Instruction::SGET, 6u, 4u), // Eliminated (block #3 dominates #6).
- DEF_MIR(Instruction::SGET, 4u, 5u),
- DEF_MIR(Instruction::SGET, 6u, 5u), // Not eliminated (block #4 doesn't dominate #6).
- DEF_MIR(Instruction::SGET, 5u, 6u),
- DEF_MIR(Instruction::SGET, 6u, 6u), // Not eliminated (block #5 doesn't dominate #6).
- DEF_MIR(Instruction::SGET, 4u, 7u),
- DEF_MIR(Instruction::SGET, 5u, 7u),
- DEF_MIR(Instruction::SGET, 6u, 7u), // Eliminated (initialized in both blocks #3 and #4).
- DEF_MIR(Instruction::SGET, 4u, 8u),
- DEF_MIR(Instruction::SGET, 5u, 9u),
- DEF_MIR(Instruction::SGET, 6u, 8u), // Eliminated (with sfield[9] in block #5).
- DEF_MIR(Instruction::SPUT, 6u, 9u), // Eliminated (with sfield[8] in block #4).
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 10u), // Unresolved.
+ DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 10u), // Unresolved.
+ DEF_SGET_SPUT_V0(3u, Instruction::SPUT, 0u),
+ DEF_SGET_SPUT_V0(6u, Instruction::SGET, 0u), // Eliminated (BB #3 dominates #6).
+ DEF_SGET_SPUT_V0(4u, Instruction::SPUT, 1u),
+ DEF_SGET_SPUT_V0(6u, Instruction::SGET, 1u), // Not eliminated (BB #4 doesn't dominate #6).
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 2u),
+ DEF_SGET_SPUT_V0(4u, Instruction::SGET, 2u), // Eliminated (BB #3 dominates #4).
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 3u),
+ DEF_SGET_SPUT_V0(5u, Instruction::SGET, 3u), // Eliminated (BB #3 dominates #5).
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 4u),
+ DEF_SGET_SPUT_V0(6u, Instruction::SGET, 4u), // Eliminated (BB #3 dominates #6).
+ DEF_SGET_SPUT_V0(4u, Instruction::SGET, 5u),
+ DEF_SGET_SPUT_V0(6u, Instruction::SGET, 5u), // Not eliminated (BB #4 doesn't dominate #6).
+ DEF_SGET_SPUT_V0(5u, Instruction::SGET, 6u),
+ DEF_SGET_SPUT_V0(6u, Instruction::SGET, 6u), // Not eliminated (BB #5 doesn't dominate #6).
+ DEF_SGET_SPUT_V0(4u, Instruction::SGET, 7u),
+ DEF_SGET_SPUT_V0(5u, Instruction::SGET, 7u),
+ DEF_SGET_SPUT_V0(6u, Instruction::SGET, 7u), // Eliminated (initialized in both #3 and #4).
+ DEF_SGET_SPUT_V0(4u, Instruction::SGET, 8u),
+ DEF_SGET_SPUT_V0(5u, Instruction::SGET, 9u),
+ DEF_SGET_SPUT_V0(6u, Instruction::SGET, 8u), // Eliminated (with sfield[9] in BB #5).
+ DEF_SGET_SPUT_V0(6u, Instruction::SPUT, 9u), // Eliminated (with sfield[8] in BB #4).
};
static const bool expected_ignore_clinit_check[] = {
false, true, // Unresolved: sfield[10], method[2]
@@ -317,7 +423,7 @@
};
PrepareSFields(sfields);
- PrepareBasicBlocks(bbs);
+ PrepareDiamond();
PrepareMIRs(mirs);
PerformClassInitCheckElimination();
ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
@@ -332,26 +438,18 @@
{ 0u, 1u, 0u, 0u },
{ 1u, 1u, 1u, 1u },
};
- static const BBDef bbs[] = {
- DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
- DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
- DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(5)),
- DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)),
- DEF_BB(kDalvikByteCode, DEF_SUCC2(5, 4), DEF_PRED2(3, 4)), // "taken" loops to self.
- DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED1(4)),
- };
static const MIRDef mirs[] = {
- DEF_MIR(Instruction::SGET, 3u, 0u),
- DEF_MIR(Instruction::SGET, 4u, 1u),
- DEF_MIR(Instruction::SGET, 5u, 0u), // Eliminated.
- DEF_MIR(Instruction::SGET, 5u, 1u), // Eliminated.
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 0u),
+ DEF_SGET_SPUT_V0(4u, Instruction::SGET, 1u),
+ DEF_SGET_SPUT_V0(5u, Instruction::SGET, 0u), // Eliminated.
+ DEF_SGET_SPUT_V0(5u, Instruction::SGET, 1u), // Eliminated.
};
static const bool expected_ignore_clinit_check[] = {
false, false, true, true
};
PrepareSFields(sfields);
- PrepareBasicBlocks(bbs);
+ PrepareLoop();
PrepareMIRs(mirs);
PerformClassInitCheckElimination();
ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
@@ -368,42 +466,24 @@
{ 2u, 1u, 2u, 2u },
{ 3u, 1u, 3u, 3u },
};
- static const BBDef bbs[] = {
- DEF_BB(kNullBlock, DEF_SUCC0(), DEF_PRED0()),
- DEF_BB(kEntryBlock, DEF_SUCC1(3), DEF_PRED0()),
- DEF_BB(kExitBlock, DEF_SUCC0(), DEF_PRED1(6)),
- DEF_BB(kDalvikByteCode, DEF_SUCC1(4), DEF_PRED1(1)), // The top.
- DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // The throwing insn.
- DEF_BB(kDalvikByteCode, DEF_SUCC1(6), DEF_PRED1(3)), // Catch handler.
- DEF_BB(kDalvikByteCode, DEF_SUCC1(2), DEF_PRED2(4, 5)), // The merged block.
- };
static const MIRDef mirs[] = {
- DEF_MIR(Instruction::SGET, 3u, 0u), // Before the exception edge.
- DEF_MIR(Instruction::SGET, 3u, 1u), // Before the exception edge.
- DEF_MIR(Instruction::SGET, 4u, 2u), // After the exception edge.
- DEF_MIR(Instruction::SGET, 4u, 3u), // After the exception edge.
- DEF_MIR(Instruction::SGET, 5u, 0u), // In catch handler; class init check eliminated.
- DEF_MIR(Instruction::SGET, 5u, 2u), // In catch handler; class init check not eliminated.
- DEF_MIR(Instruction::SGET, 6u, 0u), // Class init check eliminated.
- DEF_MIR(Instruction::SGET, 6u, 1u), // Class init check eliminated.
- DEF_MIR(Instruction::SGET, 6u, 2u), // Class init check eliminated.
- DEF_MIR(Instruction::SGET, 6u, 3u), // Class init check not eliminated.
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 0u), // Before the exception edge.
+ DEF_SGET_SPUT_V0(3u, Instruction::SGET, 1u), // Before the exception edge.
+ DEF_SGET_SPUT_V0(4u, Instruction::SGET, 2u), // After the exception edge.
+ DEF_SGET_SPUT_V0(4u, Instruction::SGET, 3u), // After the exception edge.
+ DEF_SGET_SPUT_V0(5u, Instruction::SGET, 0u), // In catch handler; clinit check eliminated.
+ DEF_SGET_SPUT_V0(5u, Instruction::SGET, 2u), // In catch handler; clinit check not eliminated.
+ DEF_SGET_SPUT_V0(6u, Instruction::SGET, 0u), // Class init check eliminated.
+ DEF_SGET_SPUT_V0(6u, Instruction::SGET, 1u), // Class init check eliminated.
+ DEF_SGET_SPUT_V0(6u, Instruction::SGET, 2u), // Class init check eliminated.
+ DEF_SGET_SPUT_V0(6u, Instruction::SGET, 3u), // Class init check not eliminated.
};
static const bool expected_ignore_clinit_check[] = {
false, false, false, false, true, false, true, true, true, false
};
PrepareSFields(sfields);
- PrepareBasicBlocks(bbs);
- BasicBlock* catch_handler = cu_.mir_graph->GetBasicBlock(5u);
- catch_handler->catch_entry = true;
- // Add successor block info to the check block.
- BasicBlock* check_bb = cu_.mir_graph->GetBasicBlock(3u);
- check_bb->successor_block_list_type = kCatch;
- SuccessorBlockInfo* successor_block_info = reinterpret_cast<SuccessorBlockInfo*>
- (cu_.arena.Alloc(sizeof(SuccessorBlockInfo), kArenaAllocSuccessor));
- successor_block_info->block = catch_handler->id;
- check_bb->successor_blocks.push_back(successor_block_info);
+ PrepareCatch();
PrepareMIRs(mirs);
PerformClassInitCheckElimination();
ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_);
@@ -413,4 +493,189 @@
}
}
+#define DEF_IGET_IPUT(bb, opcode, vA, vB, field_info) \
+ { bb, opcode, field_info, vA, vB, 0u }
+#define DEF_AGET_APUT(bb, opcode, vA, vB, vC) \
+ { bb, opcode, 0u, vA, vB, vC }
+#define DEF_INVOKE(bb, opcode, vC) \
+ { bb, opcode, 0u, 0u, 0u, vC }
+#define DEF_OTHER1(bb, opcode, vA) \
+ { bb, opcode, 0u, vA, 0u, 0u }
+#define DEF_OTHER2(bb, opcode, vA, vB) \
+ { bb, opcode, 0u, vA, vB, 0u }
+
+TEST_F(NullCheckEliminationTest, SingleBlock) {
+ static const IFieldDef ifields[] = {
+ { 0u, 1u, 0u, 0u },
+ { 1u, 1u, 0u, 1u },
+ { 2u, 1u, 0u, 2u }, // Object.
+ };
+ static const MIRDef mirs[] = {
+ DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 0u, 100u, 2u),
+ DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 0u, 1u),
+ DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 2u, 100u, 2u), // Differs from 0u (no LVN here).
+ DEF_IGET_IPUT(3u, Instruction::IGET, 3u, 2u, 1u),
+ DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 101u, 0u),
+ DEF_IGET_IPUT(3u, Instruction::IGET, 5u, 102u, 0u),
+ DEF_IGET_IPUT(3u, Instruction::IGET, 6u, 103u, 0u),
+ DEF_IGET_IPUT(3u, Instruction::IGET, 7u, 103u, 1u),
+ DEF_IGET_IPUT(3u, Instruction::IPUT, 8u, 104u, 0u),
+ DEF_IGET_IPUT(3u, Instruction::IPUT, 9u, 104u, 1u),
+ DEF_IGET_IPUT(3u, Instruction::IGET, 10u, 105u, 0u),
+ DEF_IGET_IPUT(3u, Instruction::IPUT, 11u, 105u, 1u),
+ DEF_IGET_IPUT(3u, Instruction::IPUT, 12u, 106u, 0u),
+ DEF_IGET_IPUT(3u, Instruction::IGET, 13u, 106u, 1u),
+ DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 107),
+ DEF_IGET_IPUT(3u, Instruction::IGET, 15u, 107u, 1u),
+ DEF_IGET_IPUT(3u, Instruction::IGET, 16u, 108u, 0u),
+ DEF_INVOKE(3u, Instruction::INVOKE_DIRECT, 108),
+ DEF_AGET_APUT(3u, Instruction::AGET, 18u, 109u, 110u),
+ DEF_AGET_APUT(3u, Instruction::APUT, 19u, 109u, 111u),
+ DEF_OTHER2(3u, Instruction::ARRAY_LENGTH, 20u, 112u),
+ DEF_AGET_APUT(3u, Instruction::AGET, 21u, 112u, 113u),
+ DEF_OTHER1(3u, Instruction::MONITOR_ENTER, 114u),
+ DEF_OTHER1(3u, Instruction::MONITOR_EXIT, 114u),
+ };
+ static const bool expected_ignore_null_check[] = {
+ false, false, true, false /* Not doing LVN. */,
+ false, true /* Set before running NCE. */,
+ false, true, // IGET, IGET
+ false, true, // IPUT, IPUT
+ false, true, // IGET, IPUT
+ false, true, // IPUT, IGET
+ false, true, // INVOKE, IGET
+ false, true, // IGET, INVOKE
+ false, true, // AGET, APUT
+ false, true, // ARRAY_LENGTH, AGET
+ false, true, // MONITOR_ENTER, MONITOR_EXIT
+ };
+
+ PrepareIFields(ifields);
+ PrepareSingleBlock();
+ PrepareMIRs(mirs);
+
+ // Mark IGET 5u as null-checked to test that NCE doesn't clear this flag.
+ mirs_[5u].optimization_flags |= MIR_IGNORE_NULL_CHECK;
+
+ PerformNullCheckElimination();
+ ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
+ for (size_t i = 0u; i != arraysize(mirs); ++i) {
+ EXPECT_EQ(expected_ignore_null_check[i],
+ (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
+ }
+}
+
+TEST_F(NullCheckEliminationTest, Diamond) {
+ static const IFieldDef ifields[] = {
+ { 0u, 1u, 0u, 0u },
+ { 1u, 1u, 0u, 1u },
+ { 2u, 1u, 0u, 2u }, // int[].
+ };
+ static const MIRDef mirs[] = {
+ // NOTE: MIRs here are ordered by unique tests. They will be put into appropriate blocks.
+ DEF_IGET_IPUT(3u, Instruction::IPUT, 0u, 100u, 0u),
+ DEF_IGET_IPUT(6u, Instruction::IGET, 1u, 100u, 1u), // Eliminated (BB #3 dominates #6).
+ DEF_IGET_IPUT(3u, Instruction::IGET, 2u, 101u, 0u),
+ DEF_IGET_IPUT(4u, Instruction::IPUT, 3u, 101u, 0u), // Eliminated (BB #3 dominates #4).
+ DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u),
+ DEF_IGET_IPUT(5u, Instruction::IPUT, 5u, 102u, 1u), // Eliminated (BB #3 dominates #5).
+ DEF_IGET_IPUT(4u, Instruction::IPUT, 6u, 103u, 0u),
+ DEF_IGET_IPUT(6u, Instruction::IPUT, 7u, 103u, 1u), // Not eliminated (going through BB #5).
+ DEF_IGET_IPUT(5u, Instruction::IGET, 8u, 104u, 1u),
+ DEF_IGET_IPUT(6u, Instruction::IGET, 9u, 104u, 0u), // Not eliminated (going through BB #4).
+ DEF_INVOKE(4u, Instruction::INVOKE_DIRECT, 105u),
+ DEF_IGET_IPUT(5u, Instruction::IGET, 11u, 105u, 1u),
+ DEF_IGET_IPUT(6u, Instruction::IPUT, 12u, 105u, 0u), // Eliminated.
+ DEF_IGET_IPUT(3u, Instruction::IGET_OBJECT, 13u, 106u, 2u),
+ DEF_OTHER1(3u, Instruction::IF_EQZ, 13u), // Last insn in the BB #3.
+ DEF_OTHER2(5u, Instruction::NEW_ARRAY, 13u, 107u),
+ DEF_AGET_APUT(6u, Instruction::AGET, 16u, 13u, 108u), // Eliminated.
+ };
+ static const bool expected_ignore_null_check[] = {
+ false, true, // BB #3 IPUT, BB #6 IGET
+ false, true, // BB #3 IGET, BB #4 IPUT
+ false, true, // BB #3 IGET, BB #5 IPUT
+ false, false, // BB #4 IPUT, BB #6 IPUT
+ false, false, // BB #5 IGET, BB #6 IGET
+ false, false, true, // BB #4 INVOKE, BB #5 IGET, BB #6 IPUT
+ false, false, // BB #3 IGET_OBJECT & IF_EQZ
+ false, true, // BB #5 NEW_ARRAY, BB #6 AGET
+ };
+
+ PrepareIFields(ifields);
+ PrepareDiamond();
+ PrepareMIRs(mirs);
+ PerformNullCheckElimination();
+ ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
+ for (size_t i = 0u; i != arraysize(mirs); ++i) {
+ EXPECT_EQ(expected_ignore_null_check[i],
+ (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
+ }
+}
+
+TEST_F(NullCheckEliminationTest, Loop) {
+ static const IFieldDef ifields[] = {
+ { 0u, 1u, 0u, 0u },
+ { 1u, 1u, 1u, 1u },
+ };
+ static const MIRDef mirs[] = {
+ DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u),
+ DEF_IGET_IPUT(4u, Instruction::IGET, 1u, 101u, 0u),
+ DEF_IGET_IPUT(5u, Instruction::IGET, 2u, 100u, 1u), // Eliminated.
+ DEF_IGET_IPUT(5u, Instruction::IGET, 3u, 101u, 1u), // Eliminated.
+ DEF_IGET_IPUT(3u, Instruction::IGET, 4u, 102u, 0u),
+ DEF_IGET_IPUT(4u, Instruction::IGET, 5u, 102u, 1u), // Not eliminated (MOVE_OBJECT_16).
+ DEF_OTHER2(4u, Instruction::MOVE_OBJECT_16, 102u, 103u),
+ };
+ static const bool expected_ignore_null_check[] = {
+ false, false, true, true,
+ false, false, false,
+ };
+
+ PrepareIFields(ifields);
+ PrepareLoop();
+ PrepareMIRs(mirs);
+ PerformNullCheckElimination();
+ ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
+ for (size_t i = 0u; i != arraysize(mirs); ++i) {
+ EXPECT_EQ(expected_ignore_null_check[i],
+ (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
+ }
+}
+
+TEST_F(NullCheckEliminationTest, Catch) {
+ static const IFieldDef ifields[] = {
+ { 0u, 1u, 0u, 0u },
+ { 1u, 1u, 1u, 1u },
+ };
+ static const MIRDef mirs[] = {
+ DEF_IGET_IPUT(3u, Instruction::IGET, 0u, 100u, 0u), // Before the exception edge.
+ DEF_IGET_IPUT(3u, Instruction::IGET, 1u, 101u, 0u), // Before the exception edge.
+ DEF_IGET_IPUT(4u, Instruction::IGET, 2u, 102u, 0u), // After the exception edge.
+ DEF_IGET_IPUT(4u, Instruction::IGET, 3u, 103u, 0u), // After the exception edge.
+ DEF_IGET_IPUT(5u, Instruction::IGET, 4u, 100u, 1u), // In catch handler; eliminated.
+ DEF_IGET_IPUT(5u, Instruction::IGET, 5u, 102u, 1u), // In catch handler; not eliminated.
+ DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 100u, 0u), // Null check eliminated.
+ DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 101u, 1u), // Null check eliminated.
+ DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 102u, 0u), // Null check eliminated.
+ DEF_IGET_IPUT(6u, Instruction::IGET, 6u, 103u, 1u), // Null check not eliminated.
+ };
+ static const bool expected_ignore_null_check[] = {
+ false, false, false, false, true, false, true, true, true, false
+ };
+
+ PrepareIFields(ifields);
+ PrepareCatch();
+ PrepareMIRs(mirs);
+ PerformNullCheckElimination();
+ ASSERT_EQ(arraysize(expected_ignore_null_check), mir_count_);
+ for (size_t i = 0u; i != arraysize(mirs); ++i) {
+ EXPECT_EQ(expected_ignore_null_check[i],
+ (mirs_[i].optimization_flags & MIR_IGNORE_NULL_CHECK) != 0) << i;
+ }
+}
+
+// Undefine MIR_DEF for null check elimination.
+#undef MIR_DEF
+
} // namespace art
diff --git a/compiler/dex/pass_driver_me_opts.cc b/compiler/dex/pass_driver_me_opts.cc
index cd3ffd4..f83b96c 100644
--- a/compiler/dex/pass_driver_me_opts.cc
+++ b/compiler/dex/pass_driver_me_opts.cc
@@ -20,6 +20,7 @@
#include "dataflow_iterator.h"
#include "dataflow_iterator-inl.h"
#include "pass_driver_me_opts.h"
+#include "post_opt_passes.h"
namespace art {
@@ -35,11 +36,13 @@
const Pass* const PassDriver<PassDriverMEOpts>::g_passes[] = {
GetPassInstance<CacheFieldLoweringInfo>(),
GetPassInstance<CacheMethodLoweringInfo>(),
- GetPassInstance<SpecialMethodInliner>(),
- GetPassInstance<CodeLayout>(),
- GetPassInstance<NullCheckElimination>(),
- GetPassInstance<TypeInference>(),
+ GetPassInstance<CalculatePredecessors>(),
+ GetPassInstance<DFSOrders>(),
GetPassInstance<ClassInitCheckElimination>(),
+ GetPassInstance<SpecialMethodInliner>(),
+ GetPassInstance<NullCheckElimination>(),
+ GetPassInstance<CodeLayout>(),
+ GetPassInstance<TypeInference>(),
GetPassInstance<GlobalValueNumberingPass>(),
GetPassInstance<BBCombine>(),
GetPassInstance<BBOptimizations>(),
diff --git a/compiler/dex/pass_driver_me_post_opt.cc b/compiler/dex/pass_driver_me_post_opt.cc
index 4acab6c..e767139 100644
--- a/compiler/dex/pass_driver_me_post_opt.cc
+++ b/compiler/dex/pass_driver_me_post_opt.cc
@@ -33,8 +33,6 @@
const Pass* const PassDriver<PassDriverMEPostOpt>::g_passes[] = {
GetPassInstance<InitializeData>(),
GetPassInstance<ClearPhiInstructions>(),
- GetPassInstance<CalculatePredecessors>(),
- GetPassInstance<DFSOrders>(),
GetPassInstance<BuildDomination>(),
GetPassInstance<TopologicalSortOrders>(),
GetPassInstance<DefBlockMatrix>(),
diff --git a/compiler/dex/ssa_transformation.cc b/compiler/dex/ssa_transformation.cc
index 4388041..dd74976 100644
--- a/compiler/dex/ssa_transformation.cc
+++ b/compiler/dex/ssa_transformation.cc
@@ -66,8 +66,9 @@
}
void MIRGraph::RecordDFSOrders(BasicBlock* block) {
- DCHECK(temp_scoped_alloc_.get() != nullptr);
- ScopedArenaVector<BasicBlock*> succ(temp_scoped_alloc_->Adapter());
+ ScopedArenaAllocator allocator(&cu_->arena_stack);
+ ScopedArenaVector<BasicBlock*> succ(allocator.Adapter());
+ succ.reserve(GetNumBlocks());
MarkPreOrder(block);
succ.push_back(block);
while (!succ.empty()) {