• 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_INTERPRETER_INSTRUCTION_HANDLER_BASE_H_
17 #define PANDA_INTERPRETER_INSTRUCTION_HANDLER_BASE_H_
18 
19 #include <isa_constants_gen.h>
20 #include "runtime/include/method.h"
21 #include "runtime/interpreter/instruction_handler_state.h"
22 
23 namespace ark::interpreter {
24 
25 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
26 #define LOG_INST()                                                                           \
27     LOG(DEBUG, INTERPRETER) << std::hex << std::setw(sizeof(uintptr_t)) << std::setfill('0') \
28                             << reinterpret_cast<uintptr_t>(this->GetInst().GetAddress()) << std::dec << ": "
29 
30 #ifdef PANDA_ENABLE_GLOBAL_REGISTER_VARIABLES
31 #include "arch/global_regs.h"
32 
33 class StaticFrameHandlerT : public StaticFrameHandler {
34 public:
StaticFrameHandlerT(Frame * frame)35     ALWAYS_INLINE inline explicit StaticFrameHandlerT(Frame *frame) : StaticFrameHandler(frame) {}
36 
37 private:
GetVRegisters()38     ALWAYS_INLINE inline interpreter::VRegister *GetVRegisters()
39     {
40         return reinterpret_cast<interpreter::VRegister *>(arch::regs::GetFp());
41     }
42 
GetMirrorVRegisters()43     ALWAYS_INLINE inline interpreter::VRegister *GetMirrorVRegisters()
44     {
45         return reinterpret_cast<interpreter::VRegister *>(arch::regs::GetMirrorFp());
46     }
47 };
48 
49 class DynamicFrameHandlerT : public DynamicFrameHandler {
50 public:
DynamicFrameHandlerT(Frame * frame)51     ALWAYS_INLINE inline explicit DynamicFrameHandlerT(Frame *frame) : DynamicFrameHandler(frame) {}
52 
53 private:
GetVRegisters()54     ALWAYS_INLINE inline interpreter::VRegister *GetVRegisters()
55     {
56         return reinterpret_cast<interpreter::VRegister *>(arch::regs::GetFp());
57     }
58 };
59 
60 #else
61 
62 using StaticFrameHandlerT = StaticFrameHandler;
63 using DynamicFrameHandlerT = DynamicFrameHandler;
64 
65 #endif  // PANDA_ENABLE_GLOBAL_REGISTER_VARIABLES
66 
67 template <class RuntimeIfaceT, bool IS_DYNAMIC>
68 class InstructionHandlerBase {
69 public:
InstructionHandlerBase(InstructionHandlerState * state)70     ALWAYS_INLINE explicit InstructionHandlerBase(InstructionHandlerState *state) : state_(state) {}
71 
GetExceptionOpcode()72     ALWAYS_INLINE uint16_t GetExceptionOpcode() const
73     {
74         // Need to call GetInst().GetOpcode() in this case too, otherwise compiler can generate non optimal code
75         return static_cast<unsigned>(GetInst().GetPrimaryOpcode()) + state_->GetOpcodeExtension();
76     }
77 
GetPrimaryOpcode()78     ALWAYS_INLINE uint8_t GetPrimaryOpcode() const
79     {
80         return static_cast<unsigned>(GetInst().GetPrimaryOpcode());
81     }
82 
GetSecondaryOpcode()83     ALWAYS_INLINE uint8_t GetSecondaryOpcode() const
84     {
85         return static_cast<unsigned>(GetInst().GetSecondaryOpcode());
86     }
87 
IsPrimaryOpcodeValid()88     ALWAYS_INLINE bool IsPrimaryOpcodeValid() const
89     {
90         return GetInst().IsPrimaryOpcodeValid();
91     }
92 
DumpVRegs()93     void DumpVRegs()
94     {
95 #if PANDA_ENABLE_SLOW_DEBUG
96         // Skip dump if logger is disable. This allows us to speed up interpretation in the 'Debug' build.
97         if (!Logger::IsLoggingOn(Logger::Level::DEBUG, Logger::Component::INTERPRETER)) {
98             return;
99         }
100 
101         static constexpr uint64_t STANDARD_DEBUG_INDENT = 5;
102         LOG(DEBUG, INTERPRETER) << PandaString(STANDARD_DEBUG_INDENT, ' ') << "acc."
103                                 << GetAccAsVReg<IS_DYNAMIC>().DumpVReg();
104         auto frameHandler = GetFrameHandler();
105         for (size_t i = 0; i < GetFrame()->GetSize(); ++i) {
106             LOG(DEBUG, INTERPRETER) << PandaString(STANDARD_DEBUG_INDENT, ' ') << "v" << i << "."
107                                     << frameHandler.GetVReg(i).DumpVReg();
108         }
109 #endif
110     }
111 
UpdateBytecodeOffset()112     ALWAYS_INLINE uint32_t UpdateBytecodeOffset()
113     {
114         auto pc = GetBytecodeOffset();
115         GetFrame()->SetBytecodeOffset(pc);
116         return pc;
117     }
118 
InstrumentInstruction()119     void InstrumentInstruction()
120     {
121         // Should set ACC to Frame, so that ACC will be marked when GC
122         GetFrame()->SetAcc(GetAcc());
123 
124         auto pc = UpdateBytecodeOffset();
125         RuntimeIfaceT::GetNotificationManager()->BytecodePcChangedEvent(GetThread(), GetFrame()->GetMethod(), pc);
126 
127         // BytecodePcChangedEvent hook can call the GC, so we need to update the ACC
128         GetAcc() = GetFrame()->GetAcc();
129     }
130 
InstrumentForceReturn()131     void InstrumentForceReturn()
132     {
133         interpreter::AccVRegister result;  // empty result, because force exit
134         GetAcc() = result;
135         GetFrame()->GetAcc() = result;
136     }
137 
GetAcc()138     ALWAYS_INLINE const AccVRegisterT &GetAcc() const
139     {
140         return state_->GetAcc();
141     }
142 
GetAcc()143     ALWAYS_INLINE AccVRegisterT &GetAcc()
144     {
145         return state_->GetAcc();
146     }
147 
148 #ifdef PANDA_ENABLE_GLOBAL_REGISTER_VARIABLES
149     template <bool IS_DYNAMIC_T = IS_DYNAMIC>
GetAccAsVReg()150     ALWAYS_INLINE AccVRegisterTRef<IS_DYNAMIC_T> GetAccAsVReg()
151     {
152         return AccVRegisterTRef<IS_DYNAMIC_T>(&state_->GetAcc());
153     }
154 #else
155     template <bool IS_DYNAMIC_T = IS_DYNAMIC>
GetAccAsVReg()156     ALWAYS_INLINE typename std::enable_if<IS_DYNAMIC_T, DynamicVRegisterRef>::type GetAccAsVReg()
157     {
158         return state_->GetAcc().template AsVRegRef<IS_DYNAMIC_T>();
159     }
160 
161     template <bool IS_DYNAMIC_T = IS_DYNAMIC>
GetAccAsVReg()162     ALWAYS_INLINE typename std::enable_if<!IS_DYNAMIC_T, StaticVRegisterRef>::type GetAccAsVReg()
163     {
164         return state_->GetAcc().template AsVRegRef<IS_DYNAMIC_T>();
165     }
166 #endif  // PANDA_ENABLE_GLOBAL_REGISTER_VARIABLES
167 
GetInst()168     ALWAYS_INLINE BytecodeInstruction GetInst() const
169     {
170         return state_->GetInst();
171     }
172 
173     void DebugDump();
174 
175     template <bool IS_DYNAMIC_T = IS_DYNAMIC>
GetFrameHandler()176     ALWAYS_INLINE typename std::enable_if<IS_DYNAMIC_T, DynamicFrameHandlerT>::type GetFrameHandler()
177     {
178         return DynamicFrameHandlerT(state_->GetFrame());
179     }
180 
181     template <bool IS_DYNAMIC_T = IS_DYNAMIC>
GetFrameHandler()182     ALWAYS_INLINE typename std::enable_if<!IS_DYNAMIC_T, StaticFrameHandlerT>::type GetFrameHandler()
183     {
184         return StaticFrameHandlerT(state_->GetFrame());
185     }
186 
187     template <bool IS_DYNAMIC_T = IS_DYNAMIC>
GetFrameHandler(Frame * frame)188     ALWAYS_INLINE typename std::enable_if<IS_DYNAMIC_T, DynamicFrameHandler>::type GetFrameHandler(Frame *frame)
189     {
190         return DynamicFrameHandler(frame);
191     }
192 
193     template <bool IS_DYNAMIC_T = IS_DYNAMIC>
GetFrameHandler(Frame * frame)194     ALWAYS_INLINE typename std::enable_if<!IS_DYNAMIC_T, StaticFrameHandler>::type GetFrameHandler(Frame *frame)
195     {
196         return StaticFrameHandler(frame);
197     }
198 
GetFrame()199     ALWAYS_INLINE Frame *GetFrame() const
200     {
201         return state_->GetFrame();
202     }
203 
SetFrame(Frame * frame)204     ALWAYS_INLINE void SetFrame(Frame *frame)
205     {
206         state_->SetFrame(frame);
207     }
208 
GetThread()209     ALWAYS_INLINE ManagedThread *GetThread() const
210     {
211         return state_->GetThread();
212     }
213 
GetDispatchTable()214     ALWAYS_INLINE const void *const *GetDispatchTable() const
215     {
216         return state_->GetDispatchTable();
217     }
218 
219 protected:
220     template <BytecodeInstruction::Format FORMAT, bool CAN_THROW>
MoveToNextInst()221     ALWAYS_INLINE void MoveToNextInst()
222     {
223         SetInst(GetInst().template GetNext<FORMAT>());
224 
225         if (CAN_THROW) {
226             SetOpcodeExtension(0);
227         }
228     }
229 
230     template <bool CAN_THROW>
JumpToInst(int32_t offset)231     ALWAYS_INLINE void JumpToInst(int32_t offset)
232     {
233         SetInst(GetInst().JumpTo(offset));
234 
235         if (CAN_THROW) {
236             SetOpcodeExtension(0);
237         }
238     }
239 
240     template <bool CAN_THROW>
JumpTo(const uint8_t * pc)241     ALWAYS_INLINE void JumpTo(const uint8_t *pc)
242     {
243         SetInst(BytecodeInstruction(pc));
244 
245         if (CAN_THROW) {
246             SetOpcodeExtension(0);
247         }
248     }
249 
MoveToExceptionHandler()250     ALWAYS_INLINE void MoveToExceptionHandler()
251     {
252         SetOpcodeExtension(UINT8_MAX + NUM_PREFIXED + 1);
253         SetOpcodeExtension(GetOpcodeExtension() - GetPrimaryOpcode());
254     }
255 
SetThread(ManagedThread * thread)256     ALWAYS_INLINE void SetThread(ManagedThread *thread)
257     {
258         state_->SetThread(thread);
259     }
260 
SetInst(BytecodeInstruction inst)261     ALWAYS_INLINE void SetInst(BytecodeInstruction inst)
262     {
263         state_->SetInst(inst);
264     }
265 
SetDispatchTable(const void * const * dispatchTable)266     ALWAYS_INLINE void SetDispatchTable(const void *const *dispatchTable)
267     {
268         return state_->SetDispatchTable(dispatchTable);
269     }
270 
SaveState()271     ALWAYS_INLINE void SaveState()
272     {
273         state_->SaveState();
274     }
275 
RestoreState()276     ALWAYS_INLINE void RestoreState()
277     {
278         state_->RestoreState();
279     }
280 
GetOpcodeExtension()281     ALWAYS_INLINE uint16_t GetOpcodeExtension() const
282     {
283         return state_->GetOpcodeExtension();
284     }
285 
SetOpcodeExtension(uint16_t opcodeExtension)286     ALWAYS_INLINE void SetOpcodeExtension(uint16_t opcodeExtension)
287     {
288         state_->SetOpcodeExtension(opcodeExtension);
289     }
290 
GetFakeInstBuf()291     ALWAYS_INLINE auto &GetFakeInstBuf()
292     {
293         return state_->GetFakeInstBuf();
294     }
295 
296     template <bool IS_CALL>
UpdateHotness(Method * method)297     ALWAYS_INLINE void UpdateHotness(Method *method)
298     {
299         method->DecrementHotnessCounter<IS_CALL>(0, nullptr);
300     }
301 
GetBytecodeOffset()302     ALWAYS_INLINE uint32_t GetBytecodeOffset() const
303     {
304         return state_->GetBytecodeOffset();
305     }
306 
GetInstructionHandlerState()307     ALWAYS_INLINE InstructionHandlerState *GetInstructionHandlerState()
308     {
309         return state_;
310     }
311 
312     template <bool TAKEN>
UpdateBranchStatistics()313     ALWAYS_INLINE void UpdateBranchStatistics()
314     {
315         ProfilingData *profData = this->GetFrame()->GetMethod()->GetProfilingDataWithoutCheck();
316         if (profData != nullptr) {
317             auto pc = this->GetBytecodeOffset();
318             if constexpr (TAKEN) {
319                 profData->UpdateBranchTaken(pc);
320             } else {
321                 profData->UpdateBranchNotTaken(pc);
322             }
323         }
324     }
325 
UpdateThrowStatistics()326     ALWAYS_INLINE void UpdateThrowStatistics()
327     {
328         ProfilingData *profData = this->GetFrame()->GetMethod()->GetProfilingDataWithoutCheck();
329         if (profData != nullptr) {
330             auto pc = this->GetBytecodeOffset();
331             profData->UpdateThrowTaken(pc);
332         }
333     }
334 
UpdateHotnessOSR(Method * method,int offset)335     ALWAYS_INLINE bool UpdateHotnessOSR(Method *method, int offset)
336     {
337         ASSERT(ArchTraits<RUNTIME_ARCH>::SUPPORT_OSR);
338         if (this->GetFrame()->IsDeoptimized() || !Runtime::GetOptions().IsCompilerEnableOsr()) {
339             method->DecrementHotnessCounter<false>(0, nullptr);
340             return false;
341         }
342         return method->DecrementHotnessCounter<false>(this->GetBytecodeOffset() + offset, &this->GetAcc(), true);
343     }
344 
InstrumentBranches(int32_t offset)345     ALWAYS_INLINE bool InstrumentBranches(int32_t offset)
346     {
347         // Offset may be 0 in case of infinite empty loops (see issue #5301)
348         if (offset > 0) {
349             return false;
350         }
351         if (this->GetThread()->TestAllFlags()) {
352             this->GetFrame()->SetAcc(this->GetAcc());
353             RuntimeIfaceT::Safepoint();
354             this->GetAcc() = this->GetFrame()->GetAcc();
355         }
356         if constexpr (ArchTraits<RUNTIME_ARCH>::SUPPORT_OSR) {
357             if (UpdateHotnessOSR(this->GetFrame()->GetMethod(), offset)) {
358                 static_assert(static_cast<unsigned>(BytecodeInstruction::Opcode::RETURN_VOID) <=
359                               std::numeric_limits<uint8_t>::max());
360                 this->GetFakeInstBuf()[0] = static_cast<uint8_t>(BytecodeInstruction::Opcode::RETURN_VOID);
361                 this->SetInst(BytecodeInstruction(this->GetFakeInstBuf().data()));
362                 return true;
363             }
364         } else {
365             this->UpdateHotness<false>(this->GetFrame()->GetMethod());
366         }
367         return false;
368     }
369 
370 private:
371     InstructionHandlerState *state_;
372 };
373 
374 }  // namespace ark::interpreter
375 
376 #endif  // PANDA_INTERPRETER_INSTRUCTION_HANDLER_BASE_H_
377