diff options
| author | 2014-10-16 08:58:35 +0000 | |
|---|---|---|
| committer | 2014-10-16 08:58:35 +0000 | |
| commit | 8fc5acfd382bdc0d7920e8a13439b64344a8988a (patch) | |
| tree | 015f28fe6c7d853b54a70514b2bf2ca4c734e62c /compiler | |
| parent | 16b842af16286243baa5e1ad07ce25f14a730df3 (diff) | |
| parent | 7baa6f8783b12bb4b159ed4648145be5912215f2 (diff) | |
Merge "Rewrite null check elimination to work on dalvik regs."
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/dex/bb_optimizations.h | 4 | ||||
| -rw-r--r-- | compiler/dex/mir_dataflow.cc | 94 | ||||
| -rw-r--r-- | compiler/dex/mir_field_info.h | 1 | ||||
| -rw-r--r-- | compiler/dex/mir_graph.h | 32 | ||||
| -rw-r--r-- | compiler/dex/mir_optimization.cc | 230 | ||||
| -rw-r--r-- | compiler/dex/mir_optimization_test.cc | 559 | ||||
| -rw-r--r-- | compiler/dex/pass_driver_me_opts.cc | 7 | ||||
| -rw-r--r-- | compiler/dex/pass_driver_me_post_opt.cc | 2 | ||||
| -rw-r--r-- | compiler/dex/ssa_transformation.cc | 5 |
9 files changed, 585 insertions, 349 deletions
diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h index fce23bc2fb..3b1be514d6 100644 --- a/compiler/dex/bb_optimizations.h +++ b/compiler/dex/bb_optimizations.h @@ -143,7 +143,7 @@ class CodeLayout : public PassME { 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 TypeInference : public PassME { 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 51b6709533..6d3f229c78 100644 --- a/compiler/dex/mir_dataflow.cc +++ b/compiler/dex/mir_dataflow.cc @@ -118,10 +118,10 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { 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 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { 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 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { 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 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { 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 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { 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 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { 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 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { 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 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { 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 @@ const uint64_t MIRGraph::oat_data_flow_attributes_[kMirOpLast] = { 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 9745c412c9..1842a16840 100644 --- a/compiler/dex/mir_field_info.h +++ b/compiler/dex/mir_field_info.h @@ -137,6 +137,7 @@ class MirIFieldLoweringInfo : public MirFieldInfo { // 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 90cddedc7a..a7069b00e1 100644 --- a/compiler/dex/mir_graph.h +++ b/compiler/dex/mir_graph.h @@ -49,17 +49,14 @@ enum DataFlowAttributePos { 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 @@ enum DataFlowAttributePos { #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 @@ enum DataFlowAttributePos { #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 @@ enum DataFlowAttributePos { #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 @@ class MIRGraph { 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 01f55b9249..2a958532aa 100644 --- a/compiler/dex/mir_optimization.cc +++ b/compiler/dex/mir_optimization.cc @@ -827,7 +827,7 @@ bool MIRGraph::EliminateNullChecksGate() { 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 @@ bool MIRGraph::EliminateNullChecksGate() { * 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); - } - } 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]); - } - } - } + vregs_to_check->ClearBit(this_reg); } } else { - // Starting state is union of all incoming arcs + 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); - DCHECK(pred_bb->data_flow_info != nullptr); - if (temp_bit_matrix_[pred_bb->id] == nullptr) { - continue; + 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; + } + } + } } 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 @@ bool MIRGraph::EliminateNullChecks(BasicBlock* bb) { * 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 @@ bool MIRGraph::EliminateNullChecks(BasicBlock* bb) { 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 @@ void MIRGraph::EliminateNullChecksEnd() { 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 @@ bool MIRGraph::EliminateClassInitChecksGate() { // 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::EliminateClassInitChecksGate() { */ 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 @@ bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) { 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 @@ bool MIRGraph::EliminateClassInitChecks(BasicBlock* bb) { 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 55e547e56f..337d4efda3 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 @@ class ClassInitCheckEliminationTest : public testing::Test { }; 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 @@ class ClassInitCheckEliminationTest : public testing::Test { #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 @@ class ClassInitCheckEliminationTest : public testing::Test { 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 @@ class ClassInitCheckEliminationTest : public testing::Test { 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,15 +212,60 @@ class ClassInitCheckEliminationTest : public testing::Test { DoPrepareMIRs(defs, count); } + 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_; + CompilationUnit cu_; + size_t mir_count_; + MIR* mirs_; + 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->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()); + 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); @@ -196,21 +274,64 @@ class ClassInitCheckEliminationTest : public testing::Test { } ClassInitCheckEliminationTest() - : pool_(), - cu_(&pool_), - mir_count_(0u), - mirs_(nullptr), - code_item_(nullptr) { - cu_.mir_graph.reset(new MIRGraph(&cu_, &cu_.arena)); + : MirOptimizationTest() { } +}; - ArenaPool pool_; - CompilationUnit cu_; - size_t mir_count_; - MIR* mirs_; - DexFile::CodeItem* code_item_; +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 @@ TEST_F(ClassInitCheckEliminationTest, SingleBlock) { { 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 @@ TEST_F(ClassInitCheckEliminationTest, Diamond) { { 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 @@ TEST_F(ClassInitCheckEliminationTest, Diamond) { }; PrepareSFields(sfields); - PrepareBasicBlocks(bbs); + PrepareDiamond(); PrepareMIRs(mirs); PerformClassInitCheckElimination(); ASSERT_EQ(arraysize(expected_ignore_clinit_check), mir_count_); @@ -332,26 +438,18 @@ TEST_F(ClassInitCheckEliminationTest, Loop) { { 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 @@ TEST_F(ClassInitCheckEliminationTest, Catch) { { 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 @@ TEST_F(ClassInitCheckEliminationTest, Catch) { } } +#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 cd3ffd4cc8..f83b96caf5 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 @@ template<> const Pass* const PassDriver<PassDriverMEOpts>::g_passes[] = { GetPassInstance<CacheFieldLoweringInfo>(), GetPassInstance<CacheMethodLoweringInfo>(), + GetPassInstance<CalculatePredecessors>(), + GetPassInstance<DFSOrders>(), + GetPassInstance<ClassInitCheckElimination>(), GetPassInstance<SpecialMethodInliner>(), - GetPassInstance<CodeLayout>(), GetPassInstance<NullCheckElimination>(), + GetPassInstance<CodeLayout>(), GetPassInstance<TypeInference>(), - GetPassInstance<ClassInitCheckElimination>(), 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 4acab6c6e5..e767139f92 100644 --- a/compiler/dex/pass_driver_me_post_opt.cc +++ b/compiler/dex/pass_driver_me_post_opt.cc @@ -33,8 +33,6 @@ template<> 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 4388041fac..dd74976247 100644 --- a/compiler/dex/ssa_transformation.cc +++ b/compiler/dex/ssa_transformation.cc @@ -66,8 +66,9 @@ void MIRGraph::MarkPreOrder(BasicBlock* block) { } 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()) { |