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