Fix simplifier issue with predicated ifield get
In cases where all targets of a HPredicatedInstanceFieldGet
instruction are known to not be null the simplifier would attempt to
replace the default value with a null instruction. This would cause a
null-pointer dereference. Correct the simplifier to handle this case
correctly.
Moved some LSE test helper functions to CommonCompilerTestHelper to
avoid duplicating code.
Fixed an incorrect (though until now unused) constructor for
HPredicatatedInstanceFieldGet (the default value and target we
swapped).
Test: ./test.py --host
Test: ./art/tools/compile_jars.py --profile-file bad-compile.txt ~/imgur.apk
Bug: 183942773
Change-Id: I66f4ce37d768d5e457047a3f80bd4cb9aa4546a3
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 2f99809..935e255 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -426,6 +426,7 @@
"jni/jni_cfi_test.cc",
"optimizing/codegen_test.cc",
"optimizing/execution_subgraph_test.cc",
+ "optimizing/instruction_simplifier_test.cc",
"optimizing/load_store_analysis_test.cc",
"optimizing/load_store_elimination_test.cc",
"optimizing/optimizing_cfi_test.cc",
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 8970372..938a775 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -958,14 +958,38 @@
single_value = default_val->InputAt(idx);
} else if (single_value != default_val->InputAt(idx) &&
!single_value->Equals(default_val->InputAt(idx))) {
- // Multiple values, can't combine.
+ // Multiple values are associated with potential nulls, can't combine.
return;
}
}
}
- if (single_value->StrictlyDominates(pred_get)) {
+ if (single_value == nullptr) {
+ // None of the inputs can be null so we can just remove the predicatedness
+ // of this instruction.
+ DCHECK(std::none_of(inputs.cbegin(), inputs.cend(), [](const HInstruction* input) {
+ return input->CanBeNull();
+ }));
+ HInstruction* replace_with = new (GetGraph()->GetAllocator())
+ HInstanceFieldGet(pred_get->GetTarget(),
+ pred_get->GetFieldInfo().GetField(),
+ pred_get->GetFieldType(),
+ pred_get->GetFieldOffset(),
+ pred_get->IsVolatile(),
+ pred_get->GetFieldInfo().GetFieldIndex(),
+ pred_get->GetFieldInfo().GetDeclaringClassDefIndex(),
+ pred_get->GetFieldInfo().GetDexFile(),
+ pred_get->GetDexPc());
+ if (pred_get->GetType() == DataType::Type::kReference) {
+ replace_with->SetReferenceTypeInfo(pred_get->GetReferenceTypeInfo());
+ }
+ pred_get->GetBlock()->InsertInstructionBefore(replace_with, pred_get);
+ pred_get->ReplaceWith(replace_with);
+ pred_get->GetBlock()->RemoveInstruction(pred_get);
+ RecordSimplification();
+ } else if (single_value->StrictlyDominates(pred_get)) {
// Combine all the maybe null values into one.
pred_get->ReplaceInput(single_value, 0);
+ RecordSimplification();
}
}
diff --git a/compiler/optimizing/instruction_simplifier_test.cc b/compiler/optimizing/instruction_simplifier_test.cc
new file mode 100644
index 0000000..7d93809
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_test.cc
@@ -0,0 +1,310 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "instruction_simplifier.h"
+
+#include <initializer_list>
+#include <tuple>
+
+#include "gtest/gtest.h"
+#include "nodes.h"
+#include "optimizing/data_type.h"
+#include "optimizing_unit_test.h"
+
+namespace art {
+
+class InstructionSimplifierTest : public CommonCompilerTest, public OptimizingUnitTestHelper {
+ public:
+ void SetUp() override {
+ CommonCompilerTest::SetUp();
+ gLogVerbosity.compiler = true;
+ }
+
+ void TearDown() override {
+ CommonCompilerTest::TearDown();
+ gLogVerbosity.compiler = false;
+ }
+};
+
+// // ENTRY
+// switch (param) {
+// case 1:
+// obj1 = param2; break;
+// case 2:
+// obj1 = param3; break;
+// default:
+// obj2 = new Obj();
+// }
+// val_phi = PHI[3,4,10]
+// target_phi = PHI[param2, param3, obj2]
+// return PredFieldGet[val_phi, target_phi] => PredFieldGet[val_phi, target_phi]
+TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetNoMerge) {
+ 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(case1);
+ GET_BLOCK(case2);
+ GET_BLOCK(case3);
+ GET_BLOCK(breturn);
+#undef GET_BLOCK
+
+ HInstruction* bool_value = MakeParam(DataType::Type::kInt32);
+ HInstruction* obj1_param = MakeParam(DataType::Type::kReference);
+ HInstruction* obj2_param = MakeParam(DataType::Type::kReference);
+ HInstruction* c3 = graph_->GetIntConstant(3);
+ HInstruction* c4 = graph_->GetIntConstant(4);
+ HInstruction* c10 = graph_->GetIntConstant(10);
+
+ HInstruction* cls = MakeClassLoad();
+ HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, bool_value);
+ entry->AddInstruction(cls);
+ entry->AddInstruction(switch_inst);
+ ManuallyBuildEnvFor(cls, {});
+
+ HInstruction* goto_c1 = new (GetAllocator()) HGoto();
+ case1->AddInstruction(goto_c1);
+
+ HInstruction* goto_c2 = new (GetAllocator()) HGoto();
+ case2->AddInstruction(goto_c2);
+
+ HInstruction* obj3 = MakeNewInstance(cls);
+ HInstruction* goto_c3 = new (GetAllocator()) HGoto();
+ case3->AddInstruction(obj3);
+ case3->AddInstruction(goto_c3);
+
+ HPhi* val_phi = MakePhi({c3, c4, c10});
+ HPhi* obj_phi = MakePhi({obj1_param, obj2_param, obj3});
+ HPredicatedInstanceFieldGet* read_end =
+ new (GetAllocator()) HPredicatedInstanceFieldGet(obj_phi,
+ nullptr,
+ val_phi,
+ val_phi->GetType(),
+ MemberOffset(10),
+ false,
+ 42,
+ 0,
+ graph_->GetDexFile(),
+ 0);
+ HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
+ breturn->AddPhi(val_phi);
+ breturn->AddPhi(obj_phi);
+ breturn->AddInstruction(read_end);
+ breturn->AddInstruction(return_exit);
+
+ SetupExit(exit);
+
+ LOG(INFO) << "Pre simplification " << blks;
+ graph_->ClearDominanceInformation();
+ graph_->BuildDominatorTree();
+ InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
+ simp.Run();
+
+ LOG(INFO) << "Post simplify " << blks;
+
+ EXPECT_INS_RETAINED(read_end);
+
+ EXPECT_INS_EQ(read_end->GetTarget(), obj_phi);
+ EXPECT_INS_EQ(read_end->GetDefaultValue(), val_phi);
+}
+
+// // ENTRY
+// switch (param) {
+// case 1:
+// obj1 = param2; break;
+// case 2:
+// obj1 = param3; break;
+// default:
+// obj2 = new Obj();
+// }
+// val_phi = PHI[3,3,10]
+// target_phi = PHI[param2, param3, obj2]
+// return PredFieldGet[val_phi, target_phi] => PredFieldGet[3, target_phi]
+TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetMerge) {
+ 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(case1);
+ GET_BLOCK(case2);
+ GET_BLOCK(case3);
+ GET_BLOCK(breturn);
+#undef GET_BLOCK
+
+ HInstruction* bool_value = MakeParam(DataType::Type::kInt32);
+ HInstruction* obj1_param = MakeParam(DataType::Type::kReference);
+ HInstruction* obj2_param = MakeParam(DataType::Type::kReference);
+ HInstruction* c3 = graph_->GetIntConstant(3);
+ HInstruction* c10 = graph_->GetIntConstant(10);
+
+ HInstruction* cls = MakeClassLoad();
+ HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, bool_value);
+ entry->AddInstruction(cls);
+ entry->AddInstruction(switch_inst);
+ ManuallyBuildEnvFor(cls, {});
+
+ HInstruction* goto_c1 = new (GetAllocator()) HGoto();
+ case1->AddInstruction(goto_c1);
+
+ HInstruction* goto_c2 = new (GetAllocator()) HGoto();
+ case2->AddInstruction(goto_c2);
+
+ HInstruction* obj3 = MakeNewInstance(cls);
+ HInstruction* goto_c3 = new (GetAllocator()) HGoto();
+ case3->AddInstruction(obj3);
+ case3->AddInstruction(goto_c3);
+
+ HPhi* val_phi = MakePhi({c3, c3, c10});
+ HPhi* obj_phi = MakePhi({obj1_param, obj2_param, obj3});
+ HPredicatedInstanceFieldGet* read_end =
+ new (GetAllocator()) HPredicatedInstanceFieldGet(obj_phi,
+ nullptr,
+ val_phi,
+ val_phi->GetType(),
+ MemberOffset(10),
+ false,
+ 42,
+ 0,
+ graph_->GetDexFile(),
+ 0);
+ HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
+ breturn->AddPhi(val_phi);
+ breturn->AddPhi(obj_phi);
+ breturn->AddInstruction(read_end);
+ breturn->AddInstruction(return_exit);
+
+ SetupExit(exit);
+
+ LOG(INFO) << "Pre simplification " << blks;
+ graph_->ClearDominanceInformation();
+ graph_->BuildDominatorTree();
+ InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
+ simp.Run();
+
+ LOG(INFO) << "Post simplify " << blks;
+
+ EXPECT_FALSE(obj3->CanBeNull());
+ EXPECT_INS_RETAINED(read_end);
+
+ EXPECT_INS_EQ(read_end->GetTarget(), obj_phi);
+ EXPECT_INS_EQ(read_end->GetDefaultValue(), c3);
+}
+
+// // ENTRY
+// if (param) {
+// obj1 = new Obj();
+// } else {
+// obj2 = new Obj();
+// }
+// val_phi = PHI[3,10]
+// target_phi = PHI[obj1, obj2]
+// return PredFieldGet[val_phi, target_phi] => FieldGet[target_phi]
+TEST_F(InstructionSimplifierTest, SimplifyPredicatedFieldGetNoNull) {
+ 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(left);
+ GET_BLOCK(right);
+ GET_BLOCK(breturn);
+#undef GET_BLOCK
+
+ HInstruction* bool_value = MakeParam(DataType::Type::kBool);
+ HInstruction* c3 = graph_->GetIntConstant(3);
+ HInstruction* c10 = graph_->GetIntConstant(10);
+
+ HInstruction* cls = MakeClassLoad();
+ HInstruction* if_inst = new (GetAllocator()) HIf(bool_value);
+ entry->AddInstruction(cls);
+ entry->AddInstruction(if_inst);
+ ManuallyBuildEnvFor(cls, {});
+
+ HInstruction* obj1 = MakeNewInstance(cls);
+ HInstruction* goto_left = new (GetAllocator()) HGoto();
+ left->AddInstruction(obj1);
+ left->AddInstruction(goto_left);
+
+ HInstruction* obj2 = MakeNewInstance(cls);
+ HInstruction* goto_right = new (GetAllocator()) HGoto();
+ right->AddInstruction(obj2);
+ right->AddInstruction(goto_right);
+
+ HPhi* val_phi = MakePhi({c3, c10});
+ HPhi* obj_phi = MakePhi({obj1, obj2});
+ HInstruction* read_end = new (GetAllocator()) HPredicatedInstanceFieldGet(obj_phi,
+ nullptr,
+ val_phi,
+ val_phi->GetType(),
+ MemberOffset(10),
+ false,
+ 42,
+ 0,
+ graph_->GetDexFile(),
+ 0);
+ HInstruction* return_exit = new (GetAllocator()) HReturn(read_end);
+ breturn->AddPhi(val_phi);
+ breturn->AddPhi(obj_phi);
+ breturn->AddInstruction(read_end);
+ breturn->AddInstruction(return_exit);
+
+ SetupExit(exit);
+
+ LOG(INFO) << "Pre simplification " << blks;
+ graph_->ClearDominanceInformation();
+ graph_->BuildDominatorTree();
+ InstructionSimplifier simp(graph_, /*codegen=*/nullptr);
+ simp.Run();
+
+ LOG(INFO) << "Post simplify " << blks;
+
+ EXPECT_FALSE(obj1->CanBeNull());
+ EXPECT_FALSE(obj2->CanBeNull());
+ EXPECT_INS_REMOVED(read_end);
+
+ HInstanceFieldGet* ifget = FindSingleInstruction<HInstanceFieldGet>(graph_, breturn);
+ ASSERT_NE(ifget, nullptr);
+ EXPECT_INS_EQ(ifget->InputAt(0), obj_phi);
+}
+
+} // namespace art
diff --git a/compiler/optimizing/load_store_elimination_test.cc b/compiler/optimizing/load_store_elimination_test.cc
index 9b000a1..9ba364e 100644
--- a/compiler/optimizing/load_store_elimination_test.cc
+++ b/compiler/optimizing/load_store_elimination_test.cc
@@ -38,26 +38,6 @@
namespace art {
-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()) { \
@@ -65,13 +45,6 @@
} \
} 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:
@@ -85,12 +58,6 @@
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(bool with_partial = true) {
graph_->BuildDominatorTree();
LoadStoreElimination lse(graph_, /*stats=*/nullptr);
@@ -300,134 +267,6 @@
j_ = parameters_.back();
}
- void ManuallyBuildEnvFor(HInstruction* ins, const std::initializer_list<HInstruction*>& env) {
- ArenaVector<HInstruction*> current_locals(env, GetAllocator()->Adapter(kArenaAllocInstruction));
- OptimizingUnitTestHelper::ManuallyBuildEnvFor(ins, ¤t_locals);
- }
-
- HLoadClass* MakeClassLoad(std::optional<dex::TypeIndex> ti = std::nullopt) {
- return new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(),
- ti ? *ti : dex::TypeIndex(class_idx_++),
- graph_->GetDexFile(),
- /* klass= */ null_klass_,
- /* is_referrers_class= */ false,
- /* dex_pc= */ 0,
- /* needs_access_check= */ false);
- }
-
- HNewInstance* MakeNewInstance(HInstruction* cls, uint32_t dex_pc = 0u) {
- EXPECT_TRUE(cls->IsLoadClass() || cls->IsClinitCheck()) << *cls;
- HLoadClass* load =
- cls->IsLoadClass() ? cls->AsLoadClass() : cls->AsClinitCheck()->GetLoadClass();
- return new (GetAllocator()) HNewInstance(cls,
- dex_pc,
- load->GetTypeIndex(),
- graph_->GetDexFile(),
- /* finalizable= */ false,
- QuickEntrypointEnum::kQuickAllocObjectInitialized);
- }
-
- HInstanceFieldSet* MakeIFieldSet(HInstruction* inst,
- HInstruction* data,
- MemberOffset off,
- uint32_t dex_pc = 0u) {
- return new (GetAllocator()) HInstanceFieldSet(inst,
- data,
- /* field= */ nullptr,
- /* field_type= */ data->GetType(),
- /* field_offset= */ off,
- /* is_volatile= */ false,
- /* field_idx= */ 0,
- /* declaring_class_def_index= */ 0,
- graph_->GetDexFile(),
- dex_pc);
- }
-
- HInstanceFieldGet* MakeIFieldGet(HInstruction* inst,
- DataType::Type type,
- MemberOffset off,
- uint32_t dex_pc = 0u) {
- return new (GetAllocator()) HInstanceFieldGet(inst,
- /* field= */ nullptr,
- /* field_type= */ type,
- /* field_offset= */ off,
- /* is_volatile= */ false,
- /* field_idx= */ 0,
- /* declaring_class_def_index= */ 0,
- graph_->GetDexFile(),
- dex_pc);
- }
-
- HInvokeStaticOrDirect* MakeInvoke(DataType::Type return_type,
- const std::vector<HInstruction*>& args) {
- MethodReference method_reference{/* file= */ &graph_->GetDexFile(), /* index= */ method_idx_++};
- HInvokeStaticOrDirect* res = new (GetAllocator())
- HInvokeStaticOrDirect(GetAllocator(),
- args.size(),
- return_type,
- /* dex_pc= */ 0,
- method_reference,
- /* resolved_method= */ nullptr,
- HInvokeStaticOrDirect::DispatchInfo{},
- InvokeType::kStatic,
- /* resolved_method_reference= */ method_reference,
- HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
- for (auto [ins, idx] : ZipCount(MakeIterationRange(args))) {
- res->SetRawInputAt(idx, ins);
- }
- return res;
- }
-
- HPhi* MakePhi(const std::vector<HInstruction*>& ins) {
- EXPECT_GE(ins.size(), 2u) << "Phi requires at least 2 inputs";
- HPhi* phi =
- new (GetAllocator()) HPhi(GetAllocator(), kNoRegNumber, ins.size(), ins[0]->GetType());
- for (auto [i, idx] : ZipCount(MakeIterationRange(ins))) {
- phi->SetRawInputAt(idx, i);
- }
- return phi;
- }
-
- void SetupExit(HBasicBlock* exit) {
- exit->AddInstruction(new (GetAllocator()) HExit());
- }
-
- dex::TypeIndex DefaultTypeIndexForType(DataType::Type type) {
- switch (type) {
- case DataType::Type::kBool:
- return dex::TypeIndex(1);
- case DataType::Type::kUint8:
- case DataType::Type::kInt8:
- return dex::TypeIndex(2);
- case DataType::Type::kUint16:
- case DataType::Type::kInt16:
- return dex::TypeIndex(3);
- case DataType::Type::kUint32:
- case DataType::Type::kInt32:
- return dex::TypeIndex(4);
- case DataType::Type::kUint64:
- case DataType::Type::kInt64:
- return dex::TypeIndex(5);
- case DataType::Type::kReference:
- return dex::TypeIndex(6);
- case DataType::Type::kFloat32:
- return dex::TypeIndex(7);
- case DataType::Type::kFloat64:
- return dex::TypeIndex(8);
- case DataType::Type::kVoid:
- EXPECT_TRUE(false) << "No type for void!";
- return dex::TypeIndex(1000);
- }
- }
-
- // Creates a parameter. The instruction is automatically added to the entry-block
- HParameterValue* MakeParam(DataType::Type type, std::optional<dex::TypeIndex> ti = std::nullopt) {
- HParameterValue* val = new (GetAllocator()) HParameterValue(
- graph_->GetDexFile(), ti ? *ti : DefaultTypeIndexForType(type), param_count_++, type);
- graph_->GetEntryBlock()->AddInstruction(val);
- return val;
- }
-
HBasicBlock* pre_header_;
HBasicBlock* loop_;
@@ -439,12 +278,6 @@
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> {};
@@ -2314,156 +2147,6 @@
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>);
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index c47ca3b..48dae87 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -6245,8 +6245,8 @@
field_idx,
declaring_class_def_index,
dex_file) {
- SetRawInputAt(0, value);
- SetRawInputAt(1, default_value);
+ SetRawInputAt(1, value);
+ SetRawInputAt(0, default_value);
}
bool IsClonable() const override {
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index cf97c41..12c1b98 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -18,8 +18,12 @@
#define ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
#include <memory>
+#include <ostream>
#include <string_view>
+#include <string>
+#include <tuple>
#include <vector>
+#include <variant>
#include "base/indenter.h"
#include "base/malloc_arena_pool.h"
@@ -58,6 +62,33 @@
#define FIVE_REGISTERS_CODE_ITEM(...) N_REGISTERS_CODE_ITEM(5, __VA_ARGS__)
#define SIX_REGISTERS_CODE_ITEM(...) N_REGISTERS_CODE_ITEM(6, __VA_ARGS__)
+struct InstructionDumper {
+ public:
+ HInstruction* ins_;
+};
+
+inline bool operator==(const InstructionDumper& a, const InstructionDumper& b) {
+ return a.ins_ == b.ins_;
+}
+inline bool operator!=(const InstructionDumper& a, const InstructionDumper& b) {
+ return !(a == b);
+}
+
+inline 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 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})
+
inline LiveInterval* BuildInterval(const size_t ranges[][2],
size_t number_of_ranges,
ScopedArenaAllocator* allocator,
@@ -107,6 +138,80 @@
ScopedArenaAllocator scoped_allocator_;
};
+class AdjacencyListGraph {
+ public:
+ using Edge = std::pair<const std::string_view, const std::string_view>;
+ AdjacencyListGraph(
+ HGraph* graph,
+ ArenaAllocator* alloc,
+ const std::string_view entry_name,
+ const std::string_view exit_name,
+ const std::vector<Edge>& adj) : graph_(graph) {
+ auto create_block = [&]() {
+ HBasicBlock* blk = new (alloc) HBasicBlock(graph_);
+ graph_->AddBlock(blk);
+ return blk;
+ };
+ HBasicBlock* entry = create_block();
+ HBasicBlock* exit = create_block();
+ graph_->SetEntryBlock(entry);
+ graph_->SetExitBlock(exit);
+ name_to_block_.Put(entry_name, entry);
+ name_to_block_.Put(exit_name, exit);
+ for (const auto& [src, dest] : adj) {
+ HBasicBlock* src_blk = name_to_block_.GetOrCreate(src, create_block);
+ HBasicBlock* dest_blk = name_to_block_.GetOrCreate(dest, create_block);
+ src_blk->AddSuccessor(dest_blk);
+ }
+ graph_->ClearReachabilityInformation();
+ graph_->ComputeDominanceInformation();
+ graph_->ComputeReachabilityInformation();
+ for (auto [name, blk] : name_to_block_) {
+ block_to_name_.Put(blk, name);
+ }
+ }
+
+ bool HasBlock(const HBasicBlock* blk) const {
+ return block_to_name_.find(blk) != block_to_name_.end();
+ }
+
+ std::string_view GetName(const HBasicBlock* blk) const {
+ return block_to_name_.Get(blk);
+ }
+
+ HBasicBlock* Get(const std::string_view& sv) const {
+ return name_to_block_.Get(sv);
+ }
+
+ AdjacencyListGraph(AdjacencyListGraph&&) = default;
+ AdjacencyListGraph(const AdjacencyListGraph&) = default;
+ AdjacencyListGraph& operator=(AdjacencyListGraph&&) = default;
+ AdjacencyListGraph& operator=(const AdjacencyListGraph&) = default;
+
+ std::ostream& Dump(std::ostream& os) const {
+ struct Namer : public BlockNamer {
+ public:
+ explicit Namer(const AdjacencyListGraph& alg) : BlockNamer(), alg_(alg) {}
+ std::ostream& PrintName(std::ostream& os, HBasicBlock* blk) const override {
+ if (alg_.HasBlock(blk)) {
+ return os << alg_.GetName(blk) << " (" << blk->GetBlockId() << ")";
+ } else {
+ return os << "<Unnamed B" << blk->GetBlockId() << ">";
+ }
+ }
+
+ const AdjacencyListGraph& alg_;
+ };
+ Namer namer(*this);
+ return graph_->Dump(os, namer);
+ }
+
+ private:
+ HGraph* graph_;
+ SafeMap<const std::string_view, HBasicBlock*> name_to_block_;
+ SafeMap<const HBasicBlock*, const std::string_view> block_to_name_;
+};
+
// Have a separate helper so the OptimizingCFITest can inherit it without causing
// multiple inheritance errors from having two gtest as a parent twice.
class OptimizingUnitTestHelper {
@@ -290,6 +395,140 @@
}
}
+ 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 ManuallyBuildEnvFor(HInstruction* ins, const std::initializer_list<HInstruction*>& env) {
+ ArenaVector<HInstruction*> current_locals(env, GetAllocator()->Adapter(kArenaAllocInstruction));
+ OptimizingUnitTestHelper::ManuallyBuildEnvFor(ins, ¤t_locals);
+ }
+
+ HLoadClass* MakeClassLoad(std::optional<dex::TypeIndex> ti = std::nullopt) {
+ return new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(),
+ ti ? *ti : dex::TypeIndex(class_idx_++),
+ graph_->GetDexFile(),
+ /* klass= */ null_klass_,
+ /* is_referrers_class= */ false,
+ /* dex_pc= */ 0,
+ /* needs_access_check= */ false);
+ }
+
+ HNewInstance* MakeNewInstance(HInstruction* cls, uint32_t dex_pc = 0u) {
+ EXPECT_TRUE(cls->IsLoadClass() || cls->IsClinitCheck()) << *cls;
+ HLoadClass* load =
+ cls->IsLoadClass() ? cls->AsLoadClass() : cls->AsClinitCheck()->GetLoadClass();
+ return new (GetAllocator()) HNewInstance(cls,
+ dex_pc,
+ load->GetTypeIndex(),
+ graph_->GetDexFile(),
+ /* finalizable= */ false,
+ QuickEntrypointEnum::kQuickAllocObjectInitialized);
+ }
+
+ HInstanceFieldSet* MakeIFieldSet(HInstruction* inst,
+ HInstruction* data,
+ MemberOffset off,
+ uint32_t dex_pc = 0u) {
+ return new (GetAllocator()) HInstanceFieldSet(inst,
+ data,
+ /* field= */ nullptr,
+ /* field_type= */ data->GetType(),
+ /* field_offset= */ off,
+ /* is_volatile= */ false,
+ /* field_idx= */ 0,
+ /* declaring_class_def_index= */ 0,
+ graph_->GetDexFile(),
+ dex_pc);
+ }
+
+ HInstanceFieldGet* MakeIFieldGet(HInstruction* inst,
+ DataType::Type type,
+ MemberOffset off,
+ uint32_t dex_pc = 0u) {
+ return new (GetAllocator()) HInstanceFieldGet(inst,
+ /* field= */ nullptr,
+ /* field_type= */ type,
+ /* field_offset= */ off,
+ /* is_volatile= */ false,
+ /* field_idx= */ 0,
+ /* declaring_class_def_index= */ 0,
+ graph_->GetDexFile(),
+ dex_pc);
+ }
+
+ HInvokeStaticOrDirect* MakeInvoke(DataType::Type return_type,
+ const std::vector<HInstruction*>& args) {
+ MethodReference method_reference{/* file= */ &graph_->GetDexFile(), /* index= */ method_idx_++};
+ HInvokeStaticOrDirect* res = new (GetAllocator())
+ HInvokeStaticOrDirect(GetAllocator(),
+ args.size(),
+ return_type,
+ /* dex_pc= */ 0,
+ method_reference,
+ /* resolved_method= */ nullptr,
+ HInvokeStaticOrDirect::DispatchInfo{},
+ InvokeType::kStatic,
+ /* resolved_method_reference= */ method_reference,
+ HInvokeStaticOrDirect::ClinitCheckRequirement::kNone);
+ for (auto [ins, idx] : ZipCount(MakeIterationRange(args))) {
+ res->SetRawInputAt(idx, ins);
+ }
+ return res;
+ }
+
+ HPhi* MakePhi(const std::vector<HInstruction*>& ins) {
+ EXPECT_GE(ins.size(), 2u) << "Phi requires at least 2 inputs";
+ HPhi* phi =
+ new (GetAllocator()) HPhi(GetAllocator(), kNoRegNumber, ins.size(), ins[0]->GetType());
+ for (auto [i, idx] : ZipCount(MakeIterationRange(ins))) {
+ phi->SetRawInputAt(idx, i);
+ }
+ return phi;
+ }
+
+ void SetupExit(HBasicBlock* exit) {
+ exit->AddInstruction(new (GetAllocator()) HExit());
+ }
+
+ dex::TypeIndex DefaultTypeIndexForType(DataType::Type type) {
+ switch (type) {
+ case DataType::Type::kBool:
+ return dex::TypeIndex(1);
+ case DataType::Type::kUint8:
+ case DataType::Type::kInt8:
+ return dex::TypeIndex(2);
+ case DataType::Type::kUint16:
+ case DataType::Type::kInt16:
+ return dex::TypeIndex(3);
+ case DataType::Type::kUint32:
+ case DataType::Type::kInt32:
+ return dex::TypeIndex(4);
+ case DataType::Type::kUint64:
+ case DataType::Type::kInt64:
+ return dex::TypeIndex(5);
+ case DataType::Type::kReference:
+ return dex::TypeIndex(6);
+ case DataType::Type::kFloat32:
+ return dex::TypeIndex(7);
+ case DataType::Type::kFloat64:
+ return dex::TypeIndex(8);
+ case DataType::Type::kVoid:
+ EXPECT_TRUE(false) << "No type for void!";
+ return dex::TypeIndex(1000);
+ }
+ }
+
+ // Creates a parameter. The instruction is automatically added to the entry-block
+ HParameterValue* MakeParam(DataType::Type type, std::optional<dex::TypeIndex> ti = std::nullopt) {
+ HParameterValue* val = new (GetAllocator()) HParameterValue(
+ graph_->GetDexFile(), ti ? *ti : DefaultTypeIndexForType(type), param_count_++, type);
+ graph_->GetEntryBlock()->AddInstruction(val);
+ return val;
+ }
+
protected:
bool CheckGraph(HGraph* graph, bool check_ref_type_info, std::ostream& oss) {
GraphChecker checker(graph);
@@ -308,6 +547,12 @@
HBasicBlock* exit_block_;
std::vector<HInstruction*> parameters_;
+
+ size_t param_count_ = 0;
+ size_t class_idx_ = 42;
+ uint32_t method_idx_ = 100;
+
+ ScopedNullHandle<mirror::Class> null_klass_;
};
class OptimizingUnitTest : public CommonArtTest, public OptimizingUnitTestHelper {};
@@ -336,82 +581,158 @@
return instruction->GetBlock() == nullptr;
}
-class AdjacencyListGraph {
+inline std::ostream& operator<<(std::ostream& oss, const AdjacencyListGraph& alg) {
+ return alg.Dump(oss);
+}
+
+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:
- using Edge = std::pair<const std::string_view, const std::string_view>;
- AdjacencyListGraph(
- HGraph* graph,
- ArenaAllocator* alloc,
- const std::string_view entry_name,
- const std::string_view exit_name,
- const std::vector<Edge>& adj) : graph_(graph) {
- auto create_block = [&]() {
- HBasicBlock* blk = new (alloc) HBasicBlock(graph_);
- graph_->AddBlock(blk);
- return blk;
- };
- HBasicBlock* entry = create_block();
- HBasicBlock* exit = create_block();
- graph_->SetEntryBlock(entry);
- graph_->SetExitBlock(exit);
- name_to_block_.Put(entry_name, entry);
- name_to_block_.Put(exit_name, exit);
- for (const auto& [src, dest] : adj) {
- HBasicBlock* src_blk = name_to_block_.GetOrCreate(src, create_block);
- HBasicBlock* dest_blk = name_to_block_.GetOrCreate(dest, create_block);
- src_blk->AddSuccessor(dest_blk);
+ 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);
}
- graph_->ClearReachabilityInformation();
- graph_->ComputeDominanceInformation();
- graph_->ComputeReachabilityInformation();
- for (auto [name, blk] : name_to_block_) {
- block_to_name_.Put(blk, name);
- }
- }
-
- bool HasBlock(const HBasicBlock* blk) const {
- return block_to_name_.find(blk) != block_to_name_.end();
- }
-
- std::string_view GetName(const HBasicBlock* blk) const {
- return block_to_name_.Get(blk);
- }
-
- HBasicBlock* Get(const std::string_view& sv) const {
- return name_to_block_.Get(sv);
- }
-
- AdjacencyListGraph(AdjacencyListGraph&&) = default;
- AdjacencyListGraph(const AdjacencyListGraph&) = default;
- AdjacencyListGraph& operator=(AdjacencyListGraph&&) = default;
- AdjacencyListGraph& operator=(const AdjacencyListGraph&) = default;
-
- std::ostream& Dump(std::ostream& os) const {
- struct Namer : public BlockNamer {
- public:
- explicit Namer(const AdjacencyListGraph& alg) : BlockNamer(), alg_(alg) {}
- std::ostream& PrintName(std::ostream& os, HBasicBlock* blk) const override {
- if (alg_.HasBlock(blk)) {
- return os << alg_.GetName(blk) << " (" << blk->GetBlockId() << ")";
- } else {
- return os << "<Unnamed B" << blk->GetBlockId() << ">";
- }
- }
-
- const AdjacencyListGraph& alg_;
- };
- Namer namer(*this);
- return graph_->Dump(os, namer);
}
private:
- HGraph* graph_;
- SafeMap<const std::string_view, HBasicBlock*> name_to_block_;
- SafeMap<const HBasicBlock*, const std::string_view> block_to_name_;
+ 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_;
};
-inline std::ostream& operator<<(std::ostream& oss, const AdjacencyListGraph& alg) {
- return alg.Dump(oss);
+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));
}
} // namespace art