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#include <isa_constants_gen.h> 17#include "plugins_interpreters-inl.h" 18 19namespace ark::interpreter { 20 21// NOLINTBEGIN(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-avoid-goto, hicpp-no-assembler) 22 23% [[:nodebug, 'HANDLE_'], [:debug, 'DEBUG_HANDLE_']].each do |mode, prefix| 24template <class RuntimeIfaceT, bool IS_DYNAMIC, bool IS_PROFILE_ENABLED> 25// NOLINTNEXTLINE(readability-function-size) 26% if mode == :debug 27void ExecuteImplDebug(ManagedThread *thread, const uint8_t *pc, Frame *frame, bool jumpToEh) { 28% else 29void ExecuteImpl(ManagedThread *thread, const uint8_t *pc, Frame *frame, bool jumpToEh) { 30 if (UNLIKELY(Runtime::GetCurrent()->IsDebugMode())) { 31 ExecuteImplDebug<RuntimeIfaceT, IS_DYNAMIC>(thread, pc, frame, jumpToEh); 32 return; 33 } 34% end 35#if defined(__clang__) 36#pragma clang diagnostic push 37#pragma clang diagnostic ignored "-Wvoid-ptr-dereference" 38#pragma clang diagnostic ignored "-Wgnu-label-as-value" 39#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" 40#elif defined(__GNUC__) 41#pragma GCC diagnostic push 42#pragma GCC diagnostic ignored "-Wpedantic" 43#endif 44 45% if mode == :debug 46// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 47#define INSTRUMENT_FRAME() do { if (handler.GetFrame()->IsForcePop() || handler.GetFrame()->IsRetryInstruction()) { goto INSTRUMENT_FRAME_HANDLER; } } while (0) 48% end 49 50 ASSERT(!thread->IsCurrentFrameCompiled()); 51 52 if (!thread->template StackOverflowCheck<true, true>()) { 53 return; 54 } 55 56 EVENT_METHOD_ENTER(frame->GetMethod()->GetFullName(), events::MethodEnterKind::INTERP, thread->RecordMethodEnter()); 57 58#if EVENT_METHOD_EXIT_ENABLED 59 auto method_exit_event = [frame, thread](void* /* unused */) { 60 EVENT_METHOD_EXIT(frame->GetMethod()->GetFullName(), events::MethodExitKind::INTERP, thread->RecordMethodExit()); 61 }; 62 PandaUniquePtr<void, decltype(method_exit_event)> method_exit(&method_exit_event, method_exit_event); 63#endif 64 65 static constexpr uint32_t DISPATCH_TABLE_LEN = 256 + NUM_PREFIXED + 1; 66 67#if PANDA_WITH_QUICKENER 68% Panda.quickened_plugins.each_key do |namespace| 69% if mode == :nodebug 70 static std::array<const void*, DISPATCH_TABLE_LEN> quick_<%= namespace %>_inst_dispatch_table { 71% Quick::select[namespace].each do |ins| 72 &&HANDLE_<%= ins.handler_name %>, 73% end 74% n = Panda::dispatch_table.handler_names.size - Quick::select[namespace].size 75% (0..n-1).each do || 76 &&HANDLE_INVALID, 77% end 78 &&EXCEPTION_HANDLER, 79 }; 80 81 static std::array<const void*, DISPATCH_TABLE_LEN> quick_<%= namespace %>_debug_dispatch_table { 82% Quick::select[namespace].each do |ins| 83 &&HANDLE_DEBUG_SWITCH, 84% end 85 &&EXCEPTION_HANDLER, 86 }; 87% else 88 static std::array<const void*, DISPATCH_TABLE_LEN> quick_<%= namespace %>_inst_dispatch_table { 89% Quick::select[namespace].each do |ins| 90 &&DEBUG_HANDLE_<%= ins.handler_name %>, 91% end 92 &&DEBUG_EXCEPTION_HANDLER, 93 }; 94% end 95% end 96#endif 97 98% if mode == :nodebug 99 static std::array<const void*, DISPATCH_TABLE_LEN> instDispatchTable { 100% Panda::dispatch_table.handler_names.each do |name| 101 &&HANDLE_<%= name %>, 102% end 103 &&EXCEPTION_HANDLER, 104 }; 105 106 static std::array<const void*, DISPATCH_TABLE_LEN> debugDispatchTable { 107% Panda::dispatch_table.handler_names.each do || 108 &&HANDLE_DEBUG_SWITCH, 109% end 110 &&EXCEPTION_HANDLER, 111 }; 112 113% else 114 static std::array<const void*, DISPATCH_TABLE_LEN> instDispatchTable { 115% Panda::dispatch_table.handler_names.each do |name| 116 &&DEBUG_HANDLE_<%= name %>, 117% end 118 &&DEBUG_EXCEPTION_HANDLER, 119 }; 120% end 121 122 constexpr bool IS_DEBUG = <%= mode == :debug %>; 123 124 if (frame->GetMethod()->GetPandaFile()->GetHeader()->quickenedFlag) { 125 switch (frame->GetMethod()->GetClass()->GetSourceLang()) { 126#if PANDA_WITH_QUICKENER 127% Panda.quickened_plugins.each_key do |namespace| 128 case panda_file::SourceLang::<%= namespace.upcase %>: 129 thread->SetCurrentDispatchTable<IS_DEBUG>(quick_<%= namespace %>_inst_dispatch_table.data()); 130% if mode == :debug 131 thread->SetDebugDispatchTable(quick_<%= namespace %>_inst_dispatch_table.data()); 132% else 133 thread->SetDebugDispatchTable(quick_<%= namespace %>_debug_dispatch_table.data()); 134% end 135 break; 136% end 137#endif 138 default: 139 thread->SetCurrentDispatchTable<IS_DEBUG>(instDispatchTable.data()); 140% if mode == :debug 141 thread->SetDebugDispatchTable(instDispatchTable.data()); 142% else 143 thread->SetDebugDispatchTable(debugDispatchTable.data()); 144% end 145 break; 146 } 147 } else { 148 thread->SetCurrentDispatchTable<IS_DEBUG>(instDispatchTable.data()); 149% if mode == :debug 150 thread->SetDebugDispatchTable(instDispatchTable.data()); 151% else 152 thread->SetDebugDispatchTable(debugDispatchTable.data()); 153% end 154 } 155 156 InstructionHandlerState state(thread, pc, frame, thread->GetCurrentDispatchTable<IS_DEBUG>()); 157 if (jumpToEh) { 158 goto* state.GetDispatchTable()[DISPATCH_TABLE_LEN - 1]; 159 } 160 ASSERT(frame->GetMethod()->GetPandaFile()->GetHeader()->quickenedFlag || state.IsPrimaryOpcodeValid()); 161 162 DISPATCH(state.GetDispatchTable(), state.GetPrimaryOpcode()); 163 164% Panda::instructions.each do |i| 165% mnemonic = i.mnemonic.split('.').map { |p| p == '64' ? 'Wide' : p.capitalize }.join 166<%= prefix %><%= i.handler_name %>: { 167% namespace = i.namespace == 'core' ? '' : "#{i.namespace}::" 168% if i.namespace != 'core' 169#ifdef PANDA_WITH_<%= i.namespace.upcase %> 170% end 171 <%= namespace %>InstructionHandler<RuntimeIfaceT, IS_DYNAMIC, IS_DEBUG, IS_PROFILE_ENABLED> handler(&state); 172% if mode == :debug 173 if (handler.GetFrame()->IsForcePop() || handler.GetFrame()->IsRetryInstruction()) { 174 goto INSTRUMENT_FRAME_HANDLER; 175 } 176 handler.InstrumentInstruction(); 177 if (handler.GetFrame()->IsForcePop() || handler.GetFrame()->IsRetryInstruction()) { 178 goto INSTRUMENT_FRAME_HANDLER; 179 } 180 handler.GetFrame()->GetAcc() = handler.GetAcc(); 181% end 182 handler.DumpVRegs(); 183 handler.template Handle<%= mnemonic %><BytecodeInstruction::Format::<%= i.format.pretty.upcase %>>(); 184% if i.properties.include?('return') 185 if (handler.GetFrame()->IsStackless()) { 186 handler.HandleReturnStackless(); 187 ASSERT(frame->GetMethod()->GetPandaFile()->GetHeader()->quickenedFlag 188 || handler.IsPrimaryOpcodeValid() || (handler.GetExceptionOpcode() == UINT8_MAX + NUM_PREFIXED + 1)); 189 DISPATCH(state.GetDispatchTable(), handler.GetExceptionOpcode()); 190 } else { 191 return; 192 } 193% else 194% if !i.exceptions.include?('x_none') 195 ASSERT(frame->GetMethod()->GetPandaFile()->GetHeader()->quickenedFlag 196 || handler.IsPrimaryOpcodeValid() || (handler.GetExceptionOpcode() == UINT8_MAX + NUM_PREFIXED + 1)); 197 DISPATCH(state.GetDispatchTable(), handler.GetExceptionOpcode()); 198% else 199 ASSERT(frame->GetMethod()->GetPandaFile()->GetHeader()->quickenedFlag 200 || handler.IsPrimaryOpcodeValid()); 201 DISPATCH(state.GetDispatchTable(), handler.GetPrimaryOpcode()); 202% end 203% end 204% if i.namespace != 'core' 205#endif // PANDA_WITH_<%= i.namespace.upcase %> 206% end 207} 208% end 209<%= prefix %>INVALID: { 210 LOG(FATAL,INTERPRETER) << "Incorrect opcode"; 211} 212% Panda::prefixes.each do |p| 213<%= prefix %><%= p.handler_name %>: { 214 const auto secondaryOpcode = state.GetSecondaryOpcode(); 215 216 LOG(DEBUG, INTERPRETER) << "Prefix subdispatch: " << "<%= p.name %>, " << static_cast<int32_t>(secondaryOpcode); 217 218 ASSERT(secondaryOpcode <= <%= Panda::dispatch_table.secondary_opcode_bound(p) %>); 219 const size_t dispatchIdx = <%= Panda::dispatch_table.secondary_opcode_offset(p) %> + secondaryOpcode; 220 ASSERT(dispatchIdx < DISPATCH_TABLE_LEN); 221 DISPATCH(state.GetDispatchTable(), dispatchIdx); 222} 223% end 224 225% # quickened handlers 226#if PANDA_WITH_QUICKENER 227% Panda.quickened_plugins.each_key do |namespace| 228% Quick.select[namespace].select { |instr| instr.namespace == namespace }.each do |i| 229% mnemonic = i.mnemonic.split('.').map { |p| p == '64' ? 'Wide' : p.capitalize }.join 230<%= prefix %><%= i.handler_name %>: { 231% namespace = i.namespace == 'core' ? '' : "#{i.namespace}::" 232% if i.namespace != 'core' 233#ifdef PANDA_WITH_<%= i.namespace.upcase %> 234% end 235 <%= namespace %>InstructionHandler<RuntimeIfaceT, IS_DYNAMIC, IS_DEBUG, true> handler(&state); 236% if mode == :debug 237 INSTRUMENT_FRAME(); 238 handler.InstrumentInstruction(); 239 INSTRUMENT_FRAME(); 240 handler.GetFrame()->GetAcc() = handler.GetAcc(); 241% end 242 handler.DumpVRegs(); 243 handler.template Handle<%= mnemonic %><BytecodeInstruction::Format::<%= i.format.pretty.upcase %>>(); 244% if i.properties.include?('return') 245 if (handler.GetFrame()->IsStackless()) { 246 handler.HandleReturnStackless(); 247 ASSERT(frame->GetMethod()->GetPandaFile()->GetHeader()->quickenedFlag 248 || handler.IsPrimaryOpcodeValid() || (handler.GetExceptionOpcode() == UINT8_MAX + NUM_PREFIXED + 1)); 249 DISPATCH(state.GetDispatchTable(), handler.GetExceptionOpcode()); 250 } else { 251 return; 252 } 253% else 254% if !i.exceptions.include?('x_none') 255 ASSERT(frame->GetMethod()->GetPandaFile()->GetHeader()->quickenedFlag 256 || handler.IsPrimaryOpcodeValid() || (handler.GetExceptionOpcode() == UINT8_MAX + NUM_PREFIXED + 1)); 257 DISPATCH(state.GetDispatchTable(), handler.GetExceptionOpcode()); 258% else 259 ASSERT(frame->GetMethod()->GetPandaFile()->GetHeader()->quickenedFlag 260 || handler.IsPrimaryOpcodeValid()); 261 DISPATCH(state.GetDispatchTable(), handler.GetPrimaryOpcode()); 262% end 263% end 264% if i.namespace != 'core' 265#endif // PANDA_WITH_<%= i.namespace.upcase %> 266% end 267} 268% end 269% end 270#endif 271 272% if mode == :debug 273DEBUG_EXCEPTION_HANDLER: { 274% else 275EXCEPTION_HANDLER: { 276% end 277 ASSERT(thread->HasPendingException()); 278 279 InstructionHandler<RuntimeIfaceT, IS_DYNAMIC, IS_DEBUG> handler(&state); 280 uint32_t pcOffset = panda_file::INVALID_OFFSET; 281 282% if mode == :debug 283 RuntimeIfaceT::GetNotificationManager()->ExceptionThrowEvent(thread, handler.GetFrame()->GetMethod(), thread->GetException(), pcOffset); 284% end 285 pcOffset = handler.FindCatchBlockStackless(); 286 if (pcOffset == panda_file::INVALID_OFFSET) { 287 if constexpr (RUNTIME_ARCH == Arch::AARCH64 || RUNTIME_ARCH == Arch::AARCH32 || RUNTIME_ARCH == Arch::X86_64) { 288 return FindCatchBlockInCallStack(thread); 289 } else { 290 return; 291 } 292 } 293 294 auto *method = handler.GetFrame()->GetMethod(); 295 ASSERT(method != nullptr); 296 LanguageContext ctx = RuntimeIfaceT::GetLanguageContext(*method); 297 ObjectHeader *exceptionObject = thread->GetException(); 298 ctx.SetExceptionToVReg(handler.GetFrame()->GetAcc(), exceptionObject); 299 300 thread->ClearException(); 301% if mode == :debug 302 RuntimeIfaceT::GetNotificationManager()->ExceptionCatchEvent(thread, handler.GetFrame()->GetMethod(), exceptionObject, pcOffset); 303% end 304 305 Span<const uint8_t> sp(handler.GetFrame()->GetMethod()->GetInstructions(), pcOffset); 306 state = InstructionHandlerState(thread, sp.cend(), handler.GetFrame(), handler.GetDispatchTable()); 307 308 ASSERT(frame->GetMethod()->GetPandaFile()->GetHeader()->quickenedFlag || state.IsPrimaryOpcodeValid()); 309 goto* state.GetDispatchTable()[state.GetPrimaryOpcode()]; 310} 311 312% if mode == :debug 313INSTRUMENT_FRAME_HANDLER: { 314 InstructionHandler<RuntimeIfaceT, IS_DYNAMIC, IS_DEBUG> handler(&state); 315 316 ASSERT(handler.GetFrame()->IsForcePop() || handler.GetFrame()->IsRetryInstruction()); 317 318 if (handler.GetFrame()->IsForcePop()) { 319 handler.GetFrame()->ClearForcePop(); 320 handler.InstrumentForceReturn(); 321 if (handler.GetFrame()->IsStackless()) { 322 handler.HandleInstrumentForceReturn(); 323 ASSERT(frame->GetMethod()->GetPandaFile()->GetHeader()->quickenedFlag || handler.IsPrimaryOpcodeValid()); 324 DISPATCH(handler.GetDispatchTable(), handler.GetPrimaryOpcode()); 325 } else { 326 return; 327 } 328 } 329 330 handler.GetFrame()->ClearRetryInstruction(); 331 auto* method = handler.GetFrame()->GetMethod(); 332 state = InstructionHandlerState(thread, method->GetInstructions() + handler.GetFrame()->GetBytecodeOffset(), handler.GetFrame(), handler.GetDispatchTable()); 333 ASSERT(frame->GetMethod()->GetPandaFile()->GetHeader()->quickenedFlag || state.IsPrimaryOpcodeValid()); 334 goto* state.GetDispatchTable()[state.GetPrimaryOpcode()]; 335} 336% end 337 338% if mode == :nodebug 339HANDLE_DEBUG_SWITCH: { 340 state.GetFrame()->GetAcc() = state.GetAcc(); 341 ExecuteImplDebug<RuntimeIfaceT, IS_DYNAMIC>(state.GetThread(), state.GetInst().GetAddress(), 342 state.GetFrame(), jumpToEh); 343 return; 344} 345% end 346 347#if defined(__clang__) 348#pragma clang diagnostic pop 349#elif defined(__GNUC__) 350#pragma GCC diagnostic pop 351#endif 352} 353% end 354 355// NOLINTEND(cppcoreguidelines-pro-bounds-pointer-arithmetic,cppcoreguidelines-avoid-goto, hicpp-no-assembler) 356 357} // namespace ark::interpreter 358