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 PANDA_INST_BUILDER_H 17 #define PANDA_INST_BUILDER_H 18 19 #include "compiler_options.h" 20 #include "optimizer/ir/graph.h" 21 #include "optimizer/ir/basicblock.h" 22 #include "optimizer/analysis/loop_analyzer.h" 23 #include "code_info/vreg_info.h" 24 #include "code_data_accessor.h" 25 #include "file_items.h" 26 #include "compiler_logger.h" 27 28 #include "bytecode_instruction.h" 29 30 namespace ark::compiler { 31 constexpr int64_t INVALID_OFFSET = std::numeric_limits<int64_t>::max(); 32 33 class InstBuilder { 34 public: 35 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 36 #define ENV_IDX(ENV_TYPE) /* CC-OFFNXT(G.PRE.02, G.PRE.09) namespace member, code generation */ \ 37 static constexpr uint8_t ENV_TYPE##_IDX = VRegInfo::VRegType::ENV_TYPE - VRegInfo::VRegType::ENV_BEGIN; 38 VREGS_ENV_TYPE_DEFS(ENV_IDX) 39 #undef ENV_IDX 40 41 InstBuilder(Graph *graph, RuntimeInterface::MethodPtr method, CallInst *callerInst, uint32_t inliningDepth); 42 43 NO_COPY_SEMANTIC(InstBuilder); 44 NO_MOVE_SEMANTIC(InstBuilder); ~InstBuilder()45 ~InstBuilder() 46 { 47 GetGraph()->EraseMarker(noTypeMarker_); 48 GetGraph()->EraseMarker(visitedBlockMarker_); 49 } 50 51 /** 52 * Content of this function is auto generated from inst_builder.erb and is located in inst_builder_gen.cpp file 53 * @param instruction Pointer to bytecode instruction 54 */ 55 void BuildInstruction(const BytecodeInstruction *instruction); 56 57 void InitEnv(BasicBlock *bb); 58 IsFailed()59 bool IsFailed() const 60 { 61 return failed_; 62 } 63 64 /// Return jump offset for instruction `inst`, 0 if it is not jump instruction. 65 static int64_t GetInstructionJumpOffset(const BytecodeInstruction *inst); 66 67 void SetCurrentBlock(BasicBlock *bb); 68 GetCurrentBlock()69 BasicBlock *GetCurrentBlock() const 70 { 71 return currentBb_; 72 } 73 74 void Prepare(bool isInlinedGraph); 75 76 void FixType(PhiInst *inst, BasicBlock *bb); 77 void FixType(Inst *inst); 78 void FixInstructions(); 79 void ResolveConstants(); 80 void SplitConstant(ConstantInst *constInst); 81 82 static void RemoveNotDominateInputs(SaveStateInst *saveState); 83 84 size_t GetPc(const uint8_t *instPtr) const; 85 CreateSafePoint(BasicBlock * bb)86 auto CreateSafePoint(BasicBlock *bb) 87 { 88 #ifndef NDEBUG 89 ResetSafepointDistance(); 90 #endif 91 return CreateSaveState(Opcode::SafePoint, bb->GetGuestPc()); 92 } 93 CreateSaveStateOsr(BasicBlock * bb)94 auto CreateSaveStateOsr(BasicBlock *bb) 95 { 96 return CreateSaveState(Opcode::SaveStateOsr, bb->GetGuestPc()); 97 } 98 CreateSaveStateDeoptimize(uint32_t pc)99 auto CreateSaveStateDeoptimize(uint32_t pc) 100 { 101 return CreateSaveState(Opcode::SaveStateDeoptimize, pc); 102 } 103 104 void UpdateDefs(); 105 bool UpdateDefsForPreds(size_t vreg, std::optional<Inst *> &value); 106 GetCurrentDefs()107 const auto &GetCurrentDefs() 108 { 109 ASSERT(currentDefs_ != nullptr); 110 return *currentDefs_; 111 } 112 113 void AddCatchPhiInputs(const ArenaUnorderedSet<BasicBlock *> &catchHandlers, const InstVector &defs, 114 Inst *throwableInst); 115 116 SaveStateInst *CreateSaveState(Opcode opc, size_t pc); 117 118 static void SetParamSpillFill(Graph *graph, ParameterInst *paramInst, size_t numArgs, size_t i, 119 DataType::Type type); 120 121 #ifndef NDEBUG 122 void TryInsertSafepoint(BasicBlock *bb = nullptr, bool insertSP = false) 123 { 124 auto curBb = bb != nullptr ? bb : currentBb_; 125 if (GetGraph()->IsBytecodeOptimizer() || curBb->IsOsrEntry() || 126 !g_options.IsCompilerEnforceSafepointPlacement()) { 127 return; 128 } 129 if ((curBb->GetLastInst() == nullptr || curBb->GetLastInst()->GetOpcode() != Opcode::SafePoint) && 130 (insertSP || --safepointDistance_ <= 0)) { 131 auto *sp = CreateSafePoint(curBb); 132 currentBb_->AppendInst(sp); 133 #ifdef PANDA_COMPILER_DEBUG_INFO 134 if (sp->GetPc() != INVALID_PC) { 135 sp->SetCurrentMethod(method_); 136 } 137 #endif 138 COMPILER_LOG(DEBUG, IR_BUILDER) << *sp; 139 } 140 } 141 #endif 142 143 protected: 144 template <typename T> AddInstruction(T inst)145 void AddInstruction(T inst) 146 { 147 ASSERT(currentBb_); 148 currentBb_->AppendInst(inst); 149 #ifdef PANDA_COMPILER_DEBUG_INFO 150 if (inst->GetPc() != INVALID_PC) { 151 inst->SetCurrentMethod(method_); 152 } 153 #endif 154 COMPILER_LOG(DEBUG, IR_BUILDER) << *inst; 155 } 156 157 template <typename T, typename... Ts> AddInstruction(T inst,Ts...insts)158 void AddInstruction(T inst, Ts... insts) 159 { 160 AddInstruction(inst); 161 AddInstruction(insts...); 162 } 163 164 void UpdateDefinition(size_t vreg, Inst *inst); 165 void UpdateDefinitionAcc(Inst *inst); 166 void UpdateDefinitionLexEnv(Inst *inst); 167 Inst *GetDefinition(size_t vreg); 168 Inst *GetDefinitionAcc(); 169 Inst *GetEnvDefinition(uint8_t envIdx); 170 171 void BuildCastToAnyString(const BytecodeInstruction *bcInst); 172 GetGraph()173 Graph *GetGraph() 174 { 175 return graph_; 176 } 177 GetGraph()178 const Graph *GetGraph() const 179 { 180 return graph_; 181 } 182 GetRuntime()183 const RuntimeInterface *GetRuntime() const 184 { 185 return runtime_; 186 } 187 GetRuntime()188 RuntimeInterface *GetRuntime() 189 { 190 return runtime_; 191 } 192 GetMethod()193 RuntimeInterface::MethodPtr GetMethod() const 194 { 195 return method_; 196 } 197 198 /// Get count of arguments for the method specified by id 199 size_t GetMethodArgumentsCount(uintptr_t id) const; 200 201 private: 202 void SyncWithGraph(); 203 204 void UpdateDefsForCatch(); 205 void UpdateDefsForLoopHead(); 206 GetVRegsCount()207 size_t GetVRegsCount() const 208 { 209 return vregsAndArgsCount_ + 1 + GetGraph()->GetEnvCount(); 210 } 211 212 ConstantInst *FindOrCreate32BitConstant(uint32_t value); 213 ConstantInst *FindOrCreateConstant(uint64_t value); 214 ConstantInst *FindOrCreateAnyConstant(DataType::Any value); 215 ConstantInst *FindOrCreateDoubleConstant(double value); 216 ConstantInst *FindOrCreateFloatConstant(float value); 217 218 enum SaveStateType { 219 CHECK = 0, // side_exit = true, move_to_side_exit = true 220 CALL, // side_exit = false, move_to_side_exit = false 221 VIRT_CALL // side_exit = true, move_to_side_exit = false 222 }; 223 224 ClassInst *CreateLoadAndInitClassGeneric(uint32_t classId, size_t pc); 225 CreateCast(Inst * input,DataType::Type type,DataType::Type operandsType,size_t pc)226 Inst *CreateCast(Inst *input, DataType::Type type, DataType::Type operandsType, size_t pc) 227 { 228 auto cast = GetGraph()->CreateInstCast(type, pc, input, operandsType); 229 if (!input->HasType()) { 230 input->SetType(operandsType); 231 } 232 return cast; 233 } 234 CreateNewObjectInst(size_t pc,uint32_t typeId,SaveStateInst * saveState,Inst * initClass)235 NewObjectInst *CreateNewObjectInst(size_t pc, uint32_t typeId, SaveStateInst *saveState, Inst *initClass) 236 { 237 auto newObj = graph_->CreateInstNewObject(DataType::REFERENCE, pc, initClass, saveState, 238 TypeIdMixin {typeId, graph_->GetMethod()}); 239 return newObj; 240 } 241 242 template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE = true> 243 class BuildCallHelper { 244 public: 245 BuildCallHelper(const BytecodeInstruction *bcInst, InstBuilder *builder, Inst *additionalInput = nullptr); 246 247 void BuildIntrinsic(); 248 void BuildDefaultIntrinsic(RuntimeInterface::IntrinsicId intrinsicId, bool isVirtual); 249 void BuildDefaultStaticIntrinsic(RuntimeInterface::IntrinsicId intrinsicId); 250 void BuildDefaultVirtualCallIntrinsic(RuntimeInterface::IntrinsicId intrinsicId); 251 void BuildMonitorIntrinsic(bool isEnter); 252 253 void BuildStaticCallIntrinsic(RuntimeInterface::IntrinsicId intrinsicId); 254 void BuildVirtualCallIntrinsic(RuntimeInterface::IntrinsicId intrinsicId); 255 256 void AddCallInstruction(); 257 void BuildCallInst(uint32_t classId); 258 void BuildCallStaticInst(uint32_t classId); 259 void BuildInitClassInstForCallStatic(uint32_t classId); 260 261 void BuildCallVirtualInst(); 262 void SetCallArgs(Inst *additionalInput = nullptr); 263 uint32_t GetClassId(); GetGraph()264 auto GetGraph() 265 { 266 return builder_->GetGraph(); 267 } GetRuntime()268 auto GetRuntime() 269 { 270 return builder_->GetRuntime(); 271 } GetMethod()272 auto GetMethod() 273 { 274 return builder_->GetMethod(); 275 } Builder()276 auto Builder() 277 { 278 return builder_; 279 } 280 281 private: 282 InstBuilder *builder_ {}; 283 const BytecodeInstruction *bcInst_ {}; 284 RuntimeInterface::MethodPtr method_ {}; 285 uint32_t methodId_ {}; 286 uint32_t pc_ {}; 287 InputTypesMixin<DynamicInputsInst> *call_ {}; 288 Inst *resolver_ {}; 289 Inst *nullCheck_ {}; 290 SaveStateInst *saveState_ {}; 291 bool hasImplicitArg_ {}; 292 }; 293 Inst *GetArgDefinition(const BytecodeInstruction *bcInst, size_t idx, bool accRead, bool isRange = false); 294 Inst *GetArgDefinitionRange(const BytecodeInstruction *bcInst, size_t idx); 295 template <bool IS_VIRTUAL> 296 void AddArgNullcheckIfNeeded(RuntimeInterface::IntrinsicId intrinsic, Inst *inst, Inst *saveState, size_t bcAddr); 297 void BuildMonitor(const BytecodeInstruction *bcInst, Inst *def, bool isEnter); 298 Inst *BuildFloatInst(const BytecodeInstruction *bcInst); 299 template <bool IS_RANGE, bool ACC_READ> 300 void BuildIntrinsic(const BytecodeInstruction *bcInst, bool isRange, bool accRead); 301 template <bool IS_RANGE, bool ACC_READ> 302 void BuildDefaultIntrinsic(bool isVirtual, const BytecodeInstruction *bcInst); 303 void BuildAbsIntrinsic(const BytecodeInstruction *bcInst, bool accRead); 304 template <Opcode OPCODE> 305 void BuildBinaryOperationIntrinsic(const BytecodeInstruction *bcInst, bool accRead); 306 void BuildSqrtIntrinsic(const BytecodeInstruction *bcInst, bool accRead); 307 void BuildIsNanIntrinsic(const BytecodeInstruction *bcInst, bool accRead); 308 void BuildStringLengthIntrinsic(const BytecodeInstruction *bcInst, bool accRead); 309 void BuildStringIsEmptyIntrinsic(const BytecodeInstruction *bcInst, bool accRead); 310 void BuildCharIsUpperCaseIntrinsic(const BytecodeInstruction *bcInst, bool accRead); 311 void BuildCharToUpperCaseIntrinsic(const BytecodeInstruction *bcInst, bool accRead); 312 void BuildCharIsLowerCaseIntrinsic(const BytecodeInstruction *bcInst, bool accRead); 313 void BuildCharToLowerCaseIntrinsic(const BytecodeInstruction *bcInst, bool accRead); 314 void BuildMonitorIntrinsic(const BytecodeInstruction *bcInst, bool isEnter, bool accRead); 315 virtual void BuildThrow(const BytecodeInstruction *bcInst); 316 void BuildLenArray(const BytecodeInstruction *bcInst); 317 virtual void BuildNewArray(const BytecodeInstruction *bcInst); 318 virtual void BuildNewObject(const BytecodeInstruction *bcInst); 319 virtual void BuildLoadConstArray(const BytecodeInstruction *bcInst); 320 void BuildLoadConstStringArray(const BytecodeInstruction *bcInst); 321 template <typename T> 322 void BuildUnfoldLoadConstArray(const BytecodeInstruction *bcInst, DataType::Type type, 323 const pandasm::LiteralArray &litArray); 324 template <typename T> 325 void BuildUnfoldLoadConstPrimitiveArray(const BytecodeInstruction *bcInst, DataType::Type type, 326 const pandasm::LiteralArray &litArray, NewArrayInst *arrayInst); 327 template <typename T> 328 void BuildUnfoldLoadConstStringArray(const BytecodeInstruction *bcInst, DataType::Type type, 329 const pandasm::LiteralArray &litArray, NewArrayInst *arrayInst); 330 void BuildInitString(const BytecodeInstruction *bcInst); 331 virtual void BuildInitObject(const BytecodeInstruction *bcInst, bool isRange); 332 CallInst *BuildCallStaticForInitObject(const BytecodeInstruction *bcInst, uint32_t methodId, Inst **resolver); 333 void BuildMultiDimensionalArrayObject(const BytecodeInstruction *bcInst, bool isRange); 334 void BuildInitObjectMultiDimensionalArray(const BytecodeInstruction *bcInst, bool isRange); 335 template <bool IS_ACC_WRITE> 336 void BuildLoadObject(const BytecodeInstruction *bcInst, DataType::Type type); 337 template <bool IS_ACC_READ> 338 void BuildStoreObject(const BytecodeInstruction *bcInst, DataType::Type type); 339 Inst *BuildStoreObjectInst(const BytecodeInstruction *bcInst, DataType::Type type, RuntimeInterface::FieldPtr field, 340 uint32_t fieldId, Inst **resolveInst); 341 virtual void BuildLoadStatic(const BytecodeInstruction *bcInst, DataType::Type type); 342 Inst *BuildLoadStaticInst(size_t pc, DataType::Type type, uint32_t typeId, Inst *saveState); 343 virtual void BuildStoreStatic(const BytecodeInstruction *bcInst, DataType::Type type); 344 Inst *BuildStoreStaticInst(const BytecodeInstruction *bcInst, DataType::Type type, uint32_t typeId, 345 Inst *storeInput, Inst *saveState); 346 virtual void BuildCheckCast(const BytecodeInstruction *bcInst); 347 virtual void BuildIsInstance(const BytecodeInstruction *bcInst); 348 Inst *BuildLoadClass(RuntimeInterface::IdType typeId, size_t pc, Inst *saveState); 349 virtual void BuildLoadArray(const BytecodeInstruction *bcInst, DataType::Type type); 350 virtual void BuildStoreArray(const BytecodeInstruction *bcInst, DataType::Type type); 351 template <bool CREATE_REF_CHECK> 352 void BuildStoreArrayInst(const BytecodeInstruction *bcInst, DataType::Type type, Inst *arrayRef, Inst *index, 353 Inst *value); 354 std::tuple<SaveStateInst *, Inst *, LengthMethodInst *, BoundsCheckInst *> BuildChecksBeforeArray( 355 size_t pc, Inst *arrayRef, bool withNullcheck = true); 356 template <Opcode OPCODE> 357 void BuildLoadFromPool(const BytecodeInstruction *bcInst); 358 void BuildCastToAnyNumber(const BytecodeInstruction *bcInst); 359 AnyTypeCheckInst *BuildAnyTypeCheckInst(size_t bcAddr, Inst *input, Inst *saveState, 360 AnyBaseType type = AnyBaseType::UNDEFINED_TYPE); 361 void InitAnyTypeCheckInst(AnyTypeCheckInst *anyCheck, bool typeWasProfiled = false, 362 profiling::AnyInputType allowedInputType = {}) 363 { 364 anyCheck->SetAllowedInputType(allowedInputType); 365 anyCheck->SetIsTypeWasProfiled(typeWasProfiled); 366 } 367 368 bool TryBuildStringCharAtIntrinsic(const BytecodeInstruction *bcInst, bool accRead); 369 #include "inst_builder_extensions.inl.h" 370 GetClassId()371 auto GetClassId() const 372 { 373 return classId_; 374 } 375 GetNoTypeMarker()376 Marker GetNoTypeMarker() const 377 { 378 return noTypeMarker_; 379 } 380 GetVisitedBlockMarker()381 Marker GetVisitedBlockMarker() const 382 { 383 return visitedBlockMarker_; 384 } 385 ForceUnresolved()386 bool ForceUnresolved() const 387 { 388 #ifndef NDEBUG 389 return g_options.IsCompilerForceUnresolved() && !graph_->IsBytecodeOptimizer(); 390 #else 391 return false; 392 #endif 393 } 394 395 void SetTypeRec(Inst *inst, DataType::Type type); 396 397 /// Convert Panda bytecode type to COMPILER IR type 398 static DataType::Type ConvertPbcType(panda_file::Type type); 399 400 /// Get return type of the method specified by id 401 DataType::Type GetMethodReturnType(uintptr_t id) const; 402 /// Get type of argument of the method specified by id 403 DataType::Type GetMethodArgumentType(uintptr_t id, size_t index) const; 404 /// Get return type of currently compiling method 405 DataType::Type GetCurrentMethodReturnType() const; 406 /// Get type of argument of currently compiling method 407 DataType::Type GetCurrentMethodArgumentType(size_t index) const; 408 /// Get count of arguments of currently compiling method 409 size_t GetCurrentMethodArgumentsCount() const; 410 411 template <bool IS_STATIC> 412 bool IsInConstructor() const; 413 414 #ifndef PANDA_ETS_INTEROP_JS TryBuildInteropCall(const BytecodeInstruction * bcInst,bool isRange,bool accRead)415 bool TryBuildInteropCall([[maybe_unused]] const BytecodeInstruction *bcInst, [[maybe_unused]] bool isRange, 416 [[maybe_unused]] bool accRead) 417 { 418 return false; 419 } 420 #endif 421 422 #ifndef NDEBUG ResetSafepointDistance()423 void ResetSafepointDistance() 424 { 425 safepointDistance_ = static_cast<int32_t>(g_options.GetCompilerSafepointDistanceLimit()); 426 } 427 #endif 428 429 private: 430 static constexpr size_t ONE_FOR_OBJECT = 1; 431 static constexpr size_t ONE_FOR_SSTATE = 1; 432 433 Graph *graph_ {nullptr}; 434 RuntimeInterface *runtime_ {nullptr}; 435 BasicBlock *currentBb_ {nullptr}; 436 437 RuntimeInterface::MethodProfile methodProfile_ {}; 438 439 // Definitions vector of currently processed basic block 440 InstVector *currentDefs_ {nullptr}; 441 // Result of LoadFromConstantPool which will be added to SaveState inputs 442 Inst *additionalDef_ {nullptr}; 443 // Contains definitions of the virtual registers in all basic blocks 444 ArenaVector<InstVector> defs_; 445 446 RuntimeInterface::MethodPtr method_ {nullptr}; 447 // Set to true if builder failed to build IR 448 bool failed_ {false}; 449 // Number of virtual registers and method arguments 450 const size_t vregsAndArgsCount_; 451 // Marker for instructions with undefined type in the building phase 452 Marker noTypeMarker_; 453 Marker visitedBlockMarker_; 454 455 // Pointer to start position of bytecode instructions buffer 456 const uint8_t *instructionsBuf_ {nullptr}; 457 458 CallInst *callerInst_ {nullptr}; 459 uint32_t inliningDepth_ {0}; 460 size_t classId_; 461 #ifndef NDEBUG 462 int32_t safepointDistance_ {0}; 463 #endif 464 #include "intrinsics_ir_build.inl.h" 465 }; 466 } // namespace ark::compiler 467 468 #endif // PANDA_INST_BUILDER_H 469