• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 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_checker.h"
18 #include "graph_cloner.h"
19 #include "graph_checker_macros.h"
20 #include "optimizer/analysis/dominators_tree.h"
21 #include "optimizer/analysis/rpo.h"
22 #include "optimizer/analysis/linear_order.h"
23 #include "optimizer/analysis/loop_analyzer.h"
24 #include "optimizer/ir/analysis.h"
25 #include "optimizer/ir/datatype.h"
26 #include "optimizer/ir/inst.h"
27 #include "optimizer/optimizations/cleanup.h"
28 #include "inst_checker_gen.h"
29 
30 namespace ark::compiler {
31 
GraphChecker(Graph * graph)32 GraphChecker::GraphChecker(Graph *graph)
33 {
34     graph_ = graph;
35     PreCloneChecks(graph);
36     graph_ = GraphCloner(graph, GetAllocator(), GetLocalAllocator()).CloneGraph();
37     GetGraph()->GetPassManager()->SetCheckMode(true);
38     passName_ = std::string("");
39 }
40 
GraphChecker(Graph * graph,const char * passName)41 GraphChecker::GraphChecker(Graph *graph, const char *passName)
42 {
43     graph_ = graph;
44     PreCloneChecks(graph);
45     graph_ = GraphCloner(graph, GetAllocator(), GetLocalAllocator()).CloneGraph();
46     GetGraph()->GetPassManager()->SetCheckMode(true);
47     passName_ = std::string(passName);
48     ASSERT(passName != nullptr);
49 }
50 
PreCloneChecks(Graph * graph)51 void GraphChecker::PreCloneChecks(Graph *graph)
52 {
53     UserInputCheck(graph);
54 }
55 
UserInputCheck(Graph * graph)56 void GraphChecker::UserInputCheck(Graph *graph)
57 {
58     for (auto block : graph->GetVectorBlocks()) {
59         if (block == nullptr) {
60             continue;
61         }
62         for (auto inst : block->AllInsts()) {
63             auto u = inst->GetFirstUser();
64             CHECKER_IF_NOT_PRINT(u == nullptr || u->GetPrev() == nullptr);
65             while (u != nullptr) {
66                 CHECKER_IF_NOT_PRINT(u->GetNext() == nullptr || u->GetNext()->GetPrev() == u);
67                 u = u->GetNext();
68             }
69             for (auto &user : inst->GetUsers()) {
70                 [[maybe_unused]] auto userInst = user.GetInst();
71                 CHECKER_IF_NOT_PRINT(userInst->GetBasicBlock() != nullptr);
72                 CHECKER_DO_IF_NOT_AND_PRINT(CheckInstHasInput(userInst, inst),
73                                             std::cerr << "Instruction is not an input to its user\n"
74                                                       << "input: " << *inst << std::endl
75                                                       << "user:  " << *userInst << std::endl);
76             }
77             for (auto &input : inst->GetInputs()) {
78                 [[maybe_unused]] auto inputInst = input.GetInst();
79                 CHECKER_DO_IF_NOT_AND_PRINT(inputInst != nullptr && inputInst->GetBasicBlock() != nullptr,
80                                             std::cerr << "Instruction has invalid input:\n"
81                                                       << "user: " << *inst << std::endl);
82                 CHECKER_DO_IF_NOT_AND_PRINT(CheckInstHasUser(inputInst, inst),
83                                             std::cerr << "Instruction is not a user to its input:\n"
84                                                       << "user: " << *inst << std::endl
85                                                       << "input:  " << *inputInst << std::endl);
86             }
87             // Check `require_state` flag
88             auto it = std::find_if(inst->GetInputs().begin(), inst->GetInputs().end(),
89                                    [](Input input) { return input.GetInst()->IsSaveState(); });
90             [[maybe_unused]] bool hasSaveState = (it != inst->GetInputs().end());
91             CHECKER_DO_IF_NOT_AND_PRINT(inst->RequireState() == hasSaveState,
92                                         std::cerr << "Incorrect 'require_state' flag in the inst: " << *inst);
93             if (inst->RequireState()) {
94                 CHECKER_IF_NOT_PRINT(it->GetInst() == inst->GetSaveState());
95             }
96             if (inst->IsPhi()) {
97                 // causes failure in GraphCloner::BuildDataFlow if not checked before clone
98                 CHECKER_DO_IF_NOT_AND_PRINT(inst->GetInputsCount() == block->GetPredsBlocks().size(),
99                                             std::cerr << "Incorrect phi's inputs count: " << inst->GetInputsCount()
100                                                       << " inputs, " << block->GetPredsBlocks().size()
101                                                       << " pred blocks\n"
102                                                       << *inst);
103             }
104         }
105     }
106 }
107 
Check()108 bool GraphChecker::Check()
109 {
110     success_ = InstChecker::Run(GetGraph());
111     CHECK_STATUS();
112 
113 #ifdef COMPILER_DEBUG_CHECKS
114     if (GetGraph()->IsAnalysisValid<DominatorsTree>()) {
115         CheckDomTree();
116     } else {
117         GetGraph()->RunPass<DominatorsTree>();
118     }
119     CHECK_STATUS();
120     if (GetGraph()->IsAnalysisValid<LoopAnalyzer>()) {
121         CheckLoopAnalysis();
122     } else {
123         GetGraph()->RunPass<LoopAnalyzer>();
124     }
125     CHECK_STATUS();
126     CheckStartBlock();
127     CHECK_STATUS();
128     CheckEndBlock();
129     CHECK_STATUS();
130     size_t blocksCount = 0;
131     size_t blocksId = -1;
132     for (auto block : GetGraph()->GetVectorBlocks()) {
133         ++blocksId;
134         if (block == nullptr) {
135             continue;
136         }
137         CHECKER_MESSAGE_IF_NOT_AND_PRINT(block->GetGraph() == GetGraph(), "Block linked to incorrect graph");
138         CHECKER_MESSAGE_IF_NOT_AND_PRINT(block->GetId() == blocksId,
139                                          "Block ID must be equal to its ID in graph vector");
140         CheckBlock(block);
141         CHECK_STATUS();
142         blocksCount++;
143     }
144     CHECKER_MESSAGE_IF_NOT_AND_PRINT(blocksCount == GetGraph()->GetBlocksRPO().size(), "There is disconnected block");
145     CheckLoops();
146     CHECK_STATUS();
147     // Visit graph to check instructions types
148     CheckGraph();
149     CHECK_STATUS();
150     // Check that call.Inlined and Return.Inlined in correct order
151     // and check that savestate has correct link to call.inlined.
152     CheckCallReturnInlined();
153     CHECK_STATUS();
154     if (NeedCheckSaveState()) {
155         // Check that objects in stack.
156         CheckSaveStateInputs();
157         // Check that between savestate and it's runtime call user have not reference insts.
158         CheckSaveStatesWithRuntimeCallUsers();
159     }
160 #endif  // COMPILER_DEBUG_CHECKS
161     return GetStatus();
162 }
163 
164 #ifdef COMPILER_DEBUG_CHECKS
NeedCheckSaveState()165 bool GraphChecker::NeedCheckSaveState()
166 {
167     return !GetGraph()->IsBytecodeOptimizer() && GetGraph()->GetParentGraph() == nullptr &&
168            GetGraph()->IsInliningComplete();
169 }
170 #endif  // COMPILER_DEBUG_CHECKS
171 
CheckBlock(BasicBlock * block)172 void GraphChecker::CheckBlock([[maybe_unused]] BasicBlock *block)
173 {
174 #ifdef COMPILER_DEBUG_CHECKS
175     CheckControlFlow(block);
176     CheckDataFlow(block);
177     for (auto phiInst : block->PhiInsts()) {
178         CheckPhiInputs(phiInst);
179     }
180     if (!GetGraph()->IsLowLevelInstructionsEnabled() && !GetGraph()->IsDynamicMethod() && !GetGraph()->IsAbcKit()) {
181         CheckNoLowLevel(block);
182     }
183     if (!block->IsEndBlock() && !block->IsStartBlock()) {
184         CheckBlockEdges(*block);
185     }
186     if (block->IsTryBegin()) {
187         CheckTryBeginBlock(*block);
188     }
189     if (block->NeedsJump()) {
190         CheckJump(*block);
191     }
192 #endif  // COMPILER_DEBUG_CHECKS
193 }
194 
CheckControlFlow(BasicBlock * block)195 void GraphChecker::CheckControlFlow(BasicBlock *block)
196 {
197     auto numSuccs = block->GetSuccsBlocks().size();
198     CHECKER_MESSAGE_IF_NOT_AND_PRINT(block->IsEndBlock() || block->IsTryBegin() || block->IsTryEnd() ||
199                                          (numSuccs > 0 && numSuccs <= MAX_SUCCS_NUM) ||
200                                          block->GetLastInst()->GetOpcode() == Opcode::Throw,
201                                      "Non-end block and non-try-begin block should have 1 or 2 successesors");
202 
203     for ([[maybe_unused]] auto pred : block->GetPredsBlocks()) {
204         CHECKER_DO_IF_NOT_AND_PRINT(CheckBlockHasSuccessor(pred, block),
205                                     std::cerr << "Block " << block->GetId() << " is not a successor to its predecessor "
206                                               << pred->GetId());
207     }
208     for ([[maybe_unused]] auto succ : block->GetSuccsBlocks()) {
209         CHECKER_MESSAGE_IF_NOT_AND_PRINT(CheckBlockHasPredecessor(succ, block),
210                                          "Block is not a predecessor to its successor");
211     }
212 
213     if (numSuccs == MAX_SUCCS_NUM) {
214         CHECKER_MESSAGE_IF_NOT_AND_PRINT(block->GetSuccessor(0) != block->GetSuccessor(1),
215                                          "Wrong CFG - block with two same successors");
216     }
217 }
218 
PrintFailedMethodAndPass() const219 void GraphChecker::PrintFailedMethodAndPass() const
220 {
221     auto methodName = GetGraph()->GetRuntime()->GetMethodFullName(GetGraph()->GetMethod(), false);
222     std::cerr << std::endl
223               << "Failed method: " << methodName << std::endl
224               << (!GetPassName().empty() ? ("After pass: " + GetPassName() + "\n") : "");
225 }
PrintFailedMethodAndPassVisitor(GraphVisitor * v)226 void GraphChecker::PrintFailedMethodAndPassVisitor(GraphVisitor *v)
227 {
228     auto graphChecker = static_cast<GraphChecker *>(v);
229     auto methodName =
230         graphChecker->GetGraph()->GetRuntime()->GetMethodFullName(graphChecker->GetGraph()->GetMethod(), false);
231     std::cerr << std::endl
232               << "Failed method: " << methodName << std::endl
233               << (!graphChecker->GetPassName().empty() ? ("After pass: " + graphChecker->GetPassName() + "\n") : "");
234 }
235 
CheckInputType(Inst * inst)236 void GraphChecker::CheckInputType(Inst *inst)
237 {
238     // NOTE(mbolshov): Fix types for LiveOut in irtoc
239     if (inst->GetOpcode() == Opcode::LiveOut) {
240         return;
241     }
242     // NOTE(dkofanov): Fix get input type for call insts
243     if (inst->IsCall()) {
244         return;
245     }
246     for (size_t i = 0; i < inst->GetInputsCount(); ++i) {
247         [[maybe_unused]] auto input = inst->GetInput(i).GetInst();
248         // NOTE(mbolshov): Fix types for LiveIn in irtoc
249         if (input->GetOpcode() == Opcode::LiveIn) {
250             continue;
251         }
252         [[maybe_unused]] auto inputType = GetCommonType(inst->GetInputType(i));
253         [[maybe_unused]] auto realInputType = GetCommonType(input->GetType());
254         if (GetGraph()->IsAbcKit()) {
255             if (inputType == DataType::ANY && input->IsConst() && (inst->IsIntrinsic() || inst->IsPhi())) {
256                 continue;
257             }
258         }
259         CHECKER_DO_IF_NOT_AND_PRINT(
260             inputType == realInputType ||
261                 (realInputType == DataType::ANY &&
262                  (inputType == DataType::REFERENCE || inputType == DataType::POINTER)) ||
263                 (IsZeroConstant(input) && (inputType == DataType::REFERENCE || inputType == DataType::POINTER)) ||
264                 (inputType == DataType::ANY && input->IsConst() && realInputType == DataType::INT64) ||
265                 (GetGraph()->IsThrowApplied() &&
266                  (input->GetBasicBlock()->IsEndWithThrow() ||
267                   ((input->GetOpcode() == Opcode::CatchPhi || input->GetOpcode() == Opcode::Phi) &&
268                    (inst->GetOpcode() == Opcode::Phi || inst->GetOpcode() == Opcode::CatchPhi)))),
269             (std::cerr << "Input type don't equal to real input type\n"
270                        << "inst: " << *inst << std::endl
271                        << "input type: " << DataType::ToString(inputType) << std::endl
272                        << "input: " << *input << std::endl,
273              GetGraph()->Dump(&std::cerr)));
274     }
275 }
276 
GetInliningDepth(Inst * inst)277 uint32_t GetInliningDepth(Inst *inst)
278 {
279     uint32_t inliningDepth = 0;
280     auto ss = inst->IsSaveState() ? inst : inst->GetSaveState();
281     while (ss != nullptr) {
282         auto caller = static_cast<SaveStateInst *>(ss)->GetCallerInst();
283         if (caller == nullptr) {
284             break;
285         }
286         ss = caller->GetSaveState();
287         inliningDepth++;
288     }
289     return inliningDepth;
290 }
291 
CheckUserOfInt32(BasicBlock * block,Inst * inst,User & user)292 void GraphChecker::CheckUserOfInt32([[maybe_unused]] BasicBlock *block, Inst *inst, User &user)
293 {
294     ASSERT(DataType::Is32Bits(inst->GetType(), GetGraph()->GetArch()));
295     auto *userInst = user.GetInst();
296     if (!userInst->HasType()) {
297         return;
298     }
299     auto arch = GetGraph()->GetArch();
300     // Unsigned Load in AARCH64 zerod all high bits
301 #ifndef NDEBUG
302     if (inst->IsLoad() && !DataType::IsTypeSigned(inst->GetType()) && arch == Arch::AARCH64 &&
303         GetGraph()->IsLowLevelInstructionsEnabled()) {
304 #else
305     if (inst->IsLoad() && !DataType::IsTypeSigned(inst->GetType()) && arch == Arch::AARCH64) {
306 #endif  // !NDEBUG
307         return;
308     }
309     [[maybe_unused]] auto userInputType = userInst->GetInputType(user.GetIndex());
310     [[maybe_unused]] bool refToPtr = userInputType == DataType::POINTER && inst->GetType() == DataType::REFERENCE;
311     CHECKER_DO_IF_NOT_AND_PRINT(DataType::Is32Bits(userInputType, arch) || refToPtr ||
312                                     (block->GetGraph()->IsDynamicMethod() && userInputType == DataType::ANY),
313                                 std::cerr << "Undefined high-part of input instruction for its user\n"
314                                           << "input: " << *inst << std::endl
315                                           << "user:  " << *userInst << std::endl);
316 }
317 
318 void GraphChecker::CheckInstUsers(Inst *inst, [[maybe_unused]] BasicBlock *block)
319 {
320     for ([[maybe_unused]] auto &user : inst->GetUsers()) {
321         auto userInst = user.GetInst();
322         CHECKER_DO_IF_NOT_AND_PRINT(CheckInstHasInput(userInst, inst),
323                                     (std::cerr << "Instruction is not an input to its user\n"
324                                                << "input: " << *inst << std::endl
325                                                << "user:  " << *userInst << std::endl,
326                                      GetGraph()->Dump(&std::cerr)));
327         if (!userInst->IsPhi() && !userInst->IsCatchPhi()) {
328             CHECKER_DO_IF_NOT_AND_PRINT(
329                 inst->IsDominate(userInst) || ((GetGraph()->IsRegAllocApplied() || GetGraph()->IsThrowApplied()) &&
330                                                IsTryCatchDomination(inst->GetBasicBlock(), userInst->GetBasicBlock())),
331                 (std::cerr << "Instruction doesn't dominate its user\n"
332                            << "input: bb " << inst->GetBasicBlock()->GetId() << *inst << std::endl
333                            << "user: bb " << userInst->GetBasicBlock()->GetId() << *userInst << std::endl,
334                  GetGraph()->Dump(&std::cerr)));
335         }
336         if (DataType::Is32Bits(inst->GetType(), GetGraph()->GetArch())) {
337             CheckUserOfInt32(block, inst, user);
338         }
339     }
340 }
341 
342 void GraphChecker::CheckDataFlow(BasicBlock *block)
343 {
344     for (auto inst : block->AllInsts()) {
345         CHECKER_DO_IF_NOT_AND_PRINT(inst->GetBasicBlock() == block,
346                                     std::cerr << "Instruction block's pointer isn't correct" << *inst << std::endl);
347         if (block != GetGraph()->GetStartBlock()) {
348             CHECKER_DO_IF_NOT_AND_PRINT(inst->GetOpcode() != Opcode::Parameter,
349                                         std::cerr << "Not entry block can't contain Parameter instructions" << *inst
350                                                   << std::endl);
351         }
352         if (inst->GetPrev() == nullptr) {
353             CHECKER_MESSAGE_IF_NOT_AND_PRINT(*block->AllInsts().begin() == inst,
354                                              "First block instruction isn't correct");
355         }
356         if (inst->GetNext() == nullptr) {
357             CHECKER_MESSAGE_IF_NOT_AND_PRINT(*block->AllInstsSafeReverse().begin() == inst,
358                                              "Last block instruction isn't correct");
359         }
360         CheckInputType(inst);
361 
362 #ifndef NDEBUG
363         if (GetGraph()->IsInliningComplete() && GetGraph()->GetParentGraph() == nullptr) {
364             // Check depth
365             CHECKER_DO_IF_NOT_AND_PRINT(inst->GetInliningDepth() == GetInliningDepth(inst),
366                                         std::cerr << *inst << std::endl
367                                                   << "Current depth = " << inst->GetInliningDepth() << std::endl
368                                                   << "Correct depth = " << GetInliningDepth(inst) << std::endl);
369         }
370 #endif
371         CheckInstUsers(inst, block);
372 
373         for ([[maybe_unused]] auto input : inst->GetInputs()) {
374             CHECKER_DO_IF_NOT_AND_PRINT(CheckInstHasUser(input.GetInst(), inst),
375                                         std::cerr << "Instruction is not a user to its input:\n"
376                                                   << "input: " << *input.GetInst() << std::endl
377                                                   << "user:  " << *inst << std::endl);
378         }
379     }
380 }
381 
382 void GraphChecker::CheckCallReturnInlined()
383 {
384     [[maybe_unused]] bool throwExit = false;
385     if (GetGraph()->HasEndBlock()) {
386         for (auto block : GetGraph()->GetEndBlock()->GetPredsBlocks()) {
387             if (block->IsTryEnd()) {
388                 continue;
389             }
390             if (block->IsEndWithThrowOrDeoptimize()) {
391                 throwExit = true;
392                 break;
393             }
394         }
395     }
396     ArenaStack<Inst *> inlinedCalles(GetLocalAllocator()->Adapter());
397     for (auto block : GetGraph()->GetBlocksRPO()) {
398         for (auto inst : block->Insts()) {
399             if (inst->IsCall() && static_cast<CallInst *>(inst)->IsInlined()) {
400                 CHECKER_MESSAGE_IF_NOT_AND_PRINT(inst->NoDest(), "Inlined call should have NO_DST flag");
401                 inlinedCalles.push(inst);
402             } else if (inst->GetOpcode() == Opcode::ReturnInlined && block->IsEndWithThrowOrDeoptimize()) {
403                 // NOTE(Sergey Chernykh) fix checker
404                 continue;
405             } else if (inst->GetOpcode() == Opcode::ReturnInlined) {
406                 CHECKER_IF_NOT_PRINT(!inlinedCalles.empty());
407                 ASSERT(inlinedCalles.top()->GetSaveState() == inst->GetSaveState() || throwExit ||
408                        GetGraph()->IsRegAllocApplied());
409                 inlinedCalles.pop();
410             }
411         }
412     }
413     CHECKER_IF_NOT_PRINT(inlinedCalles.empty() || throwExit);
414 #ifndef NDEBUG
415     // avoid check after ir_builder in inline pass
416     if (!GetGraph()->IsInliningComplete() || GetGraph()->GetParentGraph() != nullptr) {
417         return;
418     }
419     for (auto block : GetGraph()->GetBlocksRPO()) {
420         for (auto inst : block->Insts()) {
421             if (inst->IsSaveState()) {
422                 CheckSaveStateCaller(static_cast<SaveStateInst *>(inst));
423             }
424         }
425     }
426 #endif
427 }
428 
429 bool GraphChecker::FindCaller([[maybe_unused]] Inst *caller, BasicBlock *domBlock, ArenaStack<Inst *> *inlinedCalls)
430 {
431     for (auto inst : domBlock->InstsSafeReverse()) {
432         if (inst->GetOpcode() == Opcode::ReturnInlined) {
433             inlinedCalls->push(inst);
434         } else if (inst->IsCall() && static_cast<CallInst *>(inst)->IsInlined()) {
435             if (!inlinedCalls->empty()) {
436                 inlinedCalls->pop();
437             } else {
438                 CHECKER_IF_NOT_PRINT(caller == inst);
439                 return true;
440             }
441         }
442     }
443     return false;
444 }
445 
446 void GraphChecker::CheckSaveStateCaller(SaveStateInst *savestate)
447 {
448     CHECKER_IF_NOT_PRINT(savestate != nullptr);
449     auto block = savestate->GetBasicBlock();
450     ArenaStack<Inst *> inlinedCalls(GetLocalAllocator()->Adapter());
451     auto caller = savestate->GetCallerInst();
452     if (caller == nullptr) {
453         return;
454     }
455     CHECKER_IF_NOT_PRINT(caller->GetBasicBlock() != nullptr);
456     CHECKER_IF_NOT_PRINT(caller->GetBasicBlock()->GetGraph() == block->GetGraph());
457     ASSERT(caller->IsInlined());
458     auto domBlock = block;
459     bool skip = true;
460     for (auto inst : domBlock->InstsSafeReverse()) {
461         if (inst == savestate) {
462             skip = false;
463         }
464         if (skip) {
465             continue;
466         }
467         if (inst->GetOpcode() == Opcode::ReturnInlined) {
468             inlinedCalls.push(inst);
469         } else if (inst->IsCall() && static_cast<CallInst *>(inst)->IsInlined()) {
470             if (!inlinedCalls.empty()) {
471                 inlinedCalls.pop();
472             } else {
473                 CHECKER_IF_NOT_PRINT(caller == inst);
474                 return;
475             }
476         }
477     }
478     domBlock = domBlock->GetDominator();
479     while (domBlock != nullptr) {
480         if (FindCaller(caller, domBlock, &inlinedCalls)) {
481             return;
482         }
483         domBlock = domBlock->GetDominator();
484     }
485     UNREACHABLE();
486 }
487 
488 void GraphChecker::CheckStartBlock()
489 {
490     [[maybe_unused]] Inst *hasNullptr = nullptr;
491     [[maybe_unused]] int32_t lastNum = -2;
492     CHECKER_IF_NOT_PRINT(GetGraph()->GetStartBlock());
493     CHECKER_MESSAGE_IF_NOT_AND_PRINT(GetGraph()->GetStartBlock()->GetPredsBlocks().empty(),
494                                      "Start block can't have predecessors");
495     CHECKER_MESSAGE_IF_NOT_AND_PRINT(GetGraph()->GetStartBlock()->GetSuccsBlocks().size() == 1,
496                                      "Start block should have one successor");
497     for (auto inst : GetGraph()->GetStartBlock()->AllInsts()) {
498         [[maybe_unused]] Opcode opc = inst->GetOpcode();
499         CHECKER_DO_IF_NOT_AND_PRINT(
500             opc == Opcode::Constant || opc == Opcode::Parameter || opc == Opcode::SafePoint ||
501                 opc == Opcode::SpillFill || opc == Opcode::NullPtr || opc == Opcode::NOP || opc == Opcode::LiveIn ||
502                 opc == Opcode::LoadUndefined,
503             std::cerr
504                 << "Entry block can contain Constant, Parameter, NullPtr, SafePoint, NOP or SpillFill instructions"
505                 << *inst << std::endl);
506         if (opc == Opcode::Parameter) {
507             auto argNum = inst->CastToParameter()->GetArgNumber();
508             auto num = static_cast<int32_t>(argNum);
509             if (argNum == ParameterInst::DYNAMIC_NUM_ARGS) {
510                 num = -1;
511             }
512             CHECKER_DO_IF_NOT_AND_PRINT(
513                 lastNum < num,
514                 std::cerr << "The argument number in the parameter must be greater than that of the previous parameter"
515                           << *inst << std::endl);
516             lastNum = num;
517         }
518         if (opc == Opcode::NullPtr) {
519             CHECKER_MESSAGE_IF_NOT_AND_PRINT(hasNullptr == nullptr,
520                                              "There should be not more than one NullPtr instruction");
521             hasNullptr = inst;
522         }
523     }
524 }
525 
526 void GraphChecker::CheckEndBlock()
527 {
528     if (!GetGraph()->HasEndBlock()) {
529         CHECKER_MESSAGE_IF_NOT_AND_PRINT(HasOuterInfiniteLoop(), "Graph without infinite loops should have end block");
530         return;
531     }
532     CHECKER_MESSAGE_IF_NOT_AND_PRINT(GetGraph()->GetEndBlock()->GetSuccsBlocks().empty(),
533                                      "End block can't have successors");
534     [[maybe_unused]] auto iter = GetGraph()->GetEndBlock()->Insts();
535     CHECKER_MESSAGE_IF_NOT_AND_PRINT(iter.begin() == iter.end(), "End block can't have instructions");
536 }
537 
538 void GraphChecker::CheckGraph()
539 {
540     size_t numInst = GetGraph()->GetCurrentInstructionId();
541     ArenaVector<bool> instVec(numInst, GetLocalAllocator()->Adapter());
542     for (auto &bb : GetGraph()->GetBlocksRPO()) {
543         for (auto inst : bb->AllInsts()) {
544             auto id = inst->GetId();
545             CHECKER_DO_IF_NOT_AND_PRINT(
546                 id < numInst,
547                 (std::cerr << "Instruction ID must be less than graph instruction counter: " << numInst << "\n",
548                  inst->Dump(&std::cerr)));
549             CHECKER_DO_IF_NOT_AND_PRINT(
550                 !instVec[id], (std::cerr << "Instruction with same Id already exists:\n", inst->Dump(&std::cerr)));
551             instVec[id] = true;
552             CHECKER_DO_IF_NOT_AND_PRINT(
553                 GetGraph()->IsDynamicMethod() || inst->GetType() != DataType::ANY,
554                 (std::cerr << "The type ANY is supported only for dynamic languages\n", inst->Dump(&std::cerr)));
555 #ifndef NDEBUG
556             CHECKER_DO_IF_NOT_AND_PRINT(inst->SupportsMode(GetGraph()->GetCompilerMode()),
557                                         (std::cerr << "Instruction used in wrong mode\n", inst->Dump(&std::cerr)));
558 #endif
559             VisitInstruction(inst);
560         }
561     }
562 }
563 
564 void GraphChecker::CheckPhiInputs(Inst *phiInst)
565 {
566     for (size_t index = 0; index < phiInst->GetInputsCount(); ++index) {
567         [[maybe_unused]] auto pred = phiInst->CastToPhi()->GetPhiInputBb(index);
568         [[maybe_unused]] auto inputBb = phiInst->CastToPhi()->GetPhiInput(pred)->GetBasicBlock();
569         CHECKER_DO_IF_NOT_AND_PRINT(
570             inputBb->IsDominate(pred) || ((GetGraph()->IsRegAccAllocApplied() || GetGraph()->IsThrowApplied()) &&
571                                           IsTryCatchDomination(inputBb, pred)),
572             (std::cerr
573                  << "Block where phi-input is located should dominate predecessor block corresponding to this input\n"
574                  << "Block inputBb " << inputBb->GetId() << " should dominate pred " << pred->GetId() << std::endl
575                  << "phiInst " << *phiInst << " from BB " << phiInst->GetBasicBlock()->GetId() << std::endl,
576              std::cerr << "inputBb ", inputBb->Dump(&std::cerr), std::cerr << "pred ", pred->Dump(&std::cerr),
577              std::cerr << "phiBb ", phiInst->GetBasicBlock()->Dump(&std::cerr), GetGraph()->Dump(&std::cerr)));
578     }
579 }
580 
581 bool GraphChecker::CheckInstRegUsageSaved(const Inst *inst, Register reg) const
582 {
583     if (reg == GetAccReg()) {
584         return true;
585     }
586     auto graph = inst->GetBasicBlock()->GetGraph();
587     // Empty vector regs mask means we are using dynamic general regs set.
588     if (DataType::IsFloatType(inst->GetType()) && !graph->GetUsedRegs<DataType::FLOAT64>()->empty()) {
589         return graph->GetUsedRegs<DataType::FLOAT64>()->at(reg);
590     }
591     return graph->GetUsedRegs<DataType::INT64>()->at(reg);
592 }
593 
594 [[maybe_unused]] static bool CheckSpillFillMultiple(const compiler::Inst *inst)
595 {
596     switch (inst->GetOpcode()) {
597         case Opcode::Parameter:
598             return false;
599         case Opcode::LoadObject:
600         case Opcode::NewArray:
601         case Opcode::NewObject:
602             // In this case for BytecodeOptimizer SpillFill will be added after instruction, not before
603             // user-insturction. So this check can't find it and it is skipped.
604             return !inst->GetBasicBlock()->GetGraph()->IsBytecodeOptimizer();
605         default:
606             return true;
607     }
608 }
609 
610 void GraphChecker::CheckNoLowLevel(BasicBlock *block)
611 {
612     for ([[maybe_unused]] auto inst : block->Insts()) {
613         CHECKER_DO_IF_NOT_AND_PRINT(!inst->IsLowLevel(), inst->Dump(&std::cerr));
614     }
615 }
616 
617 void GraphChecker::MarkBlocksInLoop(Loop *loop, Marker mrk)
618 {
619     CHECKER_IF_NOT_PRINT(loop->IsIrreducible() || loop->IsRoot() || loop->GetHeader() != nullptr);
620     CHECKER_IF_NOT_PRINT(loop->IsIrreducible() || loop->IsRoot() || loop->IsTryCatchLoop() ||
621                          loop->GetPreHeader() != nullptr);
622     // Mark blocks and check if marker was not set before
623     for ([[maybe_unused]] auto block : loop->GetBlocks()) {
624         CHECKER_IF_NOT_PRINT(!block->SetMarker(mrk));
625     }
626 
627     for (auto inner : loop->GetInnerLoops()) {
628         MarkBlocksInLoop(inner, mrk);
629     }
630 }
631 
632 bool GraphChecker::CheckBlockHasPredecessor(BasicBlock *block, BasicBlock *predecessor)
633 {
634     CHECKER_IF_NOT_PRINT(block != nullptr && predecessor != nullptr);
635     for (auto pred : block->GetPredsBlocks()) {
636         if (pred == predecessor) {
637             return true;
638         }
639     }
640     return false;
641 }
642 
643 bool GraphChecker::CheckBlockHasSuccessor(BasicBlock *block, BasicBlock *successor)
644 {
645     CHECKER_IF_NOT_PRINT(block != nullptr && successor != nullptr);
646     for (auto succ : block->GetSuccsBlocks()) {
647         if (succ == successor) {
648             return true;
649         }
650     }
651     return false;
652 }
653 
654 bool GraphChecker::BlockContainsInstruction(BasicBlock *block, Opcode opcode)
655 {
656     return std::find_if(block->Insts().begin(), block->Insts().end(),
657                         [opcode](Inst *inst) { return inst->GetOpcode() == opcode; }) != block->Insts().end();
658 }
659 
660 void GraphChecker::CheckLoopHasSafePoint(Loop *loop)
661 {
662     [[maybe_unused]] auto it =
663         std::find_if(loop->GetBlocks().begin(), loop->GetBlocks().end(),
664                      [this](BasicBlock *block) { return BlockContainsInstruction(block, Opcode::SafePoint); });
665     // Irreducible isn't fully populated - only 'one of the headers' and back-edge,
666     // SafePoint can be inserted to the another 'header' and search will be failed
667     CHECKER_DO_IF_NOT_AND_PRINT(loop->IsTryCatchLoop() || loop->IsIrreducible() || it != loop->GetBlocks().end(),
668                                 std::cerr << "Loop " << loop->GetId() << " must have safepoint\n");
669     for (auto inner : loop->GetInnerLoops()) {
670         CheckLoopHasSafePoint(inner);
671     }
672 }
673 
674 void GraphChecker::CheckLoops()
675 {
676     CHECKER_IF_NOT_PRINT(GetGraph()->GetAnalysis<LoopAnalyzer>().IsValid());
677     CHECKER_IF_NOT_PRINT(GetGraph()->GetRootLoop() != nullptr);
678     CHECKER_IF_NOT_PRINT(GetGraph()->GetRootLoop()->IsRoot());
679     CHECKER_IF_NOT_PRINT(GetGraph()->GetRootLoop()->GetHeader() == nullptr);
680     CHECKER_IF_NOT_PRINT(GetGraph()->GetRootLoop()->GetPreHeader() == nullptr);
681     auto rootLoop = GetGraph()->GetRootLoop();
682     auto mrk = GetGraph()->NewMarker();
683     MarkBlocksInLoop(rootLoop, mrk);
684 
685     for ([[maybe_unused]] auto block : GetGraph()->GetBlocksRPO()) {
686         [[maybe_unused]] auto loop = block->GetLoop();
687         CHECKER_IF_NOT_PRINT(loop != nullptr);
688         CHECKER_IF_NOT_PRINT(block->IsMarked(mrk));
689         if (!block->IsLoopHeader()) {
690             CHECKER_IF_NOT_PRINT(!block->IsOsrEntry());
691             if (block->IsLoopPreHeader()) {
692                 CHECKER_IF_NOT_PRINT(block->GetNextLoop()->GetPreHeader() == block);
693             }
694             continue;
695         }
696         CHECKER_IF_NOT_PRINT(loop->IsTryCatchLoop() || loop->IsIrreducible() ||
697                              loop->GetPreHeader()->GetNextLoop() == loop);
698         if (block->IsOsrEntry()) {
699             CHECKER_IF_NOT_PRINT(GetGraph()->IsOsrMode());
700             auto ssOsr = block->GetFirstInst();
701             while (ssOsr != nullptr && (ssOsr->IsCatchPhi() || ssOsr->GetOpcode() == Opcode::Try)) {
702                 ssOsr = ssOsr->GetNext();
703             }
704             CHECKER_IF_NOT_PRINT(ssOsr != nullptr && ssOsr->GetOpcode() == Opcode::SaveStateOsr);
705         }
706         [[maybe_unused]] auto preds = block->GetPredsBlocks();
707         for ([[maybe_unused]] auto pred : preds) {
708             CHECKER_IF_NOT_PRINT(pred->GetLoop() != loop || loop->HasBackEdge(pred));
709         }
710 
711         if (!loop->IsIrreducible()) {
712             for ([[maybe_unused]] auto back : loop->GetBackEdges()) {
713                 CHECKER_IF_NOT_PRINT(std::find(preds.begin(), preds.end(), back) != preds.end());
714             }
715         }
716     }
717     GetGraph()->EraseMarker(mrk);
718     if (g_options.IsCompilerUseSafepoint() && GetGraph()->SupportManagedCode()) {
719         for (auto inner : rootLoop->GetInnerLoops()) {
720             CheckLoopHasSafePoint(inner);
721         }
722     }
723 }
724 
725 void GraphChecker::CheckDomTree()
726 {
727     CHECKER_IF_NOT_PRINT(GetGraph()->GetAnalysis<DominatorsTree>().IsValid());
728     ArenaVector<BasicBlock *> dominators(GetGraph()->GetVectorBlocks().size(), GetLocalAllocator()->Adapter());
729     for (auto block : GetGraph()->GetBlocksRPO()) {
730         dominators[block->GetId()] = block->GetDominator();
731     }
732     // Rebuild dom-tree
733     GetGraph()->InvalidateAnalysis<DominatorsTree>();
734     GetGraph()->RunPass<DominatorsTree>();
735 
736     for ([[maybe_unused]] auto block : GetGraph()->GetBlocksRPO()) {
737         CHECKER_DO_IF_NOT_AND_PRINT(
738             dominators[block->GetId()] == block->GetDominator(),
739             std::cerr << "Basic block with id " << block->GetId() << " has incorrect dominator with id "
740                       << dominators[block->GetId()]->GetId() << std::endl
741                       << "Correct dominator must be block with id " << block->GetDominator()->GetId() << std::endl
742                       << "Note: basic blocks' ids in the original graph and in the cloned graph can be different"
743                       << std::endl);
744     }
745 }
746 
747 void GraphChecker::CheckLoopAnalysis()
748 {
749     // Save current loop info
750     ArenaUnorderedMap<BasicBlock *, Loop *> loops(GetLocalAllocator()->Adapter());
751     [[maybe_unused]] auto rootLoop = GetGraph()->GetRootLoop();
752     for (auto block : GetGraph()->GetBlocksRPO()) {
753         if (block->IsLoopHeader()) {
754             loops.emplace(block, block->GetLoop());
755         }
756     }
757     for (auto block : GetGraph()->GetBlocksRPO()) {
758         if (block->GetSuccsBlocks().size() != 2U) {
759             continue;
760         }
761         [[maybe_unused]] auto loop = block->GetLoop();
762         CHECKER_DO_IF_NOT_AND_PRINT(loop->IsIrreducible() || block->GetFalseSuccessor()->GetLoop() == loop ||
763                                         block->GetFalseSuccessor()->GetLoop()->GetOuterLoop() == loop ||
764                                         block->GetTrueSuccessor()->GetLoop() == loop ||
765                                         block->GetTrueSuccessor()->GetLoop()->GetOuterLoop() == loop,
766                                     std::cerr << "One of successors of loop exit basic block id " << block->GetId()
767                                               << " must be in same or inner loop" << std::endl);
768     }
769 
770     // Build new loop info and compare with saved one
771     GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
772     GetGraph()->RunPass<LoopAnalyzer>();
773     CHECKER_MESSAGE_IF_NOT_AND_PRINT(*rootLoop == *GetGraph()->GetRootLoop(), "Root loop is incorrect\n");
774     for (auto &[block, loop] : loops) {
775         auto expectedLoop = block->GetLoop();
776         // An irreducible loop can have different heads, depending on the order of traversal
777         if (loop->IsIrreducible()) {
778             CHECKER_IF_NOT_PRINT(expectedLoop->IsIrreducible());
779             continue;
780         }
781         CHECKER_IF_NOT_PRINT(block->IsLoopHeader());
782         if (loop == nullptr || expectedLoop == nullptr) {
783             UNREACHABLE();
784             return;
785         }
786         CHECKER_DO_IF_NOT_AND_PRINT(*loop == *expectedLoop, std::cerr << "Loop " << loop->GetId() << " is incorrect\n");
787     }
788 }
789 
790 /// Check that there is root's inner loop without exit-points
791 bool GraphChecker::HasOuterInfiniteLoop()
792 {
793     const auto &loops = GetGraph()->GetRootLoop()->GetInnerLoops();
794     return std::find_if(loops.begin(), loops.end(), [](const Loop *loop) { return loop->IsInfinite(); }) != loops.end();
795 }
796 
797 bool GraphChecker::CheckInstHasInput(Inst *inst, Inst *input)
798 {
799     CHECKER_IF_NOT_PRINT(inst != nullptr && input != nullptr);
800     CHECKER_IF_NOT_PRINT(input->GetBasicBlock() != nullptr);
801     CHECKER_IF_NOT_PRINT(input->GetBasicBlock()->GetGraph() != nullptr);
802     for (auto node : inst->GetInputs()) {
803         if (node.GetInst() == input) {
804             return true;
805         }
806     }
807     return false;
808 }
809 
810 bool GraphChecker::CheckInstHasUser(Inst *inst, Inst *user)
811 {
812     CHECKER_IF_NOT_PRINT(inst != nullptr && user != nullptr);
813     CHECKER_IF_NOT_PRINT(user->GetBasicBlock() != nullptr);
814     CHECKER_IF_NOT_PRINT(user->GetBasicBlock()->GetGraph() != nullptr);
815     for (auto &node : inst->GetUsers()) {
816         if (node.GetInst() == user) {
817             return true;
818         }
819     }
820     return false;
821 }
822 
823 void GraphChecker::CheckBlockEdges(const BasicBlock &block)
824 {
825     [[maybe_unused]] auto lastInstInBlock = block.GetLastInst();
826     if (block.GetSuccsBlocks().size() > 1) {
827         CHECKER_DO_IF_NOT_AND_PRINT(
828             !block.IsEmpty() || block.IsTryEnd(),
829             (std::cerr << "Block with 2 successors have no instructions or should be try-end" << std::endl,
830              GetGraph()->Dump(&std::cerr)));
831         CHECKER_DO_IF_NOT_AND_PRINT(
832             block.IsTryBegin() || block.IsTryEnd() || lastInstInBlock->IsControlFlow(),
833             (std::cerr << "Last instruction must be control flow in block with 2 successors" << std::endl,
834              GetGraph()->Dump(&std::cerr)));
835     } else if (block.GetSuccsBlocks().size() == 1) {
836         if (block.GetSuccsBlocks()[0]->IsEndBlock()) {
837             if (block.IsEmpty()) {
838                 CHECKER_IF_NOT_PRINT(block.IsTryEnd());
839                 return;
840             }
841             auto lastInst = block.GetLastInst();
842             [[maybe_unused]] auto opc = lastInst->GetOpcode();
843             CHECKER_MESSAGE_IF_NOT_AND_PRINT(
844                 lastInst->GetFlag(inst_flags::TERMINATOR),
845                 "Last instruction in block before exit-block must be Return or Throw instruction.");
846         }
847     }
848 }
849 
850 void GraphChecker::CheckTryBeginBlock(const BasicBlock &block)
851 {
852     CHECKER_IF_NOT_PRINT(block.IsTryBegin());
853     auto tryInstIt = std::find_if(block.AllInsts().begin(), block.AllInsts().end(),
854                                   [](Inst *inst) { return inst->GetOpcode() == Opcode::Try; });
855     CHECKER_MESSAGE_IF_NOT_AND_PRINT(tryInstIt != block.AllInsts().end(),
856                                      "Try-begin basic block should contain try-instructions");
857     [[maybe_unused]] auto tryInst = (*tryInstIt)->CastToTry();
858     for ([[maybe_unused]] auto succIndex : *tryInst->GetCatchEdgeIndexes()) {
859         CHECKER_MESSAGE_IF_NOT_AND_PRINT(succIndex < block.GetSuccsBlocks().size(),
860                                          "Try instruction holds incorrect try-begin block successor number");
861     }
862 }
863 
864 void GraphChecker::CheckJump(const BasicBlock &block)
865 {
866     CHECKER_IF_NOT_PRINT(GetGraph()->IsRegAllocApplied());
867     CHECKER_IF_NOT_PRINT(GetGraph()->IsAnalysisValid<LinearOrder>());
868     if (block.IsIfBlock()) {
869         const auto &blocksVector = GetGraph()->GetBlocksLinearOrder();
870         auto ifBlockIt = std::find(blocksVector.begin(), blocksVector.end(), &block);
871         CHECKER_IF_NOT_PRINT(ifBlockIt != blocksVector.end());
872         auto blockAfterIf = std::next(ifBlockIt);
873         if (blockAfterIf != blocksVector.end()) {
874             CHECKER_MESSAGE_IF_NOT_AND_PRINT(*blockAfterIf != (*ifBlockIt)->GetFalseSuccessor(),
875                                              "`If-block` with immediate `false`-successor shouldn't have `JumpFlag`");
876             CHECKER_MESSAGE_IF_NOT_AND_PRINT(*blockAfterIf != (*ifBlockIt)->GetTrueSuccessor(),
877                                              "`true`-successor should be replaced with `false`-successor");
878         }
879     }
880     [[maybe_unused]] auto numSuccs = block.GetSuccsBlocks().size();
881     CHECKER_MESSAGE_IF_NOT_AND_PRINT(numSuccs == 1 || block.IsTryBegin() || block.IsTryEnd() || block.IsIfBlock(),
882                                      "Basic block with Jump must have 1 successor or should be try-begin or if block");
883 }
884 
885 /**
886  * Regalloc propagates catch-phi's inputs to the users and can broke user's domination. In this case:
887  * - input_block should be placed inside try block;
888  * - try-begin block should dominate user_block;
889  *
890  * [try-begin]----------\
891  *     |                |
892  * [input_block]        |
893  *     |                |
894  * [try-end]----------->|
895  *                      |
896  *                [catch-begin]
897  *                      |
898  *                [user_block]
899  */
900 bool GraphChecker::IsTryCatchDomination(const BasicBlock *inputBlock, const BasicBlock *userBlock) const
901 {
902     if (!GetGraph()->IsRegAllocApplied() && !GetGraph()->IsThrowApplied()) {
903         return false;
904     }
905     if (inputBlock->IsTry()) {
906         auto blocks = GetGraph()->GetTryBeginBlocks();
907         auto it =
908             std::find_if(blocks.begin(), blocks.end(), [userBlock](auto &bb) { return bb->IsDominate(userBlock); });
909         return it != blocks.end();
910     }
911     return false;
912 }
913 
914 #ifdef COMPILER_DEBUG_CHECKS
915 static inline bool AllUsersDominate(Inst *inst1, Inst *inst2)
916 {
917     for (auto &user : inst1->GetUsers()) {
918         if (!user.GetInst()->IsDominate(inst2)) {
919             return false;
920         }
921     }
922     return true;
923 }
924 #endif
925 
926 // It is necessary to check because if reference instruction (it is some object) will contain between SaveState and some
927 // RuntimeCall user of SaveState, during startup in runtime call GC can be triggered and move our object, so we
928 // will lost them, his new position (object is not contained in SaveState).
929 void GraphChecker::CheckSaveStatesWithRuntimeCallUsers()
930 {
931 #ifdef COMPILER_DEBUG_CHECKS
932     for (auto &block : GetGraph()->GetBlocksRPO()) {
933         for (const auto &ss : block->AllInsts()) {
934             if (ss->GetOpcode() != Opcode::SaveState) {
935                 continue;
936             }
937             CheckSaveStatesWithRuntimeCallUsers(block, ss->CastToSaveState());
938         }
939     }
940 #endif
941 }
942 
943 void GraphChecker::CheckSaveStatesWithRuntimeCallUsers(BasicBlock *block, SaveStateInst *ss)
944 {
945     for (auto &user : ss->GetUsers()) {
946         auto userInst = user.GetInst();
947         if (!userInst->IsRuntimeCall() || userInst->IsCheck()) {
948             continue;
949         }
950         ASSERT(userInst->GetBasicBlock() == ss->GetBasicBlock());
951         auto it = InstSafeIterator<IterationType::ALL, IterationDirection::BACKWARD>(*block, userInst);
952         for (++it; *it != ss; ++it) {
953             if ((*it)->GetOpcode() == Opcode::CastAnyTypeValue) {
954                 continue;
955             }
956             // Non-reference instructions, checks, nullptr and classes cannot be moved by GC
957             // The correct state when all users of Object(is "*it") between SaveState and RuntimeCall(is
958             // "user_inst")
959             CHECKER_DO_IF_NOT_AND_PRINT(!(*it)->IsMovableObject() || AllUsersDominate(*it, userInst),
960                                         std::cerr << "We have inst v" << (*it)->GetId()
961                                                   << " which is located between SaveState v" << ss->GetId()
962                                                   << " and RuntimeCall v" << userInst->GetId() << ":\n"
963                                                   << **it << std::endl
964                                                   << *ss << std::endl
965                                                   << *userInst << std::endl);
966         }
967     }
968 }
969 
970 #ifdef COMPILER_DEBUG_CHECKS
971 void GraphChecker::PrepareUsers(Inst *inst, ArenaVector<User *> *users)
972 {
973     for (auto &user : inst->GetUsers()) {
974         users->push_back(&user);
975     }
976     auto i = std::find_if(users->begin(), users->end(), [](User *user) { return user->GetInst()->IsCheck(); });
977     while (i != users->end()) {
978         auto userInst = (*i)->GetInst();
979         // erase before push_backs to avoid iterator invalidation
980         users->erase(i);
981         // skip AnyTypeChecks with primitive type
982         if (userInst->GetOpcode() != Opcode::AnyTypeCheck || userInst->IsReferenceOrAny()) {
983             for (auto &u : userInst->GetUsers()) {
984                 users->push_back(&u);
985             }
986         }
987         i = std::find_if(users->begin(), users->end(), [](User *user) { return user->GetInst()->IsCheck(); });
988     }
989     for (auto &it : (*users)) {
990         [[maybe_unused]] auto user = it->GetInst();
991         CHECKER_IF_NOT_PRINT(!user->IsCheck());
992     }
993 }
994 #endif
995 
996 #ifdef COMPILER_DEBUG_CHECKS
997 // If all indirect (through other Phis) inputs of Phi cannot be moved by GC, result of Phi itself
998 // also cannot be moved (and does not need to be checked in CheckSaveStateInputs as input of its users)
999 bool GraphChecker::IsPhiSafeToSkipObjectCheck(Inst *inst, Marker visited)
1000 {
1001     CHECKER_IF_NOT_PRINT(inst->IsPhi());
1002     CHECKER_IF_NOT_PRINT(visited != UNDEF_MARKER);
1003     if (inst->SetMarker(visited)) {
1004         return true;
1005     }
1006     for (auto &input : inst->GetInputs()) {
1007         auto inputInst = input.GetInst();
1008         if (inputInst->IsPhi()) {
1009             if (!IsPhiSafeToSkipObjectCheck(inputInst, visited)) {
1010                 return false;
1011             }
1012         } else if (inputInst->IsMovableObject() || inputInst->IsCheck()) {
1013             // Check instructions with reference type do not need to be saved in SaveState themselves,
1014             // but we need to return false here for them (because their result can be moved by GC)
1015             return false;
1016         }
1017     }
1018     return true;
1019 }
1020 
1021 // If all indirect (through other Phis) users of Phi are SaveState instructions, result of Phi
1022 // is not used in compiled code (and does not need to be checked in CheckSaveStateInputs as user of its inputs)
1023 bool GraphChecker::IsPhiUserSafeToSkipObjectCheck(Inst *inst, Marker visited)
1024 {
1025     CHECKER_IF_NOT_PRINT(inst->IsPhi());
1026     CHECKER_IF_NOT_PRINT(visited != UNDEF_MARKER);
1027     if (inst->SetMarker(visited)) {
1028         return true;
1029     }
1030     for (auto &user : inst->GetUsers()) {
1031         auto userInst = user.GetInst();
1032         if (userInst->IsSaveState() || userInst->IsCatchPhi() ||
1033             (userInst->IsPhi() && IsPhiUserSafeToSkipObjectCheck(userInst, visited))) {
1034             continue;
1035         }
1036         return false;
1037     }
1038     return true;
1039 }
1040 
1041 // Checks if object is correctly used in SaveStates between it and user
1042 class ObjectSSChecker {
1043 public:
1044     explicit ObjectSSChecker(Inst *object) : object_(object), visited_(object->GetBasicBlock()->GetGraph()) {}
1045 
1046     bool Run(const Inst *user, const BasicBlock *block, Inst *startFrom)
1047     {
1048         if (startFrom != nullptr) {
1049             auto it = InstSafeIterator<IterationType::ALL, IterationDirection::BACKWARD>(*block, startFrom);
1050             for (; it != block->AllInstsSafeReverse().end(); ++it) {
1051                 auto inst = *it;
1052                 if (inst == nullptr) {
1053                     break;
1054                 }
1055                 if (inst->SetMarker(visited_.GetMarker()) || inst == object_ || inst == user) {
1056                     return true;
1057                 }
1058                 if (!FindAndRemindObjectInSaveState(inst)) {
1059                     return false;
1060                 }
1061             }
1062         }
1063         for (auto pred : block->GetPredsBlocks()) {
1064             // Catch-begin block has edge from try-end block, and all try-blocks should be visited from this edge.
1065             // `object` can be placed inside try-block - after try-begin, so that visiting try-begin is wrong
1066             if (block->IsCatchBegin() && pred->IsTryBegin()) {
1067                 continue;
1068             }
1069             if (!Run(user, pred, pred->GetLastInst())) {
1070                 return false;
1071             }
1072         }
1073         return true;
1074     }
1075 
1076     Inst *FailedSS()
1077     {
1078         return failedSs_;
1079     }
1080 
1081 private:
1082     static bool FindObjectInSaveState(Inst *object, Inst *ss)
1083     {
1084         if (!object->IsMovableObject()) {
1085             return true;
1086         }
1087         while (ss != nullptr && object->IsDominate(ss)) {
1088             auto it = std::find_if(ss->GetInputs().begin(), ss->GetInputs().end(), [object, ss](Input input) {
1089                 return ss->GetDataFlowInput(input.GetInst()) == object;
1090             });
1091             if (it != ss->GetInputs().end()) {
1092                 return true;
1093             }
1094             auto caller = static_cast<SaveStateInst *>(ss)->GetCallerInst();
1095             if (caller == nullptr) {
1096                 break;
1097             }
1098             ss = caller->GetSaveState();
1099         }
1100         return false;
1101     }
1102 
1103     bool FindAndRemindObjectInSaveState(Inst *inst)
1104     {
1105         if (IsSaveStateForGc(inst) && !FindObjectInSaveState(object_, inst)) {
1106             failedSs_ = inst;
1107             return false;
1108         }
1109         return true;
1110     }
1111 
1112     Inst *object_;
1113     Inst *failedSs_ {};
1114     MarkerHolder visited_;
1115 };
1116 
1117 void GraphChecker::CheckSaveStateInputs(Inst *inst, ArenaVector<User *> *users)
1118 {
1119     if (!inst->IsMovableObject()) {
1120         return;
1121     }
1122     // true if we do not need check for this input (because it cannot be moved by GC)
1123     bool skipObjCheck = false;
1124     if (inst->GetOpcode() == Opcode::Phi) {
1125         MarkerHolder visited(GetGraph());
1126         skipObjCheck = IsPhiSafeToSkipObjectCheck(inst, visited.GetMarker());
1127     }
1128 
1129     PrepareUsers(inst, users);
1130 
1131     ObjectSSChecker objectSSChecker(inst);
1132     MarkerHolder osrVisited(GetGraph());
1133     for (auto &it : *users) {
1134         auto user = it->GetInst();
1135         if (user->IsCatchPhi()) {
1136             continue;
1137         }
1138         // true if we do not need check for this user (because it is Phi used only in SaveStates)
1139         bool skipObjCheckUser = false;
1140         BasicBlock *startBb;
1141         Inst *startInst;
1142         if (user->IsPhi()) {
1143             MarkerHolder visited(GetGraph());
1144             skipObjCheckUser = IsPhiUserSafeToSkipObjectCheck(user, visited.GetMarker());
1145             // check SaveStates on path between inst and the end of corresponding predecessor of Phi's block
1146             startBb = user->GetBasicBlock()->GetPredsBlocks()[it->GetBbNum()];
1147             startInst = startBb->GetLastInst();
1148         } else {
1149             // check SaveStates on path between inst and user
1150             startBb = user->GetBasicBlock();
1151             startInst = user->GetPrev();
1152         }
1153         CHECKER_DO_IF_NOT_AND_PRINT(skipObjCheck || skipObjCheckUser || objectSSChecker.Run(user, startBb, startInst),
1154                                     std::cerr << "Object v" << inst->GetId() << " used in v" << user->GetId()
1155                                               << ", but not found on the path between them in the "
1156                                               << objectSSChecker.FailedSS()->GetOpcodeStr() << " v"
1157                                               << objectSSChecker.FailedSS()->GetId() << ":\n"
1158                                               << *inst << std::endl
1159                                               << *user << std::endl
1160                                               << *objectSSChecker.FailedSS() << std::endl);
1161         CheckSaveStateOsrRec(inst, user, startBb, osrVisited.GetMarker());
1162     }
1163     users->clear();
1164 }
1165 #endif
1166 
1167 void GraphChecker::CheckSaveStateInputs()
1168 {
1169 #ifdef COMPILER_DEBUG_CHECKS
1170     ArenaVector<User *> users(GetLocalAllocator()->Adapter());
1171     for (auto &block : GetGraph()->GetBlocksRPO()) {
1172         for (const auto &inst : block->AllInsts()) {
1173             CheckSaveStateInputs(inst, &users);
1174         }
1175     }
1176 #endif
1177 }
1178 
1179 void GraphChecker::CheckSaveStateOsrRec(const Inst *inst, const Inst *user, BasicBlock *block, Marker visited)
1180 {
1181     if (block->SetMarker(visited)) {
1182         return;
1183     }
1184     if (inst->GetBasicBlock() == block) {
1185         return;
1186     }
1187     if (block->IsOsrEntry()) {
1188         CHECKER_IF_NOT_PRINT(GetGraph()->IsOsrMode());
1189         auto ss = block->GetFirstInst();
1190         CHECKER_IF_NOT_PRINT(ss != nullptr && ss->GetOpcode() == Opcode::SaveStateOsr);
1191         [[maybe_unused]] auto it =
1192             std::find_if(ss->GetInputs().begin(), ss->GetInputs().end(),
1193                          [inst, ss](Input input) { return ss->GetDataFlowInput(input.GetInst()) == inst; });
1194         CHECKER_DO_IF_NOT_AND_PRINT(it != ss->GetInputs().end(),
1195                                     std::cerr << "Inst v" << inst->GetId() << " used in v" << user->GetId()
1196                                               << ", but not found on the path between them in the "
1197                                               << ss->GetOpcodeStr() << " v" << ss->GetId() << ":\n"
1198                                               << *inst << std::endl
1199                                               << *user << std::endl
1200                                               << *ss << std::endl);
1201     }
1202     for (auto pred : block->GetPredsBlocks()) {
1203         CheckSaveStateOsrRec(inst, user, pred, visited);
1204     }
1205 }
1206 
1207 /*
1208  * Visitors to check instructions types
1209  */
1210 void GraphChecker::VisitMov(GraphVisitor *v, Inst *inst)
1211 {
1212     CheckUnaryOperationTypes(v, inst);
1213 }
1214 void GraphChecker::VisitNeg(GraphVisitor *v, Inst *inst)
1215 {
1216     CheckUnaryOperationTypes(v, inst);
1217 }
1218 void GraphChecker::VisitAbs(GraphVisitor *v, Inst *inst)
1219 {
1220     CheckUnaryOperationTypes(v, inst);
1221 }
1222 void GraphChecker::VisitSqrt(GraphVisitor *v, Inst *inst)
1223 {
1224     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::IsFloatType(inst->GetType()),
1225                                         (std::cerr << "\nSqrt must have float type\n", inst->Dump(&std::cerr)));
1226     CheckUnaryOperationTypes(v, inst);
1227 }
1228 
1229 void GraphChecker::VisitAddI(GraphVisitor *v, Inst *inst)
1230 {
1231     if (inst->GetType() == DataType::POINTER) {
1232         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1233             v, inst->GetInputType(0) == DataType::POINTER || inst->GetInputType(0) == DataType::REFERENCE,
1234             (std::cerr << "\nptr AddI must have ptr or ref input type\n", inst->Dump(&std::cerr)));
1235         return;
1236     }
1237     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1238                                         (std::cerr << "\nAddI must have integer type\n", inst->Dump(&std::cerr)));
1239     CheckUnaryOperationTypes(v, inst);
1240 }
1241 void GraphChecker::VisitSubI(GraphVisitor *v, Inst *inst)
1242 {
1243     if (inst->GetType() == DataType::POINTER) {
1244         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1245             v, inst->GetInputType(0) == DataType::POINTER || inst->GetInputType(0) == DataType::REFERENCE,
1246             (std::cerr << "\nptr SubI must have ptr or ref input type\n", inst->Dump(&std::cerr)));
1247         return;
1248     }
1249     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1250                                         (std::cerr << "\nSubI must have integer type\n", inst->Dump(&std::cerr)));
1251     CheckUnaryOperationTypes(v, inst);
1252 }
1253 void GraphChecker::VisitMulI(GraphVisitor *v, Inst *inst)
1254 {
1255     [[maybe_unused]] auto type = inst->GetType();
1256     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1257         v,
1258         DataType::Is32Bits(type, static_cast<GraphChecker *>(v)->GetGraph()->GetArch()) && !DataType::IsReference(type),
1259         (std::cerr << "\nMulI must have Int32 type\n", inst->Dump(&std::cerr)));
1260     CheckUnaryOperationTypes(v, inst);
1261 }
1262 void GraphChecker::VisitDivI(GraphVisitor *v, Inst *inst)
1263 {
1264     [[maybe_unused]] auto type = inst->GetType();
1265     if (static_cast<GraphChecker *>(v)->GetGraph()->IsBytecodeOptimizer()) {
1266         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1267             v,
1268             DataType::Is32Bits(type, static_cast<GraphChecker *>(v)->GetGraph()->GetArch()) &&
1269                 !DataType::IsReference(type),
1270             (std::cerr << "\nDivI must have Int32 type\n", inst->Dump(&std::cerr)));
1271     } else {
1272         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1273             v, !DataType::IsLessInt32(type) && !DataType::IsReference(type),
1274             (std::cerr << "\nDivI must have at least Int32 type\n", inst->Dump(&std::cerr)));
1275     }
1276     CheckUnaryOperationTypes(v, inst);
1277 }
1278 void GraphChecker::VisitModI(GraphVisitor *v, Inst *inst)
1279 {
1280     [[maybe_unused]] auto type = inst->GetType();
1281     if (static_cast<GraphChecker *>(v)->GetGraph()->IsBytecodeOptimizer()) {
1282         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1283             v,
1284             DataType::Is32Bits(type, static_cast<GraphChecker *>(v)->GetGraph()->GetArch()) &&
1285                 !DataType::IsReference(type),
1286             (std::cerr << "\nModI must have Int32 type\n", inst->Dump(&std::cerr)));
1287     } else {
1288         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1289             v, !DataType::IsLessInt32(type) && !DataType::IsReference(type),
1290             (std::cerr << "\nModI must have at least Int32 type\n", inst->Dump(&std::cerr)));
1291     }
1292     CheckUnaryOperationTypes(v, inst);
1293 }
1294 void GraphChecker::VisitAndI(GraphVisitor *v, Inst *inst)
1295 {
1296     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1297                                         (std::cerr << "\nAndI must have integer type\n", inst->Dump(&std::cerr)));
1298     CheckUnaryOperationTypes(v, inst);
1299 }
1300 void GraphChecker::VisitOrI(GraphVisitor *v, Inst *inst)
1301 {
1302     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1303                                         (std::cerr << "\nOrI must have integer type\n", inst->Dump(&std::cerr)));
1304     CheckUnaryOperationTypes(v, inst);
1305 }
1306 void GraphChecker::VisitXorI(GraphVisitor *v, Inst *inst)
1307 {
1308     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1309                                         (std::cerr << "\nXorI must have integer type\n", inst->Dump(&std::cerr)));
1310     CheckUnaryOperationTypes(v, inst);
1311 }
1312 void GraphChecker::VisitShlI(GraphVisitor *v, Inst *inst)
1313 {
1314     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1315                                         (std::cerr << "\nShlI must have integer type\n", inst->Dump(&std::cerr)));
1316     CheckUnaryOperationTypes(v, inst);
1317     [[maybe_unused]] auto imm = static_cast<BinaryImmOperation *>(inst)->GetImm();
1318     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1319         v, imm <= DataType::GetTypeSize(inst->GetType(), static_cast<GraphChecker *>(v)->GetGraph()->GetArch()),
1320         (std::cerr << "\nShlI have shift more then size of type\n", inst->Dump(&std::cerr)));
1321 }
1322 void GraphChecker::VisitShrI(GraphVisitor *v, Inst *inst)
1323 {
1324     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1325                                         (std::cerr << "\nShrI must have integer type\n", inst->Dump(&std::cerr)));
1326     CheckUnaryOperationTypes(v, inst);
1327     [[maybe_unused]] auto imm = static_cast<BinaryImmOperation *>(inst)->GetImm();
1328     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1329         v, imm <= DataType::GetTypeSize(inst->GetType(), static_cast<GraphChecker *>(v)->GetGraph()->GetArch()),
1330         (std::cerr << "\nShrI have shift more then size of type\n", inst->Dump(&std::cerr)));
1331 }
1332 void GraphChecker::VisitAShlI(GraphVisitor *v, Inst *inst)
1333 {
1334     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1335                                         (std::cerr << "\nAShrI must have integer type\n", inst->Dump(&std::cerr)));
1336     CheckUnaryOperationTypes(v, inst);
1337     [[maybe_unused]] auto imm = static_cast<BinaryImmOperation *>(inst)->GetImm();
1338     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1339         v, imm <= DataType::GetTypeSize(inst->GetType(), static_cast<GraphChecker *>(v)->GetGraph()->GetArch()),
1340         (std::cerr << "\nAShlI have shift more then size of type\n", inst->Dump(&std::cerr)));
1341 }
1342 void GraphChecker::VisitNot(GraphVisitor *v, Inst *inst)
1343 {
1344     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1345                                         (std::cerr << "\nNot must have integer type\n", inst->Dump(&std::cerr)));
1346     CheckUnaryOperationTypes(v, inst);
1347 }
1348 
1349 // Size does't check after lowering, because for example lowering for Arm64 can remove some casts (ex. u32->u64)
1350 bool IsIntWithPointerSize([[maybe_unused]] DataType::Type type, [[maybe_unused]] Graph *graph)
1351 {
1352 #ifndef NDEBUG
1353     auto arch = graph->GetArch();
1354     return DataType::GetCommonType(type) == DataType::INT64 &&
1355            (DataType::GetTypeSize(type, arch) == DataType::GetTypeSize(DataType::POINTER, arch) ||
1356             graph->IsLowLevelInstructionsEnabled());
1357 #else
1358     return true;
1359 #endif
1360 }
1361 
1362 void GraphChecker::VisitAdd(GraphVisitor *v, Inst *inst)
1363 {
1364     auto graph = static_cast<GraphChecker *>(v)->GetGraph();
1365     if (!graph->SupportManagedCode() && inst->GetType() == DataType::POINTER) {
1366         [[maybe_unused]] auto type1 = inst->GetInput(0).GetInst()->GetType();
1367         [[maybe_unused]] auto type2 = inst->GetInput(1).GetInst()->GetType();
1368         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1369             v, type1 != type2, (std::cerr << "\nptr Add must have ptr and int input types\n", inst->Dump(&std::cerr)));
1370         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1371             v,
1372             (type1 == DataType::POINTER && IsIntWithPointerSize(type2, graph)) ||
1373                 (type2 == DataType::POINTER && IsIntWithPointerSize(type1, graph)),
1374             (std::cerr << "\nptr Add must have ptr and int input types\n", inst->Dump(&std::cerr)));
1375         return;
1376     }
1377     CheckBinaryOperationTypes(v, inst);
1378 }
1379 void GraphChecker::VisitSub(GraphVisitor *v, Inst *inst)
1380 {
1381     auto graph = static_cast<GraphChecker *>(v)->GetGraph();
1382     if (!graph->SupportManagedCode()) {
1383         [[maybe_unused]] auto type1 = inst->GetInput(0).GetInst()->GetType();
1384         [[maybe_unused]] auto type2 = inst->GetInput(1).GetInst()->GetType();
1385         if (inst->GetType() == DataType::POINTER) {
1386             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1387                 v, type1 != type2,
1388                 (std::cerr << "\nptr Sub must have ptr and int input types\n", inst->Dump(&std::cerr)));
1389             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1390                 v, (type1 == DataType::POINTER && IsIntWithPointerSize(type2, graph)),
1391                 (std::cerr << "\nptr Sub must have ptr and int input types\n", inst->Dump(&std::cerr)));
1392             return;
1393         }
1394         if (type1 == DataType::POINTER && type2 == DataType::POINTER) {
1395             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1396                 v,
1397                 DataType::GetCommonType(inst->GetType()) == DataType::INT64 &&
1398                     IsIntWithPointerSize(inst->GetType(), graph),
1399                 (std::cerr << "\n Sub with 2 ptr inputs must have int type\n", inst->Dump(&std::cerr)));
1400             return;
1401         }
1402     }
1403     CheckBinaryOperationTypes(v, inst);
1404 }
1405 void GraphChecker::VisitMul(GraphVisitor *v, Inst *inst)
1406 {
1407     CheckBinaryOperationTypes(v, inst);
1408 }
1409 void GraphChecker::VisitDiv(GraphVisitor *v, Inst *inst)
1410 {
1411     CheckBinaryOperationTypes(v, inst);
1412 }
1413 void GraphChecker::VisitMod(GraphVisitor *v, Inst *inst)
1414 {
1415     CheckBinaryOperationTypes(v, inst);
1416 }
1417 void GraphChecker::VisitMin(GraphVisitor *v, Inst *inst)
1418 {
1419     CheckBinaryOperationTypes(v, inst);
1420 }
1421 void GraphChecker::VisitMax(GraphVisitor *v, Inst *inst)
1422 {
1423     CheckBinaryOperationTypes(v, inst);
1424 }
1425 void GraphChecker::VisitShl(GraphVisitor *v, Inst *inst)
1426 {
1427     CheckBinaryOperationTypes(v, inst, true);
1428 }
1429 void GraphChecker::VisitShr(GraphVisitor *v, Inst *inst)
1430 {
1431     CheckBinaryOperationTypes(v, inst, true);
1432 }
1433 void GraphChecker::VisitAShr(GraphVisitor *v, Inst *inst)
1434 {
1435     CheckBinaryOperationTypes(v, inst, true);
1436 }
1437 void GraphChecker::VisitAnd(GraphVisitor *v, Inst *inst)
1438 {
1439     CheckBinaryOperationTypes(v, inst, true);
1440 }
1441 void GraphChecker::VisitOr(GraphVisitor *v, Inst *inst)
1442 {
1443     CheckBinaryOperationTypes(v, inst, true);
1444 }
1445 void GraphChecker::VisitXor(GraphVisitor *v, Inst *inst)
1446 {
1447     CheckBinaryOperationTypes(v, inst, true);
1448 }
1449 
1450 void GraphChecker::VisitAddOverflow(GraphVisitor *v, Inst *inst)
1451 {
1452     CheckBinaryOverflowOperation(v, inst->CastToAddOverflow());
1453 }
1454 void GraphChecker::VisitSubOverflow(GraphVisitor *v, Inst *inst)
1455 {
1456     CheckBinaryOverflowOperation(v, inst->CastToSubOverflow());
1457 }
1458 void GraphChecker::VisitLoadArray(GraphVisitor *v, Inst *inst)
1459 {
1460     CheckMemoryInstruction(v, inst);
1461 }
1462 void GraphChecker::VisitLoadArrayI(GraphVisitor *v, Inst *inst)
1463 {
1464     CheckMemoryInstruction(v, inst);
1465 }
1466 void GraphChecker::VisitLoadArrayPair(GraphVisitor *v, Inst *inst)
1467 {
1468     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1469         v, MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1470         (std::cerr << "Unallowed type of coalesced load\n", inst->Dump(&std::cerr)));
1471     CheckMemoryInstruction(v, inst);
1472 }
1473 void GraphChecker::VisitLoadObjectPair(GraphVisitor *v, Inst *inst)
1474 {
1475     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1476         v, MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1477         (std::cerr << "Unallowed type of coalesced load\n", inst->Dump(&std::cerr)));
1478     CheckMemoryInstruction(v, inst);
1479     auto loadObj = inst->CastToLoadObjectPair();
1480     ASSERT(loadObj->GetObjectType() == MEM_OBJECT || loadObj->GetObjectType() == MEM_STATIC);
1481     ASSERT(loadObj->GetVolatile() == false);
1482     auto field0 = loadObj->GetObjField0();
1483     auto field1 = loadObj->GetObjField1();
1484     auto graph = static_cast<GraphChecker *>(v)->GetGraph();
1485     [[maybe_unused]] size_t offset0 = GetObjectOffset(graph, loadObj->GetObjectType(), field0, loadObj->GetTypeId0());
1486     [[maybe_unused]] size_t offset1 = GetObjectOffset(graph, loadObj->GetObjectType(), field1, loadObj->GetTypeId1());
1487     [[maybe_unused]] size_t fieldSize = GetTypeSize(inst->GetType(), graph->GetArch()) / BYTE_SIZE;
1488     ASSERT((offset0 + fieldSize == offset1) || (offset1 + fieldSize == offset0));
1489 }
1490 void GraphChecker::VisitLoadArrayPairI(GraphVisitor *v, Inst *inst)
1491 {
1492     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1493         v, MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1494         (std::cerr << "Unallowed type of coalesced load\n", inst->Dump(&std::cerr)));
1495     CheckMemoryInstruction(v, inst);
1496 }
1497 
1498 void GraphChecker::VisitLoadPairPart(GraphVisitor *v, Inst *inst)
1499 {
1500     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1501         v, MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1502         (std::cerr << "Unallowed type of coalesced load\n", inst->Dump(&std::cerr)));
1503     CheckMemoryInstruction(v, inst);
1504     [[maybe_unused]] auto op1 = inst->GetInputs()[0].GetInst();
1505     [[maybe_unused]] auto idx = inst->CastToLoadPairPart()->GetImm();
1506     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, op1->WithGluedInsts(),
1507                                         (std::cerr << "Input instruction is not a Pair\n", inst->Dump(&std::cerr)));
1508     if (op1->GetOpcode() == Opcode::LoadArrayPairI) {
1509         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, idx < op1->CastToLoadArrayPairI()->GetDstCount(),
1510                                             (std::cerr << "Pair index is out of bounds\n", inst->Dump(&std::cerr)));
1511     } else if (op1->GetOpcode() == Opcode::LoadArrayPair) {
1512         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, idx < op1->CastToLoadArrayPair()->GetDstCount(),
1513                                             (std::cerr << "Pair index is out of bounds\n", inst->Dump(&std::cerr)));
1514     } else {
1515         ASSERT(op1->GetOpcode() == Opcode::LoadObjectPair);
1516     }
1517     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1518         v, CheckCommonTypes(inst, inst->GetInputs()[0].GetInst()),
1519         (std::cerr << "Types of load vector element and vector input are not compatible\n", inst->Dump(&std::cerr)));
1520 
1521     // Strict order here
1522     auto prev = inst->GetPrev();
1523     while (prev != nullptr && prev != op1) {
1524         if (prev->GetOpcode() == Opcode::LoadPairPart || prev->GetOpcode() == Opcode::SpillFill) {
1525             prev = prev->GetPrev();
1526         } else {
1527             break;
1528         }
1529     }
1530     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1531         v, prev != nullptr && prev == op1,
1532         (std::cerr << "LoadPairPart(s) instructions must follow immediately after appropriate "
1533                       "LoadArrayPair(I) or LoadObjectPair\n",
1534          inst->Dump(&std::cerr), prev->Dump(&std::cerr), inst->GetBasicBlock()->GetGraph()->Dump(&std::cerr)));
1535 }
1536 
1537 void GraphChecker::VisitStoreArrayPair(GraphVisitor *v, Inst *inst)
1538 {
1539     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1540         v, MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1541         (std::cerr << "Unallowed type of coalesced store\n", inst->Dump(&std::cerr)));
1542     CheckMemoryInstruction(v, inst);
1543     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1544         v, CheckCommonTypes(inst, inst->GetInputs()[2U].GetInst()),
1545         (std::cerr << "Types of store and the first stored value are not compatible\n", inst->Dump(&std::cerr)));
1546     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1547         v, CheckCommonTypes(inst, inst->GetInputs()[3U].GetInst()),
1548         (std::cerr << "Types of store and the second stored value are not compatible\n", inst->Dump(&std::cerr)));
1549     [[maybe_unused]] bool needBarrier = inst->CastToStoreArrayPair()->GetNeedBarrier();
1550     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1551         v, needBarrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1552         (std::cerr << "StoreArrayPair has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1553 }
1554 
1555 void GraphChecker::VisitStoreObjectPair(GraphVisitor *v, Inst *inst)
1556 {
1557     auto storeObj = inst->CastToStoreObjectPair();
1558     bool needBarrier = storeObj->GetNeedBarrier();
1559     CheckMemoryInstruction(v, inst, needBarrier);
1560     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1561         v, CheckCommonTypes(inst, inst->GetInputs()[1].GetInst()),
1562         (std::cerr << "Types of store and the first store input are not compatible\n", inst->Dump(&std::cerr)));
1563     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1564         v, CheckCommonTypes(inst, inst->GetInputs()[2].GetInst()),
1565         (std::cerr << "Types of store and the second store input are not compatible\n", inst->Dump(&std::cerr)));
1566     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1567         v, needBarrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1568         (std::cerr << "StoreObjectPair has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1569 
1570     ASSERT(storeObj->GetObjectType() == MEM_OBJECT || storeObj->GetObjectType() == MEM_STATIC);
1571     ASSERT(storeObj->GetVolatile() == false);
1572     auto field0 = storeObj->GetObjField0();
1573     auto field1 = storeObj->GetObjField1();
1574     auto graph = static_cast<GraphChecker *>(v)->GetGraph();
1575     [[maybe_unused]] size_t offset0 = GetObjectOffset(graph, storeObj->GetObjectType(), field0, storeObj->GetTypeId0());
1576     [[maybe_unused]] size_t offset1 = GetObjectOffset(graph, storeObj->GetObjectType(), field1, storeObj->GetTypeId1());
1577     [[maybe_unused]] size_t fieldSize = GetTypeSize(inst->GetType(), graph->GetArch()) / BYTE_SIZE;
1578     ASSERT((offset0 + fieldSize == offset1) || (offset1 + fieldSize == offset0));
1579 }
1580 
1581 void GraphChecker::VisitStoreArrayPairI(GraphVisitor *v, Inst *inst)
1582 {
1583     bool needBarrier = inst->CastToStoreArrayPairI()->GetNeedBarrier();
1584     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1585         v, MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1586         (std::cerr << "Unallowed type of coalesced store\n", inst->Dump(&std::cerr)));
1587     CheckMemoryInstruction(v, inst, needBarrier);
1588     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1589         v, CheckCommonTypes(inst, inst->GetInputs()[1].GetInst()),
1590         (std::cerr << "Types of store and the first stored value are not compatible\n", inst->Dump(&std::cerr)));
1591     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1592         v, CheckCommonTypes(inst, inst->GetInputs()[2U].GetInst()),
1593         (std::cerr << "Types of store and the second stored value are not compatible\n", inst->Dump(&std::cerr)));
1594     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1595         v, needBarrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1596         (std::cerr << "StoreArrayPairI has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1597 }
1598 
1599 void GraphChecker::VisitStore(GraphVisitor *v, Inst *inst)
1600 {
1601     bool needBarrier = inst->CastToStore()->GetNeedBarrier();
1602     CheckMemoryInstruction(v, inst, needBarrier);
1603 }
1604 
1605 void GraphChecker::VisitStoreI(GraphVisitor *v, Inst *inst)
1606 {
1607     bool needBarrier = inst->CastToStoreI()->GetNeedBarrier();
1608     CheckMemoryInstruction(v, inst, needBarrier);
1609 }
1610 
1611 void GraphChecker::VisitStoreArray(GraphVisitor *v, Inst *inst)
1612 {
1613     bool needBarrier = inst->CastToStoreArray()->GetNeedBarrier();
1614     CheckMemoryInstruction(v, inst, needBarrier);
1615     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1616         v, CheckCommonTypes(inst, inst->GetInputs()[2U].GetInst()),
1617         (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1618     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1619         v, needBarrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1620         (std::cerr << "StoreArray has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1621 }
1622 
1623 void GraphChecker::VisitStoreArrayI(GraphVisitor *v, Inst *inst)
1624 {
1625     bool needBarrier = inst->CastToStoreArrayI()->GetNeedBarrier();
1626     CheckMemoryInstruction(v, inst, needBarrier);
1627     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1628         v, CheckCommonTypes(inst, inst->GetInputs()[1].GetInst()),
1629         (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1630     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1631         v, needBarrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1632         (std::cerr << "StoreArrayI has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1633 }
1634 
1635 void GraphChecker::VisitStoreStatic(GraphVisitor *v, Inst *inst)
1636 {
1637     bool needBarrier = inst->CastToStoreStatic()->GetNeedBarrier();
1638     CheckMemoryInstruction(v, inst, needBarrier);
1639     auto graph = static_cast<GraphChecker *>(v)->GetGraph();
1640     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1641         v, CheckCommonTypes(inst, inst->GetInputs()[1].GetInst()),
1642         (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1643     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1644         v, needBarrier == (inst->GetType() == DataType::REFERENCE),
1645         (std::cerr << "StoreStatic has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1646     [[maybe_unused]] auto initInst = inst->GetInputs()[0].GetInst();
1647     if (initInst->IsPhi()) {
1648         return;
1649     }
1650     [[maybe_unused]] auto opcode = initInst->GetOpcode();
1651     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1652         v, (opcode == Opcode::LoadAndInitClass || opcode == Opcode::LoadImmediate),
1653         (std::cerr << "The first input for the StoreStatic should be LoadAndInitClass or LoadImmediate",
1654          inst->Dump(&std::cerr), initInst->Dump(&std::cerr)));
1655     [[maybe_unused]] auto storeStatic = inst->CastToStoreStatic();
1656     [[maybe_unused]] auto classId =
1657         graph->GetRuntime()->GetClassIdForField(storeStatic->GetMethod(), storeStatic->GetTypeId());
1658     // See comment in VisitNewObject about this if statement
1659     if (opcode == Opcode::LoadAndInitClass && initInst->CastToLoadAndInitClass()->GetClass() == nullptr) {
1660         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, initInst->CastToLoadAndInitClass()->GetTypeId() == classId,
1661                                             (std::cerr << "StoreStatic and LoadAndInitClass must have equal class",
1662                                              inst->Dump(&std::cerr), initInst->Dump(&std::cerr)));
1663     }
1664 }
1665 
1666 void GraphChecker::VisitUnresolvedStoreStatic(GraphVisitor *v, Inst *inst)
1667 {
1668     bool needBarrier = inst->CastToUnresolvedStoreStatic()->GetNeedBarrier();
1669     CheckMemoryInstruction(v, inst, needBarrier);
1670     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1671         v, CheckCommonTypes(inst, inst->GetInputs()[0].GetInst()),
1672         (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1673     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1674         v, needBarrier == (inst->GetType() == DataType::REFERENCE),
1675         (std::cerr << "UnresolvedStoreStatic has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1676     [[maybe_unused]] auto ss = inst->GetInputs()[1].GetInst();
1677     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1678         v, ss->GetOpcode() == Opcode::SaveState,
1679         (std::cerr << "UnresolvedStoreStatic instruction second operand is not a SaveState", inst->Dump(&std::cerr),
1680          ss->Dump(&std::cerr)));
1681 }
1682 
1683 void GraphChecker::VisitStoreObject(GraphVisitor *v, Inst *inst)
1684 {
1685     bool needBarrier = inst->CastToStoreObject()->GetNeedBarrier();
1686     CheckMemoryInstruction(v, inst, needBarrier);
1687     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1688         v, CheckCommonTypes(inst, inst->GetInputs()[1].GetInst()),
1689         (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1690     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1691         v, needBarrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1692         (std::cerr << "StoreObject has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1693     CheckObjectType(v, inst, inst->CastToStoreObject()->GetObjectType(), inst->CastToStoreObject()->GetTypeId());
1694 }
1695 
1696 void GraphChecker::VisitStoreObjectDynamic(GraphVisitor *v, Inst *inst)
1697 {
1698     bool needBarrier = inst->CastToStoreObjectDynamic()->IsBarrier();
1699     CheckMemoryInstruction(v, inst, needBarrier);
1700 }
1701 
1702 void GraphChecker::VisitFillConstArray(GraphVisitor *v, Inst *inst)
1703 {
1704     bool needBarrier = inst->CastToFillConstArray()->GetNeedBarrier();
1705     CheckMemoryInstruction(v, inst, needBarrier);
1706 }
1707 
1708 void GraphChecker::VisitStoreResolvedObjectField(GraphVisitor *v, Inst *inst)
1709 {
1710     bool needBarrier = inst->CastToStoreResolvedObjectField()->GetNeedBarrier();
1711     CheckMemoryInstruction(v, inst, needBarrier);
1712 }
1713 
1714 void GraphChecker::VisitStoreResolvedObjectFieldStatic(GraphVisitor *v, Inst *inst)
1715 {
1716     bool needBarrier = inst->CastToStoreResolvedObjectFieldStatic()->GetNeedBarrier();
1717     CheckMemoryInstruction(v, inst, needBarrier);
1718 }
1719 
1720 void GraphChecker::VisitLoadStatic(GraphVisitor *v, Inst *inst)
1721 {
1722     CheckMemoryInstruction(v, inst);
1723     auto graph = static_cast<GraphChecker *>(v)->GetGraph();
1724     [[maybe_unused]] auto initInst = inst->GetInputs()[0].GetInst();
1725     if (initInst->IsPhi()) {
1726         return;
1727     }
1728     [[maybe_unused]] auto opcode = initInst->GetOpcode();
1729     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1730         v, opcode == Opcode::LoadAndInitClass || opcode == Opcode::LoadImmediate,
1731         (std::cerr << "The first input for the LoadStatic should be LoadAndInitClass or LoadImmediate",
1732          inst->Dump(&std::cerr), initInst->Dump(&std::cerr)));
1733     [[maybe_unused]] auto loadStatic = inst->CastToLoadStatic();
1734     [[maybe_unused]] auto classId =
1735         graph->GetRuntime()->GetClassIdForField(loadStatic->GetMethod(), loadStatic->GetTypeId());
1736     // See comment in VisitNewObject about this if statement
1737     if (opcode == Opcode::LoadAndInitClass && initInst->CastToLoadAndInitClass()->GetClass() == nullptr) {
1738         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, initInst->CastToLoadAndInitClass()->GetTypeId() == classId,
1739                                             (std::cerr << "LoadStatic and LoadAndInitClass must have equal class",
1740                                              inst->Dump(&std::cerr), initInst->Dump(&std::cerr)));
1741     }
1742 }
1743 
1744 void GraphChecker::VisitLoadClass([[maybe_unused]] GraphVisitor *v, Inst *inst)
1745 {
1746     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetType() == DataType::REFERENCE,
1747                                         (std::cerr << "LoadClass must have Reference type", inst->Dump(&std::cerr)));
1748     for (auto &user : inst->GetUsers()) {
1749         [[maybe_unused]] auto userInst = user.GetInst();
1750         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1751             v,
1752             userInst->GetOpcode() == Opcode::CheckCast || userInst->GetOpcode() == Opcode::IsInstance ||
1753                 userInst->GetOpcode() == Opcode::Phi || userInst->GetOpcode() == Opcode::Intrinsic,
1754             (std::cerr << "Incorrect user of the LoadClass", inst->Dump(&std::cerr), userInst->Dump(&std::cerr)));
1755     }
1756 }
1757 
1758 void GraphChecker::VisitLoadAndInitClass([[maybe_unused]] GraphVisitor *v, Inst *inst)
1759 {
1760     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1761         v, inst->GetType() == DataType::REFERENCE,
1762         (std::cerr << "LoadAndInitClass must have Reference type", inst->Dump(&std::cerr)));
1763     for (auto &user : inst->GetUsers()) {
1764         [[maybe_unused]] auto userInst = user.GetInst();
1765         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1766             v,
1767             userInst->GetOpcode() == Opcode::LoadStatic ||
1768                 (userInst->GetOpcode() == Opcode::LoadObject &&
1769                  userInst->CastToLoadObject()->GetObjectType() == ObjectType::MEM_STATIC) ||
1770                 userInst->GetOpcode() == Opcode::StoreStatic ||
1771                 (userInst->GetOpcode() == Opcode::StoreObject &&
1772                  userInst->CastToStoreObject()->GetObjectType() == ObjectType::MEM_STATIC) ||
1773                 userInst->GetOpcode() == Opcode::NewObject || userInst->GetOpcode() == Opcode::Phi ||
1774                 userInst->GetOpcode() == Opcode::MultiArray || userInst->GetOpcode() == Opcode::InitObject ||
1775                 userInst->GetOpcode() == Opcode::UnresolvedStoreStatic || userInst->GetOpcode() == Opcode::Intrinsic ||
1776                 userInst->GetOpcode() == Opcode::NewArray || userInst->GetOpcode() == Opcode::IsInstance ||
1777                 userInst->GetOpcode() == Opcode::CheckCast,
1778             (std::cerr << "Incorrect user of the LoadAndInitClass", inst->Dump(&std::cerr),
1779              userInst->Dump(&std::cerr)));
1780     }
1781 }
1782 
1783 void GraphChecker::VisitUnresolvedLoadAndInitClass([[maybe_unused]] GraphVisitor *v, Inst *inst)
1784 {
1785     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1786         v, inst->GetType() == DataType::REFERENCE,
1787         (std::cerr << "UnresolvedLoadAndInitClass must have Reference type", inst->Dump(&std::cerr)));
1788     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1789         v, inst->CastToUnresolvedLoadAndInitClass()->GetClass() == nullptr,
1790         (std::cerr << "UnresolvedLoadAndInitClass must have a null ClassPtr", inst->Dump(&std::cerr)));
1791     [[maybe_unused]] auto ss = inst->GetInputs()[0].GetInst();
1792     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1793         v, ss->GetOpcode() == Opcode::SaveState,
1794         (std::cerr << "UnresolvedLoadAndInitClass instruction first operand is not a SaveState", inst->Dump(&std::cerr),
1795          ss->Dump(&std::cerr)));
1796     for (auto &user : inst->GetUsers()) {
1797         [[maybe_unused]] auto userInst = user.GetInst();
1798         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1799             v,
1800             userInst->GetOpcode() == Opcode::LoadStatic || userInst->GetOpcode() == Opcode::StoreStatic ||
1801                 userInst->GetOpcode() == Opcode::NewObject || userInst->GetOpcode() == Opcode::NewArray ||
1802                 userInst->GetOpcode() == Opcode::Phi || userInst->GetOpcode() == Opcode::MultiArray ||
1803                 userInst->GetOpcode() == Opcode::UnresolvedStoreStatic,
1804             (std::cerr << "Incorrect user of the UnresolvedLoadAndInitClass", inst->Dump(&std::cerr),
1805              userInst->Dump(&std::cerr)));
1806     }
1807 }
1808 
1809 void GraphChecker::VisitGetInstanceClass([[maybe_unused]] GraphVisitor *v, Inst *inst)
1810 {
1811     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1812         v, inst->GetType() == DataType::REFERENCE,
1813         (std::cerr << "GetInstanceClass must have Reference type", inst->Dump(&std::cerr)));
1814     for (auto &user : inst->GetUsers()) {
1815         [[maybe_unused]] auto userInst = user.GetInst();
1816         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, !userInst->IsSaveState(),
1817                                             (std::cerr << "Incorrect user of the GetInstanceClass",
1818                                              inst->Dump(&std::cerr), userInst->Dump(&std::cerr)));
1819     }
1820     ASSERT_DO(
1821         !inst->GetBasicBlock()->GetGraph()->IsDynamicMethod(),
1822         (std::cerr
1823          << "we can't use GetInstanceClass in dynamic, because object class can be changed. Use LoadObject MEM_CLASS"));
1824 }
1825 
1826 void GraphChecker::VisitNewObject([[maybe_unused]] GraphVisitor *v, Inst *inst)
1827 {
1828     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetType() == DataType::REFERENCE,
1829                                         (std::cerr << "NewObject must be have Reference type", inst->Dump(&std::cerr)));
1830     [[maybe_unused]] auto initInst = inst->GetInputs()[0].GetInst();
1831     if (initInst->IsPhi()) {
1832         return;
1833     }
1834     [[maybe_unused]] auto opcode = initInst->GetOpcode();
1835     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v,
1836                                         opcode == Opcode::LoadAndInitClass || opcode == Opcode::LoadRuntimeClass ||
1837                                             opcode == Opcode::UnresolvedLoadAndInitClass ||
1838                                             opcode == Opcode::LoadImmediate,
1839                                         (std::cerr << "The first input for the NewObject should be LoadAndInitClass or "
1840                                                       "UnresolvedLoadAndInitClass or LoadRuntimeClass or LoadImmediate",
1841                                          inst->Dump(&std::cerr), initInst->Dump(&std::cerr)));
1842     [[maybe_unused]] auto ssInst = inst->GetInputs()[1].GetInst();
1843     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, ssInst->GetOpcode() == Opcode::SaveState,
1844                                         (std::cerr << "The second input for the NewObject should be SaveState",
1845                                          inst->Dump(&std::cerr), ssInst->Dump(&std::cerr)));
1846     // If InitClass contains an already resolved class, then IDs may be different. Because VN can remove the
1847     // duplicated InitClass and keep only one that is located in the inlined method and has a different id
1848     // accordingly.
1849     if (initInst->GetOpcode() == Opcode::LoadAndInitClass &&
1850         initInst->CastToLoadAndInitClass()->GetClass() == nullptr) {
1851         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1852             v, initInst->CastToLoadAndInitClass()->GetTypeId() == inst->CastToNewObject()->GetTypeId(),
1853             std::cerr << "NewObject and LoadAndInitClass must have equal class:\n"
1854                       << *inst << '\n'
1855                       << *initInst << std::endl);
1856     } else if (initInst->GetOpcode() == Opcode::UnresolvedLoadAndInitClass) {
1857         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1858             v, initInst->CastToUnresolvedLoadAndInitClass()->GetTypeId() == inst->CastToNewObject()->GetTypeId(),
1859             std::cerr << "NewObject and UnresolvedLoadAndInitClass must have equal class:\n"
1860                       << *inst << '\n'
1861                       << *initInst << std::endl);
1862     }
1863 }
1864 
1865 void GraphChecker::VisitLoadRuntimeClass([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1866 {
1867     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1868         v, inst->CastToLoadRuntimeClass()->GetTypeId() == TypeIdMixin::MEM_PROMISE_CLASS_ID,
1869         (std::cerr << "LoadRuntimeClass should have TypeId MEM_PROMISE_CLASS_ID", inst->Dump(&std::cerr)));
1870 }
1871 
1872 void GraphChecker::VisitInitObject([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1873 {
1874     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1875         v, g_options.IsCompilerSupportInitObjectInst(),
1876         (std::cerr << "Instruction InitObject isn't supported", inst->Dump(&std::cerr)));
1877 }
1878 
1879 void GraphChecker::VisitInitClass([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1880 {
1881     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetType() == DataType::NO_TYPE,
1882                                         (std::cerr << "InitClass doesn't have type", inst->Dump(&std::cerr)));
1883 }
1884 
1885 void GraphChecker::VisitIntrinsic([[maybe_unused]] GraphVisitor *v, Inst *inst)
1886 {
1887     switch (inst->CastToIntrinsic()->GetIntrinsicId()) {
1888 #include "intrinsics_graph_checker.inl"
1889         default: {
1890             return;
1891         }
1892     }
1893 }
1894 
1895 void GraphChecker::VisitLoadObject(GraphVisitor *v, Inst *inst)
1896 {
1897     CheckMemoryInstruction(v, inst);
1898     CheckObjectType(v, inst, inst->CastToLoadObject()->GetObjectType(), inst->CastToLoadObject()->GetTypeId());
1899 }
1900 
1901 void GraphChecker::VisitConstant([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1902 {
1903     [[maybe_unused]] auto type = inst->GetType();
1904     [[maybe_unused]] auto isDynamic = static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod();
1905     if (static_cast<GraphChecker *>(v)->GetGraph()->IsBytecodeOptimizer()) {
1906         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1907             v,
1908             type == DataType::FLOAT32 || type == DataType::FLOAT64 || type == DataType::INT64 ||
1909                 type == DataType::INT32 || (type == DataType::ANY && isDynamic),
1910             (std::cerr << "Constant inst can be only FLOAT32, FLOAT64, INT32 or INT64\n", inst->Dump(&std::cerr)));
1911 
1912     } else {
1913         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1914             v,
1915             type == DataType::FLOAT32 || type == DataType::FLOAT64 || type == DataType::INT64 ||
1916                 (type == DataType::ANY && isDynamic),
1917             (std::cerr << "Constant instruction can be only FLOAT32, FLOAT64 or INT64\n", inst->Dump(&std::cerr)));
1918     }
1919 }
1920 
1921 void GraphChecker::VisitNullPtr([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1922 {
1923     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1924         v, inst->GetType() == DataType::REFERENCE,
1925         (std::cerr << "NullPtr instruction should have REFERENCE type only\n", inst->Dump(&std::cerr)));
1926 
1927     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1928         v, static_cast<GraphChecker *>(v)->IncrementNullPtrInstCounterAndGet() == 1,
1929         (std::cerr << "There should be not more than one NullPtr instruction in graph\n",
1930          inst->GetBasicBlock()->Dump(&std::cerr)));
1931 }
1932 
1933 void GraphChecker::VisitLoadUndefined([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1934 {
1935     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1936         v, inst->GetType() == DataType::REFERENCE,
1937         (std::cerr << "LoadUndefined instruction should have REFERENCE type only\n", inst->Dump(&std::cerr)));
1938 
1939     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1940         v, static_cast<GraphChecker *>(v)->IncrementLoadUndefinedInstCounterAndGet() == 1,
1941         (std::cerr << "There should be not more than one LoadUndefined instruction in graph\n",
1942          inst->GetBasicBlock()->Dump(&std::cerr)));
1943 }
1944 
1945 void GraphChecker::VisitPhi([[maybe_unused]] GraphVisitor *v, Inst *inst)
1946 {
1947     for ([[maybe_unused]] auto input : inst->GetInputs()) {
1948         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1949             v,
1950             CheckCommonTypes(inst, input.GetInst()) ||
1951                 (static_cast<GraphChecker *>(v)->GetGraph()->IsThrowApplied() &&
1952                  (input.GetInst()->GetBasicBlock()->IsEndWithThrow() ||
1953                   input.GetInst()->GetOpcode() == Opcode::CatchPhi || input.GetInst()->GetOpcode() == Opcode::Phi)),
1954             (std::cerr << "Types of phi result and phi input are not compatible\n"
1955                        << *inst << std::endl
1956                        << *input.GetInst()));
1957     }
1958 }
1959 
1960 void GraphChecker::VisitParameter([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1961 {
1962     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetType() != DataType::NO_TYPE,
1963                                         (std::cerr << "The parametr doesn't have type:\n", inst->Dump(&std::cerr)));
1964 }
1965 
1966 void GraphChecker::VisitCompare([[maybe_unused]] GraphVisitor *v, Inst *inst)
1967 {
1968     [[maybe_unused]] auto op1 = inst->GetInputs()[0].GetInst();
1969     [[maybe_unused]] auto op2 = inst->GetInputs()[1].GetInst();
1970     for (size_t i = 0; i < inst->GetInputsCount(); i++) {
1971         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetInputType(i) != DataType::NO_TYPE,
1972                                             std::cerr << "Source operand type is not set: " << *inst << std::endl);
1973     }
1974     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetInputType(0) == inst->GetInputType(1),
1975                                         std::cerr << "Conditional instruction has different inputs type: " << *inst
1976                                                   << std::endl);
1977     if (inst->GetInputType(0) == DataType::REFERENCE) {
1978         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1979             v,
1980             inst->CastToCompare()->GetCc() == ConditionCode::CC_NE ||
1981                 inst->CastToCompare()->GetCc() == ConditionCode::CC_EQ,
1982             (std::cerr << "Reference compare must have CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
1983         if (op1->IsConst()) {
1984             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, IsZeroConstant(op1),
1985                                                 (std::cerr << "Constant reference input must be integer 0: \n",
1986                                                  inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1987         } else {
1988             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1989                 v, op1->GetType() == DataType::REFERENCE,
1990                 (std::cerr << "Condition instruction 1st operand type is not a reference\n", inst->Dump(&std::cerr),
1991                  op1->Dump(&std::cerr)));
1992         }
1993         if (op2->IsConst()) {
1994             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, IsZeroConstant(op2),
1995                                                 (std::cerr << "Constant reference input must be integer 0: \n",
1996                                                  inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
1997         } else {
1998             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
1999                 v, op2->GetType() == DataType::REFERENCE,
2000                 (std::cerr << "Condition instruction 2nd operand type is not a reference\n", inst->Dump(&std::cerr),
2001                  op2->Dump(&std::cerr)));
2002         }
2003     }
2004     CHECKER_MESSAGE_IF_NOT_AND_PRINT_VISITOR(v, inst->GetType() == DataType::BOOL,
2005                                              "Condition instruction type is not a bool");
2006 }
2007 
2008 void GraphChecker::VisitCast([[maybe_unused]] GraphVisitor *v, Inst *inst)
2009 {
2010     [[maybe_unused]] auto dstType = inst->GetType();
2011     [[maybe_unused]] auto srcType = inst->GetInputType(0);
2012     [[maybe_unused]] auto inputType = inst->GetInput(0).GetInst()->GetType();
2013 
2014     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2015         v, DataType::IsTypeNumeric(dstType),
2016         (std::cerr << "Cast instruction dst type is not a numeric type\n", inst->Dump(&std::cerr)));
2017     if (static_cast<GraphChecker *>(v)->GetGraph()->GetMode().SupportManagedCode()) {
2018         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2019             v, DataType::IsTypeNumeric(srcType),
2020             (std::cerr << "Cast instruction src type is not a numeric type\n", inst->Dump(&std::cerr)));
2021         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2022             v, DataType::IsTypeNumeric(inputType),
2023             (std::cerr << "Cast instruction operand type is not a numeric type\n", inst->Dump(&std::cerr)));
2024     }
2025     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::GetCommonType(srcType) == DataType::GetCommonType(inputType),
2026                                         (std::cerr << "Incorrect src_type and input type\n", inst->Dump(&std::cerr)));
2027     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, !(DataType::IsFloatType(srcType) && DataType::IsLessInt32(dstType)),
2028                                         (std::cerr << "Cast instruction from "
2029                                                    << DataType::internal::TYPE_NAMES.at(srcType) << " to "
2030                                                    << DataType::internal::TYPE_NAMES.at(dstType) << " don't support\n",
2031                                          inst->Dump(&std::cerr)));
2032 }
2033 
2034 void GraphChecker::VisitCmp([[maybe_unused]] GraphVisitor *v, Inst *inst)
2035 {
2036     [[maybe_unused]] auto op1 = inst->GetInput(0).GetInst();
2037     [[maybe_unused]] auto op2 = inst->GetInput(1).GetInst();
2038     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::IsTypeNumeric(op1->GetType()),
2039                                         (std::cerr << "Cmp instruction 1st operand type is not a numeric type\n",
2040                                          inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
2041     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::IsTypeNumeric(op2->GetType()),
2042                                         (std::cerr << "Cmp instruction 2st operand type is not a numeric type\n",
2043                                          inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
2044     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2045         v, DataType::GetCommonType(op1->GetType()) == DataType::GetCommonType(inst->GetInputType(0)),
2046         (std::cerr << "Input type and Cmp Input Type are not equal\n", inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
2047     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2048         v, DataType::GetCommonType(op2->GetType()) == DataType::GetCommonType(inst->GetInputType(1)),
2049         (std::cerr << "Input type and Cmp Input Type are not equal\n", inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
2050     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetType() == DataType::INT32,
2051                                         (std::cerr << "Cmp instruction type is not a int32\n", inst->Dump(&std::cerr)));
2052     for (size_t i = 0; i < inst->GetInputsCount(); i++) {
2053         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetInputType(i) != DataType::NO_TYPE,
2054                                             std::cerr << "Source operand type is not set: " << *inst << std::endl);
2055     }
2056 }
2057 
2058 void GraphChecker::VisitMonitor([[maybe_unused]] GraphVisitor *v, Inst *inst)
2059 {
2060     [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
2061     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetType() == DataType::VOID,
2062                                         (std::cerr << "Monitor type is not a void", inst->Dump(&std::cerr)));
2063     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::IsReference(op->GetType()),
2064                                         (std::cerr << "Monitor instruction 1st operand type is not a reference",
2065                                          inst->Dump(&std::cerr), op->Dump(&std::cerr)));
2066     [[maybe_unused]] auto op1 = inst->GetInputs()[1].GetInst();
2067     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, op1->GetOpcode() == Opcode::SaveState,
2068                                         (std::cerr << "Monitor instruction second operand is not a SaveState",
2069                                          inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
2070 }
2071 
2072 void GraphChecker::VisitReturn(GraphVisitor *v, Inst *inst)
2073 {
2074     [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
2075     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, CheckCommonTypes(inst, op),
2076                                         (std::cerr << "Types of return and its input are not compatible\n return:\n",
2077                                          inst->Dump(&std::cerr), std::cerr << "\n input:\n", op->Dump(&std::cerr)));
2078     CheckContrlFlowInst(v, inst);
2079     [[maybe_unused]] auto numSuccs = inst->GetBasicBlock()->GetSuccsBlocks().size();
2080     CHECKER_MESSAGE_IF_NOT_AND_PRINT_VISITOR(v, numSuccs == 1, "Basic block with Return must have 1 successor");
2081     [[maybe_unused]] auto succ = inst->GetBasicBlock()->GetSuccsBlocks()[0];
2082     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, succ->IsEndBlock() || succ->IsTryEnd(),
2083                                         std::cerr
2084                                             << "Basic block with Return must have end or try end block as successor:\n"
2085                                             << *inst << std::endl);
2086 }
2087 
2088 void GraphChecker::VisitReturnVoid(GraphVisitor *v, Inst *inst)
2089 {
2090     CheckContrlFlowInst(v, inst);
2091     [[maybe_unused]] auto numSuccs = inst->GetBasicBlock()->GetSuccsBlocks().size();
2092     CHECKER_MESSAGE_IF_NOT_AND_PRINT_VISITOR(v, numSuccs == 1, "Basic block with ReturnVoid must have 1 successor");
2093     [[maybe_unused]] auto succ = inst->GetBasicBlock()->GetSuccsBlocks()[0];
2094     CHECKER_MESSAGE_IF_NOT_AND_PRINT_VISITOR(
2095         v, succ->IsEndBlock() || succ->IsTryEnd(),
2096         "Basic block with ReturnVoid must have end or try_end block as successor.");
2097 }
2098 
2099 void GraphChecker::VisitNullCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
2100 {
2101     [[maybe_unused]] Inst *array = inst->GetInput(0).GetInst();
2102     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::IsReference(array->GetType()) || array->GetType() == DataType::ANY,
2103                                         (std::cerr << "\n Types of input NullCheck must be REFERENCE or ANY: \n",
2104                                          inst->Dump(&std::cerr), array->Dump(&std::cerr)));
2105     [[maybe_unused]] auto ss = inst->GetInput(1).GetInst();
2106     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2107         v, ss->GetOpcode() == Opcode::SaveState || ss->GetOpcode() == Opcode::SaveStateDeoptimize,
2108         (std::cerr << "\n Second input of NullCheck must be SaveState: \n", inst->Dump(&std::cerr),
2109          ss->Dump(&std::cerr)));
2110 }
2111 
2112 void GraphChecker::VisitBoundsCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
2113 {
2114     for (int i = 0; i < 1; i++) {
2115         [[maybe_unused]] auto op = inst->GetInputs()[i].GetInst();
2116         [[maybe_unused]] auto opType = op->GetType();
2117         // NOTE(pishin): actually type should be INT32, but predecessor may be Call instruction with type u16, u8
2118         // e.t.c
2119         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2120             v,
2121             (op->IsConst() && opType == DataType::INT64) ||
2122                 (DataType::GetCommonType(opType) == DataType::INT64 &&
2123                  Is32Bits(opType, static_cast<GraphChecker *>(v)->GetGraph()->GetArch())),
2124             (std::cerr << "Types of " << i << " input BoundsCheck must be INT32 or less:\n", inst->Dump(&std::cerr)));
2125     }
2126 }
2127 
2128 void GraphChecker::VisitBoundsCheckI([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2129 {
2130     CHECKER_IF_NOT_PRINT_VISITOR(v, !inst->HasUsers());
2131 }
2132 
2133 void GraphChecker::VisitRefTypeCheck(GraphVisitor *v, Inst *inst)
2134 {
2135     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2136         v, (inst->GetType() == DataType::REFERENCE),
2137         (std::cerr << "Types of RefTypeCheck must be REFERENCE\n", inst->Dump(&std::cerr)));
2138     for (unsigned i = 0; i < 2U; i++) {
2139         [[maybe_unused]] auto op = inst->GetInputs()[i].GetInst();
2140         [[maybe_unused]] auto opType = op->GetType();
2141         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, (opType == DataType::REFERENCE),
2142                                             (std::cerr << "Types of " << i << " input RefTypeCheck must be REFERENCE\n",
2143                                              inst->Dump(&std::cerr), op->Dump(&std::cerr)));
2144     }
2145     CheckThrows(v, inst,
2146                 {Opcode::StoreArray, Opcode::StoreArrayPair, Opcode::StoreArrayI, Opcode::StoreArrayPairI,
2147                  Opcode::SaveState, Opcode::SafePoint});
2148 }
2149 
2150 void GraphChecker::VisitNegativeCheck(GraphVisitor *v, Inst *inst)
2151 {
2152     [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
2153     [[maybe_unused]] auto opType = op->GetType();
2154     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2155         v, DataType::GetCommonType(opType) == DataType::INT64,
2156         (std::cerr << "Type of NegativeCheck must be integer\n", inst->Dump(&std::cerr)));
2157     if (inst->GetBasicBlock()->GetGraph()->IsDynamicMethod()) {
2158         // In dynamic methods for negative values we creates f64 Mod, so we insert NegativeCheck before Mod
2159         // Lowering can change Mod to And(I)
2160         CheckThrows(
2161             v, inst,
2162             {Opcode::NewArray, Opcode::MultiArray, Opcode::Phi, Opcode::Mod, Opcode::ModI, Opcode::And, Opcode::AndI});
2163     } else {
2164         CheckThrows(v, inst, {Opcode::NewArray, Opcode::MultiArray, Opcode::Phi});
2165     }
2166 }
2167 
2168 void GraphChecker::VisitNotPositiveCheck(GraphVisitor *v, Inst *inst)
2169 {
2170     [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
2171     [[maybe_unused]] auto opType = op->GetType();
2172     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2173         v, DataType::GetCommonType(opType) == DataType::INT64,
2174         (std::cerr << "Type of NotPositiveCheck must be integer\n", inst->Dump(&std::cerr)));
2175     ASSERT(inst->GetBasicBlock()->GetGraph()->IsDynamicMethod());
2176     CheckThrows(v, inst, {Opcode::Phi, Opcode::Mod, Opcode::ModI});
2177 }
2178 
2179 void GraphChecker::VisitZeroCheck(GraphVisitor *v, Inst *inst)
2180 {
2181     [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
2182     [[maybe_unused]] auto opType = op->GetType();
2183     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2184         v, DataType::GetCommonType(opType) == DataType::INT64,
2185         (std::cerr << "Type of ZeroCheck input must be integer\n", inst->Dump(&std::cerr)));
2186     CheckThrows(v, inst, {Opcode::Div, Opcode::DivI, Opcode::Mod, Opcode::ModI, Opcode::Phi});
2187 }
2188 
2189 void GraphChecker::VisitDeoptimizeIf([[maybe_unused]] GraphVisitor *v, Inst *inst)
2190 {
2191     [[maybe_unused]] auto op = inst->GetInput(0).GetInst();
2192     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2193         v, op->GetType() == DataType::BOOL || op->IsBoolConst(),
2194         (std::cerr << "Type of first input DeoptimizeIf must be BOOL:\n", inst->Dump(&std::cerr)));
2195     [[maybe_unused]] auto ss = inst->GetInput(1).GetInst();
2196     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2197         v, ss->GetOpcode() == Opcode::SaveStateDeoptimize || ss->GetOpcode() == Opcode::SaveState,
2198         (std::cerr << "Second input DeoptimizeIf must be SaveStateDeoptimize or SaveState:\n", inst->Dump(&std::cerr)));
2199 }
2200 
2201 void GraphChecker::VisitLenArray([[maybe_unused]] GraphVisitor *v, Inst *inst)
2202 {
2203     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetType() == DataType::INT32,
2204                                         (std::cerr << "Type of LenArray must be INT32:\n", inst->Dump(&std::cerr)));
2205     [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
2206     if (op->GetOpcode() == Opcode::NullCheck) {
2207         op = op->GetInput(0).GetInst();
2208     }
2209     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2210         v, DataType::IsReference(op->GetType()),
2211         (std::cerr << "Types of input LenArray must be REFERENCE:\n", inst->Dump(&std::cerr), op->Dump(&std::cerr)));
2212 }
2213 
2214 void GraphChecker::VisitCallVirtual([[maybe_unused]] GraphVisitor *v, Inst *inst)
2215 {
2216     auto graph = inst->GetBasicBlock()->GetGraph();
2217     // In AbcKit mode there are no SaveStates
2218     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetInputs().Size() > 0 || graph->IsAbcKit(),
2219                                         (std::cerr << "Virtual function must have inputs:\n", inst->Dump(&std::cerr)));
2220     if (graph->IsAbcKit()) {
2221         return;
2222     }
2223     [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
2224     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::IsReference(op->GetType()),
2225                                         (std::cerr << "Types of first input CallVirtual must be REFERENCE(this):\n",
2226                                          inst->Dump(&std::cerr), op->Dump(&std::cerr)));
2227 }
2228 
2229 void GraphChecker::VisitCallDynamic([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2230 {
2231     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2232         v, static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
2233         (std::cerr << "CallDynamic is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
2234 }
2235 
2236 void GraphChecker::VisitSaveState([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2237 {
2238     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, (static_cast<SaveStateInst *>(inst))->Verify(),
2239                                         std::cerr << "Inconsistent SaveState instruction:\n"
2240                                                   << *inst << std::endl);
2241 #ifndef NDEBUG
2242     auto ss = inst->CastToSaveState();
2243     if (ss->GetInputsWereDeleted()) {
2244         for (auto &user : inst->GetUsers()) {
2245             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2246                 v, !user.GetInst()->RequireRegMap(),
2247                 std::cerr << "Some inputs from save_state were deleted, but the user requireRegMap:\n"
2248                           << *inst << std::endl
2249                           << *(user.GetInst()) << std::endl);
2250         }
2251     }
2252     auto graph = inst->GetBasicBlock()->GetGraph();
2253     if (graph->IsDynamicMethod() && !graph->IsDynUnitTest() && !graph->IsBytecodeOptimizer()) {
2254 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
2255 #define VREG_ENV_TYPES(VREG_TYPE) VRegInfo::VRegType::VREG_TYPE,
2256         for (auto envType : {VREGS_ENV_TYPE_DEFS(VREG_ENV_TYPES)}) {
2257             bool founded = false;
2258             for (size_t i = 0; i < inst->GetInputsCount(); ++i) {
2259                 auto vreg = ss->GetVirtualRegister(i);
2260                 founded |= vreg.GetVRegType() == envType;
2261             }
2262             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, founded,
2263                                                 std::cerr << VRegInfo::VRegTypeToString(envType) << " not found");
2264         }
2265 #undef VREG_ENV_TYPES
2266     }
2267 
2268 #endif
2269 }
2270 
2271 void GraphChecker::VisitSafePoint([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2272 {
2273     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, !inst->HasUsers(),
2274                                         std::cerr << "SafePoint must not have users:\n"
2275                                                   << *inst << std::endl);
2276     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, (static_cast<SaveStateInst *>(inst))->Verify(),
2277                                         std::cerr << "Inconsistent SafePoint instruction:\n"
2278                                                   << *inst << std::endl);
2279 }
2280 
2281 void GraphChecker::VisitSaveStateOsr([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2282 {
2283     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, !inst->HasUsers(),
2284                                         std::cerr << "SafeStateOsr must not have users:\n"
2285                                                   << *inst << std::endl);
2286     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, (static_cast<SaveStateInst *>(inst))->Verify(),
2287                                         std::cerr << "Inconsistent SafeStateOsr instruction:\n"
2288                                                   << *inst << std::endl);
2289     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, static_cast<GraphChecker *>(v)->GetGraph()->IsOsrMode(),
2290                                         std::cerr << "SafeStateOsr must be created in the OSR mode only\n");
2291     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetBasicBlock()->IsOsrEntry(),
2292                                         std::cerr << "SafeStateOsr's basic block must be osr-entry\n");
2293     auto firstInst = inst->GetBasicBlock()->GetFirstInst();
2294     while (firstInst != nullptr && (firstInst->IsCatchPhi() || firstInst->GetOpcode() == Opcode::Try)) {
2295         firstInst = firstInst->GetNext();
2296     }
2297     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, firstInst == inst,
2298                                         std::cerr << "SafeStateOsr must be the first instruction in the basic block\n");
2299 }
2300 
2301 void GraphChecker::VisitThrow([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2302 {
2303     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::IsReference(inst->GetInput(0).GetInst()->GetType()),
2304                                         std::cerr << "Throw instruction must have input with reference type: " << *inst
2305                                                   << std::endl);
2306     [[maybe_unused]] auto bb = inst->GetBasicBlock();
2307     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2308         v, inst == bb->GetLastInst(),
2309         std::cerr << "Throw instruction must be last instruction in the basic block: " << *inst << std::endl);
2310     for ([[maybe_unused]] auto succ : bb->GetSuccsBlocks()) {
2311         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2312             v, succ->IsEndBlock() || succ->IsTryEnd() || succ->IsCatchBegin(),
2313             std::cerr << "Throw block must have end block or try-end or catch-begin block as successor\n");
2314     }
2315 }
2316 
2317 void GraphChecker::VisitCheckCast([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2318 {
2319     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2320         v, DataType::IsReference(inst->GetInput(0).GetInst()->GetType()),
2321         std::cerr << "CheckCast instruction must have input 0 with reference type: " << *inst << std::endl);
2322 
2323     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2324         v, DataType::IsReference(inst->GetInput(1).GetInst()->GetType()),
2325         std::cerr << "CheckCast instruction must have input 1 with reference type: " << *inst << std::endl);
2326 
2327     [[maybe_unused]] auto saveState = inst->GetInput(2).GetInst();
2328     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, saveState != nullptr,
2329                                         std::cerr << "CheckCast instruction must have SaveState as input 2: " << *inst
2330                                                   << std::endl);
2331     if (inst->CanDeoptimize()) {
2332         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2333             v, (saveState->GetOpcode() == Opcode::SaveState || saveState->GetOpcode() == Opcode::SaveStateDeoptimize),
2334             std::cerr << "CheckCast D instruction must have SaveState or SaveStateDeoptimize as input 2: " << *inst
2335                       << std::endl);
2336     } else {
2337         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2338             v, saveState->GetOpcode() == Opcode::SaveState,
2339             std::cerr << "CheckCast instruction must have SaveState as input 2: " << *inst << std::endl);
2340     }
2341 }
2342 
2343 void GraphChecker::VisitIsInstance([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2344 {
2345     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2346         v, DataType::IsReference(inst->GetInput(0).GetInst()->GetType()),
2347         std::cerr << "IsInstance instruction must have input 0 with reference type: " << *inst << std::endl);
2348     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2349         v, DataType::IsReference(inst->GetInput(1).GetInst()->GetType()),
2350         std::cerr << "IsInstance instruction must have input 1 with reference type: " << *inst << std::endl);
2351 
2352     [[maybe_unused]] auto saveState = inst->GetInput(2).GetInst();
2353     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, (saveState != nullptr && saveState->GetOpcode() == Opcode::SaveState),
2354                                         std::cerr << "IsInstance instruction must have SaveState as input 2: " << *inst
2355                                                   << std::endl);
2356 
2357     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetType() == DataType::BOOL,
2358                                         (std::cerr << "Types of IsInstance must be BOOL:\n", inst->Dump(&std::cerr)));
2359 }
2360 
2361 void GraphChecker::VisitSelectWithReference([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2362 {
2363     [[maybe_unused]] auto op1 = inst->GetInput(1).GetInst();
2364     [[maybe_unused]] auto op2 = inst->GetInput(2U).GetInst();
2365     [[maybe_unused]] auto op3 = inst->GetInput(3U).GetInst();
2366     [[maybe_unused]] auto cc = inst->CastToSelect()->GetCc();
2367 
2368     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2369         v, cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
2370         (std::cerr << "Select reference comparison must be CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
2371     if (op2->IsConst()) {
2372         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, IsZeroConstant(op2),
2373                                             (std::cerr << "Constant reference input must be integer 0: \n",
2374                                              inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
2375     } else {
2376         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, op2->GetType() == DataType::REFERENCE,
2377                                             (std::cerr << "Select instruction 3rd operand type is not a reference\n",
2378                                              inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
2379     }
2380     if (op3->IsConst()) {
2381         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, IsZeroConstant(op3),
2382                                             (std::cerr << "Constant reference input must be integer 0: \n",
2383                                              inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
2384     } else {
2385         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, op3->GetType() == DataType::REFERENCE,
2386                                             (std::cerr << "Select instruction 4th operand type is not a reference\n",
2387                                              inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
2388     }
2389 }
2390 
2391 void GraphChecker::VisitSelect([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2392 {
2393     [[maybe_unused]] auto op0 = inst->GetInput(0).GetInst();
2394     [[maybe_unused]] auto op1 = inst->GetInput(1).GetInst();
2395     [[maybe_unused]] auto op2 = inst->GetInput(2U).GetInst();
2396     [[maybe_unused]] auto op3 = inst->GetInput(3U).GetInst();
2397 
2398     for (size_t i = 0; i < inst->GetInputsCount(); i++) {
2399         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetInputType(i) != DataType::NO_TYPE,
2400                                             std::cerr << "Source operand type is not set: " << *inst << std::endl);
2401     }
2402 
2403     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2404         v,
2405         DataType::GetCommonType(inst->GetType()) == DataType::INT64 ||
2406             IsFloatType(DataType::GetCommonType(inst->GetType())) || inst->GetType() == DataType::REFERENCE ||
2407             inst->GetType() == DataType::ANY,
2408         (std::cerr << "Select instruction type is not integer or reference or any", inst->Dump(&std::cerr)));
2409     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2410         v,
2411         DataType::GetCommonType(op0->GetType()) == DataType::INT64 ||
2412             IsFloatType(DataType::GetCommonType(op0->GetType())) || op0->GetType() == DataType::REFERENCE ||
2413             op0->GetType() == DataType::ANY,
2414         (std::cerr << "Select instruction 1st operand type is not integer or reference or any",
2415          inst->Dump(&std::cerr)));
2416     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2417         v,
2418         DataType::GetCommonType(op1->GetType()) == DataType::INT64 ||
2419             IsFloatType(DataType::GetCommonType(op1->GetType())) || op1->GetType() == DataType::REFERENCE ||
2420             op1->GetType() == DataType::ANY,
2421         (std::cerr << "Select instruction 2nd operand type is not integer or reference or any",
2422          inst->Dump(&std::cerr)));
2423 
2424     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2425         v, CheckCommonTypes(op0, op1),
2426         (std::cerr << "Types of two first select instruction operands are not compatible\n", op0->Dump(&std::cerr),
2427          op1->Dump(&std::cerr), inst->Dump(&std::cerr)));
2428     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2429         v, CheckCommonTypes(inst, op0),
2430         (std::cerr << "Types of instruction result and its operands are not compatible\n", inst->Dump(&std::cerr)));
2431 
2432     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetInputType(2U) == inst->GetInputType(3U),
2433                                         std::cerr << "Select comparison arguments has different inputs type: " << *inst
2434                                                   << std::endl);
2435     if (inst->GetInputType(2U) == DataType::REFERENCE) {
2436         VisitSelectWithReference(v, inst);
2437     }
2438 }
2439 
2440 void GraphChecker::VisitSelectImmWithReference([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2441 {
2442     [[maybe_unused]] auto op1 = inst->GetInput(1).GetInst();
2443     [[maybe_unused]] auto op2 = inst->GetInput(2U).GetInst();
2444     [[maybe_unused]] auto op3 = inst->CastToSelectImm()->GetImm();
2445     [[maybe_unused]] auto cc = inst->CastToSelectImm()->GetCc();
2446 
2447     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2448         v, cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
2449         (std::cerr << "SelectImm reference comparison must be CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
2450     if (op2->IsConst()) {
2451         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, IsZeroConstant(op2),
2452                                             (std::cerr << "Constant reference input must be integer 0: \n",
2453                                              inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
2454     } else {
2455         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2456             v, op2->GetType() == DataType::REFERENCE,
2457             (std::cerr << "Condition with immediate jump 1st operand type is not a reference\n", inst->Dump(&std::cerr),
2458              op1->Dump(&std::cerr)));
2459     }
2460     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2461         v, op3 == 0, (std::cerr << "Reference can be compared only with 0 immediate: \n", inst->Dump(&std::cerr)));
2462 }
2463 
2464 void GraphChecker::VisitSelectImmNotReference([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2465 {
2466     [[maybe_unused]] auto op2 = inst->GetInput(2U).GetInst();
2467     [[maybe_unused]] bool isDynamic = static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod();
2468 
2469     CHECKER_MESSAGE_IF_NOT_AND_PRINT_VISITOR(
2470         v,
2471         DataType::GetCommonType(op2->GetType()) == DataType::INT64 ||
2472             (isDynamic && DataType::GetCommonType(op2->GetType()) == DataType::ANY),
2473         "SelectImm 3rd operand type is not an integer or any");
2474 
2475     if (DataType::GetCommonType(op2->GetType()) == DataType::ANY) {
2476         [[maybe_unused]] auto cc = inst->CastToSelectImm()->GetCc();
2477         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2478             v, cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
2479             (std::cerr << "SelectImm any comparison must be CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
2480     }
2481 }
2482 
2483 void GraphChecker::VisitSelectImm([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2484 {
2485     [[maybe_unused]] auto op0 = inst->GetInput(0).GetInst();
2486     [[maybe_unused]] auto op1 = inst->GetInput(1).GetInst();
2487     [[maybe_unused]] bool isDynamic = static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod();
2488 
2489     for (size_t i = 0; i < inst->GetInputsCount(); i++) {
2490         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetInputType(i) != DataType::NO_TYPE,
2491                                             std::cerr << "Source operand type is not set: " << *inst << std::endl);
2492     }
2493 
2494     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2495         v,
2496         DataType::GetCommonType(inst->GetType()) == DataType::INT64 || inst->GetType() == DataType::REFERENCE ||
2497             IsFloatType(DataType::GetCommonType(inst->GetType())) || (isDynamic && inst->GetType() == DataType::ANY),
2498         (std::cerr << "SelectImm instruction type is not integer or reference or any", inst->Dump(&std::cerr)));
2499     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2500         v,
2501         DataType::GetCommonType(op0->GetType()) == DataType::INT64 ||
2502             IsFloatType(DataType::GetCommonType(op0->GetType())) || op0->GetType() == DataType::REFERENCE ||
2503             (isDynamic && op0->GetType() == DataType::ANY),
2504         (std::cerr << "SelectImm instruction 1st operand type is not integer or reference or any",
2505          inst->Dump(&std::cerr)));
2506     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2507         v,
2508         DataType::GetCommonType(op1->GetType()) == DataType::INT64 ||
2509             IsFloatType(DataType::GetCommonType(op1->GetType())) || op1->GetType() == DataType::REFERENCE ||
2510             (isDynamic && op1->GetType() == DataType::ANY),
2511         (std::cerr << "SelectImm instruction 2nd operand type is not integer or reference or any",
2512          inst->Dump(&std::cerr)));
2513 
2514     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2515         v, CheckCommonTypes(op0, op1),
2516         (std::cerr << "Types of two first SelectImm instruction operands are not compatible\n", op0->Dump(&std::cerr),
2517          op1->Dump(&std::cerr), inst->Dump(&std::cerr)));
2518     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2519         v, CheckCommonTypes(inst, op0),
2520         (std::cerr << "Types of instruction result and its operands are not compatible\n", inst->Dump(&std::cerr)));
2521 
2522     if (inst->GetInputType(2U) == DataType::REFERENCE) {
2523         VisitSelectImmWithReference(v, inst);
2524     } else {
2525         VisitSelectImmNotReference(v, inst);
2526     }
2527 }
2528 
2529 void GraphChecker::VisitIf(GraphVisitor *v, Inst *inst)
2530 {
2531     CheckContrlFlowInst(v, inst);
2532     [[maybe_unused]] auto numSuccs = inst->GetBasicBlock()->GetSuccsBlocks().size();
2533     CHECKER_MESSAGE_IF_NOT_AND_PRINT_VISITOR(v, numSuccs == MAX_SUCCS_NUM,
2534                                              "Basic block with If must have 2 successesors");
2535 
2536     [[maybe_unused]] auto op1 = inst->GetInputs()[0].GetInst();
2537     [[maybe_unused]] auto op2 = inst->GetInputs()[1].GetInst();
2538     for (size_t i = 0; i < inst->GetInputsCount(); i++) {
2539         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetInputType(i) != DataType::NO_TYPE,
2540                                             std::cerr << "Source operand type is not set: " << *inst << std::endl);
2541     }
2542     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetInputType(0) == inst->GetInputType(1),
2543                                         std::cerr << "If has different inputs type: " << *inst << std::endl);
2544     if (inst->GetInputType(0) == DataType::REFERENCE) {
2545         [[maybe_unused]] auto cc = inst->CastToIf()->GetCc();
2546         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2547             v, cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
2548             (std::cerr << "Reference comparison in If must be CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
2549         if (op1->IsConst()) {
2550             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, IsZeroConstant(op1),
2551                                                 (std::cerr << "Constant reference input must be integer 0: \n",
2552                                                  inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
2553         } else {
2554             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, op1->GetType() == DataType::REFERENCE,
2555                                                 (std::cerr << "If 1st operand type is not a reference\n",
2556                                                  inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
2557         }
2558         if (op2->IsConst()) {
2559             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, IsZeroConstant(op2),
2560                                                 (std::cerr << "Constant reference input must be integer 0: \n",
2561                                                  inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
2562         } else {
2563             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, op2->GetType() == DataType::REFERENCE,
2564                                                 (std::cerr << "If 2nd operand type is not a reference\n",
2565                                                  inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
2566         }
2567     }
2568 }
2569 
2570 void GraphChecker::VisitIfImm(GraphVisitor *v, Inst *inst)
2571 {
2572     CheckContrlFlowInst(v, inst);
2573     [[maybe_unused]] auto numSuccs = inst->GetBasicBlock()->GetSuccsBlocks().size();
2574     CHECKER_MESSAGE_IF_NOT_AND_PRINT_VISITOR(v, numSuccs == MAX_SUCCS_NUM,
2575                                              "Basic block with IfImm must have 2 successesors");
2576 
2577     [[maybe_unused]] auto op1 = inst->GetInput(0).GetInst();
2578     [[maybe_unused]] auto op2 = inst->CastToIfImm()->GetImm();
2579     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, inst->GetInputType(0) != DataType::NO_TYPE,
2580                                         std::cerr << "Source operand type is not set: " << *inst << std::endl);
2581     if (inst->GetInputType(0) == DataType::REFERENCE) {
2582         [[maybe_unused]] auto cc = inst->CastToIfImm()->GetCc();
2583         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2584             v, cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
2585             (std::cerr << "Reference comparison in IfImm must have CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
2586         if (op1->IsConst()) {
2587             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, IsZeroConstant(op1),
2588                                                 (std::cerr << "Constant reference input must be integer 0: \n",
2589                                                  inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
2590         } else {
2591             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, op1->GetType() == DataType::REFERENCE,
2592                                                 (std::cerr << "IfImm operand type should be here a reference: \n",
2593                                                  inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
2594         }
2595         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2596             v, op2 == 0, (std::cerr << "Reference can be compared only with 0 immediate: \n", inst->Dump(&std::cerr)));
2597     } else {
2598         CHECKER_MESSAGE_IF_NOT_AND_PRINT_VISITOR(
2599             v,
2600             DataType::GetCommonType(op1->GetType()) == DataType::INT64 ||
2601                 (static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod() && op1->GetType() == DataType::ANY),
2602             "IfImm operand type should be here an integer");
2603     }
2604 }
2605 
2606 void GraphChecker::VisitTry([[maybe_unused]] GraphVisitor *v, Inst *inst)
2607 {
2608     [[maybe_unused]] auto bb = inst->GetBasicBlock();
2609     CHECKER_MESSAGE_IF_NOT_AND_PRINT_VISITOR(v, bb->IsTryBegin(),
2610                                              "TryInst should be placed in the try-begin basic block");
2611 }
2612 
2613 void GraphChecker::VisitNOP([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2614 {
2615     CHECKER_MESSAGE_IF_NOT_AND_PRINT_VISITOR(v, inst->GetUsers().Empty(), "NOP can not have users\n");
2616 }
2617 
2618 void GraphChecker::VisitAndNot([[maybe_unused]] GraphVisitor *v, Inst *inst)
2619 {
2620     CheckBinaryOperationTypes(v, inst, true);
2621 }
2622 void GraphChecker::VisitOrNot([[maybe_unused]] GraphVisitor *v, Inst *inst)
2623 {
2624     CheckBinaryOperationTypes(v, inst, true);
2625 }
2626 void GraphChecker::VisitXorNot([[maybe_unused]] GraphVisitor *v, Inst *inst)
2627 {
2628     CheckBinaryOperationTypes(v, inst, true);
2629 }
2630 void GraphChecker::VisitMNeg([[maybe_unused]] GraphVisitor *v, Inst *inst)
2631 {
2632     CheckBinaryOperationTypes(v, inst, false);
2633 }
2634 void GraphChecker::VisitMAdd(GraphVisitor *v, Inst *inst)
2635 {
2636     CheckTernaryOperationTypes(v, inst);
2637 }
2638 void GraphChecker::VisitMSub(GraphVisitor *v, Inst *inst)
2639 {
2640     CheckTernaryOperationTypes(v, inst);
2641 }
2642 
2643 void GraphChecker::VisitCompareAnyType([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2644 {
2645     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2646         v, static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
2647         (std::cerr << "CompareAnyType is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
2648     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2649         v, inst->CastToCompareAnyType()->GetAllowedInputType() == profiling::AnyInputType::DEFAULT,
2650         (std::cerr << "CompareAnyType doesn't support special values or treating int as double\n",
2651          inst->Dump(&std::cerr)));
2652 }
2653 
2654 void GraphChecker::VisitCastAnyTypeValue([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2655 {
2656     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2657         v, static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
2658         (std::cerr << "CastAnyTypeValue is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
2659     if ((inst->CastToCastAnyTypeValue()->GetAllowedInputType() & profiling::AnyInputType::INTEGER) != 0) {
2660         [[maybe_unused]] auto type = AnyBaseTypeToDataType(inst->CastToCastAnyTypeValue()->GetAnyType());
2661         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2662             v, type == DataType::FLOAT64,
2663             (std::cerr << "Target type of CastAnyTypeValue with Integer allowed must be FLOAT64\n",
2664              inst->Dump(&std::cerr)));
2665     } else if (inst->CastToCastAnyTypeValue()->GetAllowedInputType() != profiling::AnyInputType::DEFAULT) {
2666         [[maybe_unused]] auto type = AnyBaseTypeToDataType(inst->CastToCastAnyTypeValue()->GetAnyType());
2667         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2668             v, type == DataType::FLOAT64 || type == DataType::INT32,
2669             (std::cerr << "Target type of CastAnyTypeValue with special value allowed must be FLOAT64 or INT32\n",
2670              inst->Dump(&std::cerr)));
2671     }
2672 }
2673 
2674 void GraphChecker::VisitCastValueToAnyType([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2675 {
2676     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2677         v, static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
2678         (std::cerr << "CastValueToAnyType is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
2679 
2680     const auto *inputInst = inst->GetInput(0).GetInst();
2681     auto inputType = inst->GetInputType(0);
2682     auto outputType = AnyBaseTypeToDataType(inst->CastToCastValueToAnyType()->GetAnyType());
2683 
2684     // CHECKER_DO_IF_NOT_AND_PRINT(input_type != DataType::ANY, // NOTE(vpukhov): AnyConst
2685     //          (std::cerr << "CastValueToAnyType cannot accept inputs of ANY type:\n", inst->Dump(&std::cerr)));
2686 
2687     if (inputInst->IsConst() && (inputType == DataType::Type::INT64 || inputType == DataType::Type::INT32)) {
2688         if (outputType == DataType::Type::BOOL) {
2689             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2690                 v, inputInst->IsBoolConst(),
2691                 (std::cerr << "Integral constant input not coercible to BOOL:\n", inst->Dump(&std::cerr)));
2692             return;
2693         }
2694 
2695         if (outputType == DataType::INT32 && inputType == DataType::INT64) {
2696             [[maybe_unused]] auto value = static_cast<int64_t>(inputInst->CastToConstant()->GetInt64Value());
2697             CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2698                 v, value == static_cast<int32_t>(value),
2699                 (std::cerr << "Integral constant input not coercible to INT32:\n", inst->Dump(&std::cerr)));
2700             return;
2701         }
2702 
2703         if (outputType == DataType::Type::REFERENCE) {
2704             return;  // Always coercible
2705         }
2706 
2707         if (outputType == DataType::Type::VOID) {
2708             return;  // Always coercible
2709         }
2710 
2711         // Otherwise proceed with the generic check.
2712     }
2713 }
2714 
2715 void GraphChecker::VisitAnyTypeCheck([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2716 {
2717     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2718         v, static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
2719         (std::cerr << "AnyTypeCheck is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
2720     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2721         v, inst->GetInput(0).GetInst()->GetType() == DataType::Type::ANY,
2722         (std::cerr << "First input in AnyTypeCheck must be Any type:\n", inst->Dump(&std::cerr)));
2723     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2724         v, inst->GetInput(1).GetInst()->IsSaveState(),
2725         (std::cerr << "Second input in AnyTypeCheck must be SaveState:\n", inst->Dump(&std::cerr)));
2726     if ((inst->CastToAnyTypeCheck()->GetAllowedInputType() & profiling::AnyInputType::INTEGER) != 0) {
2727         [[maybe_unused]] auto type = AnyBaseTypeToDataType(inst->CastToAnyTypeCheck()->GetAnyType());
2728         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2729             v, type == DataType::FLOAT64,
2730             (std::cerr << "Target type of AnyTypeCheck with Integer allowed must be FLOAT64\n",
2731              inst->Dump(&std::cerr)));
2732     } else if (inst->CastToAnyTypeCheck()->GetAllowedInputType() != profiling::AnyInputType::DEFAULT) {
2733         [[maybe_unused]] auto type = AnyBaseTypeToDataType(inst->CastToAnyTypeCheck()->GetAnyType());
2734         CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2735             v, type == DataType::FLOAT64 || type == DataType::INT32,
2736             (std::cerr << "Target type of AnyTypeCheck with special value allowed must be FLOAT64 or INT32\n",
2737              inst->Dump(&std::cerr)));
2738     }
2739 }
2740 
2741 void GraphChecker::VisitHclassCheck([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2742 {
2743     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2744         v, static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
2745         (std::cerr << "HclassCheck is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
2746     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2747         v, inst->GetInput(0).GetInst()->GetType() == DataType::Type::REFERENCE,
2748         (std::cerr << "First input in HclassCheck must be Ref type:\n", inst->Dump(&std::cerr)));
2749     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2750         v,
2751         (inst->GetInput(0).GetInst()->GetOpcode() == Opcode::LoadObject &&
2752          inst->GetInput(0).GetInst()->CastToLoadObject()->GetObjectType() == ObjectType::MEM_DYN_HCLASS),
2753         (std::cerr << "First input in HclassCheck must be LoadObject Hclass:\n", inst->Dump(&std::cerr)));
2754     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2755         v, inst->GetInput(1).GetInst()->IsSaveState(),
2756         (std::cerr << "Second input in HclassCheck must be SaveState:\n", inst->Dump(&std::cerr)));
2757     [[maybe_unused]] auto hclassCheck = inst->CastToHclassCheck();
2758     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2759         v, hclassCheck->GetCheckIsFunction() || hclassCheck->GetCheckFunctionIsNotClassConstructor(),
2760         (std::cerr << "HclassCheck must have at least one check: IsFunction or FunctionIsNotClassConstructor\n",
2761          inst->Dump(&std::cerr)));
2762 }
2763 
2764 void GraphChecker::VisitBitcast([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2765 {
2766     [[maybe_unused]] auto arch = static_cast<GraphChecker *>(v)->GetGraph()->GetArch();
2767     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(
2768         v,
2769         DataType::GetTypeSize(inst->GetInput(0).GetInst()->GetType(), arch) ==
2770             DataType::GetTypeSize(inst->GetType(), arch),
2771         (std::cerr << "Size of input and output types must be equal\n", inst->Dump(&std::cerr)));
2772 }
2773 
2774 void GraphChecker::VisitLoadString([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2775 {
2776     CHECKER_IF_NOT_PRINT_VISITOR(v, !static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod() ||
2777                                         static_cast<GraphChecker *>(v)->GetGraph()->IsBytecodeOptimizer());
2778 }
2779 
2780 void GraphChecker::VisitLoadType([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2781 {
2782     CHECKER_IF_NOT_PRINT_VISITOR(v, !static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod());
2783 }
2784 
2785 void GraphChecker::VisitLoadUnresolvedType([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2786 {
2787     CHECKER_IF_NOT_PRINT_VISITOR(v, !static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod());
2788 }
2789 
2790 void GraphChecker::VisitLoadFromConstantPool([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2791 {
2792     CHECKER_IF_NOT_PRINT_VISITOR(v, static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod());
2793 }
2794 
2795 void GraphChecker::VisitLoadImmediate([[maybe_unused]] GraphVisitor *v, Inst *inst)
2796 {
2797     auto loadImm = inst->CastToLoadImmediate();
2798     [[maybe_unused]] auto type = inst->GetType();
2799     [[maybe_unused]] auto objType = loadImm->GetObjectType();
2800     CHECKER_IF_NOT_PRINT_VISITOR(v, objType != LoadImmediateInst::ObjectType::UNKNOWN);
2801     CHECKER_IF_NOT_PRINT_VISITOR(
2802         v, (type == DataType::REFERENCE &&
2803             (objType == LoadImmediateInst::ObjectType::CLASS || objType == LoadImmediateInst::ObjectType::OBJECT)) ||
2804                (type == DataType::POINTER &&
2805                 (objType == LoadImmediateInst::ObjectType::METHOD || objType == LoadImmediateInst::ObjectType::STRING ||
2806                  objType == LoadImmediateInst::ObjectType::PANDA_FILE_OFFSET || loadImm->IsTlsOffset())) ||
2807                (type == DataType::ANY && objType == LoadImmediateInst::ObjectType::CONSTANT_POOL));
2808     CHECKER_IF_NOT_PRINT_VISITOR(v, objType != LoadImmediateInst::ObjectType::PANDA_FILE_OFFSET ||
2809                                         static_cast<GraphChecker *>(v)->GetGraph()->IsAotMode());
2810     CHECKER_IF_NOT_PRINT_VISITOR(v, loadImm->GetObject() != nullptr);
2811 }
2812 
2813 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
2814 #define VISIT_BINARY_SHIFTED_REGISTER(opc)                                                      \
2815     void GraphChecker::Visit##opc(GraphVisitor *v, Inst *inst)                                  \
2816     {                                                                                           \
2817         CheckBinaryOperationWithShiftedOperandTypes(                                            \
2818             v, inst, inst->GetOpcode() != Opcode::AddSR && inst->GetOpcode() != Opcode::SubSR); \
2819     }
2820 
2821 VISIT_BINARY_SHIFTED_REGISTER(AddSR)
2822 VISIT_BINARY_SHIFTED_REGISTER(SubSR)
2823 VISIT_BINARY_SHIFTED_REGISTER(AndSR)
2824 VISIT_BINARY_SHIFTED_REGISTER(OrSR)
2825 VISIT_BINARY_SHIFTED_REGISTER(XorSR)
2826 VISIT_BINARY_SHIFTED_REGISTER(AndNotSR)
2827 VISIT_BINARY_SHIFTED_REGISTER(OrNotSR)
2828 VISIT_BINARY_SHIFTED_REGISTER(XorNotSR)
2829 #undef VISIT_BINARY_SHIFTED_REGISTER
2830 
2831 void GraphChecker::VisitNegSR(GraphVisitor *v, [[maybe_unused]] Inst *inst)
2832 {
2833     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, DataType::GetCommonType(inst->GetType()) == DataType::INT64,
2834                                         (std::cerr << "NegSR must have integer type\n", inst->Dump(&std::cerr)));
2835     CheckUnaryOperationTypes(v, inst);
2836     [[maybe_unused]] auto shiftType = static_cast<UnaryShiftedRegisterOperation *>(inst)->GetShiftType();
2837     CHECKER_DO_IF_NOT_AND_PRINT_VISITOR(v, shiftType != ShiftType::INVALID_SHIFT && shiftType != ShiftType::ROR,
2838                                         (std::cerr << "Operation has invalid shift type\n", inst->Dump(&std::cerr)));
2839 }
2840 
2841 }  // namespace ark::compiler
2842