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