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