• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_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 
IsInBootContext()113     bool IsInBootContext()
114     {
115         auto method = static_cast<ark::Method *>(GetGraph()->GetMethod());
116         return method->GetClass()->GetLoadContext()->IsBootContext();
117     }
118 
119     void AddCatchPhiInputs(const ArenaUnorderedSet<BasicBlock *> &catchHandlers, const InstVector &defs,
120                            Inst *throwableInst);
121 
122     SaveStateInst *CreateSaveState(Opcode opc, size_t pc);
123 
124     static void SetParamSpillFill(Graph *graph, ParameterInst *paramInst, size_t numArgs, size_t i,
125                                   DataType::Type type);
126 
127 #ifndef NDEBUG
128     void TryInsertSafepoint(BasicBlock *bb = nullptr, bool insertSP = false)
129     {
130         auto curBb = bb != nullptr ? bb : currentBb_;
131         if (GetGraph()->IsBytecodeOptimizer() || curBb->IsOsrEntry() ||
132             !g_options.IsCompilerEnforceSafepointPlacement()) {
133             return;
134         }
135         if ((curBb->GetLastInst() == nullptr || curBb->GetLastInst()->GetOpcode() != Opcode::SafePoint) &&
136             (insertSP || --safepointDistance_ <= 0)) {
137             auto *sp = CreateSafePoint(curBb);
138             currentBb_->AppendInst(sp);
139 #ifdef PANDA_COMPILER_DEBUG_INFO
140             if (sp->GetPc() != INVALID_PC) {
141                 sp->SetCurrentMethod(method_);
142             }
143 #endif
144             COMPILER_LOG(DEBUG, IR_BUILDER) << *sp;
145         }
146     }
147 #endif
148 
149 protected:
150     template <typename T>
AddInstruction(T inst)151     void AddInstruction(T inst)
152     {
153         ASSERT(currentBb_);
154         currentBb_->AppendInst(inst);
155 #ifdef PANDA_COMPILER_DEBUG_INFO
156         if (inst->GetPc() != INVALID_PC) {
157             inst->SetCurrentMethod(method_);
158         }
159 #endif
160         COMPILER_LOG(DEBUG, IR_BUILDER) << *inst;
161     }
162 
163     template <typename T, typename... Ts>
AddInstruction(T inst,Ts...insts)164     void AddInstruction(T inst, Ts... insts)
165     {
166         AddInstruction(inst);
167         AddInstruction(insts...);
168     }
169 
170     void UpdateDefinition(size_t vreg, Inst *inst);
171     void UpdateDefinitionAcc(Inst *inst);
172     void UpdateDefinitionLexEnv(Inst *inst);
173     Inst *GetDefinition(size_t vreg);
174     Inst *GetDefinitionAcc();
175     Inst *GetEnvDefinition(uint8_t envIdx);
176 
177     void BuildCastToAnyString(const BytecodeInstruction *bcInst);
178 
GetGraph()179     Graph *GetGraph()
180     {
181         return graph_;
182     }
183 
GetGraph()184     const Graph *GetGraph() const
185     {
186         return graph_;
187     }
188 
GetRuntime()189     const RuntimeInterface *GetRuntime() const
190     {
191         return runtime_;
192     }
193 
GetRuntime()194     RuntimeInterface *GetRuntime()
195     {
196         return runtime_;
197     }
198 
GetMethod()199     RuntimeInterface::MethodPtr GetMethod() const
200     {
201         return method_;
202     }
203 
204     /// Get count of arguments for the method specified by id
205     size_t GetMethodArgumentsCount(uintptr_t id) const;
206 
207 private:
208     void SyncWithGraph();
209 
210     void UpdateDefsForCatch();
211     void UpdateDefsForLoopHead();
212 
GetVRegsCount()213     size_t GetVRegsCount() const
214     {
215         return vregsAndArgsCount_ + 1 + GetGraph()->GetEnvCount();
216     }
217 
SetCallNativeFlags(CallInst * callInst,RuntimeInterface::MethodPtr method)218     void SetCallNativeFlags(CallInst *callInst, RuntimeInterface::MethodPtr method) const
219     {
220         bool isNativeApi = method != nullptr && GetRuntime()->IsMethodNativeApi(method);
221         callInst->SetIsNative(isNativeApi);
222         callInst->SetCanNativeException(isNativeApi && GetRuntime()->HasNativeException(method));
223     }
224 
225     ConstantInst *FindOrCreate32BitConstant(uint32_t value);
226     ConstantInst *FindOrCreateConstant(uint64_t value);
227     ConstantInst *FindOrCreateAnyConstant(DataType::Any value);
228     ConstantInst *FindOrCreateDoubleConstant(double value);
229     ConstantInst *FindOrCreateFloatConstant(float value);
230 
231     enum SaveStateType {
232         CHECK = 0,  // side_exit = true,  move_to_side_exit = true
233         CALL,       // side_exit = false,  move_to_side_exit = false
234         VIRT_CALL   // side_exit = true,  move_to_side_exit = false
235     };
236 
237     ClassInst *CreateLoadAndInitClassGeneric(uint32_t classId, size_t pc);
238 
CreateCast(Inst * input,DataType::Type type,DataType::Type operandsType,size_t pc)239     Inst *CreateCast(Inst *input, DataType::Type type, DataType::Type operandsType, size_t pc)
240     {
241         auto cast = GetGraph()->CreateInstCast(type, pc, input, operandsType);
242         if (!input->HasType()) {
243             input->SetType(operandsType);
244         }
245         return cast;
246     }
247 
CreateNewObjectInst(size_t pc,uint32_t typeId,SaveStateInst * saveState,Inst * initClass)248     NewObjectInst *CreateNewObjectInst(size_t pc, uint32_t typeId, SaveStateInst *saveState, Inst *initClass)
249     {
250         auto newObj = graph_->CreateInstNewObject(DataType::REFERENCE, pc, initClass, saveState,
251                                                   TypeIdMixin {typeId, graph_->GetMethod()});
252         return newObj;
253     }
254 
255     template <Opcode OPCODE, bool IS_RANGE, bool ACC_READ, bool HAS_SAVE_STATE = true>
256     class BuildCallHelper {
257     public:
258         BuildCallHelper(const BytecodeInstruction *bcInst, InstBuilder *builder, Inst *additionalInput = nullptr);
259 
260         bool TryBuildIntrinsic();
261         void BuildIntrinsic();
262         void BuildDefaultIntrinsic(RuntimeInterface::IntrinsicId intrinsicId, bool isVirtual);
263         void BuildDefaultStaticIntrinsic(RuntimeInterface::IntrinsicId intrinsicId);
264         void BuildDefaultVirtualCallIntrinsic(RuntimeInterface::IntrinsicId intrinsicId);
265         void BuildMonitorIntrinsic(bool isEnter);
266 
267         void BuildStaticCallIntrinsic(RuntimeInterface::IntrinsicId intrinsicId);
268         void BuildVirtualCallIntrinsic(RuntimeInterface::IntrinsicId intrinsicId);
269 
270         void AddCallInstruction();
271         void BuildCallInst(uint32_t classId);
272         void BuildCallStaticInst(uint32_t classId);
273         void BuildInitClassInstForCallStatic(uint32_t classId);
274 
275         void BuildCallVirtualInst();
276         void SetCallArgs(Inst *additionalInput = nullptr);
277         uint32_t GetClassId();
GetGraph()278         auto GetGraph()
279         {
280             return builder_->GetGraph();
281         }
GetRuntime()282         auto GetRuntime()
283         {
284             return builder_->GetRuntime();
285         }
GetMethod()286         auto GetMethod()
287         {
288             return builder_->GetMethod();
289         }
Builder()290         auto Builder()
291         {
292             return builder_;
293         }
294 
295     private:
296         InstBuilder *builder_ {};
297         const BytecodeInstruction *bcInst_ {};
298         RuntimeInterface::MethodPtr method_ {};
299         uint32_t methodId_ {};
300         uint32_t pc_ {};
301         InputTypesMixin<DynamicInputsInst> *call_ {};
302         Inst *resolver_ {};
303         Inst *nullCheck_ {};
304         SaveStateInst *saveState_ {};
305         bool hasImplicitArg_ {};
306     };
307     Inst *GetArgDefinition(const BytecodeInstruction *bcInst, size_t idx, bool accRead, bool isRange = false);
308     Inst *GetArgDefinitionRange(const BytecodeInstruction *bcInst, size_t idx);
309     template <bool IS_VIRTUAL>
310     void AddArgNullcheckIfNeeded(RuntimeInterface::IntrinsicId intrinsic, Inst *inst, Inst *saveState, size_t bcAddr);
311     void BuildMonitor(const BytecodeInstruction *bcInst, Inst *def, bool isEnter);
312     Inst *BuildFloatInst(const BytecodeInstruction *bcInst);
313     template <bool IS_RANGE, bool ACC_READ>
314     void BuildIntrinsic(const BytecodeInstruction *bcInst, bool isRange, bool accRead);
315     template <bool IS_RANGE, bool ACC_READ>
316     void BuildDefaultIntrinsic(bool isVirtual, const BytecodeInstruction *bcInst);
317     void BuildAbsIntrinsic(const BytecodeInstruction *bcInst, bool accRead);
318     template <Opcode OPCODE>
319     void BuildBinaryOperationIntrinsic(const BytecodeInstruction *bcInst, bool accRead);
320     void BuildSqrtIntrinsic(const BytecodeInstruction *bcInst, bool accRead);
321     void BuildIsNanIntrinsic(const BytecodeInstruction *bcInst, bool accRead);
322     void BuildStringLengthIntrinsic(const BytecodeInstruction *bcInst, bool accRead);
323     void BuildStringIsEmptyIntrinsic(const BytecodeInstruction *bcInst, bool accRead);
324     void BuildCharIsUpperCaseIntrinsic(const BytecodeInstruction *bcInst, bool accRead);
325     void BuildCharToUpperCaseIntrinsic(const BytecodeInstruction *bcInst, bool accRead);
326     void BuildCharIsLowerCaseIntrinsic(const BytecodeInstruction *bcInst, bool accRead);
327     void BuildCharToLowerCaseIntrinsic(const BytecodeInstruction *bcInst, bool accRead);
328     void BuildMonitorIntrinsic(const BytecodeInstruction *bcInst, bool isEnter, bool accRead);
329     virtual void BuildThrow(const BytecodeInstruction *bcInst);
330     void BuildLenArray(const BytecodeInstruction *bcInst);
331     virtual void BuildNewArray(const BytecodeInstruction *bcInst);
332     virtual void BuildNewObject(const BytecodeInstruction *bcInst);
333     virtual void BuildLoadConstArray(const BytecodeInstruction *bcInst);
334     void BuildLoadConstStringArray(const BytecodeInstruction *bcInst);
335     template <typename T>
336     void BuildUnfoldLoadConstArray(const BytecodeInstruction *bcInst, DataType::Type type,
337                                    const pandasm::LiteralArray &litArray);
338     template <typename T>
339     void BuildUnfoldLoadConstPrimitiveArray(const BytecodeInstruction *bcInst, DataType::Type type,
340                                             const pandasm::LiteralArray &litArray, NewArrayInst *arrayInst);
341     template <typename T>
342     void BuildUnfoldLoadConstStringArray(const BytecodeInstruction *bcInst, DataType::Type type,
343                                          const pandasm::LiteralArray &litArray, NewArrayInst *arrayInst);
344     void BuildInitString(const BytecodeInstruction *bcInst);
345     virtual void BuildInitObject(const BytecodeInstruction *bcInst, bool isRange);
346     CallInst *BuildCallStaticForInitObject(const BytecodeInstruction *bcInst, uint32_t methodId, Inst **resolver);
347     void BuildMultiDimensionalArrayObject(const BytecodeInstruction *bcInst, bool isRange);
348     void BuildInitObjectMultiDimensionalArray(const BytecodeInstruction *bcInst, bool isRange);
349     template <bool IS_ACC_WRITE>
350     void BuildLoadObject(const BytecodeInstruction *bcInst, DataType::Type type);
351     template <bool IS_ACC_READ>
352     void BuildStoreObject(const BytecodeInstruction *bcInst, DataType::Type type);
353     Inst *BuildStoreObjectInst(const BytecodeInstruction *bcInst, DataType::Type type, RuntimeInterface::FieldPtr field,
354                                uint32_t fieldId, Inst **resolveInst);
355     virtual void BuildLoadStatic(const BytecodeInstruction *bcInst, DataType::Type type);
356     Inst *BuildLoadStaticInst(size_t pc, DataType::Type type, uint32_t typeId, Inst *saveState);
357     virtual void BuildStoreStatic(const BytecodeInstruction *bcInst, DataType::Type type);
358     Inst *BuildStoreStaticInst(const BytecodeInstruction *bcInst, DataType::Type type, uint32_t typeId,
359                                Inst *storeInput, Inst *saveState);
360     virtual void BuildCheckCast(const BytecodeInstruction *bcInst);
361     virtual void BuildIsInstance(const BytecodeInstruction *bcInst);
362     Inst *BuildLoadClass(RuntimeInterface::IdType typeId, size_t pc, Inst *saveState);
363     virtual void BuildLoadArray(const BytecodeInstruction *bcInst, DataType::Type type);
364     virtual void BuildStoreArray(const BytecodeInstruction *bcInst, DataType::Type type);
365     template <bool CREATE_REF_CHECK>
366     void BuildStoreArrayInst(const BytecodeInstruction *bcInst, DataType::Type type, Inst *arrayRef, Inst *index,
367                              Inst *value);
368     std::tuple<SaveStateInst *, Inst *, LengthMethodInst *, BoundsCheckInst *> BuildChecksBeforeArray(
369         size_t pc, Inst *arrayRef, bool withNullcheck = true);
370     template <Opcode OPCODE>
371     void BuildLoadFromPool(const BytecodeInstruction *bcInst);
372     void BuildCastToAnyNumber(const BytecodeInstruction *bcInst);
373     AnyTypeCheckInst *BuildAnyTypeCheckInst(size_t bcAddr, Inst *input, Inst *saveState,
374                                             AnyBaseType type = AnyBaseType::UNDEFINED_TYPE);
375     void InitAnyTypeCheckInst(AnyTypeCheckInst *anyCheck, bool typeWasProfiled = false,
376                               profiling::AnyInputType allowedInputType = {})
377     {
378         anyCheck->SetAllowedInputType(allowedInputType);
379         anyCheck->SetIsTypeWasProfiled(typeWasProfiled);
380     }
381 
382     bool TryBuildStringCharAtIntrinsic(const BytecodeInstruction *bcInst, bool accRead);
383 #include "inst_builder_extensions.inl.h"
384 
GetClassId()385     auto GetClassId() const
386     {
387         return classId_;
388     }
389 
GetNoTypeMarker()390     Marker GetNoTypeMarker() const
391     {
392         return noTypeMarker_;
393     }
394 
GetVisitedBlockMarker()395     Marker GetVisitedBlockMarker() const
396     {
397         return visitedBlockMarker_;
398     }
399 
ForceUnresolved()400     bool ForceUnresolved() const
401     {
402 #ifndef NDEBUG
403         return g_options.IsCompilerForceUnresolved() && !graph_->IsBytecodeOptimizer();
404 #else
405         return false;
406 #endif
407     }
408 
409     void SetTypeRec(Inst *inst, DataType::Type type);
410 
411     /// Convert Panda bytecode type to COMPILER IR type
412     static DataType::Type ConvertPbcType(panda_file::Type type);
413 
414     /// Get return type of the method specified by id
415     DataType::Type GetMethodReturnType(uintptr_t id) const;
416     /// Get type of argument of the method specified by id
417     DataType::Type GetMethodArgumentType(uintptr_t id, size_t index) const;
418     /// Get return type of currently compiling method
419     DataType::Type GetCurrentMethodReturnType() const;
420     /// Get type of argument of currently compiling method
421     DataType::Type GetCurrentMethodArgumentType(size_t index) const;
422     /// Get count of arguments of currently compiling method
423     size_t GetCurrentMethodArgumentsCount() const;
424 
425     template <bool IS_STATIC>
426     bool IsInConstructor() const;
427 
428 #ifndef PANDA_ETS_INTEROP_JS
TryBuildInteropCall(const BytecodeInstruction * bcInst,bool isRange,bool accRead)429     bool TryBuildInteropCall([[maybe_unused]] const BytecodeInstruction *bcInst, [[maybe_unused]] bool isRange,
430                              [[maybe_unused]] bool accRead)
431     {
432         return false;
433     }
434 #endif
435 
436 #ifndef NDEBUG
ResetSafepointDistance()437     void ResetSafepointDistance()
438     {
439         safepointDistance_ = static_cast<int32_t>(g_options.GetCompilerSafepointDistanceLimit());
440     }
441 #endif
442 
443 private:
444     static constexpr size_t ONE_FOR_OBJECT = 1;
445     static constexpr size_t ONE_FOR_SSTATE = 1;
446 
447     Graph *graph_ {nullptr};
448     RuntimeInterface *runtime_ {nullptr};
449     BasicBlock *currentBb_ {nullptr};
450 
451     RuntimeInterface::MethodProfile methodProfile_ {};
452 
453     // Definitions vector of currently processed basic block
454     InstVector *currentDefs_ {nullptr};
455     // Result of LoadFromConstantPool which will be added to SaveState inputs
456     Inst *additionalDef_ {nullptr};
457     // Contains definitions of the virtual registers in all basic blocks
458     ArenaVector<InstVector> defs_;
459 
460     RuntimeInterface::MethodPtr method_ {nullptr};
461     // Set to true if builder failed to build IR
462     bool failed_ {false};
463     // Number of virtual registers and method arguments
464     const size_t vregsAndArgsCount_;
465     // Marker for instructions with undefined type in the building phase
466     Marker noTypeMarker_;
467     Marker visitedBlockMarker_;
468 
469     // Pointer to start position of bytecode instructions buffer
470     const uint8_t *instructionsBuf_ {nullptr};
471 
472     CallInst *callerInst_ {nullptr};
473     uint32_t inliningDepth_ {0};
474     size_t classId_;
475 #ifndef NDEBUG
476     int32_t safepointDistance_ {0};
477 #endif
478 #include "intrinsics_ir_build.inl.h"
479 };
480 }  // namespace ark::compiler
481 
482 #endif  // PANDA_INST_BUILDER_H
483