1 /**
2 * Copyright (c) 2021-2025 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 ASSERT(thread != nullptr);
462 // The compilation process will start performing
463 // once the counter value decreases to a value that is or less than 0
464 if (GetHotnessCounter() > 0) {
465 if (TryVerify<IS_CALL>()) {
466 DecrementHotnessCounter();
467 }
468 return false;
469 }
470
471 if (!Runtime::GetCurrent()->IsProfilerEnabled()) {
472 if (TryVerify<IS_CALL>()) {
473 ResetHotnessCounter();
474 }
475 return false;
476 }
477
478 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
479 if constexpr (!ArchTraits<RUNTIME_ARCH>::SUPPORT_OSR) {
480 ASSERT(!osr);
481 }
482
483 auto &options = Runtime::GetOptions();
484 uint32_t threshold = options.GetCompilerHotnessThreshold();
485 uint32_t profThreshold = options.GetCompilerProfilingThreshold();
486 if ((profThreshold < threshold) && !IsProfiling() && !HasCompiledCode() && !IsProfiled()) {
487 SetHotnessCounter(threshold - profThreshold - 1);
488 if (thread->GetVM()->IsStaticProfileEnabled()) {
489 StartProfiling();
490 }
491 if (!IsProfiling()) {
492 SetProfiled();
493 }
494 } else if (Runtime::GetCurrent()->IsJitEnabled()) {
495 CompilationStage status = GetCompilationStatus();
496 if (!(status == FAILED || status == WAITING || status == COMPILATION)) {
497 ASSERT((!osr) == (acc == nullptr));
498 SetAcc<AccVRegisterPtrT>(thread, acc);
499
500 if (func.IsHeapObject()) {
501 return DecrementHotnessCounterForTaggedFunction<IS_CALL>(thread, bcOffset, osr, func);
502 }
503 if (!TryVerify<IS_CALL>()) {
504 return false;
505 }
506 auto compiler = Runtime::GetCurrent()->GetPandaVM()->GetCompiler();
507 return compiler->CompileMethod(this, bcOffset, osr, func); // SUPPRESS_CSA(alpha.core.WasteObjHeader)
508 }
509 if (status == WAITING) {
510 DecrementHotnessCounter();
511 }
512 }
513 return false;
514 }
515
516 template <bool IS_CALL>
DecrementHotnessCounterForTaggedFunction(ManagedThread * thread,uintptr_t bcOffset,bool osr,TaggedValue func)517 inline bool Method::DecrementHotnessCounterForTaggedFunction(ManagedThread *thread, uintptr_t bcOffset, bool osr,
518 TaggedValue func)
519 {
520 [[maybe_unused]] HandleScope<ObjectHeader *> scope(thread);
521 VMHandle<ObjectHeader> handleFunc(thread, func.GetHeapObject());
522 if (TryVerify<IS_CALL>()) {
523 auto compiler = Runtime::GetCurrent()->GetPandaVM()->GetCompiler();
524 return compiler->CompileMethod(this, bcOffset, osr, TaggedValue(handleFunc.GetPtr()));
525 }
526
527 return false;
528 }
529
530 template <bool IS_CALL>
531 // CC-OFFNXT(G.FUD.06) perf critical
TryVerify()532 inline bool Method::TryVerify()
533 {
534 if constexpr (!IS_CALL) {
535 return true;
536 }
537 if (!IsIntrinsic() && (GetVerificationStage() != Method::VerificationStage::VERIFIED_OK)) {
538 if (UNLIKELY(!Verify())) {
539 auto ctx = Runtime::GetCurrent()->GetLanguageContext(*this);
540 ark::ThrowVerificationException(ctx, GetFullName());
541 return false;
542 }
543 }
544 return true;
545 }
546
547 template <bool IS_CALL, class AccVRegisterPtrT>
DecrementHotnessCounter(uintptr_t bytecodeOffset,AccVRegisterPtrT acc,bool osr,TaggedValue func)548 inline bool Method::DecrementHotnessCounter(uintptr_t bytecodeOffset, AccVRegisterPtrT acc, bool osr, TaggedValue func)
549 {
550 return DecrementHotnessCounter<IS_CALL>(ManagedThread::GetCurrent(), bytecodeOffset, acc, osr, func);
551 }
552
553 template <typename Callback>
EnumerateTypes(Callback handler)554 void Method::EnumerateTypes(Callback handler) const
555 {
556 panda_file::MethodDataAccessor mda(*(pandaFile_), fileId_);
557 mda.EnumerateTypesInProto(handler);
558 }
559
560 template <typename Callback>
EnumerateTryBlocks(Callback callback)561 void Method::EnumerateTryBlocks(Callback callback) const
562 {
563 ASSERT(!IsAbstract());
564
565 panda_file::MethodDataAccessor mda(*(pandaFile_), fileId_);
566 panda_file::CodeDataAccessor cda(*(pandaFile_), mda.GetCodeId().value());
567
568 cda.EnumerateTryBlocks(callback);
569 }
570
571 template <typename Callback>
EnumerateCatchBlocks(Callback callback)572 void Method::EnumerateCatchBlocks(Callback callback) const
573 {
574 ASSERT(!IsAbstract());
575
576 using TryBlock = panda_file::CodeDataAccessor::TryBlock;
577 using CatchBlock = panda_file::CodeDataAccessor::CatchBlock;
578
579 EnumerateTryBlocks([&callback, code = GetInstructions()](const TryBlock &tryBlock) {
580 bool next = true;
581 const uint8_t *tryStartPc = reinterpret_cast<uint8_t *>(reinterpret_cast<uintptr_t>(code) +
582 static_cast<uintptr_t>(tryBlock.GetStartPc()));
583 const uint8_t *tryEndPc = reinterpret_cast<uint8_t *>(reinterpret_cast<uintptr_t>(tryStartPc) +
584 static_cast<uintptr_t>(tryBlock.GetLength()));
585 // ugly, but API of TryBlock is bad designed: enumaration is paired with mutation & updating
586 const_cast<TryBlock &>(tryBlock).EnumerateCatchBlocks(
587 [&callback, &next, tryStartPc, tryEndPc](const CatchBlock &catchBlock) {
588 return next = callback(tryStartPc, tryEndPc, catchBlock);
589 });
590 return next;
591 });
592 }
593
594 template <typename Callback>
EnumerateExceptionHandlers(Callback callback)595 void Method::EnumerateExceptionHandlers(Callback callback) const
596 {
597 ASSERT(!IsAbstract());
598
599 using CatchBlock = panda_file::CodeDataAccessor::CatchBlock;
600
601 EnumerateCatchBlocks([this, callback = std::move(callback)](const uint8_t *tryStartPc, const uint8_t *tryEndPc,
602 const CatchBlock &catchBlock) {
603 auto typeIdx = catchBlock.GetTypeIdx();
604 const uint8_t *pc =
605 &GetInstructions()[catchBlock.GetHandlerPc()]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
606 size_t size = catchBlock.GetCodeSize();
607 const Class *cls = nullptr;
608 if (typeIdx != panda_file::INVALID_INDEX) {
609 Runtime *runtime = Runtime::GetCurrent();
610 auto typeId = GetClass()->ResolveClassIndex(typeIdx);
611 // NOTE: remove next code, after solving #1220 '[Runtime] Proposal for class descriptors in panda files'
612 // and clean up of ClassLinker API
613 // cut
614 LanguageContext ctx = runtime->GetLanguageContext(*this);
615 cls = runtime->GetClassLinker()->GetExtension(ctx)->GetClass(*(pandaFile_), typeId);
616 // end cut
617 }
618 return callback(tryStartPc, tryEndPc, cls, pc, size);
619 });
620 }
621
InitProfilingData(ark::ProfilingData * profilingData)622 inline bool Method::InitProfilingData(ark::ProfilingData *profilingData)
623 {
624 ProfilingData *oldValue = nullptr;
625 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
626 while (!pointer_.profilingData.compare_exchange_weak(oldValue, profilingData)) {
627 if (oldValue != nullptr) {
628 // We're late, some thread already started profiling.
629 return false;
630 }
631 }
632 return true;
633 }
634
635 } // namespace ark
636
637 #endif // !PANDA_RUNTIME_METHOD_INL_H_
638