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