1 /* 2 * Copyright (c) 2021-2025 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 PANDA_IR_BUILDER_H 17 #define PANDA_IR_BUILDER_H 18 19 #include "bytecode_instruction.h" 20 #include "optimizer/ir/graph.h" 21 #include "optimizer/ir/basicblock.h" 22 #include "optimizer/pass.h" 23 #include "utils/logger.h" 24 #include "pbc_iterator.h" 25 #include "inst_builder.h" 26 27 namespace ark { 28 class Method; 29 } // namespace ark 30 31 namespace ark::compiler { 32 33 struct BlocksConnectorInfo { 34 bool fallthrough {}; 35 bool deadInstructions {}; 36 BytecodeInstruction prevInst {nullptr}; 37 }; 38 39 /// Build IR from panda bytecode 40 class PANDA_PUBLIC_API IrBuilder : public Optimization { 41 struct Boundaries { 42 uint32_t beginPc; 43 uint32_t endPc; 44 }; 45 46 struct CatchCodeBlock { 47 uint32_t pc {}; 48 uint32_t typeId {}; 49 }; 50 51 struct TryCodeBlock { 52 Boundaries boundaries {}; // NOLINT(misc-non-private-member-variables-in-classes) 53 BasicBlock *beginBb {nullptr}; // NOLINT(misc-non-private-member-variables-in-classes) 54 BasicBlock *endBb {nullptr}; // NOLINT(misc-non-private-member-variables-in-classes) 55 ArenaVector<CatchCodeBlock> *catches {nullptr}; // NOLINT(misc-non-private-member-variables-in-classes) 56 ArenaVector<BasicBlock *> *basicBlocks {nullptr}; // NOLINT(misc-non-private-member-variables-in-classes) 57 uint32_t id {INVALID_ID}; // NOLINT(misc-non-private-member-variables-in-classes) 58 bool containsThrowableInst {false}; // NOLINT(misc-non-private-member-variables-in-classes) 59 ArenaSet<BasicBlock *> *throwBlocks {nullptr}; // NOLINT(misc-non-private-member-variables-in-classes) 60 InitTryCodeBlock61 void Init(Graph *graph, uint32_t tryId) 62 { 63 id = tryId; 64 auto allocator = graph->GetLocalAllocator(); 65 catches = allocator->New<ArenaVector<CatchCodeBlock>>(allocator->Adapter()); 66 beginBb = graph->CreateEmptyBlock(boundaries.beginPc); 67 ASSERT(beginBb != nullptr); 68 beginBb->SetTryBegin(true); 69 endBb = graph->CreateEmptyBlock(boundaries.endPc); 70 ASSERT(endBb != nullptr); 71 endBb->SetTryEnd(true); 72 // Order of try-blocks should be saved in the graph to restore it in the generated bytecode 73 graph->AppendTryBeginBlock(beginBb); 74 } 75 }; 76 77 public: IrBuilder(Graph * graph)78 explicit IrBuilder(Graph *graph) : IrBuilder(graph, graph->GetMethod(), nullptr, 0) {} 79 IrBuilder(Graph * graph,RuntimeInterface::MethodPtr method,CallInst * callerInst,uint32_t inliningDepth)80 IrBuilder(Graph *graph, RuntimeInterface::MethodPtr method, CallInst *callerInst, uint32_t inliningDepth) 81 : Optimization(graph), 82 blocks_(graph->GetLocalAllocator()->Adapter()), 83 catchesPc_(graph->GetLocalAllocator()->Adapter()), 84 tryBlocks_(graph->GetLocalAllocator()->Adapter()), 85 openedTryBlocks_(graph->GetLocalAllocator()->Adapter()), 86 catchHandlers_(graph->GetLocalAllocator()->Adapter()), 87 instDefs_(graph->GetLocalAllocator()->Adapter()), 88 method_(method), 89 isInlinedGraph_(callerInst != nullptr), 90 callerInst_(callerInst), 91 inliningDepth_(inliningDepth) 92 { 93 } 94 95 NO_COPY_SEMANTIC(IrBuilder); 96 NO_MOVE_SEMANTIC(IrBuilder); 97 ~IrBuilder() override = default; 98 99 bool RunImpl() override; 100 GetPassName()101 const char *GetPassName() const override 102 { 103 return "IrBuilder"; 104 } 105 GetMethod()106 auto GetMethod() const 107 { 108 return method_; 109 } 110 111 private: CreateBlock(size_t pc)112 void CreateBlock(size_t pc) 113 { 114 if (blocks_[pc] == nullptr) { 115 blocks_[pc] = GetGraph()->CreateEmptyBlock(); 116 blocks_[pc]->SetGuestPc(pc); 117 } 118 } 119 GetBlockForPc(size_t pc)120 BasicBlock *GetBlockForPc(size_t pc) 121 { 122 return blocks_[pc]; 123 } 124 GetPrevBlockForPc(size_t pc)125 BasicBlock *GetPrevBlockForPc(size_t pc) 126 { 127 do { 128 ASSERT(pc > 0); 129 pc--; 130 } while (blocks_[pc] == nullptr || blocks_[pc]->GetGraph() == nullptr); 131 return blocks_[pc]; 132 } 133 134 bool CheckMethodLimitations(const BytecodeInstructions &instructions, size_t vregsCount); 135 void BuildBasicBlocks(const BytecodeInstructions &instructions); 136 bool CreateSaveStateForLoopBlocks(BasicBlock *bb); 137 bool BuildBasicBlock(BasicBlock *bb, const uint8_t *instructionsBuf); 138 template <bool NEED_SS_DEOPT> 139 bool AddInstructionToBB(BasicBlock *bb, BytecodeInstruction &inst, size_t pc, bool *ssDeoptWasBuilded); 140 template <bool NEED_SS_DEOPT> 141 bool BuildInstructionsForBB(BasicBlock *bb, const uint8_t *instructionsBuf); 142 void SplitConstant(ConstantInst *constInst); 143 void ConnectBasicBlocks(const BytecodeInstructions &instructions); 144 void CreateTryCatchBoundariesBlocks(); 145 void ResolveTryCatchBlocks(); 146 void ConnectTryCatchBlocks(); 147 IrBuilder::TryCodeBlock *InsertTryBlockInfo(const Boundaries &tryBoundaries); 148 void TrackTryBoundaries(size_t pc, const BytecodeInstruction &inst, BasicBlock *targetBb, 149 BlocksConnectorInfo &info); 150 BasicBlock *GetBlockToJump(BytecodeInstruction *inst, size_t pc); 151 BasicBlock *GetBlockForSaveStateDeoptimize(BasicBlock *block); 152 void MarkTryCatchBlocks(Marker marker); 153 template <class Callback> 154 void EnumerateTryBlocksCoveredPc(uint32_t pc, const Callback &callback); 155 void SetMemoryBarrierFlag(); 156 void ConnectTryCodeBlock(const TryCodeBlock &tryBlock, const ArenaMap<uint32_t, BasicBlock *> &catchBlocks); 157 void ProcessThrowableInstructions(Inst *throwableInst); 158 void RestoreTryEnd(const TryCodeBlock &tryBlock); 159 uint32_t FindCatchBlockInPandaFile(Class *cls, uint32_t pc) const; 160 void ConnectThrowBlock(BasicBlock *throwBlock, const TryCodeBlock &tryBlock); 161 void ConnectThrowBlocks(); 162 bool BuildIrImpl(size_t vregsCount); 163 bool BuildIr(size_t vregsCount); 164 RuntimeInterface::ClassPtr FindExceptionClass(BasicBlock *throwBlock, int32_t *throwPc); 165 bool FindAppropriateCatchBlock(const TryCodeBlock &tryBlock, BasicBlock *throwBlock, uint32_t catchPc); 166 BasicBlock *FindCatchBegin(BasicBlock *bb); 167 SetInstBuilder(InstBuilder * instBuilder)168 void SetInstBuilder(InstBuilder *instBuilder) 169 { 170 instBuilder_ = instBuilder; 171 instBuilder_->Prepare(isInlinedGraph_); 172 } GetInstBuilder()173 InstBuilder *GetInstBuilder() const 174 { 175 return instBuilder_; 176 } 177 178 private: 179 ArenaVector<BasicBlock *> blocks_; 180 ArenaSet<uint32_t> catchesPc_; 181 ArenaMultiMap<uint32_t, TryCodeBlock> tryBlocks_; 182 ArenaList<TryCodeBlock *> openedTryBlocks_; 183 ArenaUnorderedSet<BasicBlock *> catchHandlers_; 184 InstVector instDefs_; 185 RuntimeInterface::MethodPtr method_ = nullptr; 186 bool isInlinedGraph_ {false}; 187 CallInst *callerInst_ {nullptr}; 188 uint32_t inliningDepth_ {0}; 189 InstBuilder *instBuilder_ {nullptr}; 190 }; 191 192 class IrBuilderInliningAnalysis : public Analysis { 193 public: IrBuilderInliningAnalysis(Graph * graph,RuntimeInterface::MethodPtr method)194 IrBuilderInliningAnalysis(Graph *graph, RuntimeInterface::MethodPtr method) : Analysis(graph), method_(method) {} 195 ~IrBuilderInliningAnalysis() override = default; 196 NO_COPY_SEMANTIC(IrBuilderInliningAnalysis); 197 NO_MOVE_SEMANTIC(IrBuilderInliningAnalysis); 198 199 bool RunImpl() override; 200 GetPassName()201 const char *GetPassName() const override 202 { 203 return "IrBuilderInlineAnalysis"; 204 } 205 GetMethod()206 auto GetMethod() const 207 { 208 return method_; 209 } 210 HasRuntimeCalls()211 auto HasRuntimeCalls() const 212 { 213 return hasRuntimeCalls_; 214 } 215 216 private: 217 virtual bool IsSuitableForInline(const BytecodeInstruction *inst); 218 219 private: 220 RuntimeInterface::MethodPtr method_; 221 bool hasRuntimeCalls_ {false}; 222 }; 223 224 class IrBuilderExternalInliningAnalysis : public IrBuilderInliningAnalysis { 225 public: IrBuilderExternalInliningAnalysis(Graph * graph,RuntimeInterface::MethodPtr method)226 IrBuilderExternalInliningAnalysis(Graph *graph, RuntimeInterface::MethodPtr method) 227 : IrBuilderInliningAnalysis(graph, method) 228 { 229 } 230 231 NO_COPY_SEMANTIC(IrBuilderExternalInliningAnalysis); 232 NO_MOVE_SEMANTIC(IrBuilderExternalInliningAnalysis); 233 ~IrBuilderExternalInliningAnalysis() override = default; 234 GetPassName()235 const char *GetPassName() const override 236 { 237 return "IrBuilderExternalInliningAnalysis"; 238 } 239 240 private: 241 bool IsSuitableForInline(const BytecodeInstruction *inst) override; 242 }; 243 244 } // namespace ark::compiler 245 246 #endif // PANDA_IR_BUILDER_H 247