• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "compiler_options.h"
17 #include "graph_cloner.h"
18 #include "compiler_options.h"
19 #include "optimizer/analysis/dominators_tree.h"
20 #include "optimizer/analysis/rpo.h"
21 #include "optimizer/analysis/linear_order.h"
22 #include "optimizer/analysis/loop_analyzer.h"
23 #include "optimizer/ir/datatype.h"
24 #include "optimizer/optimizations/cleanup.h"
25 #include "inst_checker_gen.h"
26 #include "graph_checker.h"
27 
28 namespace panda::compiler {
29 
GraphChecker(Graph * graph)30 GraphChecker::GraphChecker(Graph *graph)
31 {
32     PreCloneChecks(graph);
33     graph_ = GraphCloner(graph, GetAllocator(), GetLocalAllocator()).CloneGraph();
34     GetGraph()->GetPassManager()->SetCheckMode(true);
35 }
36 
PreCloneChecks(Graph * graph)37 void GraphChecker::PreCloneChecks(Graph *graph)
38 {
39     UserInputCheck(graph);
40 }
41 
UserInputCheck(Graph * graph)42 void GraphChecker::UserInputCheck(Graph *graph)
43 {
44     for (auto block : graph->GetVectorBlocks()) {
45         if (block == nullptr) {
46             continue;
47         }
48         for (auto inst : block->AllInsts()) {
49             auto u = inst->GetFirstUser();
50             ASSERT(u == nullptr || u->GetPrev() == nullptr);
51             while (u != nullptr) {
52                 ASSERT(u->GetNext() == nullptr || u->GetNext()->GetPrev() == u);
53                 u = u->GetNext();
54             }
55             for (auto &user : inst->GetUsers()) {
56                 [[maybe_unused]] auto user_inst = user.GetInst();
57                 ASSERT(user_inst->GetBasicBlock() != nullptr);
58                 ASSERT_DO(CheckInstHasInput(user_inst, inst), std::cerr << "Instruction is not an input to its user\n"
59                                                                         << "input: " << *inst << std::endl
60                                                                         << "user:  " << *user_inst << std::endl);
61             }
62             for (auto &input : inst->GetInputs()) {
63                 [[maybe_unused]] auto input_inst = input.GetInst();
64                 ASSERT(input_inst != nullptr && input_inst->GetBasicBlock() != nullptr);
65                 ASSERT_DO(CheckInstHasUser(input_inst, inst), std::cerr << "Instruction is not a user to its input:\n"
66                                                                         << "user: " << *inst << std::endl
67                                                                         << "input:  " << *input_inst << std::endl);
68             }
69             // Check `require_state` flag
70             auto it = std::find_if(inst->GetInputs().begin(), inst->GetInputs().end(),
71                                    [](Input input) { return input.GetInst()->IsSaveState(); });
72             [[maybe_unused]] bool has_save_state = (it != inst->GetInputs().end());
73             ASSERT_DO(inst->RequireState() == has_save_state,
74                       std::cerr << "Incorrect 'require_state' flag in the inst: " << *inst);
75             if (inst->RequireState()) {
76                 ASSERT(it->GetInst() == inst->GetSaveState());
77             }
78         }
79     }
80 }
81 
Check()82 void GraphChecker::Check()
83 {
84     if (!GetGraph()->IsDynamicMethod()) {
85         InstChecker::Run(GetGraph());
86     }
87 
88 #ifndef NDEBUG
89     if (GetGraph()->IsAnalysisValid<DominatorsTree>()) {
90         CheckDomTree();
91     } else {
92         GetGraph()->RunPass<DominatorsTree>();
93     }
94     if (GetGraph()->IsAnalysisValid<LoopAnalyzer>()) {
95         CheckLoopAnalysis();
96     } else {
97         GetGraph()->RunPass<LoopAnalyzer>();
98     }
99     CheckStartBlock();
100     CheckEndBlock();
101     size_t blocks_count = 0;
102     size_t blocks_id = -1;
103     for (auto block : GetGraph()->GetVectorBlocks()) {
104         ++blocks_id;
105         if (block == nullptr) {
106             continue;
107         }
108         ASSERT_PRINT(block->GetGraph() == GetGraph(), "Block linked to incorrect graph");
109         ASSERT_PRINT(block->GetId() == blocks_id, "Block ID must be equal to its ID in graph vector");
110         CheckBlock(block);
111         blocks_count++;
112     }
113     ASSERT_PRINT(blocks_count == GetGraph()->GetBlocksRPO().size(), "There is disconnected block");
114     CheckLoops();
115     // Visit graph to check instructions types
116     CheckGraph();
117     // Check that call.Inlined and Return.Inlined in correct order
118     // and check that savestate has correct link to call.inlined.
119     CheckCallReturnInlined();
120     if (NeedCheckSaveState()) {
121         // Check that objects in stack.
122         CheckSaveStateInputs();
123         // Check that between savestate and it's runtime call user have not reference insts.
124         CheckSaveStatesWithRuntimeCallUsers();
125     }
126 
127 #endif  // !NDEBUG
128 }
129 
130 #ifndef NDEBUG
NeedCheckSaveState()131 bool GraphChecker::NeedCheckSaveState()
132 {
133     return !GetGraph()->IsBytecodeOptimizer() && GetGraph()->GetParentGraph() == nullptr &&
134            GetGraph()->IsInliningComplete() && !GetGraph()->IsSchedulerComplete();
135 }
136 #endif  // !NDEBUG
137 
CheckBlock(BasicBlock * block)138 void GraphChecker::CheckBlock([[maybe_unused]] BasicBlock *block)
139 {
140 #ifndef NDEBUG
141     CheckControlFlow(block);
142     CheckDataFlow(block);
143     for (auto phi_inst : block->PhiInsts()) {
144         CheckPhiInputs(phi_inst);
145     }
146     if (!GetGraph()->IsLowLevelInstructionsEnabled() && !GetGraph()->IsDynamicMethod()) {
147         CheckNoLowLevel(block);
148     }
149     if (!block->IsEndBlock() && !block->IsStartBlock()) {
150         CheckBlockEdges(*block);
151     }
152     if (block->IsTryBegin()) {
153         CheckTryBeginBlock(*block);
154     }
155     if (block->NeedsJump()) {
156         CheckJump(*block);
157     }
158 #endif  // !NDEBUG
159 }
160 
CheckControlFlow(BasicBlock * block)161 void GraphChecker::CheckControlFlow(BasicBlock *block)
162 {
163     auto num_succs = block->GetSuccsBlocks().size();
164     ASSERT_PRINT(block->IsEndBlock() || block->IsTryBegin() || block->IsTryEnd() ||
165                      (num_succs > 0 && num_succs <= MAX_SUCCS_NUM) ||
166                      block->GetLastInst()->GetOpcode() == Opcode::Throw,
167                  "Non-end block and non-try-begin block should have 1 or 2 successesors");
168 
169     for ([[maybe_unused]] auto pred : block->GetPredsBlocks()) {
170         ASSERT_PRINT(CheckBlockHasSuccessor(pred, block), "Block is not a successor to its predecessor");
171     }
172     for ([[maybe_unused]] auto succ : block->GetSuccsBlocks()) {
173         ASSERT_PRINT(CheckBlockHasPredecessor(succ, block), "Block is not a predecessor to its successor");
174     }
175 
176     if (num_succs == MAX_SUCCS_NUM) {
177         ASSERT_PRINT(block->GetSuccessor(0) != block->GetSuccessor(1), "Wrong CFG - block with two same successors");
178     }
179 
180     for ([[maybe_unused]] auto phi : block->PhiInsts()) {
181         ASSERT_DO(phi->GetInputsCount() == block->GetPredsBlocks().size(),
182                   std::cerr << phi->GetInputsCount() << " " << block->GetPredsBlocks().size()
183                             << "Incorrect phi's inputs count" << *phi);
184     }
185 }
186 
CheckDataFlow(BasicBlock * block)187 void GraphChecker::CheckDataFlow(BasicBlock *block)
188 {
189     auto graph = block->GetGraph();
190     for (auto inst : block->AllInsts()) {
191         ASSERT_DO(inst->GetBasicBlock() == block,
192                   std::cerr << "Instruction block's pointer isn't correct" << *inst << std::endl);
193         if (block != graph->GetStartBlock()) {
194             ASSERT_DO(inst->GetOpcode() != Opcode::Parameter,
195                       std::cerr << "Not entry block can't contain Parameter instructions" << *inst << std::endl);
196         }
197         if (inst->GetPrev() == nullptr) {
198             ASSERT_PRINT(*block->AllInsts().begin() == inst, "First block instruction isn't correct");
199         }
200         if (inst->GetNext() == nullptr) {
201             ASSERT_PRINT(*block->AllInstsSafeReverse().begin() == inst, "Last block instruction isn't correct");
202         }
203         auto opc = inst->GetOpcode();
204         // Inst with reference type must have no_cse and no_hoist flags.
205         if (inst->GetType() == DataType::REFERENCE && !inst->IsClassInst() && opc != Opcode::GetInstanceClass) {
206             ASSERT(inst->IsNotCseApplicable());
207             ASSERT(inst->IsNotHoistable());
208         }
209         for ([[maybe_unused]] auto &user : inst->GetUsers()) {
210             auto user_inst = user.GetInst();
211             ASSERT_DO(CheckInstHasInput(user_inst, inst), std::cerr << "Instruction is not an input to its user\n"
212                                                                     << "input: " << *inst << std::endl
213                                                                     << "user:  " << *user_inst << std::endl);
214             if (!user_inst->IsPhi() && !user_inst->IsCatchPhi()) {
215                 ASSERT_DO(inst->IsDominate(user_inst) ||
216                               (GetGraph()->IsRegAllocApplied() &&
217                                IsTryCatchDomination(inst->GetBasicBlock(), user_inst->GetBasicBlock())),
218                           std::cerr << "Instruction doesn't dominate its user\n"
219                                     << "input: " << *inst << std::endl
220                                     << "user:  " << *user_inst << std::endl);
221             }
222             auto arch = graph->GetArch();
223             if (DataType::Is32Bits(inst->GetType(), arch)) {
224                 if (!user_inst->HasType()) {
225                     continue;
226                 }
227                 // Unsigned Load in AARCH64 zerod all high bits
228 #ifndef NDEBUG
229                 if (inst->IsLoad() && !DataType::IsTypeSigned(inst->GetType()) && arch == Arch::AARCH64 &&
230                     graph->IsLowLevelInstructionsEnabled()) {
231 #else
232                 if (inst->IsLoad() && !DataType::IsTypeSigned(inst->GetType()) && arch == Arch::AARCH64) {
233 #endif  // !NDEBUG
234                     continue;
235                 }
236                 [[maybe_unused]] auto user_input_type = user_inst->GetInputType(user.GetIndex());
237                 [[maybe_unused]] bool ref_to_ptr =
238                     user_input_type == DataType::POINTER && inst->GetType() == DataType::REFERENCE;
239                 ASSERT_DO(DataType::Is32Bits(user_input_type, arch) || ref_to_ptr ||
240                               (block->GetGraph()->IsDynamicMethod() && user_input_type == DataType::ANY),
241                           std::cerr << "Undefined high-part of input instruction for its user\n"
242                                     << "input: " << *inst << std::endl
243                                     << "user:  " << *user_inst << std::endl);
244             }
245         }
246         for ([[maybe_unused]] auto input : inst->GetInputs()) {
247             ASSERT_DO(CheckInstHasUser(input.GetInst(), inst), std::cerr << "Instruction is not a user to its input:\n"
248                                                                          << "input: " << *input.GetInst() << std::endl
249                                                                          << "user:  " << *inst << std::endl);
250         }
251     }
252 }
253 
254 void GraphChecker::CheckCallReturnInlined()
255 {
256     ArenaStack<Inst *> inlined_calles(GetLocalAllocator()->Adapter());
257     for (auto block : GetGraph()->GetBlocksRPO()) {
258         for (auto inst : block->Insts()) {
259             if (inst->IsCall() && static_cast<CallInst *>(inst)->IsInlined()) {
260                 ASSERT_PRINT(inst->NoDest(), "Inlined call should have NO_DST flag");
261                 inlined_calles.push(inst);
262             } else if (inst->GetOpcode() == Opcode::ReturnInlined) {
263                 // TODO(Sergey Chernykh) fix checker
264                 if (block->GetLastInst()->GetOpcode() == Opcode::Throw ||
265                     block->GetLastInst()->GetOpcode() == Opcode::Deoptimize) {
266                     continue;
267                 }
268                 ASSERT(!inlined_calles.empty());
269                 inlined_calles.pop();
270             }
271         }
272     }
273     [[maybe_unused]] bool throw_exit = false;
274     if (GetGraph()->HasEndBlock()) {
275         for (auto block : GetGraph()->GetEndBlock()->GetPredsBlocks()) {
276             if (block->IsTryEnd()) {
277                 continue;
278             }
279             if (block->GetLastInst()->GetOpcode() == Opcode::Throw ||
280                 block->GetLastInst()->GetOpcode() == Opcode::Deoptimize) {
281                 throw_exit = true;
282                 break;
283             }
284         }
285     }
286     ASSERT(inlined_calles.empty() || throw_exit);
287 #ifndef NDEBUG
288     // avoid check after ir_builder in inline pass
289     if (!GetGraph()->IsInliningComplete() || GetGraph()->GetParentGraph() != nullptr) {
290         return;
291     }
292     for (auto block : GetGraph()->GetBlocksRPO()) {
293         for (auto inst : block->Insts()) {
294             if (inst->IsSaveState()) {
295                 CheckSaveStateCaller(static_cast<SaveStateInst *>(inst));
296             }
297         }
298     }
299 #endif
300 }
301 
302 void GraphChecker::CheckSaveStateCaller(SaveStateInst *savestate)
303 {
304     ASSERT(savestate != nullptr);
305     auto block = savestate->GetBasicBlock();
306     ArenaStack<Inst *> inlined_calles(GetLocalAllocator()->Adapter());
307     auto caller = savestate->GetCallerInst();
308     if (caller == nullptr) {
309         return;
310     }
311     ASSERT(caller->GetBasicBlock() != nullptr);
312     ASSERT(caller->GetBasicBlock()->GetGraph() == block->GetGraph());
313     auto dom_block = block;
314     bool skip = true;
315     for (auto inst : dom_block->InstsSafeReverse()) {
316         if (inst == savestate) {
317             skip = false;
318         }
319         if (skip) {
320             continue;
321         }
322         if (inst->GetOpcode() == Opcode::ReturnInlined) {
323             inlined_calles.push(inst);
324         } else if (inst->IsCall() && static_cast<CallInst *>(inst)->IsInlined()) {
325             if (!inlined_calles.empty()) {
326                 inlined_calles.pop();
327             } else {
328                 ASSERT(caller == inst);
329                 return;
330             }
331         }
332     }
333     dom_block = dom_block->GetDominator();
334     while (dom_block != nullptr) {
335         for (auto inst : dom_block->InstsSafeReverse()) {
336             if (inst->GetOpcode() == Opcode::ReturnInlined) {
337                 inlined_calles.push(inst);
338             } else if (inst->IsCall() && static_cast<CallInst *>(inst)->IsInlined()) {
339                 if (!inlined_calles.empty()) {
340                     inlined_calles.pop();
341                 } else {
342                     ASSERT(caller == inst);
343                     return;
344                 }
345             }
346         }
347         dom_block = dom_block->GetDominator();
348     }
349     UNREACHABLE();
350 }
351 
352 void GraphChecker::CheckStartBlock()
353 {
354     [[maybe_unused]] Inst *has_nullptr = nullptr;
355     [[maybe_unused]] int32_t last_num = -1;
356     ASSERT(GetGraph()->GetStartBlock());
357     ASSERT_PRINT(GetGraph()->GetStartBlock()->GetPredsBlocks().empty(), "Start block can't have predecessors");
358     ASSERT_PRINT(GetGraph()->GetStartBlock()->GetSuccsBlocks().size() == 1, "Start block should have one successor");
359     for (auto inst : GetGraph()->GetStartBlock()->AllInsts()) {
360         [[maybe_unused]] Opcode opc = inst->GetOpcode();
361         ASSERT_DO(
362             opc == Opcode::Constant || opc == Opcode::Parameter || opc == Opcode::SafePoint ||
363                 opc == Opcode::SpillFill || opc == Opcode::NullPtr || opc == Opcode::NOP || opc == Opcode::LiveIn,
364             std::cerr
365                 << "Entry block can contain Constant, Parameter, NullPtr, SafePoint, NOP or SpillFill instructions"
366                 << *inst << std::endl);
367         if (opc == Opcode::Parameter) {
368             auto arg_num = inst->CastToParameter()->GetArgNumber();
369             ASSERT_DO(
370                 last_num < static_cast<int32_t>(arg_num),
371                 std::cerr << "The argument number in the parameter must be greater than that of the previous parameter"
372                           << *inst << std::endl);
373             last_num = static_cast<int32_t>(arg_num);
374         }
375         if (opc == Opcode::NullPtr) {
376             ASSERT_PRINT(has_nullptr == nullptr, "There should be not more than one NullPtr instruction");
377             has_nullptr = inst;
378         }
379     }
380 }
381 
382 void GraphChecker::CheckEndBlock()
383 {
384     if (!GetGraph()->HasEndBlock()) {
385         ASSERT_PRINT(HasOuterInfiniteLoop(), "Graph without infinite loops should have end block");
386         return;
387     }
388     ASSERT_PRINT(GetGraph()->GetEndBlock()->GetSuccsBlocks().empty(), "End block can't have successors");
389     [[maybe_unused]] auto iter = GetGraph()->GetEndBlock()->Insts();
390     ASSERT_PRINT(iter.begin() == iter.end(), "End block can't have instructions");
391 }
392 
393 void GraphChecker::CheckGraph()
394 {
395     size_t num_inst = GetGraph()->GetCurrentInstructionId();
396     ArenaVector<bool> inst_vec(num_inst, GetLocalAllocator()->Adapter());
397     for (auto &bb : GetGraph()->GetBlocksRPO()) {
398         for (auto inst : bb->AllInsts()) {
399             auto id = inst->GetId();
400             ASSERT_DO(id < num_inst,
401                       (std::cerr << "Instruction ID must be less than graph instruction counter: " << num_inst << "\n",
402                        inst->Dump(&std::cerr)));
403             ASSERT_DO(!inst_vec[id],
404                       (std::cerr << "Instruction with same Id already exists:\n", inst->Dump(&std::cerr)));
405             inst_vec[id] = true;
406             ASSERT_DO(GetGraph()->IsDynamicMethod() || inst->GetType() != DataType::ANY,
407                       (std::cerr << "The type ANY is supported only for dynamic languages\n", inst->Dump(&std::cerr)));
408             ASSERT_DO(inst->SupportsMode(GetGraph()->GetCompilerMode()),
409                       (std::cerr << "Instruction used in wrong mode\n", inst->Dump(&std::cerr)));
410             VisitInstruction(inst);
411         }
412     }
413 }
414 
415 void GraphChecker::CheckPhiInputs(Inst *phi_inst)
416 {
417     for (size_t index = 0; index < phi_inst->GetInputsCount(); ++index) {
418         [[maybe_unused]] auto pred = phi_inst->CastToPhi()->GetPhiInputBb(index);
419         [[maybe_unused]] auto input_bb = phi_inst->CastToPhi()->GetPhiInput(pred)->GetBasicBlock();
420         ASSERT_DO(input_bb->IsDominate(pred) || IsTryCatchDomination(input_bb, pred),
421                   (std::cerr
422                    << "Block where phi-input is located should dominate predecessor block corresponding to this input\n"
423                    << "Block " << input_bb->GetId() << " should dominate " << pred->GetId() << std::endl
424                    << *phi_inst));
425     }
426 }
427 
428 bool GraphChecker::CheckInstRegUsageSaved(const Inst *inst, Register reg) const
429 {
430     if (reg == ACC_REG_ID) {
431         return true;
432     }
433     auto graph = inst->GetBasicBlock()->GetGraph();
434     // Empty vector regs mask means we are using dynamic general regs set.
435     if (DataType::IsFloatType(inst->GetType()) && !graph->GetUsedRegs<DataType::FLOAT64>()->empty()) {
436         return graph->GetUsedRegs<DataType::FLOAT64>()->at(reg);
437     }
438     return graph->GetUsedRegs<DataType::INT64>()->at(reg);
439 }
440 
441 [[maybe_unused]] static bool checkSpillFillMultiple(const compiler::Inst *inst)
442 {
443     switch (inst->GetOpcode()) {
444         case Opcode::Parameter:
445             return false;
446         case Opcode::LoadObject:
447         case Opcode::NewArray:
448         case Opcode::NewObject:
449             // In this case for BytecodeOptimizer SpillFill will be added after instruction, not before
450             // user-insturction. So this check can't find it and it is skipped.
451             return !inst->GetBasicBlock()->GetGraph()->IsBytecodeOptimizer();
452         default:
453             return true;
454     }
455 }
456 
457 void GraphChecker::CheckNoLowLevel(BasicBlock *block)
458 {
459     for ([[maybe_unused]] auto inst : block->Insts()) {
460         ASSERT_DO(!inst->IsLowLevel(), inst->Dump(&std::cerr));
461     }
462 }
463 
464 void GraphChecker::MarkBlocksInLoop(Loop *loop, Marker mrk)
465 {
466     ASSERT(loop->IsIrreducible() || loop->IsRoot() || loop->GetHeader() != nullptr);
467     ASSERT(loop->IsIrreducible() || loop->IsRoot() || loop->IsTryCatchLoop() || loop->GetPreHeader() != nullptr);
468     // Mark blocks and check if marker was not set before
469     for ([[maybe_unused]] auto block : loop->GetBlocks()) {
470         ASSERT(!block->SetMarker(mrk));
471     }
472 
473     for (auto inner : loop->GetInnerLoops()) {
474         MarkBlocksInLoop(inner, mrk);
475     }
476 }
477 
478 bool GraphChecker::CheckBlockHasPredecessor(BasicBlock *block, BasicBlock *predecessor)
479 {
480     ASSERT(block != nullptr && predecessor != nullptr);
481     for (auto pred : block->GetPredsBlocks()) {
482         if (pred == predecessor) {
483             return true;
484         }
485     }
486     return false;
487 }
488 
489 bool GraphChecker::CheckBlockHasSuccessor(BasicBlock *block, BasicBlock *successor)
490 {
491     ASSERT(block != nullptr && successor != nullptr);
492     for (auto succ : block->GetSuccsBlocks()) {
493         if (succ == successor) {
494             return true;
495         }
496     }
497     return false;
498 }
499 
500 bool GraphChecker::BlockContainsInstruction(BasicBlock *block, Opcode opcode)
501 {
502     return std::find_if(block->Insts().begin(), block->Insts().end(),
503                         [opcode](Inst *inst) { return inst->GetOpcode() == opcode; }) != block->Insts().end();
504 }
505 
506 void GraphChecker::CheckLoopHasSafePoint(Loop *loop)
507 {
508     [[maybe_unused]] auto it =
509         std::find_if(loop->GetBlocks().begin(), loop->GetBlocks().end(),
510                      [this](BasicBlock *block) { return BlockContainsInstruction(block, Opcode::SafePoint); });
511     // Irreducible isn't fully populated - only 'one of the headers' and back-edge,
512     // SafePoint can be inserted to the another 'header' and search will be failed
513     ASSERT_DO(loop->IsTryCatchLoop() || loop->IsIrreducible() || it != loop->GetBlocks().end(),
514               std::cerr << "Loop " << loop->GetId() << " must have safepoint\n");
515     for (auto inner : loop->GetInnerLoops()) {
516         CheckLoopHasSafePoint(inner);
517     }
518 }
519 
520 void GraphChecker::CheckLoops()
521 {
522     ASSERT(GetGraph()->GetAnalysis<LoopAnalyzer>().IsValid());
523     ASSERT(GetGraph()->GetRootLoop() != nullptr);
524     ASSERT(GetGraph()->GetRootLoop()->IsRoot());
525     ASSERT(GetGraph()->GetRootLoop()->GetHeader() == nullptr);
526     ASSERT(GetGraph()->GetRootLoop()->GetPreHeader() == nullptr);
527     auto root_loop = GetGraph()->GetRootLoop();
528     auto mrk = GetGraph()->NewMarker();
529     MarkBlocksInLoop(root_loop, mrk);
530 
531     for ([[maybe_unused]] auto block : GetGraph()->GetBlocksRPO()) {
532         [[maybe_unused]] auto loop = block->GetLoop();
533         ASSERT(loop != nullptr);
534         ASSERT(block->IsMarked(mrk));
535         if (block->IsLoopHeader()) {
536             if (block->IsOsrEntry()) {
537                 ASSERT(GetGraph()->IsOsrMode());
538                 auto ss_osr = block->GetFirstInst();
539                 while (ss_osr != nullptr && (ss_osr->IsCatchPhi() || ss_osr->GetOpcode() == Opcode::Try)) {
540                     ss_osr = ss_osr->GetNext();
541                 }
542                 ASSERT(ss_osr != nullptr && ss_osr->GetOpcode() == Opcode::SaveStateOsr);
543             }
544             [[maybe_unused]] auto preds = block->GetPredsBlocks();
545             for ([[maybe_unused]] auto pred : preds) {
546                 ASSERT(pred->GetLoop() != loop || loop->HasBackEdge(pred));
547             }
548 
549             if (!loop->IsIrreducible()) {
550                 for ([[maybe_unused]] auto back : loop->GetBackEdges()) {
551                     ASSERT(std::find(preds.begin(), preds.end(), back) != preds.end());
552                 }
553             }
554         } else {
555             ASSERT(!block->IsOsrEntry());
556         }
557     }
558     GetGraph()->EraseMarker(mrk);
559     if (options.IsCompilerUseSafepoint() && GetGraph()->SupportManagedCode()) {
560         for (auto inner : root_loop->GetInnerLoops()) {
561             CheckLoopHasSafePoint(inner);
562         }
563     }
564 }
565 
566 void GraphChecker::CheckDomTree()
567 {
568     ASSERT(GetGraph()->GetAnalysis<DominatorsTree>().IsValid());
569     ArenaVector<BasicBlock *> dominators(GetGraph()->GetVectorBlocks().size(), GetLocalAllocator()->Adapter());
570     for (auto block : GetGraph()->GetBlocksRPO()) {
571         dominators[block->GetId()] = block->GetDominator();
572     }
573     // Rebuild dom-tree
574     GetGraph()->InvalidateAnalysis<DominatorsTree>();
575     GetGraph()->RunPass<DominatorsTree>();
576 
577     for ([[maybe_unused]] auto block : GetGraph()->GetBlocksRPO()) {
578         ASSERT_DO(dominators[block->GetId()] == block->GetDominator(),
579                   std::cerr << "Basic block with id " << block->GetId() << " has incorrect dominator with id "
580                             << dominators[block->GetId()]->GetId() << std::endl
581                             << "Correct dominator must be block with id " << block->GetDominator()->GetId() << std::endl
582                             << "Note: basic blocks' ids in the original graph and in the cloned graph can be different"
583                             << std::endl);
584     }
585 }
586 
587 void GraphChecker::CheckLoopAnalysis()
588 {
589     // Save current loop info
590     ArenaUnorderedMap<BasicBlock *, Loop *> loops(GetLocalAllocator()->Adapter());
591     [[maybe_unused]] auto root_loop = GetGraph()->GetRootLoop();
592     for (auto block : GetGraph()->GetBlocksRPO()) {
593         if (block->IsLoopHeader()) {
594             loops.emplace(block, block->GetLoop());
595         }
596     }
597     // Build new loop info and compare with saved one
598     GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
599     GetGraph()->RunPass<LoopAnalyzer>();
600     ASSERT_PRINT(*root_loop == *GetGraph()->GetRootLoop(), "Root loop is incorrect\n");
601     for (auto &[block, loop] : loops) {
602         auto expected_loop = block->GetLoop();
603         // An irreducible loop can have different heads, depending on the order of traversal
604         if (loop->IsIrreducible()) {
605             ASSERT(expected_loop->IsIrreducible());
606             continue;
607         }
608         ASSERT(block->IsLoopHeader());
609         if (loop == nullptr || expected_loop == nullptr) {
610             UNREACHABLE();
611             return;
612         }
613         ASSERT_DO(*loop == *expected_loop, std::cerr << "Loop " << loop->GetId() << " is incorrect\n");
614     }
615 }
616 
617 /**
618  * Check that there is root's inner loop without exit-points
619  */
620 bool GraphChecker::HasOuterInfiniteLoop()
621 {
622     const auto &loops = GetGraph()->GetRootLoop()->GetInnerLoops();
623     return std::find_if(loops.begin(), loops.end(), [](const Loop *loop) { return loop->IsInfinite(); }) != loops.end();
624 }
625 
626 bool GraphChecker::CheckInstHasInput(Inst *inst, Inst *input)
627 {
628     ASSERT(inst != nullptr && input != nullptr);
629     ASSERT(input->GetBasicBlock() != nullptr);
630     ASSERT(input->GetBasicBlock()->GetGraph() != nullptr);
631     for (auto node : inst->GetInputs()) {
632         if (node.GetInst() == input) {
633             return true;
634         }
635     }
636     return false;
637 }
638 
639 bool GraphChecker::CheckInstHasUser(Inst *inst, Inst *user)
640 {
641     ASSERT(inst != nullptr && user != nullptr);
642     ASSERT(user->GetBasicBlock() != nullptr);
643     ASSERT(user->GetBasicBlock()->GetGraph() != nullptr);
644     for (auto &node : inst->GetUsers()) {
645         if (node.GetInst() == user) {
646             return true;
647         }
648     }
649     return false;
650 }
651 
652 void GraphChecker::CheckBlockEdges(const BasicBlock &block)
653 {
654     [[maybe_unused]] auto last_inst_in_block = block.GetLastInst();
655     if (block.GetSuccsBlocks().size() > 1) {
656         ASSERT_PRINT(!block.IsEmpty() || block.IsTryEnd(),
657                      "Block with 2 successors have no instructions or should be try-end");
658         ASSERT_PRINT(block.IsTryBegin() || block.IsTryEnd() || last_inst_in_block->IsControlFlow(),
659                      "Last instruction must be control flow in block with 2 successors");
660     } else if (block.GetSuccsBlocks().size() == 1) {
661         if (block.GetSuccsBlocks()[0]->IsEndBlock()) {
662             if (block.IsEmpty()) {
663                 ASSERT(block.IsTryEnd());
664                 return;
665             }
666             auto last_inst = block.GetLastInst();
667             [[maybe_unused]] auto opc = last_inst->GetOpcode();
668             ASSERT_PRINT(last_inst->GetFlag(inst_flags::TERMINATOR),
669                          "Last instruction in block before exit-block must be Return or Throw instruction.");
670         }
671     }
672 }
673 
674 void GraphChecker::CheckTryBeginBlock(const BasicBlock &block)
675 {
676     ASSERT(block.IsTryBegin());
677     auto try_inst_it = std::find_if(block.AllInsts().begin(), block.AllInsts().end(),
678                                     [](Inst *inst) { return inst->GetOpcode() == Opcode::Try; });
679     ASSERT_PRINT(try_inst_it != block.AllInsts().end(), "Try-begin basic block should contain try-instructions");
680     [[maybe_unused]] auto try_inst = (*try_inst_it)->CastToTry();
681     for ([[maybe_unused]] auto succ_index : *try_inst->GetCatchEdgeIndexes()) {
682         ASSERT_PRINT(succ_index < block.GetSuccsBlocks().size(),
683                      "Try instruction holds incorrect try-begin block successor number");
684     }
685 }
686 
687 void GraphChecker::CheckJump(const BasicBlock &block)
688 {
689     ASSERT(GetGraph()->IsRegAllocApplied());
690     ASSERT(GetGraph()->IsAnalysisValid<LinearOrder>());
691     if (block.IsIfBlock()) {
692         const auto &blocks_vector = GetGraph()->GetBlocksLinearOrder();
693         auto if_block_it = std::find(blocks_vector.begin(), blocks_vector.end(), &block);
694         ASSERT(if_block_it != blocks_vector.end());
695         auto block_after_if = std::next(if_block_it);
696         if (block_after_if != blocks_vector.end()) {
697             ASSERT_PRINT(*block_after_if != (*if_block_it)->GetFalseSuccessor(),
698                          "`If-block` with immediate `false`-successor shouldn't have `JumpFlag`");
699             ASSERT_PRINT(*block_after_if != (*if_block_it)->GetTrueSuccessor(),
700                          "`true`-successor should be replaced with `false`-successor");
701         }
702     }
703     [[maybe_unused]] auto num_succs = block.GetSuccsBlocks().size();
704     ASSERT_PRINT(num_succs == 1 || block.IsTryBegin() || block.IsTryEnd() || block.IsIfBlock(),
705                  "Basic block with Jump must have 1 successor or should be try-begin or if block");
706 }
707 
708 /**
709  * Regalloc propagates catch-phi's inputs to the users and can broke user's domination. In this case:
710  * - input_block should be placed inside try block;
711  * - try-begin block should dominate user_block;
712  *
713  * [try-begin]----------\
714  *     |                |
715  * [input_block]        |
716  *     |                |
717  * [try-end]----------->|
718  *                      |
719  *                [catch-begin]
720  *                      |
721  *                [user_block]
722  */
723 bool GraphChecker::IsTryCatchDomination(const BasicBlock *input_block, const BasicBlock *user_block) const
724 {
725     ASSERT(GetGraph()->IsRegAllocApplied());
726     if (input_block->IsTry()) {
727         auto blocks = GetGraph()->GetTryBeginBlocks();
728         auto it =
729             std::find_if(blocks.begin(), blocks.end(), [user_block](auto &bb) { return bb->IsDominate(user_block); });
730         return it != blocks.end();
731     }
732     return false;
733 }
734 
735 bool IsObjectCheckDisabledForOpcode(const Inst *inst)
736 {
737     auto opc = inst->GetOpcode();
738     return inst->IsCheck() || inst->IsConst() || opc == Opcode::NullPtr || inst->IsClassInst() ||
739            opc == Opcode::GetInstanceClass;
740 }
741 
742 void GraphChecker::CheckSaveStatesWithRuntimeCallUsers()
743 {
744 #ifndef NDEBUG
745     for (auto &block : GetGraph()->GetBlocksRPO()) {
746         for (const auto &ss : block->AllInsts()) {
747             if (ss->GetOpcode() != Opcode::SaveState) {
748                 continue;
749             }
750             for (auto &user : ss->GetUsers()) {
751                 auto user_inst = user.GetInst();
752                 if (!user_inst->IsRuntimeCall()) {
753                     continue;
754                 }
755                 ASSERT(user_inst->GetBasicBlock() == ss->GetBasicBlock());
756                 auto it = InstSafeIterator<IterationType::ALL, IterationDirection::BACKWARD>(*block, user_inst);
757                 for (++it; *it != ss; ++it) {
758                     // Non-reference instructions, checks, nullptr and classes cannot be moved by GC
759                     ASSERT((*it)->GetType() != DataType::REFERENCE || IsObjectCheckDisabledForOpcode(*it));
760                 }
761             }
762         }
763     }
764 #endif
765 }
766 
767 void PrepareUsers(Inst *inst, ArenaVector<User *> *users)
768 {
769     for (auto &user : inst->GetUsers()) {
770         users->push_back(&user);
771     }
772     auto i = std::find_if(users->begin(), users->end(), [](User *user) { return user->GetInst()->IsCheck(); });
773     while (i != users->end()) {
774         for (auto &u : (*i)->GetInst()->GetUsers()) {
775             users->push_back(&u);
776         }
777         users->erase(i);
778         i = std::find_if(users->begin(), users->end(), [](User *user) { return user->GetInst()->IsCheck(); });
779     }
780     for (auto &it : (*users)) {
781         [[maybe_unused]] auto user = it->GetInst();
782         ASSERT(!user->IsCheck());
783     }
784 }
785 
786 void GraphChecker::CheckSaveStateInputs()
787 {
788 #ifndef NDEBUG
789     ArenaVector<User *> users(GetLocalAllocator()->Adapter());
790     for (auto &block : GetGraph()->GetBlocksRPO()) {
791         for (const auto &inst : block->AllInsts()) {
792             if (IsObjectCheckDisabledForOpcode(inst)) {
793                 continue;
794             }
795             // skip phi which all inputs is disabled
796             if (inst->GetOpcode() == Opcode::Phi) {
797                 bool skip_flag = true;
798                 for (const auto &input : inst->GetInputs()) {
799                     skip_flag &= IsObjectCheckDisabledForOpcode(input.GetInst());
800                 }
801                 if (skip_flag) {
802                     continue;
803                 }
804             }
805 
806             PrepareUsers(inst, &users);
807 
808             auto object_visited = GetGraph()->NewMarker();
809             auto osr_visited = GetGraph()->NewMarker();
810             for (auto &it : users) {
811                 auto user = it->GetInst();
812                 // For Phi we need to check only pass between object and phi
813                 if (user->IsPhi() || user->IsCatchPhi()) {
814                     continue;
815                 }
816                 // Virtual register can be overwrite
817                 if (user->IsSaveState()) {
818                     continue;
819                 }
820                 if (inst->GetType() == DataType::REFERENCE) {
821                     CheckObjectRec(inst, user, user->GetBasicBlock(), user->GetPrev(), object_visited);
822                 }
823                 CheckSaveStateOsrRec(inst, user, user->GetBasicBlock(), osr_visited);
824             }
825             GetGraph()->EraseMarker(object_visited);
826             GetGraph()->EraseMarker(osr_visited);
827             users.clear();
828         }
829     }
830 #endif
831 }
832 
833 void GraphChecker::FindObjectInSaveState(const Inst *object, Inst *ss) const
834 {
835     while (ss != nullptr && object->IsDominate(ss)) {
836         auto it = std::find_if(ss->GetInputs().begin(), ss->GetInputs().end(),
837                                [object, ss](Input input) { return ss->GetDataFlowInput(input.GetInst()) == object; });
838         if (it != ss->GetInputs().end()) {
839             return;
840         }
841         auto caller = static_cast<SaveStateInst *>(ss)->GetCallerInst();
842         if (caller == nullptr) {
843             break;
844         }
845         ss = caller->GetSaveState();
846     }
847     if (object != nullptr && ss != nullptr) {
848         std::cerr << "Object not found in the SaveState: " << std::endl
849                   << *object << std::endl
850                   << " " << *ss << std::endl;
851     }
852     UNREACHABLE();
853 }
854 
855 bool IsSaveStateForGc(Inst *inst)
856 {
857     if (inst->GetOpcode() == Opcode::SafePoint) {
858         return true;
859     }
860     if (inst->GetOpcode() == Opcode::SaveState) {
861         for (auto &user : inst->GetUsers()) {
862             if (user.GetInst()->IsRuntimeCall()) {
863                 return true;
864             }
865         }
866     }
867     return false;
868 }
869 
870 void GraphChecker::CheckObjectRec(const Inst *object, const Inst *user, const BasicBlock *block, Inst *start_from,
871                                   Marker visited) const
872 {
873     if (start_from != nullptr) {
874         auto it = InstSafeIterator<IterationType::ALL, IterationDirection::BACKWARD>(*block, start_from);
875         for (; it != block->AllInstsSafeReverse().end(); ++it) {
876             auto inst = *it;
877             if (inst == nullptr) {
878                 break;
879             }
880             if (inst->SetMarker(visited)) {
881                 return;
882             }
883             if (IsSaveStateForGc(inst)) {
884                 FindObjectInSaveState(object, inst);
885             } else if (inst == object || inst == user) {
886                 return;
887             }
888         }
889     }
890     for (auto pred : block->GetPredsBlocks()) {
891         // Catch-begin block has edge from try-end block, and all try-blocks should be visited from this edge.
892         // `object` can be placed inside try-block - after try-begin, so that visiting try-begin is wrong
893         if (block->IsCatchBegin() && pred->IsTryBegin()) {
894             continue;
895         }
896         CheckObjectRec(object, user, pred, pred->GetLastInst(), visited);
897     }
898 }
899 
900 void GraphChecker::CheckSaveStateOsrRec(const Inst *inst, const Inst *user, BasicBlock *block, Marker visited)
901 {
902     if (block->SetMarker(visited)) {
903         return;
904     }
905     if (inst->GetBasicBlock() == block) {
906         return;
907     }
908     if (block->IsOsrEntry()) {
909         ASSERT(GetGraph()->IsOsrMode());
910         auto ss = block->GetFirstInst();
911         ASSERT(ss != nullptr && ss->GetOpcode() == Opcode::SaveStateOsr);
912         [[maybe_unused]] auto it =
913             std::find_if(ss->GetInputs().begin(), ss->GetInputs().end(),
914                          [inst, ss](Input input) { return ss->GetDataFlowInput(input.GetInst()) == inst; });
915         ASSERT(it != ss->GetInputs().end());
916     }
917     for (auto pred : block->GetPredsBlocks()) {
918         CheckSaveStateOsrRec(inst, user, pred, visited);
919     }
920 }
921 
922 /*
923  * Visitors to check instructions types
924  */
925 void GraphChecker::VisitMov([[maybe_unused]] GraphVisitor *v, Inst *inst)
926 {
927     CheckUnaryOperationTypes(inst);
928 }
929 void GraphChecker::VisitNeg([[maybe_unused]] GraphVisitor *v, Inst *inst)
930 {
931     CheckUnaryOperationTypes(inst);
932 }
933 void GraphChecker::VisitAbs([[maybe_unused]] GraphVisitor *v, Inst *inst)
934 {
935     CheckUnaryOperationTypes(inst);
936 }
937 void GraphChecker::VisitSqrt([[maybe_unused]] GraphVisitor *v, Inst *inst)
938 {
939     ASSERT_DO(DataType::IsFloatType(inst->GetType()),
940               (std::cerr << "\nSqrt must have float type\n", inst->Dump(&std::cerr)));
941     CheckUnaryOperationTypes(inst);
942 }
943 
944 void GraphChecker::VisitAddI([[maybe_unused]] GraphVisitor *v, Inst *inst)
945 {
946     if (inst->GetType() == DataType::POINTER) {
947         ASSERT_DO(inst->GetInputType(0) == DataType::POINTER,
948                   (std::cerr << "\nptr AddI must have ptr input type\n", inst->Dump(&std::cerr)));
949         return;
950     }
951     ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
952               (std::cerr << "\nAddI must have integer type\n", inst->Dump(&std::cerr)));
953     CheckUnaryOperationTypes(inst);
954 }
955 void GraphChecker::VisitSubI([[maybe_unused]] GraphVisitor *v, Inst *inst)
956 {
957     if (!static_cast<GraphChecker *>(v)->GetGraph()->SupportManagedCode() && inst->GetType() == DataType::POINTER) {
958         ASSERT_DO(inst->GetInputType(0) == DataType::POINTER,
959                   (std::cerr << "\nptr SubI must have ptr input type\n", inst->Dump(&std::cerr)));
960         return;
961     }
962     ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
963               (std::cerr << "\nSubI must have integer type\n", inst->Dump(&std::cerr)));
964     CheckUnaryOperationTypes(inst);
965 }
966 void GraphChecker::VisitMulI([[maybe_unused]] GraphVisitor *v, Inst *inst)
967 {
968     [[maybe_unused]] auto type = inst->GetType();
969     ASSERT_DO(DataType::Is32Bits(type, static_cast<GraphChecker *>(v)->GetGraph()->GetArch()) &&
970                   !DataType::IsReference(type),
971               (std::cerr << "\nMulI must have Int32 type\n", inst->Dump(&std::cerr)));
972     CheckUnaryOperationTypes(inst);
973 }
974 void GraphChecker::VisitDivI([[maybe_unused]] GraphVisitor *v, Inst *inst)
975 {
976     [[maybe_unused]] auto type = inst->GetType();
977     ASSERT_DO(DataType::Is32Bits(type, static_cast<GraphChecker *>(v)->GetGraph()->GetArch()) &&
978                   !DataType::IsReference(type),
979               (std::cerr << "\nDivI must have Int32 type\n", inst->Dump(&std::cerr)));
980     CheckUnaryOperationTypes(inst);
981 }
982 void GraphChecker::VisitModI([[maybe_unused]] GraphVisitor *v, Inst *inst)
983 {
984     [[maybe_unused]] auto type = inst->GetType();
985     ASSERT_DO(DataType::Is32Bits(type, static_cast<GraphChecker *>(v)->GetGraph()->GetArch()) &&
986                   !DataType::IsReference(type),
987               (std::cerr << "\nModI must have Int32 type\n", inst->Dump(&std::cerr)));
988     CheckUnaryOperationTypes(inst);
989 }
990 void GraphChecker::VisitAndI([[maybe_unused]] GraphVisitor *v, Inst *inst)
991 {
992     ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
993               (std::cerr << "\nAndI must have integer type\n", inst->Dump(&std::cerr)));
994     CheckUnaryOperationTypes(inst);
995 }
996 void GraphChecker::VisitOrI([[maybe_unused]] GraphVisitor *v, Inst *inst)
997 {
998     ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
999               (std::cerr << "\nOrI must have integer type\n", inst->Dump(&std::cerr)));
1000     CheckUnaryOperationTypes(inst);
1001 }
1002 void GraphChecker::VisitXorI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1003 {
1004     ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1005               (std::cerr << "\nXorI must have integer type\n", inst->Dump(&std::cerr)));
1006     CheckUnaryOperationTypes(inst);
1007 }
1008 void GraphChecker::VisitShlI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1009 {
1010     ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1011               (std::cerr << "\nShlI must have integer type\n", inst->Dump(&std::cerr)));
1012     CheckUnaryOperationTypes(inst);
1013     [[maybe_unused]] auto imm = static_cast<BinaryImmOperation *>(inst)->GetImm();
1014     ASSERT_DO(imm <= DataType::GetTypeSize(inst->GetType(), static_cast<GraphChecker *>(v)->GetGraph()->GetArch()),
1015               (std::cerr << "\nShlI have shift more then size of type\n", inst->Dump(&std::cerr)));
1016 }
1017 void GraphChecker::VisitShrI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1018 {
1019     ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1020               (std::cerr << "\nShrI must have integer type\n", inst->Dump(&std::cerr)));
1021     CheckUnaryOperationTypes(inst);
1022     [[maybe_unused]] auto imm = static_cast<BinaryImmOperation *>(inst)->GetImm();
1023     ASSERT_DO(imm <= DataType::GetTypeSize(inst->GetType(), static_cast<GraphChecker *>(v)->GetGraph()->GetArch()),
1024               (std::cerr << "\nShrI have shift more then size of type\n", inst->Dump(&std::cerr)));
1025 }
1026 void GraphChecker::VisitAShlI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1027 {
1028     ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1029               (std::cerr << "\nAShrI must have integer type\n", inst->Dump(&std::cerr)));
1030     CheckUnaryOperationTypes(inst);
1031     [[maybe_unused]] auto imm = static_cast<BinaryImmOperation *>(inst)->GetImm();
1032     ASSERT_DO(imm <= DataType::GetTypeSize(inst->GetType(), static_cast<GraphChecker *>(v)->GetGraph()->GetArch()),
1033               (std::cerr << "\nAShlI have shift more then size of type\n", inst->Dump(&std::cerr)));
1034 }
1035 void GraphChecker::VisitNot([[maybe_unused]] GraphVisitor *v, Inst *inst)
1036 {
1037     ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1038               (std::cerr << "\nNot must have integer type\n", inst->Dump(&std::cerr)));
1039     CheckUnaryOperationTypes(inst);
1040 }
1041 
1042 void GraphChecker::VisitAdd([[maybe_unused]] GraphVisitor *v, Inst *inst)
1043 {
1044     if (!static_cast<GraphChecker *>(v)->GetGraph()->SupportManagedCode() && inst->GetType() == DataType::POINTER) {
1045         [[maybe_unused]] auto type_1 = inst->GetInput(0).GetInst()->GetType();
1046         [[maybe_unused]] auto type_2 = inst->GetInput(1).GetInst()->GetType();
1047         ASSERT_DO(type_1 != type_2,
1048                   (std::cerr << "\nptr Add must have ptr and int input types\n", inst->Dump(&std::cerr)));
1049         ASSERT_DO((type_1 == DataType::POINTER && DataType::GetCommonType(type_2) == DataType::INT64) ||
1050                       (type_2 == DataType::POINTER && DataType::GetCommonType(type_1) == DataType::INT64),
1051                   (std::cerr << "\nptr Add must have ptr and int input types\n", inst->Dump(&std::cerr)));
1052         return;
1053     }
1054     CheckBinaryOperationTypes(inst);
1055 }
1056 void GraphChecker::VisitSub([[maybe_unused]] GraphVisitor *v, Inst *inst)
1057 {
1058     if (!static_cast<GraphChecker *>(v)->GetGraph()->SupportManagedCode()) {
1059         [[maybe_unused]] auto type_1 = inst->GetInput(0).GetInst()->GetType();
1060         [[maybe_unused]] auto type_2 = inst->GetInput(1).GetInst()->GetType();
1061         if (inst->GetType() == DataType::POINTER) {
1062             ASSERT_DO(type_1 != type_2,
1063                       (std::cerr << "\nptr Sub must have ptr and int input types\n", inst->Dump(&std::cerr)));
1064             ASSERT_DO((type_1 == DataType::POINTER && DataType::GetCommonType(type_2) == DataType::INT64) ||
1065                           (type_2 == DataType::POINTER && DataType::GetCommonType(type_1) == DataType::INT64),
1066                       (std::cerr << "\nptr Sub must have ptr and int input types\n", inst->Dump(&std::cerr)));
1067             return;
1068         }
1069         if (type_1 == DataType::POINTER && type_2 == DataType::POINTER) {
1070             ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1071                       (std::cerr << "\n Sub with 2 ptr inputs must have int type\n", inst->Dump(&std::cerr)));
1072             return;
1073         }
1074     }
1075     CheckBinaryOperationTypes(inst);
1076 }
1077 void GraphChecker::VisitMul([[maybe_unused]] GraphVisitor *v, Inst *inst)
1078 {
1079     CheckBinaryOperationTypes(inst);
1080 }
1081 void GraphChecker::VisitDiv([[maybe_unused]] GraphVisitor *v, Inst *inst)
1082 {
1083     CheckBinaryOperationTypes(inst);
1084 }
1085 void GraphChecker::VisitMod([[maybe_unused]] GraphVisitor *v, Inst *inst)
1086 {
1087     CheckBinaryOperationTypes(inst);
1088 }
1089 void GraphChecker::VisitMin([[maybe_unused]] GraphVisitor *v, Inst *inst)
1090 {
1091     CheckBinaryOperationTypes(inst);
1092 }
1093 void GraphChecker::VisitMax([[maybe_unused]] GraphVisitor *v, Inst *inst)
1094 {
1095     CheckBinaryOperationTypes(inst);
1096 }
1097 void GraphChecker::VisitShl([[maybe_unused]] GraphVisitor *v, Inst *inst)
1098 {
1099     CheckBinaryOperationTypes(inst, true);
1100 }
1101 void GraphChecker::VisitShr([[maybe_unused]] GraphVisitor *v, Inst *inst)
1102 {
1103     CheckBinaryOperationTypes(inst, true);
1104 }
1105 void GraphChecker::VisitAShr([[maybe_unused]] GraphVisitor *v, Inst *inst)
1106 {
1107     CheckBinaryOperationTypes(inst, true);
1108 }
1109 void GraphChecker::VisitAnd([[maybe_unused]] GraphVisitor *v, Inst *inst)
1110 {
1111     CheckBinaryOperationTypes(inst, true);
1112 }
1113 void GraphChecker::VisitOr([[maybe_unused]] GraphVisitor *v, Inst *inst)
1114 {
1115     CheckBinaryOperationTypes(inst, true);
1116 }
1117 void GraphChecker::VisitXor([[maybe_unused]] GraphVisitor *v, Inst *inst)
1118 {
1119     CheckBinaryOperationTypes(inst, true);
1120 }
1121 
1122 void GraphChecker::VisitAddOverflow([[maybe_unused]] GraphVisitor *v, Inst *inst)
1123 {
1124     CheckBinaryOverflowOperation(inst->CastToAddOverflow());
1125 }
1126 void GraphChecker::VisitSubOverflow([[maybe_unused]] GraphVisitor *v, Inst *inst)
1127 {
1128     CheckBinaryOverflowOperation(inst->CastToSubOverflow());
1129 }
1130 void GraphChecker::VisitLoadArray([[maybe_unused]] GraphVisitor *v, Inst *inst)
1131 {
1132     CheckMemoryInstruction(inst);
1133 }
1134 void GraphChecker::VisitLoadArrayI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1135 {
1136     CheckMemoryInstruction(inst);
1137 }
1138 void GraphChecker::VisitLoadArrayPair([[maybe_unused]] GraphVisitor *v, Inst *inst)
1139 {
1140     ASSERT_DO(MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1141               (std::cerr << "Unallowed type of coalesced load\n", inst->Dump(&std::cerr)));
1142     CheckMemoryInstruction(inst);
1143 }
1144 void GraphChecker::VisitLoadArrayPairI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1145 {
1146     ASSERT_DO(MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1147               (std::cerr << "Unallowed type of coalesced load\n", inst->Dump(&std::cerr)));
1148     CheckMemoryInstruction(inst);
1149 }
1150 
1151 void GraphChecker::VisitLoadPairPart([[maybe_unused]] GraphVisitor *v, Inst *inst)
1152 {
1153     ASSERT_DO(MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1154               (std::cerr << "Unallowed type of coalesced load\n", inst->Dump(&std::cerr)));
1155     CheckMemoryInstruction(inst);
1156     [[maybe_unused]] auto op1 = inst->GetInputs()[0].GetInst();
1157     [[maybe_unused]] auto idx = inst->CastToLoadPairPart()->GetImm();
1158     ASSERT_DO(op1->GetOpcode() == Opcode::LoadArrayPair || op1->GetOpcode() == Opcode::LoadArrayPairI,
1159               (std::cerr << "Input instruction is not a Pair\n", inst->Dump(&std::cerr)));
1160     if (op1->GetOpcode() == Opcode::LoadArrayPairI) {
1161         ASSERT_DO(idx < op1->CastToLoadArrayPairI()->GetDstCount(),
1162                   (std::cerr << "Pair index is out of bounds\n", inst->Dump(&std::cerr)));
1163     } else {
1164         ASSERT_DO(idx < op1->CastToLoadArrayPair()->GetDstCount(),
1165                   (std::cerr << "Pair index is out of bounds\n", inst->Dump(&std::cerr)));
1166     }
1167     ASSERT_DO(
1168         CheckCommonTypes(inst, inst->GetInputs()[0].GetInst()),
1169         (std::cerr << "Types of load vector element and vector input are not compatible\n", inst->Dump(&std::cerr)));
1170 
1171     // Strict order here
1172     auto prev = inst->GetPrev();
1173     while (prev != nullptr && prev != op1) {
1174         if (prev->GetOpcode() == Opcode::LoadPairPart || prev->GetOpcode() == Opcode::SpillFill) {
1175             prev = prev->GetPrev();
1176         } else {
1177             break;
1178         }
1179     }
1180     ASSERT_DO(prev != nullptr && prev == op1,
1181               (std::cerr << "LoadPairPart(s) instructions must follow immediately after appropriate LoadArrayPair(I)\n",
1182                inst->Dump(&std::cerr)));
1183 }
1184 
1185 void GraphChecker::VisitStoreArrayPair([[maybe_unused]] GraphVisitor *v, Inst *inst)
1186 {
1187     ASSERT_DO(MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1188               (std::cerr << "Unallowed type of coalesced store\n", inst->Dump(&std::cerr)));
1189     CheckMemoryInstruction(inst);
1190     ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[2U].GetInst()),
1191               (std::cerr << "Types of store and the first stored value are not compatible\n", inst->Dump(&std::cerr)));
1192     ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[3U].GetInst()),
1193               (std::cerr << "Types of store and the second stored value are not compatible\n", inst->Dump(&std::cerr)));
1194     [[maybe_unused]] bool need_barrier = inst->CastToStoreArrayPair()->GetNeedBarrier();
1195     ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1196               (std::cerr << "StoreArrayPair has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1197 }
1198 
1199 void GraphChecker::VisitStoreArrayPairI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1200 {
1201     ASSERT_DO(MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1202               (std::cerr << "Unallowed type of coalesced store\n", inst->Dump(&std::cerr)));
1203     CheckMemoryInstruction(inst);
1204     ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[1].GetInst()),
1205               (std::cerr << "Types of store and the first stored value are not compatible\n", inst->Dump(&std::cerr)));
1206     ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[2U].GetInst()),
1207               (std::cerr << "Types of store and the second stored value are not compatible\n", inst->Dump(&std::cerr)));
1208     [[maybe_unused]] bool need_barrier = inst->CastToStoreArrayPairI()->GetNeedBarrier();
1209     ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1210               (std::cerr << "StoreArrayPairI has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1211 }
1212 
1213 void GraphChecker::VisitStoreArray([[maybe_unused]] GraphVisitor *v, Inst *inst)
1214 {
1215     CheckMemoryInstruction(inst);
1216     ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[2U].GetInst()),
1217               (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1218     [[maybe_unused]] bool need_barrier = inst->CastToStoreArray()->GetNeedBarrier();
1219     ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1220               (std::cerr << "StoreArray has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1221 }
1222 
1223 void GraphChecker::VisitStoreArrayI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1224 {
1225     CheckMemoryInstruction(inst);
1226     ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[1].GetInst()),
1227               (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1228     [[maybe_unused]] bool need_barrier = inst->CastToStoreArrayI()->GetNeedBarrier();
1229     ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1230               (std::cerr << "StoreArrayI has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1231 }
1232 
1233 void GraphChecker::VisitStoreStatic([[maybe_unused]] GraphVisitor *v, Inst *inst)
1234 {
1235     CheckMemoryInstruction(inst);
1236     auto graph = static_cast<GraphChecker *>(v)->GetGraph();
1237     ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[1].GetInst()),
1238               (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1239     [[maybe_unused]] bool need_barrier = inst->CastToStoreStatic()->GetNeedBarrier();
1240     ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE),
1241               (std::cerr << "StoreStatic has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1242     [[maybe_unused]] auto init_inst = inst->GetInputs()[0].GetInst();
1243     if (init_inst->IsPhi()) {
1244         return;
1245     }
1246     ASSERT_DO(init_inst->GetOpcode() == Opcode::LoadAndInitClass,
1247               (std::cerr << "The first input for the StoreStatic should be LoadAndInitClass", inst->Dump(&std::cerr),
1248                init_inst->Dump(&std::cerr)));
1249     [[maybe_unused]] auto store_static = inst->CastToStoreStatic();
1250     [[maybe_unused]] auto class_id =
1251         graph->GetRuntime()->GetClassIdForField(store_static->GetMethod(), store_static->GetTypeId());
1252     // See comment in VisitNewObject about this if statement
1253     if (init_inst->CastToLoadAndInitClass()->GetClass() == nullptr) {
1254         ASSERT_DO(init_inst->CastToLoadAndInitClass()->GetTypeId() == class_id,
1255                   (std::cerr << "StoreStatic and LoadAndInitClass must have equal class", inst->Dump(&std::cerr),
1256                    init_inst->Dump(&std::cerr)));
1257     }
1258 }
1259 
1260 void GraphChecker::VisitUnresolvedStoreStatic([[maybe_unused]] GraphVisitor *v, Inst *inst)
1261 {
1262     CheckMemoryInstruction(inst);
1263     ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[0].GetInst()),
1264               (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1265     [[maybe_unused]] bool need_barrier = inst->CastToUnresolvedStoreStatic()->GetNeedBarrier();
1266     ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE),
1267               (std::cerr << "UnresolvedStoreStatic has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1268     [[maybe_unused]] auto ss = inst->GetInputs()[1].GetInst();
1269     ASSERT_DO(ss->GetOpcode() == Opcode::SaveState,
1270               (std::cerr << "UnresolvedStoreStatic instruction second operand is not a SaveState",
1271                inst->Dump(&std::cerr), ss->Dump(&std::cerr)));
1272 }
1273 
1274 void GraphChecker::VisitStoreObject([[maybe_unused]] GraphVisitor *v, Inst *inst)
1275 {
1276     CheckMemoryInstruction(inst);
1277     ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[1].GetInst()),
1278               (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1279     [[maybe_unused]] bool need_barrier = inst->CastToStoreObject()->GetNeedBarrier();
1280     ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1281               (std::cerr << "StoreObject has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1282 }
1283 
1284 void GraphChecker::VisitUnresolvedStoreObject([[maybe_unused]] GraphVisitor *v, Inst *inst)
1285 {
1286     CheckMemoryInstruction(inst);
1287     ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[1].GetInst()),
1288               (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1289     [[maybe_unused]] bool need_barrier = inst->CastToUnresolvedStoreObject()->GetNeedBarrier();
1290     ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE),
1291               (std::cerr << "UnresolvedStoreObject has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1292     [[maybe_unused]] auto ss = inst->GetInputs()[2U].GetInst();
1293     ASSERT_DO(ss->GetOpcode() == Opcode::SaveState,
1294               (std::cerr << "UnresolvedStoreObject instruction third operand is not a SaveState",
1295                inst->Dump(&std::cerr), ss->Dump(&std::cerr)));
1296 }
1297 
1298 void GraphChecker::VisitLoadStatic([[maybe_unused]] GraphVisitor *v, Inst *inst)
1299 {
1300     CheckMemoryInstruction(inst);
1301     auto graph = static_cast<GraphChecker *>(v)->GetGraph();
1302     [[maybe_unused]] auto init_inst = inst->GetInputs()[0].GetInst();
1303     if (init_inst->IsPhi()) {
1304         return;
1305     }
1306     ASSERT_DO(init_inst->GetOpcode() == Opcode::LoadAndInitClass,
1307               (std::cerr << "The first input for the LoadStatic should be LoadAndInitClass", inst->Dump(&std::cerr),
1308                init_inst->Dump(&std::cerr)));
1309     [[maybe_unused]] auto load_static = inst->CastToLoadStatic();
1310     [[maybe_unused]] auto class_id =
1311         graph->GetRuntime()->GetClassIdForField(load_static->GetMethod(), load_static->GetTypeId());
1312     // See comment in VisitNewObject about this if statement
1313     if (init_inst->CastToLoadAndInitClass()->GetClass() == nullptr) {
1314         ASSERT_DO(init_inst->CastToLoadAndInitClass()->GetTypeId() == class_id,
1315                   (std::cerr << "LoadStatic and LoadAndInitClass must have equal class", inst->Dump(&std::cerr),
1316                    init_inst->Dump(&std::cerr)));
1317     }
1318 }
1319 
1320 void GraphChecker::VisitUnresolvedLoadStatic([[maybe_unused]] GraphVisitor *v, Inst *inst)
1321 {
1322     CheckMemoryInstruction(inst);
1323     [[maybe_unused]] auto ss = inst->GetInputs()[0].GetInst();
1324     ASSERT_DO(ss->GetOpcode() == Opcode::SaveState,
1325               (std::cerr << "UnresolvedLoadStatic instruction first operand is not a SaveState", inst->Dump(&std::cerr),
1326                ss->Dump(&std::cerr)));
1327 }
1328 
1329 void GraphChecker::VisitLoadClass([[maybe_unused]] GraphVisitor *v, Inst *inst)
1330 {
1331     ASSERT_DO(inst->GetType() == DataType::REFERENCE,
1332               (std::cerr << "LoadClass must have Reference type", inst->Dump(&std::cerr)));
1333     for (auto &user : inst->GetUsers()) {
1334         [[maybe_unused]] auto user_inst = user.GetInst();
1335         ASSERT_DO(
1336             user_inst->GetOpcode() == Opcode::CheckCast || user_inst->GetOpcode() == Opcode::IsInstance ||
1337                 user_inst->GetOpcode() == Opcode::Phi,
1338             (std::cerr << "Incorrect user of the LoadClass", inst->Dump(&std::cerr), user_inst->Dump(&std::cerr)));
1339     }
1340 }
1341 
1342 void GraphChecker::VisitLoadAndInitClass([[maybe_unused]] GraphVisitor *v, Inst *inst)
1343 {
1344     ASSERT_DO(inst->GetType() == DataType::REFERENCE,
1345               (std::cerr << "LoadAndInitClass must have Reference type", inst->Dump(&std::cerr)));
1346     for (auto &user : inst->GetUsers()) {
1347         [[maybe_unused]] auto user_inst = user.GetInst();
1348         ASSERT_DO(user_inst->GetOpcode() == Opcode::LoadStatic || user_inst->GetOpcode() == Opcode::StoreStatic ||
1349                       user_inst->GetOpcode() == Opcode::NewObject || user_inst->GetOpcode() == Opcode::Phi ||
1350                       user_inst->GetOpcode() == Opcode::MultiArray || user_inst->GetOpcode() == Opcode::InitObject ||
1351                       user_inst->GetOpcode() == Opcode::UnresolvedLoadStatic ||
1352                       user_inst->GetOpcode() == Opcode::UnresolvedStoreStatic ||
1353                       user_inst->GetOpcode() == Opcode::Intrinsic || user_inst->GetOpcode() == Opcode::NewArray ||
1354                       user_inst->GetOpcode() == Opcode::IsInstance || user_inst->GetOpcode() == Opcode::CheckCast,
1355                   (std::cerr << "Incorrect user of the LoadAndInitClass", inst->Dump(&std::cerr),
1356                    user_inst->Dump(&std::cerr)));
1357     }
1358 }
1359 
1360 void GraphChecker::VisitUnresolvedLoadAndInitClass([[maybe_unused]] GraphVisitor *v, Inst *inst)
1361 {
1362     ASSERT_DO(inst->GetType() == DataType::REFERENCE,
1363               (std::cerr << "UnresolvedLoadAndInitClass must have Reference type", inst->Dump(&std::cerr)));
1364     ASSERT_DO(inst->CastToUnresolvedLoadAndInitClass()->GetClass() == nullptr,
1365               (std::cerr << "UnresolvedLoadAndInitClass must have a null ClassPtr", inst->Dump(&std::cerr)));
1366     [[maybe_unused]] auto ss = inst->GetInputs()[0].GetInst();
1367     ASSERT_DO(ss->GetOpcode() == Opcode::SaveState,
1368               (std::cerr << "UnresolvedLoadAndInitClass instruction first operand is not a SaveState",
1369                inst->Dump(&std::cerr), ss->Dump(&std::cerr)));
1370     for (auto &user : inst->GetUsers()) {
1371         [[maybe_unused]] auto user_inst = user.GetInst();
1372         ASSERT_DO(user_inst->GetOpcode() == Opcode::LoadStatic || user_inst->GetOpcode() == Opcode::StoreStatic ||
1373                       user_inst->GetOpcode() == Opcode::NewObject || user_inst->GetOpcode() == Opcode::NewArray ||
1374                       user_inst->GetOpcode() == Opcode::Phi || user_inst->GetOpcode() == Opcode::MultiArray ||
1375                       user_inst->GetOpcode() == Opcode::UnresolvedLoadStatic ||
1376                       user_inst->GetOpcode() == Opcode::UnresolvedStoreStatic,
1377                   (std::cerr << "Incorrect user of the UnresolvedLoadAndInitClass", inst->Dump(&std::cerr),
1378                    user_inst->Dump(&std::cerr)));
1379     }
1380 }
1381 
1382 void GraphChecker::VisitNewObject([[maybe_unused]] GraphVisitor *v, Inst *inst)
1383 {
1384     ASSERT_DO(inst->GetType() == DataType::REFERENCE,
1385               (std::cerr << "NewObject must be have Reference type", inst->Dump(&std::cerr)));
1386     [[maybe_unused]] auto init_inst = inst->GetInputs()[0].GetInst();
1387     if (init_inst->IsPhi()) {
1388         return;
1389     }
1390     ASSERT_DO(
1391         init_inst->GetOpcode() == Opcode::LoadAndInitClass ||
1392             init_inst->GetOpcode() == Opcode::UnresolvedLoadAndInitClass,
1393         (std::cerr << "The first input for the NewObject should be LoadAndInitClass or UnresolvedLoadAndInitClass",
1394          inst->Dump(&std::cerr), init_inst->Dump(&std::cerr)));
1395     [[maybe_unused]] auto ss_inst = inst->GetInputs()[1].GetInst();
1396     ASSERT_DO(ss_inst->GetOpcode() == Opcode::SaveState,
1397               (std::cerr << "The second input for the NewObject should be SaveState", inst->Dump(&std::cerr),
1398                ss_inst->Dump(&std::cerr)));
1399     // If InitClass contains an already resolved class, then IDs may be different. Because VN can remove the
1400     // duplicated InitClass and keep only one that is located in the inlined method and has a different id
1401     // accordingly.
1402     if (init_inst->GetOpcode() == Opcode::LoadAndInitClass &&
1403         init_inst->CastToLoadAndInitClass()->GetClass() == nullptr) {
1404         ASSERT_DO(init_inst->CastToLoadAndInitClass()->GetTypeId() == inst->CastToNewObject()->GetTypeId(),
1405                   std::cerr << "NewObject and LoadAndInitClass must have equal class:\n"
1406                             << *inst << '\n'
1407                             << *init_inst << std::endl);
1408     } else if (init_inst->GetOpcode() == Opcode::UnresolvedLoadAndInitClass) {
1409         ASSERT_DO(init_inst->CastToUnresolvedLoadAndInitClass()->GetTypeId() == inst->CastToNewObject()->GetTypeId(),
1410                   std::cerr << "NewObject and UnresolvedLoadAndInitClass must have equal class:\n"
1411                             << *inst << '\n'
1412                             << *init_inst << std::endl);
1413     }
1414 }
1415 
1416 void GraphChecker::VisitInitObject([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1417 {
1418     ASSERT_DO(options.IsCompilerSupportInitObjectInst(),
1419               (std::cerr << "Instruction InitObject isn't supported", inst->Dump(&std::cerr)));
1420 }
1421 
1422 void GraphChecker::VisitInitClass([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1423 {
1424     ASSERT_DO(inst->GetType() == DataType::NO_TYPE,
1425               (std::cerr << "InitClass doesn't have type", inst->Dump(&std::cerr)));
1426 }
1427 
1428 void GraphChecker::VisitLoadObject([[maybe_unused]] GraphVisitor *v, Inst *inst)
1429 {
1430     CheckMemoryInstruction(inst);
1431 }
1432 
1433 void GraphChecker::VisitUnresolvedLoadObject([[maybe_unused]] GraphVisitor *v, Inst *inst)
1434 {
1435     CheckMemoryInstruction(inst);
1436     [[maybe_unused]] auto ss = inst->GetInputs()[1].GetInst();
1437     ASSERT_DO(ss->GetOpcode() == Opcode::SaveState,
1438               (std::cerr << "UnresolvedLoadObject instruction second operand is not a SaveState",
1439                inst->Dump(&std::cerr), ss->Dump(&std::cerr)));
1440 }
1441 
1442 void GraphChecker::VisitConstant([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1443 {
1444     [[maybe_unused]] auto type = inst->GetType();
1445     [[maybe_unused]] auto is_dynamic = static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod();
1446     if (static_cast<GraphChecker *>(v)->GetGraph()->IsBytecodeOptimizer()) {
1447         ASSERT_DO(
1448             type == DataType::FLOAT32 || type == DataType::FLOAT64 || type == DataType::INT64 ||
1449                 type == DataType::INT32 || (type == DataType::ANY && is_dynamic),
1450             (std::cerr << "Constant inst can be only FLOAT32, FLOAT64, INT32 or INT64\n", inst->Dump(&std::cerr)));
1451 
1452     } else {
1453         ASSERT_DO(
1454             type == DataType::FLOAT32 || type == DataType::FLOAT64 || type == DataType::INT64 ||
1455                 (type == DataType::ANY && is_dynamic),
1456             (std::cerr << "Constant instruction can be only FLOAT32, FLOAT64 or INT64\n", inst->Dump(&std::cerr)));
1457     }
1458 }
1459 
1460 void GraphChecker::VisitNullPtr([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1461 {
1462     ASSERT_DO(inst->GetType() == DataType::REFERENCE,
1463               (std::cerr << "NullPtr instruction should have REFERENCE type only\n", inst->Dump(&std::cerr)));
1464 
1465     ASSERT_DO(static_cast<GraphChecker *>(v)->IncrementNullPtrInstCounterAndGet() == 1,
1466               (std::cerr << "There should be not more than one NullPtr instruction in graph\n",
1467                inst->GetBasicBlock()->Dump(&std::cerr)));
1468 }
1469 
1470 void GraphChecker::VisitPhi([[maybe_unused]] GraphVisitor *v, Inst *inst)
1471 {
1472     for ([[maybe_unused]] auto input : inst->GetInputs()) {
1473         ASSERT_DO(CheckCommonTypes(inst, input.GetInst()),
1474                   (std::cerr << "Types of phi result and phi input are not compatible\n"
1475                              << *inst << std::endl
1476                              << *input.GetInst()));
1477     }
1478 }
1479 
1480 void GraphChecker::VisitParameter([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1481 {
1482     ASSERT_DO(inst->GetType() != DataType::NO_TYPE,
1483               (std::cerr << "The parametr doesn't have type:\n", inst->Dump(&std::cerr)));
1484 }
1485 
1486 void GraphChecker::VisitCompare([[maybe_unused]] GraphVisitor *v, Inst *inst)
1487 {
1488     [[maybe_unused]] auto op1 = inst->GetInputs()[0].GetInst();
1489     [[maybe_unused]] auto op2 = inst->GetInputs()[1].GetInst();
1490     for (size_t i = 0; i < inst->GetInputsCount(); i++) {
1491         ASSERT_DO(inst->GetInputType(i) != DataType::NO_TYPE,
1492                   std::cerr << "Source operand type is not set: " << *inst << std::endl);
1493     }
1494     ASSERT_DO(inst->GetInputType(0) == inst->GetInputType(1),
1495               std::cerr << "Conditional instruction has different inputs type: " << *inst << std::endl);
1496     if (inst->GetInputType(0) == DataType::REFERENCE) {
1497         ASSERT_DO(inst->CastToCompare()->GetCc() == ConditionCode::CC_NE ||
1498                       inst->CastToCompare()->GetCc() == ConditionCode::CC_EQ,
1499                   (std::cerr << "Reference compare must have CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
1500         if (op1->IsConst()) {
1501             ASSERT_DO(IsZeroConstant(op1), (std::cerr << "Constant reference input must be integer 0: \n",
1502                                             inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1503         } else {
1504             ASSERT_DO(op1->GetType() == DataType::REFERENCE,
1505                       (std::cerr << "Condition instruction 1st operand type is not a reference\n",
1506                        inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1507         }
1508         if (op2->IsConst()) {
1509             ASSERT_DO(IsZeroConstant(op2), (std::cerr << "Constant reference input must be integer 0: \n",
1510                                             inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
1511         } else {
1512             ASSERT_DO(op2->GetType() == DataType::REFERENCE,
1513                       (std::cerr << "Condition instruction 2nd operand type is not a reference\n",
1514                        inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
1515         }
1516     }
1517     ASSERT_PRINT(inst->GetType() == DataType::BOOL, "Condition instruction type is not a bool");
1518 }
1519 
1520 void GraphChecker::VisitCast([[maybe_unused]] GraphVisitor *v, Inst *inst)
1521 {
1522     [[maybe_unused]] auto dst_type = inst->GetType();
1523     [[maybe_unused]] auto src_type = inst->GetInputType(0);
1524     [[maybe_unused]] auto input_type = inst->GetInput(0).GetInst()->GetType();
1525 
1526     if (dst_type == DataType::ANY) {
1527         ASSERT_DO((DataType::IsTypeNumeric(input_type) || input_type == DataType::REFERENCE),
1528                   (std::cerr << "Cast instruction operand type is not a numeric or reference type\n",
1529                    inst->Dump(&std::cerr)));
1530     } else {
1531         ASSERT_DO(DataType::IsTypeNumeric(dst_type),
1532                   (std::cerr << "Cast instruction dst type is not a numeric type\n", inst->Dump(&std::cerr)));
1533         if (static_cast<GraphChecker *>(v)->GetGraph()->GetMode().SupportManagedCode()) {
1534             ASSERT_DO(DataType::IsTypeNumeric(src_type),
1535                       (std::cerr << "Cast instruction src type is not a numeric type\n", inst->Dump(&std::cerr)));
1536             ASSERT_DO(DataType::IsTypeNumeric(input_type),
1537                       (std::cerr << "Cast instruction operand type is not a numeric type\n", inst->Dump(&std::cerr)));
1538         }
1539         ASSERT_DO(DataType::GetCommonType(src_type) == DataType::GetCommonType(input_type),
1540                   (std::cerr << "Incorrect src_type and input type\n", inst->Dump(&std::cerr)));
1541         ASSERT_DO(!(DataType::IsFloatType(src_type) && DataType::IsLessInt32(dst_type)),
1542                   (std::cerr << "Cast instruction from " << DataType::internal::TYPE_NAMES.at(src_type) << " to "
1543                              << DataType::internal::TYPE_NAMES.at(dst_type) << " don't support\n",
1544                    inst->Dump(&std::cerr)));
1545     }
1546 }
1547 
1548 void GraphChecker::VisitCmp([[maybe_unused]] GraphVisitor *v, Inst *inst)
1549 {
1550     [[maybe_unused]] auto op1 = inst->GetInput(0).GetInst();
1551     [[maybe_unused]] auto op2 = inst->GetInput(1).GetInst();
1552     ASSERT_DO(DataType::IsTypeNumeric(op1->GetType()),
1553               (std::cerr << "Cmp instruction 1st operand type is not a numeric type\n", inst->Dump(&std::cerr),
1554                op1->Dump(&std::cerr)));
1555     ASSERT_DO(DataType::IsTypeNumeric(op2->GetType()),
1556               (std::cerr << "Cmp instruction 2st operand type is not a numeric type\n", inst->Dump(&std::cerr),
1557                op2->Dump(&std::cerr)));
1558     ASSERT_DO(
1559         DataType::GetCommonType(op1->GetType()) == DataType::GetCommonType(inst->GetInputType(0)),
1560         (std::cerr << "Input type and Cmp Input Type are not equal\n", inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1561     ASSERT_DO(
1562         DataType::GetCommonType(op2->GetType()) == DataType::GetCommonType(inst->GetInputType(1)),
1563         (std::cerr << "Input type and Cmp Input Type are not equal\n", inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
1564     ASSERT_DO(inst->GetType() == DataType::INT32,
1565               (std::cerr << "Cmp instruction type is not a int32\n", inst->Dump(&std::cerr)));
1566     for (size_t i = 0; i < inst->GetInputsCount(); i++) {
1567         ASSERT_DO(inst->GetInputType(i) != DataType::NO_TYPE,
1568                   std::cerr << "Source operand type is not set: " << *inst << std::endl);
1569     }
1570 }
1571 
1572 void GraphChecker::VisitMonitor([[maybe_unused]] GraphVisitor *v, Inst *inst)
1573 {
1574     [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
1575     ASSERT_DO(inst->GetType() == DataType::VOID, (std::cerr << "Monitor type is not a void", inst->Dump(&std::cerr)));
1576     ASSERT_DO(DataType::IsReference(op->GetType()),
1577               (std::cerr << "Monitor instruction 1st operand type is not a reference", inst->Dump(&std::cerr),
1578                op->Dump(&std::cerr)));
1579     [[maybe_unused]] auto op1 = inst->GetInputs()[1].GetInst();
1580     ASSERT_DO(op1->GetOpcode() == Opcode::SaveState,
1581               (std::cerr << "Monitor instruction second operand is not a SaveState", inst->Dump(&std::cerr),
1582                op1->Dump(&std::cerr)));
1583 }
1584 
1585 void GraphChecker::VisitReturn([[maybe_unused]] GraphVisitor *v, Inst *inst)
1586 {
1587     [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
1588     ASSERT_DO(CheckCommonTypes(inst, op), (std::cerr << "Types of return and its input are not compatible\n return:\n",
1589                                            inst->Dump(&std::cerr), std::cerr << "\n input:\n", op->Dump(&std::cerr)));
1590     CheckContrlFlowInst(inst);
1591     [[maybe_unused]] auto num_succs = inst->GetBasicBlock()->GetSuccsBlocks().size();
1592     ASSERT_PRINT(num_succs == 1, "Basic block with Return must have 1 successor");
1593     [[maybe_unused]] auto succ = inst->GetBasicBlock()->GetSuccsBlocks()[0];
1594     ASSERT_DO(succ->IsEndBlock() || succ->IsTryEnd(),
1595               std::cerr << "Basic block with Return must have end or try end block as successor:\n"
1596                         << *inst << std::endl);
1597 }
1598 
1599 void GraphChecker::VisitReturnVoid([[maybe_unused]] GraphVisitor *v, Inst *inst)
1600 {
1601     CheckContrlFlowInst(inst);
1602     [[maybe_unused]] auto num_succs = inst->GetBasicBlock()->GetSuccsBlocks().size();
1603     ASSERT_PRINT(num_succs == 1, "Basic block with ReturnVoid must have 1 successor");
1604     [[maybe_unused]] auto succ = inst->GetBasicBlock()->GetSuccsBlocks()[0];
1605     ASSERT_PRINT(succ->IsEndBlock() || succ->IsTryEnd(),
1606                  "Basic block with ReturnVoid must have end or try_end block as successor.");
1607 }
1608 
1609 void GraphChecker::VisitNullCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1610 {
1611     [[maybe_unused]] Inst *array = inst->GetInput(0).GetInst();
1612     ASSERT_DO(DataType::IsReference(array->GetType()) || array->GetType() == DataType::ANY,
1613               (std::cerr << "\n Types of input NullCheck must be REFERENCE or ANY: \n", inst->Dump(&std::cerr),
1614                array->Dump(&std::cerr)));
1615     [[maybe_unused]] auto ss = inst->GetInput(1).GetInst();
1616     ASSERT_DO(ss->GetOpcode() == Opcode::SaveState || ss->GetOpcode() == Opcode::SaveStateDeoptimize,
1617               (std::cerr << "\n Second input of NullCheck must be SaveState: \n", inst->Dump(&std::cerr),
1618                ss->Dump(&std::cerr)));
1619 }
1620 
1621 void GraphChecker::VisitBoundsCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1622 {
1623     for (int i = 0; i < 1; i++) {
1624         [[maybe_unused]] auto op = inst->GetInputs()[i].GetInst();
1625         [[maybe_unused]] auto op_type = op->GetType();
1626         // TODO(pishin): actually type should be INT32, but predecessor may be Call instruction with type u16, u8
1627         // e.t.c
1628         ASSERT_DO(
1629             (op->IsConst() && op_type == DataType::INT64) ||
1630                 (DataType::GetCommonType(op_type) == DataType::INT64 &&
1631                  Is32Bits(op_type, static_cast<GraphChecker *>(v)->GetGraph()->GetArch())),
1632             (std::cerr << "Types of " << i << " input BoundsCheck must be INT32 or less:\n", inst->Dump(&std::cerr)));
1633     }
1634     CheckThrows(inst, {Opcode::LoadArray, Opcode::StoreArray, Opcode::LoadArrayPair, Opcode::StoreArrayPair,
1635                        Opcode::Phi, Opcode::Intrinsic, Opcode::Store, Opcode::Load, Opcode::LoadCompressedStringChar});
1636 }
1637 
1638 void GraphChecker::VisitRefTypeCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1639 {
1640     ASSERT_DO((inst->GetType() == DataType::REFERENCE),
1641               (std::cerr << "Types of RefTypeCheck must be REFERENCE\n", inst->Dump(&std::cerr)));
1642     for (unsigned i = 0; i < 2U; i++) {
1643         [[maybe_unused]] auto op = inst->GetInputs()[i].GetInst();
1644         [[maybe_unused]] auto op_type = op->GetType();
1645         ASSERT_DO((op_type == DataType::REFERENCE),
1646                   (std::cerr << "Types of " << i << " input RefTypeCheck must be REFERENCE\n", inst->Dump(&std::cerr),
1647                    op->Dump(&std::cerr)));
1648     }
1649     CheckThrows(inst, {Opcode::StoreArray, Opcode::StoreArrayPair, Opcode::StoreArrayI, Opcode::StoreArrayPairI});
1650 }
1651 
1652 void GraphChecker::VisitNegativeCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1653 {
1654     [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
1655     [[maybe_unused]] auto op_type = op->GetType();
1656     ASSERT_DO(DataType::GetCommonType(op_type) == DataType::INT64,
1657               (std::cerr << "Type of NegativeCheck ZeroCheck must be integer\n", inst->Dump(&std::cerr)));
1658     CheckThrows(inst, {Opcode::NewArray, Opcode::MultiArray, Opcode::Phi});
1659 }
1660 
1661 void GraphChecker::VisitZeroCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1662 {
1663     [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
1664     [[maybe_unused]] auto op_type = op->GetType();
1665     ASSERT_DO(DataType::GetCommonType(op_type) == DataType::INT64,
1666               (std::cerr << "Type of ZeroCheck input must be integer\n", inst->Dump(&std::cerr)));
1667     CheckThrows(inst, {Opcode::Div, Opcode::Mod, Opcode::Phi});
1668 }
1669 
1670 void GraphChecker::VisitDeoptimizeIf([[maybe_unused]] GraphVisitor *v, Inst *inst)
1671 {
1672     [[maybe_unused]] auto op = inst->GetInput(0).GetInst();
1673     ASSERT_DO(op->GetType() == DataType::BOOL || op->IsBoolConst(),
1674               (std::cerr << "Type of first input DeoptimizeIf must be BOOL:\n", inst->Dump(&std::cerr)));
1675     [[maybe_unused]] auto ss = inst->GetInput(1).GetInst();
1676     ASSERT_DO(
1677         ss->GetOpcode() == Opcode::SaveStateDeoptimize || ss->GetOpcode() == Opcode::SaveState,
1678         (std::cerr << "Second input DeoptimizeIf must be SaveStateDeoptimize or SaveState:\n", inst->Dump(&std::cerr)));
1679 }
1680 
1681 void GraphChecker::VisitLenArray([[maybe_unused]] GraphVisitor *v, Inst *inst)
1682 {
1683     ASSERT_DO(inst->GetType() == DataType::INT32,
1684               (std::cerr << "Type of LenArray must be INT32:\n", inst->Dump(&std::cerr)));
1685     [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
1686     if (op->GetOpcode() == Opcode::NullCheck) {
1687         op = op->GetInput(0).GetInst();
1688     }
1689     ASSERT_DO(DataType::IsReference(op->GetType()), (std::cerr << "Types of input LenArray must be REFERENCE:\n",
1690                                                      inst->Dump(&std::cerr), op->Dump(&std::cerr)));
1691 }
1692 
1693 void GraphChecker::VisitiUnresolvedCallStatic([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1694 {
1695     ASSERT_DO(inst->CastToUnresolvedCallStatic()->GetCallMethod() != nullptr,
1696               (std::cerr << "UnresolvedCallStatic must have non-null MethodPtr", inst->Dump(&std::cerr)));
1697     [[maybe_unused]] auto ss = inst->GetInputs()[inst->GetInputsCount() - 1].GetInst();
1698     ASSERT_DO(ss->GetOpcode() == Opcode::SaveState,
1699               (std::cerr << "UnresolvedCallStatic instruction last operand is not a SaveState", inst->Dump(&std::cerr),
1700                ss->Dump(&std::cerr)));
1701 }
1702 
1703 void GraphChecker::VisitCallVirtual([[maybe_unused]] GraphVisitor *v, Inst *inst)
1704 {
1705     ASSERT_DO(inst->GetInputs().Size() > 0,
1706               (std::cerr << "Virtual function must have inputs:\n", inst->Dump(&std::cerr)));
1707     [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
1708     ASSERT_DO(DataType::IsReference(op->GetType()),
1709               (std::cerr << "Types of first input CallVirtual must be REFERENCE(this):\n", inst->Dump(&std::cerr),
1710                op->Dump(&std::cerr)));
1711 }
1712 
1713 void GraphChecker::VisitUnresolvedCallVirtual([[maybe_unused]] GraphVisitor *v, Inst *inst)
1714 {
1715     ASSERT_DO(inst->GetInputs().Size() > 0,
1716               (std::cerr << "Virtual function must have inputs:\n", inst->Dump(&std::cerr)));
1717     ASSERT_DO(inst->CastToUnresolvedCallVirtual()->GetCallMethod() != nullptr,
1718               (std::cerr << "UnresolvedCallVirtual must have non-null MethodPtr", inst->Dump(&std::cerr)));
1719     [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
1720     ASSERT_DO(DataType::IsReference(op->GetType()),
1721               (std::cerr << "Types of first input UnresolvedCallVirtual must be REFERENCE(this):\n",
1722                inst->Dump(&std::cerr), op->Dump(&std::cerr)));
1723     [[maybe_unused]] auto ss = inst->GetInputs()[inst->GetInputsCount() - 1].GetInst();
1724     ASSERT_DO(ss->GetOpcode() == Opcode::SaveState,
1725               (std::cerr << "UnresolvedCallVirtual instruction last operand is not a SaveState", inst->Dump(&std::cerr),
1726                ss->Dump(&std::cerr)));
1727 }
1728 
1729 void GraphChecker::VisitCallDynamic([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1730 {
1731     ASSERT_DO(static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
1732               (std::cerr << "CallDynamic is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
1733 }
1734 
1735 void GraphChecker::VisitSaveState([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1736 {
1737     ASSERT_DO((static_cast<SaveStateInst *>(inst))->Verify(), std::cerr << "Inconsistent SaveState instruction:\n"
1738                                                                         << *inst << std::endl);
1739 #ifndef NDEBUG
1740     auto ss = inst->CastToSaveState();
1741     if (ss->GetInputsWereDeleted()) {
1742         for (auto &user : inst->GetUsers()) {
1743             ASSERT_DO(!user.GetInst()->RequireRegMap(),
1744                       std::cerr << "Some inpust from save_state were deleted, but the user requireRegMap:\n"
1745                                 << *inst << std::endl
1746                                 << *(user.GetInst()) << std::endl);
1747         }
1748     }
1749 #endif
1750 }
1751 
1752 void GraphChecker::VisitSafePoint([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1753 {
1754     ASSERT_DO(!inst->HasUsers(), std::cerr << "SafePoint must not have users:\n" << *inst << std::endl);
1755     ASSERT_DO((static_cast<SaveStateInst *>(inst))->Verify(), std::cerr << "Inconsistent SafePoint instruction:\n"
1756                                                                         << *inst << std::endl);
1757 }
1758 
1759 void GraphChecker::VisitSaveStateOsr([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1760 {
1761     ASSERT_DO(!inst->HasUsers(), std::cerr << "SafeStateOsr must not have users:\n" << *inst << std::endl);
1762     ASSERT_DO((static_cast<SaveStateInst *>(inst))->Verify(), std::cerr << "Inconsistent SafeStateOsr instruction:\n"
1763                                                                         << *inst << std::endl);
1764     ASSERT_DO(static_cast<GraphChecker *>(v)->GetGraph()->IsOsrMode(),
1765               std::cerr << "SafeStateOsr must be created in the OSR mode only\n");
1766     ASSERT_DO(inst->GetBasicBlock()->IsOsrEntry(), std::cerr << "SafeStateOsr's basic block must be osr-entry\n");
1767     auto first_inst = inst->GetBasicBlock()->GetFirstInst();
1768     while (first_inst != nullptr && (first_inst->IsCatchPhi() || first_inst->GetOpcode() == Opcode::Try)) {
1769         first_inst = first_inst->GetNext();
1770     }
1771     ASSERT_DO(first_inst == inst, std::cerr << "SafeStateOsr must be the first instruction in the basic block\n");
1772 }
1773 
1774 void GraphChecker::VisitThrow([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1775 {
1776     ASSERT_DO(DataType::IsReference(inst->GetInput(0).GetInst()->GetType()),
1777               std::cerr << "Throw instruction must have input with reference type: " << *inst << std::endl);
1778     [[maybe_unused]] auto bb = inst->GetBasicBlock();
1779     ASSERT_DO(inst == bb->GetLastInst(),
1780               std::cerr << "Throw instruction must be last instruction in the basic block: " << *inst << std::endl);
1781     for ([[maybe_unused]] auto succ : bb->GetSuccsBlocks()) {
1782         ASSERT_DO(succ->IsEndBlock() || succ->IsTryEnd(),
1783                   std::cerr << "Throw block must have end block or try-end block as successor\n");
1784     }
1785 }
1786 
1787 void GraphChecker::VisitCheckCast([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1788 {
1789     ASSERT_DO(DataType::IsReference(inst->GetInput(0).GetInst()->GetType()),
1790               std::cerr << "CheckCast instruction must have input 0 with reference type: " << *inst << std::endl);
1791 
1792     ASSERT_DO(DataType::IsReference(inst->GetInput(1).GetInst()->GetType()),
1793               std::cerr << "CheckCast instruction must have input 1 with reference type: " << *inst << std::endl);
1794 
1795     [[maybe_unused]] auto save_state = inst->GetInput(2).GetInst();
1796     ASSERT_DO((save_state != nullptr && save_state->GetOpcode() == Opcode::SaveState),
1797               std::cerr << "CheckCast instruction must have SaveState as input 2: " << *inst << std::endl);
1798 }
1799 
1800 void GraphChecker::VisitIsInstance([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1801 {
1802     ASSERT_DO(DataType::IsReference(inst->GetInput(0).GetInst()->GetType()),
1803               std::cerr << "IsInstance instruction must have input 0 with reference type: " << *inst << std::endl);
1804     ASSERT_DO(DataType::IsReference(inst->GetInput(1).GetInst()->GetType()),
1805               std::cerr << "IsInstance instruction must have input 1 with reference type: " << *inst << std::endl);
1806 
1807     [[maybe_unused]] auto save_state = inst->GetInput(2).GetInst();
1808     ASSERT_DO((save_state != nullptr && save_state->GetOpcode() == Opcode::SaveState),
1809               std::cerr << "IsInstance instruction must have SaveState as input 2: " << *inst << std::endl);
1810 
1811     ASSERT_DO(inst->GetType() == DataType::BOOL,
1812               (std::cerr << "Types of IsInstance must be BOOL:\n", inst->Dump(&std::cerr)));
1813 }
1814 
1815 void GraphChecker::VisitSelect([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1816 {
1817     [[maybe_unused]] auto op0 = inst->GetInput(0).GetInst();
1818     [[maybe_unused]] auto op1 = inst->GetInput(1).GetInst();
1819     [[maybe_unused]] auto op2 = inst->GetInput(2U).GetInst();
1820     [[maybe_unused]] auto op3 = inst->GetInput(3U).GetInst();
1821 
1822     for (size_t i = 0; i < inst->GetInputsCount(); i++) {
1823         ASSERT_DO(inst->GetInputType(i) != DataType::NO_TYPE,
1824                   std::cerr << "Source operand type is not set: " << *inst << std::endl);
1825     }
1826 
1827     ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64 || inst->GetType() == DataType::REFERENCE ||
1828                   inst->GetType() == DataType::ANY,
1829               (std::cerr << "Select instruction type is not integer or reference or any", inst->Dump(&std::cerr)));
1830     ASSERT_DO(DataType::GetCommonType(op0->GetType()) == DataType::INT64 || op0->GetType() == DataType::REFERENCE ||
1831                   op0->GetType() == DataType::ANY,
1832               (std::cerr << "Select instruction 1st operand type is not integer or reference or any",
1833                inst->Dump(&std::cerr)));
1834     ASSERT_DO(DataType::GetCommonType(op1->GetType()) == DataType::INT64 || op1->GetType() == DataType::REFERENCE ||
1835                   op1->GetType() == DataType::ANY,
1836               (std::cerr << "Select instruction 2nd operand type is not integer or reference or any",
1837                inst->Dump(&std::cerr)));
1838 
1839     ASSERT_DO(CheckCommonTypes(op0, op1),
1840               (std::cerr << "Types of two first select instruction operands are not compatible\n",
1841                op0->Dump(&std::cerr), op1->Dump(&std::cerr), inst->Dump(&std::cerr)));
1842     ASSERT_DO(
1843         CheckCommonTypes(inst, op0),
1844         (std::cerr << "Types of instruction result and its operands are not compatible\n", inst->Dump(&std::cerr)));
1845 
1846     ASSERT_DO(inst->GetInputType(2U) == inst->GetInputType(3U),
1847               std::cerr << "Select comparison arguments has different inputs type: " << *inst << std::endl);
1848     if (inst->GetInputType(2U) == DataType::REFERENCE) {
1849         [[maybe_unused]] auto cc = inst->CastToSelect()->GetCc();
1850         ASSERT_DO(cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
1851                   (std::cerr << "Select reference comparison must be CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
1852         if (op2->IsConst()) {
1853             ASSERT_DO(IsZeroConstant(op2), (std::cerr << "Constant reference input must be integer 0: \n",
1854                                             inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1855         } else {
1856             ASSERT_DO(op2->GetType() == DataType::REFERENCE,
1857                       (std::cerr << "Select instruction 3rd operand type is not a reference\n", inst->Dump(&std::cerr),
1858                        op1->Dump(&std::cerr)));
1859         }
1860         if (op3->IsConst()) {
1861             ASSERT_DO(IsZeroConstant(op3), (std::cerr << "Constant reference input must be integer 0: \n",
1862                                             inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
1863         } else {
1864             ASSERT_DO(op3->GetType() == DataType::REFERENCE,
1865                       (std::cerr << "Select instruction 4th operand type is not a reference\n", inst->Dump(&std::cerr),
1866                        op2->Dump(&std::cerr)));
1867         }
1868     }
1869 }
1870 
1871 void GraphChecker::VisitSelectImm([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1872 {
1873     [[maybe_unused]] auto op0 = inst->GetInput(0).GetInst();
1874     [[maybe_unused]] auto op1 = inst->GetInput(1).GetInst();
1875     [[maybe_unused]] auto op2 = inst->GetInput(2U).GetInst();
1876     [[maybe_unused]] auto op3 = inst->CastToSelectImm()->GetImm();
1877     [[maybe_unused]] bool is_dynamic = static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod();
1878 
1879     for (size_t i = 0; i < inst->GetInputsCount(); i++) {
1880         ASSERT_DO(inst->GetInputType(i) != DataType::NO_TYPE,
1881                   std::cerr << "Source operand type is not set: " << *inst << std::endl);
1882     }
1883 
1884     ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64 || inst->GetType() == DataType::REFERENCE ||
1885                   (is_dynamic && inst->GetType() == DataType::ANY),
1886               (std::cerr << "SelectImm instruction type is not integer or reference or any", inst->Dump(&std::cerr)));
1887     ASSERT_DO(DataType::GetCommonType(op0->GetType()) == DataType::INT64 || op0->GetType() == DataType::REFERENCE ||
1888                   (is_dynamic && op0->GetType() == DataType::ANY),
1889               (std::cerr << "SelectImm instruction 1st operand type is not integer or reference or any",
1890                inst->Dump(&std::cerr)));
1891     ASSERT_DO(DataType::GetCommonType(op1->GetType()) == DataType::INT64 || op1->GetType() == DataType::REFERENCE ||
1892                   (is_dynamic && op1->GetType() == DataType::ANY),
1893               (std::cerr << "SelectImm instruction 2nd operand type is not integer or reference or any",
1894                inst->Dump(&std::cerr)));
1895 
1896     ASSERT_DO(CheckCommonTypes(op0, op1),
1897               (std::cerr << "Types of two first SelectImm instruction operands are not compatible\n",
1898                op0->Dump(&std::cerr), op1->Dump(&std::cerr), inst->Dump(&std::cerr)));
1899     ASSERT_DO(
1900         CheckCommonTypes(inst, op0),
1901         (std::cerr << "Types of instruction result and its operands are not compatible\n", inst->Dump(&std::cerr)));
1902 
1903     if (inst->GetInputType(2U) == DataType::REFERENCE) {
1904         [[maybe_unused]] auto cc = inst->CastToSelectImm()->GetCc();
1905         ASSERT_DO(cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
1906                   (std::cerr << "SelectImm reference comparison must be CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
1907         if (op2->IsConst()) {
1908             ASSERT_DO(IsZeroConstant(op2), (std::cerr << "Constant reference input must be integer 0: \n",
1909                                             inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1910         } else {
1911             ASSERT_DO(op2->GetType() == DataType::REFERENCE,
1912                       (std::cerr << "Condition with immediate jump 1st operand type is not a reference\n",
1913                        inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1914         }
1915         ASSERT_DO(op3 == 0,
1916                   (std::cerr << "Reference can be compared only with 0 immediate: \n", inst->Dump(&std::cerr)));
1917     } else {
1918         ASSERT_PRINT(DataType::GetCommonType(op2->GetType()) == DataType::INT64 ||
1919                          (is_dynamic && DataType::GetCommonType(op2->GetType()) == DataType::ANY),
1920                      "SelectImm 3rd operand type is not an integer or any");
1921 
1922         if (DataType::GetCommonType(op2->GetType()) == DataType::ANY) {
1923             [[maybe_unused]] auto cc = inst->CastToSelectImm()->GetCc();
1924             ASSERT_DO(cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
1925                       (std::cerr << "SelectImm any comparison must be CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
1926         }
1927     }
1928 }
1929 
1930 void GraphChecker::VisitIf([[maybe_unused]] GraphVisitor *v, Inst *inst)
1931 {
1932     CheckContrlFlowInst(inst);
1933     [[maybe_unused]] auto num_succs = inst->GetBasicBlock()->GetSuccsBlocks().size();
1934     ASSERT_PRINT(num_succs == MAX_SUCCS_NUM, "Basic block with If must have 2 successesors");
1935 
1936     [[maybe_unused]] auto op1 = inst->GetInputs()[0].GetInst();
1937     [[maybe_unused]] auto op2 = inst->GetInputs()[1].GetInst();
1938     for (size_t i = 0; i < inst->GetInputsCount(); i++) {
1939         ASSERT_DO(inst->GetInputType(i) != DataType::NO_TYPE,
1940                   std::cerr << "Source operand type is not set: " << *inst << std::endl);
1941     }
1942     ASSERT_DO(inst->GetInputType(0) == inst->GetInputType(1),
1943               std::cerr << "If has different inputs type: " << *inst << std::endl);
1944     if (inst->GetInputType(0) == DataType::REFERENCE) {
1945         [[maybe_unused]] auto cc = inst->CastToIf()->GetCc();
1946         ASSERT_DO(cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
1947                   (std::cerr << "Reference comparison in If must be CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
1948         if (op1->IsConst()) {
1949             ASSERT_DO(IsZeroConstant(op1), (std::cerr << "Constant reference input must be integer 0: \n",
1950                                             inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1951         } else {
1952             ASSERT_DO(op1->GetType() == DataType::REFERENCE, (std::cerr << "If 1st operand type is not a reference\n",
1953                                                               inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1954         }
1955         if (op2->IsConst()) {
1956             ASSERT_DO(IsZeroConstant(op2), (std::cerr << "Constant reference input must be integer 0: \n",
1957                                             inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
1958         } else {
1959             ASSERT_DO(op2->GetType() == DataType::REFERENCE, (std::cerr << "If 2nd operand type is not a reference\n",
1960                                                               inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
1961         }
1962     }
1963 }
1964 
1965 void GraphChecker::VisitIfImm([[maybe_unused]] GraphVisitor *v, Inst *inst)
1966 {
1967     CheckContrlFlowInst(inst);
1968     [[maybe_unused]] auto num_succs = inst->GetBasicBlock()->GetSuccsBlocks().size();
1969     ASSERT_PRINT(num_succs == MAX_SUCCS_NUM, "Basic block with IfImm must have 2 successesors");
1970 
1971     [[maybe_unused]] auto op1 = inst->GetInput(0).GetInst();
1972     [[maybe_unused]] auto op2 = inst->CastToIfImm()->GetImm();
1973     ASSERT_DO(inst->GetInputType(0) != DataType::NO_TYPE,
1974               std::cerr << "Source operand type is not set: " << *inst << std::endl);
1975     if (inst->GetInputType(0) == DataType::REFERENCE) {
1976         [[maybe_unused]] auto cc = inst->CastToIfImm()->GetCc();
1977         ASSERT_DO(cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
1978                   (std::cerr << "Reference comparison in IfImm must have CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
1979         if (op1->IsConst()) {
1980             ASSERT_DO(IsZeroConstant(op1), (std::cerr << "Constant reference input must be integer 0: \n",
1981                                             inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1982         } else {
1983             ASSERT_DO(op1->GetType() == DataType::REFERENCE,
1984                       (std::cerr << "IfImm operand type should be here a reference: \n", inst->Dump(&std::cerr),
1985                        op1->Dump(&std::cerr)));
1986         }
1987         ASSERT_DO(op2 == 0,
1988                   (std::cerr << "Reference can be compared only with 0 immediate: \n", inst->Dump(&std::cerr)));
1989     } else {
1990         ASSERT_PRINT(
1991             DataType::GetCommonType(op1->GetType()) == DataType::INT64 ||
1992                 (static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod() && op1->GetType() == DataType::ANY),
1993             "IfImm operand type should be here an integer");
1994     }
1995 }
1996 
1997 void GraphChecker::VisitTry([[maybe_unused]] GraphVisitor *v, Inst *inst)
1998 {
1999     [[maybe_unused]] auto bb = inst->GetBasicBlock();
2000     ASSERT_PRINT(bb->IsTryBegin(), "TryInst should be placed in the try-begin basic block");
2001 }
2002 
2003 void GraphChecker::VisitNOP([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2004 {
2005     ASSERT_PRINT(inst->GetUsers().Empty(), "NOP can not have users\n");
2006 }
2007 
2008 void GraphChecker::VisitAndNot([[maybe_unused]] GraphVisitor *v, Inst *inst)
2009 {
2010     CheckBinaryOperationTypes(inst, true);
2011 }
2012 void GraphChecker::VisitOrNot([[maybe_unused]] GraphVisitor *v, Inst *inst)
2013 {
2014     CheckBinaryOperationTypes(inst, true);
2015 }
2016 void GraphChecker::VisitXorNot([[maybe_unused]] GraphVisitor *v, Inst *inst)
2017 {
2018     CheckBinaryOperationTypes(inst, true);
2019 }
2020 void GraphChecker::VisitMNeg([[maybe_unused]] GraphVisitor *v, Inst *inst)
2021 {
2022     CheckBinaryOperationTypes(inst, false);
2023 }
2024 void GraphChecker::VisitMAdd([[maybe_unused]] GraphVisitor *v, Inst *inst)
2025 {
2026     CheckTernaryOperationTypes(inst);
2027 }
2028 void GraphChecker::VisitMSub([[maybe_unused]] GraphVisitor *v, Inst *inst)
2029 {
2030     CheckTernaryOperationTypes(inst);
2031 }
2032 
2033 void GraphChecker::VisitCompareAnyType([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2034 {
2035     ASSERT_DO(static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
2036               (std::cerr << "CompareAnyType is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
2037 }
2038 
2039 void GraphChecker::VisitCastAnyTypeValue([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2040 {
2041     ASSERT_DO(static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
2042               (std::cerr << "CastAnyTypeValue is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
2043 }
2044 
2045 void GraphChecker::VisitCastValueToAnyType([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2046 {
2047     ASSERT_DO(static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
2048               (std::cerr << "CastValueToAnyType is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
2049 
2050     const auto *input_inst = inst->GetInput(0).GetInst();
2051     auto input_type = inst->GetInputType(0);
2052     auto output_type = AnyBaseTypeToDataType(inst->CastToCastValueToAnyType()->GetAnyType());
2053 
2054     ASSERT_DO(input_type != DataType::ANY,
2055               (std::cerr << "CastValueToAnyType cannot accept inputs of ANY type:\n", inst->Dump(&std::cerr)));
2056 
2057     if (input_inst->IsConst() && (input_type == DataType::Type::INT64 || input_type == DataType::Type::INT32)) {
2058         if (output_type == DataType::Type::BOOL) {
2059             ASSERT_DO(input_inst->IsBoolConst(),
2060                       (std::cerr << "Integral constant input not coercible to BOOL:\n", inst->Dump(&std::cerr)));
2061             return;
2062         }
2063 
2064         if (output_type == DataType::INT32 && input_type == DataType::INT64) {
2065             [[maybe_unused]] int64_t value = input_inst->CastToConstant()->GetInt64Value();
2066             ASSERT_DO(value == static_cast<int32_t>(value),
2067                       (std::cerr << "Integral constant input not coercible to INT32:\n", inst->Dump(&std::cerr)));
2068             return;
2069         }
2070 
2071         if (output_type == DataType::Type::REFERENCE) {
2072             return;  // Always coercible
2073         }
2074 
2075         if (output_type == DataType::Type::VOID) {
2076             return;  // Always coercible
2077         }
2078 
2079         // Otherwise proceed with the generic check.
2080     }
2081 }
2082 
2083 void GraphChecker::VisitAnyTypeCheck([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2084 {
2085     ASSERT_DO(static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
2086               (std::cerr << "AnyTypeCheck is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
2087     ASSERT_DO(inst->GetInput(0).GetInst()->GetType() == DataType::Type::ANY,
2088               (std::cerr << "First input in AnyTypeCheck must be Any type:\n", inst->Dump(&std::cerr)));
2089     ASSERT_DO(inst->GetInput(1).GetInst()->IsSaveState(),
2090               (std::cerr << "Second input in AnyTypeCheck must be SaveState:\n", inst->Dump(&std::cerr)));
2091 }
2092 
2093 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
2094 #define VisitBinaryShiftedRegister(opc)                                                         \
2095     void GraphChecker::Visit##opc(GraphVisitor *v, Inst *inst)                                  \
2096     {                                                                                           \
2097         CheckBinaryOperationWithShiftedOperandTypes(                                            \
2098             v, inst, inst->GetOpcode() != Opcode::AddSR && inst->GetOpcode() != Opcode::SubSR); \
2099     }
2100 
2101 VisitBinaryShiftedRegister(AddSR) VisitBinaryShiftedRegister(SubSR) VisitBinaryShiftedRegister(AndSR)
2102     VisitBinaryShiftedRegister(OrSR) VisitBinaryShiftedRegister(XorSR) VisitBinaryShiftedRegister(AndNotSR)
2103         VisitBinaryShiftedRegister(OrNotSR) VisitBinaryShiftedRegister(XorNotSR)
2104 #undef VisitBinaryShiftedRegister
2105 
2106             void GraphChecker::VisitNegSR([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2107 {
2108     ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
2109               (std::cerr << "NegSR must have integer type\n", inst->Dump(&std::cerr)));
2110     CheckUnaryOperationTypes(inst);
2111     [[maybe_unused]] auto shift_type = static_cast<UnaryShiftedRegisterOperation *>(inst)->GetShiftType();
2112     ASSERT_DO(shift_type != ShiftType::INVALID_SHIFT && shift_type != ShiftType::ROR,
2113               (std::cerr << "Operation has invalid shift type\n", inst->Dump(&std::cerr)));
2114 }
2115 
2116 }  // namespace panda::compiler
2117