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