1 /**
2 * Copyright (c) 2021-2022 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_cloner.h"
18 #include "compiler_options.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/datatype.h"
24 #include "optimizer/optimizations/cleanup.h"
25 #include "inst_checker_gen.h"
26 #include "graph_checker.h"
27
28 namespace panda::compiler {
29
GraphChecker(Graph * graph)30 GraphChecker::GraphChecker(Graph *graph)
31 {
32 PreCloneChecks(graph);
33 graph_ = GraphCloner(graph, GetAllocator(), GetLocalAllocator()).CloneGraph();
34 GetGraph()->GetPassManager()->SetCheckMode(true);
35 }
36
PreCloneChecks(Graph * graph)37 void GraphChecker::PreCloneChecks(Graph *graph)
38 {
39 UserInputCheck(graph);
40 }
41
UserInputCheck(Graph * graph)42 void GraphChecker::UserInputCheck(Graph *graph)
43 {
44 for (auto block : graph->GetVectorBlocks()) {
45 if (block == nullptr) {
46 continue;
47 }
48 for (auto inst : block->AllInsts()) {
49 auto u = inst->GetFirstUser();
50 ASSERT(u == nullptr || u->GetPrev() == nullptr);
51 while (u != nullptr) {
52 ASSERT(u->GetNext() == nullptr || u->GetNext()->GetPrev() == u);
53 u = u->GetNext();
54 }
55 for (auto &user : inst->GetUsers()) {
56 [[maybe_unused]] auto user_inst = user.GetInst();
57 ASSERT(user_inst->GetBasicBlock() != nullptr);
58 ASSERT_DO(CheckInstHasInput(user_inst, inst), std::cerr << "Instruction is not an input to its user\n"
59 << "input: " << *inst << std::endl
60 << "user: " << *user_inst << std::endl);
61 }
62 for (auto &input : inst->GetInputs()) {
63 [[maybe_unused]] auto input_inst = input.GetInst();
64 ASSERT(input_inst != nullptr && input_inst->GetBasicBlock() != nullptr);
65 ASSERT_DO(CheckInstHasUser(input_inst, inst), std::cerr << "Instruction is not a user to its input:\n"
66 << "user: " << *inst << std::endl
67 << "input: " << *input_inst << std::endl);
68 }
69 // Check `require_state` flag
70 auto it = std::find_if(inst->GetInputs().begin(), inst->GetInputs().end(),
71 [](Input input) { return input.GetInst()->IsSaveState(); });
72 [[maybe_unused]] bool has_save_state = (it != inst->GetInputs().end());
73 ASSERT_DO(inst->RequireState() == has_save_state,
74 std::cerr << "Incorrect 'require_state' flag in the inst: " << *inst);
75 if (inst->RequireState()) {
76 ASSERT(it->GetInst() == inst->GetSaveState());
77 }
78 }
79 }
80 }
81
Check()82 void GraphChecker::Check()
83 {
84 if (!GetGraph()->IsDynamicMethod()) {
85 InstChecker::Run(GetGraph());
86 }
87
88 #ifndef NDEBUG
89 if (GetGraph()->IsAnalysisValid<DominatorsTree>()) {
90 CheckDomTree();
91 } else {
92 GetGraph()->RunPass<DominatorsTree>();
93 }
94 if (GetGraph()->IsAnalysisValid<LoopAnalyzer>()) {
95 CheckLoopAnalysis();
96 } else {
97 GetGraph()->RunPass<LoopAnalyzer>();
98 }
99 CheckStartBlock();
100 CheckEndBlock();
101 size_t blocks_count = 0;
102 size_t blocks_id = -1;
103 for (auto block : GetGraph()->GetVectorBlocks()) {
104 ++blocks_id;
105 if (block == nullptr) {
106 continue;
107 }
108 ASSERT_PRINT(block->GetGraph() == GetGraph(), "Block linked to incorrect graph");
109 ASSERT_PRINT(block->GetId() == blocks_id, "Block ID must be equal to its ID in graph vector");
110 CheckBlock(block);
111 blocks_count++;
112 }
113 ASSERT_PRINT(blocks_count == GetGraph()->GetBlocksRPO().size(), "There is disconnected block");
114 CheckLoops();
115 // Visit graph to check instructions types
116 CheckGraph();
117 // Check that call.Inlined and Return.Inlined in correct order
118 // and check that savestate has correct link to call.inlined.
119 CheckCallReturnInlined();
120 if (NeedCheckSaveState()) {
121 // Check that objects in stack.
122 CheckSaveStateInputs();
123 // Check that between savestate and it's runtime call user have not reference insts.
124 CheckSaveStatesWithRuntimeCallUsers();
125 }
126
127 #endif // !NDEBUG
128 }
129
130 #ifndef NDEBUG
NeedCheckSaveState()131 bool GraphChecker::NeedCheckSaveState()
132 {
133 return !GetGraph()->IsBytecodeOptimizer() && GetGraph()->GetParentGraph() == nullptr &&
134 GetGraph()->IsInliningComplete() && !GetGraph()->IsSchedulerComplete();
135 }
136 #endif // !NDEBUG
137
CheckBlock(BasicBlock * block)138 void GraphChecker::CheckBlock([[maybe_unused]] BasicBlock *block)
139 {
140 #ifndef NDEBUG
141 CheckControlFlow(block);
142 CheckDataFlow(block);
143 for (auto phi_inst : block->PhiInsts()) {
144 CheckPhiInputs(phi_inst);
145 }
146 if (!GetGraph()->IsLowLevelInstructionsEnabled() && !GetGraph()->IsDynamicMethod()) {
147 CheckNoLowLevel(block);
148 }
149 if (!block->IsEndBlock() && !block->IsStartBlock()) {
150 CheckBlockEdges(*block);
151 }
152 if (block->IsTryBegin()) {
153 CheckTryBeginBlock(*block);
154 }
155 if (block->NeedsJump()) {
156 CheckJump(*block);
157 }
158 #endif // !NDEBUG
159 }
160
CheckControlFlow(BasicBlock * block)161 void GraphChecker::CheckControlFlow(BasicBlock *block)
162 {
163 auto num_succs = block->GetSuccsBlocks().size();
164 ASSERT_PRINT(block->IsEndBlock() || block->IsTryBegin() || block->IsTryEnd() ||
165 (num_succs > 0 && num_succs <= MAX_SUCCS_NUM) ||
166 block->GetLastInst()->GetOpcode() == Opcode::Throw,
167 "Non-end block and non-try-begin block should have 1 or 2 successesors");
168
169 for ([[maybe_unused]] auto pred : block->GetPredsBlocks()) {
170 ASSERT_PRINT(CheckBlockHasSuccessor(pred, block), "Block is not a successor to its predecessor");
171 }
172 for ([[maybe_unused]] auto succ : block->GetSuccsBlocks()) {
173 ASSERT_PRINT(CheckBlockHasPredecessor(succ, block), "Block is not a predecessor to its successor");
174 }
175
176 if (num_succs == MAX_SUCCS_NUM) {
177 ASSERT_PRINT(block->GetSuccessor(0) != block->GetSuccessor(1), "Wrong CFG - block with two same successors");
178 }
179
180 for ([[maybe_unused]] auto phi : block->PhiInsts()) {
181 ASSERT_DO(phi->GetInputsCount() == block->GetPredsBlocks().size(),
182 std::cerr << phi->GetInputsCount() << " " << block->GetPredsBlocks().size()
183 << "Incorrect phi's inputs count" << *phi);
184 }
185 }
186
CheckDataFlow(BasicBlock * block)187 void GraphChecker::CheckDataFlow(BasicBlock *block)
188 {
189 auto graph = block->GetGraph();
190 for (auto inst : block->AllInsts()) {
191 ASSERT_DO(inst->GetBasicBlock() == block,
192 std::cerr << "Instruction block's pointer isn't correct" << *inst << std::endl);
193 if (block != graph->GetStartBlock()) {
194 ASSERT_DO(inst->GetOpcode() != Opcode::Parameter,
195 std::cerr << "Not entry block can't contain Parameter instructions" << *inst << std::endl);
196 }
197 if (inst->GetPrev() == nullptr) {
198 ASSERT_PRINT(*block->AllInsts().begin() == inst, "First block instruction isn't correct");
199 }
200 if (inst->GetNext() == nullptr) {
201 ASSERT_PRINT(*block->AllInstsSafeReverse().begin() == inst, "Last block instruction isn't correct");
202 }
203 auto opc = inst->GetOpcode();
204 // Inst with reference type must have no_cse and no_hoist flags.
205 if (inst->GetType() == DataType::REFERENCE && !inst->IsClassInst() && opc != Opcode::GetInstanceClass) {
206 ASSERT(inst->IsNotCseApplicable());
207 ASSERT(inst->IsNotHoistable());
208 }
209 for ([[maybe_unused]] auto &user : inst->GetUsers()) {
210 auto user_inst = user.GetInst();
211 ASSERT_DO(CheckInstHasInput(user_inst, inst), std::cerr << "Instruction is not an input to its user\n"
212 << "input: " << *inst << std::endl
213 << "user: " << *user_inst << std::endl);
214 if (!user_inst->IsPhi() && !user_inst->IsCatchPhi()) {
215 ASSERT_DO(inst->IsDominate(user_inst) ||
216 (GetGraph()->IsRegAllocApplied() &&
217 IsTryCatchDomination(inst->GetBasicBlock(), user_inst->GetBasicBlock())),
218 std::cerr << "Instruction doesn't dominate its user\n"
219 << "input: " << *inst << std::endl
220 << "user: " << *user_inst << std::endl);
221 }
222 auto arch = graph->GetArch();
223 if (DataType::Is32Bits(inst->GetType(), arch)) {
224 if (!user_inst->HasType()) {
225 continue;
226 }
227 // Unsigned Load in AARCH64 zerod all high bits
228 #ifndef NDEBUG
229 if (inst->IsLoad() && !DataType::IsTypeSigned(inst->GetType()) && arch == Arch::AARCH64 &&
230 graph->IsLowLevelInstructionsEnabled()) {
231 #else
232 if (inst->IsLoad() && !DataType::IsTypeSigned(inst->GetType()) && arch == Arch::AARCH64) {
233 #endif // !NDEBUG
234 continue;
235 }
236 [[maybe_unused]] auto user_input_type = user_inst->GetInputType(user.GetIndex());
237 [[maybe_unused]] bool ref_to_ptr =
238 user_input_type == DataType::POINTER && inst->GetType() == DataType::REFERENCE;
239 ASSERT_DO(DataType::Is32Bits(user_input_type, arch) || ref_to_ptr ||
240 (block->GetGraph()->IsDynamicMethod() && user_input_type == DataType::ANY),
241 std::cerr << "Undefined high-part of input instruction for its user\n"
242 << "input: " << *inst << std::endl
243 << "user: " << *user_inst << std::endl);
244 }
245 }
246 for ([[maybe_unused]] auto input : inst->GetInputs()) {
247 ASSERT_DO(CheckInstHasUser(input.GetInst(), inst), std::cerr << "Instruction is not a user to its input:\n"
248 << "input: " << *input.GetInst() << std::endl
249 << "user: " << *inst << std::endl);
250 }
251 }
252 }
253
254 void GraphChecker::CheckCallReturnInlined()
255 {
256 ArenaStack<Inst *> inlined_calles(GetLocalAllocator()->Adapter());
257 for (auto block : GetGraph()->GetBlocksRPO()) {
258 for (auto inst : block->Insts()) {
259 if (inst->IsCall() && static_cast<CallInst *>(inst)->IsInlined()) {
260 ASSERT_PRINT(inst->NoDest(), "Inlined call should have NO_DST flag");
261 inlined_calles.push(inst);
262 } else if (inst->GetOpcode() == Opcode::ReturnInlined) {
263 // TODO(Sergey Chernykh) fix checker
264 if (block->GetLastInst()->GetOpcode() == Opcode::Throw ||
265 block->GetLastInst()->GetOpcode() == Opcode::Deoptimize) {
266 continue;
267 }
268 ASSERT(!inlined_calles.empty());
269 inlined_calles.pop();
270 }
271 }
272 }
273 [[maybe_unused]] bool throw_exit = false;
274 if (GetGraph()->HasEndBlock()) {
275 for (auto block : GetGraph()->GetEndBlock()->GetPredsBlocks()) {
276 if (block->IsTryEnd()) {
277 continue;
278 }
279 if (block->GetLastInst()->GetOpcode() == Opcode::Throw ||
280 block->GetLastInst()->GetOpcode() == Opcode::Deoptimize) {
281 throw_exit = true;
282 break;
283 }
284 }
285 }
286 ASSERT(inlined_calles.empty() || throw_exit);
287 #ifndef NDEBUG
288 // avoid check after ir_builder in inline pass
289 if (!GetGraph()->IsInliningComplete() || GetGraph()->GetParentGraph() != nullptr) {
290 return;
291 }
292 for (auto block : GetGraph()->GetBlocksRPO()) {
293 for (auto inst : block->Insts()) {
294 if (inst->IsSaveState()) {
295 CheckSaveStateCaller(static_cast<SaveStateInst *>(inst));
296 }
297 }
298 }
299 #endif
300 }
301
302 void GraphChecker::CheckSaveStateCaller(SaveStateInst *savestate)
303 {
304 ASSERT(savestate != nullptr);
305 auto block = savestate->GetBasicBlock();
306 ArenaStack<Inst *> inlined_calles(GetLocalAllocator()->Adapter());
307 auto caller = savestate->GetCallerInst();
308 if (caller == nullptr) {
309 return;
310 }
311 ASSERT(caller->GetBasicBlock() != nullptr);
312 ASSERT(caller->GetBasicBlock()->GetGraph() == block->GetGraph());
313 auto dom_block = block;
314 bool skip = true;
315 for (auto inst : dom_block->InstsSafeReverse()) {
316 if (inst == savestate) {
317 skip = false;
318 }
319 if (skip) {
320 continue;
321 }
322 if (inst->GetOpcode() == Opcode::ReturnInlined) {
323 inlined_calles.push(inst);
324 } else if (inst->IsCall() && static_cast<CallInst *>(inst)->IsInlined()) {
325 if (!inlined_calles.empty()) {
326 inlined_calles.pop();
327 } else {
328 ASSERT(caller == inst);
329 return;
330 }
331 }
332 }
333 dom_block = dom_block->GetDominator();
334 while (dom_block != nullptr) {
335 for (auto inst : dom_block->InstsSafeReverse()) {
336 if (inst->GetOpcode() == Opcode::ReturnInlined) {
337 inlined_calles.push(inst);
338 } else if (inst->IsCall() && static_cast<CallInst *>(inst)->IsInlined()) {
339 if (!inlined_calles.empty()) {
340 inlined_calles.pop();
341 } else {
342 ASSERT(caller == inst);
343 return;
344 }
345 }
346 }
347 dom_block = dom_block->GetDominator();
348 }
349 UNREACHABLE();
350 }
351
352 void GraphChecker::CheckStartBlock()
353 {
354 [[maybe_unused]] Inst *has_nullptr = nullptr;
355 [[maybe_unused]] int32_t last_num = -1;
356 ASSERT(GetGraph()->GetStartBlock());
357 ASSERT_PRINT(GetGraph()->GetStartBlock()->GetPredsBlocks().empty(), "Start block can't have predecessors");
358 ASSERT_PRINT(GetGraph()->GetStartBlock()->GetSuccsBlocks().size() == 1, "Start block should have one successor");
359 for (auto inst : GetGraph()->GetStartBlock()->AllInsts()) {
360 [[maybe_unused]] Opcode opc = inst->GetOpcode();
361 ASSERT_DO(
362 opc == Opcode::Constant || opc == Opcode::Parameter || opc == Opcode::SafePoint ||
363 opc == Opcode::SpillFill || opc == Opcode::NullPtr || opc == Opcode::NOP || opc == Opcode::LiveIn,
364 std::cerr
365 << "Entry block can contain Constant, Parameter, NullPtr, SafePoint, NOP or SpillFill instructions"
366 << *inst << std::endl);
367 if (opc == Opcode::Parameter) {
368 auto arg_num = inst->CastToParameter()->GetArgNumber();
369 ASSERT_DO(
370 last_num < static_cast<int32_t>(arg_num),
371 std::cerr << "The argument number in the parameter must be greater than that of the previous parameter"
372 << *inst << std::endl);
373 last_num = static_cast<int32_t>(arg_num);
374 }
375 if (opc == Opcode::NullPtr) {
376 ASSERT_PRINT(has_nullptr == nullptr, "There should be not more than one NullPtr instruction");
377 has_nullptr = inst;
378 }
379 }
380 }
381
382 void GraphChecker::CheckEndBlock()
383 {
384 if (!GetGraph()->HasEndBlock()) {
385 ASSERT_PRINT(HasOuterInfiniteLoop(), "Graph without infinite loops should have end block");
386 return;
387 }
388 ASSERT_PRINT(GetGraph()->GetEndBlock()->GetSuccsBlocks().empty(), "End block can't have successors");
389 [[maybe_unused]] auto iter = GetGraph()->GetEndBlock()->Insts();
390 ASSERT_PRINT(iter.begin() == iter.end(), "End block can't have instructions");
391 }
392
393 void GraphChecker::CheckGraph()
394 {
395 size_t num_inst = GetGraph()->GetCurrentInstructionId();
396 ArenaVector<bool> inst_vec(num_inst, GetLocalAllocator()->Adapter());
397 for (auto &bb : GetGraph()->GetBlocksRPO()) {
398 for (auto inst : bb->AllInsts()) {
399 auto id = inst->GetId();
400 ASSERT_DO(id < num_inst,
401 (std::cerr << "Instruction ID must be less than graph instruction counter: " << num_inst << "\n",
402 inst->Dump(&std::cerr)));
403 ASSERT_DO(!inst_vec[id],
404 (std::cerr << "Instruction with same Id already exists:\n", inst->Dump(&std::cerr)));
405 inst_vec[id] = true;
406 ASSERT_DO(GetGraph()->IsDynamicMethod() || inst->GetType() != DataType::ANY,
407 (std::cerr << "The type ANY is supported only for dynamic languages\n", inst->Dump(&std::cerr)));
408 ASSERT_DO(inst->SupportsMode(GetGraph()->GetCompilerMode()),
409 (std::cerr << "Instruction used in wrong mode\n", inst->Dump(&std::cerr)));
410 VisitInstruction(inst);
411 }
412 }
413 }
414
415 void GraphChecker::CheckPhiInputs(Inst *phi_inst)
416 {
417 for (size_t index = 0; index < phi_inst->GetInputsCount(); ++index) {
418 [[maybe_unused]] auto pred = phi_inst->CastToPhi()->GetPhiInputBb(index);
419 [[maybe_unused]] auto input_bb = phi_inst->CastToPhi()->GetPhiInput(pred)->GetBasicBlock();
420 ASSERT_DO(input_bb->IsDominate(pred) || IsTryCatchDomination(input_bb, pred),
421 (std::cerr
422 << "Block where phi-input is located should dominate predecessor block corresponding to this input\n"
423 << "Block " << input_bb->GetId() << " should dominate " << pred->GetId() << std::endl
424 << *phi_inst));
425 }
426 }
427
428 bool GraphChecker::CheckInstRegUsageSaved(const Inst *inst, Register reg) const
429 {
430 if (reg == ACC_REG_ID) {
431 return true;
432 }
433 auto graph = inst->GetBasicBlock()->GetGraph();
434 // Empty vector regs mask means we are using dynamic general regs set.
435 if (DataType::IsFloatType(inst->GetType()) && !graph->GetUsedRegs<DataType::FLOAT64>()->empty()) {
436 return graph->GetUsedRegs<DataType::FLOAT64>()->at(reg);
437 }
438 return graph->GetUsedRegs<DataType::INT64>()->at(reg);
439 }
440
441 [[maybe_unused]] static bool checkSpillFillMultiple(const compiler::Inst *inst)
442 {
443 switch (inst->GetOpcode()) {
444 case Opcode::Parameter:
445 return false;
446 case Opcode::LoadObject:
447 case Opcode::NewArray:
448 case Opcode::NewObject:
449 // In this case for BytecodeOptimizer SpillFill will be added after instruction, not before
450 // user-insturction. So this check can't find it and it is skipped.
451 return !inst->GetBasicBlock()->GetGraph()->IsBytecodeOptimizer();
452 default:
453 return true;
454 }
455 }
456
457 void GraphChecker::CheckNoLowLevel(BasicBlock *block)
458 {
459 for ([[maybe_unused]] auto inst : block->Insts()) {
460 ASSERT_DO(!inst->IsLowLevel(), inst->Dump(&std::cerr));
461 }
462 }
463
464 void GraphChecker::MarkBlocksInLoop(Loop *loop, Marker mrk)
465 {
466 ASSERT(loop->IsIrreducible() || loop->IsRoot() || loop->GetHeader() != nullptr);
467 ASSERT(loop->IsIrreducible() || loop->IsRoot() || loop->IsTryCatchLoop() || loop->GetPreHeader() != nullptr);
468 // Mark blocks and check if marker was not set before
469 for ([[maybe_unused]] auto block : loop->GetBlocks()) {
470 ASSERT(!block->SetMarker(mrk));
471 }
472
473 for (auto inner : loop->GetInnerLoops()) {
474 MarkBlocksInLoop(inner, mrk);
475 }
476 }
477
478 bool GraphChecker::CheckBlockHasPredecessor(BasicBlock *block, BasicBlock *predecessor)
479 {
480 ASSERT(block != nullptr && predecessor != nullptr);
481 for (auto pred : block->GetPredsBlocks()) {
482 if (pred == predecessor) {
483 return true;
484 }
485 }
486 return false;
487 }
488
489 bool GraphChecker::CheckBlockHasSuccessor(BasicBlock *block, BasicBlock *successor)
490 {
491 ASSERT(block != nullptr && successor != nullptr);
492 for (auto succ : block->GetSuccsBlocks()) {
493 if (succ == successor) {
494 return true;
495 }
496 }
497 return false;
498 }
499
500 bool GraphChecker::BlockContainsInstruction(BasicBlock *block, Opcode opcode)
501 {
502 return std::find_if(block->Insts().begin(), block->Insts().end(),
503 [opcode](Inst *inst) { return inst->GetOpcode() == opcode; }) != block->Insts().end();
504 }
505
506 void GraphChecker::CheckLoopHasSafePoint(Loop *loop)
507 {
508 [[maybe_unused]] auto it =
509 std::find_if(loop->GetBlocks().begin(), loop->GetBlocks().end(),
510 [this](BasicBlock *block) { return BlockContainsInstruction(block, Opcode::SafePoint); });
511 // Irreducible isn't fully populated - only 'one of the headers' and back-edge,
512 // SafePoint can be inserted to the another 'header' and search will be failed
513 ASSERT_DO(loop->IsTryCatchLoop() || loop->IsIrreducible() || it != loop->GetBlocks().end(),
514 std::cerr << "Loop " << loop->GetId() << " must have safepoint\n");
515 for (auto inner : loop->GetInnerLoops()) {
516 CheckLoopHasSafePoint(inner);
517 }
518 }
519
520 void GraphChecker::CheckLoops()
521 {
522 ASSERT(GetGraph()->GetAnalysis<LoopAnalyzer>().IsValid());
523 ASSERT(GetGraph()->GetRootLoop() != nullptr);
524 ASSERT(GetGraph()->GetRootLoop()->IsRoot());
525 ASSERT(GetGraph()->GetRootLoop()->GetHeader() == nullptr);
526 ASSERT(GetGraph()->GetRootLoop()->GetPreHeader() == nullptr);
527 auto root_loop = GetGraph()->GetRootLoop();
528 auto mrk = GetGraph()->NewMarker();
529 MarkBlocksInLoop(root_loop, mrk);
530
531 for ([[maybe_unused]] auto block : GetGraph()->GetBlocksRPO()) {
532 [[maybe_unused]] auto loop = block->GetLoop();
533 ASSERT(loop != nullptr);
534 ASSERT(block->IsMarked(mrk));
535 if (block->IsLoopHeader()) {
536 if (block->IsOsrEntry()) {
537 ASSERT(GetGraph()->IsOsrMode());
538 auto ss_osr = block->GetFirstInst();
539 while (ss_osr != nullptr && (ss_osr->IsCatchPhi() || ss_osr->GetOpcode() == Opcode::Try)) {
540 ss_osr = ss_osr->GetNext();
541 }
542 ASSERT(ss_osr != nullptr && ss_osr->GetOpcode() == Opcode::SaveStateOsr);
543 }
544 [[maybe_unused]] auto preds = block->GetPredsBlocks();
545 for ([[maybe_unused]] auto pred : preds) {
546 ASSERT(pred->GetLoop() != loop || loop->HasBackEdge(pred));
547 }
548
549 if (!loop->IsIrreducible()) {
550 for ([[maybe_unused]] auto back : loop->GetBackEdges()) {
551 ASSERT(std::find(preds.begin(), preds.end(), back) != preds.end());
552 }
553 }
554 } else {
555 ASSERT(!block->IsOsrEntry());
556 }
557 }
558 GetGraph()->EraseMarker(mrk);
559 if (options.IsCompilerUseSafepoint() && GetGraph()->SupportManagedCode()) {
560 for (auto inner : root_loop->GetInnerLoops()) {
561 CheckLoopHasSafePoint(inner);
562 }
563 }
564 }
565
566 void GraphChecker::CheckDomTree()
567 {
568 ASSERT(GetGraph()->GetAnalysis<DominatorsTree>().IsValid());
569 ArenaVector<BasicBlock *> dominators(GetGraph()->GetVectorBlocks().size(), GetLocalAllocator()->Adapter());
570 for (auto block : GetGraph()->GetBlocksRPO()) {
571 dominators[block->GetId()] = block->GetDominator();
572 }
573 // Rebuild dom-tree
574 GetGraph()->InvalidateAnalysis<DominatorsTree>();
575 GetGraph()->RunPass<DominatorsTree>();
576
577 for ([[maybe_unused]] auto block : GetGraph()->GetBlocksRPO()) {
578 ASSERT_DO(dominators[block->GetId()] == block->GetDominator(),
579 std::cerr << "Basic block with id " << block->GetId() << " has incorrect dominator with id "
580 << dominators[block->GetId()]->GetId() << std::endl
581 << "Correct dominator must be block with id " << block->GetDominator()->GetId() << std::endl
582 << "Note: basic blocks' ids in the original graph and in the cloned graph can be different"
583 << std::endl);
584 }
585 }
586
587 void GraphChecker::CheckLoopAnalysis()
588 {
589 // Save current loop info
590 ArenaUnorderedMap<BasicBlock *, Loop *> loops(GetLocalAllocator()->Adapter());
591 [[maybe_unused]] auto root_loop = GetGraph()->GetRootLoop();
592 for (auto block : GetGraph()->GetBlocksRPO()) {
593 if (block->IsLoopHeader()) {
594 loops.emplace(block, block->GetLoop());
595 }
596 }
597 // Build new loop info and compare with saved one
598 GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
599 GetGraph()->RunPass<LoopAnalyzer>();
600 ASSERT_PRINT(*root_loop == *GetGraph()->GetRootLoop(), "Root loop is incorrect\n");
601 for (auto &[block, loop] : loops) {
602 auto expected_loop = block->GetLoop();
603 // An irreducible loop can have different heads, depending on the order of traversal
604 if (loop->IsIrreducible()) {
605 ASSERT(expected_loop->IsIrreducible());
606 continue;
607 }
608 ASSERT(block->IsLoopHeader());
609 if (loop == nullptr || expected_loop == nullptr) {
610 UNREACHABLE();
611 return;
612 }
613 ASSERT_DO(*loop == *expected_loop, std::cerr << "Loop " << loop->GetId() << " is incorrect\n");
614 }
615 }
616
617 /**
618 * Check that there is root's inner loop without exit-points
619 */
620 bool GraphChecker::HasOuterInfiniteLoop()
621 {
622 const auto &loops = GetGraph()->GetRootLoop()->GetInnerLoops();
623 return std::find_if(loops.begin(), loops.end(), [](const Loop *loop) { return loop->IsInfinite(); }) != loops.end();
624 }
625
626 bool GraphChecker::CheckInstHasInput(Inst *inst, Inst *input)
627 {
628 ASSERT(inst != nullptr && input != nullptr);
629 ASSERT(input->GetBasicBlock() != nullptr);
630 ASSERT(input->GetBasicBlock()->GetGraph() != nullptr);
631 for (auto node : inst->GetInputs()) {
632 if (node.GetInst() == input) {
633 return true;
634 }
635 }
636 return false;
637 }
638
639 bool GraphChecker::CheckInstHasUser(Inst *inst, Inst *user)
640 {
641 ASSERT(inst != nullptr && user != nullptr);
642 ASSERT(user->GetBasicBlock() != nullptr);
643 ASSERT(user->GetBasicBlock()->GetGraph() != nullptr);
644 for (auto &node : inst->GetUsers()) {
645 if (node.GetInst() == user) {
646 return true;
647 }
648 }
649 return false;
650 }
651
652 void GraphChecker::CheckBlockEdges(const BasicBlock &block)
653 {
654 [[maybe_unused]] auto last_inst_in_block = block.GetLastInst();
655 if (block.GetSuccsBlocks().size() > 1) {
656 ASSERT_PRINT(!block.IsEmpty() || block.IsTryEnd(),
657 "Block with 2 successors have no instructions or should be try-end");
658 ASSERT_PRINT(block.IsTryBegin() || block.IsTryEnd() || last_inst_in_block->IsControlFlow(),
659 "Last instruction must be control flow in block with 2 successors");
660 } else if (block.GetSuccsBlocks().size() == 1) {
661 if (block.GetSuccsBlocks()[0]->IsEndBlock()) {
662 if (block.IsEmpty()) {
663 ASSERT(block.IsTryEnd());
664 return;
665 }
666 auto last_inst = block.GetLastInst();
667 [[maybe_unused]] auto opc = last_inst->GetOpcode();
668 ASSERT_PRINT(last_inst->GetFlag(inst_flags::TERMINATOR),
669 "Last instruction in block before exit-block must be Return or Throw instruction.");
670 }
671 }
672 }
673
674 void GraphChecker::CheckTryBeginBlock(const BasicBlock &block)
675 {
676 ASSERT(block.IsTryBegin());
677 auto try_inst_it = std::find_if(block.AllInsts().begin(), block.AllInsts().end(),
678 [](Inst *inst) { return inst->GetOpcode() == Opcode::Try; });
679 ASSERT_PRINT(try_inst_it != block.AllInsts().end(), "Try-begin basic block should contain try-instructions");
680 [[maybe_unused]] auto try_inst = (*try_inst_it)->CastToTry();
681 for ([[maybe_unused]] auto succ_index : *try_inst->GetCatchEdgeIndexes()) {
682 ASSERT_PRINT(succ_index < block.GetSuccsBlocks().size(),
683 "Try instruction holds incorrect try-begin block successor number");
684 }
685 }
686
687 void GraphChecker::CheckJump(const BasicBlock &block)
688 {
689 ASSERT(GetGraph()->IsRegAllocApplied());
690 ASSERT(GetGraph()->IsAnalysisValid<LinearOrder>());
691 if (block.IsIfBlock()) {
692 const auto &blocks_vector = GetGraph()->GetBlocksLinearOrder();
693 auto if_block_it = std::find(blocks_vector.begin(), blocks_vector.end(), &block);
694 ASSERT(if_block_it != blocks_vector.end());
695 auto block_after_if = std::next(if_block_it);
696 if (block_after_if != blocks_vector.end()) {
697 ASSERT_PRINT(*block_after_if != (*if_block_it)->GetFalseSuccessor(),
698 "`If-block` with immediate `false`-successor shouldn't have `JumpFlag`");
699 ASSERT_PRINT(*block_after_if != (*if_block_it)->GetTrueSuccessor(),
700 "`true`-successor should be replaced with `false`-successor");
701 }
702 }
703 [[maybe_unused]] auto num_succs = block.GetSuccsBlocks().size();
704 ASSERT_PRINT(num_succs == 1 || block.IsTryBegin() || block.IsTryEnd() || block.IsIfBlock(),
705 "Basic block with Jump must have 1 successor or should be try-begin or if block");
706 }
707
708 /**
709 * Regalloc propagates catch-phi's inputs to the users and can broke user's domination. In this case:
710 * - input_block should be placed inside try block;
711 * - try-begin block should dominate user_block;
712 *
713 * [try-begin]----------\
714 * | |
715 * [input_block] |
716 * | |
717 * [try-end]----------->|
718 * |
719 * [catch-begin]
720 * |
721 * [user_block]
722 */
723 bool GraphChecker::IsTryCatchDomination(const BasicBlock *input_block, const BasicBlock *user_block) const
724 {
725 ASSERT(GetGraph()->IsRegAllocApplied());
726 if (input_block->IsTry()) {
727 auto blocks = GetGraph()->GetTryBeginBlocks();
728 auto it =
729 std::find_if(blocks.begin(), blocks.end(), [user_block](auto &bb) { return bb->IsDominate(user_block); });
730 return it != blocks.end();
731 }
732 return false;
733 }
734
735 bool IsObjectCheckDisabledForOpcode(const Inst *inst)
736 {
737 auto opc = inst->GetOpcode();
738 return inst->IsCheck() || inst->IsConst() || opc == Opcode::NullPtr || inst->IsClassInst() ||
739 opc == Opcode::GetInstanceClass;
740 }
741
742 void GraphChecker::CheckSaveStatesWithRuntimeCallUsers()
743 {
744 #ifndef NDEBUG
745 for (auto &block : GetGraph()->GetBlocksRPO()) {
746 for (const auto &ss : block->AllInsts()) {
747 if (ss->GetOpcode() != Opcode::SaveState) {
748 continue;
749 }
750 for (auto &user : ss->GetUsers()) {
751 auto user_inst = user.GetInst();
752 if (!user_inst->IsRuntimeCall()) {
753 continue;
754 }
755 ASSERT(user_inst->GetBasicBlock() == ss->GetBasicBlock());
756 auto it = InstSafeIterator<IterationType::ALL, IterationDirection::BACKWARD>(*block, user_inst);
757 for (++it; *it != ss; ++it) {
758 // Non-reference instructions, checks, nullptr and classes cannot be moved by GC
759 ASSERT((*it)->GetType() != DataType::REFERENCE || IsObjectCheckDisabledForOpcode(*it));
760 }
761 }
762 }
763 }
764 #endif
765 }
766
767 void PrepareUsers(Inst *inst, ArenaVector<User *> *users)
768 {
769 for (auto &user : inst->GetUsers()) {
770 users->push_back(&user);
771 }
772 auto i = std::find_if(users->begin(), users->end(), [](User *user) { return user->GetInst()->IsCheck(); });
773 while (i != users->end()) {
774 for (auto &u : (*i)->GetInst()->GetUsers()) {
775 users->push_back(&u);
776 }
777 users->erase(i);
778 i = std::find_if(users->begin(), users->end(), [](User *user) { return user->GetInst()->IsCheck(); });
779 }
780 for (auto &it : (*users)) {
781 [[maybe_unused]] auto user = it->GetInst();
782 ASSERT(!user->IsCheck());
783 }
784 }
785
786 void GraphChecker::CheckSaveStateInputs()
787 {
788 #ifndef NDEBUG
789 ArenaVector<User *> users(GetLocalAllocator()->Adapter());
790 for (auto &block : GetGraph()->GetBlocksRPO()) {
791 for (const auto &inst : block->AllInsts()) {
792 if (IsObjectCheckDisabledForOpcode(inst)) {
793 continue;
794 }
795 // skip phi which all inputs is disabled
796 if (inst->GetOpcode() == Opcode::Phi) {
797 bool skip_flag = true;
798 for (const auto &input : inst->GetInputs()) {
799 skip_flag &= IsObjectCheckDisabledForOpcode(input.GetInst());
800 }
801 if (skip_flag) {
802 continue;
803 }
804 }
805
806 PrepareUsers(inst, &users);
807
808 auto object_visited = GetGraph()->NewMarker();
809 auto osr_visited = GetGraph()->NewMarker();
810 for (auto &it : users) {
811 auto user = it->GetInst();
812 // For Phi we need to check only pass between object and phi
813 if (user->IsPhi() || user->IsCatchPhi()) {
814 continue;
815 }
816 // Virtual register can be overwrite
817 if (user->IsSaveState()) {
818 continue;
819 }
820 if (inst->GetType() == DataType::REFERENCE) {
821 CheckObjectRec(inst, user, user->GetBasicBlock(), user->GetPrev(), object_visited);
822 }
823 CheckSaveStateOsrRec(inst, user, user->GetBasicBlock(), osr_visited);
824 }
825 GetGraph()->EraseMarker(object_visited);
826 GetGraph()->EraseMarker(osr_visited);
827 users.clear();
828 }
829 }
830 #endif
831 }
832
833 void GraphChecker::FindObjectInSaveState(const Inst *object, Inst *ss) const
834 {
835 while (ss != nullptr && object->IsDominate(ss)) {
836 auto it = std::find_if(ss->GetInputs().begin(), ss->GetInputs().end(),
837 [object, ss](Input input) { return ss->GetDataFlowInput(input.GetInst()) == object; });
838 if (it != ss->GetInputs().end()) {
839 return;
840 }
841 auto caller = static_cast<SaveStateInst *>(ss)->GetCallerInst();
842 if (caller == nullptr) {
843 break;
844 }
845 ss = caller->GetSaveState();
846 }
847 if (object != nullptr && ss != nullptr) {
848 std::cerr << "Object not found in the SaveState: " << std::endl
849 << *object << std::endl
850 << " " << *ss << std::endl;
851 }
852 UNREACHABLE();
853 }
854
855 bool IsSaveStateForGc(Inst *inst)
856 {
857 if (inst->GetOpcode() == Opcode::SafePoint) {
858 return true;
859 }
860 if (inst->GetOpcode() == Opcode::SaveState) {
861 for (auto &user : inst->GetUsers()) {
862 if (user.GetInst()->IsRuntimeCall()) {
863 return true;
864 }
865 }
866 }
867 return false;
868 }
869
870 void GraphChecker::CheckObjectRec(const Inst *object, const Inst *user, const BasicBlock *block, Inst *start_from,
871 Marker visited) const
872 {
873 if (start_from != nullptr) {
874 auto it = InstSafeIterator<IterationType::ALL, IterationDirection::BACKWARD>(*block, start_from);
875 for (; it != block->AllInstsSafeReverse().end(); ++it) {
876 auto inst = *it;
877 if (inst == nullptr) {
878 break;
879 }
880 if (inst->SetMarker(visited)) {
881 return;
882 }
883 if (IsSaveStateForGc(inst)) {
884 FindObjectInSaveState(object, inst);
885 } else if (inst == object || inst == user) {
886 return;
887 }
888 }
889 }
890 for (auto pred : block->GetPredsBlocks()) {
891 // Catch-begin block has edge from try-end block, and all try-blocks should be visited from this edge.
892 // `object` can be placed inside try-block - after try-begin, so that visiting try-begin is wrong
893 if (block->IsCatchBegin() && pred->IsTryBegin()) {
894 continue;
895 }
896 CheckObjectRec(object, user, pred, pred->GetLastInst(), visited);
897 }
898 }
899
900 void GraphChecker::CheckSaveStateOsrRec(const Inst *inst, const Inst *user, BasicBlock *block, Marker visited)
901 {
902 if (block->SetMarker(visited)) {
903 return;
904 }
905 if (inst->GetBasicBlock() == block) {
906 return;
907 }
908 if (block->IsOsrEntry()) {
909 ASSERT(GetGraph()->IsOsrMode());
910 auto ss = block->GetFirstInst();
911 ASSERT(ss != nullptr && ss->GetOpcode() == Opcode::SaveStateOsr);
912 [[maybe_unused]] auto it =
913 std::find_if(ss->GetInputs().begin(), ss->GetInputs().end(),
914 [inst, ss](Input input) { return ss->GetDataFlowInput(input.GetInst()) == inst; });
915 ASSERT(it != ss->GetInputs().end());
916 }
917 for (auto pred : block->GetPredsBlocks()) {
918 CheckSaveStateOsrRec(inst, user, pred, visited);
919 }
920 }
921
922 /*
923 * Visitors to check instructions types
924 */
925 void GraphChecker::VisitMov([[maybe_unused]] GraphVisitor *v, Inst *inst)
926 {
927 CheckUnaryOperationTypes(inst);
928 }
929 void GraphChecker::VisitNeg([[maybe_unused]] GraphVisitor *v, Inst *inst)
930 {
931 CheckUnaryOperationTypes(inst);
932 }
933 void GraphChecker::VisitAbs([[maybe_unused]] GraphVisitor *v, Inst *inst)
934 {
935 CheckUnaryOperationTypes(inst);
936 }
937 void GraphChecker::VisitSqrt([[maybe_unused]] GraphVisitor *v, Inst *inst)
938 {
939 ASSERT_DO(DataType::IsFloatType(inst->GetType()),
940 (std::cerr << "\nSqrt must have float type\n", inst->Dump(&std::cerr)));
941 CheckUnaryOperationTypes(inst);
942 }
943
944 void GraphChecker::VisitAddI([[maybe_unused]] GraphVisitor *v, Inst *inst)
945 {
946 if (inst->GetType() == DataType::POINTER) {
947 ASSERT_DO(inst->GetInputType(0) == DataType::POINTER,
948 (std::cerr << "\nptr AddI must have ptr input type\n", inst->Dump(&std::cerr)));
949 return;
950 }
951 ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
952 (std::cerr << "\nAddI must have integer type\n", inst->Dump(&std::cerr)));
953 CheckUnaryOperationTypes(inst);
954 }
955 void GraphChecker::VisitSubI([[maybe_unused]] GraphVisitor *v, Inst *inst)
956 {
957 if (!static_cast<GraphChecker *>(v)->GetGraph()->SupportManagedCode() && inst->GetType() == DataType::POINTER) {
958 ASSERT_DO(inst->GetInputType(0) == DataType::POINTER,
959 (std::cerr << "\nptr SubI must have ptr input type\n", inst->Dump(&std::cerr)));
960 return;
961 }
962 ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
963 (std::cerr << "\nSubI must have integer type\n", inst->Dump(&std::cerr)));
964 CheckUnaryOperationTypes(inst);
965 }
966 void GraphChecker::VisitMulI([[maybe_unused]] GraphVisitor *v, Inst *inst)
967 {
968 [[maybe_unused]] auto type = inst->GetType();
969 ASSERT_DO(DataType::Is32Bits(type, static_cast<GraphChecker *>(v)->GetGraph()->GetArch()) &&
970 !DataType::IsReference(type),
971 (std::cerr << "\nMulI must have Int32 type\n", inst->Dump(&std::cerr)));
972 CheckUnaryOperationTypes(inst);
973 }
974 void GraphChecker::VisitDivI([[maybe_unused]] GraphVisitor *v, Inst *inst)
975 {
976 [[maybe_unused]] auto type = inst->GetType();
977 ASSERT_DO(DataType::Is32Bits(type, static_cast<GraphChecker *>(v)->GetGraph()->GetArch()) &&
978 !DataType::IsReference(type),
979 (std::cerr << "\nDivI must have Int32 type\n", inst->Dump(&std::cerr)));
980 CheckUnaryOperationTypes(inst);
981 }
982 void GraphChecker::VisitModI([[maybe_unused]] GraphVisitor *v, Inst *inst)
983 {
984 [[maybe_unused]] auto type = inst->GetType();
985 ASSERT_DO(DataType::Is32Bits(type, static_cast<GraphChecker *>(v)->GetGraph()->GetArch()) &&
986 !DataType::IsReference(type),
987 (std::cerr << "\nModI must have Int32 type\n", inst->Dump(&std::cerr)));
988 CheckUnaryOperationTypes(inst);
989 }
990 void GraphChecker::VisitAndI([[maybe_unused]] GraphVisitor *v, Inst *inst)
991 {
992 ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
993 (std::cerr << "\nAndI must have integer type\n", inst->Dump(&std::cerr)));
994 CheckUnaryOperationTypes(inst);
995 }
996 void GraphChecker::VisitOrI([[maybe_unused]] GraphVisitor *v, Inst *inst)
997 {
998 ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
999 (std::cerr << "\nOrI must have integer type\n", inst->Dump(&std::cerr)));
1000 CheckUnaryOperationTypes(inst);
1001 }
1002 void GraphChecker::VisitXorI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1003 {
1004 ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1005 (std::cerr << "\nXorI must have integer type\n", inst->Dump(&std::cerr)));
1006 CheckUnaryOperationTypes(inst);
1007 }
1008 void GraphChecker::VisitShlI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1009 {
1010 ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1011 (std::cerr << "\nShlI must have integer type\n", inst->Dump(&std::cerr)));
1012 CheckUnaryOperationTypes(inst);
1013 [[maybe_unused]] auto imm = static_cast<BinaryImmOperation *>(inst)->GetImm();
1014 ASSERT_DO(imm <= DataType::GetTypeSize(inst->GetType(), static_cast<GraphChecker *>(v)->GetGraph()->GetArch()),
1015 (std::cerr << "\nShlI have shift more then size of type\n", inst->Dump(&std::cerr)));
1016 }
1017 void GraphChecker::VisitShrI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1018 {
1019 ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1020 (std::cerr << "\nShrI must have integer type\n", inst->Dump(&std::cerr)));
1021 CheckUnaryOperationTypes(inst);
1022 [[maybe_unused]] auto imm = static_cast<BinaryImmOperation *>(inst)->GetImm();
1023 ASSERT_DO(imm <= DataType::GetTypeSize(inst->GetType(), static_cast<GraphChecker *>(v)->GetGraph()->GetArch()),
1024 (std::cerr << "\nShrI have shift more then size of type\n", inst->Dump(&std::cerr)));
1025 }
1026 void GraphChecker::VisitAShlI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1027 {
1028 ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1029 (std::cerr << "\nAShrI must have integer type\n", inst->Dump(&std::cerr)));
1030 CheckUnaryOperationTypes(inst);
1031 [[maybe_unused]] auto imm = static_cast<BinaryImmOperation *>(inst)->GetImm();
1032 ASSERT_DO(imm <= DataType::GetTypeSize(inst->GetType(), static_cast<GraphChecker *>(v)->GetGraph()->GetArch()),
1033 (std::cerr << "\nAShlI have shift more then size of type\n", inst->Dump(&std::cerr)));
1034 }
1035 void GraphChecker::VisitNot([[maybe_unused]] GraphVisitor *v, Inst *inst)
1036 {
1037 ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1038 (std::cerr << "\nNot must have integer type\n", inst->Dump(&std::cerr)));
1039 CheckUnaryOperationTypes(inst);
1040 }
1041
1042 void GraphChecker::VisitAdd([[maybe_unused]] GraphVisitor *v, Inst *inst)
1043 {
1044 if (!static_cast<GraphChecker *>(v)->GetGraph()->SupportManagedCode() && inst->GetType() == DataType::POINTER) {
1045 [[maybe_unused]] auto type_1 = inst->GetInput(0).GetInst()->GetType();
1046 [[maybe_unused]] auto type_2 = inst->GetInput(1).GetInst()->GetType();
1047 ASSERT_DO(type_1 != type_2,
1048 (std::cerr << "\nptr Add must have ptr and int input types\n", inst->Dump(&std::cerr)));
1049 ASSERT_DO((type_1 == DataType::POINTER && DataType::GetCommonType(type_2) == DataType::INT64) ||
1050 (type_2 == DataType::POINTER && DataType::GetCommonType(type_1) == DataType::INT64),
1051 (std::cerr << "\nptr Add must have ptr and int input types\n", inst->Dump(&std::cerr)));
1052 return;
1053 }
1054 CheckBinaryOperationTypes(inst);
1055 }
1056 void GraphChecker::VisitSub([[maybe_unused]] GraphVisitor *v, Inst *inst)
1057 {
1058 if (!static_cast<GraphChecker *>(v)->GetGraph()->SupportManagedCode()) {
1059 [[maybe_unused]] auto type_1 = inst->GetInput(0).GetInst()->GetType();
1060 [[maybe_unused]] auto type_2 = inst->GetInput(1).GetInst()->GetType();
1061 if (inst->GetType() == DataType::POINTER) {
1062 ASSERT_DO(type_1 != type_2,
1063 (std::cerr << "\nptr Sub must have ptr and int input types\n", inst->Dump(&std::cerr)));
1064 ASSERT_DO((type_1 == DataType::POINTER && DataType::GetCommonType(type_2) == DataType::INT64) ||
1065 (type_2 == DataType::POINTER && DataType::GetCommonType(type_1) == DataType::INT64),
1066 (std::cerr << "\nptr Sub must have ptr and int input types\n", inst->Dump(&std::cerr)));
1067 return;
1068 }
1069 if (type_1 == DataType::POINTER && type_2 == DataType::POINTER) {
1070 ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
1071 (std::cerr << "\n Sub with 2 ptr inputs must have int type\n", inst->Dump(&std::cerr)));
1072 return;
1073 }
1074 }
1075 CheckBinaryOperationTypes(inst);
1076 }
1077 void GraphChecker::VisitMul([[maybe_unused]] GraphVisitor *v, Inst *inst)
1078 {
1079 CheckBinaryOperationTypes(inst);
1080 }
1081 void GraphChecker::VisitDiv([[maybe_unused]] GraphVisitor *v, Inst *inst)
1082 {
1083 CheckBinaryOperationTypes(inst);
1084 }
1085 void GraphChecker::VisitMod([[maybe_unused]] GraphVisitor *v, Inst *inst)
1086 {
1087 CheckBinaryOperationTypes(inst);
1088 }
1089 void GraphChecker::VisitMin([[maybe_unused]] GraphVisitor *v, Inst *inst)
1090 {
1091 CheckBinaryOperationTypes(inst);
1092 }
1093 void GraphChecker::VisitMax([[maybe_unused]] GraphVisitor *v, Inst *inst)
1094 {
1095 CheckBinaryOperationTypes(inst);
1096 }
1097 void GraphChecker::VisitShl([[maybe_unused]] GraphVisitor *v, Inst *inst)
1098 {
1099 CheckBinaryOperationTypes(inst, true);
1100 }
1101 void GraphChecker::VisitShr([[maybe_unused]] GraphVisitor *v, Inst *inst)
1102 {
1103 CheckBinaryOperationTypes(inst, true);
1104 }
1105 void GraphChecker::VisitAShr([[maybe_unused]] GraphVisitor *v, Inst *inst)
1106 {
1107 CheckBinaryOperationTypes(inst, true);
1108 }
1109 void GraphChecker::VisitAnd([[maybe_unused]] GraphVisitor *v, Inst *inst)
1110 {
1111 CheckBinaryOperationTypes(inst, true);
1112 }
1113 void GraphChecker::VisitOr([[maybe_unused]] GraphVisitor *v, Inst *inst)
1114 {
1115 CheckBinaryOperationTypes(inst, true);
1116 }
1117 void GraphChecker::VisitXor([[maybe_unused]] GraphVisitor *v, Inst *inst)
1118 {
1119 CheckBinaryOperationTypes(inst, true);
1120 }
1121
1122 void GraphChecker::VisitAddOverflow([[maybe_unused]] GraphVisitor *v, Inst *inst)
1123 {
1124 CheckBinaryOverflowOperation(inst->CastToAddOverflow());
1125 }
1126 void GraphChecker::VisitSubOverflow([[maybe_unused]] GraphVisitor *v, Inst *inst)
1127 {
1128 CheckBinaryOverflowOperation(inst->CastToSubOverflow());
1129 }
1130 void GraphChecker::VisitLoadArray([[maybe_unused]] GraphVisitor *v, Inst *inst)
1131 {
1132 CheckMemoryInstruction(inst);
1133 }
1134 void GraphChecker::VisitLoadArrayI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1135 {
1136 CheckMemoryInstruction(inst);
1137 }
1138 void GraphChecker::VisitLoadArrayPair([[maybe_unused]] GraphVisitor *v, Inst *inst)
1139 {
1140 ASSERT_DO(MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1141 (std::cerr << "Unallowed type of coalesced load\n", inst->Dump(&std::cerr)));
1142 CheckMemoryInstruction(inst);
1143 }
1144 void GraphChecker::VisitLoadArrayPairI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1145 {
1146 ASSERT_DO(MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1147 (std::cerr << "Unallowed type of coalesced load\n", inst->Dump(&std::cerr)));
1148 CheckMemoryInstruction(inst);
1149 }
1150
1151 void GraphChecker::VisitLoadPairPart([[maybe_unused]] GraphVisitor *v, Inst *inst)
1152 {
1153 ASSERT_DO(MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1154 (std::cerr << "Unallowed type of coalesced load\n", inst->Dump(&std::cerr)));
1155 CheckMemoryInstruction(inst);
1156 [[maybe_unused]] auto op1 = inst->GetInputs()[0].GetInst();
1157 [[maybe_unused]] auto idx = inst->CastToLoadPairPart()->GetImm();
1158 ASSERT_DO(op1->GetOpcode() == Opcode::LoadArrayPair || op1->GetOpcode() == Opcode::LoadArrayPairI,
1159 (std::cerr << "Input instruction is not a Pair\n", inst->Dump(&std::cerr)));
1160 if (op1->GetOpcode() == Opcode::LoadArrayPairI) {
1161 ASSERT_DO(idx < op1->CastToLoadArrayPairI()->GetDstCount(),
1162 (std::cerr << "Pair index is out of bounds\n", inst->Dump(&std::cerr)));
1163 } else {
1164 ASSERT_DO(idx < op1->CastToLoadArrayPair()->GetDstCount(),
1165 (std::cerr << "Pair index is out of bounds\n", inst->Dump(&std::cerr)));
1166 }
1167 ASSERT_DO(
1168 CheckCommonTypes(inst, inst->GetInputs()[0].GetInst()),
1169 (std::cerr << "Types of load vector element and vector input are not compatible\n", inst->Dump(&std::cerr)));
1170
1171 // Strict order here
1172 auto prev = inst->GetPrev();
1173 while (prev != nullptr && prev != op1) {
1174 if (prev->GetOpcode() == Opcode::LoadPairPart || prev->GetOpcode() == Opcode::SpillFill) {
1175 prev = prev->GetPrev();
1176 } else {
1177 break;
1178 }
1179 }
1180 ASSERT_DO(prev != nullptr && prev == op1,
1181 (std::cerr << "LoadPairPart(s) instructions must follow immediately after appropriate LoadArrayPair(I)\n",
1182 inst->Dump(&std::cerr)));
1183 }
1184
1185 void GraphChecker::VisitStoreArrayPair([[maybe_unused]] GraphVisitor *v, Inst *inst)
1186 {
1187 ASSERT_DO(MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1188 (std::cerr << "Unallowed type of coalesced store\n", inst->Dump(&std::cerr)));
1189 CheckMemoryInstruction(inst);
1190 ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[2U].GetInst()),
1191 (std::cerr << "Types of store and the first stored value are not compatible\n", inst->Dump(&std::cerr)));
1192 ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[3U].GetInst()),
1193 (std::cerr << "Types of store and the second stored value are not compatible\n", inst->Dump(&std::cerr)));
1194 [[maybe_unused]] bool need_barrier = inst->CastToStoreArrayPair()->GetNeedBarrier();
1195 ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1196 (std::cerr << "StoreArrayPair has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1197 }
1198
1199 void GraphChecker::VisitStoreArrayPairI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1200 {
1201 ASSERT_DO(MemoryCoalescing::AcceptedType(inst->GetType()) || DataType::IsReference(inst->GetType()),
1202 (std::cerr << "Unallowed type of coalesced store\n", inst->Dump(&std::cerr)));
1203 CheckMemoryInstruction(inst);
1204 ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[1].GetInst()),
1205 (std::cerr << "Types of store and the first stored value are not compatible\n", inst->Dump(&std::cerr)));
1206 ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[2U].GetInst()),
1207 (std::cerr << "Types of store and the second stored value are not compatible\n", inst->Dump(&std::cerr)));
1208 [[maybe_unused]] bool need_barrier = inst->CastToStoreArrayPairI()->GetNeedBarrier();
1209 ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1210 (std::cerr << "StoreArrayPairI has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1211 }
1212
1213 void GraphChecker::VisitStoreArray([[maybe_unused]] GraphVisitor *v, Inst *inst)
1214 {
1215 CheckMemoryInstruction(inst);
1216 ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[2U].GetInst()),
1217 (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1218 [[maybe_unused]] bool need_barrier = inst->CastToStoreArray()->GetNeedBarrier();
1219 ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1220 (std::cerr << "StoreArray has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1221 }
1222
1223 void GraphChecker::VisitStoreArrayI([[maybe_unused]] GraphVisitor *v, Inst *inst)
1224 {
1225 CheckMemoryInstruction(inst);
1226 ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[1].GetInst()),
1227 (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1228 [[maybe_unused]] bool need_barrier = inst->CastToStoreArrayI()->GetNeedBarrier();
1229 ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1230 (std::cerr << "StoreArrayI has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1231 }
1232
1233 void GraphChecker::VisitStoreStatic([[maybe_unused]] GraphVisitor *v, Inst *inst)
1234 {
1235 CheckMemoryInstruction(inst);
1236 auto graph = static_cast<GraphChecker *>(v)->GetGraph();
1237 ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[1].GetInst()),
1238 (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1239 [[maybe_unused]] bool need_barrier = inst->CastToStoreStatic()->GetNeedBarrier();
1240 ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE),
1241 (std::cerr << "StoreStatic has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1242 [[maybe_unused]] auto init_inst = inst->GetInputs()[0].GetInst();
1243 if (init_inst->IsPhi()) {
1244 return;
1245 }
1246 ASSERT_DO(init_inst->GetOpcode() == Opcode::LoadAndInitClass,
1247 (std::cerr << "The first input for the StoreStatic should be LoadAndInitClass", inst->Dump(&std::cerr),
1248 init_inst->Dump(&std::cerr)));
1249 [[maybe_unused]] auto store_static = inst->CastToStoreStatic();
1250 [[maybe_unused]] auto class_id =
1251 graph->GetRuntime()->GetClassIdForField(store_static->GetMethod(), store_static->GetTypeId());
1252 // See comment in VisitNewObject about this if statement
1253 if (init_inst->CastToLoadAndInitClass()->GetClass() == nullptr) {
1254 ASSERT_DO(init_inst->CastToLoadAndInitClass()->GetTypeId() == class_id,
1255 (std::cerr << "StoreStatic and LoadAndInitClass must have equal class", inst->Dump(&std::cerr),
1256 init_inst->Dump(&std::cerr)));
1257 }
1258 }
1259
1260 void GraphChecker::VisitUnresolvedStoreStatic([[maybe_unused]] GraphVisitor *v, Inst *inst)
1261 {
1262 CheckMemoryInstruction(inst);
1263 ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[0].GetInst()),
1264 (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1265 [[maybe_unused]] bool need_barrier = inst->CastToUnresolvedStoreStatic()->GetNeedBarrier();
1266 ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE),
1267 (std::cerr << "UnresolvedStoreStatic has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1268 [[maybe_unused]] auto ss = inst->GetInputs()[1].GetInst();
1269 ASSERT_DO(ss->GetOpcode() == Opcode::SaveState,
1270 (std::cerr << "UnresolvedStoreStatic instruction second operand is not a SaveState",
1271 inst->Dump(&std::cerr), ss->Dump(&std::cerr)));
1272 }
1273
1274 void GraphChecker::VisitStoreObject([[maybe_unused]] GraphVisitor *v, Inst *inst)
1275 {
1276 CheckMemoryInstruction(inst);
1277 ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[1].GetInst()),
1278 (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1279 [[maybe_unused]] bool need_barrier = inst->CastToStoreObject()->GetNeedBarrier();
1280 ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE) || inst->GetType() == DataType::ANY,
1281 (std::cerr << "StoreObject has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1282 }
1283
1284 void GraphChecker::VisitUnresolvedStoreObject([[maybe_unused]] GraphVisitor *v, Inst *inst)
1285 {
1286 CheckMemoryInstruction(inst);
1287 ASSERT_DO(CheckCommonTypes(inst, inst->GetInputs()[1].GetInst()),
1288 (std::cerr << "Types of store and store input are not compatible\n", inst->Dump(&std::cerr)));
1289 [[maybe_unused]] bool need_barrier = inst->CastToUnresolvedStoreObject()->GetNeedBarrier();
1290 ASSERT_DO(need_barrier == (inst->GetType() == DataType::REFERENCE),
1291 (std::cerr << "UnresolvedStoreObject has incorrect value NeedBarrier", inst->Dump(&std::cerr)));
1292 [[maybe_unused]] auto ss = inst->GetInputs()[2U].GetInst();
1293 ASSERT_DO(ss->GetOpcode() == Opcode::SaveState,
1294 (std::cerr << "UnresolvedStoreObject instruction third operand is not a SaveState",
1295 inst->Dump(&std::cerr), ss->Dump(&std::cerr)));
1296 }
1297
1298 void GraphChecker::VisitLoadStatic([[maybe_unused]] GraphVisitor *v, Inst *inst)
1299 {
1300 CheckMemoryInstruction(inst);
1301 auto graph = static_cast<GraphChecker *>(v)->GetGraph();
1302 [[maybe_unused]] auto init_inst = inst->GetInputs()[0].GetInst();
1303 if (init_inst->IsPhi()) {
1304 return;
1305 }
1306 ASSERT_DO(init_inst->GetOpcode() == Opcode::LoadAndInitClass,
1307 (std::cerr << "The first input for the LoadStatic should be LoadAndInitClass", inst->Dump(&std::cerr),
1308 init_inst->Dump(&std::cerr)));
1309 [[maybe_unused]] auto load_static = inst->CastToLoadStatic();
1310 [[maybe_unused]] auto class_id =
1311 graph->GetRuntime()->GetClassIdForField(load_static->GetMethod(), load_static->GetTypeId());
1312 // See comment in VisitNewObject about this if statement
1313 if (init_inst->CastToLoadAndInitClass()->GetClass() == nullptr) {
1314 ASSERT_DO(init_inst->CastToLoadAndInitClass()->GetTypeId() == class_id,
1315 (std::cerr << "LoadStatic and LoadAndInitClass must have equal class", inst->Dump(&std::cerr),
1316 init_inst->Dump(&std::cerr)));
1317 }
1318 }
1319
1320 void GraphChecker::VisitUnresolvedLoadStatic([[maybe_unused]] GraphVisitor *v, Inst *inst)
1321 {
1322 CheckMemoryInstruction(inst);
1323 [[maybe_unused]] auto ss = inst->GetInputs()[0].GetInst();
1324 ASSERT_DO(ss->GetOpcode() == Opcode::SaveState,
1325 (std::cerr << "UnresolvedLoadStatic instruction first operand is not a SaveState", inst->Dump(&std::cerr),
1326 ss->Dump(&std::cerr)));
1327 }
1328
1329 void GraphChecker::VisitLoadClass([[maybe_unused]] GraphVisitor *v, Inst *inst)
1330 {
1331 ASSERT_DO(inst->GetType() == DataType::REFERENCE,
1332 (std::cerr << "LoadClass must have Reference type", inst->Dump(&std::cerr)));
1333 for (auto &user : inst->GetUsers()) {
1334 [[maybe_unused]] auto user_inst = user.GetInst();
1335 ASSERT_DO(
1336 user_inst->GetOpcode() == Opcode::CheckCast || user_inst->GetOpcode() == Opcode::IsInstance ||
1337 user_inst->GetOpcode() == Opcode::Phi,
1338 (std::cerr << "Incorrect user of the LoadClass", inst->Dump(&std::cerr), user_inst->Dump(&std::cerr)));
1339 }
1340 }
1341
1342 void GraphChecker::VisitLoadAndInitClass([[maybe_unused]] GraphVisitor *v, Inst *inst)
1343 {
1344 ASSERT_DO(inst->GetType() == DataType::REFERENCE,
1345 (std::cerr << "LoadAndInitClass must have Reference type", inst->Dump(&std::cerr)));
1346 for (auto &user : inst->GetUsers()) {
1347 [[maybe_unused]] auto user_inst = user.GetInst();
1348 ASSERT_DO(user_inst->GetOpcode() == Opcode::LoadStatic || user_inst->GetOpcode() == Opcode::StoreStatic ||
1349 user_inst->GetOpcode() == Opcode::NewObject || user_inst->GetOpcode() == Opcode::Phi ||
1350 user_inst->GetOpcode() == Opcode::MultiArray || user_inst->GetOpcode() == Opcode::InitObject ||
1351 user_inst->GetOpcode() == Opcode::UnresolvedLoadStatic ||
1352 user_inst->GetOpcode() == Opcode::UnresolvedStoreStatic ||
1353 user_inst->GetOpcode() == Opcode::Intrinsic || user_inst->GetOpcode() == Opcode::NewArray ||
1354 user_inst->GetOpcode() == Opcode::IsInstance || user_inst->GetOpcode() == Opcode::CheckCast,
1355 (std::cerr << "Incorrect user of the LoadAndInitClass", inst->Dump(&std::cerr),
1356 user_inst->Dump(&std::cerr)));
1357 }
1358 }
1359
1360 void GraphChecker::VisitUnresolvedLoadAndInitClass([[maybe_unused]] GraphVisitor *v, Inst *inst)
1361 {
1362 ASSERT_DO(inst->GetType() == DataType::REFERENCE,
1363 (std::cerr << "UnresolvedLoadAndInitClass must have Reference type", inst->Dump(&std::cerr)));
1364 ASSERT_DO(inst->CastToUnresolvedLoadAndInitClass()->GetClass() == nullptr,
1365 (std::cerr << "UnresolvedLoadAndInitClass must have a null ClassPtr", inst->Dump(&std::cerr)));
1366 [[maybe_unused]] auto ss = inst->GetInputs()[0].GetInst();
1367 ASSERT_DO(ss->GetOpcode() == Opcode::SaveState,
1368 (std::cerr << "UnresolvedLoadAndInitClass instruction first operand is not a SaveState",
1369 inst->Dump(&std::cerr), ss->Dump(&std::cerr)));
1370 for (auto &user : inst->GetUsers()) {
1371 [[maybe_unused]] auto user_inst = user.GetInst();
1372 ASSERT_DO(user_inst->GetOpcode() == Opcode::LoadStatic || user_inst->GetOpcode() == Opcode::StoreStatic ||
1373 user_inst->GetOpcode() == Opcode::NewObject || user_inst->GetOpcode() == Opcode::NewArray ||
1374 user_inst->GetOpcode() == Opcode::Phi || user_inst->GetOpcode() == Opcode::MultiArray ||
1375 user_inst->GetOpcode() == Opcode::UnresolvedLoadStatic ||
1376 user_inst->GetOpcode() == Opcode::UnresolvedStoreStatic,
1377 (std::cerr << "Incorrect user of the UnresolvedLoadAndInitClass", inst->Dump(&std::cerr),
1378 user_inst->Dump(&std::cerr)));
1379 }
1380 }
1381
1382 void GraphChecker::VisitNewObject([[maybe_unused]] GraphVisitor *v, Inst *inst)
1383 {
1384 ASSERT_DO(inst->GetType() == DataType::REFERENCE,
1385 (std::cerr << "NewObject must be have Reference type", inst->Dump(&std::cerr)));
1386 [[maybe_unused]] auto init_inst = inst->GetInputs()[0].GetInst();
1387 if (init_inst->IsPhi()) {
1388 return;
1389 }
1390 ASSERT_DO(
1391 init_inst->GetOpcode() == Opcode::LoadAndInitClass ||
1392 init_inst->GetOpcode() == Opcode::UnresolvedLoadAndInitClass,
1393 (std::cerr << "The first input for the NewObject should be LoadAndInitClass or UnresolvedLoadAndInitClass",
1394 inst->Dump(&std::cerr), init_inst->Dump(&std::cerr)));
1395 [[maybe_unused]] auto ss_inst = inst->GetInputs()[1].GetInst();
1396 ASSERT_DO(ss_inst->GetOpcode() == Opcode::SaveState,
1397 (std::cerr << "The second input for the NewObject should be SaveState", inst->Dump(&std::cerr),
1398 ss_inst->Dump(&std::cerr)));
1399 // If InitClass contains an already resolved class, then IDs may be different. Because VN can remove the
1400 // duplicated InitClass and keep only one that is located in the inlined method and has a different id
1401 // accordingly.
1402 if (init_inst->GetOpcode() == Opcode::LoadAndInitClass &&
1403 init_inst->CastToLoadAndInitClass()->GetClass() == nullptr) {
1404 ASSERT_DO(init_inst->CastToLoadAndInitClass()->GetTypeId() == inst->CastToNewObject()->GetTypeId(),
1405 std::cerr << "NewObject and LoadAndInitClass must have equal class:\n"
1406 << *inst << '\n'
1407 << *init_inst << std::endl);
1408 } else if (init_inst->GetOpcode() == Opcode::UnresolvedLoadAndInitClass) {
1409 ASSERT_DO(init_inst->CastToUnresolvedLoadAndInitClass()->GetTypeId() == inst->CastToNewObject()->GetTypeId(),
1410 std::cerr << "NewObject and UnresolvedLoadAndInitClass must have equal class:\n"
1411 << *inst << '\n'
1412 << *init_inst << std::endl);
1413 }
1414 }
1415
1416 void GraphChecker::VisitInitObject([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1417 {
1418 ASSERT_DO(options.IsCompilerSupportInitObjectInst(),
1419 (std::cerr << "Instruction InitObject isn't supported", inst->Dump(&std::cerr)));
1420 }
1421
1422 void GraphChecker::VisitInitClass([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1423 {
1424 ASSERT_DO(inst->GetType() == DataType::NO_TYPE,
1425 (std::cerr << "InitClass doesn't have type", inst->Dump(&std::cerr)));
1426 }
1427
1428 void GraphChecker::VisitLoadObject([[maybe_unused]] GraphVisitor *v, Inst *inst)
1429 {
1430 CheckMemoryInstruction(inst);
1431 }
1432
1433 void GraphChecker::VisitUnresolvedLoadObject([[maybe_unused]] GraphVisitor *v, Inst *inst)
1434 {
1435 CheckMemoryInstruction(inst);
1436 [[maybe_unused]] auto ss = inst->GetInputs()[1].GetInst();
1437 ASSERT_DO(ss->GetOpcode() == Opcode::SaveState,
1438 (std::cerr << "UnresolvedLoadObject instruction second operand is not a SaveState",
1439 inst->Dump(&std::cerr), ss->Dump(&std::cerr)));
1440 }
1441
1442 void GraphChecker::VisitConstant([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1443 {
1444 [[maybe_unused]] auto type = inst->GetType();
1445 [[maybe_unused]] auto is_dynamic = static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod();
1446 if (static_cast<GraphChecker *>(v)->GetGraph()->IsBytecodeOptimizer()) {
1447 ASSERT_DO(
1448 type == DataType::FLOAT32 || type == DataType::FLOAT64 || type == DataType::INT64 ||
1449 type == DataType::INT32 || (type == DataType::ANY && is_dynamic),
1450 (std::cerr << "Constant inst can be only FLOAT32, FLOAT64, INT32 or INT64\n", inst->Dump(&std::cerr)));
1451
1452 } else {
1453 ASSERT_DO(
1454 type == DataType::FLOAT32 || type == DataType::FLOAT64 || type == DataType::INT64 ||
1455 (type == DataType::ANY && is_dynamic),
1456 (std::cerr << "Constant instruction can be only FLOAT32, FLOAT64 or INT64\n", inst->Dump(&std::cerr)));
1457 }
1458 }
1459
1460 void GraphChecker::VisitNullPtr([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1461 {
1462 ASSERT_DO(inst->GetType() == DataType::REFERENCE,
1463 (std::cerr << "NullPtr instruction should have REFERENCE type only\n", inst->Dump(&std::cerr)));
1464
1465 ASSERT_DO(static_cast<GraphChecker *>(v)->IncrementNullPtrInstCounterAndGet() == 1,
1466 (std::cerr << "There should be not more than one NullPtr instruction in graph\n",
1467 inst->GetBasicBlock()->Dump(&std::cerr)));
1468 }
1469
1470 void GraphChecker::VisitPhi([[maybe_unused]] GraphVisitor *v, Inst *inst)
1471 {
1472 for ([[maybe_unused]] auto input : inst->GetInputs()) {
1473 ASSERT_DO(CheckCommonTypes(inst, input.GetInst()),
1474 (std::cerr << "Types of phi result and phi input are not compatible\n"
1475 << *inst << std::endl
1476 << *input.GetInst()));
1477 }
1478 }
1479
1480 void GraphChecker::VisitParameter([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1481 {
1482 ASSERT_DO(inst->GetType() != DataType::NO_TYPE,
1483 (std::cerr << "The parametr doesn't have type:\n", inst->Dump(&std::cerr)));
1484 }
1485
1486 void GraphChecker::VisitCompare([[maybe_unused]] GraphVisitor *v, Inst *inst)
1487 {
1488 [[maybe_unused]] auto op1 = inst->GetInputs()[0].GetInst();
1489 [[maybe_unused]] auto op2 = inst->GetInputs()[1].GetInst();
1490 for (size_t i = 0; i < inst->GetInputsCount(); i++) {
1491 ASSERT_DO(inst->GetInputType(i) != DataType::NO_TYPE,
1492 std::cerr << "Source operand type is not set: " << *inst << std::endl);
1493 }
1494 ASSERT_DO(inst->GetInputType(0) == inst->GetInputType(1),
1495 std::cerr << "Conditional instruction has different inputs type: " << *inst << std::endl);
1496 if (inst->GetInputType(0) == DataType::REFERENCE) {
1497 ASSERT_DO(inst->CastToCompare()->GetCc() == ConditionCode::CC_NE ||
1498 inst->CastToCompare()->GetCc() == ConditionCode::CC_EQ,
1499 (std::cerr << "Reference compare must have CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
1500 if (op1->IsConst()) {
1501 ASSERT_DO(IsZeroConstant(op1), (std::cerr << "Constant reference input must be integer 0: \n",
1502 inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1503 } else {
1504 ASSERT_DO(op1->GetType() == DataType::REFERENCE,
1505 (std::cerr << "Condition instruction 1st operand type is not a reference\n",
1506 inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1507 }
1508 if (op2->IsConst()) {
1509 ASSERT_DO(IsZeroConstant(op2), (std::cerr << "Constant reference input must be integer 0: \n",
1510 inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
1511 } else {
1512 ASSERT_DO(op2->GetType() == DataType::REFERENCE,
1513 (std::cerr << "Condition instruction 2nd operand type is not a reference\n",
1514 inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
1515 }
1516 }
1517 ASSERT_PRINT(inst->GetType() == DataType::BOOL, "Condition instruction type is not a bool");
1518 }
1519
1520 void GraphChecker::VisitCast([[maybe_unused]] GraphVisitor *v, Inst *inst)
1521 {
1522 [[maybe_unused]] auto dst_type = inst->GetType();
1523 [[maybe_unused]] auto src_type = inst->GetInputType(0);
1524 [[maybe_unused]] auto input_type = inst->GetInput(0).GetInst()->GetType();
1525
1526 if (dst_type == DataType::ANY) {
1527 ASSERT_DO((DataType::IsTypeNumeric(input_type) || input_type == DataType::REFERENCE),
1528 (std::cerr << "Cast instruction operand type is not a numeric or reference type\n",
1529 inst->Dump(&std::cerr)));
1530 } else {
1531 ASSERT_DO(DataType::IsTypeNumeric(dst_type),
1532 (std::cerr << "Cast instruction dst type is not a numeric type\n", inst->Dump(&std::cerr)));
1533 if (static_cast<GraphChecker *>(v)->GetGraph()->GetMode().SupportManagedCode()) {
1534 ASSERT_DO(DataType::IsTypeNumeric(src_type),
1535 (std::cerr << "Cast instruction src type is not a numeric type\n", inst->Dump(&std::cerr)));
1536 ASSERT_DO(DataType::IsTypeNumeric(input_type),
1537 (std::cerr << "Cast instruction operand type is not a numeric type\n", inst->Dump(&std::cerr)));
1538 }
1539 ASSERT_DO(DataType::GetCommonType(src_type) == DataType::GetCommonType(input_type),
1540 (std::cerr << "Incorrect src_type and input type\n", inst->Dump(&std::cerr)));
1541 ASSERT_DO(!(DataType::IsFloatType(src_type) && DataType::IsLessInt32(dst_type)),
1542 (std::cerr << "Cast instruction from " << DataType::internal::TYPE_NAMES.at(src_type) << " to "
1543 << DataType::internal::TYPE_NAMES.at(dst_type) << " don't support\n",
1544 inst->Dump(&std::cerr)));
1545 }
1546 }
1547
1548 void GraphChecker::VisitCmp([[maybe_unused]] GraphVisitor *v, Inst *inst)
1549 {
1550 [[maybe_unused]] auto op1 = inst->GetInput(0).GetInst();
1551 [[maybe_unused]] auto op2 = inst->GetInput(1).GetInst();
1552 ASSERT_DO(DataType::IsTypeNumeric(op1->GetType()),
1553 (std::cerr << "Cmp instruction 1st operand type is not a numeric type\n", inst->Dump(&std::cerr),
1554 op1->Dump(&std::cerr)));
1555 ASSERT_DO(DataType::IsTypeNumeric(op2->GetType()),
1556 (std::cerr << "Cmp instruction 2st operand type is not a numeric type\n", inst->Dump(&std::cerr),
1557 op2->Dump(&std::cerr)));
1558 ASSERT_DO(
1559 DataType::GetCommonType(op1->GetType()) == DataType::GetCommonType(inst->GetInputType(0)),
1560 (std::cerr << "Input type and Cmp Input Type are not equal\n", inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1561 ASSERT_DO(
1562 DataType::GetCommonType(op2->GetType()) == DataType::GetCommonType(inst->GetInputType(1)),
1563 (std::cerr << "Input type and Cmp Input Type are not equal\n", inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
1564 ASSERT_DO(inst->GetType() == DataType::INT32,
1565 (std::cerr << "Cmp instruction type is not a int32\n", inst->Dump(&std::cerr)));
1566 for (size_t i = 0; i < inst->GetInputsCount(); i++) {
1567 ASSERT_DO(inst->GetInputType(i) != DataType::NO_TYPE,
1568 std::cerr << "Source operand type is not set: " << *inst << std::endl);
1569 }
1570 }
1571
1572 void GraphChecker::VisitMonitor([[maybe_unused]] GraphVisitor *v, Inst *inst)
1573 {
1574 [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
1575 ASSERT_DO(inst->GetType() == DataType::VOID, (std::cerr << "Monitor type is not a void", inst->Dump(&std::cerr)));
1576 ASSERT_DO(DataType::IsReference(op->GetType()),
1577 (std::cerr << "Monitor instruction 1st operand type is not a reference", inst->Dump(&std::cerr),
1578 op->Dump(&std::cerr)));
1579 [[maybe_unused]] auto op1 = inst->GetInputs()[1].GetInst();
1580 ASSERT_DO(op1->GetOpcode() == Opcode::SaveState,
1581 (std::cerr << "Monitor instruction second operand is not a SaveState", inst->Dump(&std::cerr),
1582 op1->Dump(&std::cerr)));
1583 }
1584
1585 void GraphChecker::VisitReturn([[maybe_unused]] GraphVisitor *v, Inst *inst)
1586 {
1587 [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
1588 ASSERT_DO(CheckCommonTypes(inst, op), (std::cerr << "Types of return and its input are not compatible\n return:\n",
1589 inst->Dump(&std::cerr), std::cerr << "\n input:\n", op->Dump(&std::cerr)));
1590 CheckContrlFlowInst(inst);
1591 [[maybe_unused]] auto num_succs = inst->GetBasicBlock()->GetSuccsBlocks().size();
1592 ASSERT_PRINT(num_succs == 1, "Basic block with Return must have 1 successor");
1593 [[maybe_unused]] auto succ = inst->GetBasicBlock()->GetSuccsBlocks()[0];
1594 ASSERT_DO(succ->IsEndBlock() || succ->IsTryEnd(),
1595 std::cerr << "Basic block with Return must have end or try end block as successor:\n"
1596 << *inst << std::endl);
1597 }
1598
1599 void GraphChecker::VisitReturnVoid([[maybe_unused]] GraphVisitor *v, Inst *inst)
1600 {
1601 CheckContrlFlowInst(inst);
1602 [[maybe_unused]] auto num_succs = inst->GetBasicBlock()->GetSuccsBlocks().size();
1603 ASSERT_PRINT(num_succs == 1, "Basic block with ReturnVoid must have 1 successor");
1604 [[maybe_unused]] auto succ = inst->GetBasicBlock()->GetSuccsBlocks()[0];
1605 ASSERT_PRINT(succ->IsEndBlock() || succ->IsTryEnd(),
1606 "Basic block with ReturnVoid must have end or try_end block as successor.");
1607 }
1608
1609 void GraphChecker::VisitNullCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1610 {
1611 [[maybe_unused]] Inst *array = inst->GetInput(0).GetInst();
1612 ASSERT_DO(DataType::IsReference(array->GetType()) || array->GetType() == DataType::ANY,
1613 (std::cerr << "\n Types of input NullCheck must be REFERENCE or ANY: \n", inst->Dump(&std::cerr),
1614 array->Dump(&std::cerr)));
1615 [[maybe_unused]] auto ss = inst->GetInput(1).GetInst();
1616 ASSERT_DO(ss->GetOpcode() == Opcode::SaveState || ss->GetOpcode() == Opcode::SaveStateDeoptimize,
1617 (std::cerr << "\n Second input of NullCheck must be SaveState: \n", inst->Dump(&std::cerr),
1618 ss->Dump(&std::cerr)));
1619 }
1620
1621 void GraphChecker::VisitBoundsCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1622 {
1623 for (int i = 0; i < 1; i++) {
1624 [[maybe_unused]] auto op = inst->GetInputs()[i].GetInst();
1625 [[maybe_unused]] auto op_type = op->GetType();
1626 // TODO(pishin): actually type should be INT32, but predecessor may be Call instruction with type u16, u8
1627 // e.t.c
1628 ASSERT_DO(
1629 (op->IsConst() && op_type == DataType::INT64) ||
1630 (DataType::GetCommonType(op_type) == DataType::INT64 &&
1631 Is32Bits(op_type, static_cast<GraphChecker *>(v)->GetGraph()->GetArch())),
1632 (std::cerr << "Types of " << i << " input BoundsCheck must be INT32 or less:\n", inst->Dump(&std::cerr)));
1633 }
1634 CheckThrows(inst, {Opcode::LoadArray, Opcode::StoreArray, Opcode::LoadArrayPair, Opcode::StoreArrayPair,
1635 Opcode::Phi, Opcode::Intrinsic, Opcode::Store, Opcode::Load, Opcode::LoadCompressedStringChar});
1636 }
1637
1638 void GraphChecker::VisitRefTypeCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1639 {
1640 ASSERT_DO((inst->GetType() == DataType::REFERENCE),
1641 (std::cerr << "Types of RefTypeCheck must be REFERENCE\n", inst->Dump(&std::cerr)));
1642 for (unsigned i = 0; i < 2U; i++) {
1643 [[maybe_unused]] auto op = inst->GetInputs()[i].GetInst();
1644 [[maybe_unused]] auto op_type = op->GetType();
1645 ASSERT_DO((op_type == DataType::REFERENCE),
1646 (std::cerr << "Types of " << i << " input RefTypeCheck must be REFERENCE\n", inst->Dump(&std::cerr),
1647 op->Dump(&std::cerr)));
1648 }
1649 CheckThrows(inst, {Opcode::StoreArray, Opcode::StoreArrayPair, Opcode::StoreArrayI, Opcode::StoreArrayPairI});
1650 }
1651
1652 void GraphChecker::VisitNegativeCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1653 {
1654 [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
1655 [[maybe_unused]] auto op_type = op->GetType();
1656 ASSERT_DO(DataType::GetCommonType(op_type) == DataType::INT64,
1657 (std::cerr << "Type of NegativeCheck ZeroCheck must be integer\n", inst->Dump(&std::cerr)));
1658 CheckThrows(inst, {Opcode::NewArray, Opcode::MultiArray, Opcode::Phi});
1659 }
1660
1661 void GraphChecker::VisitZeroCheck([[maybe_unused]] GraphVisitor *v, Inst *inst)
1662 {
1663 [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
1664 [[maybe_unused]] auto op_type = op->GetType();
1665 ASSERT_DO(DataType::GetCommonType(op_type) == DataType::INT64,
1666 (std::cerr << "Type of ZeroCheck input must be integer\n", inst->Dump(&std::cerr)));
1667 CheckThrows(inst, {Opcode::Div, Opcode::Mod, Opcode::Phi});
1668 }
1669
1670 void GraphChecker::VisitDeoptimizeIf([[maybe_unused]] GraphVisitor *v, Inst *inst)
1671 {
1672 [[maybe_unused]] auto op = inst->GetInput(0).GetInst();
1673 ASSERT_DO(op->GetType() == DataType::BOOL || op->IsBoolConst(),
1674 (std::cerr << "Type of first input DeoptimizeIf must be BOOL:\n", inst->Dump(&std::cerr)));
1675 [[maybe_unused]] auto ss = inst->GetInput(1).GetInst();
1676 ASSERT_DO(
1677 ss->GetOpcode() == Opcode::SaveStateDeoptimize || ss->GetOpcode() == Opcode::SaveState,
1678 (std::cerr << "Second input DeoptimizeIf must be SaveStateDeoptimize or SaveState:\n", inst->Dump(&std::cerr)));
1679 }
1680
1681 void GraphChecker::VisitLenArray([[maybe_unused]] GraphVisitor *v, Inst *inst)
1682 {
1683 ASSERT_DO(inst->GetType() == DataType::INT32,
1684 (std::cerr << "Type of LenArray must be INT32:\n", inst->Dump(&std::cerr)));
1685 [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
1686 if (op->GetOpcode() == Opcode::NullCheck) {
1687 op = op->GetInput(0).GetInst();
1688 }
1689 ASSERT_DO(DataType::IsReference(op->GetType()), (std::cerr << "Types of input LenArray must be REFERENCE:\n",
1690 inst->Dump(&std::cerr), op->Dump(&std::cerr)));
1691 }
1692
1693 void GraphChecker::VisitiUnresolvedCallStatic([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1694 {
1695 ASSERT_DO(inst->CastToUnresolvedCallStatic()->GetCallMethod() != nullptr,
1696 (std::cerr << "UnresolvedCallStatic must have non-null MethodPtr", inst->Dump(&std::cerr)));
1697 [[maybe_unused]] auto ss = inst->GetInputs()[inst->GetInputsCount() - 1].GetInst();
1698 ASSERT_DO(ss->GetOpcode() == Opcode::SaveState,
1699 (std::cerr << "UnresolvedCallStatic instruction last operand is not a SaveState", inst->Dump(&std::cerr),
1700 ss->Dump(&std::cerr)));
1701 }
1702
1703 void GraphChecker::VisitCallVirtual([[maybe_unused]] GraphVisitor *v, Inst *inst)
1704 {
1705 ASSERT_DO(inst->GetInputs().Size() > 0,
1706 (std::cerr << "Virtual function must have inputs:\n", inst->Dump(&std::cerr)));
1707 [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
1708 ASSERT_DO(DataType::IsReference(op->GetType()),
1709 (std::cerr << "Types of first input CallVirtual must be REFERENCE(this):\n", inst->Dump(&std::cerr),
1710 op->Dump(&std::cerr)));
1711 }
1712
1713 void GraphChecker::VisitUnresolvedCallVirtual([[maybe_unused]] GraphVisitor *v, Inst *inst)
1714 {
1715 ASSERT_DO(inst->GetInputs().Size() > 0,
1716 (std::cerr << "Virtual function must have inputs:\n", inst->Dump(&std::cerr)));
1717 ASSERT_DO(inst->CastToUnresolvedCallVirtual()->GetCallMethod() != nullptr,
1718 (std::cerr << "UnresolvedCallVirtual must have non-null MethodPtr", inst->Dump(&std::cerr)));
1719 [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
1720 ASSERT_DO(DataType::IsReference(op->GetType()),
1721 (std::cerr << "Types of first input UnresolvedCallVirtual must be REFERENCE(this):\n",
1722 inst->Dump(&std::cerr), op->Dump(&std::cerr)));
1723 [[maybe_unused]] auto ss = inst->GetInputs()[inst->GetInputsCount() - 1].GetInst();
1724 ASSERT_DO(ss->GetOpcode() == Opcode::SaveState,
1725 (std::cerr << "UnresolvedCallVirtual instruction last operand is not a SaveState", inst->Dump(&std::cerr),
1726 ss->Dump(&std::cerr)));
1727 }
1728
1729 void GraphChecker::VisitCallDynamic([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1730 {
1731 ASSERT_DO(static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
1732 (std::cerr << "CallDynamic is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
1733 }
1734
1735 void GraphChecker::VisitSaveState([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1736 {
1737 ASSERT_DO((static_cast<SaveStateInst *>(inst))->Verify(), std::cerr << "Inconsistent SaveState instruction:\n"
1738 << *inst << std::endl);
1739 #ifndef NDEBUG
1740 auto ss = inst->CastToSaveState();
1741 if (ss->GetInputsWereDeleted()) {
1742 for (auto &user : inst->GetUsers()) {
1743 ASSERT_DO(!user.GetInst()->RequireRegMap(),
1744 std::cerr << "Some inpust from save_state were deleted, but the user requireRegMap:\n"
1745 << *inst << std::endl
1746 << *(user.GetInst()) << std::endl);
1747 }
1748 }
1749 #endif
1750 }
1751
1752 void GraphChecker::VisitSafePoint([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1753 {
1754 ASSERT_DO(!inst->HasUsers(), std::cerr << "SafePoint must not have users:\n" << *inst << std::endl);
1755 ASSERT_DO((static_cast<SaveStateInst *>(inst))->Verify(), std::cerr << "Inconsistent SafePoint instruction:\n"
1756 << *inst << std::endl);
1757 }
1758
1759 void GraphChecker::VisitSaveStateOsr([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1760 {
1761 ASSERT_DO(!inst->HasUsers(), std::cerr << "SafeStateOsr must not have users:\n" << *inst << std::endl);
1762 ASSERT_DO((static_cast<SaveStateInst *>(inst))->Verify(), std::cerr << "Inconsistent SafeStateOsr instruction:\n"
1763 << *inst << std::endl);
1764 ASSERT_DO(static_cast<GraphChecker *>(v)->GetGraph()->IsOsrMode(),
1765 std::cerr << "SafeStateOsr must be created in the OSR mode only\n");
1766 ASSERT_DO(inst->GetBasicBlock()->IsOsrEntry(), std::cerr << "SafeStateOsr's basic block must be osr-entry\n");
1767 auto first_inst = inst->GetBasicBlock()->GetFirstInst();
1768 while (first_inst != nullptr && (first_inst->IsCatchPhi() || first_inst->GetOpcode() == Opcode::Try)) {
1769 first_inst = first_inst->GetNext();
1770 }
1771 ASSERT_DO(first_inst == inst, std::cerr << "SafeStateOsr must be the first instruction in the basic block\n");
1772 }
1773
1774 void GraphChecker::VisitThrow([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1775 {
1776 ASSERT_DO(DataType::IsReference(inst->GetInput(0).GetInst()->GetType()),
1777 std::cerr << "Throw instruction must have input with reference type: " << *inst << std::endl);
1778 [[maybe_unused]] auto bb = inst->GetBasicBlock();
1779 ASSERT_DO(inst == bb->GetLastInst(),
1780 std::cerr << "Throw instruction must be last instruction in the basic block: " << *inst << std::endl);
1781 for ([[maybe_unused]] auto succ : bb->GetSuccsBlocks()) {
1782 ASSERT_DO(succ->IsEndBlock() || succ->IsTryEnd(),
1783 std::cerr << "Throw block must have end block or try-end block as successor\n");
1784 }
1785 }
1786
1787 void GraphChecker::VisitCheckCast([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1788 {
1789 ASSERT_DO(DataType::IsReference(inst->GetInput(0).GetInst()->GetType()),
1790 std::cerr << "CheckCast instruction must have input 0 with reference type: " << *inst << std::endl);
1791
1792 ASSERT_DO(DataType::IsReference(inst->GetInput(1).GetInst()->GetType()),
1793 std::cerr << "CheckCast instruction must have input 1 with reference type: " << *inst << std::endl);
1794
1795 [[maybe_unused]] auto save_state = inst->GetInput(2).GetInst();
1796 ASSERT_DO((save_state != nullptr && save_state->GetOpcode() == Opcode::SaveState),
1797 std::cerr << "CheckCast instruction must have SaveState as input 2: " << *inst << std::endl);
1798 }
1799
1800 void GraphChecker::VisitIsInstance([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1801 {
1802 ASSERT_DO(DataType::IsReference(inst->GetInput(0).GetInst()->GetType()),
1803 std::cerr << "IsInstance instruction must have input 0 with reference type: " << *inst << std::endl);
1804 ASSERT_DO(DataType::IsReference(inst->GetInput(1).GetInst()->GetType()),
1805 std::cerr << "IsInstance instruction must have input 1 with reference type: " << *inst << std::endl);
1806
1807 [[maybe_unused]] auto save_state = inst->GetInput(2).GetInst();
1808 ASSERT_DO((save_state != nullptr && save_state->GetOpcode() == Opcode::SaveState),
1809 std::cerr << "IsInstance instruction must have SaveState as input 2: " << *inst << std::endl);
1810
1811 ASSERT_DO(inst->GetType() == DataType::BOOL,
1812 (std::cerr << "Types of IsInstance must be BOOL:\n", inst->Dump(&std::cerr)));
1813 }
1814
1815 void GraphChecker::VisitSelect([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1816 {
1817 [[maybe_unused]] auto op0 = inst->GetInput(0).GetInst();
1818 [[maybe_unused]] auto op1 = inst->GetInput(1).GetInst();
1819 [[maybe_unused]] auto op2 = inst->GetInput(2U).GetInst();
1820 [[maybe_unused]] auto op3 = inst->GetInput(3U).GetInst();
1821
1822 for (size_t i = 0; i < inst->GetInputsCount(); i++) {
1823 ASSERT_DO(inst->GetInputType(i) != DataType::NO_TYPE,
1824 std::cerr << "Source operand type is not set: " << *inst << std::endl);
1825 }
1826
1827 ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64 || inst->GetType() == DataType::REFERENCE ||
1828 inst->GetType() == DataType::ANY,
1829 (std::cerr << "Select instruction type is not integer or reference or any", inst->Dump(&std::cerr)));
1830 ASSERT_DO(DataType::GetCommonType(op0->GetType()) == DataType::INT64 || op0->GetType() == DataType::REFERENCE ||
1831 op0->GetType() == DataType::ANY,
1832 (std::cerr << "Select instruction 1st operand type is not integer or reference or any",
1833 inst->Dump(&std::cerr)));
1834 ASSERT_DO(DataType::GetCommonType(op1->GetType()) == DataType::INT64 || op1->GetType() == DataType::REFERENCE ||
1835 op1->GetType() == DataType::ANY,
1836 (std::cerr << "Select instruction 2nd operand type is not integer or reference or any",
1837 inst->Dump(&std::cerr)));
1838
1839 ASSERT_DO(CheckCommonTypes(op0, op1),
1840 (std::cerr << "Types of two first select instruction operands are not compatible\n",
1841 op0->Dump(&std::cerr), op1->Dump(&std::cerr), inst->Dump(&std::cerr)));
1842 ASSERT_DO(
1843 CheckCommonTypes(inst, op0),
1844 (std::cerr << "Types of instruction result and its operands are not compatible\n", inst->Dump(&std::cerr)));
1845
1846 ASSERT_DO(inst->GetInputType(2U) == inst->GetInputType(3U),
1847 std::cerr << "Select comparison arguments has different inputs type: " << *inst << std::endl);
1848 if (inst->GetInputType(2U) == DataType::REFERENCE) {
1849 [[maybe_unused]] auto cc = inst->CastToSelect()->GetCc();
1850 ASSERT_DO(cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
1851 (std::cerr << "Select reference comparison must be CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
1852 if (op2->IsConst()) {
1853 ASSERT_DO(IsZeroConstant(op2), (std::cerr << "Constant reference input must be integer 0: \n",
1854 inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1855 } else {
1856 ASSERT_DO(op2->GetType() == DataType::REFERENCE,
1857 (std::cerr << "Select instruction 3rd operand type is not a reference\n", inst->Dump(&std::cerr),
1858 op1->Dump(&std::cerr)));
1859 }
1860 if (op3->IsConst()) {
1861 ASSERT_DO(IsZeroConstant(op3), (std::cerr << "Constant reference input must be integer 0: \n",
1862 inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
1863 } else {
1864 ASSERT_DO(op3->GetType() == DataType::REFERENCE,
1865 (std::cerr << "Select instruction 4th operand type is not a reference\n", inst->Dump(&std::cerr),
1866 op2->Dump(&std::cerr)));
1867 }
1868 }
1869 }
1870
1871 void GraphChecker::VisitSelectImm([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
1872 {
1873 [[maybe_unused]] auto op0 = inst->GetInput(0).GetInst();
1874 [[maybe_unused]] auto op1 = inst->GetInput(1).GetInst();
1875 [[maybe_unused]] auto op2 = inst->GetInput(2U).GetInst();
1876 [[maybe_unused]] auto op3 = inst->CastToSelectImm()->GetImm();
1877 [[maybe_unused]] bool is_dynamic = static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod();
1878
1879 for (size_t i = 0; i < inst->GetInputsCount(); i++) {
1880 ASSERT_DO(inst->GetInputType(i) != DataType::NO_TYPE,
1881 std::cerr << "Source operand type is not set: " << *inst << std::endl);
1882 }
1883
1884 ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64 || inst->GetType() == DataType::REFERENCE ||
1885 (is_dynamic && inst->GetType() == DataType::ANY),
1886 (std::cerr << "SelectImm instruction type is not integer or reference or any", inst->Dump(&std::cerr)));
1887 ASSERT_DO(DataType::GetCommonType(op0->GetType()) == DataType::INT64 || op0->GetType() == DataType::REFERENCE ||
1888 (is_dynamic && op0->GetType() == DataType::ANY),
1889 (std::cerr << "SelectImm instruction 1st operand type is not integer or reference or any",
1890 inst->Dump(&std::cerr)));
1891 ASSERT_DO(DataType::GetCommonType(op1->GetType()) == DataType::INT64 || op1->GetType() == DataType::REFERENCE ||
1892 (is_dynamic && op1->GetType() == DataType::ANY),
1893 (std::cerr << "SelectImm instruction 2nd operand type is not integer or reference or any",
1894 inst->Dump(&std::cerr)));
1895
1896 ASSERT_DO(CheckCommonTypes(op0, op1),
1897 (std::cerr << "Types of two first SelectImm instruction operands are not compatible\n",
1898 op0->Dump(&std::cerr), op1->Dump(&std::cerr), inst->Dump(&std::cerr)));
1899 ASSERT_DO(
1900 CheckCommonTypes(inst, op0),
1901 (std::cerr << "Types of instruction result and its operands are not compatible\n", inst->Dump(&std::cerr)));
1902
1903 if (inst->GetInputType(2U) == DataType::REFERENCE) {
1904 [[maybe_unused]] auto cc = inst->CastToSelectImm()->GetCc();
1905 ASSERT_DO(cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
1906 (std::cerr << "SelectImm reference comparison must be CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
1907 if (op2->IsConst()) {
1908 ASSERT_DO(IsZeroConstant(op2), (std::cerr << "Constant reference input must be integer 0: \n",
1909 inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1910 } else {
1911 ASSERT_DO(op2->GetType() == DataType::REFERENCE,
1912 (std::cerr << "Condition with immediate jump 1st operand type is not a reference\n",
1913 inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1914 }
1915 ASSERT_DO(op3 == 0,
1916 (std::cerr << "Reference can be compared only with 0 immediate: \n", inst->Dump(&std::cerr)));
1917 } else {
1918 ASSERT_PRINT(DataType::GetCommonType(op2->GetType()) == DataType::INT64 ||
1919 (is_dynamic && DataType::GetCommonType(op2->GetType()) == DataType::ANY),
1920 "SelectImm 3rd operand type is not an integer or any");
1921
1922 if (DataType::GetCommonType(op2->GetType()) == DataType::ANY) {
1923 [[maybe_unused]] auto cc = inst->CastToSelectImm()->GetCc();
1924 ASSERT_DO(cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
1925 (std::cerr << "SelectImm any comparison must be CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
1926 }
1927 }
1928 }
1929
1930 void GraphChecker::VisitIf([[maybe_unused]] GraphVisitor *v, Inst *inst)
1931 {
1932 CheckContrlFlowInst(inst);
1933 [[maybe_unused]] auto num_succs = inst->GetBasicBlock()->GetSuccsBlocks().size();
1934 ASSERT_PRINT(num_succs == MAX_SUCCS_NUM, "Basic block with If must have 2 successesors");
1935
1936 [[maybe_unused]] auto op1 = inst->GetInputs()[0].GetInst();
1937 [[maybe_unused]] auto op2 = inst->GetInputs()[1].GetInst();
1938 for (size_t i = 0; i < inst->GetInputsCount(); i++) {
1939 ASSERT_DO(inst->GetInputType(i) != DataType::NO_TYPE,
1940 std::cerr << "Source operand type is not set: " << *inst << std::endl);
1941 }
1942 ASSERT_DO(inst->GetInputType(0) == inst->GetInputType(1),
1943 std::cerr << "If has different inputs type: " << *inst << std::endl);
1944 if (inst->GetInputType(0) == DataType::REFERENCE) {
1945 [[maybe_unused]] auto cc = inst->CastToIf()->GetCc();
1946 ASSERT_DO(cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
1947 (std::cerr << "Reference comparison in If must be CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
1948 if (op1->IsConst()) {
1949 ASSERT_DO(IsZeroConstant(op1), (std::cerr << "Constant reference input must be integer 0: \n",
1950 inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1951 } else {
1952 ASSERT_DO(op1->GetType() == DataType::REFERENCE, (std::cerr << "If 1st operand type is not a reference\n",
1953 inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1954 }
1955 if (op2->IsConst()) {
1956 ASSERT_DO(IsZeroConstant(op2), (std::cerr << "Constant reference input must be integer 0: \n",
1957 inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
1958 } else {
1959 ASSERT_DO(op2->GetType() == DataType::REFERENCE, (std::cerr << "If 2nd operand type is not a reference\n",
1960 inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
1961 }
1962 }
1963 }
1964
1965 void GraphChecker::VisitIfImm([[maybe_unused]] GraphVisitor *v, Inst *inst)
1966 {
1967 CheckContrlFlowInst(inst);
1968 [[maybe_unused]] auto num_succs = inst->GetBasicBlock()->GetSuccsBlocks().size();
1969 ASSERT_PRINT(num_succs == MAX_SUCCS_NUM, "Basic block with IfImm must have 2 successesors");
1970
1971 [[maybe_unused]] auto op1 = inst->GetInput(0).GetInst();
1972 [[maybe_unused]] auto op2 = inst->CastToIfImm()->GetImm();
1973 ASSERT_DO(inst->GetInputType(0) != DataType::NO_TYPE,
1974 std::cerr << "Source operand type is not set: " << *inst << std::endl);
1975 if (inst->GetInputType(0) == DataType::REFERENCE) {
1976 [[maybe_unused]] auto cc = inst->CastToIfImm()->GetCc();
1977 ASSERT_DO(cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
1978 (std::cerr << "Reference comparison in IfImm must have CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
1979 if (op1->IsConst()) {
1980 ASSERT_DO(IsZeroConstant(op1), (std::cerr << "Constant reference input must be integer 0: \n",
1981 inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
1982 } else {
1983 ASSERT_DO(op1->GetType() == DataType::REFERENCE,
1984 (std::cerr << "IfImm operand type should be here a reference: \n", inst->Dump(&std::cerr),
1985 op1->Dump(&std::cerr)));
1986 }
1987 ASSERT_DO(op2 == 0,
1988 (std::cerr << "Reference can be compared only with 0 immediate: \n", inst->Dump(&std::cerr)));
1989 } else {
1990 ASSERT_PRINT(
1991 DataType::GetCommonType(op1->GetType()) == DataType::INT64 ||
1992 (static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod() && op1->GetType() == DataType::ANY),
1993 "IfImm operand type should be here an integer");
1994 }
1995 }
1996
1997 void GraphChecker::VisitTry([[maybe_unused]] GraphVisitor *v, Inst *inst)
1998 {
1999 [[maybe_unused]] auto bb = inst->GetBasicBlock();
2000 ASSERT_PRINT(bb->IsTryBegin(), "TryInst should be placed in the try-begin basic block");
2001 }
2002
2003 void GraphChecker::VisitNOP([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2004 {
2005 ASSERT_PRINT(inst->GetUsers().Empty(), "NOP can not have users\n");
2006 }
2007
2008 void GraphChecker::VisitAndNot([[maybe_unused]] GraphVisitor *v, Inst *inst)
2009 {
2010 CheckBinaryOperationTypes(inst, true);
2011 }
2012 void GraphChecker::VisitOrNot([[maybe_unused]] GraphVisitor *v, Inst *inst)
2013 {
2014 CheckBinaryOperationTypes(inst, true);
2015 }
2016 void GraphChecker::VisitXorNot([[maybe_unused]] GraphVisitor *v, Inst *inst)
2017 {
2018 CheckBinaryOperationTypes(inst, true);
2019 }
2020 void GraphChecker::VisitMNeg([[maybe_unused]] GraphVisitor *v, Inst *inst)
2021 {
2022 CheckBinaryOperationTypes(inst, false);
2023 }
2024 void GraphChecker::VisitMAdd([[maybe_unused]] GraphVisitor *v, Inst *inst)
2025 {
2026 CheckTernaryOperationTypes(inst);
2027 }
2028 void GraphChecker::VisitMSub([[maybe_unused]] GraphVisitor *v, Inst *inst)
2029 {
2030 CheckTernaryOperationTypes(inst);
2031 }
2032
2033 void GraphChecker::VisitCompareAnyType([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2034 {
2035 ASSERT_DO(static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
2036 (std::cerr << "CompareAnyType is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
2037 }
2038
2039 void GraphChecker::VisitCastAnyTypeValue([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2040 {
2041 ASSERT_DO(static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
2042 (std::cerr << "CastAnyTypeValue is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
2043 }
2044
2045 void GraphChecker::VisitCastValueToAnyType([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2046 {
2047 ASSERT_DO(static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
2048 (std::cerr << "CastValueToAnyType is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
2049
2050 const auto *input_inst = inst->GetInput(0).GetInst();
2051 auto input_type = inst->GetInputType(0);
2052 auto output_type = AnyBaseTypeToDataType(inst->CastToCastValueToAnyType()->GetAnyType());
2053
2054 ASSERT_DO(input_type != DataType::ANY,
2055 (std::cerr << "CastValueToAnyType cannot accept inputs of ANY type:\n", inst->Dump(&std::cerr)));
2056
2057 if (input_inst->IsConst() && (input_type == DataType::Type::INT64 || input_type == DataType::Type::INT32)) {
2058 if (output_type == DataType::Type::BOOL) {
2059 ASSERT_DO(input_inst->IsBoolConst(),
2060 (std::cerr << "Integral constant input not coercible to BOOL:\n", inst->Dump(&std::cerr)));
2061 return;
2062 }
2063
2064 if (output_type == DataType::INT32 && input_type == DataType::INT64) {
2065 [[maybe_unused]] int64_t value = input_inst->CastToConstant()->GetInt64Value();
2066 ASSERT_DO(value == static_cast<int32_t>(value),
2067 (std::cerr << "Integral constant input not coercible to INT32:\n", inst->Dump(&std::cerr)));
2068 return;
2069 }
2070
2071 if (output_type == DataType::Type::REFERENCE) {
2072 return; // Always coercible
2073 }
2074
2075 if (output_type == DataType::Type::VOID) {
2076 return; // Always coercible
2077 }
2078
2079 // Otherwise proceed with the generic check.
2080 }
2081 }
2082
2083 void GraphChecker::VisitAnyTypeCheck([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2084 {
2085 ASSERT_DO(static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod(),
2086 (std::cerr << "AnyTypeCheck is supported only for dynamic languages:\n", inst->Dump(&std::cerr)));
2087 ASSERT_DO(inst->GetInput(0).GetInst()->GetType() == DataType::Type::ANY,
2088 (std::cerr << "First input in AnyTypeCheck must be Any type:\n", inst->Dump(&std::cerr)));
2089 ASSERT_DO(inst->GetInput(1).GetInst()->IsSaveState(),
2090 (std::cerr << "Second input in AnyTypeCheck must be SaveState:\n", inst->Dump(&std::cerr)));
2091 }
2092
2093 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
2094 #define VisitBinaryShiftedRegister(opc) \
2095 void GraphChecker::Visit##opc(GraphVisitor *v, Inst *inst) \
2096 { \
2097 CheckBinaryOperationWithShiftedOperandTypes( \
2098 v, inst, inst->GetOpcode() != Opcode::AddSR && inst->GetOpcode() != Opcode::SubSR); \
2099 }
2100
2101 VisitBinaryShiftedRegister(AddSR) VisitBinaryShiftedRegister(SubSR) VisitBinaryShiftedRegister(AndSR)
2102 VisitBinaryShiftedRegister(OrSR) VisitBinaryShiftedRegister(XorSR) VisitBinaryShiftedRegister(AndNotSR)
2103 VisitBinaryShiftedRegister(OrNotSR) VisitBinaryShiftedRegister(XorNotSR)
2104 #undef VisitBinaryShiftedRegister
2105
2106 void GraphChecker::VisitNegSR([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst)
2107 {
2108 ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
2109 (std::cerr << "NegSR must have integer type\n", inst->Dump(&std::cerr)));
2110 CheckUnaryOperationTypes(inst);
2111 [[maybe_unused]] auto shift_type = static_cast<UnaryShiftedRegisterOperation *>(inst)->GetShiftType();
2112 ASSERT_DO(shift_type != ShiftType::INVALID_SHIFT && shift_type != ShiftType::ROR,
2113 (std::cerr << "Operation has invalid shift type\n", inst->Dump(&std::cerr)));
2114 }
2115
2116 } // namespace panda::compiler
2117