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