• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef COMPILER_OPTIMIZER_IR_GRAPH_CHECKER_H
17 #define COMPILER_OPTIMIZER_IR_GRAPH_CHECKER_H
18 
19 #include "compiler_options.h"
20 #include "graph.h"
21 #include "graph_visitor.h"
22 #include "optimizer/analysis/dominators_tree.h"
23 #include "optimizer/analysis/rpo.h"
24 #include "optimizer/analysis/loop_analyzer.h"
25 #include "optimizer/code_generator/registers_description.h"
26 #include "optimizer/optimizations/cleanup.h"
27 #include "optimizer/optimizations/memory_coalescing.h"
28 #include <iostream>
29 
30 // ---- Below extended ASSERT and ASSERT_DO for GraphChecker ----
31 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
32 #define ASSERT_DO_EXT(cond, func) ASSERT_DO((cond), func; PrintFailedMethodAndPass();)
33 
34 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
35 #define ASSERT_DO_EXT_VISITOR(cond, func) ASSERT_DO((cond), func; PrintFailedMethodAndPassVisitor(v);)
36 
37 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
38 #define ASSERT_EXT(cond) ASSERT_DO_EXT((cond), )
39 
40 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
41 #define ASSERT_EXT_VISITOR(cond) ASSERT_DO_EXT_VISITOR((cond), )
42 
43 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
44 #define ASSERT_EXT_PRINT(cond, message) \
45     ASSERT_DO((cond), std::cerr << (message) << std::endl; PrintFailedMethodAndPass();)
46 
47 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
48 #define ASSERT_EXT_PRINT_VISITOR(cond, message) \
49     ASSERT_DO((cond), std::cerr << (message) << std::endl; PrintFailedMethodAndPassVisitor(v);)
50 // --------------------------------------------------------------
51 
52 namespace ark::compiler {
53 inline std::ostream &operator<<(std::ostream &os, const std::initializer_list<Opcode> &opcs)
54 {
55     os << "[ ";
56     for (auto opc : opcs) {
57         os << GetOpcodeString(opc) << " ";
58     }
59     os << "]";
60     return os;
61 }
62 
63 class GraphChecker : public GraphVisitor {
64 public:
65     explicit GraphChecker(Graph *graph, const char *passName);
66     explicit GraphChecker(Graph *graph);
67 
~GraphChecker()68     ~GraphChecker() override
69     {
70         GetGraph()->GetPassManager()->SetCheckMode(false);
71     }
72 
73     NO_COPY_SEMANTIC(GraphChecker);
74     NO_MOVE_SEMANTIC(GraphChecker);
75 
76     void Check();
77 
78 private:
79     void PreCloneChecks(Graph *graph);
80     void UserInputCheck(Graph *graph);
81     void CheckBlock(BasicBlock *block);
82     void CheckDomTree();
83     void CheckLoopAnalysis();
84     void CheckStartBlock();
85     void CheckEndBlock();
86     void CheckControlFlow(BasicBlock *block);
87     void CheckDataFlow(BasicBlock *block);
88     void CheckUserOfInt32(BasicBlock *block, Inst *inst, User &user);
89     void CheckInstUsers(Inst *inst, [[maybe_unused]] BasicBlock *block);
90     void CheckPhiInputs(Inst *phiInst);
91     void CheckInstsRegisters(BasicBlock *block);
92     void CheckPhisRegisters(BasicBlock *block);
93     void CheckNoLowLevel(BasicBlock *block);
94     void CheckLoops();
95     void CheckGraph();
96     bool HasOuterInfiniteLoop();
97     bool CheckInstHasInput(Inst *inst, Inst *input);
98     bool CheckInstHasUser(Inst *inst, Inst *user);
99     void CheckCallReturnInlined();
100     void CheckSaveStateCaller(SaveStateInst *savestate);
101     bool FindCaller(Inst *caller, BasicBlock *domBlock, ArenaStack<Inst *> *inlinedCalls);
102     void CheckSpillFillHolder(Inst *inst);
103     bool CheckInstRegUsageSaved(const Inst *inst, Register reg) const;
104     void MarkBlocksInLoop(Loop *loop, Marker mrk);
105     bool CheckBlockHasPredecessor(BasicBlock *block, BasicBlock *predecessor);
106     bool CheckBlockHasSuccessor(BasicBlock *block, BasicBlock *successor);
107     bool BlockContainsInstruction(BasicBlock *block, Opcode opcode);
108     void CheckLoopHasSafePoint(Loop *loop);
109     void CheckBlockEdges(const BasicBlock &block);
110     void CheckTryBeginBlock(const BasicBlock &block);
111     void CheckJump(const BasicBlock &block);
112     bool IsTryCatchDomination(const BasicBlock *inputBlock, const BasicBlock *userBlock) const;
113     void CheckInputType(Inst *inst) const;
114 #ifndef NDEBUG
115     bool NeedCheckSaveState();
116     void PrepareUsers(Inst *inst, ArenaVector<User *> *users);
117     bool IsPhiSafeToSkipObjectCheck(Inst *inst, Marker visited);
118     bool IsPhiUserSafeToSkipObjectCheck(Inst *inst, Marker visited);
119     void CheckSaveStateInputs(Inst *inst, ArenaVector<User *> *users);
120 #endif  // !NDEBUG
121     void CheckSaveStateInputs();
122     void CheckSaveStatesWithRuntimeCallUsers();
123     void CheckSaveStatesWithRuntimeCallUsers(BasicBlock *block, SaveStateInst *ss);
124     void CheckSaveStateOsrRec(const Inst *inst, const Inst *user, BasicBlock *block, Marker visited);
125 
GetGraph()126     Graph *GetGraph() const
127     {
128         return graph_;
129     }
130 
GetAllocator()131     ArenaAllocator *GetAllocator()
132     {
133         return &allocator_;
134     }
135 
GetLocalAllocator()136     ArenaAllocator *GetLocalAllocator()
137     {
138         return &localAllocator_;
139     }
140 
GetBlocksToVisit()141     const ArenaVector<BasicBlock *> &GetBlocksToVisit() const override
142     {
143         return GetGraph()->GetBlocksRPO();
144     }
145 
146     /*
147      * Visitors to check instructions types
148      */
149     static void VisitMov([[maybe_unused]] GraphVisitor *v, Inst *inst);
150     static void VisitNeg([[maybe_unused]] GraphVisitor *v, Inst *inst);
151     static void VisitAbs([[maybe_unused]] GraphVisitor *v, Inst *inst);
152     static void VisitSqrt([[maybe_unused]] GraphVisitor *v, Inst *inst);
153     static void VisitAddI([[maybe_unused]] GraphVisitor *v, Inst *inst);
154     static void VisitSubI([[maybe_unused]] GraphVisitor *v, Inst *inst);
155     static void VisitMulI([[maybe_unused]] GraphVisitor *v, Inst *inst);
156     static void VisitDivI([[maybe_unused]] GraphVisitor *v, Inst *inst);
157     static void VisitModI([[maybe_unused]] GraphVisitor *v, Inst *inst);
158     static void VisitAndI([[maybe_unused]] GraphVisitor *v, Inst *inst);
159     static void VisitOrI([[maybe_unused]] GraphVisitor *v, Inst *inst);
160     static void VisitXorI([[maybe_unused]] GraphVisitor *v, Inst *inst);
161     static void VisitShlI([[maybe_unused]] GraphVisitor *v, Inst *inst);
162     static void VisitShrI([[maybe_unused]] GraphVisitor *v, Inst *inst);
163     static void VisitAShlI([[maybe_unused]] GraphVisitor *v, Inst *inst);
164     static void VisitNot([[maybe_unused]] GraphVisitor *v, Inst *inst);
165     static void VisitAdd([[maybe_unused]] GraphVisitor *v, Inst *inst);
166     static void VisitSub([[maybe_unused]] GraphVisitor *v, Inst *inst);
167     static void VisitMul([[maybe_unused]] GraphVisitor *v, Inst *inst);
168     static void VisitDiv([[maybe_unused]] GraphVisitor *v, Inst *inst);
169     static void VisitMod([[maybe_unused]] GraphVisitor *v, Inst *inst);
170     static void VisitMin([[maybe_unused]] GraphVisitor *v, Inst *inst);
171     static void VisitMax([[maybe_unused]] GraphVisitor *v, Inst *inst);
172     static void VisitShl([[maybe_unused]] GraphVisitor *v, Inst *inst);
173     static void VisitShr([[maybe_unused]] GraphVisitor *v, Inst *inst);
174     static void VisitAShr([[maybe_unused]] GraphVisitor *v, Inst *inst);
175     static void VisitAnd([[maybe_unused]] GraphVisitor *v, Inst *inst);
176     static void VisitOr([[maybe_unused]] GraphVisitor *v, Inst *inst);
177     static void VisitXor([[maybe_unused]] GraphVisitor *v, Inst *inst);
178     static void VisitLoadArray([[maybe_unused]] GraphVisitor *v, Inst *inst);
179     static void VisitLoadArrayI([[maybe_unused]] GraphVisitor *v, Inst *inst);
180     static void VisitLoadArrayPair([[maybe_unused]] GraphVisitor *v, Inst *inst);
181     static void VisitLoadObjectPair([[maybe_unused]] GraphVisitor *v, Inst *inst);
182     static void VisitLoadArrayPairI([[maybe_unused]] GraphVisitor *v, Inst *inst);
183     static void VisitLoadPairPart([[maybe_unused]] GraphVisitor *v, Inst *inst);
184     static void VisitStore([[maybe_unused]] GraphVisitor *v, Inst *inst);
185     static void VisitStoreI([[maybe_unused]] GraphVisitor *v, Inst *inst);
186     static void VisitStoreArrayPair([[maybe_unused]] GraphVisitor *v, Inst *inst);
187     static void VisitStoreObjectPair([[maybe_unused]] GraphVisitor *v, Inst *inst);
188     static void VisitStoreArrayPairI([[maybe_unused]] GraphVisitor *v, Inst *inst);
189     static void VisitStoreArray([[maybe_unused]] GraphVisitor *v, Inst *inst);
190     static void VisitStoreArrayI([[maybe_unused]] GraphVisitor *v, Inst *inst);
191     static void VisitStoreStatic([[maybe_unused]] GraphVisitor *v, Inst *inst);
192     static void VisitUnresolvedStoreStatic([[maybe_unused]] GraphVisitor *v, Inst *inst);
193     static void VisitStoreObject([[maybe_unused]] GraphVisitor *v, Inst *inst);
194     static void VisitStoreObjectDynamic([[maybe_unused]] GraphVisitor *v, Inst *inst);
195     static void VisitStoreResolvedObjectField([[maybe_unused]] GraphVisitor *v, Inst *inst);
196     static void VisitFillConstArray([[maybe_unused]] GraphVisitor *v, Inst *inst);
197     static void VisitStoreResolvedObjectFieldStatic([[maybe_unused]] GraphVisitor *v, Inst *inst);
198     static void VisitLoadStatic([[maybe_unused]] GraphVisitor *v, Inst *inst);
199     static void VisitLoadClass([[maybe_unused]] GraphVisitor *v, Inst *inst);
200     static void VisitLoadAndInitClass([[maybe_unused]] GraphVisitor *v, Inst *inst);
201     static void VisitUnresolvedLoadAndInitClass([[maybe_unused]] GraphVisitor *v, Inst *inst);
202     static void VisitGetInstanceClass([[maybe_unused]] GraphVisitor *v, Inst *inst);
203     static void VisitNewObject([[maybe_unused]] GraphVisitor *v, Inst *inst);
204     static void VisitInitObject([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
205     static void VisitInitClass([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
206     static void VisitIntrinsic([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
207     static void VisitLoadRuntimeClass([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
208     static void VisitLoadObject([[maybe_unused]] GraphVisitor *v, Inst *inst);
209     static void VisitConstant([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
210     static void VisitNullPtr([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
211     static void VisitLoadUndefined([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
212     static void VisitPhi([[maybe_unused]] GraphVisitor *v, Inst *inst);
213     static void VisitParameter([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
214     static void VisitCompare([[maybe_unused]] GraphVisitor *v, Inst *inst);
215     static void VisitCast([[maybe_unused]] GraphVisitor *v, Inst *inst);
216     static void VisitCmp([[maybe_unused]] GraphVisitor *v, Inst *inst);
217     static void VisitMonitor([[maybe_unused]] GraphVisitor *v, Inst *inst);
218     static void VisitReturn([[maybe_unused]] GraphVisitor *v, Inst *inst);
219     static void VisitReturnVoid([[maybe_unused]] GraphVisitor *v, Inst *inst);
220     static void VisitNullCheck([[maybe_unused]] GraphVisitor *v, Inst *inst);
221     static void VisitBoundsCheck([[maybe_unused]] GraphVisitor *v, Inst *inst);
222     static void VisitBoundsCheckI([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
223     static void VisitRefTypeCheck([[maybe_unused]] GraphVisitor *v, Inst *inst);
224     static void VisitNegativeCheck([[maybe_unused]] GraphVisitor *v, Inst *inst);
225     static void VisitNotPositiveCheck([[maybe_unused]] GraphVisitor *v, Inst *inst);
226     static void VisitZeroCheck([[maybe_unused]] GraphVisitor *v, Inst *inst);
227     static void VisitDeoptimizeIf([[maybe_unused]] GraphVisitor *v, Inst *inst);
228     static void VisitLenArray([[maybe_unused]] GraphVisitor *v, Inst *inst);
229     static void VisitCallVirtual([[maybe_unused]] GraphVisitor *v, Inst *inst);
230     static void VisitCallDynamic([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
231     static void VisitSaveState([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
232     static void VisitSafePoint([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
233     static void VisitSaveStateOsr([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
234     static void VisitThrow([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
235     static void VisitCheckCast([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
236     static void VisitIsInstance([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
237     static void VisitSelect([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
238     static void VisitSelectWithReference([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
239     static void VisitSelectImm([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
240     static void VisitSelectImmWithReference([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
241     static void VisitIf([[maybe_unused]] GraphVisitor *v, Inst *inst);
242     static void VisitIfImm([[maybe_unused]] GraphVisitor *v, Inst *inst);
243     static void VisitTry([[maybe_unused]] GraphVisitor *v, Inst *inst);
244     static void VisitNOP([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
245     static void VisitAndNot([[maybe_unused]] GraphVisitor *v, Inst *inst);
246     static void VisitOrNot([[maybe_unused]] GraphVisitor *v, Inst *inst);
247     static void VisitXorNot([[maybe_unused]] GraphVisitor *v, Inst *inst);
248     static void VisitMNeg([[maybe_unused]] GraphVisitor *v, Inst *inst);
249     static void VisitMAdd([[maybe_unused]] GraphVisitor *v, Inst *inst);
250     static void VisitMSub([[maybe_unused]] GraphVisitor *v, Inst *inst);
251     static void VisitAddSR(GraphVisitor *v, Inst *inst);
252     static void VisitSubSR(GraphVisitor *v, Inst *inst);
253     static void VisitAndSR(GraphVisitor *v, Inst *inst);
254     static void VisitOrSR(GraphVisitor *v, Inst *inst);
255     static void VisitXorSR(GraphVisitor *v, Inst *inst);
256     static void VisitAndNotSR(GraphVisitor *v, Inst *inst);
257     static void VisitOrNotSR(GraphVisitor *v, Inst *inst);
258     static void VisitXorNotSR(GraphVisitor *v, Inst *inst);
259     static void VisitNegSR([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
260     static void VisitCompareAnyType(GraphVisitor *v, Inst *inst);
261     static void VisitCastAnyTypeValue(GraphVisitor *v, Inst *inst);
262     static void VisitCastValueToAnyType(GraphVisitor *v, Inst *inst);
263     static void VisitAnyTypeCheck(GraphVisitor *v, Inst *inst);
264     static void VisitHclassCheck(GraphVisitor *v, Inst *inst);
265     static void VisitBitcast(GraphVisitor *v, Inst *inst);
266     static void VisitAddOverflow([[maybe_unused]] GraphVisitor *v, Inst *inst);
267     static void VisitSubOverflow([[maybe_unused]] GraphVisitor *v, Inst *inst);
268     static void VisitLoadString(GraphVisitor *v, Inst *inst);
269     static void VisitLoadType(GraphVisitor *v, Inst *inst);
270     static void VisitLoadUnresolvedType(GraphVisitor *v, Inst *inst);
271     static void VisitLoadFromConstantPool(GraphVisitor *v, Inst *inst);
272     static void VisitLoadImmediate([[maybe_unused]] GraphVisitor *v, Inst *inst);
273 
274 #include "visitor.inc"
275 
CheckCommonTypes(Inst * inst1,Inst * inst2)276     static bool CheckCommonTypes(Inst *inst1, Inst *inst2)
277     {
278         if (inst1->GetBasicBlock()->GetGraph()->IsDynamicMethod() &&
279             (inst1->GetType() == DataType::ANY || inst2->GetType() == DataType::ANY)) {
280             return true;
281         }
282         DataType::Type type1 = inst1->GetType();
283         DataType::Type type2 = inst2->GetType();
284         return DataType::GetCommonType(type1) == DataType::GetCommonType(type2);
285     }
286 
287     static void CheckBinaryOperationTypes(Inst *inst, bool isInt = false)
288     {
289         [[maybe_unused]] auto op1 = inst->GetInputs()[0].GetInst();
290         [[maybe_unused]] auto op2 = inst->GetInputs()[1].GetInst();
291 
292         if (inst->GetOpcode() == Opcode::Div || inst->GetOpcode() == Opcode::Mod) {
293             if (op2->GetOpcode() == Opcode::ZeroCheck) {
294                 op2 = op2->GetInput(0).GetInst();
295             }
296         }
297 
298         if (isInt) {
299             ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
300                       (std::cerr << "Binary instruction type is not a integer", inst->Dump(&std::cerr)));
301         }
302 
303         ASSERT_DO(DataType::IsTypeNumeric(op1->GetType()),
304                   (std::cerr << "Binary instruction 1st operand type is not a numeric", inst->Dump(&std::cerr)));
305         ASSERT_DO(DataType::IsTypeNumeric(op2->GetType()),
306                   (std::cerr << "Binary instruction 2nd operand type is not a numeric", inst->Dump(&std::cerr)));
307         ASSERT_DO(DataType::IsTypeNumeric(inst->GetType()),
308                   (std::cerr << "Binary instruction type is not a numeric", inst->Dump(&std::cerr)));
309 
310         ASSERT_DO(CheckCommonTypes(op1, op2), (std::cerr << "Types of binary instruction operands are not compatible\n",
311                                                op1->Dump(&std::cerr), op2->Dump(&std::cerr), inst->Dump(&std::cerr)));
312         ASSERT_DO(CheckCommonTypes(inst, op1),
313                   (std::cerr << "Types of binary instruction result and its operands are not compatible\n",
314                    inst->Dump(&std::cerr)));
315     }
316 
CheckBinaryOverflowOperation(IfInst * inst)317     static void CheckBinaryOverflowOperation(IfInst *inst)
318     {
319         // Overflow instruction are used only in dynamic methods.
320         // But ASSERT wasn't added because the instructions checks in codegen_test.cpp with default method
321         // NOTE(pishin) add an ASSERT after add assembly tests tests and remove the test from codegen_test.cpp
322         [[maybe_unused]] auto cc = inst->GetCc();
323         ASSERT_DO((cc == CC_EQ || cc == CC_NE), (std::cerr << "overflow instruction are used only CC_EQ or CC_NE"));
324         CheckBinaryOperationTypes(inst, true);
325         ASSERT_DO(!DataType::IsLessInt32(inst->GetType()),
326                   (std::cerr << "overflow instruction have INT32 or INT64 types"));
327     }
328 
CheckBinaryOperationWithShiftedOperandTypes(GraphVisitor * v,Inst * inst,bool rorSupported)329     static void CheckBinaryOperationWithShiftedOperandTypes([[maybe_unused]] GraphVisitor *v, Inst *inst,
330                                                             [[maybe_unused]] bool rorSupported)
331     {
332         CheckBinaryOperationTypes(inst, true);
333         [[maybe_unused]] auto instWShift = static_cast<BinaryShiftedRegisterOperation *>(inst);
334         ASSERT_DO(instWShift->GetShiftType() != ShiftType::INVALID_SHIFT &&
335                       (rorSupported || instWShift->GetShiftType() != ShiftType::ROR),
336                   (std::cerr << "Operation has invalid shift type\n", inst->Dump(&std::cerr)));
337     }
338 
CheckUnaryOperationTypes(Inst * inst)339     static void CheckUnaryOperationTypes(Inst *inst)
340     {
341         [[maybe_unused]] auto op = inst->GetInput(0).GetInst();
342         ASSERT_DO(CheckCommonTypes(inst, op),
343                   (std::cerr << "Types of unary instruction result and its operand are not compatible\n",
344                    inst->Dump(&std::cerr), op->Dump(&std::cerr)));
345     }
346 
347     static void CheckTernaryOperationTypes(Inst *inst, bool isInt = false)
348     {
349         [[maybe_unused]] auto op1 = inst->GetInputs()[0].GetInst();
350         [[maybe_unused]] auto op2 = inst->GetInputs()[1].GetInst();
351         [[maybe_unused]] auto op3 = inst->GetInputs()[1].GetInst();
352 
353         if (isInt) {
354             ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
355                       (std::cerr << "Ternary instruction type is not a integer", inst->Dump(&std::cerr)));
356         }
357 
358         ASSERT_DO(DataType::IsTypeNumeric(op1->GetType()),
359                   (std::cerr << "Ternary instruction 1st operand type is not a numeric", inst->Dump(&std::cerr)));
360         ASSERT_DO(DataType::IsTypeNumeric(op2->GetType()),
361                   (std::cerr << "Ternary instruction 2nd operand type is not a numeric", inst->Dump(&std::cerr)));
362         ASSERT_DO(DataType::IsTypeNumeric(op3->GetType()),
363                   (std::cerr << "Ternary instruction 2nd operand type is not a numeric", inst->Dump(&std::cerr)));
364         ASSERT_DO(DataType::IsTypeNumeric(inst->GetType()),
365                   (std::cerr << "Ternary instruction type is not a numeric", inst->Dump(&std::cerr)));
366 
367         ASSERT_DO(CheckCommonTypes(op1, op2) && CheckCommonTypes(op2, op3),
368                   (std::cerr << "Types of ternary instruction operands are not compatible\n", op1->Dump(&std::cerr),
369                    op2->Dump(&std::cerr), op3->Dump(&std::cerr), inst->Dump(&std::cerr)));
370         ASSERT_DO(CheckCommonTypes(inst, op1),
371                   (std::cerr << "Types of ternary instruction result and its operands are not compatible\n",
372                    inst->Dump(&std::cerr)));
373     }
374 
375     static void CheckMemoryInstruction([[maybe_unused]] Inst *inst, [[maybe_unused]] bool needBarrier = false)
376     {
377         ASSERT_DO(DataType::IsTypeNumeric(inst->GetType()) || inst->GetType() == DataType::REFERENCE ||
378                       inst->GetType() == DataType::ANY,
379                   (std::cerr << "Memory instruction has wrong type\n", inst->Dump(&std::cerr)));
380         if (inst->IsStore() && (inst->GetInputType(0) != DataType::POINTER) && (inst->GetType() == DataType::ANY)) {
381             ASSERT_DO(needBarrier, (std::cerr << "This store should have barrier:\n", inst->Dump(&std::cerr)));
382         }
383     }
384 
CheckObjectType(Inst * inst,ObjectType type,uint32_t typeId)385     static void CheckObjectType(Inst *inst, ObjectType type, [[maybe_unused]] uint32_t typeId)
386     {
387         auto graph = inst->GetBasicBlock()->GetGraph();
388         if (!graph->SupportManagedCode()) {
389             return;
390         }
391         if (graph->IsDynamicMethod()) {
392             // IrToc use LoadObject and StoreObject with ObjectType::MEM_OBJECT for dynamic
393             // We can inline intrinsics with the instruction under the  option --compiler-inline-full-intrinsics=true
394             if (!g_options.IsCompilerInlineFullIntrinsics()) {
395                 ASSERT_DO(type != ObjectType::MEM_OBJECT && type != ObjectType::MEM_STATIC,
396                           (std::cerr << "The object type isn't supported for dynamic\n", inst->Dump(&std::cerr)));
397             }
398             if (type == ObjectType::MEM_DYN_CLASS) {
399                 ASSERT_DO(typeId == TypeIdMixin::MEM_DYN_CLASS_ID,
400                           (std::cerr << "The object type_id for MEM_DYN_CLASS is incorrect\n", inst->Dump(&std::cerr)));
401             } else if (type == ObjectType::MEM_DYN_PROPS) {
402                 ASSERT_DO(typeId == TypeIdMixin::MEM_DYN_PROPS_ID,
403                           (std::cerr << "The object type_id for MEM_DYN_PROPS is incorrect\n", inst->Dump(&std::cerr)));
404             } else if (type == ObjectType::MEM_DYN_PROTO_HOLDER) {
405                 ASSERT_DO(typeId == TypeIdMixin::MEM_DYN_PROTO_HOLDER_ID,
406                           (std::cerr << "The object type_id for MEM_DYN_PROTO_HOLDER is incorrect\n",
407                            inst->Dump(&std::cerr)));
408             } else if (type == ObjectType::MEM_DYN_PROTO_CELL) {
409                 [[maybe_unused]] Inst *objInst = inst->GetInput(0).GetInst();
410                 ASSERT_DO(
411                     typeId == TypeIdMixin::MEM_DYN_PROTO_CELL_ID,
412                     (std::cerr << "The object type_id for MEM_DYN_PROTO_CELL is incorrect\n", inst->Dump(&std::cerr)));
413             } else if (type == ObjectType::MEM_DYN_CHANGE_FIELD) {
414                 [[maybe_unused]] Inst *objInst = inst->GetInput(0).GetInst();
415                 ASSERT_DO(typeId == TypeIdMixin::MEM_DYN_CHANGE_FIELD_ID,
416                           (std::cerr << "The object type_id for MEM_DYN_CHANGE_FIELD is incorrect\n",
417                            inst->Dump(&std::cerr)));
418             } else if (type == ObjectType::MEM_DYN_GLOBAL) {
419                 ASSERT_DO(
420                     typeId == TypeIdMixin::MEM_DYN_GLOBAL_ID,
421                     (std::cerr << "The object type_id for MEM_DYN_GLOBAL is incorrect\n", inst->Dump(&std::cerr)));
422             } else if (type == ObjectType::MEM_DYN_HCLASS) {
423                 ASSERT_DO(
424                     typeId == TypeIdMixin::MEM_DYN_HCLASS_ID,
425                     (std::cerr << "The object type_id for MEM_DYN_HCLASS is incorrect\n", inst->Dump(&std::cerr)));
426             } else {
427                 ASSERT_DO(
428                     typeId != TypeIdMixin::MEM_DYN_GLOBAL_ID && typeId != TypeIdMixin::MEM_DYN_CLASS_ID &&
429                         typeId != TypeIdMixin::MEM_DYN_PROPS_ID,
430                     (std::cerr << "The object type_id for MEM_DYN_GLOBAL is incorrect\n", inst->Dump(&std::cerr)));
431             }
432         } else {
433             ASSERT_DO(type == ObjectType::MEM_OBJECT || type == ObjectType::MEM_STATIC,
434                       (std::cerr << "The object type isn't supported for static\n", inst->Dump(&std::cerr)));
435         }
436     }
437 
CheckContrlFlowInst(Inst * inst)438     static void CheckContrlFlowInst(Inst *inst)
439     {
440         auto block = inst->GetBasicBlock();
441         [[maybe_unused]] auto lastInst = *block->AllInstsSafeReverse().begin();
442         ASSERT_DO((lastInst == inst),
443                   (std::cerr << "Control flow instruction must be last instruction in block\n CF instruction:\n",
444                    inst->Dump(&std::cerr), std::cerr << "\n last instruction:\n", lastInst->Dump(&std::cerr)));
445         ASSERT_DO(inst->GetUsers().Empty(),
446                   (std::cerr << "Control flow instruction has users\n", inst->Dump(&std::cerr)));
447     }
448 
CheckThrows(Inst * inst,std::initializer_list<Opcode> opcs)449     static void CheckThrows([[maybe_unused]] Inst *inst, [[maybe_unused]] std::initializer_list<Opcode> opcs)
450     {
451 #ifndef NDEBUG
452         const auto &inputs = inst->GetInputs();
453         auto ssInput = [&inst](const auto &input) {
454             return input.GetInst()->GetOpcode() == Opcode::SaveState ||
455                    (input.GetInst()->GetOpcode() == Opcode::SaveStateDeoptimize && inst->CanDeoptimize());
456         };
457         bool hasSaveState = std::find_if(inputs.begin(), inputs.end(), ssInput) != inputs.end();
458 
459         bool hasOpc = true;
460         for (auto &node : inst->GetUsers()) {
461             auto opc = node.GetInst()->GetOpcode();
462             hasOpc &= std::find(opcs.begin(), opcs.end(), opc) != opcs.end();
463         }
464 
465         ASSERT_DO(!inst->HasUsers() || hasOpc,
466                   (inst->Dump(&std::cerr),
467                    std::cerr << "Throw inst doesn't have any users from the list:" << opcs << std::endl));
468         ASSERT_DO(hasSaveState, (inst->Dump(&std::cerr), std::cerr << "Throw inst without SaveState" << std::endl));
469 #endif
470     }
471 
CheckSaveStateInput(Inst * inst)472     static void CheckSaveStateInput([[maybe_unused]] Inst *inst)
473     {
474         ASSERT_DO(inst->GetInputsCount() != 0 &&
475                       inst->GetInput(inst->GetInputsCount() - 1).GetInst()->GetOpcode() == Opcode::SaveState,
476                   (std::cerr << "Instruction must have SaveState as last input:\n", inst->Dump(&std::cerr)));
477     }
478 
GetPassName()479     std::string GetPassName() const
480     {
481         return passName_;
482     }
483 
IncrementNullPtrInstCounterAndGet()484     int IncrementNullPtrInstCounterAndGet()
485     {
486         return ++nullPtrInstCounter_;
487     }
488 
IncrementLoadUndefinedInstCounterAndGet()489     int IncrementLoadUndefinedInstCounterAndGet()
490     {
491         return ++loadUndefinedInstCounter_;
492     }
493 
494     void PrintFailedMethodAndPass() const;
495     static void PrintFailedMethodAndPassVisitor(GraphVisitor *v);
496 
497 private:
498     Graph *graph_;
499     ArenaAllocator allocator_ {SpaceType::SPACE_TYPE_COMPILER, nullptr, true};
500     ArenaAllocator localAllocator_ {SpaceType::SPACE_TYPE_COMPILER, nullptr, true};
501     int nullPtrInstCounter_ = 0;
502     int loadUndefinedInstCounter_ = 0;
503     std::string passName_;
504 };
505 }  // namespace ark::compiler
506 
507 #undef ASSERT_DO_EXT
508 #undef ASSERT_DO_EXT_VISITOR
509 #undef ASSERT_EXT
510 #undef ASSERT_EXT_VISITOR
511 #undef ASSERT_EXT_PRINT
512 #undef ASSERT_EXT_PRINT_VISITOR
513 
514 #endif  // COMPILER_OPTIMIZER_IR_GRAPH_CHECKER_H
515