• 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 #ifndef COMPILER_OPTIMIZER_IR_GRAPH_CHECKER_H
17 #define COMPILER_OPTIMIZER_IR_GRAPH_CHECKER_H
18 
19 #include <iostream>
20 #include "compiler_options.h"
21 #include "graph.h"
22 #include "graph_visitor.h"
23 #include "optimizer/analysis/dominators_tree.h"
24 #include "optimizer/analysis/rpo.h"
25 #include "optimizer/analysis/loop_analyzer.h"
26 #include "optimizer/code_generator/registers_description.h"
27 #include "optimizer/optimizations/cleanup.h"
28 #include "optimizer/optimizations/memory_coalescing.h"
29 
30 namespace panda::compiler {
31 inline std::ostream &operator<<(std::ostream &os, const std::initializer_list<Opcode> &opcs)
32 {
33     os << "[ ";
34     for (auto opc : opcs) {
35         os << GetOpcodeString(opc) << " ";
36     }
37     os << "]";
38     return os;
39 }
40 
41 class GraphChecker : public GraphVisitor {
42 public:
43     explicit GraphChecker(Graph *graph);
~GraphChecker()44     ~GraphChecker() override
45     {
46         GetGraph()->GetPassManager()->SetCheckMode(false);
47     }
48 
49     NO_COPY_SEMANTIC(GraphChecker);
50     NO_MOVE_SEMANTIC(GraphChecker);
51 
52     void Check();
53 
54 private:
55     void PreCloneChecks(Graph *graph);
56     void UserInputCheck(Graph *graph);
57     void CheckBlock(BasicBlock *block);
58     void CheckDomTree();
59     void CheckLoopAnalysis();
60     void CheckStartBlock();
61     void CheckEndBlock();
62     void CheckControlFlow(BasicBlock *block);
63     void CheckDataFlow(BasicBlock *block);
64     void CheckPhiInputs(Inst *phi_inst);
65     void CheckInstsRegisters(BasicBlock *block);
66     void CheckPhisRegisters(BasicBlock *block);
67     void CheckNoLowLevel(BasicBlock *block);
68     void CheckLoops();
69     void CheckGraph();
70     bool HasOuterInfiniteLoop();
71     bool CheckInstHasInput(Inst *inst, Inst *input);
72     bool CheckInstHasUser(Inst *inst, Inst *user);
73     void CheckCallReturnInlined();
74     void CheckSaveStateCaller(SaveStateInst *savestate);
75     void CheckSpillFillHolder(Inst *inst);
76     bool CheckInstRegUsageSaved(const Inst *inst, Register reg) const;
77     void MarkBlocksInLoop(Loop *loop, Marker mrk);
78     bool CheckBlockHasPredecessor(BasicBlock *block, BasicBlock *predecessor);
79     bool CheckBlockHasSuccessor(BasicBlock *block, BasicBlock *successor);
80     bool BlockContainsInstruction(BasicBlock *block, Opcode opcode);
81     void CheckLoopHasSafePoint(Loop *loop);
82     void CheckBlockEdges(const BasicBlock &block);
83     void CheckTryBeginBlock(const BasicBlock &block);
84     void CheckJump(const BasicBlock &block);
85     bool IsTryCatchDomination(const BasicBlock *input_block, const BasicBlock *user_block) const;
86 #ifndef NDEBUG
87     bool NeedCheckSaveState();
88 #endif  // !NDEBUG
89     void CheckSaveStateInputs();
90     void CheckObjectRec(const Inst *object, const Inst *user, const BasicBlock *block, Inst *start_from,
91                         Marker visited) const;
92     void FindObjectInSaveState(const Inst *object, Inst *ss) const;
93     void CheckSaveStatesWithRuntimeCallUsers();
94     void CheckSaveStateOsrRec(const Inst *inst, const Inst *user, BasicBlock *block, Marker visited);
95 
GetGraph()96     Graph *GetGraph() const
97     {
98         return graph_;
99     }
100 
GetAllocator()101     ArenaAllocator *GetAllocator()
102     {
103         return &allocator_;
104     }
105 
GetLocalAllocator()106     ArenaAllocator *GetLocalAllocator()
107     {
108         return &local_allocator_;
109     }
110 
GetBlocksToVisit()111     const ArenaVector<BasicBlock *> &GetBlocksToVisit() const override
112     {
113         return GetGraph()->GetBlocksRPO();
114     }
115 
116     /*
117      * Visitors to check instructions types
118      */
119     static void VisitMov([[maybe_unused]] GraphVisitor *v, Inst *inst);
120     static void VisitNeg([[maybe_unused]] GraphVisitor *v, Inst *inst);
121     static void VisitAbs([[maybe_unused]] GraphVisitor *v, Inst *inst);
122     static void VisitSqrt([[maybe_unused]] GraphVisitor *v, Inst *inst);
123     static void VisitAddI([[maybe_unused]] GraphVisitor *v, Inst *inst);
124     static void VisitSubI([[maybe_unused]] GraphVisitor *v, Inst *inst);
125     static void VisitMulI([[maybe_unused]] GraphVisitor *v, Inst *inst);
126     static void VisitDivI([[maybe_unused]] GraphVisitor *v, Inst *inst);
127     static void VisitModI([[maybe_unused]] GraphVisitor *v, Inst *inst);
128     static void VisitAndI([[maybe_unused]] GraphVisitor *v, Inst *inst);
129     static void VisitOrI([[maybe_unused]] GraphVisitor *v, Inst *inst);
130     static void VisitXorI([[maybe_unused]] GraphVisitor *v, Inst *inst);
131     static void VisitShlI([[maybe_unused]] GraphVisitor *v, Inst *inst);
132     static void VisitShrI([[maybe_unused]] GraphVisitor *v, Inst *inst);
133     static void VisitAShlI([[maybe_unused]] GraphVisitor *v, Inst *inst);
134     static void VisitNot([[maybe_unused]] GraphVisitor *v, Inst *inst);
135     static void VisitAdd([[maybe_unused]] GraphVisitor *v, Inst *inst);
136     static void VisitSub([[maybe_unused]] GraphVisitor *v, Inst *inst);
137     static void VisitMul([[maybe_unused]] GraphVisitor *v, Inst *inst);
138     static void VisitDiv([[maybe_unused]] GraphVisitor *v, Inst *inst);
139     static void VisitMod([[maybe_unused]] GraphVisitor *v, Inst *inst);
140     static void VisitMin([[maybe_unused]] GraphVisitor *v, Inst *inst);
141     static void VisitMax([[maybe_unused]] GraphVisitor *v, Inst *inst);
142     static void VisitShl([[maybe_unused]] GraphVisitor *v, Inst *inst);
143     static void VisitShr([[maybe_unused]] GraphVisitor *v, Inst *inst);
144     static void VisitAShr([[maybe_unused]] GraphVisitor *v, Inst *inst);
145     static void VisitAnd([[maybe_unused]] GraphVisitor *v, Inst *inst);
146     static void VisitOr([[maybe_unused]] GraphVisitor *v, Inst *inst);
147     static void VisitXor([[maybe_unused]] GraphVisitor *v, Inst *inst);
148     static void VisitLoadArray([[maybe_unused]] GraphVisitor *v, Inst *inst);
149     static void VisitLoadArrayI([[maybe_unused]] GraphVisitor *v, Inst *inst);
150     static void VisitLoadArrayPair([[maybe_unused]] GraphVisitor *v, Inst *inst);
151     static void VisitLoadArrayPairI([[maybe_unused]] GraphVisitor *v, Inst *inst);
152     static void VisitLoadPairPart([[maybe_unused]] GraphVisitor *v, Inst *inst);
153     static void VisitStoreArrayPair([[maybe_unused]] GraphVisitor *v, Inst *inst);
154     static void VisitStoreArrayPairI([[maybe_unused]] GraphVisitor *v, Inst *inst);
155     static void VisitStoreArray([[maybe_unused]] GraphVisitor *v, Inst *inst);
156     static void VisitStoreArrayI([[maybe_unused]] GraphVisitor *v, Inst *inst);
157     static void VisitStoreStatic([[maybe_unused]] GraphVisitor *v, Inst *inst);
158     static void VisitUnresolvedStoreStatic([[maybe_unused]] GraphVisitor *v, Inst *inst);
159     static void VisitStoreObject([[maybe_unused]] GraphVisitor *v, Inst *inst);
160     static void VisitUnresolvedStoreObject([[maybe_unused]] GraphVisitor *v, Inst *inst);
161     static void VisitLoadStatic([[maybe_unused]] GraphVisitor *v, Inst *inst);
162     static void VisitUnresolvedLoadStatic([[maybe_unused]] GraphVisitor *v, Inst *inst);
163     static void VisitLoadClass([[maybe_unused]] GraphVisitor *v, Inst *inst);
164     static void VisitLoadAndInitClass([[maybe_unused]] GraphVisitor *v, Inst *inst);
165     static void VisitUnresolvedLoadAndInitClass([[maybe_unused]] GraphVisitor *v, Inst *inst);
166     static void VisitNewObject([[maybe_unused]] GraphVisitor *v, Inst *inst);
167     static void VisitInitObject([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
168     static void VisitInitClass([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
169     static void VisitLoadObject([[maybe_unused]] GraphVisitor *v, Inst *inst);
170     static void VisitUnresolvedLoadObject([[maybe_unused]] GraphVisitor *v, Inst *inst);
171     static void VisitConstant([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
172     static void VisitNullPtr([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
173     static void VisitPhi([[maybe_unused]] GraphVisitor *v, Inst *inst);
174     static void VisitParameter([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
175     static void VisitCompare([[maybe_unused]] GraphVisitor *v, Inst *inst);
176     static void VisitCast([[maybe_unused]] GraphVisitor *v, Inst *inst);
177     static void VisitCmp([[maybe_unused]] GraphVisitor *v, Inst *inst);
178     static void VisitMonitor([[maybe_unused]] GraphVisitor *v, Inst *inst);
179     static void VisitReturn([[maybe_unused]] GraphVisitor *v, Inst *inst);
180 
181     static void VisitReturnVoid([[maybe_unused]] GraphVisitor *v, Inst *inst);
182     static void VisitNullCheck([[maybe_unused]] GraphVisitor *v, Inst *inst);
183     static void VisitBoundsCheck([[maybe_unused]] GraphVisitor *v, Inst *inst);
184     static void VisitRefTypeCheck([[maybe_unused]] GraphVisitor *v, Inst *inst);
185     static void VisitNegativeCheck([[maybe_unused]] GraphVisitor *v, Inst *inst);
186     static void VisitZeroCheck([[maybe_unused]] GraphVisitor *v, Inst *inst);
187     static void VisitDeoptimizeIf([[maybe_unused]] GraphVisitor *v, Inst *inst);
188     static void VisitLenArray([[maybe_unused]] GraphVisitor *v, Inst *inst);
189     static void VisitiUnresolvedCallStatic([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
190     static void VisitCallVirtual([[maybe_unused]] GraphVisitor *v, Inst *inst);
191     static void VisitUnresolvedCallVirtual([[maybe_unused]] GraphVisitor *v, Inst *inst);
192     static void VisitCallDynamic([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
193     static void VisitSaveState([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
194     static void VisitSafePoint([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
195     static void VisitSaveStateOsr([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
196     static void VisitThrow([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
197     static void VisitCheckCast([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
198     static void VisitIsInstance([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
199     static void VisitSelect([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
200     static void VisitSelectImm([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
201     static void VisitIf([[maybe_unused]] GraphVisitor *v, Inst *inst);
202     static void VisitIfImm([[maybe_unused]] GraphVisitor *v, Inst *inst);
203     static void VisitTry([[maybe_unused]] GraphVisitor *v, Inst *inst);
204     static void VisitNOP([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
205     static void VisitAndNot([[maybe_unused]] GraphVisitor *v, Inst *inst);
206     static void VisitOrNot([[maybe_unused]] GraphVisitor *v, Inst *inst);
207     static void VisitXorNot([[maybe_unused]] GraphVisitor *v, Inst *inst);
208     static void VisitMNeg([[maybe_unused]] GraphVisitor *v, Inst *inst);
209     static void VisitMAdd([[maybe_unused]] GraphVisitor *v, Inst *inst);
210     static void VisitMSub([[maybe_unused]] GraphVisitor *v, Inst *inst);
211     static void VisitAddSR(GraphVisitor *v, Inst *inst);
212     static void VisitSubSR(GraphVisitor *v, Inst *inst);
213     static void VisitAndSR(GraphVisitor *v, Inst *inst);
214     static void VisitOrSR(GraphVisitor *v, Inst *inst);
215     static void VisitXorSR(GraphVisitor *v, Inst *inst);
216     static void VisitAndNotSR(GraphVisitor *v, Inst *inst);
217     static void VisitOrNotSR(GraphVisitor *v, Inst *inst);
218     static void VisitXorNotSR(GraphVisitor *v, Inst *inst);
219     static void VisitNegSR([[maybe_unused]] GraphVisitor *v, [[maybe_unused]] Inst *inst);
220     static void VisitCompareAnyType(GraphVisitor *v, Inst *inst);
221     static void VisitCastAnyTypeValue(GraphVisitor *v, Inst *inst);
222     static void VisitCastValueToAnyType(GraphVisitor *v, Inst *inst);
223     static void VisitAnyTypeCheck(GraphVisitor *v, Inst *inst);
224 
225     static void VisitAddOverflow([[maybe_unused]] GraphVisitor *v, Inst *inst);
226     static void VisitSubOverflow([[maybe_unused]] GraphVisitor *v, Inst *inst);
227 
228 #include "visitor.inc"
229 
CheckCommonTypes(Inst * inst1,Inst * inst2)230     static bool CheckCommonTypes(Inst *inst1, Inst *inst2)
231     {
232         if (inst1->GetBasicBlock()->GetGraph()->IsDynamicMethod() &&
233             (inst1->GetType() == DataType::ANY || inst2->GetType() == DataType::ANY)) {
234             return true;
235         }
236         DataType::Type type1 = inst1->GetType();
237         DataType::Type type2 = inst2->GetType();
238         return DataType::GetCommonType(type1) == DataType::GetCommonType(type2);
239     }
240 
241     static void CheckBinaryOperationTypes(Inst *inst, bool is_int = false)
242     {
243         [[maybe_unused]] auto op1 = inst->GetInputs()[0].GetInst();
244         [[maybe_unused]] auto op2 = inst->GetInputs()[1].GetInst();
245 
246         if (inst->GetOpcode() == Opcode::Div || inst->GetOpcode() == Opcode::Mod) {
247             if (op2->GetOpcode() == Opcode::ZeroCheck) {
248                 op2 = op2->GetInput(0).GetInst();
249             }
250         }
251 
252         if (is_int) {
253             ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
254                       (std::cerr << "Binary instruction type is not a integer", inst->Dump(&std::cerr)));
255         }
256 
257         ASSERT_DO(DataType::IsTypeNumeric(op1->GetType()),
258                   (std::cerr << "Binary instruction 1st operand type is not a numeric", inst->Dump(&std::cerr)));
259         ASSERT_DO(DataType::IsTypeNumeric(op2->GetType()),
260                   (std::cerr << "Binary instruction 2nd operand type is not a numeric", inst->Dump(&std::cerr)));
261         ASSERT_DO(DataType::IsTypeNumeric(inst->GetType()),
262                   (std::cerr << "Binary instruction type is not a numeric", inst->Dump(&std::cerr)));
263 
264         ASSERT_DO(CheckCommonTypes(op1, op2), (std::cerr << "Types of binary instruction operands are not compatible\n",
265                                                op1->Dump(&std::cerr), op2->Dump(&std::cerr), inst->Dump(&std::cerr)));
266         ASSERT_DO(CheckCommonTypes(inst, op1),
267                   (std::cerr << "Types of binary instruction result and its operands are not compatible\n",
268                    inst->Dump(&std::cerr)));
269     }
270 
CheckBinaryOverflowOperation(IfInst * inst)271     static void CheckBinaryOverflowOperation(IfInst *inst)
272     {
273         // Overflow instruction are used only in dynamic methods.
274         // But ASSERT wasn't added because the instructions checks in codegen_test.cpp with default method
275         // TODO(pishin) add an ASSERT after add assembly tests tests and remove the test from codegen_test.cpp
276         [[maybe_unused]] auto cc = inst->GetCc();
277         ASSERT_DO((cc == CC_EQ || cc == CC_NE), (std::cerr << "overflow instruction are used only CC_EQ or CC_NE"));
278         CheckBinaryOperationTypes(inst, true);
279         ASSERT_DO(!DataType::IsLessInt32(inst->GetType()),
280                   (std::cerr << "overflow instruction have INT32 or INT64 types"));
281     }
282 
CheckBinaryOperationWithShiftedOperandTypes(GraphVisitor * v,Inst * inst,bool ror_supported)283     static void CheckBinaryOperationWithShiftedOperandTypes([[maybe_unused]] GraphVisitor *v, Inst *inst,
284                                                             [[maybe_unused]] bool ror_supported)
285     {
286         CheckBinaryOperationTypes(inst, true);
287         [[maybe_unused]] auto inst_w_shift = static_cast<BinaryShiftedRegisterOperation *>(inst);
288         ASSERT_DO(inst_w_shift->GetShiftType() != ShiftType::INVALID_SHIFT &&
289                       (ror_supported || inst_w_shift->GetShiftType() != ShiftType::ROR),
290                   (std::cerr << "Operation has invalid shift type\n", inst->Dump(&std::cerr)));
291     }
292 
CheckUnaryOperationTypes(Inst * inst)293     static void CheckUnaryOperationTypes(Inst *inst)
294     {
295         [[maybe_unused]] auto op = inst->GetInput(0).GetInst();
296         ASSERT_DO(CheckCommonTypes(inst, op),
297                   (std::cerr << "Types of unary instruction result and its operand are not compatible\n",
298                    inst->Dump(&std::cerr), op->Dump(&std::cerr)));
299     }
300 
301     static void CheckTernaryOperationTypes(Inst *inst, bool is_int = false)
302     {
303         [[maybe_unused]] auto op1 = inst->GetInputs()[0].GetInst();
304         [[maybe_unused]] auto op2 = inst->GetInputs()[1].GetInst();
305         [[maybe_unused]] auto op3 = inst->GetInputs()[1].GetInst();
306 
307         if (is_int) {
308             ASSERT_DO(DataType::GetCommonType(inst->GetType()) == DataType::INT64,
309                       (std::cerr << "Ternary instruction type is not a integer", inst->Dump(&std::cerr)));
310         }
311 
312         ASSERT_DO(DataType::IsTypeNumeric(op1->GetType()),
313                   (std::cerr << "Ternary instruction 1st operand type is not a numeric", inst->Dump(&std::cerr)));
314         ASSERT_DO(DataType::IsTypeNumeric(op2->GetType()),
315                   (std::cerr << "Ternary instruction 2nd operand type is not a numeric", inst->Dump(&std::cerr)));
316         ASSERT_DO(DataType::IsTypeNumeric(op3->GetType()),
317                   (std::cerr << "Ternary instruction 2nd operand type is not a numeric", inst->Dump(&std::cerr)));
318         ASSERT_DO(DataType::IsTypeNumeric(inst->GetType()),
319                   (std::cerr << "Ternary instruction type is not a numeric", inst->Dump(&std::cerr)));
320 
321         ASSERT_DO(CheckCommonTypes(op1, op2) && CheckCommonTypes(op2, op3),
322                   (std::cerr << "Types of ternary instruction operands are not compatible\n", op1->Dump(&std::cerr),
323                    op2->Dump(&std::cerr), op3->Dump(&std::cerr), inst->Dump(&std::cerr)));
324         ASSERT_DO(CheckCommonTypes(inst, op1),
325                   (std::cerr << "Types of ternary instruction result and its operands are not compatible\n",
326                    inst->Dump(&std::cerr)));
327     }
328 
CheckMemoryInstruction(Inst * inst)329     static void CheckMemoryInstruction([[maybe_unused]] Inst *inst)
330     {
331         ASSERT_DO(DataType::IsTypeNumeric(inst->GetType()) || inst->GetType() == DataType::REFERENCE ||
332                       inst->GetType() == DataType::ANY,
333                   (std::cerr << "Memory instruction has wrong type\n", inst->Dump(&std::cerr)));
334     }
335 
CheckContrlFlowInst(Inst * inst)336     static void CheckContrlFlowInst(Inst *inst)
337     {
338         auto block = inst->GetBasicBlock();
339         [[maybe_unused]] auto last_inst = *block->AllInstsSafeReverse().begin();
340         ASSERT_DO((last_inst == inst),
341                   (std::cerr << "Control flow instruction must be last instruction in block\n CF instruction:\n",
342                    inst->Dump(&std::cerr), std::cerr << "\n last instruction:\n", last_inst->Dump(&std::cerr)));
343         ASSERT_DO(inst->GetUsers().Empty(),
344                   (std::cerr << "Control flow instruction has users\n", inst->Dump(&std::cerr)));
345     }
346 
CheckThrows(Inst * inst,std::initializer_list<Opcode> opcs)347     static void CheckThrows([[maybe_unused]] Inst *inst, [[maybe_unused]] std::initializer_list<Opcode> opcs)
348     {
349 #ifndef NDEBUG
350         const auto &inputs = inst->GetInputs();
351         auto ss_input = [](const auto &input) { return input.GetInst()->GetOpcode() == Opcode::SaveState; };
352         bool has_save_state = std::find_if(inputs.begin(), inputs.end(), ss_input) != inputs.end();
353 
354         bool has_opc = true;
355         for (auto &node : inst->GetUsers()) {
356             auto opc = node.GetInst()->GetOpcode();
357             has_opc &= std::find(opcs.begin(), opcs.end(), opc) != opcs.end();
358         }
359 
360         ASSERT_DO(!inst->HasUsers() || has_opc,
361                   (inst->Dump(&std::cerr),
362                    std::cerr << "Throw inst doesn't have any users from the list:" << opcs << std::endl));
363         ASSERT_DO(has_save_state, (inst->Dump(&std::cerr), std::cerr << "Throw inst without SaveState" << std::endl));
364 #endif
365     }
366 
CheckSaveStateInput(Inst * inst)367     static void CheckSaveStateInput([[maybe_unused]] Inst *inst)
368     {
369         ASSERT_DO(inst->GetInputsCount() != 0 &&
370                       inst->GetInput(inst->GetInputsCount() - 1).GetInst()->GetOpcode() == Opcode::SaveState,
371                   (std::cerr << "Instruction must have SaveState as last input:\n", inst->Dump(&std::cerr)));
372     }
373 
IncrementNullPtrInstCounterAndGet()374     int IncrementNullPtrInstCounterAndGet()
375     {
376         return ++null_ptr_inst_counter_;
377     }
378 
379 private:
380     Graph *graph_;
381     ArenaAllocator allocator_ {SpaceType::SPACE_TYPE_COMPILER, nullptr, true};
382     ArenaAllocator local_allocator_ {SpaceType::SPACE_TYPE_COMPILER, nullptr, true};
383     int null_ptr_inst_counter_ = 0;
384 };
385 }  // namespace panda::compiler
386 #endif  // COMPILER_OPTIMIZER_IR_GRAPH_CHECKER_H
387