summaryrefslogtreecommitdiff
path: root/compiler/optimizing/load_store_elimination_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/optimizing/load_store_elimination_test.cc')
-rw-r--r--compiler/optimizing/load_store_elimination_test.cc7196
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_, &current_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, &current_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, &current_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, &current_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, &current_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, &current_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, &current_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, &current_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, &current_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, &current_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, &current_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, &current_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, &current_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, &current_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, &current_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, &current_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, &current_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