diff options
Diffstat (limited to 'compiler/optimizing/load_store_elimination_test.cc')
-rw-r--r-- | compiler/optimizing/load_store_elimination_test.cc | 7196 |
1 files changed, 5725 insertions, 1471 deletions
diff --git a/compiler/optimizing/load_store_elimination_test.cc b/compiler/optimizing/load_store_elimination_test.cc index 9904192501..9b000a1b1a 100644 --- a/compiler/optimizing/load_store_elimination_test.cc +++ b/compiler/optimizing/load_store_elimination_test.cc @@ -14,38 +14,99 @@ * limitations under the License. */ +#include "load_store_elimination.h" + +#include <initializer_list> +#include <memory> #include <tuple> +#include <variant> +#include "base/iteration_range.h" #include "compilation_kind.h" +#include "dex/dex_file_types.h" +#include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "gtest/gtest.h" #include "handle_scope.h" #include "load_store_analysis.h" -#include "load_store_elimination.h" #include "nodes.h" +#include "optimizing/data_type.h" +#include "optimizing/instruction_simplifier.h" +#include "optimizing/optimizing_compiler_stats.h" #include "optimizing_unit_test.h" - -#include "gtest/gtest.h" +#include "scoped_thread_state_change.h" namespace art { -class LoadStoreEliminationTest : public OptimizingUnitTest { +struct InstructionDumper { + public: + HInstruction* ins_; +}; + +bool operator==(const InstructionDumper& a, const InstructionDumper& b) { + return a.ins_ == b.ins_; +} +bool operator!=(const InstructionDumper& a, const InstructionDumper& b) { + return !(a == b); +} + +std::ostream& operator<<(std::ostream& os, const InstructionDumper& id) { + if (id.ins_ == nullptr) { + return os << "NULL"; + } else { + return os << "(" << id.ins_ << "): " << id.ins_->DumpWithArgs(); + } +} + +#define CHECK_SUBROUTINE_FAILURE() \ + do { \ + if (HasFatalFailure()) { \ + return; \ + } \ + } while (false) + +#define EXPECT_INS_EQ(a, b) EXPECT_EQ(InstructionDumper{a}, InstructionDumper{b}) +#define EXPECT_INS_REMOVED(a) EXPECT_TRUE(IsRemoved(a)) << "Not removed: " << (InstructionDumper{a}) +#define EXPECT_INS_RETAINED(a) EXPECT_FALSE(IsRemoved(a)) << "Removed: " << (InstructionDumper{a}) +#define ASSERT_INS_EQ(a, b) ASSERT_EQ(InstructionDumper{a}, InstructionDumper{b}) +#define ASSERT_INS_REMOVED(a) ASSERT_TRUE(IsRemoved(a)) << "Not removed: " << (InstructionDumper{a}) +#define ASSERT_INS_RETAINED(a) ASSERT_FALSE(IsRemoved(a)) << "Removed: " << (InstructionDumper{a}) + +template <typename SuperTest> +class LoadStoreEliminationTestBase : public SuperTest, public OptimizingUnitTestHelper { public: - AdjacencyListGraph SetupFromAdjacencyList( - const std::string_view entry_name, - const std::string_view exit_name, - const std::vector<AdjacencyListGraph::Edge>& adj) { + void SetUp() override { + SuperTest::SetUp(); + gLogVerbosity.compiler = true; + } + + void TearDown() override { + SuperTest::TearDown(); + gLogVerbosity.compiler = false; + } + + AdjacencyListGraph SetupFromAdjacencyList(const std::string_view entry_name, + const std::string_view exit_name, + const std::vector<AdjacencyListGraph::Edge>& adj) { return AdjacencyListGraph(graph_, GetAllocator(), entry_name, exit_name, adj); } - void PerformLSE() { + void PerformLSE(bool with_partial = true) { graph_->BuildDominatorTree(); - LoadStoreElimination lse(graph_, /*stats=*/ nullptr); - lse.Run(); + LoadStoreElimination lse(graph_, /*stats=*/nullptr); + lse.Run(with_partial); std::ostringstream oss; EXPECT_TRUE(CheckGraphSkipRefTypeInfoChecks(oss)) << oss.str(); } + void PerformLSEWithPartial() { + PerformLSE(true); + } + + void PerformLSENoPartial() { + PerformLSE(false); + } + // Create instructions shared among tests. void CreateEntryBlockInstructions() { HInstruction* c1 = graph_->GetIntConstant(1); @@ -108,9 +169,7 @@ class LoadStoreEliminationTest : public OptimizingUnitTest { } void CreateEnvForSuspendCheck() { - ArenaVector<HInstruction*> current_locals({array_, i_, j_}, - GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(suspend_check_, ¤t_locals); + ManuallyBuildEnvFor(suspend_check_, {array_, i_, j_}); } // Create the diamond-shaped CFG: @@ -153,15 +212,15 @@ class LoadStoreEliminationTest : public OptimizingUnitTest { DCHECK(block != nullptr); DCHECK(array != nullptr); DCHECK(index != nullptr); - HInstruction* vload = new (GetAllocator()) HVecLoad( - GetAllocator(), - array, - index, - DataType::Type::kInt32, - SideEffects::ArrayReadOfType(DataType::Type::kInt32), - 4, - /*is_string_char_at*/ false, - kNoDexPc); + HInstruction* vload = + new (GetAllocator()) HVecLoad(GetAllocator(), + array, + index, + DataType::Type::kInt32, + SideEffects::ArrayReadOfType(DataType::Type::kInt32), + 4, + /*is_string_char_at*/ false, + kNoDexPc); block->InsertInstructionBefore(vload, block->GetLastInstruction()); return vload; } @@ -179,22 +238,19 @@ class LoadStoreEliminationTest : public OptimizingUnitTest { DCHECK(index != nullptr); if (vdata == nullptr) { HInstruction* c1 = graph_->GetIntConstant(1); - vdata = new (GetAllocator()) HVecReplicateScalar(GetAllocator(), - c1, - DataType::Type::kInt32, - 4, - kNoDexPc); + vdata = new (GetAllocator()) + HVecReplicateScalar(GetAllocator(), c1, DataType::Type::kInt32, 4, kNoDexPc); block->InsertInstructionBefore(vdata, block->GetLastInstruction()); } - HInstruction* vstore = new (GetAllocator()) HVecStore( - GetAllocator(), - array, - index, - vdata, - DataType::Type::kInt32, - SideEffects::ArrayWriteOfType(DataType::Type::kInt32), - 4, - kNoDexPc); + HInstruction* vstore = + new (GetAllocator()) HVecStore(GetAllocator(), + array, + index, + vdata, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 4, + kNoDexPc); block->InsertInstructionBefore(vstore, block->GetLastInstruction()); return vstore; } @@ -225,34 +281,153 @@ class LoadStoreEliminationTest : public OptimizingUnitTest { if (data == nullptr) { data = graph_->GetIntConstant(1); } - HInstruction* store = new (GetAllocator()) HArraySet(array, - index, - data, - DataType::Type::kInt32, - 0); + HInstruction* store = + new (GetAllocator()) HArraySet(array, index, data, DataType::Type::kInt32, 0); block->InsertInstructionBefore(store, block->GetLastInstruction()); return store; } void InitGraphAndParameters() { InitGraph(); - AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(), - dex::TypeIndex(0), - 0, - DataType::Type::kInt32)); + AddParameter(new (GetAllocator()) HParameterValue( + graph_->GetDexFile(), dex::TypeIndex(0), 0, DataType::Type::kInt32)); array_ = parameters_.back(); - AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(), - dex::TypeIndex(1), - 1, - DataType::Type::kInt32)); + AddParameter(new (GetAllocator()) HParameterValue( + graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kInt32)); i_ = parameters_.back(); - AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(), - dex::TypeIndex(1), - 2, - DataType::Type::kInt32)); + AddParameter(new (GetAllocator()) HParameterValue( + graph_->GetDexFile(), dex::TypeIndex(1), 2, DataType::Type::kInt32)); j_ = parameters_.back(); } + void ManuallyBuildEnvFor(HInstruction* ins, const std::initializer_list<HInstruction*>& env) { + ArenaVector<HInstruction*> current_locals(env, GetAllocator()->Adapter(kArenaAllocInstruction)); + OptimizingUnitTestHelper::ManuallyBuildEnvFor(ins, ¤t_locals); + } + + HLoadClass* MakeClassLoad(std::optional<dex::TypeIndex> ti = std::nullopt) { + return new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + ti ? *ti : dex::TypeIndex(class_idx_++), + graph_->GetDexFile(), + /* klass= */ null_klass_, + /* is_referrers_class= */ false, + /* dex_pc= */ 0, + /* needs_access_check= */ false); + } + + HNewInstance* MakeNewInstance(HInstruction* cls, uint32_t dex_pc = 0u) { + EXPECT_TRUE(cls->IsLoadClass() || cls->IsClinitCheck()) << *cls; + HLoadClass* load = + cls->IsLoadClass() ? cls->AsLoadClass() : cls->AsClinitCheck()->GetLoadClass(); + return new (GetAllocator()) HNewInstance(cls, + dex_pc, + load->GetTypeIndex(), + graph_->GetDexFile(), + /* finalizable= */ false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); + } + + HInstanceFieldSet* MakeIFieldSet(HInstruction* inst, + HInstruction* data, + MemberOffset off, + uint32_t dex_pc = 0u) { + return new (GetAllocator()) HInstanceFieldSet(inst, + data, + /* field= */ nullptr, + /* field_type= */ data->GetType(), + /* field_offset= */ off, + /* is_volatile= */ false, + /* field_idx= */ 0, + /* declaring_class_def_index= */ 0, + graph_->GetDexFile(), + dex_pc); + } + + HInstanceFieldGet* MakeIFieldGet(HInstruction* inst, + DataType::Type type, + MemberOffset off, + uint32_t dex_pc = 0u) { + return new (GetAllocator()) HInstanceFieldGet(inst, + /* field= */ nullptr, + /* field_type= */ type, + /* field_offset= */ off, + /* is_volatile= */ false, + /* field_idx= */ 0, + /* declaring_class_def_index= */ 0, + graph_->GetDexFile(), + dex_pc); + } + + HInvokeStaticOrDirect* MakeInvoke(DataType::Type return_type, + const std::vector<HInstruction*>& args) { + MethodReference method_reference{/* file= */ &graph_->GetDexFile(), /* index= */ method_idx_++}; + HInvokeStaticOrDirect* res = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + args.size(), + return_type, + /* dex_pc= */ 0, + method_reference, + /* resolved_method= */ nullptr, + HInvokeStaticOrDirect::DispatchInfo{}, + InvokeType::kStatic, + /* resolved_method_reference= */ method_reference, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + for (auto [ins, idx] : ZipCount(MakeIterationRange(args))) { + res->SetRawInputAt(idx, ins); + } + return res; + } + + HPhi* MakePhi(const std::vector<HInstruction*>& ins) { + EXPECT_GE(ins.size(), 2u) << "Phi requires at least 2 inputs"; + HPhi* phi = + new (GetAllocator()) HPhi(GetAllocator(), kNoRegNumber, ins.size(), ins[0]->GetType()); + for (auto [i, idx] : ZipCount(MakeIterationRange(ins))) { + phi->SetRawInputAt(idx, i); + } + return phi; + } + + void SetupExit(HBasicBlock* exit) { + exit->AddInstruction(new (GetAllocator()) HExit()); + } + + dex::TypeIndex DefaultTypeIndexForType(DataType::Type type) { + switch (type) { + case DataType::Type::kBool: + return dex::TypeIndex(1); + case DataType::Type::kUint8: + case DataType::Type::kInt8: + return dex::TypeIndex(2); + case DataType::Type::kUint16: + case DataType::Type::kInt16: + return dex::TypeIndex(3); + case DataType::Type::kUint32: + case DataType::Type::kInt32: + return dex::TypeIndex(4); + case DataType::Type::kUint64: + case DataType::Type::kInt64: + return dex::TypeIndex(5); + case DataType::Type::kReference: + return dex::TypeIndex(6); + case DataType::Type::kFloat32: + return dex::TypeIndex(7); + case DataType::Type::kFloat64: + return dex::TypeIndex(8); + case DataType::Type::kVoid: + EXPECT_TRUE(false) << "No type for void!"; + return dex::TypeIndex(1000); + } + } + + // Creates a parameter. The instruction is automatically added to the entry-block + HParameterValue* MakeParam(DataType::Type type, std::optional<dex::TypeIndex> ti = std::nullopt) { + HParameterValue* val = new (GetAllocator()) HParameterValue( + graph_->GetDexFile(), ti ? *ti : DefaultTypeIndexForType(type), param_count_++, type); + graph_->GetEntryBlock()->AddInstruction(val); + return val; + } + HBasicBlock* pre_header_; HBasicBlock* loop_; @@ -264,6 +439,208 @@ class LoadStoreEliminationTest : public OptimizingUnitTest { HInstruction* suspend_check_; HPhi* phi_; + + size_t param_count_ = 0; + size_t class_idx_ = 42; + uint32_t method_idx_ = 100; + + ScopedNullHandle<mirror::Class> null_klass_; +}; + +class LoadStoreEliminationTest : public LoadStoreEliminationTestBase<CommonCompilerTest> {}; + +enum class TestOrder { kSameAsAlloc, kReverseOfAlloc }; +std::ostream& operator<<(std::ostream& os, const TestOrder& ord) { + switch (ord) { + case TestOrder::kSameAsAlloc: + return os << "SameAsAlloc"; + case TestOrder::kReverseOfAlloc: + return os << "ReverseOfAlloc"; + } +} + +class OrderDependentTestGroup + : public LoadStoreEliminationTestBase<CommonCompilerTestWithParam<TestOrder>> {}; + +// Various configs we can use for testing. Currently used in PartialComparison tests. +struct PartialComparisonKind { + public: + enum class Type : uint8_t { kEquals, kNotEquals }; + enum class Target : uint8_t { kNull, kValue, kSelf }; + enum class Position : uint8_t { kLeft, kRight }; + + const Type type_; + const Target target_; + const Position position_; + + bool IsDefinitelyFalse() const { + return !IsPossiblyTrue(); + } + bool IsPossiblyFalse() const { + return !IsDefinitelyTrue(); + } + bool IsDefinitelyTrue() const { + if (target_ == Target::kSelf) { + return type_ == Type::kEquals; + } else if (target_ == Target::kNull) { + return type_ == Type::kNotEquals; + } else { + return false; + } + } + bool IsPossiblyTrue() const { + if (target_ == Target::kSelf) { + return type_ == Type::kEquals; + } else if (target_ == Target::kNull) { + return type_ == Type::kNotEquals; + } else { + return true; + } + } + std::ostream& Dump(std::ostream& os) const { + os << "PartialComparisonKind{" << (type_ == Type::kEquals ? "kEquals" : "kNotEquals") << ", " + << (target_ == Target::kNull ? "kNull" : (target_ == Target::kSelf ? "kSelf" : "kValue")) + << ", " << (position_ == Position::kLeft ? "kLeft" : "kRight") << "}"; + return os; + } +}; + +std::ostream& operator<<(std::ostream& os, const PartialComparisonKind& comp) { + return comp.Dump(os); +} + +class PartialComparisonTestGroup + : public LoadStoreEliminationTestBase<CommonCompilerTestWithParam<PartialComparisonKind>> { + public: + enum class ComparisonPlacement { + kBeforeEscape, + kInEscape, + kAfterEscape, + }; + void CheckFinalInstruction(HInstruction* ins, ComparisonPlacement placement) { + using Target = PartialComparisonKind::Target; + using Type = PartialComparisonKind::Type; + using Position = PartialComparisonKind::Position; + PartialComparisonKind kind = GetParam(); + if (ins->IsIntConstant()) { + if (kind.IsDefinitelyTrue()) { + EXPECT_TRUE(ins->AsIntConstant()->IsTrue()) << kind << " " << *ins; + } else if (kind.IsDefinitelyFalse()) { + EXPECT_TRUE(ins->AsIntConstant()->IsFalse()) << kind << " " << *ins; + } else { + EXPECT_EQ(placement, ComparisonPlacement::kBeforeEscape); + EXPECT_EQ(kind.target_, Target::kValue); + // We are before escape so value is not the object + if (kind.type_ == Type::kEquals) { + EXPECT_TRUE(ins->AsIntConstant()->IsFalse()) << kind << " " << *ins; + } else { + EXPECT_TRUE(ins->AsIntConstant()->IsTrue()) << kind << " " << *ins; + } + } + return; + } + EXPECT_NE(placement, ComparisonPlacement::kBeforeEscape) + << "For comparisons before escape we should always be able to transform into a constant." + << " Instead we got:" << std::endl << ins->DumpWithArgs(); + if (placement == ComparisonPlacement::kInEscape) { + // Should be the same type. + ASSERT_TRUE(ins->IsEqual() || ins->IsNotEqual()) << *ins; + HInstruction* other = kind.position_ == Position::kLeft ? ins->AsBinaryOperation()->GetRight() + : ins->AsBinaryOperation()->GetLeft(); + if (kind.target_ == Target::kSelf) { + EXPECT_INS_EQ(ins->AsBinaryOperation()->GetLeft(), ins->AsBinaryOperation()->GetRight()) + << " ins is: " << *ins; + } else if (kind.target_ == Target::kNull) { + EXPECT_INS_EQ(other, graph_->GetNullConstant()) << " ins is: " << *ins; + } else { + EXPECT_TRUE(other->IsStaticFieldGet()) << " ins is: " << *ins; + } + if (kind.type_ == Type::kEquals) { + EXPECT_TRUE(ins->IsEqual()) << *ins; + } else { + EXPECT_TRUE(ins->IsNotEqual()) << *ins; + } + } else { + ASSERT_EQ(placement, ComparisonPlacement::kAfterEscape); + if (kind.type_ == Type::kEquals) { + // obj == <anything> can only be true if (1) it's obj == obj or (2) obj has escaped. + ASSERT_TRUE(ins->IsAnd()) << ins->DumpWithArgs(); + EXPECT_TRUE(ins->InputAt(1)->IsEqual()) << ins->DumpWithArgs(); + } else { + // obj != <anything> is true if (2) obj has escaped. + ASSERT_TRUE(ins->IsOr()) << ins->DumpWithArgs(); + EXPECT_TRUE(ins->InputAt(1)->IsNotEqual()) << ins->DumpWithArgs(); + } + // Check the first part of AND is the obj-has-escaped + ASSERT_TRUE(ins->InputAt(0)->IsNotEqual()) << ins->DumpWithArgs(); + EXPECT_TRUE(ins->InputAt(0)->InputAt(0)->IsPhi()) << ins->DumpWithArgs(); + EXPECT_TRUE(ins->InputAt(0)->InputAt(1)->IsNullConstant()) << ins->DumpWithArgs(); + // Check the second part of AND is the eq other + EXPECT_INS_EQ(ins->InputAt(1)->InputAt(kind.position_ == Position::kLeft ? 0 : 1), + ins->InputAt(0)->InputAt(0)) + << ins->DumpWithArgs(); + } + } + + struct ComparisonInstructions { + void AddSetup(HBasicBlock* blk) const { + for (HInstruction* i : setup_instructions_) { + blk->AddInstruction(i); + } + } + + void AddEnvironment(HEnvironment* env) const { + for (HInstruction* i : setup_instructions_) { + if (i->NeedsEnvironment()) { + i->CopyEnvironmentFrom(env); + } + } + } + + const std::vector<HInstruction*> setup_instructions_; + HInstruction* const cmp_; + }; + + ComparisonInstructions GetComparisonInstructions(HInstruction* partial) { + PartialComparisonKind kind = GetParam(); + std::vector<HInstruction*> setup; + HInstruction* target_other; + switch (kind.target_) { + case PartialComparisonKind::Target::kSelf: + target_other = partial; + break; + case PartialComparisonKind::Target::kNull: + target_other = graph_->GetNullConstant(); + break; + case PartialComparisonKind::Target::kValue: { + HInstruction* cls = MakeClassLoad(); + HInstruction* static_read = + new (GetAllocator()) HStaticFieldGet(cls, + /* field= */ nullptr, + DataType::Type::kReference, + /* field_offset= */ MemberOffset(40), + /* is_volatile= */ false, + /* field_idx= */ 0, + /* declaring_class_def_index= */ 0, + graph_->GetDexFile(), + /* dex_pc= */ 0); + setup.push_back(cls); + setup.push_back(static_read); + target_other = static_read; + break; + } + } + HInstruction* target_left; + HInstruction* target_right; + std::tie(target_left, target_right) = kind.position_ == PartialComparisonKind::Position::kLeft + ? std::pair{partial, target_other} + : std::pair{target_other, partial}; + HInstruction* cmp = + kind.type_ == PartialComparisonKind::Type::kEquals + ? static_cast<HInstruction*>(new (GetAllocator()) HEqual(target_left, target_right)) + : static_cast<HInstruction*>(new (GetAllocator()) HNotEqual(target_left, target_right)); + return {setup, cmp}; + } }; TEST_F(LoadStoreEliminationTest, ArrayGetSetElimination) { @@ -669,10 +1046,8 @@ TEST_F(LoadStoreEliminationTest, StoreAfterLoopWithSideEffects2) { // Add another array parameter that may alias with `array_`. // Note: We're not adding it to the suspend check environment. - AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(), - dex::TypeIndex(0), - 3, - DataType::Type::kInt32)); + AddParameter(new (GetAllocator()) HParameterValue( + graph_->GetDexFile(), dex::TypeIndex(0), 3, DataType::Type::kInt32)); HInstruction* array2 = parameters_.back(); HInstruction* c0 = graph_->GetIntConstant(0); @@ -931,43 +1306,14 @@ TEST_F(LoadStoreEliminationTest, DefaultShadowClass) { HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck(); entry->AddInstruction(suspend_check); entry->AddInstruction(new (GetAllocator()) HGoto()); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(suspend_check, ¤t_locals); - - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); + ManuallyBuildEnvFor(suspend_check, {}); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); HInstruction* const_fence = new (GetAllocator()) HConstructorFence(new_inst, 0, GetAllocator()); - HInstruction* set_field = new (GetAllocator()) HInstanceFieldSet(new_inst, - graph_->GetIntConstant(33), - nullptr, - DataType::Type::kReference, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* get_field = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kReference, - mirror::Object::ClassOffset(), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* set_field = MakeIFieldSet(new_inst, graph_->GetIntConstant(33), MemberOffset(32)); + HInstruction* get_field = + MakeIFieldGet(new_inst, DataType::Type::kReference, mirror::Object::ClassOffset()); HInstruction* return_val = new (GetAllocator()) HReturn(get_field); main->AddInstruction(cls); main->AddInstruction(new_inst); @@ -978,17 +1324,17 @@ TEST_F(LoadStoreEliminationTest, DefaultShadowClass) { cls->CopyEnvironmentFrom(suspend_check->GetEnvironment()); new_inst->CopyEnvironmentFrom(suspend_check->GetEnvironment()); - exit->AddInstruction(new (GetAllocator()) HExit()); + SetupExit(exit); graph_->ClearDominanceInformation(); PerformLSE(); - EXPECT_TRUE(IsRemoved(new_inst)); - EXPECT_TRUE(IsRemoved(const_fence)); - EXPECT_TRUE(IsRemoved(get_field)); - EXPECT_TRUE(IsRemoved(set_field)); - EXPECT_FALSE(IsRemoved(cls)); - EXPECT_EQ(cls, return_val->InputAt(0)); + EXPECT_INS_REMOVED(new_inst); + EXPECT_INS_REMOVED(const_fence); + EXPECT_INS_REMOVED(get_field); + EXPECT_INS_REMOVED(set_field); + EXPECT_INS_RETAINED(cls); + EXPECT_INS_EQ(cls, return_val->InputAt(0)); } // Object o = new Obj(); @@ -1011,43 +1357,14 @@ TEST_F(LoadStoreEliminationTest, DefaultShadowMonitor) { HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck(); entry->AddInstruction(suspend_check); entry->AddInstruction(new (GetAllocator()) HGoto()); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(suspend_check, ¤t_locals); - - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); + ManuallyBuildEnvFor(suspend_check, {}); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); HInstruction* const_fence = new (GetAllocator()) HConstructorFence(new_inst, 0, GetAllocator()); - HInstruction* set_field = new (GetAllocator()) HInstanceFieldSet(new_inst, - graph_->GetIntConstant(33), - nullptr, - DataType::Type::kReference, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* get_field = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - mirror::Object::MonitorOffset(), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* set_field = MakeIFieldSet(new_inst, graph_->GetIntConstant(33), MemberOffset(32)); + HInstruction* get_field = + MakeIFieldGet(new_inst, DataType::Type::kInt32, mirror::Object::MonitorOffset()); HInstruction* return_val = new (GetAllocator()) HReturn(get_field); main->AddInstruction(cls); main->AddInstruction(new_inst); @@ -1058,17 +1375,17 @@ TEST_F(LoadStoreEliminationTest, DefaultShadowMonitor) { cls->CopyEnvironmentFrom(suspend_check->GetEnvironment()); new_inst->CopyEnvironmentFrom(suspend_check->GetEnvironment()); - exit->AddInstruction(new (GetAllocator()) HExit()); + SetupExit(exit); graph_->ClearDominanceInformation(); PerformLSE(); - EXPECT_TRUE(IsRemoved(new_inst)); - EXPECT_TRUE(IsRemoved(const_fence)); - EXPECT_TRUE(IsRemoved(get_field)); - EXPECT_TRUE(IsRemoved(set_field)); - EXPECT_FALSE(IsRemoved(cls)); - EXPECT_EQ(graph_->GetIntConstant(0), return_val->InputAt(0)); + EXPECT_INS_REMOVED(new_inst); + EXPECT_INS_REMOVED(const_fence); + EXPECT_INS_REMOVED(get_field); + EXPECT_INS_REMOVED(set_field); + EXPECT_INS_RETAINED(cls); + EXPECT_INS_EQ(graph_->GetIntConstant(0), return_val->InputAt(0)); } // void DO_CAL() { @@ -1083,7 +1400,8 @@ TEST_F(LoadStoreEliminationTest, DefaultShadowMonitor) { // return t; // } TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) { - CreateGraph(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); AdjacencyListGraph blocks(graph_, GetAllocator(), "entry", @@ -1114,8 +1432,7 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) { loop_pre_header->AddInstruction(alloc_w); loop_pre_header->AddInstruction(pre_header_goto); // environment - ArenaVector<HInstruction*> alloc_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(alloc_w, &alloc_locals); + ManuallyBuildEnvFor(alloc_w, {}); // loop-start HPhi* i_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32); @@ -1140,44 +1457,18 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) { t_phi->AddInput(zero_const); // environment - ArenaVector<HInstruction*> suspend_locals({ alloc_w, i_phi, t_phi }, - GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(suspend, &suspend_locals); + ManuallyBuildEnvFor(suspend, { alloc_w, i_phi, t_phi }); // BODY HInstruction* last_i = new (GetAllocator()) HSub(DataType::Type::kInt32, i_phi, one_const); HInstruction* last_get = new (GetAllocator()) HArrayGet(alloc_w, last_i, DataType::Type::kInt32, 0); - HInvoke* body_value = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 2, - DataType::Type::kInt32, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - body_value->SetRawInputAt(0, last_get); - body_value->SetRawInputAt(1, one_const); + HInvoke* body_value = MakeInvoke(DataType::Type::kInt32, { last_get, one_const }); HInstruction* body_set = new (GetAllocator()) HArraySet(alloc_w, i_phi, body_value, DataType::Type::kInt32, 0); HInstruction* body_get = new (GetAllocator()) HArrayGet(alloc_w, i_phi, DataType::Type::kInt32, 0); - HInvoke* t_next = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 2, - DataType::Type::kInt32, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - t_next->SetRawInputAt(0, body_get); - t_next->SetRawInputAt(1, t_phi); + HInvoke* t_next = MakeInvoke(DataType::Type::kInt32, { body_get, t_phi }); HInstruction* i_next = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_phi, one_const); HInstruction* body_goto = new (GetAllocator()) HGoto(); loop_body->AddInstruction(last_i); @@ -1199,8 +1490,7 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) { loop_post->AddInstruction(return_inst); // exit - HInstruction* exit_inst = new (GetAllocator()) HExit(); - exit->AddInstruction(exit_inst); + SetupExit(exit); graph_->ClearDominanceInformation(); graph_->ClearLoopInformation(); @@ -1211,18 +1501,17 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) { // back into the array. if (IsRemoved(last_get)) { // If we were able to remove the previous read the entire array should be removable. - EXPECT_TRUE(IsRemoved(body_set)); - EXPECT_TRUE(IsRemoved(alloc_w)); + EXPECT_INS_REMOVED(body_set); + EXPECT_INS_REMOVED(alloc_w); } else { // This is the branch we actually take for now. If we rely on being able to // read the array we'd better remember to write to it as well. - EXPECT_FALSE(IsRemoved(body_set)); + EXPECT_INS_RETAINED(body_set); } // The last 'get' should always be removable. - EXPECT_TRUE(IsRemoved(body_get)); + EXPECT_INS_REMOVED(body_get); } - // void DO_CAL2() { // int i = 1; // int[] w = new int[80]; @@ -1239,7 +1528,8 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) { // return t; // } TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap2) { - CreateGraph(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); AdjacencyListGraph blocks(graph_, GetAllocator(), "entry", @@ -1270,8 +1560,7 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap2) { loop_pre_header->AddInstruction(alloc_w); loop_pre_header->AddInstruction(pre_header_goto); // environment - ArenaVector<HInstruction*> alloc_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(alloc_w, &alloc_locals); + ManuallyBuildEnvFor(alloc_w, {}); // loop-start HPhi* i_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32); @@ -1296,50 +1585,24 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap2) { t_phi->AddInput(zero_const); // environment - ArenaVector<HInstruction*> suspend_locals({ alloc_w, i_phi, t_phi }, - GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(suspend, &suspend_locals); + ManuallyBuildEnvFor(suspend, { alloc_w, i_phi, t_phi }); // BODY HInstruction* last_i = new (GetAllocator()) HSub(DataType::Type::kInt32, i_phi, one_const); - HInstruction* last_get_1, *last_get_2, *last_get_3; - HInstruction* body_value_1, *body_value_2, *body_value_3; - HInstruction* body_set_1, *body_set_2, *body_set_3; - HInstruction* body_get_1, *body_get_2, *body_get_3; - HInstruction* t_next_1, *t_next_2, *t_next_3; + HInstruction *last_get_1, *last_get_2, *last_get_3; + HInstruction *body_value_1, *body_value_2, *body_value_3; + HInstruction *body_set_1, *body_set_2, *body_set_3; + HInstruction *body_get_1, *body_get_2, *body_get_3; + HInstruction *t_next_1, *t_next_2, *t_next_3; auto make_instructions = [&](HInstruction* last_t_value) { HInstruction* last_get = new (GetAllocator()) HArrayGet(alloc_w, last_i, DataType::Type::kInt32, 0); - HInvoke* body_value = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 2, - DataType::Type::kInt32, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - body_value->SetRawInputAt(0, last_get); - body_value->SetRawInputAt(1, one_const); + HInvoke* body_value = MakeInvoke(DataType::Type::kInt32, { last_get, one_const }); HInstruction* body_set = new (GetAllocator()) HArraySet(alloc_w, i_phi, body_value, DataType::Type::kInt32, 0); HInstruction* body_get = new (GetAllocator()) HArrayGet(alloc_w, i_phi, DataType::Type::kInt32, 0); - HInvoke* t_next = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 2, - DataType::Type::kInt32, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - t_next->SetRawInputAt(0, body_get); - t_next->SetRawInputAt(1, last_t_value); + HInvoke* t_next = MakeInvoke(DataType::Type::kInt32, { body_get, last_t_value }); loop_body->AddInstruction(last_get); loop_body->AddInstruction(body_value); loop_body->AddInstruction(body_set); @@ -1372,8 +1635,7 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap2) { loop_post->AddInstruction(return_inst); // exit - HInstruction* exit_inst = new (GetAllocator()) HExit(); - exit->AddInstruction(exit_inst); + SetupExit(exit); graph_->ClearDominanceInformation(); graph_->ClearLoopInformation(); @@ -1384,28 +1646,29 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap2) { // back into the array. if (IsRemoved(last_get_1)) { // If we were able to remove the previous read the entire array should be removable. - EXPECT_TRUE(IsRemoved(body_set_1)); - EXPECT_TRUE(IsRemoved(body_set_2)); - EXPECT_TRUE(IsRemoved(body_set_3)); - EXPECT_TRUE(IsRemoved(last_get_1)); - EXPECT_TRUE(IsRemoved(last_get_2)); - EXPECT_TRUE(IsRemoved(alloc_w)); + EXPECT_INS_REMOVED(body_set_1); + EXPECT_INS_REMOVED(body_set_2); + EXPECT_INS_REMOVED(body_set_3); + EXPECT_INS_REMOVED(last_get_1); + EXPECT_INS_REMOVED(last_get_2); + EXPECT_INS_REMOVED(alloc_w); } else { // This is the branch we actually take for now. If we rely on being able to // read the array we'd better remember to write to it as well. - EXPECT_FALSE(IsRemoved(body_set_3)); + EXPECT_INS_RETAINED(body_set_3); } // The last 'get' should always be removable. - EXPECT_TRUE(IsRemoved(body_get_1)); - EXPECT_TRUE(IsRemoved(body_get_2)); - EXPECT_TRUE(IsRemoved(body_get_3)); + EXPECT_INS_REMOVED(body_get_1); + EXPECT_INS_REMOVED(body_get_2); + EXPECT_INS_REMOVED(body_get_3); // shadowed writes should always be removed - EXPECT_TRUE(IsRemoved(body_set_1)); - EXPECT_TRUE(IsRemoved(body_set_2)); + EXPECT_INS_REMOVED(body_set_1); + EXPECT_INS_REMOVED(body_set_2); } TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) { - CreateGraph(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); AdjacencyListGraph blocks(graph_, GetAllocator(), "entry", @@ -1428,10 +1691,9 @@ TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) { HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0); HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1); HInstruction* two_const = graph_->GetConstant(DataType::Type::kInt32, 2); - HInstruction* param = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 0, DataType::Type::kBool); + HInstruction* param = MakeParam(DataType::Type::kBool); + HInstruction* entry_goto = new (GetAllocator()) HGoto(); - entry->AddInstruction(param); entry->AddInstruction(entry_goto); HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, two_const, 0, 0); @@ -1439,22 +1701,10 @@ TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) { start->AddInstruction(alloc_w); start->AddInstruction(branch); // environment - ArenaVector<HInstruction*> alloc_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(alloc_w, &alloc_locals); + ManuallyBuildEnvFor(alloc_w, {}); // left - HInvoke* left_value = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kInt32, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - left_value->SetRawInputAt(0, zero_const); + HInvoke* left_value = MakeInvoke(DataType::Type::kInt32, { zero_const }); HInstruction* left_set_1 = new (GetAllocator()) HArraySet(alloc_w, zero_const, left_value, DataType::Type::kInt32, 0); HInstruction* left_set_2 = @@ -1464,23 +1714,10 @@ TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) { left->AddInstruction(left_set_1); left->AddInstruction(left_set_2); left->AddInstruction(left_goto); - ArenaVector<HInstruction*> left_locals({ alloc_w }, - GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(left_value, &alloc_locals); + ManuallyBuildEnvFor(left_value, { alloc_w }); // right - HInvoke* right_value = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kInt32, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - right_value->SetRawInputAt(0, one_const); + HInvoke* right_value = MakeInvoke(DataType::Type::kInt32, { one_const }); HInstruction* right_set_1 = new (GetAllocator()) HArraySet(alloc_w, zero_const, right_value, DataType::Type::kInt32, 0); HInstruction* right_set_2 = @@ -1490,9 +1727,7 @@ TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) { right->AddInstruction(right_set_1); right->AddInstruction(right_set_2); right->AddInstruction(right_goto); - ArenaVector<HInstruction*> right_locals({ alloc_w }, - GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(right_value, &alloc_locals); + ManuallyBuildEnvFor(right_value, { alloc_w }); // ret HInstruction* read_1 = @@ -1507,27 +1742,27 @@ TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) { ret->AddInstruction(return_inst); // exit - HInstruction* exit_inst = new (GetAllocator()) HExit(); - exit->AddInstruction(exit_inst); + SetupExit(exit); graph_->ClearDominanceInformation(); graph_->ClearLoopInformation(); PerformLSE(); - EXPECT_TRUE(IsRemoved(read_1)); - EXPECT_TRUE(IsRemoved(read_2)); - EXPECT_TRUE(IsRemoved(left_set_1)); - EXPECT_TRUE(IsRemoved(left_set_2)); - EXPECT_TRUE(IsRemoved(right_set_1)); - EXPECT_TRUE(IsRemoved(right_set_2)); - EXPECT_TRUE(IsRemoved(alloc_w)); + EXPECT_INS_REMOVED(read_1); + EXPECT_INS_REMOVED(read_2); + EXPECT_INS_REMOVED(left_set_1); + EXPECT_INS_REMOVED(left_set_2); + EXPECT_INS_REMOVED(right_set_1); + EXPECT_INS_REMOVED(right_set_2); + EXPECT_INS_REMOVED(alloc_w); - EXPECT_FALSE(IsRemoved(left_value)); - EXPECT_FALSE(IsRemoved(right_value)); + EXPECT_INS_RETAINED(left_value); + EXPECT_INS_RETAINED(right_value); } TEST_F(LoadStoreEliminationTest, ArrayMergeDefault) { - CreateGraph(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); AdjacencyListGraph blocks(graph_, GetAllocator(), "entry", @@ -1550,10 +1785,9 @@ TEST_F(LoadStoreEliminationTest, ArrayMergeDefault) { HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0); HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1); HInstruction* two_const = graph_->GetConstant(DataType::Type::kInt32, 2); - HInstruction* param = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 0, DataType::Type::kBool); + HInstruction* param = MakeParam(DataType::Type::kBool); HInstruction* entry_goto = new (GetAllocator()) HGoto(); - entry->AddInstruction(param); + entry->AddInstruction(entry_goto); HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, two_const, 0, 0); @@ -1562,7 +1796,7 @@ TEST_F(LoadStoreEliminationTest, ArrayMergeDefault) { start->AddInstruction(branch); // environment ArenaVector<HInstruction*> alloc_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(alloc_w, &alloc_locals); + ManuallyBuildEnvFor(alloc_w, {}); // left HInstruction* left_set_1 = @@ -1597,20 +1831,19 @@ TEST_F(LoadStoreEliminationTest, ArrayMergeDefault) { ret->AddInstruction(return_inst); // exit - HInstruction* exit_inst = new (GetAllocator()) HExit(); - exit->AddInstruction(exit_inst); + SetupExit(exit); graph_->ClearDominanceInformation(); graph_->ClearLoopInformation(); PerformLSE(); - EXPECT_TRUE(IsRemoved(read_1)); - EXPECT_TRUE(IsRemoved(read_2)); - EXPECT_TRUE(IsRemoved(left_set_1)); - EXPECT_TRUE(IsRemoved(left_set_2)); - EXPECT_TRUE(IsRemoved(right_set_1)); - EXPECT_TRUE(IsRemoved(right_set_2)); - EXPECT_TRUE(IsRemoved(alloc_w)); + EXPECT_INS_REMOVED(read_1); + EXPECT_INS_REMOVED(read_2); + EXPECT_INS_REMOVED(left_set_1); + EXPECT_INS_REMOVED(left_set_2); + EXPECT_INS_REMOVED(right_set_1); + EXPECT_INS_REMOVED(right_set_2); + EXPECT_INS_REMOVED(alloc_w); } // // ENTRY @@ -1651,22 +1884,22 @@ TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) { CreateGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - { { "entry", "bswitch" }, - { "bswitch", "case1" }, - { "bswitch", "case2" }, - { "bswitch", "case3" }, - { "case1", "breturn" }, - { "case2", "breturn" }, - { "case3", "loop_pre_header" }, - { "loop_pre_header", "loop_header" }, - { "loop_header", "loop_body" }, - { "loop_body", "loop_if_left" }, - { "loop_body", "loop_if_right" }, - { "loop_if_left", "loop_end" }, - { "loop_if_right", "loop_end" }, - { "loop_end", "loop_header" }, - { "loop_header", "breturn" }, - { "breturn", "exit" } })); + {{"entry", "bswitch"}, + {"bswitch", "case1"}, + {"bswitch", "case2"}, + {"bswitch", "case3"}, + {"case1", "breturn"}, + {"case2", "breturn"}, + {"case3", "loop_pre_header"}, + {"loop_pre_header", "loop_header"}, + {"loop_header", "loop_body"}, + {"loop_body", "loop_if_left"}, + {"loop_body", "loop_if_right"}, + {"loop_if_left", "loop_end"}, + {"loop_if_right", "loop_end"}, + {"loop_end", "loop_header"}, + {"loop_header", "breturn"}, + {"breturn", "exit"}})); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(bswitch); @@ -1683,104 +1916,41 @@ TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) { GET_BLOCK(loop_if_right); GET_BLOCK(loop_end); #undef GET_BLOCK - HInstruction* switch_val = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kInt32); + HInstruction* switch_val = MakeParam(DataType::Type::kInt32); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); HInstruction* c3 = graph_->GetIntConstant(3); HInstruction* c5 = graph_->GetIntConstant(5); - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); HInstruction* entry_goto = new (GetAllocator()) HGoto(); - entry->AddInstruction(switch_val); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(entry_goto); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(cls, ¤t_locals); + ManuallyBuildEnvFor(cls, {}); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, switch_val); bswitch->AddInstruction(switch_inst); - HInstruction* write_c1 = new (GetAllocator()) HInstanceFieldSet(new_inst, - c1, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* call_c1 = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* write_c1 = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* call_c1 = MakeInvoke(DataType::Type::kVoid, { new_inst }); HInstruction* goto_c1 = new (GetAllocator()) HGoto(); - call_c1->AsInvoke()->SetRawInputAt(0, new_inst); case1->AddInstruction(write_c1); case1->AddInstruction(call_c1); case1->AddInstruction(goto_c1); call_c1->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_c2 = new (GetAllocator()) HInstanceFieldSet(new_inst, - c2, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* call_c2 = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* write_c2 = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* call_c2 = MakeInvoke(DataType::Type::kVoid, { new_inst }); HInstruction* goto_c2 = new (GetAllocator()) HGoto(); - call_c2->AsInvoke()->SetRawInputAt(0, new_inst); case2->AddInstruction(write_c2); case2->AddInstruction(call_c2); case2->AddInstruction(goto_c2); call_c2->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_c3 = new (GetAllocator()) HInstanceFieldSet(new_inst, - c3, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_c3 = MakeIFieldSet(new_inst, c3, MemberOffset(32)); HInstruction* goto_c3 = new (GetAllocator()) HGoto(); case3->AddInstruction(write_c3); case3->AddInstruction(goto_c3); @@ -1789,17 +1959,7 @@ TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) { loop_pre_header->AddInstruction(goto_preheader); HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck(); - HInstruction* call_loop_header = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 0, - DataType::Type::kBool, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* call_loop_header = MakeInvoke(DataType::Type::kBool, {}); HInstruction* if_loop_header = new (GetAllocator()) HIf(call_loop_header); loop_header->AddInstruction(suspend_check_header); loop_header->AddInstruction(call_loop_header); @@ -1807,17 +1967,7 @@ TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) { call_loop_header->CopyEnvironmentFrom(cls->GetEnvironment()); suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* call_loop_body = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 0, - DataType::Type::kBool, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {}); HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body); loop_body->AddInstruction(call_loop_body); loop_body->AddInstruction(if_loop_body); @@ -1826,16 +1976,7 @@ TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) { HInstruction* goto_loop_left = new (GetAllocator()) HGoto(); loop_if_left->AddInstruction(goto_loop_left); - HInstruction* write_loop_right = new (GetAllocator()) HInstanceFieldSet(new_inst, - c5, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32)); HInstruction* goto_loop_right = new (GetAllocator()) HGoto(); loop_if_right->AddInstruction(write_loop_right); loop_if_right->AddInstruction(goto_loop_right); @@ -1843,31 +1984,23 @@ TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) { HInstruction* goto_loop_end = new (GetAllocator()) HGoto(); loop_end->AddInstruction(goto_loop_end); - HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); breturn->AddInstruction(read_bottom); breturn->AddInstruction(return_exit); - HInstruction* exit_ins = new (GetAllocator()) HExit(); - exit->AddInstruction(exit_ins); + SetupExit(exit); + // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); - PerformLSE(); - - EXPECT_FALSE(IsRemoved(read_bottom)); - EXPECT_FALSE(IsRemoved(write_c1)); - EXPECT_FALSE(IsRemoved(write_c2)); - EXPECT_FALSE(IsRemoved(write_c3)); - // EXPECT_FALSE(IsRemoved(write_loop_left)); - EXPECT_FALSE(IsRemoved(write_loop_right)); + LOG(INFO) << "Pre LSE " << blks; + PerformLSENoPartial(); + + EXPECT_INS_RETAINED(read_bottom); + EXPECT_INS_RETAINED(write_c1); + EXPECT_INS_RETAINED(write_c2); + EXPECT_INS_RETAINED(write_c3); + EXPECT_INS_RETAINED(write_loop_right); } // // ENTRY @@ -1887,7 +2020,8 @@ TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) { // EXIT // return PHI(foo_l, foo_r) TEST_F(LoadStoreEliminationTest, PartialLoadElimination) { - InitGraph(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit_REAL", { { "entry", "left" }, @@ -1899,99 +2033,37 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination) { HBasicBlock* left = blks.Get("left"); HBasicBlock* right = blks.Get("right"); HBasicBlock* exit = blks.Get("exit"); - HInstruction* bool_value = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(if_inst); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(cls, ¤t_locals); + ManuallyBuildEnvFor(cls, {}); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, - c1, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* call_left = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - HInstruction* read_left = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(16), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* read_left = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(16)); HInstruction* goto_left = new (GetAllocator()) HGoto(); - call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(write_left); left->AddInstruction(call_left); left->AddInstruction(read_left); left->AddInstruction(goto_left); call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, - c2, - nullptr, - DataType::Type::kInt32, - MemberOffset(16), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* read_right = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(16), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(16)); + HInstruction* read_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(16)); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(read_right); right->AddInstruction(goto_right); - HInstruction* phi_final = - new (GetAllocator()) HPhi(GetAllocator(), 12, 2, DataType::Type::kInt32); - phi_final->SetRawInputAt(0, read_left); - phi_final->SetRawInputAt(1, read_right); + HInstruction* phi_final = MakePhi({read_left, read_right}); HInstruction* return_exit = new (GetAllocator()) HReturn(phi_final); exit->AddPhi(phi_final->AsPhi()); exit->AddInstruction(return_exit); @@ -2022,10 +2094,10 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination) { // } // EXIT // return obj.field -// TODO We eventually want to be able to eliminate the right write along with the final read but -// will need either new blocks or new instructions. +// This test runs with partial LSE disabled. TEST_F(LoadStoreEliminationTest, PartialLoadPreserved) { - InitGraph(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit_REAL", { { "entry", "left" }, @@ -2037,93 +2109,42 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved) { HBasicBlock* left = blks.Get("left"); HBasicBlock* right = blks.Get("right"); HBasicBlock* exit = blks.Get("exit"); - HInstruction* bool_value = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(if_inst); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(cls, ¤t_locals); + ManuallyBuildEnvFor(cls, {}); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, - c1, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* call_left = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); HInstruction* goto_left = new (GetAllocator()) HGoto(); - call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(write_left); left->AddInstruction(call_left); left->AddInstruction(goto_left); call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, - c2, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(goto_right); - HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); exit->AddInstruction(read_bottom); exit->AddInstruction(return_exit); // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); - PerformLSE(); + PerformLSENoPartial(); - ASSERT_FALSE(IsRemoved(read_bottom)); - ASSERT_FALSE(IsRemoved(write_right)); + EXPECT_INS_RETAINED(read_bottom) << *read_bottom; + EXPECT_INS_RETAINED(write_right) << *write_right; } // // ENTRY @@ -2144,10 +2165,10 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved) { // } // EXIT // return obj.field -// TODO We eventually want to be able to eliminate the right write along with the final read but -// will need either new blocks or new instructions. +// NB This test is for non-partial LSE flow. Normally the obj.field writes will be removed TEST_F(LoadStoreEliminationTest, PartialLoadPreserved2) { - InitGraph(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit_REAL", { { "entry", "left" }, @@ -2166,60 +2187,24 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved2) { HBasicBlock* right_second = blks.Get("right_second"); HBasicBlock* right_end = blks.Get("right_end"); HBasicBlock* exit = blks.Get("exit"); - HInstruction* bool_value = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); - HInstruction* bool_value_2 = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 2, DataType::Type::kBool); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value_2 = MakeParam(DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(bool_value); - entry->AddInstruction(bool_value_2); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(if_inst); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(cls, ¤t_locals); + ManuallyBuildEnvFor(cls, {}); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, - c1, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* call_left = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); HInstruction* goto_left = new (GetAllocator()) HGoto(); - call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(write_left); left->AddInstruction(call_left); left->AddInstruction(goto_left); @@ -2228,30 +2213,12 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved2) { HInstruction* right_if = new (GetAllocator()) HIf(bool_value_2); right_start->AddInstruction(right_if); - HInstruction* write_right_first = new (GetAllocator()) HInstanceFieldSet(new_inst, - c2, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_right_first = MakeIFieldSet(new_inst, c2, MemberOffset(32)); HInstruction* goto_right_first = new (GetAllocator()) HGoto(); right_first->AddInstruction(write_right_first); right_first->AddInstruction(goto_right_first); - HInstruction* write_right_second = new (GetAllocator()) HInstanceFieldSet(new_inst, - c3, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_right_second = MakeIFieldSet(new_inst, c3, MemberOffset(32)); HInstruction* goto_right_second = new (GetAllocator()) HGoto(); right_second->AddInstruction(write_right_second); right_second->AddInstruction(goto_right_second); @@ -2259,25 +2226,17 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved2) { HInstruction* goto_right_end = new (GetAllocator()) HGoto(); right_end->AddInstruction(goto_right_end); - HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); exit->AddInstruction(read_bottom); exit->AddInstruction(return_exit); // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); - PerformLSE(); + PerformLSENoPartial(); - ASSERT_FALSE(IsRemoved(read_bottom)); - EXPECT_FALSE(IsRemoved(write_right_first)); - EXPECT_FALSE(IsRemoved(write_right_second)); + EXPECT_INS_RETAINED(read_bottom); + EXPECT_INS_RETAINED(write_right_first); + EXPECT_INS_RETAINED(write_right_second); } // // ENTRY @@ -2296,14 +2255,15 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved2) { // ELIMINATE // return obj.field TEST_F(LoadStoreEliminationTest, PartialLoadElimination2) { - InitGraph(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - { { "entry", "left" }, - { "entry", "right" }, - { "left", "breturn"}, - { "right", "breturn" }, - { "breturn", "exit" } })); + {{"entry", "left"}, + {"entry", "right"}, + {"left", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(exit); @@ -2311,98 +2271,1888 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination2) { GET_BLOCK(left); GET_BLOCK(right); #undef GET_BLOCK - HInstruction* bool_value = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(if_inst); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(cls, ¤t_locals); + ManuallyBuildEnvFor(cls, {}); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* call_left = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, - c1, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); HInstruction* goto_left = new (GetAllocator()) HGoto(); - call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(call_left); left->AddInstruction(write_left); left->AddInstruction(goto_left); call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, - c2, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(goto_right); - HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); breturn->AddInstruction(read_bottom); breturn->AddInstruction(return_exit); - HInstruction* exit_instruction = new (GetAllocator()) HExit(); - exit->AddInstruction(exit_instruction); + SetupExit(exit); + // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); PerformLSE(); - EXPECT_TRUE(IsRemoved(read_bottom)); - EXPECT_TRUE(IsRemoved(write_right)); - EXPECT_FALSE(IsRemoved(write_left)); - EXPECT_FALSE(IsRemoved(call_left)); + EXPECT_INS_REMOVED(read_bottom); + EXPECT_INS_REMOVED(write_right); + EXPECT_INS_RETAINED(write_left); + EXPECT_INS_RETAINED(call_left); +} + +class PatternMatchGraphVisitor : public HGraphVisitor { + private: + struct HandlerWrapper { + public: + virtual ~HandlerWrapper() {} + virtual void operator()(HInstruction* h) = 0; + }; + + template <HInstruction::InstructionKind kKind, typename F> + struct KindWrapper; + +#define GEN_HANDLER(nm, unused) \ + template <typename F> \ + struct KindWrapper<HInstruction::InstructionKind::k##nm, F> : public HandlerWrapper { \ + public: \ + explicit KindWrapper(F f) : f_(f) {} \ + void operator()(HInstruction* h) override { \ + if constexpr (std::is_invocable_v<F, H##nm*>) { \ + f_(h->As##nm()); \ + } else { \ + LOG(FATAL) << "Incorrect call with " << #nm; \ + } \ + } \ + \ + private: \ + F f_; \ + }; + + FOR_EACH_CONCRETE_INSTRUCTION(GEN_HANDLER) +#undef GEN_HANDLER + + template <typename F> + std::unique_ptr<HandlerWrapper> GetWrapper(HInstruction::InstructionKind kind, F f) { + switch (kind) { +#define GEN_GETTER(nm, unused) \ + case HInstruction::InstructionKind::k##nm: \ + return std::unique_ptr<HandlerWrapper>( \ + new KindWrapper<HInstruction::InstructionKind::k##nm, F>(f)); + FOR_EACH_CONCRETE_INSTRUCTION(GEN_GETTER) +#undef GEN_GETTER + default: + LOG(FATAL) << "Unable to handle kind " << kind; + return nullptr; + } + } + + public: + template <typename... Inst> + explicit PatternMatchGraphVisitor(HGraph* graph, Inst... handlers) : HGraphVisitor(graph) { + FillHandlers(handlers...); + } + + void VisitInstruction(HInstruction* instruction) override { + auto& h = handlers_[instruction->GetKind()]; + if (h.get() != nullptr) { + (*h)(instruction); + } + } + + private: + template <typename Func> + constexpr HInstruction::InstructionKind GetKind() { +#define CHECK_INST(nm, unused) \ + if constexpr (std::is_invocable_v<Func, H##nm*>) { \ + return HInstruction::InstructionKind::k##nm; \ + } + FOR_EACH_CONCRETE_INSTRUCTION(CHECK_INST); +#undef CHECK_INST + static_assert(!std::is_invocable_v<Func, HInstruction*>, + "Use on generic HInstruction not allowed"); +#define STATIC_ASSERT_ABSTRACT(nm, unused) && !std::is_invocable_v<Func, H##nm*> + static_assert(true FOR_EACH_ABSTRACT_INSTRUCTION(STATIC_ASSERT_ABSTRACT), + "Must not be abstract instruction"); +#undef STATIC_ASSERT_ABSTRACT +#define STATIC_ASSERT_CONCRETE(nm, unused) || std::is_invocable_v<Func, H##nm*> + static_assert(false FOR_EACH_CONCRETE_INSTRUCTION(STATIC_ASSERT_CONCRETE), + "Must be a concrete instruction"); +#undef STATIC_ASSERT_CONCRETE + return HInstruction::InstructionKind::kLastInstructionKind; + } + template <typename First> + void FillHandlers(First h1) { + HInstruction::InstructionKind type = GetKind<First>(); + CHECK_NE(type, HInstruction::kLastInstructionKind) + << "Unknown instruction kind. Only concrete ones please."; + handlers_[type] = GetWrapper(type, h1); + } + + template <typename First, typename... Inst> + void FillHandlers(First h1, Inst... handlers) { + FillHandlers(h1); + FillHandlers<Inst...>(handlers...); + } + + std::array<std::unique_ptr<HandlerWrapper>, HInstruction::InstructionKind::kLastInstructionKind> + handlers_; +}; + +template <typename... Target> +std::tuple<std::vector<Target*>...> FindAllInstructions( + HGraph* graph, + std::variant<std::nullopt_t, HBasicBlock*, std::initializer_list<HBasicBlock*>> blks = + std::nullopt) { + std::tuple<std::vector<Target*>...> res; + PatternMatchGraphVisitor vis( + graph, [&](Target* t) { std::get<std::vector<Target*>>(res).push_back(t); }...); + + if (std::holds_alternative<std::initializer_list<HBasicBlock*>>(blks)) { + for (HBasicBlock* blk : std::get<std::initializer_list<HBasicBlock*>>(blks)) { + vis.VisitBasicBlock(blk); + } + } else if (std::holds_alternative<std::nullopt_t>(blks)) { + vis.VisitInsertionOrder(); + } else { + vis.VisitBasicBlock(std::get<HBasicBlock*>(blks)); + } + return res; +} + +template <typename... Target> +std::tuple<Target*...> FindSingleInstructions( + HGraph* graph, + std::variant<std::nullopt_t, HBasicBlock*, std::initializer_list<HBasicBlock*>> blks = + std::nullopt) { + std::tuple<Target*...> res; + PatternMatchGraphVisitor vis(graph, [&](Target* t) { + EXPECT_EQ(std::get<Target*>(res), nullptr) + << *std::get<Target*>(res) << " already found but found " << *t << "!"; + std::get<Target*>(res) = t; + }...); + if (std::holds_alternative<std::initializer_list<HBasicBlock*>>(blks)) { + for (HBasicBlock* blk : std::get<std::initializer_list<HBasicBlock*>>(blks)) { + vis.VisitBasicBlock(blk); + } + } else if (std::holds_alternative<std::nullopt_t>(blks)) { + vis.VisitInsertionOrder(); + } else { + vis.VisitBasicBlock(std::get<HBasicBlock*>(blks)); + } + return res; +} + +template <typename Target> +Target* FindSingleInstruction( + HGraph* graph, + std::variant<std::nullopt_t, HBasicBlock*, std::initializer_list<HBasicBlock*>> blks = + std::nullopt) { + return std::get<Target*>(FindSingleInstructions<Target>(graph, blks)); +} + +template<typename Iter, typename Func> +typename Iter::value_type FindOrNull(Iter begin, Iter end, Func func) { + static_assert(std::is_pointer_v<typename Iter::value_type>); + auto it = std::find_if(begin, end, func); + if (it == end) { + return nullptr; + } else { + return *it; + } +} + +// // ENTRY +// Obj new_inst = new Obj(); +// new_inst.foo = 12; +// Obj obj; +// Obj out; +// int first; +// if (param0) { +// // ESCAPE_ROUTE +// if (param1) { +// // LEFT_START +// if (param2) { +// // LEFT_LEFT +// obj = new_inst; +// } else { +// // LEFT_RIGHT +// obj = obj_param; +// } +// // LEFT_MERGE +// // technically the phi is enough to cause an escape but might as well be +// // thorough. +// // obj = phi[new_inst, param] +// escape(obj); +// out = obj; +// } else { +// // RIGHT +// out = obj_param; +// } +// // EXIT +// // Can't do anything with this since we don't have good tracking for the heap-locations +// // out = phi[param, phi[new_inst, param]] +// first = out.foo +// } else { +// new_inst.foo = 15; +// first = 13; +// } +// // first = phi[out.foo, 13] +// return first + new_inst.foo; +TEST_F(LoadStoreEliminationTest, PartialPhiPropagation) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "escape_route"}, + {"entry", "noescape_route"}, + {"escape_route", "left"}, + {"escape_route", "right"}, + {"left", "left_left"}, + {"left", "left_right"}, + {"left_left", "left_merge"}, + {"left_right", "left_merge"}, + {"left_merge", "escape_end"}, + {"right", "escape_end"}, + {"escape_end", "breturn"}, + {"noescape_route", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); + GET_BLOCK(left_left); + GET_BLOCK(left_right); + GET_BLOCK(left_merge); + GET_BLOCK(escape_end); + GET_BLOCK(escape_route); + GET_BLOCK(noescape_route); +#undef GET_BLOCK + EnsurePredecessorOrder(escape_end, {left_merge, right}); + EnsurePredecessorOrder(left_merge, {left_left, left_right}); + EnsurePredecessorOrder(breturn, {escape_end, noescape_route}); + HInstruction* param0 = MakeParam(DataType::Type::kBool); + HInstruction* param1 = MakeParam(DataType::Type::kBool); + HInstruction* param2 = MakeParam(DataType::Type::kBool); + HInstruction* obj_param = MakeParam(DataType::Type::kReference); + HInstruction* c12 = graph_->GetIntConstant(12); + HInstruction* c13 = graph_->GetIntConstant(13); + HInstruction* c15 = graph_->GetIntConstant(15); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32)); + HInstruction* if_param0 = new (GetAllocator()) HIf(param0); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(store); + entry->AddInstruction(if_param0); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* store_noescape = MakeIFieldSet(new_inst, c15, MemberOffset(32)); + noescape_route->AddInstruction(store_noescape); + noescape_route->AddInstruction(new (GetAllocator()) HGoto()); + + escape_route->AddInstruction(new (GetAllocator()) HIf(param1)); + + HInstruction* if_left = new (GetAllocator()) HIf(param2); + left->AddInstruction(if_left); + + HInstruction* goto_left_left = new (GetAllocator()) HGoto(); + left_left->AddInstruction(goto_left_left); + + HInstruction* goto_left_right = new (GetAllocator()) HGoto(); + left_right->AddInstruction(goto_left_right); + + HPhi* left_phi = MakePhi({obj_param, new_inst}); + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { left_phi }); + HInstruction* goto_left_merge = new (GetAllocator()) HGoto(); + left_merge->AddPhi(left_phi); + left_merge->AddInstruction(call_left); + left_merge->AddInstruction(goto_left_merge); + left_phi->SetCanBeNull(true); + call_left->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(goto_right); + + HPhi* escape_end_phi = MakePhi({left_phi, obj_param}); + HInstruction* read_escape_end = + MakeIFieldGet(escape_end_phi, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* goto_escape_end = new (GetAllocator()) HGoto(); + escape_end->AddPhi(escape_end_phi); + escape_end->AddInstruction(read_escape_end); + escape_end->AddInstruction(goto_escape_end); + + HPhi* return_phi = MakePhi({read_escape_end, c13}); + HInstruction* read_exit = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* add_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, return_phi, read_exit); + HInstruction* return_exit = new (GetAllocator()) HReturn(add_exit); + breturn->AddPhi(return_phi); + breturn->AddInstruction(read_exit); + breturn->AddInstruction(add_exit); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_); + std::vector<HPhi*> all_return_phis; + std::tie(all_return_phis) = FindAllInstructions<HPhi>(graph_, breturn); + EXPECT_EQ(all_return_phis.size(), 3u); + EXPECT_INS_RETAINED(return_phi); + EXPECT_TRUE(std::find(all_return_phis.begin(), all_return_phis.end(), return_phi) != + all_return_phis.end()); + HPhi* instance_phi = + FindOrNull(all_return_phis.begin(), all_return_phis.end(), [&](HPhi* phi) { + return phi != return_phi && phi->GetType() == DataType::Type::kReference; + }); + ASSERT_NE(instance_phi, nullptr); + HPhi* value_phi = FindOrNull(all_return_phis.begin(), all_return_phis.end(), [&](HPhi* phi) { + return phi != return_phi && phi->GetType() == DataType::Type::kInt32; + }); + ASSERT_NE(value_phi, nullptr); + EXPECT_INS_EQ( + instance_phi->InputAt(0), + FindSingleInstruction<HNewInstance>(graph_, escape_route->GetSinglePredecessor())); + // Check materialize block + EXPECT_INS_EQ(FindSingleInstruction<HInstanceFieldSet>( + graph_, escape_route->GetSinglePredecessor()) + ->InputAt(1), + c12); + + EXPECT_INS_EQ(instance_phi->InputAt(1), graph_->GetNullConstant()); + EXPECT_INS_EQ(value_phi->InputAt(0), graph_->GetIntConstant(0)); + EXPECT_INS_EQ(value_phi->InputAt(1), c15); + EXPECT_INS_REMOVED(store_noescape); + EXPECT_INS_EQ(pred_get->GetTarget(), instance_phi); + EXPECT_INS_EQ(pred_get->GetDefaultValue(), value_phi); +} + +// // ENTRY +// // To be moved +// // NB Order important. By having alloc and store of obj1 before obj2 that +// // ensure we'll build the materialization for obj1 first (just due to how +// // we iterate.) +// obj1 = new Obj(); +// obj2 = new Obj(); // has env[obj1] +// // Swap the order of these +// obj1.foo = param_obj1; +// obj2.foo = param_obj2; +// if (param1) { +// // LEFT +// obj2.foo = obj1; +// if (param2) { +// // LEFT_LEFT +// escape(obj2); +// } else {} +// } else {} +// return select(param3, obj1.foo, obj2.foo); +// EXIT +TEST_P(OrderDependentTestGroup, PredicatedUse) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"entry", "right"}, + {"left", "left_left"}, + {"left", "left_right"}, + {"left_left", "left_end"}, + {"left_right", "left_end"}, + {"left_end", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(right); + GET_BLOCK(left); + GET_BLOCK(left_left); + GET_BLOCK(left_right); + GET_BLOCK(left_end); +#undef GET_BLOCK + TestOrder order = GetParam(); + EnsurePredecessorOrder(breturn, {left_end, right}); + EnsurePredecessorOrder(left_end, {left_left, left_right}); + HInstruction* param1 = MakeParam(DataType::Type::kBool); + HInstruction* param2 = MakeParam(DataType::Type::kBool); + HInstruction* param3 = MakeParam(DataType::Type::kBool); + HInstruction* param_obj1 = MakeParam(DataType::Type::kReference); + HInstruction* param_obj2 = MakeParam(DataType::Type::kReference); + + HInstruction* cls1 = MakeClassLoad(); + HInstruction* cls2 = MakeClassLoad(); + HInstruction* new_inst1 = MakeNewInstance(cls1); + HInstruction* new_inst2 = MakeNewInstance(cls2); + HInstruction* store1 = MakeIFieldSet(new_inst1, param_obj1, MemberOffset(32)); + HInstruction* store2 = MakeIFieldSet(new_inst2, param_obj2, MemberOffset(32)); + HInstruction* null_const = graph_->GetNullConstant(); + HInstruction* if_inst = new (GetAllocator()) HIf(param1); + entry->AddInstruction(cls1); + entry->AddInstruction(cls2); + entry->AddInstruction(new_inst1); + entry->AddInstruction(new_inst2); + if (order == TestOrder::kSameAsAlloc) { + entry->AddInstruction(store1); + entry->AddInstruction(store2); + } else { + entry->AddInstruction(store2); + entry->AddInstruction(store1); + } + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls1, {}); + cls2->CopyEnvironmentFrom(cls1->GetEnvironment()); + new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); + new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment()); + + // This is the escape of new_inst1 + HInstruction* store_left = MakeIFieldSet(new_inst2, new_inst1, MemberOffset(32)); + HInstruction* if_left = new (GetAllocator()) HIf(param2); + left->AddInstruction(store_left); + left->AddInstruction(if_left); + + HInstruction* call_left_left = MakeInvoke(DataType::Type::kVoid, { new_inst2 }); + HInstruction* goto_left_left = new (GetAllocator()) HGoto(); + left_left->AddInstruction(call_left_left); + left_left->AddInstruction(goto_left_left); + call_left_left->CopyEnvironmentFrom(new_inst2->GetEnvironment()); + + left_right->AddInstruction(new (GetAllocator()) HGoto()); + left_end->AddInstruction(new (GetAllocator()) HGoto()); + + right->AddInstruction(new (GetAllocator()) HGoto()); + + // Used to distinguish the pred-gets without having to dig through the + // multiple phi layers. + constexpr uint32_t kRead1DexPc = 10; + constexpr uint32_t kRead2DexPc = 20; + HInstruction* read1 = + MakeIFieldGet(new_inst1, DataType::Type::kReference, MemberOffset(32), kRead1DexPc); + read1->SetReferenceTypeInfo( + ReferenceTypeInfo::CreateUnchecked(graph_->GetHandleCache()->GetObjectClassHandle(), false)); + HInstruction* read2 = + MakeIFieldGet(new_inst2, DataType::Type::kReference, MemberOffset(32), kRead2DexPc); + read2->SetReferenceTypeInfo( + ReferenceTypeInfo::CreateUnchecked(graph_->GetHandleCache()->GetObjectClassHandle(), false)); + HInstruction* sel_return = new (GetAllocator()) HSelect(param3, read1, read2, 0); + HInstruction* return_exit = new (GetAllocator()) HReturn(sel_return); + breturn->AddInstruction(read1); + breturn->AddInstruction(read2); + breturn->AddInstruction(sel_return); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_RETAINED(call_left_left); + EXPECT_INS_REMOVED(read1); + EXPECT_INS_REMOVED(read2); + EXPECT_INS_REMOVED(new_inst1); + EXPECT_INS_REMOVED(new_inst2); + EXPECT_TRUE(new_inst1->GetUses().empty()) << *new_inst1 << " " << new_inst1->GetUses(); + EXPECT_TRUE(new_inst2->GetUses().empty()) << *new_inst2 << " " << new_inst2->GetUses(); + EXPECT_INS_RETAINED(sel_return); + // Make sure the selector is the same + EXPECT_INS_EQ(sel_return->InputAt(2), param3); + std::vector<HPredicatedInstanceFieldGet*> pred_gets; + std::tie(pred_gets) = FindAllInstructions<HPredicatedInstanceFieldGet>(graph_, breturn); + HPredicatedInstanceFieldGet* pred1 = FindOrNull(pred_gets.begin(), pred_gets.end(), [&](auto i) { + return i->GetDexPc() == kRead1DexPc; + }); + HPredicatedInstanceFieldGet* pred2 = FindOrNull(pred_gets.begin(), pred_gets.end(), [&](auto i) { + return i->GetDexPc() == kRead2DexPc; + }); + ASSERT_NE(pred1, nullptr); + ASSERT_NE(pred2, nullptr); + EXPECT_INS_EQ(sel_return->InputAt(0), pred2); + EXPECT_INS_EQ(sel_return->InputAt(1), pred1); + // Check targets + EXPECT_TRUE(pred1->GetTarget()->IsPhi()) << pred1->DumpWithArgs(); + EXPECT_TRUE(pred2->GetTarget()->IsPhi()) << pred2->DumpWithArgs(); + HInstruction* mat1 = FindSingleInstruction<HNewInstance>(graph_, left->GetSinglePredecessor()); + HInstruction* mat2 = + FindSingleInstruction<HNewInstance>(graph_, left_left->GetSinglePredecessor()); + EXPECT_INS_EQ(pred1->GetTarget()->InputAt(0), mat1); + EXPECT_INS_EQ(pred1->GetTarget()->InputAt(1), null_const); + EXPECT_TRUE(pred2->GetTarget()->InputAt(0)->IsPhi()) << pred2->DumpWithArgs(); + EXPECT_INS_EQ(pred2->GetTarget()->InputAt(0)->InputAt(0), mat2); + EXPECT_INS_EQ(pred2->GetTarget()->InputAt(0)->InputAt(1), null_const); + EXPECT_INS_EQ(pred2->GetTarget()->InputAt(1), null_const); + // Check default values. + EXPECT_TRUE(pred1->GetDefaultValue()->IsPhi()) << pred1->DumpWithArgs(); + EXPECT_TRUE(pred2->GetDefaultValue()->IsPhi()) << pred2->DumpWithArgs(); + EXPECT_INS_EQ(pred1->GetDefaultValue()->InputAt(0), null_const); + EXPECT_INS_EQ(pred1->GetDefaultValue()->InputAt(1), param_obj1); + EXPECT_TRUE(pred2->GetDefaultValue()->InputAt(0)->IsPhi()) << pred2->DumpWithArgs(); + EXPECT_INS_EQ(pred2->GetDefaultValue()->InputAt(0)->InputAt(0), null_const); + EXPECT_INS_EQ(pred2->GetDefaultValue()->InputAt(0)->InputAt(1), mat1); + EXPECT_INS_EQ(pred2->GetDefaultValue()->InputAt(1), param_obj2); +} + +// // ENTRY +// // To be moved +// // NB Order important. By having alloc and store of obj1 before obj2 that +// // ensure we'll build the materialization for obj1 first (just due to how +// // we iterate.) +// obj1 = new Obj(); +// obj.foo = 12; +// obj2 = new Obj(); // has env[obj1] +// obj2.foo = 15; +// if (param1) { +// // LEFT +// // Need to update env to nullptr +// escape(obj1/2); +// if (param2) { +// // LEFT_LEFT +// escape(obj2/1); +// } else {} +// } else {} +// return obj1.foo + obj2.foo; +// EXIT +TEST_P(OrderDependentTestGroup, PredicatedEnvUse) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"entry", "right"}, + {"left", "left_left"}, + {"left", "left_right"}, + {"left_left", "left_end"}, + {"left_right", "left_end"}, + {"left_end", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(right); + GET_BLOCK(left); + GET_BLOCK(left_left); + GET_BLOCK(left_right); + GET_BLOCK(left_end); +#undef GET_BLOCK + TestOrder order = GetParam(); + EnsurePredecessorOrder(breturn, {left_end, right}); + EnsurePredecessorOrder(left_end, {left_left, left_right}); + HInstruction* param1 = MakeParam(DataType::Type::kBool); + HInstruction* param2 = MakeParam(DataType::Type::kBool); + HInstruction* c12 = graph_->GetIntConstant(12); + HInstruction* c15 = graph_->GetIntConstant(15); + + HInstruction* cls1 = MakeClassLoad(); + HInstruction* cls2 = MakeClassLoad(); + HInstruction* new_inst1 = MakeNewInstance(cls1); + HInstruction* store1 = MakeIFieldSet(new_inst1, c12, MemberOffset(32)); + HInstruction* new_inst2 = MakeNewInstance(cls2); + HInstruction* store2 = MakeIFieldSet(new_inst2, c15, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(param1); + entry->AddInstruction(cls1); + entry->AddInstruction(cls2); + entry->AddInstruction(new_inst1); + entry->AddInstruction(store1); + entry->AddInstruction(new_inst2); + entry->AddInstruction(store2); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls1, {}); + cls2->CopyEnvironmentFrom(cls1->GetEnvironment()); + new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); + ManuallyBuildEnvFor(new_inst2, {new_inst1}); + + HInstruction* first_inst = new_inst1; + HInstruction* second_inst = new_inst2; + + if (order == TestOrder::kReverseOfAlloc) { + std::swap(first_inst, second_inst); + } + + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { first_inst }); + HInstruction* if_left = new (GetAllocator()) HIf(param2); + left->AddInstruction(call_left); + left->AddInstruction(if_left); + call_left->CopyEnvironmentFrom(new_inst2->GetEnvironment()); + + HInstruction* call_left_left = MakeInvoke(DataType::Type::kVoid, { second_inst }); + HInstruction* goto_left_left = new (GetAllocator()) HGoto(); + left_left->AddInstruction(call_left_left); + left_left->AddInstruction(goto_left_left); + call_left_left->CopyEnvironmentFrom(new_inst2->GetEnvironment()); + + left_right->AddInstruction(new (GetAllocator()) HGoto()); + left_end->AddInstruction(new (GetAllocator()) HGoto()); + + right->AddInstruction(new (GetAllocator()) HGoto()); + + HInstruction* read1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* read2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* add_return = new (GetAllocator()) HAdd(DataType::Type::kInt32, read1, read2); + HInstruction* return_exit = new (GetAllocator()) HReturn(add_return); + breturn->AddInstruction(read1); + breturn->AddInstruction(read2); + breturn->AddInstruction(add_return); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + HNewInstance* moved_new_inst1; + HInstanceFieldSet* moved_set1; + HNewInstance* moved_new_inst2; + HInstanceFieldSet* moved_set2; + HBasicBlock* first_mat_block = left->GetSinglePredecessor(); + HBasicBlock* second_mat_block = left_left->GetSinglePredecessor(); + if (order == TestOrder::kReverseOfAlloc) { + std::swap(first_mat_block, second_mat_block); + } + std::tie(moved_new_inst1, moved_set1) = + FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, first_mat_block); + std::tie(moved_new_inst2, moved_set2) = + FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, second_mat_block); + std::vector<HPredicatedInstanceFieldGet*> pred_gets; + std::vector<HPhi*> phis; + std::tie(pred_gets, phis) = FindAllInstructions<HPredicatedInstanceFieldGet, HPhi>(graph_); + EXPECT_NE(moved_new_inst1, nullptr); + EXPECT_NE(moved_new_inst2, nullptr); + EXPECT_NE(moved_set1, nullptr); + EXPECT_NE(moved_set2, nullptr); + EXPECT_INS_EQ(moved_set1->InputAt(1), c12); + EXPECT_INS_EQ(moved_set2->InputAt(1), c15); + EXPECT_INS_RETAINED(call_left); + EXPECT_INS_RETAINED(call_left_left); + EXPECT_INS_REMOVED(store1); + EXPECT_INS_REMOVED(store2); + EXPECT_INS_REMOVED(read1); + EXPECT_INS_REMOVED(read2); + EXPECT_INS_EQ(moved_new_inst2->GetEnvironment()->GetInstructionAt(0), + order == TestOrder::kSameAsAlloc + ? moved_new_inst1 + : static_cast<HInstruction*>(graph_->GetNullConstant())); +} + +// // ENTRY +// obj1 = new Obj1(); +// obj2 = new Obj2(); +// val1 = 3; +// val2 = 13; +// // The exact order the stores are written affects what the order we perform +// // partial LSE on the values +// obj1/2.field = val1/2; +// obj2/1.field = val2/1; +// if (parameter_value) { +// // LEFT +// escape(obj1); +// escape(obj2); +// } else { +// // RIGHT +// // ELIMINATE +// obj1.field = 2; +// obj2.field = 12; +// } +// EXIT +// predicated-ELIMINATE +// return obj1.field + obj2.field +TEST_P(OrderDependentTestGroup, FieldSetOrderEnv) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"entry", "right"}, + {"left", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); +#undef GET_BLOCK + TestOrder order = GetParam(); + EnsurePredecessorOrder(breturn, {left, right}); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* c2 = graph_->GetIntConstant(2); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c12 = graph_->GetIntConstant(12); + HInstruction* c13 = graph_->GetIntConstant(13); + + HInstruction* cls1 = MakeClassLoad(); + HInstruction* cls2 = MakeClassLoad(); + HInstruction* new_inst1 = MakeNewInstance(cls1); + HInstruction* new_inst2 = MakeNewInstance(cls2); + HInstruction* write_entry1 = MakeIFieldSet(new_inst1, c3, MemberOffset(32)); + HInstruction* write_entry2 = MakeIFieldSet(new_inst2, c13, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls1); + entry->AddInstruction(cls2); + entry->AddInstruction(new_inst1); + entry->AddInstruction(new_inst2); + if (order == TestOrder::kSameAsAlloc) { + entry->AddInstruction(write_entry1); + entry->AddInstruction(write_entry2); + } else { + entry->AddInstruction(write_entry2); + entry->AddInstruction(write_entry1); + } + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls1, {}); + cls2->CopyEnvironmentFrom(cls1->GetEnvironment()); + new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); + ManuallyBuildEnvFor(new_inst2, {new_inst1}); + + HInstruction* call_left1 = MakeInvoke(DataType::Type::kVoid, { new_inst1 }); + HInstruction* call_left2 = MakeInvoke(DataType::Type::kVoid, { new_inst2 }); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left1); + left->AddInstruction(call_left2); + left->AddInstruction(goto_left); + call_left1->CopyEnvironmentFrom(cls1->GetEnvironment()); + call_left2->CopyEnvironmentFrom(cls1->GetEnvironment()); + + HInstruction* write_right1 = MakeIFieldSet(new_inst1, c2, MemberOffset(32)); + HInstruction* write_right2 = MakeIFieldSet(new_inst2, c12, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(write_right1); + right->AddInstruction(write_right2); + right->AddInstruction(goto_right); + + HInstruction* read_bottom1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* read_bottom2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* combine = + new (GetAllocator()) HAdd(DataType::Type::kInt32, read_bottom1, read_bottom2); + HInstruction* return_exit = new (GetAllocator()) HReturn(combine); + breturn->AddInstruction(read_bottom1); + breturn->AddInstruction(read_bottom2); + breturn->AddInstruction(combine); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_REMOVED(write_entry1); + EXPECT_INS_REMOVED(write_entry2); + EXPECT_INS_REMOVED(read_bottom1); + EXPECT_INS_REMOVED(read_bottom2); + EXPECT_INS_REMOVED(write_right1); + EXPECT_INS_REMOVED(write_right2); + EXPECT_INS_RETAINED(call_left1); + EXPECT_INS_RETAINED(call_left2); + std::vector<HPhi*> merges; + std::vector<HPredicatedInstanceFieldGet*> pred_gets; + std::vector<HNewInstance*> materializations; + std::tie(merges, pred_gets) = + FindAllInstructions<HPhi, HPredicatedInstanceFieldGet>(graph_, breturn); + std::tie(materializations) = FindAllInstructions<HNewInstance>(graph_); + ASSERT_EQ(merges.size(), 4u); + ASSERT_EQ(pred_gets.size(), 2u); + ASSERT_EQ(materializations.size(), 2u); + HPhi* merge_value_return1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c2; + }); + HPhi* merge_value_return2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c12; + }); + HNewInstance* mat_alloc1 = FindOrNull(materializations.begin(), + materializations.end(), + [&](HNewInstance* n) { return n->InputAt(0) == cls1; }); + HNewInstance* mat_alloc2 = FindOrNull(materializations.begin(), + materializations.end(), + [&](HNewInstance* n) { return n->InputAt(0) == cls2; }); + ASSERT_NE(mat_alloc1, nullptr); + ASSERT_NE(mat_alloc2, nullptr); + HPhi* merge_alloc1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kReference && p->InputAt(0) == mat_alloc1; + }); + HPhi* merge_alloc2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kReference && p->InputAt(0) == mat_alloc2; + }); + ASSERT_NE(merge_alloc1, nullptr); + HPredicatedInstanceFieldGet* pred_get1 = + FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) { + return pg->GetTarget() == merge_alloc1; + }); + ASSERT_NE(merge_alloc2, nullptr); + HPredicatedInstanceFieldGet* pred_get2 = + FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) { + return pg->GetTarget() == merge_alloc2; + }); + ASSERT_NE(merge_value_return1, nullptr); + ASSERT_NE(merge_value_return2, nullptr); + EXPECT_INS_EQ(merge_alloc1->InputAt(1), graph_->GetNullConstant()); + EXPECT_INS_EQ(merge_alloc2->InputAt(1), graph_->GetNullConstant()); + ASSERT_NE(pred_get1, nullptr); + EXPECT_INS_EQ(pred_get1->GetTarget(), merge_alloc1); + EXPECT_INS_EQ(pred_get1->GetDefaultValue(), merge_value_return1) + << " pred-get is: " << *pred_get1; + EXPECT_INS_EQ(merge_value_return1->InputAt(0), graph_->GetIntConstant(0)) + << " merge val is: " << *merge_value_return1; + EXPECT_INS_EQ(merge_value_return1->InputAt(1), c2) << " merge val is: " << *merge_value_return1; + ASSERT_NE(pred_get2, nullptr); + EXPECT_INS_EQ(pred_get2->GetTarget(), merge_alloc2); + EXPECT_INS_EQ(pred_get2->GetDefaultValue(), merge_value_return2) + << " pred-get is: " << *pred_get2; + EXPECT_INS_EQ(merge_value_return2->InputAt(0), graph_->GetIntConstant(0)) + << " merge val is: " << *merge_value_return1; + EXPECT_INS_EQ(merge_value_return2->InputAt(1), c12) << " merge val is: " << *merge_value_return1; + EXPECT_INS_EQ(mat_alloc2->GetEnvironment()->GetInstructionAt(0), mat_alloc1); +} + +// // TODO We can compile this better if we are better able to understand lifetimes. +// // ENTRY +// obj1 = new Obj1(); +// obj2 = new Obj2(); +// // The exact order the stores are written affects what the order we perform +// // partial LSE on the values +// obj{1,2}.var = param_obj; +// obj{2,1}.var = param_obj; +// if (param_1) { +// // EARLY_RETURN +// return; +// } +// // escape of obj1 +// obj2.var = obj1; +// if (param_2) { +// // escape of obj2 with a materialization that uses obj1 +// escape(obj2); +// } +// // EXIT +// return; +TEST_P(OrderDependentTestGroup, MaterializationMovedUse) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "early_return"}, + {"early_return", "exit"}, + {"entry", "escape_1"}, + {"escape_1", "escape_2"}, + {"escape_1", "escape_1_crit_break"}, + {"escape_1_crit_break", "exit"}, + {"escape_2", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(early_return); + GET_BLOCK(escape_1); + GET_BLOCK(escape_1_crit_break); + GET_BLOCK(escape_2); +#undef GET_BLOCK + TestOrder order = GetParam(); + HInstruction* param_1 = MakeParam(DataType::Type::kBool); + HInstruction* param_2 = MakeParam(DataType::Type::kBool); + HInstruction* param_obj = MakeParam(DataType::Type::kReference); + + HInstruction* cls1 = MakeClassLoad(); + HInstruction* cls2 = MakeClassLoad(); + HInstruction* new_inst1 = MakeNewInstance(cls1); + HInstruction* new_inst2 = MakeNewInstance(cls2); + HInstruction* write_entry1 = MakeIFieldSet(new_inst1, param_obj, MemberOffset(32)); + HInstruction* write_entry2 = MakeIFieldSet(new_inst2, param_obj, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(param_1); + entry->AddInstruction(cls1); + entry->AddInstruction(cls2); + entry->AddInstruction(new_inst1); + entry->AddInstruction(new_inst2); + if (order == TestOrder::kSameAsAlloc) { + entry->AddInstruction(write_entry1); + entry->AddInstruction(write_entry2); + } else { + entry->AddInstruction(write_entry2); + entry->AddInstruction(write_entry1); + } + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls1, {}); + cls2->CopyEnvironmentFrom(cls1->GetEnvironment()); + new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); + new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment()); + + early_return->AddInstruction(new (GetAllocator()) HReturnVoid()); + + HInstruction* escape_1_set = MakeIFieldSet(new_inst2, new_inst1, MemberOffset(32)); + HInstruction* escape_1_if = new (GetAllocator()) HIf(param_2); + escape_1->AddInstruction(escape_1_set); + escape_1->AddInstruction(escape_1_if); + + escape_1_crit_break->AddInstruction(new (GetAllocator()) HReturnVoid()); + + HInstruction* escape_2_call = MakeInvoke(DataType::Type::kVoid, {new_inst2}); + HInstruction* escape_2_return = new (GetAllocator()) HReturnVoid(); + escape_2->AddInstruction(escape_2_call); + escape_2->AddInstruction(escape_2_return); + escape_2_call->CopyEnvironmentFrom(cls1->GetEnvironment()); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_REMOVED(new_inst1); + EXPECT_INS_REMOVED(new_inst2); + EXPECT_INS_REMOVED(write_entry1); + EXPECT_INS_REMOVED(write_entry2); + EXPECT_INS_REMOVED(escape_1_set); + EXPECT_INS_RETAINED(escape_2_call); + + HInstruction* obj1_mat = + FindSingleInstruction<HNewInstance>(graph_, escape_1->GetSinglePredecessor()); + HInstruction* obj1_set = + FindSingleInstruction<HInstanceFieldSet>(graph_, escape_1->GetSinglePredecessor()); + HInstruction* obj2_mat = + FindSingleInstruction<HNewInstance>(graph_, escape_2->GetSinglePredecessor()); + HInstruction* obj2_set = + FindSingleInstruction<HInstanceFieldSet>(graph_, escape_2->GetSinglePredecessor()); + ASSERT_TRUE(obj1_mat != nullptr); + ASSERT_TRUE(obj2_mat != nullptr); + ASSERT_TRUE(obj1_set != nullptr); + ASSERT_TRUE(obj2_set != nullptr); + EXPECT_INS_EQ(obj1_set->InputAt(0), obj1_mat); + EXPECT_INS_EQ(obj1_set->InputAt(1), param_obj); + EXPECT_INS_EQ(obj2_set->InputAt(0), obj2_mat); + EXPECT_INS_EQ(obj2_set->InputAt(1), obj1_mat); +} + +INSTANTIATE_TEST_SUITE_P(LoadStoreEliminationTest, + OrderDependentTestGroup, + testing::Values(TestOrder::kSameAsAlloc, TestOrder::kReverseOfAlloc)); + +// // ENTRY +// // To be moved +// obj = new Obj(); +// obj.foo = 12; +// if (parameter_value) { +// // LEFT +// escape(obj); +// } else {} +// EXIT +TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"entry", "right"}, + {"right", "breturn"}, + {"left", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {left, right}); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* c12 = graph_->GetIntConstant(12); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(store); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left); + left->AddInstruction(goto_left); + call_left->CopyEnvironmentFrom(cls->GetEnvironment()); + + right->AddInstruction(new (GetAllocator()) HGoto()); + + HInstruction* return_exit = new (GetAllocator()) HReturnVoid(); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + HNewInstance* moved_new_inst = nullptr; + HInstanceFieldSet* moved_set = nullptr; + std::tie(moved_new_inst, moved_set) = + FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_); + EXPECT_NE(moved_new_inst, nullptr); + EXPECT_NE(moved_set, nullptr); + EXPECT_INS_RETAINED(call_left); + // store removed or moved. + EXPECT_NE(store->GetBlock(), entry); + // New-inst removed or moved. + EXPECT_NE(new_inst->GetBlock(), entry); + EXPECT_INS_EQ(moved_set->InputAt(0), moved_new_inst); + EXPECT_INS_EQ(moved_set->InputAt(1), c12); +} + +// // ENTRY +// // To be moved +// obj = new Obj(); +// obj.foo = 12; +// if (parameter_value) { +// // LEFT +// escape(obj); +// } +// EXIT +// int a = obj.foo; +// obj.foo = 13; +// noescape(); +// int b = obj.foo; +// obj.foo = 14; +// noescape(); +// int c = obj.foo; +// obj.foo = 15; +// noescape(); +// return a + b + c +TEST_F(LoadStoreEliminationTest, MutiPartialLoadStore) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"entry", "right"}, + {"right", "breturn"}, + {"left", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {left, right}); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* c12 = graph_->GetIntConstant(12); + HInstruction* c13 = graph_->GetIntConstant(13); + HInstruction* c14 = graph_->GetIntConstant(14); + HInstruction* c15 = graph_->GetIntConstant(15); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(store); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left); + left->AddInstruction(goto_left); + call_left->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(goto_right); + + HInstruction* a_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* a_reset = MakeIFieldSet(new_inst, c13, MemberOffset(32)); + HInstruction* a_noescape = MakeInvoke(DataType::Type::kVoid, {}); + HInstruction* b_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* b_reset = MakeIFieldSet(new_inst, c14, MemberOffset(32)); + HInstruction* b_noescape = MakeInvoke(DataType::Type::kVoid, {}); + HInstruction* c_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* c_reset = MakeIFieldSet(new_inst, c15, MemberOffset(32)); + HInstruction* c_noescape = MakeInvoke(DataType::Type::kVoid, {}); + HInstruction* add_1_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, a_val, b_val); + HInstruction* add_2_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, c_val, add_1_exit); + HInstruction* return_exit = new (GetAllocator()) HReturn(add_2_exit); + breturn->AddInstruction(a_val); + breturn->AddInstruction(a_reset); + breturn->AddInstruction(a_noescape); + breturn->AddInstruction(b_val); + breturn->AddInstruction(b_reset); + breturn->AddInstruction(b_noescape); + breturn->AddInstruction(c_val); + breturn->AddInstruction(c_reset); + breturn->AddInstruction(c_noescape); + breturn->AddInstruction(add_1_exit); + breturn->AddInstruction(add_2_exit); + breturn->AddInstruction(return_exit); + ManuallyBuildEnvFor(a_noescape, {new_inst, a_val}); + ManuallyBuildEnvFor(b_noescape, {new_inst, a_val, b_val}); + ManuallyBuildEnvFor(c_noescape, {new_inst, a_val, b_val, c_val}); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + HNewInstance* moved_new_inst = nullptr; + HInstanceFieldSet* moved_set = nullptr; + std::tie(moved_new_inst, moved_set) = + FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, left->GetSinglePredecessor()); + std::vector<HPredicatedInstanceFieldGet*> pred_gets; + std::vector<HInstanceFieldSet*> pred_sets; + std::vector<HPhi*> return_phis; + std::tie(return_phis, pred_gets, pred_sets) = + FindAllInstructions<HPhi, HPredicatedInstanceFieldGet, HInstanceFieldSet>(graph_, breturn); + ASSERT_EQ(return_phis.size(), 2u); + HPhi* inst_phi = return_phis[0]; + HPhi* val_phi = return_phis[1]; + if (inst_phi->GetType() != DataType::Type::kReference) { + std::swap(inst_phi, val_phi); + } + ASSERT_NE(moved_new_inst, nullptr); + EXPECT_INS_EQ(inst_phi->InputAt(0), moved_new_inst); + EXPECT_INS_EQ(inst_phi->InputAt(1), graph_->GetNullConstant()); + EXPECT_INS_EQ(val_phi->InputAt(0), graph_->GetIntConstant(0)); + EXPECT_EQ(val_phi->InputAt(1), c12); + ASSERT_EQ(pred_gets.size(), 3u); + ASSERT_EQ(pred_gets.size(), pred_sets.size()); + std::vector<HInstruction*> set_values{c13, c14, c15}; + std::vector<HInstruction*> get_values{val_phi, c13, c14}; + ASSERT_NE(moved_set, nullptr); + EXPECT_INS_EQ(moved_set->InputAt(0), moved_new_inst); + EXPECT_INS_EQ(moved_set->InputAt(1), c12); + EXPECT_INS_RETAINED(call_left); + // store removed or moved. + EXPECT_NE(store->GetBlock(), entry); + // New-inst removed or moved. + EXPECT_NE(new_inst->GetBlock(), entry); + for (auto [get, val] : ZipLeft(MakeIterationRange(pred_gets), MakeIterationRange(get_values))) { + EXPECT_INS_EQ(get->GetDefaultValue(), val); + } + for (auto [set, val] : ZipLeft(MakeIterationRange(pred_sets), MakeIterationRange(set_values))) { + EXPECT_INS_EQ(set->InputAt(1), val); + EXPECT_TRUE(set->GetIsPredicatedSet()) << *set; + } + EXPECT_INS_RETAINED(a_noescape); + EXPECT_INS_RETAINED(b_noescape); + EXPECT_INS_RETAINED(c_noescape); + EXPECT_INS_EQ(add_1_exit->InputAt(0), pred_gets[0]); + EXPECT_INS_EQ(add_1_exit->InputAt(1), pred_gets[1]); + EXPECT_INS_EQ(add_2_exit->InputAt(0), pred_gets[2]); + + EXPECT_EQ(a_noescape->GetEnvironment()->Size(), 2u); + EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(0), inst_phi); + EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(1), pred_gets[0]); + EXPECT_EQ(b_noescape->GetEnvironment()->Size(), 3u); + EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(0), inst_phi); + EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(1), pred_gets[0]); + EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(2), pred_gets[1]); + EXPECT_EQ(c_noescape->GetEnvironment()->Size(), 4u); + EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(0), inst_phi); + EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(1), pred_gets[0]); + EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(2), pred_gets[1]); + EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(3), pred_gets[2]); +} + +// // ENTRY +// // To be moved +// obj = new Obj(); +// obj.foo = 12; +// int a = obj.foo; +// obj.foo = 13; +// noescape(); +// int b = obj.foo; +// obj.foo = 14; +// noescape(); +// int c = obj.foo; +// obj.foo = 15; +// noescape(); +// if (parameter_value) { +// // LEFT +// escape(obj); +// } +// EXIT +// return a + b + c + obj.foo +TEST_F(LoadStoreEliminationTest, MutiPartialLoadStore2) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + // Need to have an actual entry block since we check env-layout and the way we + // add constants would screw this up otherwise. + AdjacencyListGraph blks(SetupFromAdjacencyList("start", + "exit", + {{"start", "entry"}, + {"entry", "left"}, + {"entry", "right"}, + {"right", "breturn"}, + {"left", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(start); + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {left, right}); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* c12 = graph_->GetIntConstant(12); + HInstruction* c13 = graph_->GetIntConstant(13); + HInstruction* c14 = graph_->GetIntConstant(14); + HInstruction* c15 = graph_->GetIntConstant(15); + + HInstruction* start_suspend = new (GetAllocator()) HSuspendCheck(); + HInstruction* start_goto = new (GetAllocator()) HGoto(); + + start->AddInstruction(start_suspend); + start->AddInstruction(start_goto); + ManuallyBuildEnvFor(start_suspend, {}); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32)); + + HInstruction* a_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* a_reset = MakeIFieldSet(new_inst, c13, MemberOffset(32)); + HInstruction* a_noescape = MakeInvoke(DataType::Type::kVoid, {}); + HInstruction* b_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* b_reset = MakeIFieldSet(new_inst, c14, MemberOffset(32)); + HInstruction* b_noescape = MakeInvoke(DataType::Type::kVoid, {}); + HInstruction* c_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* c_reset = MakeIFieldSet(new_inst, c15, MemberOffset(32)); + HInstruction* c_noescape = MakeInvoke(DataType::Type::kVoid, {}); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(store); + entry->AddInstruction(a_val); + entry->AddInstruction(a_reset); + entry->AddInstruction(a_noescape); + entry->AddInstruction(b_val); + entry->AddInstruction(b_reset); + entry->AddInstruction(b_noescape); + entry->AddInstruction(c_val); + entry->AddInstruction(c_reset); + entry->AddInstruction(c_noescape); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + ManuallyBuildEnvFor(a_noescape, {new_inst, a_val}); + ManuallyBuildEnvFor(b_noescape, {new_inst, a_val, b_val}); + ManuallyBuildEnvFor(c_noescape, {new_inst, a_val, b_val, c_val}); + + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left); + left->AddInstruction(goto_left); + call_left->CopyEnvironmentFrom(c_noescape->GetEnvironment()); + + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(goto_right); + + HInstruction* val_exit = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* add_1_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, a_val, b_val); + HInstruction* add_2_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, c_val, add_1_exit); + HInstruction* add_3_exit = + new (GetAllocator()) HAdd(DataType::Type::kInt32, val_exit, add_2_exit); + HInstruction* return_exit = new (GetAllocator()) HReturn(add_3_exit); + breturn->AddInstruction(val_exit); + breturn->AddInstruction(add_1_exit); + breturn->AddInstruction(add_2_exit); + breturn->AddInstruction(add_3_exit); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + HNewInstance* moved_new_inst = nullptr; + HInstanceFieldSet* moved_set = nullptr; + std::tie(moved_new_inst, moved_set) = + FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, left->GetSinglePredecessor()); + std::vector<HPredicatedInstanceFieldGet*> pred_gets; + std::vector<HInstanceFieldSet*> pred_sets; + std::vector<HPhi*> return_phis; + std::tie(return_phis, pred_gets, pred_sets) = + FindAllInstructions<HPhi, HPredicatedInstanceFieldGet, HInstanceFieldSet>(graph_, breturn); + ASSERT_EQ(return_phis.size(), 2u); + HPhi* inst_phi = return_phis[0]; + HPhi* val_phi = return_phis[1]; + if (inst_phi->GetType() != DataType::Type::kReference) { + std::swap(inst_phi, val_phi); + } + ASSERT_NE(moved_new_inst, nullptr); + EXPECT_INS_EQ(inst_phi->InputAt(0), moved_new_inst); + EXPECT_INS_EQ(inst_phi->InputAt(1), graph_->GetNullConstant()); + EXPECT_INS_EQ(val_phi->InputAt(0), graph_->GetIntConstant(0)); + EXPECT_INS_EQ(val_phi->InputAt(1), c15); + ASSERT_EQ(pred_gets.size(), 1u); + ASSERT_EQ(pred_sets.size(), 0u); + ASSERT_NE(moved_set, nullptr); + EXPECT_INS_EQ(moved_set->InputAt(0), moved_new_inst); + EXPECT_INS_EQ(moved_set->InputAt(1), c15); + EXPECT_INS_RETAINED(call_left); + // store removed or moved. + EXPECT_NE(store->GetBlock(), entry); + // New-inst removed or moved. + EXPECT_NE(new_inst->GetBlock(), entry); + EXPECT_INS_REMOVED(a_val); + EXPECT_INS_REMOVED(b_val); + EXPECT_INS_REMOVED(c_val); + EXPECT_INS_RETAINED(a_noescape); + EXPECT_INS_RETAINED(b_noescape); + EXPECT_INS_RETAINED(c_noescape); + EXPECT_INS_EQ(add_1_exit->InputAt(0), c12); + EXPECT_INS_EQ(add_1_exit->InputAt(1), c13); + EXPECT_INS_EQ(add_2_exit->InputAt(0), c14); + EXPECT_INS_EQ(add_2_exit->InputAt(1), add_1_exit); + EXPECT_INS_EQ(add_3_exit->InputAt(0), pred_gets[0]); + EXPECT_INS_EQ(pred_gets[0]->GetDefaultValue(), val_phi); + EXPECT_INS_EQ(add_3_exit->InputAt(1), add_2_exit); + EXPECT_EQ(a_noescape->GetEnvironment()->Size(), 2u); + EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(0), graph_->GetNullConstant()); + EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(1), c12); + EXPECT_EQ(b_noescape->GetEnvironment()->Size(), 3u); + EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(0), graph_->GetNullConstant()); + EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(1), c12); + EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(2), c13); + EXPECT_EQ(c_noescape->GetEnvironment()->Size(), 4u); + EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(0), graph_->GetNullConstant()); + EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(1), c12); + EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(2), c13); + EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(3), c14); +} + +// // ENTRY +// // To be moved +// obj = new Obj(); +// // Transforms required for creation non-trivial and unimportant +// if (parameter_value) { +// obj.foo = 10 +// } else { +// obj.foo = 12; +// } +// if (parameter_value_2) { +// escape(obj); +// } +// EXIT +TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc2) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left_set"}, + {"entry", "right_set"}, + {"left_set", "merge_crit_break"}, + {"right_set", "merge_crit_break"}, + {"merge_crit_break", "merge"}, + {"merge", "escape"}, + {"escape", "breturn"}, + {"merge", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left_set); + GET_BLOCK(right_set); + GET_BLOCK(merge); + GET_BLOCK(merge_crit_break); + GET_BLOCK(escape); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {merge, escape}); + EnsurePredecessorOrder(merge_crit_break, {left_set, right_set}); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value_2 = MakeParam(DataType::Type::kBool); + HInstruction* c10 = graph_->GetIntConstant(10); + HInstruction* c12 = graph_->GetIntConstant(12); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* store_left = MakeIFieldSet(new_inst, c10, MemberOffset(32)); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left_set->AddInstruction(store_left); + left_set->AddInstruction(goto_left); + + HInstruction* store_right = MakeIFieldSet(new_inst, c12, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right_set->AddInstruction(store_right); + right_set->AddInstruction(goto_right); + + merge_crit_break->AddInstruction(new (GetAllocator()) HGoto()); + HInstruction* if_merge = new (GetAllocator()) HIf(bool_value_2); + merge->AddInstruction(if_merge); + + HInstruction* escape_instruction = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* escape_goto = new (GetAllocator()) HGoto(); + escape->AddInstruction(escape_instruction); + escape->AddInstruction(escape_goto); + escape_instruction->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* return_exit = new (GetAllocator()) HReturnVoid(); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + HNewInstance* moved_new_inst; + HInstanceFieldSet* moved_set; + std::tie(moved_new_inst, moved_set) = + FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_); + HPhi* merge_phi = FindSingleInstruction<HPhi>(graph_, merge_crit_break); + HPhi* alloc_phi = FindSingleInstruction<HPhi>(graph_, breturn); + EXPECT_INS_EQ(moved_new_inst, moved_set->InputAt(0)); + ASSERT_NE(alloc_phi, nullptr); + EXPECT_EQ(alloc_phi->InputAt(0), graph_->GetNullConstant()) + << alloc_phi->GetBlock()->GetPredecessors()[0]->GetBlockId() << " " << *alloc_phi; + EXPECT_TRUE(alloc_phi->InputAt(1)->IsNewInstance()) << *alloc_phi; + ASSERT_NE(merge_phi, nullptr); + EXPECT_EQ(merge_phi->InputCount(), 2u); + EXPECT_INS_EQ(merge_phi->InputAt(0), c10); + EXPECT_INS_EQ(merge_phi->InputAt(1), c12); + EXPECT_TRUE(merge_phi->GetUses().HasExactlyOneElement()); + EXPECT_INS_EQ(merge_phi->GetUses().front().GetUser(), moved_set); + EXPECT_INS_RETAINED(escape_instruction); + EXPECT_INS_EQ(escape_instruction->InputAt(0), moved_new_inst); + // store removed or moved. + EXPECT_NE(store_left->GetBlock(), left_set); + EXPECT_NE(store_right->GetBlock(), left_set); + // New-inst removed or moved. + EXPECT_NE(new_inst->GetBlock(), entry); +} + +// // ENTRY +// // To be moved +// obj = new Obj(); +// switch(args) { +// default: +// return obj.a; +// case b: +// obj.a = 5; break; +// case c: +// obj.b = 4; break; +// } +// escape(obj); +// return obj.a; +// EXIT +TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc3) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "early_return"}, + {"entry", "set_one"}, + {"entry", "set_two"}, + {"early_return", "exit"}, + {"set_one", "escape"}, + {"set_two", "escape"}, + {"escape", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(escape); + GET_BLOCK(early_return); + GET_BLOCK(set_one); + GET_BLOCK(set_two); +#undef GET_BLOCK + EnsurePredecessorOrder(escape, {set_one, set_two}); + HInstruction* int_val = MakeParam(DataType::Type::kInt32); + HInstruction* c0 = graph_->GetIntConstant(0); + HInstruction* c4 = graph_->GetIntConstant(4); + HInstruction* c5 = graph_->GetIntConstant(5); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(switch_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* store_one = MakeIFieldSet(new_inst, c4, MemberOffset(32)); + HInstruction* goto_one = new (GetAllocator()) HGoto(); + set_one->AddInstruction(store_one); + set_one->AddInstruction(goto_one); + + HInstruction* store_two = MakeIFieldSet(new_inst, c5, MemberOffset(32)); + HInstruction* goto_two = new (GetAllocator()) HGoto(); + set_two->AddInstruction(store_two); + set_two->AddInstruction(goto_two); + + HInstruction* read_early = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_early = new (GetAllocator()) HReturn(read_early); + early_return->AddInstruction(read_early); + early_return->AddInstruction(return_early); + + HInstruction* escape_instruction = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* read_escape = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_escape = new (GetAllocator()) HReturn(read_escape); + escape->AddInstruction(escape_instruction); + escape->AddInstruction(read_escape); + escape->AddInstruction(return_escape); + escape_instruction->CopyEnvironmentFrom(cls->GetEnvironment()); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + // Each escaping switch path gets its own materialization block. + // Blocks: + // early_return(5) -> [exit(4)] + // entry(3) -> [early_return(5), <Unnamed>(9), <Unnamed>(10)] + // escape(8) -> [exit(4)] + // exit(4) -> [] + // set_one(6) -> [escape(8)] + // set_two(7) -> [escape(8)] + // <Unnamed>(10) -> [set_two(7)] + // <Unnamed>(9) -> [set_one(6)] + HBasicBlock* materialize_one = set_one->GetSinglePredecessor(); + HBasicBlock* materialize_two = set_two->GetSinglePredecessor(); + HNewInstance* materialization_ins_one = + FindSingleInstruction<HNewInstance>(graph_, materialize_one); + HNewInstance* materialization_ins_two = + FindSingleInstruction<HNewInstance>(graph_, materialize_two); + HPhi* new_phi = FindSingleInstruction<HPhi>(graph_, escape); + EXPECT_NE(materialization_ins_one, nullptr); + EXPECT_NE(materialization_ins_two, nullptr); + EXPECT_EQ(materialization_ins_one, new_phi->InputAt(0)) + << *materialization_ins_one << " vs " << *new_phi; + EXPECT_EQ(materialization_ins_two, new_phi->InputAt(1)) + << *materialization_ins_two << " vs " << *new_phi; + + EXPECT_INS_RETAINED(escape_instruction); + EXPECT_INS_RETAINED(read_escape); + EXPECT_EQ(read_escape->InputAt(0), new_phi) << *new_phi << " vs " << *read_escape->InputAt(0); + EXPECT_EQ(store_one->InputAt(0), materialization_ins_one); + EXPECT_EQ(store_two->InputAt(0), materialization_ins_two); + EXPECT_EQ(escape_instruction->InputAt(0), new_phi); + EXPECT_INS_REMOVED(read_early); + EXPECT_EQ(return_early->InputAt(0), c0); +} + +// // ENTRY +// // To be moved +// obj = new Obj(); +// switch(args) { +// case a: +// // set_one_and_escape +// obj.a = 5; +// escape(obj); +// // FALLTHROUGH +// case c: +// // set_two +// obj.a = 4; break; +// default: +// return obj.a; +// } +// escape(obj); +// return obj.a; +// EXIT +TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc4) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + // Break the critical edge between entry and set_two with the + // set_two_critical_break node. Graph simplification would do this for us if + // we didn't do it manually. This way we have a nice-name for debugging and + // testing. + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "early_return"}, + {"entry", "set_one_and_escape"}, + {"entry", "set_two_critical_break"}, + {"set_two_critical_break", "set_two"}, + {"early_return", "exit"}, + {"set_one_and_escape", "set_two"}, + {"set_two", "escape"}, + {"escape", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(escape); + GET_BLOCK(early_return); + GET_BLOCK(set_one_and_escape); + GET_BLOCK(set_two); + GET_BLOCK(set_two_critical_break); +#undef GET_BLOCK + EnsurePredecessorOrder(set_two, {set_one_and_escape, set_two_critical_break}); + HInstruction* int_val = MakeParam(DataType::Type::kInt32); + HInstruction* c0 = graph_->GetIntConstant(0); + HInstruction* c4 = graph_->GetIntConstant(4); + HInstruction* c5 = graph_->GetIntConstant(5); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(switch_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* store_one = MakeIFieldSet(new_inst, c4, MemberOffset(32)); + HInstruction* escape_one = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_one = new (GetAllocator()) HGoto(); + set_one_and_escape->AddInstruction(store_one); + set_one_and_escape->AddInstruction(escape_one); + set_one_and_escape->AddInstruction(goto_one); + escape_one->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* goto_crit_break = new (GetAllocator()) HGoto(); + set_two_critical_break->AddInstruction(goto_crit_break); + + HInstruction* store_two = MakeIFieldSet(new_inst, c5, MemberOffset(32)); + HInstruction* goto_two = new (GetAllocator()) HGoto(); + set_two->AddInstruction(store_two); + set_two->AddInstruction(goto_two); + + HInstruction* read_early = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_early = new (GetAllocator()) HReturn(read_early); + early_return->AddInstruction(read_early); + early_return->AddInstruction(return_early); + + HInstruction* escape_instruction = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* read_escape = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_escape = new (GetAllocator()) HReturn(read_escape); + escape->AddInstruction(escape_instruction); + escape->AddInstruction(read_escape); + escape->AddInstruction(return_escape); + escape_instruction->CopyEnvironmentFrom(cls->GetEnvironment()); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_REMOVED(read_early); + EXPECT_EQ(return_early->InputAt(0), c0); + // Each escaping switch path gets its own materialization block. + // Blocks: + // early_return(5) -> [exit(4)] + // entry(3) -> [early_return(5), <Unnamed>(10), <Unnamed>(11)] + // escape(9) -> [exit(4)] + // exit(4) -> [] + // set_one_and_escape(6) -> [set_two(8)] + // set_two(8) -> [escape(9)] + // set_two_critical_break(7) -> [set_two(8)] + // <Unnamed>(11) -> [set_two_critical_break(7)] + // <Unnamed>(10) -> [set_one_and_escape(6)] + HBasicBlock* materialize_one = set_one_and_escape->GetSinglePredecessor(); + HBasicBlock* materialize_two = set_two_critical_break->GetSinglePredecessor(); + HNewInstance* materialization_ins_one = + FindSingleInstruction<HNewInstance>(graph_, materialize_one); + HNewInstance* materialization_ins_two = + FindSingleInstruction<HNewInstance>(graph_, materialize_two); + HPhi* new_phi = FindSingleInstruction<HPhi>(graph_, set_two); + ASSERT_NE(new_phi, nullptr); + ASSERT_NE(materialization_ins_one, nullptr); + ASSERT_NE(materialization_ins_two, nullptr); + EXPECT_INS_EQ(materialization_ins_one, new_phi->InputAt(0)); + EXPECT_INS_EQ(materialization_ins_two, new_phi->InputAt(1)); + + EXPECT_INS_EQ(store_one->InputAt(0), materialization_ins_one); + EXPECT_INS_EQ(store_two->InputAt(0), new_phi) << *store_two << " vs " << *new_phi; + EXPECT_INS_EQ(escape_instruction->InputAt(0), new_phi); + EXPECT_INS_RETAINED(escape_one); + EXPECT_INS_EQ(escape_one->InputAt(0), materialization_ins_one); + EXPECT_INS_RETAINED(escape_instruction); + EXPECT_INS_RETAINED(read_escape); + EXPECT_EQ(read_escape->InputAt(0), new_phi) << *new_phi << " vs " << *read_escape->InputAt(0); +} + +// // ENTRY +// // To be moved +// obj = new Obj(); +// switch(args) { +// case a: +// // set_one +// obj.a = 5; +// // nb passthrough +// case c: +// // set_two_and_escape +// obj.a += 4; +// escape(obj); +// break; +// default: +// obj.a = 10; +// } +// return obj.a; +// EXIT +TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc5) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + // Break the critical edge between entry and set_two with the + // set_two_critical_break node. Graph simplification would do this for us if + // we didn't do it manually. This way we have a nice-name for debugging and + // testing. + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "set_noescape"}, + {"entry", "set_one"}, + {"entry", "set_two_critical_break"}, + {"set_two_critical_break", "set_two_and_escape"}, + {"set_noescape", "breturn"}, + {"set_one", "set_two_and_escape"}, + {"set_two_and_escape", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(set_noescape); + GET_BLOCK(set_one); + GET_BLOCK(set_two_and_escape); + GET_BLOCK(set_two_critical_break); +#undef GET_BLOCK + EnsurePredecessorOrder(set_two_and_escape, {set_one, set_two_critical_break}); + EnsurePredecessorOrder(breturn, {set_two_and_escape, set_noescape}); + HInstruction* int_val = MakeParam(DataType::Type::kInt32); + HInstruction* c0 = graph_->GetIntConstant(0); + HInstruction* c4 = graph_->GetIntConstant(4); + HInstruction* c5 = graph_->GetIntConstant(5); + HInstruction* c10 = graph_->GetIntConstant(10); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(switch_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* store_one = MakeIFieldSet(new_inst, c5, MemberOffset(32)); + HInstruction* goto_one = new (GetAllocator()) HGoto(); + set_one->AddInstruction(store_one); + set_one->AddInstruction(goto_one); + + HInstruction* goto_crit_break = new (GetAllocator()) HGoto(); + set_two_critical_break->AddInstruction(goto_crit_break); + + HInstruction* get_two = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* add_two = new (GetAllocator()) HAdd(DataType::Type::kInt32, get_two, c4); + HInstruction* store_two = MakeIFieldSet(new_inst, add_two, MemberOffset(32)); + HInstruction* escape_two = MakeInvoke(DataType::Type::kVoid, {new_inst}); + HInstruction* goto_two = new (GetAllocator()) HGoto(); + set_two_and_escape->AddInstruction(get_two); + set_two_and_escape->AddInstruction(add_two); + set_two_and_escape->AddInstruction(store_two); + set_two_and_escape->AddInstruction(escape_two); + set_two_and_escape->AddInstruction(goto_two); + escape_two->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* store_noescape = MakeIFieldSet(new_inst, c10, MemberOffset(32)); + HInstruction* goto_noescape = new (GetAllocator()) HGoto(); + set_noescape->AddInstruction(store_noescape); + set_noescape->AddInstruction(goto_noescape); + + HInstruction* read_breturn = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_breturn = new (GetAllocator()) HReturn(read_breturn); + breturn->AddInstruction(read_breturn); + breturn->AddInstruction(return_breturn); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + // Normal LSE can get rid of these two. + EXPECT_INS_REMOVED(store_one); + EXPECT_INS_REMOVED(get_two); + EXPECT_INS_RETAINED(add_two); + EXPECT_TRUE(add_two->InputAt(0)->IsPhi()); + EXPECT_INS_EQ(add_two->InputAt(0)->InputAt(0), c5); + EXPECT_INS_EQ(add_two->InputAt(0)->InputAt(1), c0); + EXPECT_INS_EQ(add_two->InputAt(1), c4); + + HBasicBlock* materialize_one = set_one->GetSinglePredecessor(); + HBasicBlock* materialize_two = set_two_critical_break->GetSinglePredecessor(); + HNewInstance* materialization_ins_one = + FindSingleInstruction<HNewInstance>(graph_, materialize_one); + HNewInstance* materialization_ins_two = + FindSingleInstruction<HNewInstance>(graph_, materialize_two); + std::vector<HPhi*> phis; + std::tie(phis) = FindAllInstructions<HPhi>(graph_, set_two_and_escape); + HPhi* new_phi = FindOrNull( + phis.begin(), phis.end(), [&](auto p) { return p->GetType() == DataType::Type::kReference; }); + ASSERT_NE(new_phi, nullptr); + ASSERT_NE(materialization_ins_one, nullptr); + ASSERT_NE(materialization_ins_two, nullptr); + EXPECT_INS_EQ(materialization_ins_one, new_phi->InputAt(0)); + EXPECT_INS_EQ(materialization_ins_two, new_phi->InputAt(1)); + + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); + EXPECT_TRUE(pred_get->GetTarget()->IsPhi()); + EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(0), new_phi); + EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(1), graph_->GetNullConstant()); + + EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), c0); + EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), c10); } // // ENTRY @@ -2421,117 +4171,59 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination2) { // } // EXIT TEST_F(LoadStoreEliminationTest, PartialLoadElimination3) { - InitGraph(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); AdjacencyListGraph blks(SetupFromAdjacencyList( "entry", "exit", - { { "entry", "left" }, { "entry", "right" }, { "left", "exit" }, { "right", "exit" } })); + {{"entry", "left"}, {"entry", "right"}, {"left", "exit"}, {"right", "exit"}})); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(exit); GET_BLOCK(left); GET_BLOCK(right); #undef GET_BLOCK - HInstruction* bool_value = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(if_inst); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(cls, ¤t_locals); + ManuallyBuildEnvFor(cls, {}); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, - c1, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* call_left = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - HInstruction* read_left = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* read_left = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); HInstruction* return_left = new (GetAllocator()) HReturn(read_left); - call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(write_left); left->AddInstruction(call_left); left->AddInstruction(read_left); left->AddInstruction(return_left); call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, - c2, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* read_right = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* read_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); HInstruction* return_right = new (GetAllocator()) HReturn(read_right); right->AddInstruction(write_right); right->AddInstruction(read_right); right->AddInstruction(return_right); - HInstruction* exit_instruction = new (GetAllocator()) HExit(); - exit->AddInstruction(exit_instruction); + SetupExit(exit); + // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); PerformLSE(); - EXPECT_TRUE(IsRemoved(read_right)); - EXPECT_TRUE(IsRemoved(write_right)); - EXPECT_FALSE(IsRemoved(write_left)); - EXPECT_FALSE(IsRemoved(call_left)); - EXPECT_FALSE(IsRemoved(read_left)); + EXPECT_INS_REMOVED(read_right); + EXPECT_INS_REMOVED(write_right); + EXPECT_INS_RETAINED(write_left); + EXPECT_INS_RETAINED(call_left); + EXPECT_INS_RETAINED(read_left); } // // ENTRY @@ -2556,17 +4248,18 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination3) { // } // EXIT TEST_F(LoadStoreEliminationTest, PartialLoadElimination4) { - InitGraph(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - { { "entry", "entry_post" }, - { "entry_post", "right" }, - { "right", "exit" }, - { "entry_post", "left_pre" }, - { "left_pre", "left_loop" }, - { "left_loop", "left_loop" }, - { "left_loop", "left_finish" }, - { "left_finish", "exit" } })); + {{"entry", "entry_post"}, + {"entry_post", "right"}, + {"right", "exit"}, + {"entry_post", "left_pre"}, + {"left_pre", "left_loop"}, + {"left_loop", "left_loop"}, + {"left_loop", "left_finish"}, + {"left_finish", "exit"}})); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(entry_post); @@ -2580,75 +4273,32 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination4) { if (left_loop->GetSuccessors()[0] != left_finish) { left_loop->SwapSuccessors(); } - HInstruction* bool_value = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); HInstruction* goto_entry = new (GetAllocator()) HGoto(); - entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(goto_entry); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(cls, ¤t_locals); + ManuallyBuildEnvFor(cls, {}); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); entry_post->AddInstruction(if_inst); - HInstruction* write_left_pre = new (GetAllocator()) HInstanceFieldSet(new_inst, - c1, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_left_pre = MakeIFieldSet(new_inst, c1, MemberOffset(32)); HInstruction* goto_left_pre = new (GetAllocator()) HGoto(); left_pre->AddInstruction(write_left_pre); left_pre->AddInstruction(goto_left_pre); HInstruction* suspend_left_loop = new (GetAllocator()) HSuspendCheck(); - HInstruction* call_left_loop = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kBool, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - HInstruction* write_left_loop = new (GetAllocator()) HInstanceFieldSet(new_inst, - c3, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* call_left_loop = MakeInvoke(DataType::Type::kBool, { new_inst }); + HInstruction* write_left_loop = MakeIFieldSet(new_inst, c3, MemberOffset(32)); HInstruction* if_left_loop = new (GetAllocator()) HIf(call_left_loop); - call_left_loop->AsInvoke()->SetRawInputAt(0, new_inst); left_loop->AddInstruction(suspend_left_loop); left_loop->AddInstruction(call_left_loop); left_loop->AddInstruction(write_left_loop); @@ -2656,55 +4306,30 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination4) { suspend_left_loop->CopyEnvironmentFrom(cls->GetEnvironment()); call_left_loop->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* read_left_end = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* read_left_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); HInstruction* return_left_end = new (GetAllocator()) HReturn(read_left_end); left_finish->AddInstruction(read_left_end); left_finish->AddInstruction(return_left_end); - HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, - c2, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* read_right = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* read_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); HInstruction* return_right = new (GetAllocator()) HReturn(read_right); right->AddInstruction(write_right); right->AddInstruction(read_right); right->AddInstruction(return_right); - HInstruction* exit_instruction = new (GetAllocator()) HExit(); - exit->AddInstruction(exit_instruction); + SetupExit(exit); + // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); PerformLSE(); - EXPECT_FALSE(IsRemoved(write_left_pre)); - EXPECT_TRUE(IsRemoved(read_right)); - EXPECT_TRUE(IsRemoved(write_right)); - EXPECT_FALSE(IsRemoved(write_left_loop)); - EXPECT_FALSE(IsRemoved(call_left_loop)); - EXPECT_TRUE(IsRemoved(read_left_end)); + EXPECT_INS_RETAINED(write_left_pre); + EXPECT_INS_REMOVED(read_right); + EXPECT_INS_REMOVED(write_right); + EXPECT_INS_RETAINED(write_left_loop); + EXPECT_INS_RETAINED(call_left_loop); + EXPECT_INS_REMOVED(read_left_end); } // // ENTRY @@ -2725,14 +4350,15 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination4) { // ELIMINATE // return obj.field TEST_F(LoadStoreEliminationTest, PartialLoadElimination5) { - InitGraph(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - { { "entry", "left" }, - { "entry", "right" }, - { "left", "breturn" }, - { "right", "breturn" }, - { "breturn", "exit" } })); + {{"entry", "left"}, + {"entry", "right"}, + {"left", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(exit); @@ -2740,112 +4366,51 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination5) { GET_BLOCK(left); GET_BLOCK(right); #undef GET_BLOCK - HInstruction* bool_value = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(if_inst); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(cls, ¤t_locals); + ManuallyBuildEnvFor(cls, {}); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* call_left = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, - c1, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); HInstruction* goto_left = new (GetAllocator()) HGoto(); - call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(call_left); left->AddInstruction(write_left); left->AddInstruction(goto_left); call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, - c2, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* call_right = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 0, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* call_right = MakeInvoke(DataType::Type::kVoid, {}); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(call_right); right->AddInstruction(goto_right); call_right->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); breturn->AddInstruction(read_bottom); breturn->AddInstruction(return_exit); - HInstruction* exit_instruction = new (GetAllocator()) HExit(); - exit->AddInstruction(exit_instruction); + SetupExit(exit); + // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); PerformLSE(); - EXPECT_TRUE(IsRemoved(read_bottom)); - EXPECT_TRUE(IsRemoved(write_right)); - EXPECT_FALSE(IsRemoved(write_left)); - EXPECT_FALSE(IsRemoved(call_left)); - EXPECT_FALSE(IsRemoved(call_right)); + EXPECT_INS_REMOVED(read_bottom); + EXPECT_INS_REMOVED(write_right); + EXPECT_INS_RETAINED(write_left); + EXPECT_INS_RETAINED(call_left); + EXPECT_INS_RETAINED(call_right); } // // ENTRY @@ -2866,16 +4431,17 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination5) { // } // EXIT // ELIMINATE -// return obj.field +// return obj.fid TEST_F(LoadStoreEliminationTest, PartialLoadElimination6) { - InitGraph(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - { { "entry", "left" }, - { "entry", "right" }, - { "left", "breturn" }, - { "right", "breturn" }, - { "breturn", "exit" } })); + {{"entry", "left"}, + {"entry", "right"}, + {"left", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(exit); @@ -2883,138 +4449,59 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination6) { GET_BLOCK(left); GET_BLOCK(right); #undef GET_BLOCK - HInstruction* bool_value = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); HInstruction* c3 = graph_->GetIntConstant(3); HInstruction* c5 = graph_->GetIntConstant(5); - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); - HInstruction* write_entry = new (GetAllocator()) HInstanceFieldSet(new_inst, - c3, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* call_entry = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 0, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* call_entry = MakeInvoke(DataType::Type::kVoid, {}); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(write_entry); entry->AddInstruction(call_entry); entry->AddInstruction(if_inst); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(cls, ¤t_locals); + ManuallyBuildEnvFor(cls, {}); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); call_entry->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_left_start = new (GetAllocator()) HInstanceFieldSet(new_inst, - c5, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* call_left = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, - c1, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_left_start = MakeIFieldSet(new_inst, c5, MemberOffset(32)); + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); HInstruction* goto_left = new (GetAllocator()) HGoto(); - call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(write_left_start); left->AddInstruction(call_left); left->AddInstruction(write_left); left->AddInstruction(goto_left); call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, - c2, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(goto_right); - HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); breturn->AddInstruction(read_bottom); breturn->AddInstruction(return_exit); - HInstruction* exit_instruction = new (GetAllocator()) HExit(); - exit->AddInstruction(exit_instruction); + SetupExit(exit); + // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); PerformLSE(); - EXPECT_TRUE(IsRemoved(read_bottom)); - EXPECT_TRUE(IsRemoved(write_right)); - EXPECT_TRUE(IsRemoved(write_entry)); - EXPECT_FALSE(IsRemoved(write_left_start)); - EXPECT_FALSE(IsRemoved(write_left)); - EXPECT_FALSE(IsRemoved(call_left)); - EXPECT_FALSE(IsRemoved(call_entry)); + EXPECT_INS_REMOVED(read_bottom); + EXPECT_INS_REMOVED(write_right); + EXPECT_INS_REMOVED(write_entry); + EXPECT_INS_RETAINED(write_left_start); + EXPECT_INS_RETAINED(write_left); + EXPECT_INS_RETAINED(call_left); + EXPECT_INS_RETAINED(call_entry); } // // ENTRY @@ -3038,18 +4525,19 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination6) { // return obj.field; // EXIT TEST_F(LoadStoreEliminationTest, PartialLoadPreserved3) { - InitGraph(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - { { "entry", "entry_post" }, - { "entry_post", "right" }, - { "right", "return_block" }, - { "entry_post", "left_pre" }, - { "left_pre", "left_loop" }, - { "left_loop", "left_loop_post" }, - { "left_loop_post", "left_loop" }, - { "left_loop", "return_block" }, - { "return_block", "exit" } })); + {{"entry", "entry_post"}, + {"entry_post", "right"}, + {"right", "return_block"}, + {"entry_post", "left_pre"}, + {"left_pre", "left_loop"}, + {"left_loop", "left_loop_post"}, + {"left_loop_post", "left_loop"}, + {"left_loop", "return_block"}, + {"return_block", "exit"}})); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(entry_post); @@ -3064,123 +4552,63 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved3) { if (left_loop->GetSuccessors()[0] != return_block) { left_loop->SwapSuccessors(); } - HInstruction* bool_value = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); HInstruction* goto_entry = new (GetAllocator()) HGoto(); - entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(goto_entry); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(cls, ¤t_locals); + ManuallyBuildEnvFor(cls, {}); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); entry_post->AddInstruction(if_inst); - HInstruction* write_left_pre = new (GetAllocator()) HInstanceFieldSet(new_inst, - c1, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_left_pre = MakeIFieldSet(new_inst, c1, MemberOffset(32)); HInstruction* goto_left_pre = new (GetAllocator()) HGoto(); left_pre->AddInstruction(write_left_pre); left_pre->AddInstruction(goto_left_pre); HInstruction* suspend_left_loop = new (GetAllocator()) HSuspendCheck(); - HInstruction* call_left_loop = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kBool, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* call_left_loop = MakeInvoke(DataType::Type::kBool, { new_inst }); HInstruction* if_left_loop = new (GetAllocator()) HIf(call_left_loop); - call_left_loop->AsInvoke()->SetRawInputAt(0, new_inst); left_loop->AddInstruction(suspend_left_loop); left_loop->AddInstruction(call_left_loop); left_loop->AddInstruction(if_left_loop); suspend_left_loop->CopyEnvironmentFrom(cls->GetEnvironment()); call_left_loop->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_left_loop = new (GetAllocator()) HInstanceFieldSet(new_inst, - c3, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_left_loop = MakeIFieldSet(new_inst, c3, MemberOffset(32)); HInstruction* goto_left_loop = new (GetAllocator()) HGoto(); left_loop_post->AddInstruction(write_left_loop); left_loop_post->AddInstruction(goto_left_loop); - HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, - c2, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(goto_right); - HInstruction* read_return = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* read_return = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); HInstruction* return_final = new (GetAllocator()) HReturn(read_return); return_block->AddInstruction(read_return); return_block->AddInstruction(return_final); - HInstruction* exit_instruction = new (GetAllocator()) HExit(); - exit->AddInstruction(exit_instruction); + SetupExit(exit); + // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); - PerformLSE(); + PerformLSENoPartial(); - EXPECT_FALSE(IsRemoved(write_left_pre)); - EXPECT_FALSE(IsRemoved(read_return)); - EXPECT_FALSE(IsRemoved(write_right)); - EXPECT_FALSE(IsRemoved(write_left_loop)); - EXPECT_FALSE(IsRemoved(call_left_loop)); + EXPECT_INS_RETAINED(write_left_pre) << *write_left_pre; + EXPECT_INS_RETAINED(read_return) << *read_return; + EXPECT_INS_RETAINED(write_right) << *write_right; + EXPECT_INS_RETAINED(write_left_loop) << *write_left_loop; + EXPECT_INS_RETAINED(call_left_loop) << *call_left_loop; } // // ENTRY @@ -3205,17 +4633,18 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved3) { // return obj.field; // EXIT TEST_F(LoadStoreEliminationTest, PartialLoadPreserved4) { - InitGraph(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - { { "entry", "entry_post" }, - { "entry_post", "right" }, - { "right", "return_block" }, - { "entry_post", "left_pre" }, - { "left_pre", "left_loop" }, - { "left_loop", "left_loop" }, - { "left_loop", "return_block" }, - { "return_block", "exit" } })); + {{"entry", "entry_post"}, + {"entry_post", "right"}, + {"right", "return_block"}, + {"entry_post", "left_pre"}, + {"left_pre", "left_loop"}, + {"left_loop", "left_loop"}, + {"left_loop", "return_block"}, + {"return_block", "exit"}})); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(entry_post); @@ -3229,73 +4658,31 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved4) { if (left_loop->GetSuccessors()[0] != return_block) { left_loop->SwapSuccessors(); } - HInstruction* bool_value = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); HInstruction* goto_entry = new (GetAllocator()) HGoto(); - entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(goto_entry); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(cls, ¤t_locals); + ManuallyBuildEnvFor(cls, {}); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); entry_post->AddInstruction(if_inst); - HInstruction* write_left_pre = new (GetAllocator()) HInstanceFieldSet(new_inst, - c1, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_left_pre = MakeIFieldSet(new_inst, c1, MemberOffset(32)); HInstruction* goto_left_pre = new (GetAllocator()) HGoto(); left_pre->AddInstruction(write_left_pre); left_pre->AddInstruction(goto_left_pre); HInstruction* suspend_left_loop = new (GetAllocator()) HSuspendCheck(); - HInstruction* call_left_loop = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 0, - DataType::Type::kBool, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - HInstruction* write_left_loop = new (GetAllocator()) HInstanceFieldSet(new_inst, - c3, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* call_left_loop = MakeInvoke(DataType::Type::kBool, {}); + HInstruction* write_left_loop = MakeIFieldSet(new_inst, c3, MemberOffset(32)); HInstruction* if_left_loop = new (GetAllocator()) HIf(call_left_loop); left_loop->AddInstruction(suspend_left_loop); left_loop->AddInstruction(call_left_loop); @@ -3304,59 +4691,31 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved4) { suspend_left_loop->CopyEnvironmentFrom(cls->GetEnvironment()); call_left_loop->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, - c2, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* call_right = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kBool, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* call_right = MakeInvoke(DataType::Type::kBool, { new_inst }); HInstruction* goto_right = new (GetAllocator()) HGoto(); - call_right->AsInvoke()->SetRawInputAt(0, new_inst); right->AddInstruction(write_right); right->AddInstruction(call_right); right->AddInstruction(goto_right); call_right->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* read_return = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* read_return = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); HInstruction* return_final = new (GetAllocator()) HReturn(read_return); return_block->AddInstruction(read_return); return_block->AddInstruction(return_final); - HInstruction* exit_instruction = new (GetAllocator()) HExit(); - exit->AddInstruction(exit_instruction); + SetupExit(exit); + // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); - PerformLSE(); - - EXPECT_FALSE(IsRemoved(read_return)); - EXPECT_FALSE(IsRemoved(write_right)); - EXPECT_FALSE(IsRemoved(write_left_loop)); - EXPECT_FALSE(IsRemoved(call_left_loop)); - EXPECT_TRUE(IsRemoved(write_left_pre)); - EXPECT_FALSE(IsRemoved(call_right)); + PerformLSENoPartial(); + + EXPECT_INS_RETAINED(read_return); + EXPECT_INS_RETAINED(write_right); + EXPECT_INS_RETAINED(write_left_loop); + EXPECT_INS_RETAINED(call_left_loop); + EXPECT_INS_REMOVED(write_left_pre); + EXPECT_INS_RETAINED(call_right); } // // ENTRY @@ -3379,14 +4738,15 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved4) { // ELIMINATE // return obj.field TEST_F(LoadStoreEliminationTest, PartialLoadPreserved5) { - InitGraph(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - { { "entry", "left" }, - { "entry", "right" }, - { "left", "breturn" }, - { "right", "breturn" }, - { "breturn", "exit" } })); + {{"entry", "left"}, + {"entry", "right"}, + {"left", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(exit); @@ -3394,67 +4754,23 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved5) { GET_BLOCK(left); GET_BLOCK(right); #undef GET_BLOCK - HInstruction* bool_value = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(if_inst); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(cls, ¤t_locals); + ManuallyBuildEnvFor(cls, {}); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* call_left = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, - c1, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* call2_left = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 0, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* call2_left = MakeInvoke(DataType::Type::kVoid, {}); HInstruction* goto_left = new (GetAllocator()) HGoto(); - call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(call_left); left->AddInstruction(write_left); left->AddInstruction(call2_left); @@ -3462,57 +4778,30 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved5) { call_left->CopyEnvironmentFrom(cls->GetEnvironment()); call2_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, - c2, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* call_right = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 0, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* call_right = MakeInvoke(DataType::Type::kVoid, {}); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(call_right); right->AddInstruction(goto_right); call_right->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); breturn->AddInstruction(read_bottom); breturn->AddInstruction(return_exit); - HInstruction* exit_instruction = new (GetAllocator()) HExit(); - exit->AddInstruction(exit_instruction); + SetupExit(exit); + // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); - PerformLSE(); + PerformLSENoPartial(); - EXPECT_FALSE(IsRemoved(read_bottom)); - EXPECT_FALSE(IsRemoved(write_right)); - EXPECT_FALSE(IsRemoved(write_left)); - EXPECT_FALSE(IsRemoved(call_left)); - EXPECT_FALSE(IsRemoved(call_right)); + EXPECT_INS_RETAINED(read_bottom); + EXPECT_INS_RETAINED(write_right); + EXPECT_INS_RETAINED(write_left); + EXPECT_INS_RETAINED(call_left); + EXPECT_INS_RETAINED(call_right); } // // ENTRY @@ -3534,14 +4823,14 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved5) { // ELIMINATE // return obj.field TEST_F(LoadStoreEliminationTest, PartialLoadPreserved6) { - InitGraph(); + CreateGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - { { "entry", "left" }, - { "entry", "right" }, - { "left", "breturn" }, - { "right", "breturn" }, - { "breturn", "exit" } })); + {{"entry", "left"}, + {"entry", "right"}, + {"left", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(exit); @@ -3549,125 +4838,3090 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved6) { GET_BLOCK(left); GET_BLOCK(right); #undef GET_BLOCK - HInstruction* bool_value = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); - HInstruction* write_entry = new (GetAllocator()) HInstanceFieldSet(new_inst, - c3, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* call_entry = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 0, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* call_entry = MakeInvoke(DataType::Type::kVoid, {}); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(write_entry); entry->AddInstruction(call_entry); entry->AddInstruction(if_inst); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(cls, ¤t_locals); + ManuallyBuildEnvFor(cls, {}); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); call_entry->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* call_left = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kVoid, - 0, - { nullptr, 0 }, - nullptr, - {}, - InvokeType::kStatic, - { nullptr, 0 }, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, - c1, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); HInstruction* goto_left = new (GetAllocator()) HGoto(); - call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(call_left); left->AddInstruction(write_left); left->AddInstruction(goto_left); call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, - c2, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(write_right); + right->AddInstruction(goto_right); + + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); + breturn->AddInstruction(read_bottom); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + + LOG(INFO) << "Pre LSE " << blks; + PerformLSENoPartial(); + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_REMOVED(read_bottom); + EXPECT_INS_REMOVED(write_right); + EXPECT_INS_RETAINED(write_entry); + EXPECT_INS_RETAINED(write_left); + EXPECT_INS_RETAINED(call_left); + EXPECT_INS_RETAINED(call_entry); +} + +// // ENTRY +// // MOVED TO MATERIALIZATION BLOCK +// obj = new Obj(); +// ELIMINATE, moved to materialization block. Kept by escape. +// obj.field = 3; +// // Make sure this graph isn't broken +// if (obj ==/!= (STATIC.VALUE|obj|null)) { +// // partial_BLOCK +// // REMOVE (either from unreachable or normal PHI creation) +// obj.field = 4; +// } +// if (parameter_value) { +// // LEFT +// // DO NOT ELIMINATE +// escape(obj); +// } else { +// // RIGHT +// // ELIMINATE +// obj.field = 2; +// } +// EXIT +// PREDICATED GET +// return obj.field +TEST_P(PartialComparisonTestGroup, PartialComparisonBeforeCohort) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "critical_break"}, + {"entry", "partial"}, + {"partial", "merge"}, + {"critical_break", "merge"}, + {"merge", "left"}, + {"merge", "right"}, + {"left", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(merge); + GET_BLOCK(partial); + GET_BLOCK(critical_break); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); +#undef GET_BLOCK + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* c2 = graph_->GetIntConstant(2); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c4 = graph_->GetIntConstant(4); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst); + HInstruction* if_inst = new (GetAllocator()) HIf(cmp_instructions.cmp_); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(write_entry); + cmp_instructions.AddSetup(entry); + entry->AddInstruction(cmp_instructions.cmp_); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + cmp_instructions.AddEnvironment(cls->GetEnvironment()); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32)); + HInstruction* goto_partial = new (GetAllocator()) HGoto(); + partial->AddInstruction(write_partial); + partial->AddInstruction(goto_partial); + + HInstruction* goto_crit_break = new (GetAllocator()) HGoto(); + critical_break->AddInstruction(goto_crit_break); + + HInstruction* if_merge = new (GetAllocator()) HIf(bool_value); + merge->AddInstruction(if_merge); + + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left); + left->AddInstruction(goto_left); + call_left->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(write_right); + right->AddInstruction(goto_right); + + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); + breturn->AddInstruction(read_bottom); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + std::vector<HPhi*> merges; + HPredicatedInstanceFieldGet* pred_get; + HInstanceFieldSet* init_set; + std::tie(pred_get, init_set) = + FindSingleInstructions<HPredicatedInstanceFieldGet, HInstanceFieldSet>(graph_); + std::tie(merges) = FindAllInstructions<HPhi>(graph_); + ASSERT_EQ(merges.size(), 3u); + HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn; + }); + HPhi* merge_value_top = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kInt32 && p->GetBlock() != breturn; + }); + HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) { + return p->GetType() == DataType::Type::kReference; + }); + EXPECT_INS_REMOVED(read_bottom); + EXPECT_INS_REMOVED(write_entry); + EXPECT_INS_REMOVED(write_partial); + EXPECT_INS_RETAINED(call_left); + CheckFinalInstruction(if_inst->InputAt(0), ComparisonPlacement::kBeforeEscape); + EXPECT_INS_EQ(init_set->InputAt(1), merge_value_top); + EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc); + EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return); +} + +// // ENTRY +// // MOVED TO MATERIALIZATION BLOCK +// obj = new Obj(); +// ELIMINATE, moved to materialization block. Kept by escape. +// obj.field = 3; +// // Make sure this graph isn't broken +// if (parameter_value) { +// if (obj ==/!= (STATIC.VALUE|obj|null)) { +// // partial_BLOCK +// obj.field = 4; +// } +// // LEFT +// // DO NOT ELIMINATE +// escape(obj); +// } else { +// // RIGHT +// // ELIMINATE +// obj.field = 2; +// } +// EXIT +// PREDICATED GET +// return obj.field +TEST_P(PartialComparisonTestGroup, PartialComparisonInCohortBeforeEscape) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left_begin"}, + {"left_begin", "partial"}, + {"left_begin", "left_crit_break"}, + {"left_crit_break", "left"}, + {"partial", "left"}, + {"entry", "right"}, + {"left", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(partial); + GET_BLOCK(left_begin); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(left_crit_break); + GET_BLOCK(right); +#undef GET_BLOCK + EnsurePredecessorOrder(left, {left_crit_break, partial}); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* c2 = graph_->GetIntConstant(2); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c4 = graph_->GetIntConstant(4); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(write_entry); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst); + HInstruction* if_left_begin = new (GetAllocator()) HIf(cmp_instructions.cmp_); + cmp_instructions.AddSetup(left_begin); + left_begin->AddInstruction(cmp_instructions.cmp_); + left_begin->AddInstruction(if_left_begin); + cmp_instructions.AddEnvironment(cls->GetEnvironment()); + + left_crit_break->AddInstruction(new (GetAllocator()) HGoto()); + + HInstruction* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32)); + HInstruction* goto_partial = new (GetAllocator()) HGoto(); + partial->AddInstruction(write_partial); + partial->AddInstruction(goto_partial); + + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left); + left->AddInstruction(goto_left); + call_left->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(write_right); + right->AddInstruction(goto_right); + + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); + breturn->AddInstruction(read_bottom); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + std::vector<HPhi*> merges; + HInstanceFieldSet* init_set = + FindSingleInstruction<HInstanceFieldSet>(graph_, left_begin->GetSinglePredecessor()); + HInstanceFieldSet* partial_set = FindSingleInstruction<HInstanceFieldSet>(graph_, partial); + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_); + std::tie(merges) = FindAllInstructions<HPhi>(graph_); + ASSERT_EQ(merges.size(), 2u); + HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kInt32; + }); + HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) { + return p->GetType() == DataType::Type::kReference; + }); + EXPECT_EQ(merge_value_return->GetBlock(), breturn) + << blks.GetName(merge_value_return->GetBlock()); + EXPECT_INS_REMOVED(read_bottom); + EXPECT_INS_REMOVED(write_entry); + EXPECT_INS_RETAINED(write_partial); + EXPECT_INS_RETAINED(call_left); + CheckFinalInstruction(if_left_begin->InputAt(0), ComparisonPlacement::kInEscape); + EXPECT_INS_EQ(init_set->InputAt(1), c3); + EXPECT_INS_EQ(partial_set->InputAt(0), init_set->InputAt(0)); + EXPECT_INS_EQ(partial_set->InputAt(1), c4); + EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc); + EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return); +} + +// // ENTRY +// // MOVED TO MATERIALIZATION BLOCK +// obj = new Obj(); +// ELIMINATE, moved to materialization block. Kept by escape. +// obj.field = 3; +// // Make sure this graph isn't broken +// if (parameter_value) { +// // LEFT +// // DO NOT ELIMINATE +// escape(obj); +// } else { +// // RIGHT +// // ELIMINATE +// obj.field = 2; +// } +// if (obj ==/!= (STATIC.VALUE|obj|null)) { +// // partial_BLOCK +// obj.field = 4; +// } +// EXIT +// PREDICATED GET +// return obj.field +TEST_P(PartialComparisonTestGroup, PartialComparisonAfterCohort) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"entry", "right"}, + {"left", "merge"}, + {"right", "merge"}, + {"merge", "critical_break"}, + {"critical_break", "breturn"}, + {"merge", "partial"}, + {"partial", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(partial); + GET_BLOCK(critical_break); + GET_BLOCK(merge); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {critical_break, partial}); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* c2 = graph_->GetIntConstant(2); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c4 = graph_->GetIntConstant(4); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(write_entry); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left); + left->AddInstruction(goto_left); + call_left->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(write_right); + right->AddInstruction(goto_right); + + ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst); + HInstruction* if_merge = new (GetAllocator()) HIf(cmp_instructions.cmp_); + cmp_instructions.AddSetup(merge); + merge->AddInstruction(cmp_instructions.cmp_); + merge->AddInstruction(if_merge); + cmp_instructions.AddEnvironment(cls->GetEnvironment()); + + HInstanceFieldSet* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32)); + HInstruction* goto_partial = new (GetAllocator()) HGoto(); + partial->AddInstruction(write_partial); + partial->AddInstruction(goto_partial); + + HInstruction* goto_crit_break = new (GetAllocator()) HGoto(); + critical_break->AddInstruction(goto_crit_break); + + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); + breturn->AddInstruction(read_bottom); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + std::vector<HPhi*> merges; + HInstanceFieldSet* init_set = + FindSingleInstruction<HInstanceFieldSet>(graph_, left->GetSinglePredecessor()); + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_); + std::tie(merges) = FindAllInstructions<HPhi>(graph_); + ASSERT_EQ(merges.size(), 3u); + HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn; + }); + HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) { + return p->GetType() == DataType::Type::kReference; + }); + EXPECT_INS_REMOVED(read_bottom); + EXPECT_INS_REMOVED(write_entry); + EXPECT_INS_RETAINED(write_partial); + EXPECT_TRUE(write_partial->GetIsPredicatedSet()); + EXPECT_INS_RETAINED(call_left); + CheckFinalInstruction(if_merge->InputAt(0), ComparisonPlacement::kAfterEscape); + EXPECT_INS_EQ(init_set->InputAt(1), c3); + ASSERT_TRUE(write_partial->InputAt(0)->IsPhi()); + EXPECT_INS_EQ(write_partial->InputAt(0)->AsPhi()->InputAt(0), init_set->InputAt(0)); + EXPECT_INS_EQ(write_partial->InputAt(1), c4); + EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc); + EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return); +} + +// // ENTRY +// // MOVED TO MATERIALIZATION BLOCK +// obj = new Obj(); +// ELIMINATE, moved to materialization block. Kept by escape. +// obj.field = 3; +// // Make sure this graph isn't broken +// if (parameter_value) { +// // LEFT +// // DO NOT ELIMINATE +// escape(obj); +// if (obj ==/!= (STATIC.VALUE|obj|null)) { +// // partial_BLOCK +// obj.field = 4; +// } +// } else { +// // RIGHT +// // ELIMINATE +// obj.field = 2; +// } +// EXIT +// PREDICATED GET +// return obj.field +TEST_P(PartialComparisonTestGroup, PartialComparisonInCohortAfterEscape) { + PartialComparisonKind kind = GetParam(); + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"left", "partial"}, + {"partial", "left_end"}, + {"left", "left_crit_break"}, + {"left_crit_break", "left_end"}, + {"left_end", "breturn"}, + {"entry", "right"}, + {"right", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(partial); + GET_BLOCK(left_end); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(left_crit_break); + GET_BLOCK(right); +#undef GET_BLOCK + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* c2 = graph_->GetIntConstant(2); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c4 = graph_->GetIntConstant(4); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(write_entry); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst); + HInstruction* if_left = new (GetAllocator()) HIf(cmp_instructions.cmp_); + left->AddInstruction(call_left); + cmp_instructions.AddSetup(left); + left->AddInstruction(cmp_instructions.cmp_); + left->AddInstruction(if_left); + call_left->CopyEnvironmentFrom(cls->GetEnvironment()); + cmp_instructions.AddEnvironment(cls->GetEnvironment()); + if (if_left->AsIf()->IfTrueSuccessor() != partial) { + left->SwapSuccessors(); + } + + HInstruction* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32)); + HInstruction* goto_partial = new (GetAllocator()) HGoto(); + partial->AddInstruction(write_partial); + partial->AddInstruction(goto_partial); + + HInstruction* goto_left_crit_break = new (GetAllocator()) HGoto(); + left_crit_break->AddInstruction(goto_left_crit_break); + + HInstruction* goto_left_end = new (GetAllocator()) HGoto(); + left_end->AddInstruction(goto_left_end); + + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(write_right); + right->AddInstruction(goto_right); + + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); + breturn->AddInstruction(read_bottom); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + std::vector<HPhi*> merges; + std::vector<HInstanceFieldSet*> sets; + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_); + std::tie(merges, sets) = FindAllInstructions<HPhi, HInstanceFieldSet>(graph_); + ASSERT_EQ(merges.size(), 2u); + ASSERT_EQ(sets.size(), 2u); + HInstanceFieldSet* init_set = FindOrNull(sets.begin(), sets.end(), [&](HInstanceFieldSet* s) { + return s->GetBlock()->GetSingleSuccessor() == left; + }); + EXPECT_INS_EQ(init_set->InputAt(1), c3); + HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn; + }); + HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) { + return p->GetType() == DataType::Type::kReference; + }); + EXPECT_INS_REMOVED(read_bottom); + EXPECT_INS_REMOVED(write_entry); + if (kind.IsPossiblyTrue()) { + EXPECT_INS_RETAINED(write_partial); + EXPECT_TRUE(std::find(sets.begin(), sets.end(), write_partial) != sets.end()); + } + EXPECT_INS_RETAINED(call_left); + CheckFinalInstruction(if_left->InputAt(0), ComparisonPlacement::kInEscape); + EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc); + EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return); +} + +INSTANTIATE_TEST_SUITE_P( + LoadStoreEliminationTest, + PartialComparisonTestGroup, + testing::Values(PartialComparisonKind{PartialComparisonKind::Type::kEquals, + PartialComparisonKind::Target::kNull, + PartialComparisonKind::Position::kLeft}, + PartialComparisonKind{PartialComparisonKind::Type::kEquals, + PartialComparisonKind::Target::kNull, + PartialComparisonKind::Position::kRight}, + PartialComparisonKind{PartialComparisonKind::Type::kEquals, + PartialComparisonKind::Target::kValue, + PartialComparisonKind::Position::kLeft}, + PartialComparisonKind{PartialComparisonKind::Type::kEquals, + PartialComparisonKind::Target::kValue, + PartialComparisonKind::Position::kRight}, + PartialComparisonKind{PartialComparisonKind::Type::kEquals, + PartialComparisonKind::Target::kSelf, + PartialComparisonKind::Position::kLeft}, + PartialComparisonKind{PartialComparisonKind::Type::kNotEquals, + PartialComparisonKind::Target::kNull, + PartialComparisonKind::Position::kLeft}, + PartialComparisonKind{PartialComparisonKind::Type::kNotEquals, + PartialComparisonKind::Target::kNull, + PartialComparisonKind::Position::kRight}, + PartialComparisonKind{PartialComparisonKind::Type::kNotEquals, + PartialComparisonKind::Target::kSelf, + PartialComparisonKind::Position::kLeft}, + PartialComparisonKind{PartialComparisonKind::Type::kNotEquals, + PartialComparisonKind::Target::kValue, + PartialComparisonKind::Position::kLeft}, + PartialComparisonKind{PartialComparisonKind::Type::kNotEquals, + PartialComparisonKind::Target::kValue, + PartialComparisonKind::Position::kRight})); + +// // ENTRY +// obj = new Obj(); +// if (parameter_value) { +// // LEFT +// escape(obj); +// } else { +// // RIGHT +// // ELIMINATE +// obj.field = 2; +// } +// EXIT +// predicated-ELIMINATE +// obj.field = 3; +TEST_F(LoadStoreEliminationTest, PredicatedStore1) { + VariableSizedHandleScope vshs(Thread::Current()); + InitGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"entry", "right"}, + {"left", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {left, right}); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* null_const = graph_->GetNullConstant(); + HInstruction* c2 = graph_->GetIntConstant(2); + HInstruction* c3 = graph_->GetIntConstant(3); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left); + left->AddInstruction(goto_left); + call_left->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(write_right); + right->AddInstruction(goto_right); + + HInstruction* write_bottom = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturnVoid(); + breturn->AddInstruction(write_bottom); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_RETAINED(write_bottom); + EXPECT_TRUE(write_bottom->AsInstanceFieldSet()->GetIsPredicatedSet()); + EXPECT_INS_REMOVED(write_right); + EXPECT_INS_RETAINED(call_left); + HPhi* merge_alloc = FindSingleInstruction<HPhi>(graph_, breturn); + ASSERT_NE(merge_alloc, nullptr); + EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc; + EXPECT_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) << *merge_alloc << " cls? " << *cls; + EXPECT_EQ(merge_alloc->InputAt(1), null_const); +} + +// // ENTRY +// obj = new Obj(); +// obj.field = 3; +// if (parameter_value) { +// // LEFT +// escape(obj); +// } else { +// // RIGHT +// // ELIMINATE +// obj.field = 2; +// } +// // MERGE +// if (second_param) { +// // NON_ESCAPE +// obj.field = 1; +// noescape(); +// } +// EXIT +// predicated-ELIMINATE +// obj.field = 4; +TEST_F(LoadStoreEliminationTest, PredicatedStore2) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"entry", "right"}, + {"left", "merge"}, + {"right", "merge"}, + {"merge", "non_escape"}, + {"non_escape", "breturn"}, + {"merge", "merge_crit_break"}, + {"merge_crit_break", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); + GET_BLOCK(merge); + GET_BLOCK(merge_crit_break); + GET_BLOCK(non_escape); +#undef GET_BLOCK + EnsurePredecessorOrder(merge, {left, right}); + EnsurePredecessorOrder(breturn, {merge_crit_break, non_escape}); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value2 = MakeParam(DataType::Type::kBool); + HInstruction* null_const = graph_->GetNullConstant(); + HInstruction* c1 = graph_->GetIntConstant(3); + HInstruction* c2 = graph_->GetIntConstant(2); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c4 = graph_->GetIntConstant(4); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(write_entry); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left); + left->AddInstruction(goto_left); + call_left->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(write_right); + right->AddInstruction(goto_right); + + HInstruction* merge_if = new (GetAllocator()) HIf(bool_value2); + merge->AddInstruction(merge_if); + + merge_crit_break->AddInstruction(new (GetAllocator()) HGoto()); + + HInstruction* write_non_escape = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* non_escape_call = MakeInvoke(DataType::Type::kVoid, {}); + HInstruction* non_escape_goto = new (GetAllocator()) HGoto(); + non_escape->AddInstruction(write_non_escape); + non_escape->AddInstruction(non_escape_call); + non_escape->AddInstruction(non_escape_goto); + non_escape_call->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_bottom = MakeIFieldSet(new_inst, c4, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturnVoid(); + breturn->AddInstruction(write_bottom); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_RETAINED(write_bottom); + EXPECT_TRUE(write_bottom->AsInstanceFieldSet()->GetIsPredicatedSet()) << *write_bottom; + EXPECT_INS_REMOVED(write_right); + EXPECT_INS_RETAINED(call_left); + HInstanceFieldSet* pred_set = FindSingleInstruction<HInstanceFieldSet>(graph_, breturn); + HPhi* merge_alloc = FindSingleInstruction<HPhi>(graph_); + ASSERT_NE(merge_alloc, nullptr); + EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc; + EXPECT_INS_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) << " phi is: " << *merge_alloc; + EXPECT_INS_EQ(merge_alloc->InputAt(1), null_const); + ASSERT_NE(pred_set, nullptr); + EXPECT_TRUE(pred_set->GetIsPredicatedSet()) << *pred_set; + EXPECT_INS_EQ(pred_set->InputAt(0), merge_alloc); +} + +// // ENTRY +// obj = new Obj(); +// obj.field = 3; +// if (parameter_value) { +// // LEFT +// escape(obj); +// } else { +// // RIGHT +// // ELIMINATE +// obj.field = 2; +// } +// EXIT +// predicated-ELIMINATE +// return obj.field +TEST_F(LoadStoreEliminationTest, PredicatedLoad1) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"entry", "right"}, + {"left", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {left, right}); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* null_const = graph_->GetNullConstant(); + HInstruction* c2 = graph_->GetIntConstant(2); + HInstruction* c3 = graph_->GetIntConstant(3); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(write_entry); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left); + left->AddInstruction(goto_left); + call_left->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(goto_right); - HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); + breturn->AddInstruction(read_bottom); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_REMOVED(read_bottom); + EXPECT_INS_REMOVED(write_right); + EXPECT_INS_RETAINED(call_left); + std::vector<HPhi*> merges; + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); + std::tie(merges) = FindAllInstructions<HPhi>(graph_, breturn); + ASSERT_EQ(merges.size(), 2u); + HPhi* merge_value_return = FindOrNull( + merges.begin(), merges.end(), [](HPhi* p) { return p->GetType() == DataType::Type::kInt32; }); + HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) { + return p->GetType() == DataType::Type::kReference; + }); + ASSERT_NE(merge_alloc, nullptr); + EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc; + EXPECT_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) << *merge_alloc << " cls? " << *cls; + EXPECT_EQ(merge_alloc->InputAt(1), null_const); + ASSERT_NE(pred_get, nullptr); + EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc); + EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return) << " pred-get is: " << *pred_get; + EXPECT_INS_EQ(merge_value_return->InputAt(0), graph_->GetIntConstant(0)) + << " merge val is: " << *merge_value_return; + EXPECT_INS_EQ(merge_value_return->InputAt(1), c2) << " merge val is: " << *merge_value_return; +} + +// // ENTRY +// obj1 = new Obj1(); +// obj2 = new Obj2(); +// obj1.field = 3; +// obj2.field = 13; +// if (parameter_value) { +// // LEFT +// escape(obj1); +// escape(obj2); +// } else { +// // RIGHT +// // ELIMINATE +// obj1.field = 2; +// obj2.field = 12; +// } +// EXIT +// predicated-ELIMINATE +// return obj1.field + obj2.field +TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad1) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"entry", "right"}, + {"left", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {left, right}); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* c2 = graph_->GetIntConstant(2); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c12 = graph_->GetIntConstant(12); + HInstruction* c13 = graph_->GetIntConstant(13); + + HInstruction* cls1 = MakeClassLoad(); + HInstruction* cls2 = MakeClassLoad(); + HInstruction* new_inst1 = MakeNewInstance(cls1); + HInstruction* new_inst2 = MakeNewInstance(cls2); + HInstruction* write_entry1 = MakeIFieldSet(new_inst1, c3, MemberOffset(32)); + HInstruction* write_entry2 = MakeIFieldSet(new_inst2, c13, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls1); + entry->AddInstruction(cls2); + entry->AddInstruction(new_inst1); + entry->AddInstruction(new_inst2); + entry->AddInstruction(write_entry1); + entry->AddInstruction(write_entry2); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls1, {}); + cls2->CopyEnvironmentFrom(cls1->GetEnvironment()); + new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); + new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment()); + + HInstruction* call_left1 = MakeInvoke(DataType::Type::kVoid, { new_inst1 }); + HInstruction* call_left2 = MakeInvoke(DataType::Type::kVoid, { new_inst2 }); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left1); + left->AddInstruction(call_left2); + left->AddInstruction(goto_left); + call_left1->CopyEnvironmentFrom(cls1->GetEnvironment()); + call_left2->CopyEnvironmentFrom(cls1->GetEnvironment()); + + HInstruction* write_right1 = MakeIFieldSet(new_inst1, c2, MemberOffset(32)); + HInstruction* write_right2 = MakeIFieldSet(new_inst2, c12, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(write_right1); + right->AddInstruction(write_right2); + right->AddInstruction(goto_right); + + HInstruction* read_bottom1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* read_bottom2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* combine = + new (GetAllocator()) HAdd(DataType::Type::kInt32, read_bottom1, read_bottom2); + HInstruction* return_exit = new (GetAllocator()) HReturn(combine); + breturn->AddInstruction(read_bottom1); + breturn->AddInstruction(read_bottom2); + breturn->AddInstruction(combine); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_REMOVED(read_bottom1); + EXPECT_INS_REMOVED(read_bottom2); + EXPECT_INS_REMOVED(write_right1); + EXPECT_INS_REMOVED(write_right2); + EXPECT_INS_RETAINED(call_left1); + EXPECT_INS_RETAINED(call_left2); + std::vector<HPhi*> merges; + std::vector<HPredicatedInstanceFieldGet*> pred_gets; + std::tie(merges, pred_gets) = + FindAllInstructions<HPhi, HPredicatedInstanceFieldGet>(graph_, breturn); + ASSERT_EQ(merges.size(), 4u); + ASSERT_EQ(pred_gets.size(), 2u); + HPhi* merge_value_return1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c2; + }); + HPhi* merge_value_return2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c12; + }); + HPhi* merge_alloc1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kReference && + p->InputAt(0)->IsNewInstance() && + p->InputAt(0)->InputAt(0) == cls1; + }); + HPhi* merge_alloc2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kReference && + p->InputAt(0)->IsNewInstance() && + p->InputAt(0)->InputAt(0) == cls2; + }); + ASSERT_NE(merge_alloc1, nullptr); + ASSERT_NE(merge_alloc2, nullptr); + EXPECT_EQ(merge_alloc1->InputAt(1), graph_->GetNullConstant()); + EXPECT_EQ(merge_alloc2->InputAt(1), graph_->GetNullConstant()); + HPredicatedInstanceFieldGet* pred_get1 = + FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) { + return pg->GetTarget() == merge_alloc1; + }); + HPredicatedInstanceFieldGet* pred_get2 = + FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) { + return pg->GetTarget() == merge_alloc2; + }); + ASSERT_NE(pred_get1, nullptr); + EXPECT_INS_EQ(pred_get1->GetTarget(), merge_alloc1); + EXPECT_INS_EQ(pred_get1->GetDefaultValue(), merge_value_return1) + << " pred-get is: " << *pred_get1; + EXPECT_INS_EQ(merge_value_return1->InputAt(0), graph_->GetIntConstant(0)) + << " merge val is: " << *merge_value_return1; + EXPECT_INS_EQ(merge_value_return1->InputAt(1), c2) << " merge val is: " << *merge_value_return1; + ASSERT_NE(pred_get2, nullptr); + EXPECT_INS_EQ(pred_get2->GetTarget(), merge_alloc2); + EXPECT_INS_EQ(pred_get2->GetDefaultValue(), merge_value_return2) + << " pred-get is: " << *pred_get2; + EXPECT_INS_EQ(merge_value_return2->InputAt(0), graph_->GetIntConstant(0)) + << " merge val is: " << *merge_value_return1; + EXPECT_INS_EQ(merge_value_return2->InputAt(1), c12) << " merge val is: " << *merge_value_return1; +} + +// // ENTRY +// obj1 = new Obj1(); +// obj2 = new Obj2(); +// obj1.field = 3; +// obj2.field = 13; +// if (parameter_value) { +// // LEFT +// escape(obj1); +// // ELIMINATE +// obj2.field = 12; +// } else { +// // RIGHT +// // ELIMINATE +// obj1.field = 2; +// escape(obj2); +// } +// EXIT +// predicated-ELIMINATE +// return obj1.field + obj2.field +TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad2) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"entry", "right"}, + {"left", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {left, right}); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* c2 = graph_->GetIntConstant(2); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c12 = graph_->GetIntConstant(12); + HInstruction* c13 = graph_->GetIntConstant(13); + + HInstruction* cls1 = MakeClassLoad(); + HInstruction* cls2 = MakeClassLoad(); + HInstruction* new_inst1 = MakeNewInstance(cls1); + HInstruction* new_inst2 = MakeNewInstance(cls2); + HInstruction* write_entry1 = MakeIFieldSet(new_inst1, c3, MemberOffset(32)); + HInstruction* write_entry2 = MakeIFieldSet(new_inst2, c13, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls1); + entry->AddInstruction(cls2); + entry->AddInstruction(new_inst1); + entry->AddInstruction(new_inst2); + entry->AddInstruction(write_entry1); + entry->AddInstruction(write_entry2); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls1, {}); + cls2->CopyEnvironmentFrom(cls1->GetEnvironment()); + new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); + new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment()); + + HInstruction* call_left1 = MakeInvoke(DataType::Type::kVoid, { new_inst1 }); + HInstruction* write_left2 = MakeIFieldSet(new_inst2, c12, MemberOffset(32)); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left1); + left->AddInstruction(write_left2); + left->AddInstruction(goto_left); + call_left1->CopyEnvironmentFrom(cls1->GetEnvironment()); + + HInstruction* write_right1 = MakeIFieldSet(new_inst1, c2, MemberOffset(32)); + HInstruction* call_right2 = MakeInvoke(DataType::Type::kVoid, { new_inst2 }); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(write_right1); + right->AddInstruction(call_right2); + right->AddInstruction(goto_right); + call_right2->CopyEnvironmentFrom(cls1->GetEnvironment()); + + HInstruction* read_bottom1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* read_bottom2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* combine = + new (GetAllocator()) HAdd(DataType::Type::kInt32, read_bottom1, read_bottom2); + HInstruction* return_exit = new (GetAllocator()) HReturn(combine); + breturn->AddInstruction(read_bottom1); + breturn->AddInstruction(read_bottom2); + breturn->AddInstruction(combine); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_REMOVED(read_bottom1); + EXPECT_INS_REMOVED(read_bottom2); + EXPECT_INS_REMOVED(write_right1); + EXPECT_INS_REMOVED(write_left2); + EXPECT_INS_RETAINED(call_left1); + EXPECT_INS_RETAINED(call_right2); + std::vector<HPhi*> merges; + std::vector<HPredicatedInstanceFieldGet*> pred_gets; + std::tie(merges, pred_gets) = + FindAllInstructions<HPhi, HPredicatedInstanceFieldGet>(graph_, breturn); + ASSERT_EQ(merges.size(), 4u); + ASSERT_EQ(pred_gets.size(), 2u); + HPhi* merge_value_return1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c2; + }); + HPhi* merge_value_return2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kInt32 && p->InputAt(0) == c12; + }); + HPhi* merge_alloc1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kReference && p->InputAt(1)->IsNullConstant(); + }); + HPhi* merge_alloc2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kReference && p->InputAt(0)->IsNullConstant(); + }); + ASSERT_NE(merge_alloc1, nullptr); + ASSERT_NE(merge_alloc2, nullptr); + EXPECT_TRUE(merge_alloc1->InputAt(0)->IsNewInstance()) << *merge_alloc1; + EXPECT_INS_EQ(merge_alloc1->InputAt(0)->InputAt(0), cls1) << *merge_alloc1; + EXPECT_INS_EQ(merge_alloc1->InputAt(1), graph_->GetNullConstant()); + EXPECT_TRUE(merge_alloc2->InputAt(1)->IsNewInstance()) << *merge_alloc2; + EXPECT_INS_EQ(merge_alloc2->InputAt(1)->InputAt(0), cls2) << *merge_alloc2; + EXPECT_INS_EQ(merge_alloc2->InputAt(0), graph_->GetNullConstant()); + HPredicatedInstanceFieldGet* pred_get1 = + FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) { + return pg->GetTarget() == merge_alloc1; + }); + HPredicatedInstanceFieldGet* pred_get2 = + FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) { + return pg->GetTarget() == merge_alloc2; + }); + ASSERT_NE(pred_get1, nullptr); + EXPECT_INS_EQ(pred_get1->GetTarget(), merge_alloc1); + EXPECT_INS_EQ(pred_get1->GetDefaultValue(), merge_value_return1) + << " pred-get is: " << *pred_get1; + EXPECT_INS_EQ(merge_value_return1->InputAt(0), graph_->GetIntConstant(0)) + << " merge val is: " << *merge_value_return1; + EXPECT_INS_EQ(merge_value_return1->InputAt(1), c2) << " merge val is: " << *merge_value_return1; + ASSERT_NE(pred_get2, nullptr); + EXPECT_INS_EQ(pred_get2->GetTarget(), merge_alloc2); + EXPECT_INS_EQ(pred_get2->GetDefaultValue(), merge_value_return2) + << " pred-get is: " << *pred_get2; + EXPECT_INS_EQ(merge_value_return2->InputAt(1), graph_->GetIntConstant(0)) + << " merge val is: " << *merge_value_return1; + EXPECT_INS_EQ(merge_value_return2->InputAt(0), c12) << " merge val is: " << *merge_value_return1; +} + +// Based on structure seen in `java.util.List +// java.util.Collections.checkedList(java.util.List, java.lang.Class)` +// Incorrect accounting would cause attempts to materialize both obj1 and obj2 +// in each of the materialization blocks. +// // ENTRY +// Obj obj; +// if (param1) { +// // needs to be moved after param2 check +// obj1 = new Obj1(); +// obj1.foo = 33; +// if (param2) { +// return obj1.foo; +// } +// obj = obj1; +// } else { +// obj2 = new Obj2(); +// obj2.foo = 44; +// if (param2) { +// return obj2.foo; +// } +// obj = obj2; +// } +// EXIT +// // obj = PHI[obj1, obj2] +// // NB The phi acts as an escape for both obj1 and obj2 meaning as far as the +// // LSA is concerned the escape frontier is left_crit_break->breturn and +// // right_crit_break->breturn for both even though only one of the objects is +// // actually live at each edge. +// // TODO In the future we really should track liveness through PHIs which would +// // allow us to entirely remove the allocation in this test. +// return obj.foo; +TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad3) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"left", "left_end"}, + {"left_end", "breturn"}, + {"left", "left_exit_early"}, + {"left_exit_early", "exit"}, + {"entry", "right"}, + {"right", "right_end"}, + {"right_end", "breturn"}, + {"right", "right_exit_early"}, + {"right_exit_early", "exit"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(left_end); + GET_BLOCK(left_exit_early); + GET_BLOCK(right); + GET_BLOCK(right_end); + GET_BLOCK(right_exit_early); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {left_end, right_end}); + HInstruction* param1 = MakeParam(DataType::Type::kBool); + HInstruction* param2 = MakeParam(DataType::Type::kBool); + HInstruction* c33 = graph_->GetIntConstant(33); + HInstruction* c44 = graph_->GetIntConstant(44); + + HInstruction* if_inst = new (GetAllocator()) HIf(param1); + entry->AddInstruction(if_inst); + + HInstruction* cls1 = MakeClassLoad(); + HInstruction* new_inst1 = MakeNewInstance(cls1); + HInstruction* write1 = MakeIFieldSet(new_inst1, c33, MemberOffset(32)); + HInstruction* if_left = new (GetAllocator()) HIf(param2); + left->AddInstruction(cls1); + left->AddInstruction(new_inst1); + left->AddInstruction(write1); + left->AddInstruction(if_left); + ManuallyBuildEnvFor(cls1, {}); + new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); + + left_end->AddInstruction(new (GetAllocator()) HGoto()); + + HInstruction* early_exit_left_read = + MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* early_exit_left_return = new (GetAllocator()) HReturn(early_exit_left_read); + left_exit_early->AddInstruction(early_exit_left_read); + left_exit_early->AddInstruction(early_exit_left_return); + + HInstruction* cls2 = MakeClassLoad(); + HInstruction* new_inst2 = MakeNewInstance(cls2); + HInstruction* write2 = MakeIFieldSet(new_inst2, c44, MemberOffset(32)); + HInstruction* if_right = new (GetAllocator()) HIf(param2); + right->AddInstruction(cls2); + right->AddInstruction(new_inst2); + right->AddInstruction(write2); + right->AddInstruction(if_right); + cls2->CopyEnvironmentFrom(cls1->GetEnvironment()); + new_inst2->CopyEnvironmentFrom(cls2->GetEnvironment()); + + right_end->AddInstruction(new (GetAllocator()) HGoto()); + + HInstruction* early_exit_right_read = + MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* early_exit_right_return = new (GetAllocator()) HReturn(early_exit_right_read); + right_exit_early->AddInstruction(early_exit_right_read); + right_exit_early->AddInstruction(early_exit_right_return); + + HPhi* bottom_phi = MakePhi({new_inst1, new_inst2}); + HInstruction* read_bottom = MakeIFieldGet(bottom_phi, DataType::Type::kInt32, MemberOffset(32)); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); + breturn->AddPhi(bottom_phi); breturn->AddInstruction(read_bottom); breturn->AddInstruction(return_exit); - HInstruction* exit_instruction = new (GetAllocator()) HExit(); - exit->AddInstruction(exit_instruction); + SetupExit(exit); + // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_REMOVED(early_exit_left_read); + EXPECT_INS_REMOVED(early_exit_right_read); + EXPECT_INS_RETAINED(bottom_phi); + EXPECT_INS_RETAINED(read_bottom); + EXPECT_INS_EQ(early_exit_left_return->InputAt(0), c33); + EXPECT_INS_EQ(early_exit_right_return->InputAt(0), c44); + // These assert there is only 1 HNewInstance in the given blocks. + HNewInstance* moved_ni1 = + FindSingleInstruction<HNewInstance>(graph_, left_end->GetSinglePredecessor()); + HNewInstance* moved_ni2 = + FindSingleInstruction<HNewInstance>(graph_, right_end->GetSinglePredecessor()); + ASSERT_NE(moved_ni1, nullptr); + ASSERT_NE(moved_ni2, nullptr); + EXPECT_INS_EQ(bottom_phi->InputAt(0), moved_ni1); + EXPECT_INS_EQ(bottom_phi->InputAt(1), moved_ni2); +} + +// Based on structure seen in `java.util.Set java.util.Collections$UnmodifiableMap.entrySet()` +// We end up having to update a PHI generated by normal LSE. +// // ENTRY +// Obj obj_init = param_obj.BAR; +// if (param1) { +// Obj other = new Obj(); +// other.foo = 42; +// if (param2) { +// return other.foo; +// } else { +// param_obj.BAR = other; +// } +// } else { } +// EXIT +// LSE Turns this into PHI[obj_init, other] +// read_bottom = param_obj.BAR; +// // won't be changed. The escape happens with .BAR set so this is in escaping cohort. +// return read_bottom.foo; +TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad4) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"left", "left_early_return"}, + {"left_early_return", "exit"}, + {"left", "left_write_escape"}, + {"left_write_escape", "breturn"}, + {"entry", "right"}, + {"right", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(left_early_return); + GET_BLOCK(left_write_escape); + GET_BLOCK(right); +#undef GET_BLOCK + MemberOffset foo_offset = MemberOffset(32); + MemberOffset bar_offset = MemberOffset(20); + EnsurePredecessorOrder(breturn, {left_write_escape, right}); + HInstruction* c42 = graph_->GetIntConstant(42); + HInstruction* param1 = MakeParam(DataType::Type::kBool); + HInstruction* param2 = MakeParam(DataType::Type::kBool); + HInstruction* param_obj = MakeParam(DataType::Type::kReference); + + HInstruction* get_initial = MakeIFieldGet(param_obj, DataType::Type::kReference, bar_offset); + HInstruction* if_inst = new (GetAllocator()) HIf(param1); + entry->AddInstruction(get_initial); + entry->AddInstruction(if_inst); + + HInstruction* cls1 = MakeClassLoad(); + HInstruction* new_inst1 = MakeNewInstance(cls1); + HInstruction* write1 = MakeIFieldSet(new_inst1, c42, foo_offset); + HInstruction* if_left = new (GetAllocator()) HIf(param2); + left->AddInstruction(cls1); + left->AddInstruction(new_inst1); + left->AddInstruction(write1); + left->AddInstruction(if_left); + ManuallyBuildEnvFor(cls1, {}); + new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); + + HInstruction* read_early_return = MakeIFieldGet(new_inst1, DataType::Type::kInt32, foo_offset); + HInstruction* return_early = new (GetAllocator()) HReturn(read_early_return); + left_early_return->AddInstruction(read_early_return); + left_early_return->AddInstruction(return_early); + + HInstruction* write_escape = MakeIFieldSet(param_obj, new_inst1, bar_offset); + HInstruction* write_goto = new (GetAllocator()) HGoto(); + left_write_escape->AddInstruction(write_escape); + left_write_escape->AddInstruction(write_goto); + + right->AddInstruction(new (GetAllocator()) HGoto()); + + HInstruction* read_bottom = MakeIFieldGet(param_obj, DataType::Type::kReference, bar_offset); + HInstruction* final_read = MakeIFieldGet(read_bottom, DataType::Type::kInt32, foo_offset); + HInstruction* return_exit = new (GetAllocator()) HReturn(final_read); + breturn->AddInstruction(read_bottom); + breturn->AddInstruction(final_read); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_REMOVED(read_bottom); + EXPECT_INS_REMOVED(read_early_return); + EXPECT_INS_EQ(return_early->InputAt(0), c42); + EXPECT_INS_RETAINED(final_read); + HNewInstance* moved_ni = + FindSingleInstruction<HNewInstance>(graph_, left_write_escape->GetSinglePredecessor()); + EXPECT_TRUE(final_read->InputAt(0)->IsPhi()); + EXPECT_INS_EQ(final_read->InputAt(0)->InputAt(0), moved_ni); + EXPECT_INS_EQ(final_read->InputAt(0)->InputAt(1), get_initial); +} + +// // ENTRY +// obj = new Obj(); +// obj.field = 3; +// if (parameter_value) { +// // LEFT +// escape(obj); +// } else { +// // RIGHT +// // ELIMINATE +// obj.field = 2; +// } +// // MERGE +// if (second_param) { +// // NON_ESCAPE +// obj.field = 1; +// noescape(); +// } +// EXIT +// predicated-ELIMINATE +// return obj.field +TEST_F(LoadStoreEliminationTest, PredicatedLoad2) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"entry", "right"}, + {"left", "merge"}, + {"right", "merge"}, + {"merge", "non_escape"}, + {"non_escape", "breturn"}, + {"merge", "crit_break"}, + {"crit_break", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); + GET_BLOCK(merge); + GET_BLOCK(non_escape); + GET_BLOCK(crit_break); +#undef GET_BLOCK + EnsurePredecessorOrder(merge, {left, right}); + EnsurePredecessorOrder(breturn, {crit_break, non_escape}); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value2 = MakeParam(DataType::Type::kBool); + HInstruction* null_const = graph_->GetNullConstant(); + HInstruction* c1 = graph_->GetIntConstant(1); + HInstruction* c2 = graph_->GetIntConstant(2); + HInstruction* c3 = graph_->GetIntConstant(3); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(write_entry); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left); + left->AddInstruction(goto_left); + call_left->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(write_right); + right->AddInstruction(goto_right); + + HInstruction* merge_if = new (GetAllocator()) HIf(bool_value2); + merge->AddInstruction(merge_if); + + crit_break->AddInstruction(new (GetAllocator()) HGoto()); + + HInstruction* write_non_escape = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* non_escape_call = MakeInvoke(DataType::Type::kVoid, {}); + HInstruction* non_escape_goto = new (GetAllocator()) HGoto(); + non_escape->AddInstruction(write_non_escape); + non_escape->AddInstruction(non_escape_call); + non_escape->AddInstruction(non_escape_goto); + non_escape_call->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); + breturn->AddInstruction(read_bottom); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_REMOVED(read_bottom); + EXPECT_INS_REMOVED(write_right); + EXPECT_INS_RETAINED(call_left); + std::vector<HPhi*> merges; + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); + std::tie(merges) = FindAllInstructions<HPhi>(graph_); + ASSERT_EQ(merges.size(), 3u); + HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn; + }); + HPhi* merge_value_merge = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kInt32 && p->GetBlock() != breturn; + }); + HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) { + return p->GetType() == DataType::Type::kReference; + }); + ASSERT_NE(merge_alloc, nullptr); + EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc; + EXPECT_INS_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) + << " phi is: " << merge_alloc->DumpWithArgs(); + EXPECT_INS_EQ(merge_alloc->InputAt(1), null_const); + ASSERT_NE(pred_get, nullptr); + EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc); + EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return) + << "get is " << pred_get->DumpWithArgs(); + EXPECT_INS_EQ(merge_value_return->InputAt(0), merge_value_merge) + << " phi is: " << *merge_value_return; + EXPECT_INS_EQ(merge_value_return->InputAt(1), c1) + << " phi is: " << merge_value_return->DumpWithArgs(); + EXPECT_INS_EQ(merge_value_merge->InputAt(0), graph_->GetIntConstant(0)) + << " phi is: " << *merge_value_merge; + EXPECT_INS_EQ(merge_value_merge->InputAt(1), c2) + << " phi is: " << merge_value_merge->DumpWithArgs(); +} + +// // ENTRY +// obj = new Obj(); +// obj.field = 3; +// if (parameter_value) { +// // LEFT +// escape(obj); +// } else { +// // RIGHT +// // ELIMINATE +// obj.field = 2; +// } +// // MERGE +// if (second_param) { +// // NON_ESCAPE +// obj.field = 1; +// } +// noescape(); +// EXIT +// predicated-ELIMINATE +// return obj.field +TEST_F(LoadStoreEliminationTest, PredicatedLoad3) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"entry", "right"}, + {"left", "merge"}, + {"right", "merge"}, + {"merge", "non_escape"}, + {"non_escape", "breturn"}, + {"merge", "crit_break"}, + {"crit_break", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); + GET_BLOCK(merge); + GET_BLOCK(crit_break); + GET_BLOCK(non_escape); +#undef GET_BLOCK + EnsurePredecessorOrder(merge, {left, right}); + EnsurePredecessorOrder(breturn, {crit_break, non_escape}); + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value2 = MakeParam(DataType::Type::kBool); + HInstruction* null_const = graph_->GetNullConstant(); + HInstruction* c1 = graph_->GetIntConstant(1); + HInstruction* c2 = graph_->GetIntConstant(2); + HInstruction* c3 = graph_->GetIntConstant(3); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(write_entry); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left); + left->AddInstruction(goto_left); + call_left->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(write_right); + right->AddInstruction(goto_right); + + HInstruction* merge_if = new (GetAllocator()) HIf(bool_value2); + merge->AddInstruction(merge_if); + + HInstruction* write_non_escape = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* non_escape_goto = new (GetAllocator()) HGoto(); + non_escape->AddInstruction(write_non_escape); + non_escape->AddInstruction(non_escape_goto); + + crit_break->AddInstruction(new (GetAllocator()) HGoto()); + + HInstruction* bottom_call = MakeInvoke(DataType::Type::kVoid, {}); + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); + breturn->AddInstruction(bottom_call); + breturn->AddInstruction(read_bottom); + breturn->AddInstruction(return_exit); + bottom_call->CopyEnvironmentFrom(cls->GetEnvironment()); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_REMOVED(read_bottom); + EXPECT_INS_REMOVED(write_right); + EXPECT_INS_RETAINED(call_left); + std::vector<HPhi*> merges; + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); + std::tie(merges) = FindAllInstructions<HPhi>(graph_); + ASSERT_EQ(merges.size(), 3u); + HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn; + }); + HPhi* merge_value_merge = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { + return p->GetType() == DataType::Type::kInt32 && p->GetBlock() != breturn; + }); + HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) { + return p->GetType() == DataType::Type::kReference; + }); + ASSERT_NE(merge_alloc, nullptr); + EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << merge_alloc->DumpWithArgs(); + EXPECT_INS_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) + << " phi is: " << merge_alloc->DumpWithArgs(); + EXPECT_INS_EQ(merge_alloc->InputAt(1), null_const); + ASSERT_NE(pred_get, nullptr); + EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc); + EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return) + << "get is " << pred_get->DumpWithArgs(); + EXPECT_INS_EQ(merge_value_return->InputAt(0), merge_value_merge) + << " phi is: " << *merge_value_return; + EXPECT_INS_EQ(merge_value_return->InputAt(1), c1) << " phi is: " << *merge_value_return; + EXPECT_INS_EQ(merge_value_merge->InputAt(0), graph_->GetIntConstant(0)) + << " phi is: " << *merge_value_merge; + EXPECT_INS_EQ(merge_value_merge->InputAt(1), c2) << " phi is: " << *merge_value_merge; +} + +// // ENTRY +// obj = new Obj(); +// // ALL should be kept +// switch (parameter_value) { +// case 1: +// // Case1 +// obj.field = 1; +// call_func(obj); +// break; +// case 2: +// // Case2 +// obj.field = 2; +// call_func(obj); +// break; +// default: +// // Case3 +// obj.field = 3; +// do { +// if (test2()) { } else { obj.field = 5; } +// } while (test()); +// break; +// } +// EXIT +// // predicated-ELIMINATE +// return obj.field +TEST_F(LoadStoreEliminationTest, PartialLoopPhis1) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "bswitch"}, + {"bswitch", "case1"}, + {"bswitch", "case2"}, + {"bswitch", "case3"}, + {"case1", "breturn"}, + {"case2", "breturn"}, + {"case3", "loop_pre_header"}, + {"loop_pre_header", "loop_header"}, + {"loop_header", "loop_body"}, + {"loop_body", "loop_if_left"}, + {"loop_body", "loop_if_right"}, + {"loop_if_left", "loop_merge"}, + {"loop_if_right", "loop_merge"}, + {"loop_merge", "loop_end"}, + {"loop_end", "loop_header"}, + {"loop_end", "critical_break"}, + {"critical_break", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(bswitch); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(case1); + GET_BLOCK(case2); + GET_BLOCK(case3); + + GET_BLOCK(loop_pre_header); + GET_BLOCK(loop_header); + GET_BLOCK(loop_body); + GET_BLOCK(loop_if_left); + GET_BLOCK(loop_if_right); + GET_BLOCK(loop_merge); + GET_BLOCK(loop_end); + GET_BLOCK(critical_break); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {case1, case2, critical_break}); + EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_end}); + EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right}); + CHECK_SUBROUTINE_FAILURE(); + HInstruction* switch_val = MakeParam(DataType::Type::kInt32); + HInstruction* c1 = graph_->GetIntConstant(1); + HInstruction* c2 = graph_->GetIntConstant(2); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c5 = graph_->GetIntConstant(5); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* entry_goto = new (GetAllocator()) HGoto(); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(entry_goto); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, switch_val); + bswitch->AddInstruction(switch_inst); + + HInstruction* write_c1 = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* call_c1 = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_c1 = new (GetAllocator()) HGoto(); + case1->AddInstruction(write_c1); + case1->AddInstruction(call_c1); + case1->AddInstruction(goto_c1); + call_c1->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_c2 = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* call_c2 = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_c2 = new (GetAllocator()) HGoto(); + case2->AddInstruction(write_c2); + case2->AddInstruction(call_c2); + case2->AddInstruction(goto_c2); + call_c2->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_c3 = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* goto_c3 = new (GetAllocator()) HGoto(); + case3->AddInstruction(write_c3); + case3->AddInstruction(goto_c3); + + HInstruction* goto_preheader = new (GetAllocator()) HGoto(); + loop_pre_header->AddInstruction(goto_preheader); + + HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck(); + HInstruction* goto_header = new (GetAllocator()) HGoto(); + loop_header->AddInstruction(suspend_check_header); + loop_header->AddInstruction(goto_header); + suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {}); + HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body); + loop_body->AddInstruction(call_loop_body); + loop_body->AddInstruction(if_loop_body); + call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* goto_loop_left = new (GetAllocator()) HGoto(); + loop_if_left->AddInstruction(goto_loop_left); + + HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32)); + HInstruction* goto_loop_right = new (GetAllocator()) HGoto(); + loop_if_right->AddInstruction(write_loop_right); + loop_if_right->AddInstruction(goto_loop_right); + + HInstruction* goto_loop_merge = new (GetAllocator()) HGoto(); + loop_merge->AddInstruction(goto_loop_merge); + + HInstruction* call_end = MakeInvoke(DataType::Type::kBool, {}); + HInstruction* if_end = new (GetAllocator()) HIf(call_end); + loop_end->AddInstruction(call_end); + loop_end->AddInstruction(if_end); + call_end->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* goto_critical_break = new (GetAllocator()) HGoto(); + critical_break->AddInstruction(goto_critical_break); + + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); + breturn->AddInstruction(read_bottom); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); + EXPECT_INS_REMOVED(read_bottom) << *read_bottom; + ASSERT_TRUE(pred_get != nullptr); + HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi(); + ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs(); + EXPECT_INS_EQ(inst_return_phi->InputAt(0), + FindSingleInstruction<HNewInstance>(graph_, case1->GetSinglePredecessor())); + EXPECT_INS_EQ(inst_return_phi->InputAt(1), + FindSingleInstruction<HNewInstance>(graph_, case2->GetSinglePredecessor())); + EXPECT_INS_EQ(inst_return_phi->InputAt(2), graph_->GetNullConstant()); + HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi(); + ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs(); + EXPECT_INS_EQ(inst_value_phi->InputAt(0), graph_->GetIntConstant(0)); + EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0)); + HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge); + ASSERT_TRUE(loop_merge_phi != nullptr); + HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header); + ASSERT_TRUE(loop_header_phi != nullptr); + EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3); + EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi); + EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi); + EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5); + EXPECT_INS_EQ(inst_value_phi->InputAt(2), loop_merge_phi); + EXPECT_INS_RETAINED(write_c1) << *write_c1; + EXPECT_INS_RETAINED(write_c2) << *write_c2; + EXPECT_INS_REMOVED(write_c3) << *write_c3; + EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right; +} + +// // ENTRY +// obj = new Obj(); +// switch (parameter_value) { +// case 1: +// // Case1 +// obj.field = 1; +// call_func(obj); +// break; +// case 2: +// // Case2 +// obj.field = 2; +// call_func(obj); +// break; +// default: +// // Case3 +// obj.field = 3; +// while (!test()) { +// if (test2()) { } else { obj.field = 5; } +// } +// break; +// } +// EXIT +// // predicated-ELIMINATE +// return obj.field +TEST_F(LoadStoreEliminationTest, PartialLoopPhis2) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "bswitch"}, + {"bswitch", "case1"}, + {"bswitch", "case2"}, + {"bswitch", "case3"}, + {"case1", "breturn"}, + {"case2", "breturn"}, + {"case3", "loop_pre_header"}, + + {"loop_pre_header", "loop_header"}, + {"loop_header", "critical_break"}, + {"loop_header", "loop_body"}, + {"loop_body", "loop_if_left"}, + {"loop_body", "loop_if_right"}, + {"loop_if_left", "loop_merge"}, + {"loop_if_right", "loop_merge"}, + {"loop_merge", "loop_header"}, + + {"critical_break", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(bswitch); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(case1); + GET_BLOCK(case2); + GET_BLOCK(case3); + + GET_BLOCK(loop_pre_header); + GET_BLOCK(loop_header); + GET_BLOCK(loop_body); + GET_BLOCK(loop_if_left); + GET_BLOCK(loop_if_right); + GET_BLOCK(loop_merge); + GET_BLOCK(critical_break); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {case1, case2, critical_break}); + EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge}); + EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right}); + CHECK_SUBROUTINE_FAILURE(); + HInstruction* switch_val = MakeParam(DataType::Type::kInt32); + HInstruction* c1 = graph_->GetIntConstant(1); + HInstruction* c2 = graph_->GetIntConstant(2); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c5 = graph_->GetIntConstant(5); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* entry_goto = new (GetAllocator()) HGoto(); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(entry_goto); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, switch_val); + bswitch->AddInstruction(switch_inst); + + HInstruction* write_c1 = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* call_c1 = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_c1 = new (GetAllocator()) HGoto(); + case1->AddInstruction(write_c1); + case1->AddInstruction(call_c1); + case1->AddInstruction(goto_c1); + call_c1->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_c2 = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* call_c2 = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_c2 = new (GetAllocator()) HGoto(); + case2->AddInstruction(write_c2); + case2->AddInstruction(call_c2); + case2->AddInstruction(goto_c2); + call_c2->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_c3 = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* goto_c3 = new (GetAllocator()) HGoto(); + case3->AddInstruction(write_c3); + case3->AddInstruction(goto_c3); + + HInstruction* goto_preheader = new (GetAllocator()) HGoto(); + loop_pre_header->AddInstruction(goto_preheader); + + HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck(); + HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {}); + HInstruction* if_header = new (GetAllocator()) HIf(call_header); + loop_header->AddInstruction(suspend_check_header); + loop_header->AddInstruction(call_header); + loop_header->AddInstruction(if_header); + call_header->CopyEnvironmentFrom(cls->GetEnvironment()); + suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {}); + HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body); + loop_body->AddInstruction(call_loop_body); + loop_body->AddInstruction(if_loop_body); + call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* goto_loop_left = new (GetAllocator()) HGoto(); + loop_if_left->AddInstruction(goto_loop_left); + + HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32)); + HInstruction* goto_loop_right = new (GetAllocator()) HGoto(); + loop_if_right->AddInstruction(write_loop_right); + loop_if_right->AddInstruction(goto_loop_right); + + HInstruction* goto_loop_merge = new (GetAllocator()) HGoto(); + loop_merge->AddInstruction(goto_loop_merge); + + HInstruction* goto_critical_break = new (GetAllocator()) HGoto(); + critical_break->AddInstruction(goto_critical_break); + + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); + breturn->AddInstruction(read_bottom); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); + EXPECT_INS_REMOVED(read_bottom) << *read_bottom; + ASSERT_TRUE(pred_get != nullptr); + HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi(); + ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs(); + EXPECT_INS_EQ(inst_return_phi->InputAt(0), + FindSingleInstruction<HNewInstance>(graph_, case1->GetSinglePredecessor())); + EXPECT_INS_EQ(inst_return_phi->InputAt(1), + FindSingleInstruction<HNewInstance>(graph_, case2->GetSinglePredecessor())); + EXPECT_INS_EQ(inst_return_phi->InputAt(2), graph_->GetNullConstant()); + HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi(); + ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs(); + EXPECT_INS_EQ(inst_value_phi->InputAt(0), graph_->GetIntConstant(0)); + EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0)); + HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge); + ASSERT_TRUE(loop_merge_phi != nullptr); + HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header); + ASSERT_TRUE(loop_header_phi != nullptr); + EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3); + EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi); + EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi); + EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5); + EXPECT_INS_EQ(inst_value_phi->InputAt(2), loop_header_phi); + EXPECT_INS_RETAINED(write_c1) << *write_c1; + EXPECT_INS_RETAINED(write_c2) << *write_c2; + EXPECT_INS_REMOVED(write_c3) << *write_c3; + EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right; +} + +// // ENTRY +// obj = new Obj(); +// obj.field = 3; +// while (!test()) { +// if (test2()) { } else { obj.field = 5; } +// } +// if (parameter_value) { +// escape(obj); +// } +// EXIT +// return obj.field +TEST_F(LoadStoreEliminationTest, PartialLoopPhis3) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "loop_pre_header"}, + + {"loop_pre_header", "loop_header"}, + {"loop_header", "escape_check"}, + {"loop_header", "loop_body"}, + {"loop_body", "loop_if_left"}, + {"loop_body", "loop_if_right"}, + {"loop_if_left", "loop_merge"}, + {"loop_if_right", "loop_merge"}, + {"loop_merge", "loop_header"}, + + {"escape_check", "escape"}, + {"escape_check", "no_escape"}, + {"no_escape", "breturn"}, + {"escape", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(no_escape); + GET_BLOCK(escape); + GET_BLOCK(escape_check); + + GET_BLOCK(loop_pre_header); + GET_BLOCK(loop_header); + GET_BLOCK(loop_body); + GET_BLOCK(loop_if_left); + GET_BLOCK(loop_if_right); + GET_BLOCK(loop_merge); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {no_escape, escape}); + EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge}); + EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right}); + CHECK_SUBROUTINE_FAILURE(); + HInstruction* bool_val = MakeParam(DataType::Type::kBool); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c5 = graph_->GetIntConstant(5); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* entry_goto = new (GetAllocator()) HGoto(); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(entry_goto); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_pre_header = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* goto_preheader = new (GetAllocator()) HGoto(); + loop_pre_header->AddInstruction(write_pre_header); + loop_pre_header->AddInstruction(goto_preheader); + + HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck(); + HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {}); + HInstruction* if_header = new (GetAllocator()) HIf(call_header); + loop_header->AddInstruction(suspend_check_header); + loop_header->AddInstruction(call_header); + loop_header->AddInstruction(if_header); + call_header->CopyEnvironmentFrom(cls->GetEnvironment()); + suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {}); + HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body); + loop_body->AddInstruction(call_loop_body); + loop_body->AddInstruction(if_loop_body); + call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* goto_loop_left = new (GetAllocator()) HGoto(); + loop_if_left->AddInstruction(goto_loop_left); + + HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32)); + HInstruction* goto_loop_right = new (GetAllocator()) HGoto(); + loop_if_right->AddInstruction(write_loop_right); + loop_if_right->AddInstruction(goto_loop_right); + + HInstruction* goto_loop_merge = new (GetAllocator()) HGoto(); + loop_merge->AddInstruction(goto_loop_merge); + + HInstruction* if_esc_check = new (GetAllocator()) HIf(bool_val); + escape_check->AddInstruction(if_esc_check); + + HInstruction* call_escape = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_escape = new (GetAllocator()) HGoto(); + escape->AddInstruction(call_escape); + escape->AddInstruction(goto_escape); + call_escape->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* goto_no_escape = new (GetAllocator()) HGoto(); + no_escape->AddInstruction(goto_no_escape); + + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); + breturn->AddInstruction(read_bottom); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); + EXPECT_INS_REMOVED(read_bottom) << *read_bottom; + ASSERT_TRUE(pred_get != nullptr); + HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi(); + ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs(); + EXPECT_INS_EQ(inst_return_phi->InputAt(0), graph_->GetNullConstant()); + EXPECT_INS_EQ(inst_return_phi->InputAt(1), + FindSingleInstruction<HNewInstance>(graph_, escape->GetSinglePredecessor())); + HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi(); + ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs(); + HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header); + HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge); + EXPECT_INS_EQ(inst_value_phi->InputAt(0), loop_header_phi); + EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0)); + EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3); + EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi); + EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi); + EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5); + HInstanceFieldSet* mat_set = + FindSingleInstruction<HInstanceFieldSet>(graph_, escape->GetSinglePredecessor()); + ASSERT_NE(mat_set, nullptr); + EXPECT_INS_EQ(mat_set->InputAt(1), loop_header_phi); + EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right; + EXPECT_INS_REMOVED(write_pre_header) << *write_pre_header; +} + +// // ENTRY +// obj = new Obj(); +// if (parameter_value) { +// escape(obj); +// } +// obj.field = 3; +// while (!test()) { +// if (test2()) { } else { obj.field = 5; } +// } +// EXIT +// return obj.field +TEST_F(LoadStoreEliminationTest, PartialLoopPhis4) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "escape_check"}, + {"escape_check", "escape"}, + {"escape_check", "no_escape"}, + {"no_escape", "loop_pre_header"}, + {"escape", "loop_pre_header"}, + + {"loop_pre_header", "loop_header"}, + {"loop_header", "breturn"}, + {"loop_header", "loop_body"}, + {"loop_body", "loop_if_left"}, + {"loop_body", "loop_if_right"}, + {"loop_if_left", "loop_merge"}, + {"loop_if_right", "loop_merge"}, + {"loop_merge", "loop_header"}, + + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(no_escape); + GET_BLOCK(escape); + GET_BLOCK(escape_check); + + GET_BLOCK(loop_pre_header); + GET_BLOCK(loop_header); + GET_BLOCK(loop_body); + GET_BLOCK(loop_if_left); + GET_BLOCK(loop_if_right); + GET_BLOCK(loop_merge); +#undef GET_BLOCK + EnsurePredecessorOrder(loop_pre_header, {no_escape, escape}); + EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge}); + EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right}); + CHECK_SUBROUTINE_FAILURE(); + HInstruction* bool_val = MakeParam(DataType::Type::kBool); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c5 = graph_->GetIntConstant(5); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* entry_goto = new (GetAllocator()) HGoto(); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(entry_goto); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* if_esc_check = new (GetAllocator()) HIf(bool_val); + escape_check->AddInstruction(if_esc_check); + + HInstruction* call_escape = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_escape = new (GetAllocator()) HGoto(); + escape->AddInstruction(call_escape); + escape->AddInstruction(goto_escape); + call_escape->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* goto_no_escape = new (GetAllocator()) HGoto(); + no_escape->AddInstruction(goto_no_escape); + + HInstruction* write_pre_header = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* goto_preheader = new (GetAllocator()) HGoto(); + loop_pre_header->AddInstruction(write_pre_header); + loop_pre_header->AddInstruction(goto_preheader); + + HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck(); + HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {}); + HInstruction* if_header = new (GetAllocator()) HIf(call_header); + loop_header->AddInstruction(suspend_check_header); + loop_header->AddInstruction(call_header); + loop_header->AddInstruction(if_header); + call_header->CopyEnvironmentFrom(cls->GetEnvironment()); + suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {}); + HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body); + loop_body->AddInstruction(call_loop_body); + loop_body->AddInstruction(if_loop_body); + call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* goto_loop_left = new (GetAllocator()) HGoto(); + loop_if_left->AddInstruction(goto_loop_left); + + HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32)); + HInstruction* goto_loop_right = new (GetAllocator()) HGoto(); + loop_if_right->AddInstruction(write_loop_right); + loop_if_right->AddInstruction(goto_loop_right); + + HInstruction* goto_loop_merge = new (GetAllocator()) HGoto(); + loop_merge->AddInstruction(goto_loop_merge); + + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); + breturn->AddInstruction(read_bottom); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); + EXPECT_INS_REMOVED(read_bottom) << *read_bottom; + ASSERT_TRUE(pred_get != nullptr); + HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi(); + ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs(); + EXPECT_INS_EQ(inst_return_phi->InputAt(0), graph_->GetNullConstant()); + EXPECT_INS_EQ(inst_return_phi->InputAt(1), + FindSingleInstruction<HNewInstance>(graph_, escape->GetSinglePredecessor())); + HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi(); + ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs(); + HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header); + HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge); + EXPECT_INS_EQ(inst_value_phi, loop_header_phi); + EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3); + EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi); + EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi); + EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5); + EXPECT_INS_RETAINED(write_loop_right) << *write_loop_right; + EXPECT_TRUE(write_loop_right->AsInstanceFieldSet()->GetIsPredicatedSet()) << *write_loop_right; + EXPECT_INS_RETAINED(write_pre_header) << *write_pre_header; + EXPECT_TRUE(write_pre_header->AsInstanceFieldSet()->GetIsPredicatedSet()) << *write_pre_header; +} + +// // ENTRY +// obj = new Obj(); +// obj.field = 3; +// while (!test()) { +// if (test2()) { } else { obj.field += 5; } +// } +// if (parameter_value) { +// escape(obj); +// } +// EXIT +// return obj.field +TEST_F(LoadStoreEliminationTest, PartialLoopPhis5) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(/*handles=*/&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "loop_pre_header"}, + {"loop_pre_header", "loop_header"}, + {"loop_header", "escape_check"}, + {"loop_header", "loop_body"}, + {"loop_body", "loop_if_left"}, + {"loop_body", "loop_if_right"}, + {"loop_if_left", "loop_merge"}, + {"loop_if_right", "loop_merge"}, + {"loop_merge", "loop_header"}, + {"escape_check", "escape"}, + {"escape_check", "no_escape"}, + {"no_escape", "breturn"}, + {"escape", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(no_escape); + GET_BLOCK(escape); + GET_BLOCK(escape_check); + + GET_BLOCK(loop_pre_header); + GET_BLOCK(loop_header); + GET_BLOCK(loop_body); + GET_BLOCK(loop_if_left); + GET_BLOCK(loop_if_right); + GET_BLOCK(loop_merge); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {no_escape, escape}); + EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge}); + EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right}); + CHECK_SUBROUTINE_FAILURE(); + HInstruction* bool_val = MakeParam(DataType::Type::kBool); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c5 = graph_->GetIntConstant(5); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* entry_goto = new (GetAllocator()) HGoto(); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(entry_goto); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_pre_header = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* goto_preheader = new (GetAllocator()) HGoto(); + loop_pre_header->AddInstruction(write_pre_header); + loop_pre_header->AddInstruction(goto_preheader); + + HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck(); + HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {}); + HInstruction* if_header = new (GetAllocator()) HIf(call_header); + loop_header->AddInstruction(suspend_check_header); + loop_header->AddInstruction(call_header); + loop_header->AddInstruction(if_header); + call_header->CopyEnvironmentFrom(cls->GetEnvironment()); + suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {}); + HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body); + loop_body->AddInstruction(call_loop_body); + loop_body->AddInstruction(if_loop_body); + call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* goto_loop_left = new (GetAllocator()) HGoto(); + loop_if_left->AddInstruction(goto_loop_left); + + HInstruction* read_loop_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* add_loop_right = + new (GetAllocator()) HAdd(DataType::Type::kInt32, read_loop_right, c5); + HInstruction* write_loop_right = MakeIFieldSet(new_inst, add_loop_right, MemberOffset(32)); + HInstruction* goto_loop_right = new (GetAllocator()) HGoto(); + loop_if_right->AddInstruction(read_loop_right); + loop_if_right->AddInstruction(add_loop_right); + loop_if_right->AddInstruction(write_loop_right); + loop_if_right->AddInstruction(goto_loop_right); + + HInstruction* goto_loop_merge = new (GetAllocator()) HGoto(); + loop_merge->AddInstruction(goto_loop_merge); + + HInstruction* if_esc_check = new (GetAllocator()) HIf(bool_val); + escape_check->AddInstruction(if_esc_check); + + HInstruction* call_escape = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_escape = new (GetAllocator()) HGoto(); + escape->AddInstruction(call_escape); + escape->AddInstruction(goto_escape); + call_escape->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* goto_no_escape = new (GetAllocator()) HGoto(); + no_escape->AddInstruction(goto_no_escape); + + HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); + breturn->AddInstruction(read_bottom); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSEWithPartial(); + LOG(INFO) << "Post LSE " << blks; + + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); + EXPECT_INS_REMOVED(read_bottom) << *read_bottom; + ASSERT_TRUE(pred_get != nullptr); + HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi(); + ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs(); + EXPECT_INS_EQ(inst_return_phi->InputAt(0), graph_->GetNullConstant()); + EXPECT_INS_EQ(inst_return_phi->InputAt(1), + FindSingleInstruction<HNewInstance>(graph_, escape->GetSinglePredecessor())); + HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi(); + ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs(); + HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header); + HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge); + EXPECT_INS_EQ(inst_value_phi->InputAt(0), loop_header_phi); + EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0)); + EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3); + EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi); + EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi); + EXPECT_INS_EQ(loop_merge_phi->InputAt(1), add_loop_right); + EXPECT_INS_EQ(add_loop_right->InputAt(0), loop_header_phi); + EXPECT_INS_EQ(add_loop_right->InputAt(1), c5); + HInstanceFieldSet* mat_set = + FindSingleInstruction<HInstanceFieldSet>(graph_, escape->GetSinglePredecessor()); + ASSERT_NE(mat_set, nullptr); + EXPECT_INS_EQ(mat_set->InputAt(1), loop_header_phi); + EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right; + EXPECT_INS_REMOVED(write_pre_header) << *write_pre_header; +} + +// TODO This should really be in an Instruction simplifier Gtest but (1) that +// doesn't exist and (2) we should move this simplification to directly in the +// LSE pass since there is more information then. +// // ENTRY +// obj = new Obj(); +// obj.field = 3; +// if (param) { +// escape(obj); +// } else { +// obj.field = 10; +// } +// return obj.field; +TEST_F(LoadStoreEliminationTest, SimplifyTest) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"entry", "right"}, + {"left", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {left, right}); + + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c10 = graph_->GetIntConstant(10); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(write_start); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left); + left->AddInstruction(goto_left); + call_left->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_right = MakeIFieldSet(new_inst, c10, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(write_right); + right->AddInstruction(goto_right); + + HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_end); + breturn->AddInstruction(read_end); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; PerformLSE(); - EXPECT_TRUE(IsRemoved(read_bottom)); - EXPECT_TRUE(IsRemoved(write_right)); - EXPECT_FALSE(IsRemoved(write_entry)); - EXPECT_FALSE(IsRemoved(write_left)); - EXPECT_FALSE(IsRemoved(call_left)); - EXPECT_FALSE(IsRemoved(call_entry)); + // Run the code-simplifier too + LOG(INFO) << "Pre simplification " << blks; + InstructionSimplifier simp(graph_, /*codegen=*/nullptr); + simp.Run(); + + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_REMOVED(write_right); + EXPECT_INS_REMOVED(write_start); + EXPECT_INS_REMOVED(read_end); + EXPECT_INS_RETAINED(call_left); + + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); + ASSERT_NE(pred_get, nullptr); + EXPECT_INS_EQ(pred_get->GetDefaultValue(), c10); +} + + +// TODO This should really be in an Instruction simplifier Gtest but (1) that +// doesn't exist and (2) we should move this simplification to directly in the +// LSE pass since there is more information then. +// +// This checks that we don't replace phis when the replacement isn't valid at +// that point (i.e. it doesn't dominate) +// // ENTRY +// obj = new Obj(); +// obj.field = 3; +// if (param) { +// escape(obj); +// } else { +// obj.field = noescape(); +// } +// return obj.field; +TEST_F(LoadStoreEliminationTest, SimplifyTest2) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "left"}, + {"entry", "right"}, + {"left", "breturn"}, + {"right", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {left, right}); + + HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* c3 = graph_->GetIntConstant(3); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(write_start); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, {new_inst}); + HInstruction* goto_left = new (GetAllocator()) HGoto(); + left->AddInstruction(call_left); + left->AddInstruction(goto_left); + call_left->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_right = MakeInvoke(DataType::Type::kInt32, {}); + HInstruction* write_right = MakeIFieldSet(new_inst, call_right, MemberOffset(32)); + HInstruction* goto_right = new (GetAllocator()) HGoto(); + right->AddInstruction(call_right); + right->AddInstruction(write_right); + right->AddInstruction(goto_right); + call_right->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_end); + breturn->AddInstruction(read_end); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSE(); + + // Run the code-simplifier too + LOG(INFO) << "Pre simplification " << blks; + InstructionSimplifier simp(graph_, /*codegen=*/nullptr); + simp.Run(); + + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_REMOVED(write_right); + EXPECT_INS_REMOVED(write_start); + EXPECT_INS_REMOVED(read_end); + EXPECT_INS_RETAINED(call_left); + EXPECT_INS_RETAINED(call_right); + EXPECT_EQ(call_right->GetBlock(), right); + + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); + ASSERT_NE(pred_get, nullptr); + EXPECT_TRUE(pred_get->GetDefaultValue()->IsPhi()) << pred_get->DumpWithArgs(); + EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), graph_->GetIntConstant(0)) + << pred_get->DumpWithArgs(); + EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), call_right) << pred_get->DumpWithArgs(); +} + +// TODO This should really be in an Instruction simplifier Gtest but (1) that +// doesn't exist and (2) we should move this simplification to directly in the +// LSE pass since there is more information then. +// +// This checks that we replace phis even when there are multiple replacements as +// long as they are equal +// // ENTRY +// obj = new Obj(); +// obj.field = 3; +// switch (param) { +// case 1: +// escape(obj); +// break; +// case 2: +// obj.field = 10; +// break; +// case 3: +// obj.field = 10; +// break; +// } +// return obj.field; +TEST_F(LoadStoreEliminationTest, SimplifyTest3) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "case1"}, + {"entry", "case2"}, + {"entry", "case3"}, + {"case1", "breturn"}, + {"case2", "breturn"}, + {"case3", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(case1); + GET_BLOCK(case2); + GET_BLOCK(case3); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {case1, case2, case3}); + + HInstruction* int_val = MakeParam(DataType::Type::kInt32); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c10 = graph_->GetIntConstant(10); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(write_start); + entry->AddInstruction(switch_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_case1 = MakeInvoke(DataType::Type::kVoid, {new_inst}); + HInstruction* goto_case1 = new (GetAllocator()) HGoto(); + case1->AddInstruction(call_case1); + case1->AddInstruction(goto_case1); + call_case1->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_case2 = MakeIFieldSet(new_inst, c10, MemberOffset(32)); + HInstruction* goto_case2 = new (GetAllocator()) HGoto(); + case2->AddInstruction(write_case2); + case2->AddInstruction(goto_case2); + + HInstruction* write_case3 = MakeIFieldSet(new_inst, c10, MemberOffset(32)); + HInstruction* goto_case3 = new (GetAllocator()) HGoto(); + case3->AddInstruction(write_case3); + case3->AddInstruction(goto_case3); + + HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_end); + breturn->AddInstruction(read_end); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSE(); + + // Run the code-simplifier too + LOG(INFO) << "Pre simplification " << blks; + InstructionSimplifier simp(graph_, /*codegen=*/nullptr); + simp.Run(); + + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_REMOVED(write_case2); + EXPECT_INS_REMOVED(write_case3); + EXPECT_INS_REMOVED(write_start); + EXPECT_INS_REMOVED(read_end); + EXPECT_INS_RETAINED(call_case1); + + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); + ASSERT_NE(pred_get, nullptr); + EXPECT_INS_EQ(pred_get->GetDefaultValue(), c10) + << pred_get->DumpWithArgs(); +} + +// TODO This should really be in an Instruction simplifier Gtest but (1) that +// doesn't exist and (2) we should move this simplification to directly in the +// LSE pass since there is more information then. +// +// This checks that we don't replace phis even when there are multiple +// replacements if they are not equal +// // ENTRY +// obj = new Obj(); +// obj.field = 3; +// switch (param) { +// case 1: +// escape(obj); +// break; +// case 2: +// obj.field = 10; +// break; +// case 3: +// obj.field = 20; +// break; +// } +// return obj.field; +TEST_F(LoadStoreEliminationTest, SimplifyTest4) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("entry", + "exit", + {{"entry", "case1"}, + {"entry", "case2"}, + {"entry", "case3"}, + {"case1", "breturn"}, + {"case2", "breturn"}, + {"case3", "breturn"}, + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(case1); + GET_BLOCK(case2); + GET_BLOCK(case3); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {case1, case2, case3}); + + HInstruction* int_val = MakeParam(DataType::Type::kInt32); + HInstruction* c3 = graph_->GetIntConstant(3); + HInstruction* c10 = graph_->GetIntConstant(10); + HInstruction* c20 = graph_->GetIntConstant(20); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(write_start); + entry->AddInstruction(switch_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* call_case1 = MakeInvoke(DataType::Type::kVoid, {new_inst}); + HInstruction* goto_case1 = new (GetAllocator()) HGoto(); + case1->AddInstruction(call_case1); + case1->AddInstruction(goto_case1); + call_case1->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* write_case2 = MakeIFieldSet(new_inst, c10, MemberOffset(32)); + HInstruction* goto_case2 = new (GetAllocator()) HGoto(); + case2->AddInstruction(write_case2); + case2->AddInstruction(goto_case2); + + HInstruction* write_case3 = MakeIFieldSet(new_inst, c20, MemberOffset(32)); + HInstruction* goto_case3 = new (GetAllocator()) HGoto(); + case3->AddInstruction(write_case3); + case3->AddInstruction(goto_case3); + + HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_end); + breturn->AddInstruction(read_end); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSE(); + + // Run the code-simplifier too + LOG(INFO) << "Pre simplification " << blks; + InstructionSimplifier simp(graph_, /*codegen=*/nullptr); + simp.Run(); + + LOG(INFO) << "Post LSE " << blks; + + EXPECT_INS_REMOVED(write_case2); + EXPECT_INS_REMOVED(write_case3); + EXPECT_INS_REMOVED(write_start); + EXPECT_INS_REMOVED(read_end); + EXPECT_INS_RETAINED(call_case1); + + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); + ASSERT_NE(pred_get, nullptr); + EXPECT_TRUE(pred_get->GetDefaultValue()->IsPhi()) + << pred_get->DumpWithArgs(); + EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), graph_->GetIntConstant(0)); + EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), c10); + EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(2), c20); +} + +// Make sure that irreducible loops don't screw up Partial LSE. We can't pull +// phis through them so we need to treat them as escapes. +// TODO We should be able to do better than this? Need to do some research. +// // ENTRY +// obj = new Obj(); +// obj.foo = 11; +// if (param1) { +// } else { +// // irreducible loop here. NB the objdoesn't actually escape +// obj.foo = 33; +// if (param2) { +// goto inner; +// } else { +// while (test()) { +// if (test()) { +// obj.foo = 66; +// } else { +// } +// inner: +// } +// } +// } +// return obj.foo; +TEST_F(LoadStoreEliminationTest, PartialIrreducibleLoop) { + VariableSizedHandleScope vshs(Thread::Current()); + CreateGraph(&vshs); + AdjacencyListGraph blks(SetupFromAdjacencyList("start", + "exit", + {{"start", "entry"}, + {"entry", "left"}, + {"entry", "right"}, + {"left", "breturn"}, + + {"right", "right_crit_break_loop"}, + {"right_crit_break_loop", "loop_header"}, + {"right", "right_crit_break_end"}, + {"right_crit_break_end", "loop_end"}, + + {"loop_header", "loop_body"}, + {"loop_body", "loop_left"}, + {"loop_body", "loop_right"}, + {"loop_left", "loop_end"}, + {"loop_right", "loop_end"}, + {"loop_end", "loop_header"}, + {"loop_header", "loop_header_crit_break"}, + {"loop_header_crit_break", "breturn"}, + + {"breturn", "exit"}})); +#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) + GET_BLOCK(start); + GET_BLOCK(entry); + GET_BLOCK(exit); + GET_BLOCK(breturn); + GET_BLOCK(left); + GET_BLOCK(right); + GET_BLOCK(right_crit_break_end); + GET_BLOCK(right_crit_break_loop); + GET_BLOCK(loop_header); + GET_BLOCK(loop_header_crit_break); + GET_BLOCK(loop_body); + GET_BLOCK(loop_left); + GET_BLOCK(loop_right); + GET_BLOCK(loop_end); +#undef GET_BLOCK + EnsurePredecessorOrder(breturn, {left, loop_header_crit_break}); + HInstruction* c11 = graph_->GetIntConstant(11); + HInstruction* c33 = graph_->GetIntConstant(33); + HInstruction* c66 = graph_->GetIntConstant(66); + HInstruction* param1 = MakeParam(DataType::Type::kBool); + HInstruction* param2 = MakeParam(DataType::Type::kBool); + + HInstruction* suspend = new (GetAllocator()) HSuspendCheck(); + HInstruction* start_goto = new (GetAllocator()) HGoto(); + start->AddInstruction(suspend); + start->AddInstruction(start_goto); + ManuallyBuildEnvFor(suspend, {}); + + HInstruction* cls = MakeClassLoad(); + HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* write_start = MakeIFieldSet(new_inst, c11, MemberOffset(32)); + HInstruction* if_inst = new (GetAllocator()) HIf(param1); + entry->AddInstruction(cls); + entry->AddInstruction(new_inst); + entry->AddInstruction(write_start); + entry->AddInstruction(if_inst); + ManuallyBuildEnvFor(cls, {}); + new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); + + left->AddInstruction(new (GetAllocator()) HGoto()); + + right->AddInstruction(MakeIFieldSet(new_inst, c33, MemberOffset(32))); + right->AddInstruction(new (GetAllocator()) HIf(param2)); + + right_crit_break_end->AddInstruction(new (GetAllocator()) HGoto()); + right_crit_break_loop->AddInstruction(new (GetAllocator()) HGoto()); + + HInstruction* header_suspend = new (GetAllocator()) HSuspendCheck(); + HInstruction* header_invoke = MakeInvoke(DataType::Type::kBool, {}); + HInstruction* header_if = new (GetAllocator()) HIf(header_invoke); + loop_header->AddInstruction(header_suspend); + loop_header->AddInstruction(header_invoke); + loop_header->AddInstruction(header_if); + header_suspend->CopyEnvironmentFrom(cls->GetEnvironment()); + header_invoke->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* body_invoke = MakeInvoke(DataType::Type::kBool, {}); + HInstruction* body_if = new (GetAllocator()) HIf(body_invoke); + loop_body->AddInstruction(body_invoke); + loop_body->AddInstruction(body_if); + body_invoke->CopyEnvironmentFrom(cls->GetEnvironment()); + + HInstruction* left_set = MakeIFieldSet(new_inst, c66, MemberOffset(32)); + HInstruction* left_goto = MakeIFieldSet(new_inst, c66, MemberOffset(32)); + loop_left->AddInstruction(left_set); + loop_left->AddInstruction(left_goto); + + loop_right->AddInstruction(new (GetAllocator()) HGoto()); + + loop_end->AddInstruction(new (GetAllocator()) HGoto()); + + HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* return_exit = new (GetAllocator()) HReturn(read_end); + breturn->AddInstruction(read_end); + breturn->AddInstruction(return_exit); + + SetupExit(exit); + + // PerformLSE expects this to be empty. + graph_->ClearDominanceInformation(); + LOG(INFO) << "Pre LSE " << blks; + PerformLSE(); + LOG(INFO) << "Post LSE " << blks; + + EXPECT_TRUE(loop_header->IsLoopHeader()); + EXPECT_TRUE(loop_header->GetLoopInformation()->IsIrreducible()); + + EXPECT_INS_RETAINED(left_set); + EXPECT_INS_REMOVED(write_start); + EXPECT_INS_REMOVED(read_end); + + HPredicatedInstanceFieldGet* pred_get = + FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); + ASSERT_NE(pred_get, nullptr); + ASSERT_TRUE(pred_get->GetDefaultValue()->IsPhi()) << pred_get->DumpWithArgs(); + EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), c11); + EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), graph_->GetIntConstant(0)); + ASSERT_TRUE(pred_get->GetTarget()->IsPhi()) << pred_get->DumpWithArgs(); + EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(0), graph_->GetNullConstant()); + HNewInstance* mat = FindSingleInstruction<HNewInstance>(graph_, right->GetSinglePredecessor()); + ASSERT_NE(mat, nullptr); + EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(1), mat); } } // namespace art |