• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "graph_cloner.h"
17 #include "optimizer/analysis/linear_order.h"
18 #include "inst_checker_gen.h"
19 #include "graph_checker.h"
20 
21 namespace panda::compiler {
22 
GraphChecker(Graph * graph)23 GraphChecker::GraphChecker(Graph *graph)
24 {
25     PreCloneChecks(graph);
26     graph_ = GraphCloner(graph, GetAllocator(), GetLocalAllocator()).CloneGraph();
27     GetGraph()->GetPassManager()->SetCheckMode(true);
28 }
29 
PreCloneChecks(Graph * graph)30 void GraphChecker::PreCloneChecks(Graph *graph)
31 {
32     UserInputCheck(graph);
33 }
34 
UserInputCheck(Graph * graph)35 void GraphChecker::UserInputCheck(Graph *graph)
36 {
37     for (auto block : graph->GetVectorBlocks()) {
38         if (block == nullptr) {
39             continue;
40         }
41         for (auto inst : block->AllInsts()) {
42             auto u = inst->GetFirstUser();
43             ASSERT(u == nullptr || u->GetPrev() == nullptr);
44             while (u != nullptr) {
45                 ASSERT(u->GetNext() == nullptr || u->GetNext()->GetPrev() == u);
46                 u = u->GetNext();
47             }
48             for (auto &user : inst->GetUsers()) {
49                 [[maybe_unused]] auto user_inst = user.GetInst();
50                 ASSERT(user_inst->GetBasicBlock() != nullptr);
51                 ASSERT_DO(CheckInstHasInput(user_inst, inst), std::cerr << "Instruction is not an input to its user\n"
52                                                                         << "input: " << *inst << std::endl
53                                                                         << "user:  " << *user_inst << std::endl);
54             }
55             for (auto &input : inst->GetInputs()) {
56                 [[maybe_unused]] auto input_inst = input.GetInst();
57                 ASSERT(input_inst != nullptr && input_inst->GetBasicBlock() != nullptr);
58                 ASSERT_DO(CheckInstHasUser(input_inst, inst), std::cerr << "Instruction is not a user to its input:\n"
59                                                                         << "user: " << *inst << std::endl
60                                                                         << "input:  " << *input_inst << std::endl);
61             }
62             // Check `require_state` flag
63             auto it = std::find_if(inst->GetInputs().begin(), inst->GetInputs().end(),
64                                    [](Input input) { return input.GetInst()->IsSaveState(); });
65             [[maybe_unused]] bool has_save_state = (it != inst->GetInputs().end());
66             ASSERT_DO(inst->RequireState() == has_save_state,
67                       std::cerr << "Incorrect 'require_state' flag in the inst: " << *inst);
68             if (inst->RequireState()) {
69                 ASSERT(it->GetInst() == inst->GetSaveState());
70             }
71         }
72     }
73 }
74 
Check()75 void GraphChecker::Check()
76 {
77     if (!GetGraph()->IsDynamicMethod()) {
78         InstChecker::Run(GetGraph());
79     }
80 
81 #ifndef NDEBUG
82     if (GetGraph()->IsAnalysisValid<DominatorsTree>()) {
83         CheckDomTree();
84     } else {
85         GetGraph()->RunPass<DominatorsTree>();
86     }
87     if (GetGraph()->IsAnalysisValid<LoopAnalyzer>()) {
88         CheckLoopAnalysis();
89     } else {
90         GetGraph()->RunPass<LoopAnalyzer>();
91     }
92     CheckStartBlock();
93     CheckEndBlock();
94     size_t blocks_count = 0;
95     size_t blocks_id = -1;
96     for (auto block : GetGraph()->GetVectorBlocks()) {
97         ++blocks_id;
98         if (block == nullptr) {
99             continue;
100         }
101         ASSERT_PRINT(block->GetGraph() == GetGraph(), "Block linked to incorrect graph");
102         ASSERT_PRINT(block->GetId() == blocks_id, "Block ID must be equal to its ID in graph vector");
103         CheckBlock(block);
104         blocks_count++;
105     }
106     ASSERT_PRINT(blocks_count == GetGraph()->GetBlocksRPO().size(), "There is disconnected block");
107     CheckLoops();
108     // Visit graph to check instructions types
109     CheckGraph();
110     // Check that call.Inlined and Return.Inlined in correct order
111     // and check that savestate has correct link to call.inlined.
112     CheckCallReturnInlined();
113     if (NeedCheckSaveState()) {
114         // Check that objects in stack.
115         CheckSaveStateInputs();
116         // Check that between savestate and it's runtime call user have not reference insts.
117         CheckSaveStatesWithRuntimeCallUsers();
118     }
119 
120 #endif  // !NDEBUG
121 }
122 
123 #ifndef NDEBUG
NeedCheckSaveState()124 bool GraphChecker::NeedCheckSaveState()
125 {
126     return !GetGraph()->IsBytecodeOptimizer() && GetGraph()->GetParentGraph() == nullptr &&
127            GetGraph()->IsInliningComplete() && !GetGraph()->IsSchedulerComplete();
128 }
129 #endif  // !NDEBUG
130 
CheckBlock(BasicBlock * block)131 void GraphChecker::CheckBlock([[maybe_unused]] BasicBlock *block)
132 {
133 #ifndef NDEBUG
134     CheckControlFlow(block);
135     CheckDataFlow(block);
136     for (auto phi_inst : block->PhiInsts()) {
137         CheckPhiInputs(phi_inst);
138     }
139     if (!GetGraph()->IsLowLevelInstructionsEnabled() && !GetGraph()->IsDynamicMethod()) {
140         CheckNoLowLevel(block);
141     }
142     if (!block->IsEndBlock() && !block->IsStartBlock()) {
143         CheckBlockEdges(*block);
144     }
145     if (block->IsTryBegin()) {
146         CheckTryBeginBlock(*block);
147     }
148     if (block->NeedsJump()) {
149         CheckJump(*block);
150     }
151 #endif  // !NDEBUG
152 }
153 
CheckControlFlow(BasicBlock * block)154 void GraphChecker::CheckControlFlow(BasicBlock *block)
155 {
156     auto num_succs = block->GetSuccsBlocks().size();
157     ASSERT_PRINT(block->IsEndBlock() || block->IsTryBegin() || block->IsTryEnd() ||
158                      (num_succs > 0 && num_succs <= MAX_SUCCS_NUM),
159                  "Non-end block and non-try-begin block should have 1 or 2 successesors");
160 
161     for ([[maybe_unused]] auto pred : block->GetPredsBlocks()) {
162         ASSERT_PRINT(CheckBlockHasSuccessor(pred, block), "Block is not a successor to its predecessor");
163     }
164     for ([[maybe_unused]] auto succ : block->GetSuccsBlocks()) {
165         ASSERT_PRINT(CheckBlockHasPredecessor(succ, block), "Block is not a predecessor to its successor");
166     }
167 
168     if (num_succs == MAX_SUCCS_NUM) {
169         ASSERT_PRINT(block->GetSuccessor(0) != block->GetSuccessor(1), "Wrong CFG - block with two same successors");
170     }
171 
172     for ([[maybe_unused]] auto phi : block->PhiInsts()) {
173         ASSERT_DO(phi->GetInputsCount() == block->GetPredsBlocks().size(),
174                   std::cerr << phi->GetInputsCount() << " " << block->GetPredsBlocks().size()
175                             << "Incorrect phi's inputs count" << *phi);
176     }
177 }
178 
CheckDataFlow(BasicBlock * block)179 void GraphChecker::CheckDataFlow(BasicBlock *block)
180 {
181     auto graph = block->GetGraph();
182     for (auto inst : block->AllInsts()) {
183         ASSERT_DO(inst->GetBasicBlock() == block,
184                   std::cerr << "Instruction block's pointer isn't correct" << *inst << std::endl);
185         if (block != graph->GetStartBlock()) {
186             ASSERT_DO(inst->GetOpcode() != Opcode::Parameter,
187                       std::cerr << "Not entry block can't contain Parameter instructions" << *inst << std::endl);
188         }
189         if (inst->GetPrev() == nullptr) {
190             ASSERT_PRINT(*block->AllInsts().begin() == inst, "First block instruction isn't correct");
191         }
192         if (inst->GetNext() == nullptr) {
193             ASSERT_PRINT(*block->AllInstsSafeReverse().begin() == inst, "Last block instruction isn't correct");
194         }
195         // Inst with reference type must have no_cse and no_hoist flags.
196         if (inst->GetType() == DataType::REFERENCE) {
197             ASSERT(inst->IsNotCseApplicable());
198             ASSERT(inst->IsNotHoistable());
199         }
200         for ([[maybe_unused]] auto &user : inst->GetUsers()) {
201             auto user_inst = user.GetInst();
202             ASSERT_DO(CheckInstHasInput(user_inst, inst), std::cerr << "Instruction is not an input to its user\n"
203                                                                     << "input: " << *inst << std::endl
204                                                                     << "user:  " << *user_inst << std::endl);
205             if (!user_inst->IsPhi() && !user_inst->IsCatchPhi()) {
206                 ASSERT_DO(inst->IsDominate(user_inst) ||
207                               (GetGraph()->IsRegAllocApplied() &&
208                                IsTryCatchDomination(inst->GetBasicBlock(), user_inst->GetBasicBlock())),
209                           std::cerr << "Instruction doesn't dominate its user\n"
210                                     << "input: " << *inst << std::endl
211                                     << "user:  " << *user_inst << std::endl);
212             }
213             auto arch = graph->GetArch();
214             if (DataType::Is32Bits(inst->GetType(), arch)) {
215                 if (!user_inst->HasType()) {
216                     continue;
217                 }
218                 [[maybe_unused]] auto user_input_type = user_inst->GetInputType(user.GetIndex());
219                 [[maybe_unused]] bool ref_to_ptr =
220                     user_input_type == DataType::POINTER && inst->GetType() == DataType::REFERENCE;
221                 ASSERT_DO(DataType::Is32Bits(user_input_type, arch) || ref_to_ptr ||
222                               (block->GetGraph()->IsDynamicMethod() && user_input_type == DataType::ANY),
223                           std::cerr << "Undefined high-part of input instruction for its user\n"
224                                     << "input: " << *inst << std::endl
225                                     << "user:  " << *user_inst << std::endl);
226             }
227         }
228         for ([[maybe_unused]] auto input : inst->GetInputs()) {
229             ASSERT_DO(CheckInstHasUser(input.GetInst(), inst), std::cerr << "Instruction is not a user to its input:\n"
230                                                                          << "input: " << *input.GetInst() << std::endl
231                                                                          << "user:  " << *inst << std::endl);
232         }
233     }
234 }
235 
CheckCallReturnInlined()236 void GraphChecker::CheckCallReturnInlined()
237 {
238     ArenaStack<Inst *> inlined_calles(GetLocalAllocator()->Adapter());
239     if (GetGraph()->HasEndBlock()) {
240         for (auto block : GetGraph()->GetEndBlock()->GetPredsBlocks()) {
241             if (block->IsTryEnd()) {
242                 continue;
243             }
244         }
245     }
246     ASSERT(inlined_calles.empty());
247 #ifndef NDEBUG
248     // avoid check after ir_builder in inline pass
249     if (!GetGraph()->IsInliningComplete() || GetGraph()->GetParentGraph() != nullptr) {
250         return;
251     }
252 #endif
253 }
254 
CheckStartBlock()255 void GraphChecker::CheckStartBlock()
256 {
257     [[maybe_unused]] Inst *has_nullptr = nullptr;
258     [[maybe_unused]] int32_t last_num = -1;
259     ASSERT(GetGraph()->GetStartBlock());
260     ASSERT_PRINT(GetGraph()->GetStartBlock()->GetPredsBlocks().empty(), "Start block can't have predecessors");
261     ASSERT_PRINT(GetGraph()->GetStartBlock()->GetSuccsBlocks().size() == 1, "Start block should have one successor");
262     for (auto inst : GetGraph()->GetStartBlock()->AllInsts()) {
263         [[maybe_unused]] Opcode opc = inst->GetOpcode();
264         ASSERT_DO(
265             opc == Opcode::Constant || opc == Opcode::Parameter || opc == Opcode::SpillFill,
266             std::cerr
267                 << "Entry block can contain Constant, Parameter, NullPtr, SafePoint, NOP or SpillFill instructions"
268                 << *inst << std::endl);
269         if (opc == Opcode::Parameter) {
270             auto arg_num = inst->CastToParameter()->GetArgNumber();
271             ASSERT_DO(
272                 last_num < static_cast<int32_t>(arg_num),
273                 std::cerr << "The argument number in the parameter must be greater than that of the previous parameter"
274                           << *inst << std::endl);
275             last_num = static_cast<int32_t>(arg_num);
276         }
277     }
278 }
279 
CheckEndBlock()280 void GraphChecker::CheckEndBlock()
281 {
282     if (!GetGraph()->HasEndBlock()) {
283         ASSERT_PRINT(HasOuterInfiniteLoop(), "Graph without infinite loops should have end block");
284         return;
285     }
286     ASSERT_PRINT(GetGraph()->GetEndBlock()->GetSuccsBlocks().empty(), "End block can't have successors");
287     [[maybe_unused]] auto iter = GetGraph()->GetEndBlock()->Insts();
288     ASSERT_PRINT(iter.begin() == iter.end(), "End block can't have instructions");
289 }
290 
CheckGraph()291 void GraphChecker::CheckGraph()
292 {
293     size_t num_inst = GetGraph()->GetCurrentInstructionId();
294     ArenaVector<bool> inst_vec(num_inst, GetLocalAllocator()->Adapter());
295     for (auto &bb : GetGraph()->GetBlocksRPO()) {
296         for (auto inst : bb->AllInsts()) {
297             auto id = inst->GetId();
298             ASSERT_DO(id < num_inst,
299                       (std::cerr << "Instruction ID must be less than graph instruction counter: " << num_inst << "\n",
300                        inst->Dump(&std::cerr)));
301             ASSERT_DO(!inst_vec[id],
302                       (std::cerr << "Instruction with same Id already exists:\n", inst->Dump(&std::cerr)));
303             inst_vec[id] = true;
304             ASSERT_DO(GetGraph()->IsDynamicMethod() || inst->GetType() != DataType::ANY,
305                       (std::cerr << "The type ANY is supported only for dynamic languages\n", inst->Dump(&std::cerr)));
306             ASSERT_DO(inst->SupportsMode(GetGraph()->GetCompilerMode()),
307                       (std::cerr << "Instruction used in wrong mode\n", inst->Dump(&std::cerr)));
308             VisitInstruction(inst);
309         }
310     }
311 }
312 
CheckPhiInputs(Inst * phi_inst)313 void GraphChecker::CheckPhiInputs(Inst *phi_inst)
314 {
315     for (size_t index = 0; index < phi_inst->GetInputsCount(); ++index) {
316         [[maybe_unused]] auto pred = phi_inst->CastToPhi()->GetPhiInputBb(index);
317         [[maybe_unused]] auto input_bb = phi_inst->CastToPhi()->GetPhiInput(pred)->GetBasicBlock();
318         ASSERT_DO(input_bb->IsDominate(pred) || IsTryCatchDomination(input_bb, pred),
319                   (std::cerr
320                    << "Block where phi-input is located should dominate predecessor block corresponding to this input\n"
321                    << "Block " << input_bb->GetId() << " should dominate " << pred->GetId() << std::endl
322                    << *phi_inst));
323     }
324 }
325 
CheckInstRegUsageSaved(const Inst * inst,Register reg) const326 bool GraphChecker::CheckInstRegUsageSaved(const Inst *inst, Register reg) const
327 {
328     if (reg == ACC_REG_ID) {
329         return true;
330     }
331     auto graph = inst->GetBasicBlock()->GetGraph();
332     // Empty vector regs mask means we are using dynamic general regs set.
333     if (DataType::IsFloatType(inst->GetType()) && !graph->GetUsedRegs<DataType::FLOAT64>()->empty()) {
334         return graph->GetUsedRegs<DataType::FLOAT64>()->at(reg);
335     }
336     return graph->GetUsedRegs<DataType::INT64>()->at(reg);
337 }
338 
checkSpillFillMultiple(const compiler::Inst * inst)339 [[maybe_unused]] static bool checkSpillFillMultiple(const compiler::Inst *inst)
340 {
341     switch (inst->GetOpcode()) {
342         case Opcode::Parameter:
343             return false;
344         default:
345             return true;
346     }
347 }
348 
CheckNoLowLevel(BasicBlock * block)349 void GraphChecker::CheckNoLowLevel(BasicBlock *block)
350 {
351     for ([[maybe_unused]] auto inst : block->Insts()) {
352         ASSERT_DO(!inst->IsLowLevel(), inst->Dump(&std::cerr));
353     }
354 }
355 
MarkBlocksInLoop(Loop * loop,Marker mrk)356 void GraphChecker::MarkBlocksInLoop(Loop *loop, Marker mrk)
357 {
358     ASSERT(loop->IsIrreducible() || loop->IsRoot() || loop->GetHeader() != nullptr);
359     ASSERT(loop->IsIrreducible() || loop->IsRoot() || loop->IsTryCatchLoop() || loop->GetPreHeader() != nullptr);
360     // Mark blocks and check if marker was not set before
361     for ([[maybe_unused]] auto block : loop->GetBlocks()) {
362         ASSERT(!block->SetMarker(mrk));
363     }
364 
365     for (auto inner : loop->GetInnerLoops()) {
366         MarkBlocksInLoop(inner, mrk);
367     }
368 }
369 
CheckBlockHasPredecessor(BasicBlock * block,BasicBlock * predecessor)370 bool GraphChecker::CheckBlockHasPredecessor(BasicBlock *block, BasicBlock *predecessor)
371 {
372     ASSERT(block != nullptr && predecessor != nullptr);
373     for (auto pred : block->GetPredsBlocks()) {
374         if (pred == predecessor) {
375             return true;
376         }
377     }
378     return false;
379 }
380 
CheckBlockHasSuccessor(BasicBlock * block,BasicBlock * successor)381 bool GraphChecker::CheckBlockHasSuccessor(BasicBlock *block, BasicBlock *successor)
382 {
383     ASSERT(block != nullptr && successor != nullptr);
384     for (auto succ : block->GetSuccsBlocks()) {
385         if (succ == successor) {
386             return true;
387         }
388     }
389     return false;
390 }
391 
CheckLoopHasSafePoint(Loop * loop)392 void GraphChecker::CheckLoopHasSafePoint(Loop *loop)
393 {
394     ASSERT_DO(loop->IsTryCatchLoop() || loop->IsIrreducible(),
395               std::cerr << "Loop " << loop->GetId() << " must have safepoint\n");
396     for (auto inner : loop->GetInnerLoops()) {
397         CheckLoopHasSafePoint(inner);
398     }
399 }
400 
CheckLoops()401 void GraphChecker::CheckLoops()
402 {
403     ASSERT(GetGraph()->GetAnalysis<LoopAnalyzer>().IsValid());
404     ASSERT(GetGraph()->GetRootLoop() != nullptr);
405     ASSERT(GetGraph()->GetRootLoop()->IsRoot());
406     ASSERT(GetGraph()->GetRootLoop()->GetHeader() == nullptr);
407     ASSERT(GetGraph()->GetRootLoop()->GetPreHeader() == nullptr);
408     auto root_loop = GetGraph()->GetRootLoop();
409     auto mrk = GetGraph()->NewMarker();
410     MarkBlocksInLoop(root_loop, mrk);
411 
412     for ([[maybe_unused]] auto block : GetGraph()->GetBlocksRPO()) {
413         [[maybe_unused]] auto loop = block->GetLoop();
414         ASSERT(loop != nullptr);
415         ASSERT(block->IsMarked(mrk));
416         if (block->IsLoopHeader()) {
417             [[maybe_unused]] auto preds = block->GetPredsBlocks();
418             for ([[maybe_unused]] auto pred : preds) {
419                 ASSERT(pred->GetLoop() != loop || loop->HasBackEdge(pred));
420             }
421 
422             if (!loop->IsIrreducible()) {
423                 for ([[maybe_unused]] auto back : loop->GetBackEdges()) {
424                     ASSERT(std::find(preds.begin(), preds.end(), back) != preds.end());
425                 }
426             }
427         } else {
428             ASSERT(!block->IsOsrEntry());
429         }
430     }
431     GetGraph()->EraseMarker(mrk);
432     if (options.IsCompilerUseSafepoint() && GetGraph()->SupportManagedCode()) {
433         for (auto inner : root_loop->GetInnerLoops()) {
434             CheckLoopHasSafePoint(inner);
435         }
436     }
437 }
438 
CheckDomTree()439 void GraphChecker::CheckDomTree()
440 {
441     ASSERT(GetGraph()->GetAnalysis<DominatorsTree>().IsValid());
442     ArenaVector<BasicBlock *> dominators(GetGraph()->GetVectorBlocks().size(), GetLocalAllocator()->Adapter());
443     for (auto block : GetGraph()->GetBlocksRPO()) {
444         dominators[block->GetId()] = block->GetDominator();
445     }
446     // Rebuild dom-tree
447     GetGraph()->InvalidateAnalysis<DominatorsTree>();
448     GetGraph()->RunPass<DominatorsTree>();
449 
450     for ([[maybe_unused]] auto block : GetGraph()->GetBlocksRPO()) {
451         ASSERT_DO(dominators[block->GetId()] == block->GetDominator(),
452                   std::cerr << "Basic block with id " << block->GetId() << " has incorrect dominator with id "
453                             << dominators[block->GetId()]->GetId() << std::endl
454                             << "Correct dominator must be block with id " << block->GetDominator()->GetId() << std::endl
455                             << "Note: basic blocks' ids in the original graph and in the cloned graph can be different"
456                             << std::endl);
457     }
458 }
459 
CheckLoopAnalysis()460 void GraphChecker::CheckLoopAnalysis()
461 {
462     // Save current loop info
463     ArenaUnorderedMap<BasicBlock *, Loop *> loops(GetLocalAllocator()->Adapter());
464     [[maybe_unused]] auto root_loop = GetGraph()->GetRootLoop();
465     for (auto block : GetGraph()->GetBlocksRPO()) {
466         if (block->IsLoopHeader()) {
467             loops.emplace(block, block->GetLoop());
468         }
469     }
470     // Build new loop info and compare with saved one
471     GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
472     GetGraph()->RunPass<LoopAnalyzer>();
473     ASSERT_PRINT(*root_loop == *GetGraph()->GetRootLoop(), "Root loop is incorrect\n");
474     for (auto &[block, loop] : loops) {
475         auto expected_loop = block->GetLoop();
476         // An irreducible loop can have different heads, depending on the order of traversal
477         if (loop->IsIrreducible()) {
478             ASSERT(expected_loop->IsIrreducible());
479             continue;
480         }
481         ASSERT(block->IsLoopHeader());
482         if (loop == nullptr || expected_loop == nullptr) {
483             UNREACHABLE();
484             return;
485         }
486         ASSERT_DO(*loop == *expected_loop, std::cerr << "Loop " << loop->GetId() << " is incorrect\n");
487     }
488 }
489 
490 /**
491  * Check that there is root's inner loop without exit-points
492  */
HasOuterInfiniteLoop()493 bool GraphChecker::HasOuterInfiniteLoop()
494 {
495     const auto &loops = GetGraph()->GetRootLoop()->GetInnerLoops();
496     return std::find_if(loops.begin(), loops.end(), [](const Loop *loop) { return loop->IsInfinite(); }) != loops.end();
497 }
498 
CheckInstHasInput(Inst * inst,Inst * input)499 bool GraphChecker::CheckInstHasInput(Inst *inst, Inst *input)
500 {
501     ASSERT(inst != nullptr && input != nullptr);
502     ASSERT(input->GetBasicBlock() != nullptr);
503     ASSERT(input->GetBasicBlock()->GetGraph() != nullptr);
504     for (auto node : inst->GetInputs()) {
505         if (node.GetInst() == input) {
506             return true;
507         }
508     }
509     return false;
510 }
511 
CheckInstHasUser(Inst * inst,Inst * user)512 bool GraphChecker::CheckInstHasUser(Inst *inst, Inst *user)
513 {
514     ASSERT(inst != nullptr && user != nullptr);
515     ASSERT(user->GetBasicBlock() != nullptr);
516     ASSERT(user->GetBasicBlock()->GetGraph() != nullptr);
517     for (auto &node : inst->GetUsers()) {
518         if (node.GetInst() == user) {
519             return true;
520         }
521     }
522     return false;
523 }
524 
CheckBlockEdges(const BasicBlock & block)525 void GraphChecker::CheckBlockEdges(const BasicBlock &block)
526 {
527     [[maybe_unused]] auto last_inst_in_block = block.GetLastInst();
528     if (block.GetSuccsBlocks().size() > 1) {
529         ASSERT_PRINT(!block.IsEmpty() || block.IsTryEnd(),
530                      "Block with 2 successors have no instructions or should be try-end");
531         ASSERT_PRINT(block.IsTryBegin() || block.IsTryEnd() || last_inst_in_block->IsControlFlow(),
532                      "Last instruction must be control flow in block with 2 successors");
533     } else if (block.GetSuccsBlocks().size() == 1) {
534         if (block.GetSuccsBlocks()[0]->IsEndBlock()) {
535             if (block.IsEmpty()) {
536                 ASSERT(block.IsTryEnd());
537                 return;
538             }
539             auto last_inst = block.GetLastInst();
540             [[maybe_unused]] auto opc = last_inst->GetOpcode();
541             ASSERT_PRINT(last_inst->GetFlag(inst_flags::TERMINATOR),
542                          "Last instruction in block before exit-block must be Return or Throw instruction.");
543         }
544     }
545 }
546 
CheckTryBeginBlock(const BasicBlock & block)547 void GraphChecker::CheckTryBeginBlock(const BasicBlock &block)
548 {
549     ASSERT(block.IsTryBegin());
550     auto try_inst_it = std::find_if(block.AllInsts().begin(), block.AllInsts().end(),
551                                     [](Inst *inst) { return inst->GetOpcode() == Opcode::Try; });
552     ASSERT_PRINT(try_inst_it != block.AllInsts().end(), "Try-begin basic block should contain try-instructions");
553     [[maybe_unused]] auto try_inst = (*try_inst_it)->CastToTry();
554     for ([[maybe_unused]] auto succ_index : *try_inst->GetCatchEdgeIndexes()) {
555         ASSERT_PRINT(succ_index < block.GetSuccsBlocks().size(),
556                      "Try instruction holds incorrect try-begin block successor number");
557     }
558 }
559 
CheckJump(const BasicBlock & block)560 void GraphChecker::CheckJump(const BasicBlock &block)
561 {
562     ASSERT(GetGraph()->IsRegAllocApplied());
563     ASSERT(GetGraph()->IsAnalysisValid<LinearOrder>());
564     if (block.IsIfBlock()) {
565         const auto &blocks_vector = GetGraph()->GetBlocksLinearOrder();
566         auto if_block_it = std::find(blocks_vector.begin(), blocks_vector.end(), &block);
567         ASSERT(if_block_it != blocks_vector.end());
568         auto block_after_if = std::next(if_block_it);
569         if (block_after_if != blocks_vector.end()) {
570             ASSERT_PRINT(*block_after_if != (*if_block_it)->GetFalseSuccessor(),
571                          "`If-block` with immediate `false`-successor shouldn't have `JumpFlag`");
572             ASSERT_PRINT(*block_after_if != (*if_block_it)->GetTrueSuccessor(),
573                          "`true`-successor should be replaced with `false`-successor");
574         }
575     }
576     [[maybe_unused]] auto num_succs = block.GetSuccsBlocks().size();
577     ASSERT_PRINT(num_succs == 1 || block.IsTryBegin() || block.IsTryEnd() || block.IsIfBlock(),
578                  "Basic block with Jump must have 1 successor or should be try-begin or if block");
579 }
580 
581 /**
582  * Regalloc propagates catch-phi's inputs to the users and can broke user's domination. In this case:
583  * - input_block should be placed inside try block;
584  * - try-begin block should dominate user_block;
585  *
586  * [try-begin]----------\
587  *     |                |
588  * [input_block]        |
589  *     |                |
590  * [try-end]----------->|
591  *                      |
592  *                [catch-begin]
593  *                      |
594  *                [user_block]
595  */
IsTryCatchDomination(const BasicBlock * input_block,const BasicBlock * user_block) const596 bool GraphChecker::IsTryCatchDomination(const BasicBlock *input_block, const BasicBlock *user_block) const
597 {
598     ASSERT(GetGraph()->IsRegAllocApplied());
599     if (input_block->IsTry()) {
600         auto blocks = GetGraph()->GetTryBeginBlocks();
601         auto it =
602             std::find_if(blocks.begin(), blocks.end(), [user_block](auto &bb) { return bb->IsDominate(user_block); });
603         return it != blocks.end();
604     }
605     return false;
606 }
607 
IsObjectCheckDisabledForOpcode(const Inst * inst)608 bool IsObjectCheckDisabledForOpcode(const Inst *inst)
609 {
610     return inst->IsConst();
611 }
612 
CheckSaveStatesWithRuntimeCallUsers()613 void GraphChecker::CheckSaveStatesWithRuntimeCallUsers()
614 {
615 #ifndef NDEBUG
616     for (auto &block : GetGraph()->GetBlocksRPO()) {
617         for (const auto &ss : block->AllInsts()) {
618             if (ss->GetOpcode() != Opcode::SaveState) {
619                 continue;
620             }
621         }
622     }
623 #endif
624 }
625 
PrepareUsers(Inst * inst,ArenaVector<User * > * users)626 void PrepareUsers(Inst *inst, ArenaVector<User *> *users)
627 {
628     for (auto &user : inst->GetUsers()) {
629         users->push_back(&user);
630     }
631 }
632 
CheckSaveStateInputs()633 void GraphChecker::CheckSaveStateInputs()
634 {
635 #ifndef NDEBUG
636     ArenaVector<User *> users(GetLocalAllocator()->Adapter());
637     for (auto &block : GetGraph()->GetBlocksRPO()) {
638         for (const auto &inst : block->AllInsts()) {
639             if (IsObjectCheckDisabledForOpcode(inst)) {
640                 continue;
641             }
642             // skip phi which all inputs is disabled
643             if (inst->GetOpcode() == Opcode::Phi) {
644                 bool skip_flag = true;
645                 for (const auto &input : inst->GetInputs()) {
646                     skip_flag &= IsObjectCheckDisabledForOpcode(input.GetInst());
647                 }
648                 if (skip_flag) {
649                     continue;
650                 }
651             }
652 
653             PrepareUsers(inst, &users);
654 
655             auto object_visited = GetGraph()->NewMarker();
656             auto osr_visited = GetGraph()->NewMarker();
657             for (auto &it : users) {
658                 auto user = it->GetInst();
659                 // For Phi we need to check only pass between object and phi
660                 if (user->IsPhi() || user->IsCatchPhi()) {
661                     continue;
662                 }
663                 // Virtual register can be overwrite
664                 if (user->IsSaveState()) {
665                     continue;
666                 }
667                 if (inst->GetType() == DataType::REFERENCE) {
668                     CheckObjectRec(inst, user, user->GetBasicBlock(), user->GetPrev(), object_visited);
669                 }
670                 CheckSaveStateOsrRec(inst, user, user->GetBasicBlock(), osr_visited);
671             }
672             GetGraph()->EraseMarker(object_visited);
673             GetGraph()->EraseMarker(osr_visited);
674             users.clear();
675         }
676     }
677 #endif
678 }
679 
FindObjectInSaveState(const Inst * object,Inst * ss) const680 void GraphChecker::FindObjectInSaveState(const Inst *object, Inst *ss) const
681 {
682     if (object != nullptr && ss != nullptr) {
683         std::cerr << "Object not found in the SaveState: " << std::endl
684                   << *object << std::endl
685                   << " " << *ss << std::endl;
686     }
687     UNREACHABLE();
688 }
689 
CheckObjectRec(const Inst * object,const Inst * user,const BasicBlock * block,Inst * start_from,Marker visited) const690 void GraphChecker::CheckObjectRec(const Inst *object, const Inst *user, const BasicBlock *block, Inst *start_from,
691                                   Marker visited) const
692 {
693     if (start_from != nullptr) {
694         auto it = InstSafeIterator<IterationType::ALL, IterationDirection::BACKWARD>(*block, start_from);
695         for (; it != block->AllInstsSafeReverse().end(); ++it) {
696             auto inst = *it;
697             if (inst == nullptr) {
698                 break;
699             }
700             if (inst->SetMarker(visited)) {
701                 return;
702             }
703             if (inst == object || inst == user) {
704                 return;
705             }
706         }
707     }
708     for (auto pred : block->GetPredsBlocks()) {
709         // Catch-begin block has edge from try-end block, and all try-blocks should be visited from this edge.
710         // `object` can be placed inside try-block - after try-begin, so that visiting try-begin is wrong
711         if (block->IsCatchBegin() && pred->IsTryBegin()) {
712             continue;
713         }
714         CheckObjectRec(object, user, pred, pred->GetLastInst(), visited);
715     }
716 }
717 
CheckSaveStateOsrRec(const Inst * inst,const Inst * user,BasicBlock * block,Marker visited)718 void GraphChecker::CheckSaveStateOsrRec(const Inst *inst, const Inst *user, BasicBlock *block, Marker visited)
719 {
720     if (block->SetMarker(visited)) {
721         return;
722     }
723     if (inst->GetBasicBlock() == block) {
724         return;
725     }
726     if (block->IsOsrEntry()) {
727         ASSERT(GetGraph()->IsOsrMode());
728         auto ss = block->GetFirstInst();
729         ASSERT(ss != nullptr);
730         [[maybe_unused]] auto it =
731             std::find_if(ss->GetInputs().begin(), ss->GetInputs().end(),
732                          [inst, ss](Input input) { return ss->GetDataFlowInput(input.GetInst()) == inst; });
733         ASSERT(it != ss->GetInputs().end());
734     }
735     for (auto pred : block->GetPredsBlocks()) {
736         CheckSaveStateOsrRec(inst, user, pred, visited);
737     }
738 }
739 
740 /*
741  * Visitors to check instructions types
742  */
VisitReturn(GraphVisitor * v,Inst * inst)743 void GraphChecker::VisitReturn([[maybe_unused]] GraphVisitor *v, Inst *inst)
744 {
745     [[maybe_unused]] auto op = inst->GetInputs()[0].GetInst();
746     ASSERT_DO(CheckCommonTypes(inst, op), (std::cerr << "Types of return and its input are not compatible\n return:\n",
747                                            inst->Dump(&std::cerr), std::cerr << "\n input:\n", op->Dump(&std::cerr)));
748     [[maybe_unused]] auto num_succs = inst->GetBasicBlock()->GetSuccsBlocks().size();
749     ASSERT_PRINT(num_succs == 1, "Basic block with Return must have 1 successor");
750     [[maybe_unused]] auto succ = inst->GetBasicBlock()->GetSuccsBlocks()[0];
751     ASSERT_DO(succ->IsEndBlock() || succ->IsTryEnd(),
752               std::cerr << "Basic block with Return must have end or try end block as successor:\n"
753                         << *inst << std::endl);
754 }
755 
VisitIf(GraphVisitor * v,Inst * inst)756 void GraphChecker::VisitIf([[maybe_unused]] GraphVisitor *v, Inst *inst)
757 {
758     [[maybe_unused]] auto num_succs = inst->GetBasicBlock()->GetSuccsBlocks().size();
759     ASSERT_PRINT(num_succs == MAX_SUCCS_NUM, "Basic block with If must have 2 successesors");
760 
761     [[maybe_unused]] auto op1 = inst->GetInputs()[0].GetInst();
762     [[maybe_unused]] auto op2 = inst->GetInputs()[1].GetInst();
763     for (size_t i = 0; i < inst->GetInputsCount(); i++) {
764         ASSERT_DO(inst->GetInputType(i) != DataType::NO_TYPE,
765                   std::cerr << "Source operand type is not set: " << *inst << std::endl);
766     }
767     ASSERT_DO(inst->GetInputType(0) == inst->GetInputType(1),
768               std::cerr << "If has different inputs type: " << *inst << std::endl);
769     if (inst->GetInputType(0) == DataType::REFERENCE) {
770         [[maybe_unused]] auto cc = inst->CastToIf()->GetCc();
771         ASSERT_DO(cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
772                   (std::cerr << "Reference comparison in If must be CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
773         if (op1->IsConst()) {
774             ASSERT_DO(IsZeroConstant(op1), (std::cerr << "Constant reference input must be integer 0: \n",
775                                             inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
776         } else {
777             ASSERT_DO(op1->GetType() == DataType::REFERENCE, (std::cerr << "If 1st operand type is not a reference\n",
778                                                               inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
779         }
780         if (op2->IsConst()) {
781             ASSERT_DO(IsZeroConstant(op2), (std::cerr << "Constant reference input must be integer 0: \n",
782                                             inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
783         } else {
784             ASSERT_DO(op2->GetType() == DataType::REFERENCE, (std::cerr << "If 2nd operand type is not a reference\n",
785                                                               inst->Dump(&std::cerr), op2->Dump(&std::cerr)));
786         }
787     }
788 }
789 
VisitIfImm(GraphVisitor * v,Inst * inst)790 void GraphChecker::VisitIfImm([[maybe_unused]] GraphVisitor *v, Inst *inst)
791 {
792     [[maybe_unused]] auto num_succs = inst->GetBasicBlock()->GetSuccsBlocks().size();
793     ASSERT_PRINT(num_succs == MAX_SUCCS_NUM, "Basic block with IfImm must have 2 successesors");
794 
795     [[maybe_unused]] auto op1 = inst->GetInput(0).GetInst();
796     [[maybe_unused]] auto op2 = inst->CastToIfImm()->GetImm();
797     ASSERT_DO(inst->GetInputType(0) != DataType::NO_TYPE,
798               std::cerr << "Source operand type is not set: " << *inst << std::endl);
799     if (inst->GetInputType(0) == DataType::REFERENCE) {
800         [[maybe_unused]] auto cc = inst->CastToIfImm()->GetCc();
801         ASSERT_DO(cc == ConditionCode::CC_NE || cc == ConditionCode::CC_EQ,
802                   (std::cerr << "Reference comparison in IfImm must have CC_NE or CC_EQ: \n", inst->Dump(&std::cerr)));
803         if (op1->IsConst()) {
804             ASSERT_DO(IsZeroConstant(op1), (std::cerr << "Constant reference input must be integer 0: \n",
805                                             inst->Dump(&std::cerr), op1->Dump(&std::cerr)));
806         } else {
807             ASSERT_DO(op1->GetType() == DataType::REFERENCE,
808                       (std::cerr << "IfImm operand type should be here a reference: \n", inst->Dump(&std::cerr),
809                        op1->Dump(&std::cerr)));
810         }
811         ASSERT_DO(op2 == 0,
812                   (std::cerr << "Reference can be compared only with 0 immediate: \n", inst->Dump(&std::cerr)));
813     } else {
814         ASSERT_PRINT(
815             DataType::GetCommonType(op1->GetType()) == DataType::INT64 ||
816                 (static_cast<GraphChecker *>(v)->GetGraph()->IsDynamicMethod() && op1->GetType() == DataType::ANY),
817             "IfImm operand type should be here an integer");
818     }
819 }
820 }  // namespace panda::compiler
821