• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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