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