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