summaryrefslogtreecommitdiffstats
path: root/compiler/dex/mir_optimization_test.cc
diff options
context:
space:
mode:
authorVladimir Marko <vmarko@google.com>2014-10-09 18:01:24 +0100
committerVladimir Marko <vmarko@google.com>2014-10-15 17:11:24 +0100
commit7baa6f8783b12bb4b159ed4648145be5912215f2 (patch)
treebb8bca51183039dcf5f375efebe3534503e29735 /compiler/dex/mir_optimization_test.cc
parent5c5efc253507eb43265997c9afcd778f72b6cef4 (diff)
downloadart-7baa6f8783b12bb4b159ed4648145be5912215f2.zip
art-7baa6f8783b12bb4b159ed4648145be5912215f2.tar.gz
art-7baa6f8783b12bb4b159ed4648145be5912215f2.tar.bz2
Rewrite null check elimination to work on dalvik regs.
And move the null check and class init check elimination before the SSA transformation. The new pass ordering is in anticipation of subsequent changes. (An improved class init check elimination can benefit special method inlining. An improved block combination pass before SSA transformation can improve compilation time.) Also add tests for the NCE. Change-Id: Ie4fb1880e06334a703295aef454b437d58a3e878
Diffstat (limited to 'compiler/dex/mir_optimization_test.cc')
-rw-r--r--compiler/dex/mir_optimization_test.cc559
1 files changed, 412 insertions, 147 deletions
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 @@ 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