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