• 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_RUNTIME_METHOD_INL_H_
17 #define PANDA_RUNTIME_METHOD_INL_H_
18 
19 #include "entrypoints/entrypoints.h"
20 #include "libpandafile/code_data_accessor-inl.h"
21 #include "libpandafile/code_data_accessor.h"
22 #include "libpandafile/file.h"
23 #include "libpandafile/method_data_accessor-inl.h"
24 #include "libpandafile/method_data_accessor.h"
25 #include "libpandafile/proto_data_accessor-inl.h"
26 #include "libpandafile/proto_data_accessor.h"
27 #include "runtime/bridge/bridge.h"
28 #include "runtime/include/class_linker.h"
29 #include "runtime/include/method.h"
30 #include "runtime/include/runtime.h"
31 #include "runtime/include/panda_vm.h"
32 #include "runtime/include/runtime_notification.h"
33 #include "runtime/include/thread-inl.h"
34 #include "runtime/interpreter/interpreter.h"
35 #include "runtime/interpreter/runtime_interface.h"
36 #include "runtime/osr.h"
37 
38 namespace ark {
39 
operator()40 inline void FrameDeleter::operator()(Frame *frame) const
41 {
42     interpreter::RuntimeInterface::FreeFrame(thread_, frame);
43 }
44 
45 class InvokeHelperStatic {
46 public:
47     static constexpr bool IS_DYNAMIC = false;
48 
GetFrameSize(uint32_t numVregs,uint32_t numDeclaredArgs,uint32_t numActualArgs)49     ALWAYS_INLINE inline static uint32_t GetFrameSize(uint32_t numVregs, uint32_t numDeclaredArgs,
50                                                       [[maybe_unused]] uint32_t numActualArgs)
51     {
52         return numVregs + numDeclaredArgs;
53     }
54 
InitActualArgs(Frame * frame,Span<Value> argsSpan,uint32_t numVregs,uint32_t numDeclaredArgs)55     ALWAYS_INLINE inline static void InitActualArgs(Frame *frame, Span<Value> argsSpan, uint32_t numVregs,
56                                                     [[maybe_unused]] uint32_t numDeclaredArgs)
57     {
58         StaticFrameHandler staticFrameHelper(frame);
59         uint32_t numActualArgs = argsSpan.Size();
60         for (size_t i = 0; i < numActualArgs; ++i) {
61             if (argsSpan[i].IsReference()) {
62                 staticFrameHelper.GetVReg(numVregs + i).SetReference(argsSpan[i].GetAs<ObjectHeader *>());
63             } else {
64                 staticFrameHelper.GetVReg(numVregs + i).SetPrimitive(argsSpan[i].GetAs<int64_t>());
65             }
66         }
67     }
68 
InterpreterExecute(ManagedThread * thread,const uint8_t * pc,Frame * frame)69     ALWAYS_INLINE inline static void InterpreterExecute(ManagedThread *thread, const uint8_t *pc, Frame *frame)
70     {
71         interpreter::Execute(thread, pc, frame);
72     }
73 
CreateFrame(ManagedThread * thread,uint32_t nregsSize,Method * method,Frame * prev,uint32_t nregs,uint32_t numActualArgs)74     ALWAYS_INLINE static Frame *CreateFrame([[maybe_unused]] ManagedThread *thread, uint32_t nregsSize, Method *method,
75                                             Frame *prev, uint32_t nregs, uint32_t numActualArgs)
76     {
77         return interpreter::RuntimeInterface::CreateFrameWithActualArgsAndSize(nregsSize, nregs, numActualArgs, method,
78                                                                                prev);
79     }
80 };
81 
82 class InvokeHelperDynamic {
83 public:
84     static constexpr bool IS_DYNAMIC = true;
85 
GetFrameSize(uint32_t numVregs,uint32_t numDeclaredArgs,uint32_t numActualArgs)86     ALWAYS_INLINE inline static uint32_t GetFrameSize(uint32_t numVregs, uint32_t numDeclaredArgs,
87                                                       uint32_t numActualArgs)
88     {
89         return numVregs + std::max(numDeclaredArgs, numActualArgs);
90     }
91 
InitActualArgs(Frame * frame,Span<coretypes::TaggedValue> argsSpan,uint32_t numVregs,uint32_t numDeclaredArgs)92     ALWAYS_INLINE inline static void InitActualArgs(Frame *frame, Span<coretypes::TaggedValue> argsSpan,
93                                                     uint32_t numVregs, [[maybe_unused]] uint32_t numDeclaredArgs)
94     {
95         frame->SetDynamic();
96 
97         DynamicFrameHandler dynamicFrameHelper(frame);
98         uint32_t numActualArgs = argsSpan.Size();
99         for (size_t i = 0; i < numActualArgs; ++i) {
100             dynamicFrameHelper.GetVReg(numVregs + i).SetValue(argsSpan[i].GetRawData());
101         }
102 
103         for (size_t i = numActualArgs; i < numDeclaredArgs; i++) {
104             dynamicFrameHelper.GetVReg(numVregs + i).SetValue(TaggedValue::VALUE_UNDEFINED);
105         }
106     }
107 
InterpreterExecute(ManagedThread * thread,const uint8_t * pc,Frame * frame)108     ALWAYS_INLINE inline static void InterpreterExecute(ManagedThread *thread, const uint8_t *pc, Frame *frame)
109     {
110         interpreter::Execute(thread, pc, frame);
111     }
112 
CompiledCodeExecute(ManagedThread * thread,Method * method,uint32_t numArgs,coretypes::TaggedValue * args)113     ALWAYS_INLINE inline static coretypes::TaggedValue CompiledCodeExecute(ManagedThread *thread, Method *method,
114                                                                            uint32_t numArgs,
115                                                                            coretypes::TaggedValue *args)
116     {
117         Frame *currentFrame = thread->GetCurrentFrame();
118         bool isCompiled = thread->IsCurrentFrameCompiled();
119 
120         ASSERT(numArgs >= 2U);  // NOTE(asoldatov): Adjust this check
121         uint64_t ret = InvokeCompiledCodeWithArgArrayDyn(reinterpret_cast<uint64_t *>(args), numArgs, currentFrame,
122                                                          method, thread);
123         thread->SetCurrentFrameIsCompiled(isCompiled);
124         thread->SetCurrentFrame(currentFrame);
125         return coretypes::TaggedValue(ret);
126     }
127 
CreateFrame(ManagedThread * thread,uint32_t nregsSize,Method * method,Frame * prev,uint32_t nregs,uint32_t numActualArgs)128     ALWAYS_INLINE static Frame *CreateFrame([[maybe_unused]] ManagedThread *thread, uint32_t nregsSize, Method *method,
129                                             Frame *prev, uint32_t nregs, uint32_t numActualArgs)
130     {
131         return interpreter::RuntimeInterface::CreateFrameWithActualArgsAndSize(nregsSize, nregs, numActualArgs, method,
132                                                                                prev);
133     }
134 };
135 
136 template <class InvokeHelper, class ValueT>
GetReturnValueFromException()137 ValueT Method::GetReturnValueFromException()
138 {
139     if constexpr (InvokeHelper::IS_DYNAMIC) {  // NOLINT(readability-braces-around-statements)
140         return TaggedValue::Undefined();
141     } else {  // NOLINT(readability-misleading-indentation)
142         if (GetReturnType().IsReference()) {
143             return Value(nullptr);
144         }
145         return Value(static_cast<int64_t>(0));
146     }
147 }
148 
149 template <class InvokeHelper, class ValueT>
GetReturnValueFromAcc(interpreter::AccVRegister & aacVreg)150 ValueT Method::GetReturnValueFromAcc(interpreter::AccVRegister &aacVreg)
151 {
152     if constexpr (InvokeHelper::IS_DYNAMIC) {  // NOLINT(readability-braces-around-statements)
153         return TaggedValue(aacVreg.GetAs<uint64_t>());
154     } else {  // NOLINT(readability-misleading-indentation)
155         ASSERT(GetReturnType().GetId() != panda_file::Type::TypeId::TAGGED);
156         if (GetReturnType().GetId() != panda_file::Type::TypeId::VOID) {
157             interpreter::StaticVRegisterRef acc = aacVreg.AsVRegRef<false>();
158             if (acc.HasObject()) {
159                 return Value(aacVreg.GetReference());
160             }
161             return Value(aacVreg.GetLong());
162         }
163         return Value(static_cast<int64_t>(0));
164     }
165 }
166 
167 // CC-OFFNXT(G.FUD.06) perf critical
InvokeCompiledCode(ManagedThread * thread,uint32_t numArgs,Value * args)168 inline Value Method::InvokeCompiledCode(ManagedThread *thread, uint32_t numArgs, Value *args)
169 {
170     Frame *currentFrame = thread->GetCurrentFrame();
171     Span<Value> argsSpan(args, numArgs);
172     bool isCompiled = thread->IsCurrentFrameCompiled();
173     // Use frame allocator to alloc memory for parameters as thread can be terminated and
174     // InvokeCompiledCodeWithArgArray will not return in this case we will get memory leak with internal
175     // allocator
176     mem::StackFrameAllocator *allocator = thread->GetStackFrameAllocator();
177     auto valuesDeleter = [allocator](int64_t *values) {
178         if (values != nullptr) {
179             allocator->Free(values);
180         }
181     };
182     auto values = PandaUniquePtr<int64_t, decltype(valuesDeleter)>(nullptr, valuesDeleter);
183     if (numArgs > 0) {
184         // In the worse case we are calling a dynamic method in which all arguments are pairs ot int64_t
185         // That is why we allocate 2 x num_actual_args
186         size_t capacity = numArgs * sizeof(int64_t);
187         // All allocations though FrameAllocator must be aligned
188         capacity = AlignUp(capacity, GetAlignmentInBytes(DEFAULT_FRAME_ALIGNMENT));
189         values.reset(reinterpret_cast<int64_t *>(allocator->Alloc(capacity)));
190         Span<int64_t> valuesSpan(values.get(), capacity);
191         for (uint32_t i = 0; i < numArgs; ++i) {
192             if (argsSpan[i].IsReference()) {
193                 valuesSpan[i] = reinterpret_cast<int64_t>(argsSpan[i].GetAs<ObjectHeader *>());
194             } else {
195                 valuesSpan[i] = argsSpan[i].GetAs<int64_t>();
196             }
197         }
198     }
199 
200     uint64_t retValue = InvokeCompiledCodeWithArgArray(values.get(), currentFrame, this, thread);
201 
202     thread->SetCurrentFrameIsCompiled(isCompiled);
203     thread->SetCurrentFrame(currentFrame);
204     if (UNLIKELY(thread->HasPendingException())) {
205         retValue = 0;
206     }
207     return GetReturnValueFromTaggedValue(retValue);
208 }
209 
210 template <class InvokeHelper, class ValueT>
InvokeInterpretedCode(ManagedThread * thread,uint32_t numActualArgs,ValueT * args)211 ValueT Method::InvokeInterpretedCode(ManagedThread *thread, uint32_t numActualArgs, ValueT *args)
212 {
213     Frame *currentFrame = thread->GetCurrentFrame();
214     PandaUniquePtr<Frame, FrameDeleter> frame = InitFrame<InvokeHelper>(thread, numActualArgs, args, currentFrame);
215     if (UNLIKELY(frame.get() == nullptr)) {
216         ark::ThrowOutOfMemoryError("CreateFrame failed: " + GetFullName());
217         return GetReturnValueFromException<InvokeHelper, ValueT>();
218     }
219 
220     InvokeEntry<InvokeHelper>(thread, currentFrame, frame.get(), GetInstructions());
221 
222     ValueT res = (UNLIKELY(thread->HasPendingException()))
223                      ? GetReturnValueFromException<InvokeHelper, ValueT>()
224                      : GetReturnValueFromAcc<InvokeHelper, ValueT>(frame->GetAcc());
225     LOG(DEBUG, INTERPRETER) << "Invoke exit: " << GetFullName();
226     return res;
227 }
228 
229 template <class InvokeHelper>
InvokeEntry(ManagedThread * thread,Frame * currentFrame,Frame * frame,const uint8_t * pc)230 void Method::InvokeEntry(ManagedThread *thread, Frame *currentFrame, Frame *frame, const uint8_t *pc)
231 {
232     LOG(DEBUG, INTERPRETER) << "Invoke entry: " << GetFullName();
233 
234     auto isCompiled = thread->IsCurrentFrameCompiled();
235     thread->SetCurrentFrameIsCompiled(false);
236     thread->SetCurrentFrame(frame);
237 
238     if (isCompiled && currentFrame != nullptr) {
239         // Create C2I bridge frame in case of previous frame is a native frame or other compiler frame.
240         // But create only if the previous frame is not a C2I bridge already.
241         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
242         C2IBridge bridge;
243         if (!StackWalker::IsBoundaryFrame<FrameKind::INTERPRETER>(currentFrame)) {
244             bridge = {0, reinterpret_cast<uintptr_t>(currentFrame), COMPILED_CODE_TO_INTERPRETER,
245                       thread->GetNativePc()};
246             // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
247             frame->SetPrevFrame(reinterpret_cast<Frame *>(&bridge.v[1]));
248         }
249         // Workaround for
250         // issues #2888 and #2925
251         // We cannot make OSR on the methods called from here, because:
252         // 1. If caller is native method, then C2I bridge, created above, is not complete. It can be fixed by
253         //    allocating full size boundary frame.
254         // 2. If caller is compiled method, then we got here from entrypoint. But currently compiler creates
255         //    boundary frame with pseudo LR value, that doesn't point to the instruction after call, thereby
256         //    OSR will fail. It can be fixed by addresses patching, currently codegen hasn't such machinery.
257         // NOTE(msherstennikov): fix issue
258         frame->DisableOsr();
259         Runtime::GetCurrent()->GetNotificationManager()->MethodEntryEvent(thread, this);
260         InvokeHelper::InterpreterExecute(thread, pc, frame);
261         Runtime::GetCurrent()->GetNotificationManager()->MethodExitEvent(thread, this);
262         thread->SetCurrentFrameIsCompiled(true);
263     } else {
264         Runtime::GetCurrent()->GetNotificationManager()->MethodEntryEvent(thread, this);
265         InvokeHelper::InterpreterExecute(thread, pc, frame);
266         Runtime::GetCurrent()->GetNotificationManager()->MethodExitEvent(thread, this);
267     }
268     thread->SetCurrentFrame(currentFrame);
269 }
270 
InvokeDyn(ManagedThread * thread,uint32_t numArgs,coretypes::TaggedValue * args)271 inline coretypes::TaggedValue Method::InvokeDyn(ManagedThread *thread, uint32_t numArgs, coretypes::TaggedValue *args)
272 {
273     return InvokeDyn<InvokeHelperDynamic>(thread, numArgs, args);
274 }
275 
276 template <class InvokeHelper>
InvokeDyn(ManagedThread * thread,uint32_t numArgs,coretypes::TaggedValue * args)277 inline coretypes::TaggedValue Method::InvokeDyn(ManagedThread *thread, uint32_t numArgs, coretypes::TaggedValue *args)
278 {
279     return InvokeImpl<InvokeHelper>(thread, numArgs, args, false);
280 }
281 
InvokeContext(ManagedThread * thread,const uint8_t * pc,coretypes::TaggedValue * acc,uint32_t nregs,coretypes::TaggedValue * regs)282 inline coretypes::TaggedValue Method::InvokeContext(ManagedThread *thread, const uint8_t *pc,
283                                                     coretypes::TaggedValue *acc, uint32_t nregs,
284                                                     coretypes::TaggedValue *regs)
285 {
286     return InvokeContext<InvokeHelperDynamic>(thread, pc, acc, nregs, regs);
287 }
288 
289 template <class InvokeHelper>
290 // CC-OFFNXT(G.FUD.06) perf critical
InvokeContext(ManagedThread * thread,const uint8_t * pc,coretypes::TaggedValue * acc,uint32_t nregs,coretypes::TaggedValue * regs)291 inline coretypes::TaggedValue Method::InvokeContext(ManagedThread *thread, const uint8_t *pc,
292                                                     coretypes::TaggedValue *acc, uint32_t nregs,
293                                                     coretypes::TaggedValue *regs)
294 {
295     static_assert(InvokeHelper::IS_DYNAMIC == true);
296     ASSERT(GetReturnType().GetId() == panda_file::Type::TypeId::VOID ||
297            GetReturnType().GetId() == panda_file::Type::TypeId::TAGGED);
298 
299     TaggedValue res(TaggedValue::VALUE_UNDEFINED);
300 
301     if (!Verify()) {
302         auto ctx = Runtime::GetCurrent()->GetLanguageContext(*this);
303         ark::ThrowVerificationException(ctx, GetFullName());
304         // 'res' is not a heap object.
305         // SUPPRESS_CSA_NEXTLINE(alpha.core.WasteObjHeader)
306         return res;
307     }
308 
309     Frame *currentFrame = thread->GetCurrentFrame();
310     PandaUniquePtr<Frame, FrameDeleter> frame(
311         // true value is is_dynamic
312         interpreter::RuntimeInterface::CreateFrameWithActualArgs<true>(nregs, nregs, this, currentFrame),
313         FrameDeleter(thread));
314     if (UNLIKELY(frame.get() == nullptr)) {
315         ark::ThrowOutOfMemoryError("CreateFrame failed: " + GetFullName());
316         return res;
317     }
318 
319     frame->SetDynamic();
320 
321     DynamicFrameHandler dynamicFrameHelper(frame.get());
322     Span<TaggedValue> argsSpan(regs, nregs);
323     for (size_t i = 0; i < nregs; ++i) {
324         dynamicFrameHelper.GetVReg(i).SetValue(argsSpan[i].GetRawData());
325     }
326 
327     LOG(DEBUG, INTERPRETER) << "Invoke entry: " << GetFullName();
328 
329     dynamicFrameHelper.GetAcc().SetValue(acc->GetRawData());
330     InvokeEntry<InvokeHelper>(thread, currentFrame, frame.get(), pc);
331     res = TaggedValue(dynamicFrameHelper.GetAcc().GetAs<uint64_t>());
332 
333     LOG(DEBUG, INTERPRETER) << "Invoke exit: " << GetFullName();
334     return res;
335 }
336 
337 template <class InvokeHelper, class ValueT>
EnterNativeMethodFrame(ManagedThread * thread,uint32_t numVregs,uint32_t numArgs,ValueT * args)338 Frame *Method::EnterNativeMethodFrame(ManagedThread *thread, uint32_t numVregs, uint32_t numArgs, ValueT *args)
339 {
340     Frame *currentFrame = thread->GetCurrentFrame();
341     PandaUniquePtr<Frame, FrameDeleter> frame =
342         InitFrameWithNumVRegs<InvokeHelper, ValueT, true>(thread, numVregs, numArgs, args, currentFrame);
343     if (UNLIKELY(frame.get() == nullptr)) {
344         ark::ThrowOutOfMemoryError("CreateFrame failed: " + GetFullName());
345         return nullptr;
346     }
347 
348     LOG(DEBUG, INTERPRETER) << "Enter native frame";
349 
350     thread->SetCurrentFrame(frame.get());
351     return frame.release();
352 }
353 
ExitNativeMethodFrame(ManagedThread * thread)354 inline void Method::ExitNativeMethodFrame(ManagedThread *thread)
355 {
356     Frame *currentFrame = thread->GetCurrentFrame();
357     ASSERT(currentFrame != nullptr);
358 
359     LOG(DEBUG, INTERPRETER) << "Exit native frame";
360 
361     thread->SetCurrentFrame(currentFrame->GetPrevFrame());
362     FreeFrame(currentFrame);
363 }
364 
365 template <class InvokeHelper, class ValueT>
InitFrame(ManagedThread * thread,uint32_t numActualArgs,ValueT * args,Frame * currentFrame)366 PandaUniquePtr<Frame, FrameDeleter> Method::InitFrame(ManagedThread *thread, uint32_t numActualArgs, ValueT *args,
367                                                       Frame *currentFrame)
368 {
369     ASSERT(codeId_.IsValid());
370     auto numVregs = panda_file::CodeDataAccessor::GetNumVregs(*(pandaFile_), codeId_);
371     return InitFrameWithNumVRegs<InvokeHelper, ValueT, false>(thread, numVregs, numActualArgs, args, currentFrame);
372 }
373 
374 template <class InvokeHelper, class ValueT, bool IS_NATIVE_METHOD>
InitFrameWithNumVRegs(ManagedThread * thread,uint32_t numVregs,uint32_t numActualArgs,ValueT * args,Frame * currentFrame)375 PandaUniquePtr<Frame, FrameDeleter> Method::InitFrameWithNumVRegs(ManagedThread *thread, uint32_t numVregs,
376                                                                   uint32_t numActualArgs, ValueT *args,
377                                                                   Frame *currentFrame)
378 {
379     Span<ValueT> argsSpan(args, numActualArgs);
380 
381     uint32_t numDeclaredArgs = GetNumArgs();
382     uint32_t frameSize = InvokeHelper::GetFrameSize(numVregs, numDeclaredArgs, numActualArgs);
383 
384     Frame *framePtr;
385     // NOLINTNEXTLINE(readability-braces-around-statements)
386     if constexpr (IS_NATIVE_METHOD) {
387         framePtr = interpreter::RuntimeInterface::CreateNativeFrameWithActualArgs<InvokeHelper::IS_DYNAMIC>(
388             frameSize, numActualArgs, this, currentFrame);
389     } else {  // NOLINTNEXTLINE(readability-braces-around-statements)
390         framePtr = InvokeHelper::CreateFrame(thread, Frame::GetActualSize<InvokeHelper::IS_DYNAMIC>(frameSize), this,
391                                              currentFrame, frameSize, numActualArgs);
392     }
393     PandaUniquePtr<Frame, FrameDeleter> frame(framePtr, FrameDeleter(thread));
394     if (UNLIKELY(frame.get() == nullptr)) {
395         return frame;
396     }
397 
398     InvokeHelper::InitActualArgs(frame.get(), argsSpan, numVregs, numDeclaredArgs);
399 
400     frame->SetInvoke();
401     return frame;
402 }
403 
404 template <class InvokeHelper, class ValueT>
InvokeImpl(ManagedThread * thread,uint32_t numActualArgs,ValueT * args,bool proxyCall)405 ValueT Method::InvokeImpl(ManagedThread *thread, uint32_t numActualArgs, ValueT *args, bool proxyCall)
406 {
407     DecrementHotnessCounter<true>(thread, 0, nullptr);
408     if (UNLIKELY(thread->HasPendingException())) {
409         return GetReturnValueFromException<InvokeHelper, ValueT>();
410     }
411 
412     // Currently, proxy methods should always be invoked in the interpreter. This constraint should be relaxed once
413     // we support same frame layout for interpreter and compiled methods.
414     // NOTE(msherstennikov): remove `proxy_call`
415     bool runInterpreter = !HasCompiledCode() || proxyCall;
416     ASSERT(!(proxyCall && IsNative()));
417     if (!runInterpreter) {
418         if constexpr (InvokeHelper::IS_DYNAMIC) {  // NOLINT(readability-braces-around-statements)
419             return InvokeHelper::CompiledCodeExecute(thread, this, numActualArgs, args);
420         } else {  // NOLINT(readability-misleading-indentation)
421             return InvokeCompiledCode(thread, numActualArgs, args);
422         }
423     }
424     if (!thread->template StackOverflowCheck<true, false>()) {
425         return GetReturnValueFromException<InvokeHelper, ValueT>();
426     }
427 
428     return InvokeInterpretedCode<InvokeHelper>(thread, numActualArgs, args);
429 }
430 
431 template <class AccVRegisterPtrT>
SetAcc(ManagedThread * thread,AccVRegisterPtrT acc)432 inline void Method::SetAcc([[maybe_unused]] ManagedThread *thread, [[maybe_unused]] AccVRegisterPtrT acc)
433 {
434     if constexpr (!std::is_same_v<AccVRegisterPtrT, std::nullptr_t>) {  // NOLINT
435         if (acc != nullptr) {
436             thread->GetCurrentFrame()->SetAcc(*acc);
437         }
438     }
439 }
440 
441 template <class AccVRegisterPtrT>
SetAcc(AccVRegisterPtrT acc)442 inline void Method::SetAcc(AccVRegisterPtrT acc)
443 {
444     SetAcc<AccVRegisterPtrT>(ManagedThread::GetCurrent(), acc);
445 }
446 
447 /**
448  * Decrement method's hotness counter.
449  * @param bytecode_offset Offset of the target bytecode instruction. Used only for OSR.
450  * @param acc Pointer to the accumulator, it is needed because interpreter uses own Frame, not the one in the method.
451  *            Used only for OSR.
452  * @param osr Сompilation of the method will be started in OSR mode or not
453  * @param func The object of the caller function. Used only for dynamic mode.
454  * @return true if OSR has been occurred
455  */
456 template <bool IS_CALL, class AccVRegisterPtrT>
457 // CC-OFFNXT(G.FUD.06) perf critical
DecrementHotnessCounter(ManagedThread * thread,uintptr_t bcOffset,AccVRegisterPtrT acc,bool osr,TaggedValue func)458 inline bool Method::DecrementHotnessCounter(ManagedThread *thread, uintptr_t bcOffset,
459                                             [[maybe_unused]] AccVRegisterPtrT acc, bool osr, TaggedValue func)
460 {
461     // The compilation process will start performing
462     // once the counter value decreases to a value that is or less than 0
463     if (GetHotnessCounter() > 0) {
464         if (TryVerify<IS_CALL>()) {
465             DecrementHotnessCounter();
466         }
467         return false;
468     }
469 
470     if (!Runtime::GetCurrent()->IsJitEnabled()) {
471         if (TryVerify<IS_CALL>()) {
472             ResetHotnessCounter();
473         }
474         return false;
475     }
476 
477     // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
478     if constexpr (!ArchTraits<RUNTIME_ARCH>::SUPPORT_OSR) {
479         ASSERT(!osr);
480     }
481 
482     auto &options = Runtime::GetOptions();
483     uint32_t threshold = options.GetCompilerHotnessThreshold();
484     uint32_t profThreshold = options.GetCompilerProfilingThreshold();
485     if ((profThreshold < threshold) && !IsProfiling() && !HasCompiledCode() && !IsProfiled()) {
486         SetHotnessCounter(threshold - profThreshold - 1);
487         if (thread->GetVM()->IsStaticProfileEnabled()) {
488             StartProfiling();
489         }
490         if (!IsProfiling()) {
491             SetProfiled();
492         }
493     } else {
494         CompilationStage status = GetCompilationStatus();
495         if (!(status == FAILED || status == WAITING || status == COMPILATION)) {
496             ASSERT((!osr) == (acc == nullptr));
497             SetAcc<AccVRegisterPtrT>(thread, acc);
498 
499             if (func.IsHeapObject()) {
500                 return DecrementHotnessCounterForTaggedFunction<IS_CALL>(thread, bcOffset, osr, func);
501             }
502             if (!TryVerify<IS_CALL>()) {
503                 return false;
504             }
505             auto compiler = Runtime::GetCurrent()->GetPandaVM()->GetCompiler();
506             return compiler->CompileMethod(this, bcOffset, osr, func);  // SUPPRESS_CSA(alpha.core.WasteObjHeader)
507         }
508         if (status == WAITING) {
509             DecrementHotnessCounter();
510         }
511     }
512     return false;
513 }
514 
515 template <bool IS_CALL>
DecrementHotnessCounterForTaggedFunction(ManagedThread * thread,uintptr_t bcOffset,bool osr,TaggedValue func)516 inline bool Method::DecrementHotnessCounterForTaggedFunction(ManagedThread *thread, uintptr_t bcOffset, bool osr,
517                                                              TaggedValue func)
518 {
519     [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
520     VMHandle<ObjectHeader> handleFunc(thread, func.GetHeapObject());
521     if (TryVerify<IS_CALL>()) {
522         auto compiler = Runtime::GetCurrent()->GetPandaVM()->GetCompiler();
523         return compiler->CompileMethod(this, bcOffset, osr, TaggedValue(handleFunc.GetPtr()));
524     }
525 
526     return false;
527 }
528 
529 template <bool IS_CALL>
530 // CC-OFFNXT(G.FUD.06) perf critical
TryVerify()531 inline bool Method::TryVerify()
532 {
533     if constexpr (!IS_CALL) {
534         return true;
535     }
536     if (!IsIntrinsic() && (GetVerificationStage() != Method::VerificationStage::VERIFIED_OK)) {
537         if (UNLIKELY(!Verify())) {
538             auto ctx = Runtime::GetCurrent()->GetLanguageContext(*this);
539             ark::ThrowVerificationException(ctx, GetFullName());
540             return false;
541         }
542     }
543     return true;
544 }
545 
546 template <bool IS_CALL, class AccVRegisterPtrT>
DecrementHotnessCounter(uintptr_t bytecodeOffset,AccVRegisterPtrT acc,bool osr,TaggedValue func)547 inline bool Method::DecrementHotnessCounter(uintptr_t bytecodeOffset, AccVRegisterPtrT acc, bool osr, TaggedValue func)
548 {
549     return DecrementHotnessCounter<IS_CALL>(ManagedThread::GetCurrent(), bytecodeOffset, acc, osr, func);
550 }
551 
552 template <typename Callback>
EnumerateTypes(Callback handler)553 void Method::EnumerateTypes(Callback handler) const
554 {
555     panda_file::MethodDataAccessor mda(*(pandaFile_), fileId_);
556     mda.EnumerateTypesInProto(handler);
557 }
558 
559 template <typename Callback>
EnumerateTryBlocks(Callback callback)560 void Method::EnumerateTryBlocks(Callback callback) const
561 {
562     ASSERT(!IsAbstract());
563 
564     panda_file::MethodDataAccessor mda(*(pandaFile_), fileId_);
565     panda_file::CodeDataAccessor cda(*(pandaFile_), mda.GetCodeId().value());
566 
567     cda.EnumerateTryBlocks(callback);
568 }
569 
570 template <typename Callback>
EnumerateCatchBlocks(Callback callback)571 void Method::EnumerateCatchBlocks(Callback callback) const
572 {
573     ASSERT(!IsAbstract());
574 
575     using TryBlock = panda_file::CodeDataAccessor::TryBlock;
576     using CatchBlock = panda_file::CodeDataAccessor::CatchBlock;
577 
578     EnumerateTryBlocks([&callback, code = GetInstructions()](const TryBlock &tryBlock) {
579         bool next = true;
580         const uint8_t *tryStartPc = reinterpret_cast<uint8_t *>(reinterpret_cast<uintptr_t>(code) +
581                                                                 static_cast<uintptr_t>(tryBlock.GetStartPc()));
582         const uint8_t *tryEndPc = reinterpret_cast<uint8_t *>(reinterpret_cast<uintptr_t>(tryStartPc) +
583                                                               static_cast<uintptr_t>(tryBlock.GetLength()));
584         // ugly, but API of TryBlock is bad designed: enumaration is paired with mutation & updating
585         const_cast<TryBlock &>(tryBlock).EnumerateCatchBlocks(
586             [&callback, &next, tryStartPc, tryEndPc](const CatchBlock &catchBlock) {
587                 return next = callback(tryStartPc, tryEndPc, catchBlock);
588             });
589         return next;
590     });
591 }
592 
593 template <typename Callback>
EnumerateExceptionHandlers(Callback callback)594 void Method::EnumerateExceptionHandlers(Callback callback) const
595 {
596     ASSERT(!IsAbstract());
597 
598     using CatchBlock = panda_file::CodeDataAccessor::CatchBlock;
599 
600     EnumerateCatchBlocks([this, callback = std::move(callback)](const uint8_t *tryStartPc, const uint8_t *tryEndPc,
601                                                                 const CatchBlock &catchBlock) {
602         auto typeIdx = catchBlock.GetTypeIdx();
603         const uint8_t *pc =
604             &GetInstructions()[catchBlock.GetHandlerPc()];  // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
605         size_t size = catchBlock.GetCodeSize();
606         const Class *cls = nullptr;
607         if (typeIdx != panda_file::INVALID_INDEX) {
608             Runtime *runtime = Runtime::GetCurrent();
609             auto typeId = GetClass()->ResolveClassIndex(typeIdx);
610             // NOTE: remove next code, after solving #1220 '[Runtime] Proposal for class descriptors in panda files'
611             //       and clean up of ClassLinker API
612             // cut
613             LanguageContext ctx = runtime->GetLanguageContext(*this);
614             cls = runtime->GetClassLinker()->GetExtension(ctx)->GetClass(*(pandaFile_), typeId);
615             // end cut
616         }
617         return callback(tryStartPc, tryEndPc, cls, pc, size);
618     });
619 }
620 
621 }  // namespace ark
622 
623 #endif  // !PANDA_RUNTIME_METHOD_INL_H_
624