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