xref: /aosp_15_r20/art/compiler/optimizing/optimizing_unit_test.h (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
18 #define ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
19 
20 #include <memory>
21 #include <ostream>
22 #include <string_view>
23 #include <string>
24 #include <tuple>
25 #include <vector>
26 #include <variant>
27 
28 #include "base/macros.h"
29 #include "base/indenter.h"
30 #include "base/malloc_arena_pool.h"
31 #include "base/scoped_arena_allocator.h"
32 #include "builder.h"
33 #include "common_compiler_test.h"
34 #include "dex/code_item_accessors-inl.h"
35 #include "dex/dex_file.h"
36 #include "dex/dex_instruction.h"
37 #include "dex/standard_dex_file.h"
38 #include "driver/dex_compilation_unit.h"
39 #include "graph_checker.h"
40 #include "gtest/gtest.h"
41 #include "handle_scope-inl.h"
42 #include "handle_scope.h"
43 #include "mirror/class_loader.h"
44 #include "mirror/dex_cache.h"
45 #include "nodes.h"
46 #include "scoped_thread_state_change.h"
47 #include "ssa_builder.h"
48 #include "ssa_liveness_analysis.h"
49 
50 namespace art HIDDEN {
51 
52 #define NUM_INSTRUCTIONS(...)  \
53   (sizeof((uint16_t[]) {__VA_ARGS__}) /sizeof(uint16_t))
54 
55 #define N_REGISTERS_CODE_ITEM(NUM_REGS, ...)                            \
56     { NUM_REGS, 0, 0, 0, 0, 0, NUM_INSTRUCTIONS(__VA_ARGS__), 0, __VA_ARGS__ }
57 
58 #define ZERO_REGISTER_CODE_ITEM(...)   N_REGISTERS_CODE_ITEM(0, __VA_ARGS__)
59 #define ONE_REGISTER_CODE_ITEM(...)    N_REGISTERS_CODE_ITEM(1, __VA_ARGS__)
60 #define TWO_REGISTERS_CODE_ITEM(...)   N_REGISTERS_CODE_ITEM(2, __VA_ARGS__)
61 #define THREE_REGISTERS_CODE_ITEM(...) N_REGISTERS_CODE_ITEM(3, __VA_ARGS__)
62 #define FOUR_REGISTERS_CODE_ITEM(...)  N_REGISTERS_CODE_ITEM(4, __VA_ARGS__)
63 #define FIVE_REGISTERS_CODE_ITEM(...)  N_REGISTERS_CODE_ITEM(5, __VA_ARGS__)
64 #define SIX_REGISTERS_CODE_ITEM(...)   N_REGISTERS_CODE_ITEM(6, __VA_ARGS__)
65 
66 struct InstructionDumper {
67  public:
68   HInstruction* ins_;
69 };
70 
71 inline bool operator==(const InstructionDumper& a, const InstructionDumper& b) {
72   return a.ins_ == b.ins_;
73 }
74 inline bool operator!=(const InstructionDumper& a, const InstructionDumper& b) {
75   return !(a == b);
76 }
77 
78 inline std::ostream& operator<<(std::ostream& os, const InstructionDumper& id) {
79   if (id.ins_ == nullptr) {
80     return os << "NULL";
81   } else {
82     return os << "(" << id.ins_ << "): " << id.ins_->DumpWithArgs();
83   }
84 }
85 
86 #define EXPECT_INS_EQ(a, b) EXPECT_EQ(InstructionDumper{a}, InstructionDumper{b})
87 #define EXPECT_INS_REMOVED(a) EXPECT_TRUE(IsRemoved(a)) << "Not removed: " << (InstructionDumper{a})
88 #define EXPECT_INS_RETAINED(a) EXPECT_FALSE(IsRemoved(a)) << "Removed: " << (InstructionDumper{a})
89 #define ASSERT_INS_EQ(a, b) ASSERT_EQ(InstructionDumper{a}, InstructionDumper{b})
90 #define ASSERT_INS_REMOVED(a) ASSERT_TRUE(IsRemoved(a)) << "Not removed: " << (InstructionDumper{a})
91 #define ASSERT_INS_RETAINED(a) ASSERT_FALSE(IsRemoved(a)) << "Removed: " << (InstructionDumper{a})
92 
93 inline LiveInterval* BuildInterval(const size_t ranges[][2],
94                                    size_t number_of_ranges,
95                                    ScopedArenaAllocator* allocator,
96                                    int reg = -1,
97                                    HInstruction* defined_by = nullptr) {
98   LiveInterval* interval =
99       LiveInterval::MakeInterval(allocator, DataType::Type::kInt32, defined_by);
100   if (defined_by != nullptr) {
101     defined_by->SetLiveInterval(interval);
102   }
103   for (size_t i = number_of_ranges; i > 0; --i) {
104     interval->AddRange(ranges[i - 1][0], ranges[i - 1][1]);
105   }
106   interval->SetRegister(reg);
107   return interval;
108 }
109 
RemoveSuspendChecks(HGraph * graph)110 inline void RemoveSuspendChecks(HGraph* graph) {
111   for (HBasicBlock* block : graph->GetBlocks()) {
112     if (block != nullptr) {
113       if (block->GetLoopInformation() != nullptr) {
114         block->GetLoopInformation()->SetSuspendCheck(nullptr);
115       }
116       for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
117         HInstruction* current = it.Current();
118         if (current->IsSuspendCheck()) {
119           current->GetBlock()->RemoveInstruction(current);
120         }
121       }
122     }
123   }
124 }
125 
126 class ArenaPoolAndAllocator {
127  public:
ArenaPoolAndAllocator()128   ArenaPoolAndAllocator()
129       : pool_(), allocator_(&pool_), arena_stack_(&pool_), scoped_allocator_(&arena_stack_) { }
130 
GetAllocator()131   ArenaAllocator* GetAllocator() { return &allocator_; }
GetArenaStack()132   ArenaStack* GetArenaStack() { return &arena_stack_; }
GetScopedAllocator()133   ScopedArenaAllocator* GetScopedAllocator() { return &scoped_allocator_; }
134 
135  private:
136   MallocArenaPool pool_;
137   ArenaAllocator allocator_;
138   ArenaStack arena_stack_;
139   ScopedArenaAllocator scoped_allocator_;
140 };
141 
142 class AdjacencyListGraph {
143  public:
144   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)145   AdjacencyListGraph(
146       HGraph* graph,
147       ArenaAllocator* alloc,
148       const std::string_view entry_name,
149       const std::string_view exit_name,
150       const std::vector<Edge>& adj) : graph_(graph) {
151     auto create_block = [&]() {
152       HBasicBlock* blk = new (alloc) HBasicBlock(graph_);
153       graph_->AddBlock(blk);
154       return blk;
155     };
156     HBasicBlock* entry = create_block();
157     HBasicBlock* exit = create_block();
158     graph_->SetEntryBlock(entry);
159     graph_->SetExitBlock(exit);
160     name_to_block_.Put(entry_name, entry);
161     name_to_block_.Put(exit_name, exit);
162     for (const auto& [src, dest] : adj) {
163       HBasicBlock* src_blk = name_to_block_.GetOrCreate(src, create_block);
164       HBasicBlock* dest_blk = name_to_block_.GetOrCreate(dest, create_block);
165       src_blk->AddSuccessor(dest_blk);
166     }
167     graph_->ComputeDominanceInformation();
168     for (auto [name, blk] : name_to_block_) {
169       block_to_name_.Put(blk, name);
170     }
171   }
172 
HasBlock(const HBasicBlock * blk)173   bool HasBlock(const HBasicBlock* blk) const {
174     return block_to_name_.find(blk) != block_to_name_.end();
175   }
176 
GetName(const HBasicBlock * blk)177   std::string_view GetName(const HBasicBlock* blk) const {
178     return block_to_name_.Get(blk);
179   }
180 
Get(const std::string_view & sv)181   HBasicBlock* Get(const std::string_view& sv) const {
182     return name_to_block_.Get(sv);
183   }
184 
185   AdjacencyListGraph(AdjacencyListGraph&&) = default;
186   AdjacencyListGraph(const AdjacencyListGraph&) = default;
187   AdjacencyListGraph& operator=(AdjacencyListGraph&&) = default;
188   AdjacencyListGraph& operator=(const AdjacencyListGraph&) = default;
189 
Dump(std::ostream & os)190   std::ostream& Dump(std::ostream& os) const {
191     struct Namer : public BlockNamer {
192      public:
193       explicit Namer(const AdjacencyListGraph& alg) : BlockNamer(), alg_(alg) {}
194       std::ostream& PrintName(std::ostream& os, HBasicBlock* blk) const override {
195         if (alg_.HasBlock(blk)) {
196           return os << alg_.GetName(blk) << " (" << blk->GetBlockId() << ")";
197         } else {
198           return os << "<Unnamed B" << blk->GetBlockId() << ">";
199         }
200       }
201 
202       const AdjacencyListGraph& alg_;
203     };
204     Namer namer(*this);
205     return graph_->Dump(os, /* codegen_= */ nullptr, namer);
206   }
207 
208  private:
209   HGraph* graph_;
210   SafeMap<const std::string_view, HBasicBlock*> name_to_block_;
211   SafeMap<const HBasicBlock*, const std::string_view> block_to_name_;
212 };
213 
214 // Have a separate helper so the OptimizingCFITest can inherit it without causing
215 // multiple inheritance errors from having two gtest as a parent twice.
216 class OptimizingUnitTestHelper {
217  public:
OptimizingUnitTestHelper()218   OptimizingUnitTestHelper()
219       : pool_and_allocator_(new ArenaPoolAndAllocator()),
220         graph_(nullptr),
221         entry_block_(nullptr),
222         exit_block_(nullptr) { }
223 
GetAllocator()224   ArenaAllocator* GetAllocator() { return pool_and_allocator_->GetAllocator(); }
GetArenaStack()225   ArenaStack* GetArenaStack() { return pool_and_allocator_->GetArenaStack(); }
GetScopedAllocator()226   ScopedArenaAllocator* GetScopedAllocator() { return pool_and_allocator_->GetScopedAllocator(); }
227 
ResetPoolAndAllocator()228   void ResetPoolAndAllocator() {
229     pool_and_allocator_.reset(new ArenaPoolAndAllocator());
230   }
231 
232   HGraph* CreateGraph(VariableSizedHandleScope* handles = nullptr) {
233     ArenaAllocator* const allocator = pool_and_allocator_->GetAllocator();
234 
235     // Reserve a big array of 0s so the dex file constructor can offsets from the header.
236     static constexpr size_t kDexDataSize = 4 * KB;
237     const uint8_t* dex_data = reinterpret_cast<uint8_t*>(allocator->Alloc(kDexDataSize));
238 
239     // Create the dex file based on the fake data. Call the constructor so that we can use virtual
240     // functions. Don't use the arena for the StandardDexFile otherwise the dex location leaks.
241     auto container =
242         std::make_shared<MemoryDexFileContainer>(dex_data, sizeof(StandardDexFile::Header));
243     dex_files_.emplace_back(new StandardDexFile(dex_data,
244                                                 "no_location",
245                                                 /*location_checksum*/ 0,
246                                                 /*oat_dex_file*/ nullptr,
247                                                 std::move(container)));
248 
249     graph_ = new (allocator) HGraph(
250         allocator,
251         pool_and_allocator_->GetArenaStack(),
252         handles,
253         *dex_files_.back(),
254         /*method_idx*/-1,
255         kRuntimeISA);
256     return graph_;
257   }
258 
259   // Create a control-flow graph from Dex instructions.
260   HGraph* CreateCFG(const std::vector<uint16_t>& data,
261                     DataType::Type return_type = DataType::Type::kInt32) {
262     ScopedObjectAccess soa(Thread::Current());
263     VariableSizedHandleScope handles(soa.Self());
264     HGraph* graph = CreateGraph(&handles);
265 
266     // The code item data might not aligned to 4 bytes, copy it to ensure that.
267     const size_t code_item_size = data.size() * sizeof(data.front());
268     void* aligned_data = GetAllocator()->Alloc(code_item_size);
269     memcpy(aligned_data, &data[0], code_item_size);
270     CHECK_ALIGNED(aligned_data, StandardDexFile::CodeItem::kAlignment);
271     const dex::CodeItem* code_item = reinterpret_cast<const dex::CodeItem*>(aligned_data);
272 
273     {
274       const DexCompilationUnit* dex_compilation_unit =
275           new (graph->GetAllocator()) DexCompilationUnit(
276               /* class_loader= */ Handle<mirror::ClassLoader>(),  // Invalid handle.
277               /* class_linker= */ nullptr,
278               graph->GetDexFile(),
279               code_item,
280               /* class_def_idx= */ DexFile::kDexNoIndex16,
281               /* method_idx= */ dex::kDexNoIndex,
282               /* access_flags= */ 0u,
283               /* verified_method= */ nullptr,
284               /* dex_cache= */ Handle<mirror::DexCache>());  // Invalid handle.
285       CodeItemDebugInfoAccessor accessor(graph->GetDexFile(), code_item, /*dex_method_idx*/ 0u);
286       HGraphBuilder builder(graph, dex_compilation_unit, accessor, return_type);
287       bool graph_built = (builder.BuildGraph() == kAnalysisSuccess);
288       return graph_built ? graph : nullptr;
289     }
290   }
291 
292   // Create simple graph with "entry", "main" and "exit" blocks, return the "main" block.
293   // Adds `HGoto` to the "entry" block and `HExit` to the "exit block. Leaves "main" block empty.
294   HBasicBlock* InitEntryMainExitGraph(VariableSizedHandleScope* handles = nullptr) {
295     CreateGraph(handles);
296     entry_block_ = AddNewBlock();
297     HBasicBlock* main_block = AddNewBlock();
298     exit_block_ = AddNewBlock();
299 
300     graph_->SetEntryBlock(entry_block_);
301     graph_->SetExitBlock(exit_block_);
302 
303     entry_block_->AddSuccessor(main_block);
304     main_block->AddSuccessor(exit_block_);
305 
306     MakeGoto(entry_block_);
307     MakeExit(exit_block_);
308 
309     return main_block;
310   }
311 
312   // Creates a graph identical to `InitEntryMainExitGraph()` and adds `HReturnVoid`.
313   HBasicBlock* InitEntryMainExitGraphWithReturnVoid(VariableSizedHandleScope* handles = nullptr) {
314     HBasicBlock* return_block = InitEntryMainExitGraph(handles);
315     MakeReturnVoid(return_block);
316     return return_block;
317   }
318 
319   // Insert "if_block", "then_block" and "else_block" before a given `merge_block`. Return the
320   // new blocks. Adds `HGoto` to "then_block" and "else_block". Adds `HIf` to the "if_block"
321   // if the caller provides a `condition`.
322   std::tuple<HBasicBlock*, HBasicBlock*, HBasicBlock*> CreateDiamondPattern(
323       HBasicBlock* merge_block, HInstruction* condition = nullptr) {
324     HBasicBlock* if_block = AddNewBlock();
325     HBasicBlock* then_block = AddNewBlock();
326     HBasicBlock* else_block = AddNewBlock();
327 
328     HBasicBlock* predecessor = merge_block->GetSinglePredecessor();
329     predecessor->ReplaceSuccessor(merge_block, if_block);
330 
331     if_block->AddSuccessor(then_block);
332     if_block->AddSuccessor(else_block);
333     then_block->AddSuccessor(merge_block);
334     else_block->AddSuccessor(merge_block);
335 
336     if (condition != nullptr) {
337       MakeIf(if_block, condition);
338     }
339     MakeGoto(then_block);
340     MakeGoto(else_block);
341 
342     return {if_block, then_block, else_block};
343   }
344 
345   // Insert "pre-header", "loop-header" and "loop-body" blocks before a given `loop_exit` block
346   // and connect them in a `while (...) { ... }` loop pattern. Return the new blocks.
347   // Adds `HGoto` to the "pre-header" and "loop-body" blocks but leaves the "loop-header" block
348   // empty, leaving the construction of an appropriate condition and `HIf` to the caller.
349   // Note: The `loop_exit` shall be the "then" successor of the "loop-header". If the `loop_exit`
350   // is needed as the "else" successor, use `HBlock::SwapSuccessors()` to adjust the order.
CreateWhileLoop(HBasicBlock * loop_exit)351   std::tuple<HBasicBlock*, HBasicBlock*, HBasicBlock*> CreateWhileLoop(HBasicBlock* loop_exit) {
352     HBasicBlock* pre_header = AddNewBlock();
353     HBasicBlock* loop_header = AddNewBlock();
354     HBasicBlock* loop_body = AddNewBlock();
355 
356     HBasicBlock* predecessor = loop_exit->GetSinglePredecessor();
357     predecessor->ReplaceSuccessor(loop_exit, pre_header);
358 
359     pre_header->AddSuccessor(loop_header);
360     loop_header->AddSuccessor(loop_exit);  // true successor
361     loop_header->AddSuccessor(loop_body);  // false successor
362     loop_body->AddSuccessor(loop_header);
363 
364     MakeGoto(pre_header);
365     MakeGoto(loop_body);
366 
367     return {pre_header, loop_header, loop_body};
368   }
369 
370   // Insert "pre-header" and "loop" blocks before a given `loop_exit` block and connect them in a
371   // `do { ... } while (...);` loop pattern. Return the new blocks. Adds `HGoto` to the "pre-header"
372   // block but leaves the "loop" block empty, leaving the construction of an appropriate condition
373   // and `HIf` to the caller.
374   // Note: The `loop_exit` shall be the "then" successor of the "loop". If the `loop_exit`
375   // is needed as the "else" successor, use `HBlock::SwapSuccessors()` to adjust the order.
CreateDoWhileLoop(HBasicBlock * loop_exit)376   std::tuple<HBasicBlock*, HBasicBlock*> CreateDoWhileLoop(HBasicBlock* loop_exit) {
377     HBasicBlock* pre_header = AddNewBlock();
378     HBasicBlock* loop = AddNewBlock();
379 
380     HBasicBlock* predecessor = loop_exit->GetSinglePredecessor();
381     predecessor->ReplaceSuccessor(loop_exit, pre_header);
382 
383     pre_header->AddSuccessor(loop);
384     loop->AddSuccessor(loop_exit);  // true successor
385     loop->AddSuccessor(loop);  // false successor
386 
387     MakeGoto(pre_header);
388 
389     return {pre_header, loop};
390   }
391 
AddNewBlock()392   HBasicBlock* AddNewBlock() {
393     HBasicBlock* block = new (GetAllocator()) HBasicBlock(graph_);
394     graph_->AddBlock(block);
395     return block;
396   }
397 
398   // Run GraphChecker with all checks.
399   //
400   // Return: the status whether the run is successful.
401   bool CheckGraph(std::ostream& oss = std::cerr) {
402     return CheckGraph(graph_, oss);
403   }
404 
ManuallyBuildEnvFor(HInstruction * instruction,ArenaVector<HInstruction * > * current_locals)405   HEnvironment* ManuallyBuildEnvFor(HInstruction* instruction,
406                                     ArenaVector<HInstruction*>* current_locals) {
407     HEnvironment* environment = HEnvironment::Create(
408         GetAllocator(),
409         current_locals->size(),
410         graph_->GetArtMethod(),
411         instruction->GetDexPc(),
412         instruction);
413 
414     environment->CopyFrom(ArrayRef<HInstruction* const>(*current_locals));
415     instruction->SetRawEnvironment(environment);
416     return environment;
417   }
418 
EnsurePredecessorOrder(HBasicBlock * target,std::initializer_list<HBasicBlock * > preds)419   void EnsurePredecessorOrder(HBasicBlock* target, std::initializer_list<HBasicBlock*> preds) {
420     // Make sure the given preds and block predecessors have the same blocks.
421     BitVector bv(preds.size(), false, Allocator::GetCallocAllocator());
422     auto preds_and_idx = ZipCount(MakeIterationRange(target->GetPredecessors()));
423     bool correct_preds = preds.size() == target->GetPredecessors().size() &&
424                          std::all_of(preds.begin(), preds.end(), [&](HBasicBlock* pred) {
425                            return std::any_of(preds_and_idx.begin(),
426                                               preds_and_idx.end(),
427                                               // Make sure every target predecessor is used only
428                                               // once.
429                                               [&](std::pair<HBasicBlock*, uint32_t> cur) {
430                                                 if (cur.first == pred && !bv.IsBitSet(cur.second)) {
431                                                   bv.SetBit(cur.second);
432                                                   return true;
433                                                 } else {
434                                                   return false;
435                                                 }
436                                               });
437                          }) &&
438                          bv.NumSetBits() == preds.size();
439     auto dump_list = [](auto it) {
440       std::ostringstream oss;
441       oss << "[";
442       bool first = true;
443       for (HBasicBlock* b : it) {
444         if (!first) {
445           oss << ", ";
446         }
447         first = false;
448         oss << b->GetBlockId();
449       }
450       oss << "]";
451       return oss.str();
452     };
453     ASSERT_TRUE(correct_preds) << "Predecessors of " << target->GetBlockId() << " are "
454                                << dump_list(target->GetPredecessors()) << " not "
455                                << dump_list(preds);
456     if (correct_preds) {
457       std::copy(preds.begin(), preds.end(), target->predecessors_.begin());
458     }
459   }
460 
SetupFromAdjacencyList(const std::string_view entry_name,const std::string_view exit_name,const std::vector<AdjacencyListGraph::Edge> & adj)461   AdjacencyListGraph SetupFromAdjacencyList(const std::string_view entry_name,
462                                             const std::string_view exit_name,
463                                             const std::vector<AdjacencyListGraph::Edge>& adj) {
464     return AdjacencyListGraph(graph_, GetAllocator(), entry_name, exit_name, adj);
465   }
466 
ManuallyBuildEnvFor(HInstruction * ins,const std::initializer_list<HInstruction * > & env)467   void ManuallyBuildEnvFor(HInstruction* ins, const std::initializer_list<HInstruction*>& env) {
468     ArenaVector<HInstruction*> current_locals(env, GetAllocator()->Adapter(kArenaAllocInstruction));
469     OptimizingUnitTestHelper::ManuallyBuildEnvFor(ins, &current_locals);
470   }
471 
472   HLoadClass* MakeLoadClass(HBasicBlock* block,
473                             std::optional<dex::TypeIndex> ti = std::nullopt,
474                             std::optional<Handle<mirror::Class>> klass = std::nullopt,
475                             std::initializer_list<HInstruction*> env = {},
476                             uint32_t dex_pc = kNoDexPc) {
477     HLoadClass* load_class = new (GetAllocator()) HLoadClass(
478         graph_->GetCurrentMethod(),
479         ti ? *ti : dex::TypeIndex(class_idx_++),
480         graph_->GetDexFile(),
481         /* klass= */ klass ? *klass : null_klass_,
482         /* is_referrers_class= */ false,
483         dex_pc,
484         /* needs_access_check= */ false);
485     AddOrInsertInstruction(block, load_class);
486     ManuallyBuildEnvFor(load_class, env);
487     return load_class;
488   }
489 
490   HNewInstance* MakeNewInstance(HBasicBlock* block,
491                                 HInstruction* cls,
492                                 std::initializer_list<HInstruction*> env = {},
493                                 uint32_t dex_pc = kNoDexPc) {
494     EXPECT_TRUE(cls->IsLoadClass() || cls->IsClinitCheck()) << *cls;
495     HLoadClass* load =
496         cls->IsLoadClass() ? cls->AsLoadClass() : cls->AsClinitCheck()->GetLoadClass();
497     HNewInstance* new_instance = new (GetAllocator()) HNewInstance(
498         cls,
499         dex_pc,
500         load->GetTypeIndex(),
501         graph_->GetDexFile(),
502         /* finalizable= */ false,
503         QuickEntrypointEnum::kQuickAllocObjectInitialized);
504     AddOrInsertInstruction(block, new_instance);
505     ManuallyBuildEnvFor(new_instance, env);
506     return new_instance;
507   }
508 
509   HInstanceFieldSet* MakeIFieldSet(HBasicBlock* block,
510                                    HInstruction* object,
511                                    HInstruction* data,
512                                    MemberOffset off,
513                                    uint32_t dex_pc = kNoDexPc) {
514     CHECK(data != nullptr);
515     return MakeIFieldSet(block, object, data, data->GetType(), off, dex_pc);
516   }
517 
518   HInstanceFieldSet* MakeIFieldSet(HBasicBlock* block,
519                                    HInstruction* object,
520                                    HInstruction* data,
521                                    DataType::Type field_type,
522                                    MemberOffset off,
523                                    uint32_t dex_pc = kNoDexPc) {
524     HInstanceFieldSet* ifield_set = new (GetAllocator()) HInstanceFieldSet(
525         object,
526         data,
527         /* field= */ nullptr,
528         field_type,
529         /* field_offset= */ off,
530         /* is_volatile= */ false,
531         kUnknownFieldIndex,
532         kUnknownClassDefIndex,
533         graph_->GetDexFile(),
534         dex_pc);
535     AddOrInsertInstruction(block, ifield_set);
536     return ifield_set;
537   }
538 
539   HInstanceFieldGet* MakeIFieldGet(HBasicBlock* block,
540                                    HInstruction* object,
541                                    DataType::Type type,
542                                    MemberOffset off,
543                                    uint32_t dex_pc = kNoDexPc) {
544     HInstanceFieldGet* ifield_get = new (GetAllocator()) HInstanceFieldGet(
545         object,
546         /* field= */ nullptr,
547         /* field_type= */ type,
548         /* field_offset= */ off,
549         /* is_volatile= */ false,
550         kUnknownFieldIndex,
551         kUnknownClassDefIndex,
552         graph_->GetDexFile(),
553         dex_pc);
554     AddOrInsertInstruction(block, ifield_get);
555     return ifield_get;
556   }
557 
558   HNewArray* MakeNewArray(HBasicBlock* block,
559                           HInstruction* cls,
560                           HInstruction* length,
561                           size_t component_size_shift = DataType::SizeShift(DataType::Type::kInt32),
562                           std::initializer_list<HInstruction*> env = {},
563                           uint32_t dex_pc = kNoDexPc) {
564     HNewArray* new_array =
565         new (GetAllocator()) HNewArray(cls, length, dex_pc, component_size_shift);
566     AddOrInsertInstruction(block, new_array);
567     ManuallyBuildEnvFor(new_array, env);
568     return new_array;
569   }
570 
571   HArraySet* MakeArraySet(HBasicBlock* block,
572                           HInstruction* array,
573                           HInstruction* index,
574                           HInstruction* value,
575                           uint32_t dex_pc = kNoDexPc) {
576     CHECK(value != nullptr);
577     return MakeArraySet(block, array, index, value, value->GetType(), dex_pc);
578   }
579 
580   HArraySet* MakeArraySet(HBasicBlock* block,
581                           HInstruction* array,
582                           HInstruction* index,
583                           HInstruction* value,
584                           DataType::Type type,
585                           uint32_t dex_pc = kNoDexPc) {
586     HArraySet* array_set = new (GetAllocator()) HArraySet(array, index, value, type, dex_pc);
587     AddOrInsertInstruction(block, array_set);
588     return array_set;
589   }
590 
591   HArrayGet* MakeArrayGet(HBasicBlock* block,
592                           HInstruction* array,
593                           HInstruction* index,
594                           DataType::Type type,
595                           uint32_t dex_pc = kNoDexPc) {
596     HArrayGet* array_get = new (GetAllocator()) HArrayGet(array, index, type, dex_pc);
597     AddOrInsertInstruction(block, array_get);
598     return array_get;
599   }
600 
601   HArrayLength* MakeArrayLength(HBasicBlock* block,
602                                 HInstruction* array,
603                                 uint32_t dex_pc = kNoDexPc) {
604     HArrayLength* array_length = new (GetAllocator()) HArrayLength(array, dex_pc);
605     AddOrInsertInstruction(block, array_length);
606     return array_length;
607   }
608 
609   HNullCheck* MakeNullCheck(HBasicBlock* block,
610                             HInstruction* value,
611                             std::initializer_list<HInstruction*> env = {},
612                             uint32_t dex_pc = kNoDexPc) {
613     HNullCheck* null_check = new (GetAllocator()) HNullCheck(value, dex_pc);
614     AddOrInsertInstruction(block, null_check);
615     ManuallyBuildEnvFor(null_check, env);
616     return null_check;
617   }
618 
619   HBoundsCheck* MakeBoundsCheck(HBasicBlock* block,
620                                 HInstruction* index,
621                                 HInstruction* length,
622                                 std::initializer_list<HInstruction*> env = {},
623                                 uint32_t dex_pc = kNoDexPc) {
624     HBoundsCheck* bounds_check = new (GetAllocator()) HBoundsCheck(index, length, dex_pc);
625     AddOrInsertInstruction(block, bounds_check);
626     ManuallyBuildEnvFor(bounds_check, env);
627     return bounds_check;
628   }
629 
630   HVecStore* MakeVecStore(HBasicBlock* block,
631                           HInstruction* base,
632                           HInstruction* index,
633                           HInstruction* value,
634                           DataType::Type packed_type,
635                           size_t vector_size_in_bytes = kDefaultTestVectorSizeInBytes,
636                           uint32_t dex_pc = kNoDexPc) {
637     size_t num_of_elements = GetNumberOfElementsInVector(vector_size_in_bytes, packed_type);
638     SideEffects side_effects = SideEffects::ArrayWriteOfType(packed_type);
639     HVecStore* vec_store = new (GetAllocator()) HVecStore(
640         GetAllocator(), base, index, value, packed_type, side_effects, num_of_elements, dex_pc);
641     AddOrInsertInstruction(block, vec_store);
642     return vec_store;
643   }
644 
645   HVecPredSetAll* MakeVecPredSetAll(HBasicBlock* block,
646                                     HInstruction* input,
647                                     DataType::Type packed_type,
648                                     size_t vector_size_in_bytes = kDefaultTestVectorSizeInBytes,
649                                     uint32_t dex_pc = kNoDexPc) {
650     size_t num_of_elements = GetNumberOfElementsInVector(vector_size_in_bytes, packed_type);
651     HVecPredSetAll* predicate = new (GetAllocator()) HVecPredSetAll(
652         GetAllocator(), input, packed_type, num_of_elements, dex_pc);
653     AddOrInsertInstruction(block, predicate);
654     return predicate;
655   }
656 
657   HVecReplicateScalar* MakeVecReplicateScalar(
658       HBasicBlock* block,
659       HInstruction* scalar,
660       DataType::Type packed_type,
661       size_t vector_size_in_bytes = kDefaultTestVectorSizeInBytes,
662       HVecPredSetOperation* predicate = nullptr,
663       uint32_t dex_pc = kNoDexPc) {
664     size_t num_of_elements = GetNumberOfElementsInVector(vector_size_in_bytes, packed_type);
665     HVecReplicateScalar* vec_replicate_scalar = new (GetAllocator()) HVecReplicateScalar(
666         GetAllocator(), scalar, packed_type, num_of_elements, dex_pc);
667     AddOrInsertInstruction(block, vec_replicate_scalar);
668     if (predicate != nullptr) {
669       vec_replicate_scalar->SetMergingGoverningPredicate(predicate);
670     }
671     return vec_replicate_scalar;
672   }
673 
674   HVecPredToBoolean* MakeVecPredToBoolean(
675       HBasicBlock* block,
676       HInstruction* input,
677       HVecPredToBoolean::PCondKind pred_cond,
678       DataType::Type packed_type,
679       size_t vector_size_in_bytes = kDefaultTestVectorSizeInBytes,
680       uint32_t dex_pc = kNoDexPc) {
681     size_t num_of_elements = GetNumberOfElementsInVector(vector_size_in_bytes, packed_type);
682     HVecPredToBoolean* vec_pred_to_boolean = new (GetAllocator()) HVecPredToBoolean(
683         GetAllocator(),
684         input,
685         pred_cond,
686         packed_type,
687         num_of_elements,
688         dex_pc);
689     AddOrInsertInstruction(block, vec_pred_to_boolean);
690     return vec_pred_to_boolean;
691   }
692 
693   HVecPredWhile* MakeVecPredWhile(HBasicBlock* block,
694                                   HInstruction* left,
695                                   HInstruction* right,
696                                   HVecPredWhile::CondKind cond,
697                                   DataType::Type packed_type,
698                                   size_t vector_size_in_bytes = kDefaultTestVectorSizeInBytes,
699                                   uint32_t dex_pc = kNoDexPc) {
700     size_t num_of_elements = GetNumberOfElementsInVector(vector_size_in_bytes, packed_type);
701     HVecPredWhile* vec_pred_while = new (GetAllocator()) HVecPredWhile(
702         GetAllocator(),
703         left,
704         right,
705         cond,
706         packed_type,
707         num_of_elements,
708         dex_pc);
709     AddOrInsertInstruction(block, vec_pred_while);
710     return vec_pred_while;
711   }
712 
713   HInvokeStaticOrDirect* MakeInvokeStatic(HBasicBlock* block,
714                                           DataType::Type return_type,
715                                           const std::vector<HInstruction*>& args,
716                                           std::initializer_list<HInstruction*> env = {},
717                                           uint32_t dex_pc = kNoDexPc) {
718     MethodReference method_reference{/* file= */ &graph_->GetDexFile(), /* index= */ method_idx_++};
719     size_t num_64bit_args = std::count_if(args.begin(), args.end(), [](HInstruction* insn) {
720       return DataType::Is64BitType(insn->GetType());
721     });
722     HInvokeStaticOrDirect* invoke = new (GetAllocator())
723         HInvokeStaticOrDirect(GetAllocator(),
724                               args.size(),
725                               /* number_of_out_vregs= */ args.size() + num_64bit_args,
726                               return_type,
727                               dex_pc,
728                               method_reference,
729                               /* resolved_method= */ nullptr,
730                               HInvokeStaticOrDirect::DispatchInfo{},
731                               InvokeType::kStatic,
732                               /* resolved_method_reference= */ method_reference,
733                               HInvokeStaticOrDirect::ClinitCheckRequirement::kNone,
734                               !graph_->IsDebuggable());
735     for (auto [ins, idx] : ZipCount(MakeIterationRange(args))) {
736       invoke->SetRawInputAt(idx, ins);
737     }
738     AddOrInsertInstruction(block, invoke);
739     ManuallyBuildEnvFor(invoke, env);
740     return invoke;
741   }
742 
743   template <typename Type>
744   Type* MakeBinOp(HBasicBlock* block,
745                   DataType::Type result_type,
746                   HInstruction* left,
747                   HInstruction* right,
748                   uint32_t dex_pc = kNoDexPc) {
749     static_assert(std::is_base_of_v<HBinaryOperation, Type>);
750     Type* insn = new (GetAllocator()) Type(result_type, left, right, dex_pc);
751     AddOrInsertInstruction(block, insn);
752     return insn;
753   }
754 
755   HCondition* MakeCondition(HBasicBlock* block,
756                             IfCondition cond,
757                             HInstruction* first,
758                             HInstruction* second,
759                             uint32_t dex_pc = kNoDexPc) {
760     HCondition* condition = HCondition::Create(graph_, cond, first, second, dex_pc);
761     AddOrInsertInstruction(block, condition);
762     return condition;
763   }
764 
765   HVecCondition* MakeVecCondition(HBasicBlock* block,
766                                   IfCondition cond,
767                                   HInstruction* first,
768                                   HInstruction* second,
769                                   DataType::Type packed_type,
770                                   size_t vector_size_in_bytes = kDefaultTestVectorSizeInBytes,
771                                   HVecPredSetOperation* predicate = nullptr,
772                                   uint32_t dex_pc = kNoDexPc) {
773     size_t num_of_elements = GetNumberOfElementsInVector(vector_size_in_bytes, packed_type);
774     HVecCondition* condition = HVecCondition::Create(graph_,
775                                                      cond,
776                                                      first,
777                                                      second,
778                                                      packed_type,
779                                                      num_of_elements,
780                                                      dex_pc);
781     AddOrInsertInstruction(block, condition);
782     if (predicate != nullptr) {
783       condition->SetMergingGoverningPredicate(predicate);
784     }
785     return condition;
786   }
787 
788   HSelect* MakeSelect(HBasicBlock* block,
789                       HInstruction* condition,
790                       HInstruction* true_value,
791                       HInstruction* false_value,
792                       uint32_t dex_pc = kNoDexPc) {
793     HSelect* select = new (GetAllocator()) HSelect(condition, true_value, false_value, dex_pc);
794     AddOrInsertInstruction(block, select);
795     return select;
796   }
797 
798   HSuspendCheck* MakeSuspendCheck(HBasicBlock* block,
799                                   std::initializer_list<HInstruction*> env = {},
800                                   uint32_t dex_pc = kNoDexPc) {
801     HSuspendCheck* suspend_check = new (GetAllocator()) HSuspendCheck(dex_pc);
802     AddOrInsertInstruction(block, suspend_check);
803     ManuallyBuildEnvFor(suspend_check, env);
804     return suspend_check;
805   }
806 
AddOrInsertInstruction(HBasicBlock * block,HInstruction * instruction)807   void AddOrInsertInstruction(HBasicBlock* block, HInstruction* instruction) {
808     CHECK(!instruction->IsControlFlow());
809     if (block->GetLastInstruction() != nullptr && block->GetLastInstruction()->IsControlFlow()) {
810       block->InsertInstructionBefore(instruction, block->GetLastInstruction());
811     } else {
812       block->AddInstruction(instruction);
813     }
814   }
815 
816   HIf* MakeIf(HBasicBlock* block, HInstruction* cond, uint32_t dex_pc = kNoDexPc) {
817     HIf* if_insn = new (GetAllocator()) HIf(cond, dex_pc);
818     block->AddInstruction(if_insn);
819     return if_insn;
820   }
821 
822   HGoto* MakeGoto(HBasicBlock* block, uint32_t dex_pc = kNoDexPc) {
823     HGoto* goto_insn = new (GetAllocator()) HGoto(dex_pc);
824     block->AddInstruction(goto_insn);
825     return goto_insn;
826   }
827 
828   HReturnVoid* MakeReturnVoid(HBasicBlock* block, uint32_t dex_pc = kNoDexPc) {
829     HReturnVoid* return_void = new (GetAllocator()) HReturnVoid(dex_pc);
830     block->AddInstruction(return_void);
831     return return_void;
832   }
833 
834   HReturn* MakeReturn(HBasicBlock* block, HInstruction* value, uint32_t dex_pc = kNoDexPc) {
835     HReturn* return_insn = new (GetAllocator()) HReturn(value, dex_pc);
836     block->AddInstruction(return_insn);
837     return return_insn;
838   }
839 
MakeExit(HBasicBlock * exit_block)840   HExit* MakeExit(HBasicBlock* exit_block) {
841     HExit* exit = new (GetAllocator()) HExit();
842     exit_block->AddInstruction(exit);
843     return exit;
844   }
845 
MakePhi(HBasicBlock * block,const std::vector<HInstruction * > & ins)846   HPhi* MakePhi(HBasicBlock* block, const std::vector<HInstruction*>& ins) {
847     EXPECT_GE(ins.size(), 2u) << "Phi requires at least 2 inputs";
848     DataType::Type type = DataType::Kind(ins[0]->GetType());
849     HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), kNoRegNumber, ins.size(), type);
850     for (auto [i, idx] : ZipCount(MakeIterationRange(ins))) {
851       phi->SetRawInputAt(idx, i);
852     }
853     block->AddPhi(phi);
854     return phi;
855   }
856 
MakeLinearLoopVar(HBasicBlock * header,HBasicBlock * body,int32_t initial,int32_t increment)857   std::tuple<HPhi*, HAdd*> MakeLinearLoopVar(HBasicBlock* header,
858                                              HBasicBlock* body,
859                                              int32_t initial,
860                                              int32_t increment) {
861     HInstruction* initial_const = graph_->GetIntConstant(initial);
862     HInstruction* increment_const = graph_->GetIntConstant(increment);
863     return MakeLinearLoopVar(header, body, initial_const, increment_const);
864   }
865 
MakeLinearLoopVar(HBasicBlock * header,HBasicBlock * body,HInstruction * initial,HInstruction * increment)866   std::tuple<HPhi*, HAdd*> MakeLinearLoopVar(HBasicBlock* header,
867                                              HBasicBlock* body,
868                                              HInstruction* initial,
869                                              HInstruction* increment) {
870     HPhi* phi = MakePhi(header, {initial, /* placeholder */ initial});
871     HAdd* add = MakeBinOp<HAdd>(body, phi->GetType(), phi, increment);
872     phi->ReplaceInput(add, 1u);  // Update back-edge input.
873     return {phi, add};
874   }
875 
DefaultTypeIndexForType(DataType::Type type)876   dex::TypeIndex DefaultTypeIndexForType(DataType::Type type) {
877     switch (type) {
878       case DataType::Type::kBool:
879         return dex::TypeIndex(1);
880       case DataType::Type::kUint8:
881       case DataType::Type::kInt8:
882         return dex::TypeIndex(2);
883       case DataType::Type::kUint16:
884       case DataType::Type::kInt16:
885         return dex::TypeIndex(3);
886       case DataType::Type::kUint32:
887       case DataType::Type::kInt32:
888         return dex::TypeIndex(4);
889       case DataType::Type::kUint64:
890       case DataType::Type::kInt64:
891         return dex::TypeIndex(5);
892       case DataType::Type::kReference:
893         return dex::TypeIndex(6);
894       case DataType::Type::kFloat32:
895         return dex::TypeIndex(7);
896       case DataType::Type::kFloat64:
897         return dex::TypeIndex(8);
898       case DataType::Type::kVoid:
899         EXPECT_TRUE(false) << "No type for void!";
900         return dex::TypeIndex(1000);
901     }
902   }
903 
904   // Creates a parameter. The instruction is automatically added to the entry-block.
905   HParameterValue* MakeParam(DataType::Type type, std::optional<dex::TypeIndex> ti = std::nullopt) {
906     HParameterValue* val = new (GetAllocator()) HParameterValue(
907         graph_->GetDexFile(), ti ? *ti : DefaultTypeIndexForType(type), param_count_++, type);
908     AddOrInsertInstruction(graph_->GetEntryBlock(), val);
909     return val;
910   }
911 
912  protected:
CheckGraph(HGraph * graph,std::ostream & oss)913   bool CheckGraph(HGraph* graph, std::ostream& oss) {
914     GraphChecker checker(graph);
915     checker.Run();
916     checker.Dump(oss);
917     return checker.IsValid();
918   }
919 
920   std::vector<std::unique_ptr<const StandardDexFile>> dex_files_;
921   std::unique_ptr<ArenaPoolAndAllocator> pool_and_allocator_;
922 
923   HGraph* graph_;
924   HBasicBlock* entry_block_;
925   HBasicBlock* exit_block_;
926 
927   size_t param_count_ = 0;
928   size_t class_idx_ = 42;
929   uint32_t method_idx_ = 100;
930 
931   // The default size of vectors to use for tests, in bytes. 16 bytes (128 bits) is used as it is
932   // commonly the smallest size of vector used in vector extensions.
933   static constexpr size_t kDefaultTestVectorSizeInBytes = 16;
934 
935   ScopedNullHandle<mirror::Class> null_klass_;
936 };
937 
938 class OptimizingUnitTest : public CommonArtTest, public OptimizingUnitTestHelper {};
939 
940 // Naive string diff data type.
941 using diff_t = std::list<std::pair<std::string, std::string>>;
942 
943 // An alias for the empty string used to make it clear that a line is
944 // removed in a diff.
945 static const std::string removed = "";  // NOLINT [runtime/string] [4]
946 
947 // Naive patch command: apply a diff to a string.
Patch(const std::string & original,const diff_t & diff)948 inline std::string Patch(const std::string& original, const diff_t& diff) {
949   std::string result = original;
950   for (const auto& p : diff) {
951     std::string::size_type pos = result.find(p.first);
952     DCHECK_NE(pos, std::string::npos)
953         << "Could not find: \"" << p.first << "\" in \"" << result << "\"";
954     result.replace(pos, p.first.size(), p.second);
955   }
956   return result;
957 }
958 
959 // Returns if the instruction is removed from the graph.
IsRemoved(HInstruction * instruction)960 inline bool IsRemoved(HInstruction* instruction) {
961   return instruction->GetBlock() == nullptr;
962 }
963 
964 inline std::ostream& operator<<(std::ostream& oss, const AdjacencyListGraph& alg) {
965   return alg.Dump(oss);
966 }
967 
968 class PatternMatchGraphVisitor final : public HGraphVisitor {
969  private:
970   struct HandlerWrapper {
971    public:
~HandlerWrapperHandlerWrapper972     virtual ~HandlerWrapper() {}
973     virtual void operator()(HInstruction* h) = 0;
974   };
975 
976   template <HInstruction::InstructionKind kKind, typename F>
977   struct KindWrapper;
978 
979 #define GEN_HANDLER(nm, unused)                                                         \
980   template <typename F>                                                                 \
981   struct KindWrapper<HInstruction::InstructionKind::k##nm, F> : public HandlerWrapper { \
982    public:                                                                              \
983     explicit KindWrapper(F f) : f_(f) {}                                                \
984     void operator()(HInstruction* h) override {                                         \
985       if constexpr (std::is_invocable_v<F, H##nm*>) {                                   \
986         f_(h->As##nm());                                                                \
987       } else {                                                                          \
988         LOG(FATAL) << "Incorrect call with " << #nm;                                    \
989       }                                                                                 \
990     }                                                                                   \
991                                                                                         \
992    private:                                                                             \
993     F f_;                                                                               \
994   };
995 
FOR_EACH_CONCRETE_INSTRUCTION(GEN_HANDLER)996   FOR_EACH_CONCRETE_INSTRUCTION(GEN_HANDLER)
997 #undef GEN_HANDLER
998 
999   template <typename F>
1000   std::unique_ptr<HandlerWrapper> GetWrapper(HInstruction::InstructionKind kind, F f) {
1001     switch (kind) {
1002 #define GEN_GETTER(nm, unused)               \
1003   case HInstruction::InstructionKind::k##nm: \
1004     return std::unique_ptr<HandlerWrapper>(  \
1005         new KindWrapper<HInstruction::InstructionKind::k##nm, F>(f));
1006       FOR_EACH_CONCRETE_INSTRUCTION(GEN_GETTER)
1007 #undef GEN_GETTER
1008       default:
1009         LOG(FATAL) << "Unable to handle kind " << kind;
1010         return nullptr;
1011     }
1012   }
1013 
1014  public:
1015   template <typename... Inst>
PatternMatchGraphVisitor(HGraph * graph,Inst...handlers)1016   explicit PatternMatchGraphVisitor(HGraph* graph, Inst... handlers) : HGraphVisitor(graph) {
1017     FillHandlers(handlers...);
1018   }
1019 
VisitInstruction(HInstruction * instruction)1020   void VisitInstruction(HInstruction* instruction) override {
1021     auto& h = handlers_[instruction->GetKind()];
1022     if (h.get() != nullptr) {
1023       (*h)(instruction);
1024     }
1025   }
1026 
1027  private:
1028   template <typename Func>
GetKind()1029   constexpr HInstruction::InstructionKind GetKind() {
1030 #define CHECK_INST(nm, unused)                       \
1031     if constexpr (std::is_invocable_v<Func, H##nm*>) { \
1032       return HInstruction::InstructionKind::k##nm;     \
1033     }
1034     FOR_EACH_CONCRETE_INSTRUCTION(CHECK_INST);
1035 #undef CHECK_INST
1036     static_assert(!std::is_invocable_v<Func, HInstruction*>,
1037                   "Use on generic HInstruction not allowed");
1038 #define STATIC_ASSERT_ABSTRACT(nm, unused) && !std::is_invocable_v<Func, H##nm*>
1039     static_assert(true FOR_EACH_ABSTRACT_INSTRUCTION(STATIC_ASSERT_ABSTRACT),
1040                   "Must not be abstract instruction");
1041 #undef STATIC_ASSERT_ABSTRACT
1042 #define STATIC_ASSERT_CONCRETE(nm, unused) || std::is_invocable_v<Func, H##nm*>
1043     static_assert(false FOR_EACH_CONCRETE_INSTRUCTION(STATIC_ASSERT_CONCRETE),
1044                   "Must be a concrete instruction");
1045 #undef STATIC_ASSERT_CONCRETE
1046     return HInstruction::InstructionKind::kLastInstructionKind;
1047   }
1048   template <typename First>
FillHandlers(First h1)1049   void FillHandlers(First h1) {
1050     HInstruction::InstructionKind type = GetKind<First>();
1051     CHECK_NE(type, HInstruction::kLastInstructionKind)
1052         << "Unknown instruction kind. Only concrete ones please.";
1053     handlers_[type] = GetWrapper(type, h1);
1054   }
1055 
1056   template <typename First, typename... Inst>
FillHandlers(First h1,Inst...handlers)1057   void FillHandlers(First h1, Inst... handlers) {
1058     FillHandlers(h1);
1059     FillHandlers<Inst...>(handlers...);
1060   }
1061 
1062   std::array<std::unique_ptr<HandlerWrapper>, HInstruction::InstructionKind::kLastInstructionKind>
1063       handlers_;
1064 };
1065 
1066 template <typename... Target>
1067 std::tuple<std::vector<Target*>...> FindAllInstructions(
1068     HGraph* graph,
1069     std::variant<std::nullopt_t, HBasicBlock*, std::initializer_list<HBasicBlock*>> blks =
1070         std::nullopt) {
1071   std::tuple<std::vector<Target*>...> res;
1072   PatternMatchGraphVisitor vis(
1073       graph, [&](Target* t) { std::get<std::vector<Target*>>(res).push_back(t); }...);
1074 
1075   if (std::holds_alternative<std::initializer_list<HBasicBlock*>>(blks)) {
1076     for (HBasicBlock* blk : std::get<std::initializer_list<HBasicBlock*>>(blks)) {
1077       vis.VisitBasicBlock(blk);
1078     }
1079   } else if (std::holds_alternative<std::nullopt_t>(blks)) {
1080     vis.VisitInsertionOrder();
1081   } else {
1082     vis.VisitBasicBlock(std::get<HBasicBlock*>(blks));
1083   }
1084   return res;
1085 }
1086 
1087 template <typename... Target>
1088 std::tuple<Target*...> FindSingleInstructions(
1089     HGraph* graph,
1090     std::variant<std::nullopt_t, HBasicBlock*, std::initializer_list<HBasicBlock*>> blks =
1091         std::nullopt) {
1092   std::tuple<Target*...> res;
1093   PatternMatchGraphVisitor vis(graph, [&](Target* t) {
1094     EXPECT_EQ(std::get<Target*>(res), nullptr)
1095         << *std::get<Target*>(res) << " already found but found " << *t << "!";
1096     std::get<Target*>(res) = t;
1097   }...);
1098   if (std::holds_alternative<std::initializer_list<HBasicBlock*>>(blks)) {
1099     for (HBasicBlock* blk : std::get<std::initializer_list<HBasicBlock*>>(blks)) {
1100       vis.VisitBasicBlock(blk);
1101     }
1102   } else if (std::holds_alternative<std::nullopt_t>(blks)) {
1103     vis.VisitInsertionOrder();
1104   } else {
1105     vis.VisitBasicBlock(std::get<HBasicBlock*>(blks));
1106   }
1107   return res;
1108 }
1109 
1110 template <typename Target>
1111 Target* FindSingleInstruction(
1112     HGraph* graph,
1113     std::variant<std::nullopt_t, HBasicBlock*, std::initializer_list<HBasicBlock*>> blks =
1114         std::nullopt) {
1115   return std::get<Target*>(FindSingleInstructions<Target>(graph, blks));
1116 }
1117 
1118 }  // namespace art
1119 
1120 #endif  // ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_
1121