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