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