1 /*
2 * Copyright (c) 2021-2022 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 panda {
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 num_vregs,uint32_t num_declared_args,uint32_t num_actual_args)49 ALWAYS_INLINE inline static uint32_t GetFrameSize(uint32_t num_vregs, uint32_t num_declared_args,
50 [[maybe_unused]] uint32_t num_actual_args)
51 {
52 return num_vregs + num_declared_args;
53 }
54
InitActualArgs(Frame * frame,Span<Value> args_span,uint32_t num_vregs,uint32_t num_declared_args)55 ALWAYS_INLINE inline static void InitActualArgs(Frame *frame, Span<Value> args_span, uint32_t num_vregs,
56 [[maybe_unused]] uint32_t num_declared_args)
57 {
58 StaticFrameHandler static_frame_helper(frame);
59 uint32_t num_actual_args = args_span.Size();
60 for (size_t i = 0; i < num_actual_args; ++i) {
61 if (args_span[i].IsDecodedTaggedValue()) {
62 DecodedTaggedValue decoded = args_span[i].GetDecodedTaggedValue();
63 static_frame_helper.GetVReg(num_vregs + i).SetValue(decoded.value);
64 static_frame_helper.GetVReg(num_vregs + i).SetTag(decoded.tag);
65 } else if (args_span[i].IsReference()) {
66 static_frame_helper.GetVReg(num_vregs + i).SetReference(args_span[i].GetAs<ObjectHeader *>());
67 } else {
68 static_frame_helper.GetVReg(num_vregs + i).SetPrimitive(args_span[i].GetAs<int64_t>());
69 }
70 }
71 }
72
InterpreterExecute(ManagedThread * thread,const uint8_t * pc,Frame * frame)73 ALWAYS_INLINE inline static void InterpreterExecute(ManagedThread *thread, const uint8_t *pc, Frame *frame)
74 {
75 interpreter::Execute(thread, pc, frame);
76 }
77
CreateFrame(ManagedThread * thread,uint32_t nregs_size,Method * method,Frame * prev,uint32_t nregs,uint32_t num_actual_args)78 ALWAYS_INLINE static Frame *CreateFrame([[maybe_unused]] ManagedThread *thread, uint32_t nregs_size, Method *method,
79 Frame *prev, uint32_t nregs, uint32_t num_actual_args)
80 {
81 return interpreter::RuntimeInterface::CreateFrameWithActualArgsAndSize(nregs_size, nregs, num_actual_args,
82 method, prev);
83 }
84 };
85
86 class InvokeHelperDynamic {
87 public:
88 static constexpr bool is_dynamic = true;
89
GetFrameSize(uint32_t num_vregs,uint32_t num_declared_args,uint32_t num_actual_args)90 ALWAYS_INLINE inline static uint32_t GetFrameSize(uint32_t num_vregs, uint32_t num_declared_args,
91 uint32_t num_actual_args)
92 {
93 return num_vregs + std::max(num_declared_args, num_actual_args);
94 }
95
InitActualArgs(Frame * frame,Span<coretypes::TaggedValue> args_span,uint32_t num_vregs,uint32_t num_declared_args)96 ALWAYS_INLINE inline static void InitActualArgs(Frame *frame, Span<coretypes::TaggedValue> args_span,
97 uint32_t num_vregs, [[maybe_unused]] uint32_t num_declared_args)
98 {
99 frame->SetDynamic();
100
101 DynamicFrameHandler dynamic_frame_helper(frame);
102 uint32_t num_actual_args = args_span.Size();
103 for (size_t i = 0; i < num_actual_args; ++i) {
104 dynamic_frame_helper.GetVReg(num_vregs + i).SetValue(args_span[i].GetRawData());
105 }
106
107 for (size_t i = num_actual_args; i < num_declared_args; i++) {
108 dynamic_frame_helper.GetVReg(num_vregs + i).SetValue(TaggedValue::VALUE_UNDEFINED);
109 }
110 }
111
InterpreterExecute(ManagedThread * thread,const uint8_t * pc,Frame * frame)112 ALWAYS_INLINE inline static void InterpreterExecute(ManagedThread *thread, const uint8_t *pc, Frame *frame)
113 {
114 interpreter::Execute(thread, pc, frame);
115 }
116
CompiledCodeExecute(ManagedThread * thread,Method * method,uint32_t num_args,coretypes::TaggedValue * args)117 ALWAYS_INLINE inline static coretypes::TaggedValue CompiledCodeExecute(ManagedThread *thread, Method *method,
118 uint32_t num_args,
119 coretypes::TaggedValue *args)
120 {
121 Frame *current_frame = thread->GetCurrentFrame();
122 bool is_compiled = thread->IsCurrentFrameCompiled();
123
124 ASSERT(num_args >= 2U); // NOTE(asoldatov): Adjust this check
125 uint64_t ret = InvokeCompiledCodeWithArgArrayDyn(reinterpret_cast<uint64_t *>(args), num_args, current_frame,
126 method, thread);
127 thread->SetCurrentFrameIsCompiled(is_compiled);
128 thread->SetCurrentFrame(current_frame);
129 if (UNLIKELY(thread->HasPendingException())) {
130 return coretypes::TaggedValue::Exception();
131 }
132 return coretypes::TaggedValue(ret);
133 }
134
CreateFrame(ManagedThread * thread,uint32_t nregs_size,Method * method,Frame * prev,uint32_t nregs,uint32_t num_actual_args)135 ALWAYS_INLINE static Frame *CreateFrame([[maybe_unused]] ManagedThread *thread, uint32_t nregs_size, Method *method,
136 Frame *prev, uint32_t nregs, uint32_t num_actual_args)
137 {
138 return interpreter::RuntimeInterface::CreateFrameWithActualArgsAndSize(nregs_size, nregs, num_actual_args,
139 method, prev);
140 }
141 };
142
143 template <class InvokeHelper, class ValueT>
GetReturnValueFromException()144 ValueT Method::GetReturnValueFromException()
145 {
146 if constexpr (InvokeHelper::is_dynamic) { // NOLINT(readability-braces-around-statements)
147 return TaggedValue::Undefined();
148 } else { // NOLINT(readability-misleading-indentation)
149 if (GetReturnType().IsReference()) {
150 return Value(nullptr);
151 }
152 return Value(static_cast<int64_t>(0));
153 }
154 }
155
156 template <class InvokeHelper, class ValueT>
GetReturnValueFromAcc(interpreter::AccVRegister & aac_vreg)157 ValueT Method::GetReturnValueFromAcc(interpreter::AccVRegister &aac_vreg)
158 {
159 if constexpr (InvokeHelper::is_dynamic) { // NOLINT(readability-braces-around-statements)
160 return TaggedValue(aac_vreg.GetAs<uint64_t>());
161 } else { // NOLINT(readability-misleading-indentation)
162 ASSERT(GetReturnType().GetId() != panda_file::Type::TypeId::TAGGED);
163 if (GetReturnType().GetId() != panda_file::Type::TypeId::VOID) {
164 interpreter::StaticVRegisterRef acc = aac_vreg.AsVRegRef<false>();
165 if (acc.HasObject()) {
166 return Value(aac_vreg.GetReference());
167 }
168 return Value(aac_vreg.GetLong());
169 }
170 return Value(static_cast<int64_t>(0));
171 }
172 }
173
InvokeCompiledCode(ManagedThread * thread,uint32_t num_args,Value * args)174 inline Value Method::InvokeCompiledCode(ManagedThread *thread, uint32_t num_args, Value *args)
175 {
176 Frame *current_frame = thread->GetCurrentFrame();
177 Span<Value> args_span(args, num_args);
178 DecodedTaggedValue ret_value {};
179 bool is_compiled = thread->IsCurrentFrameCompiled();
180 // Use frame allocator to alloc memory for parameters as thread can be terminated and
181 // InvokeCompiledCodeWithArgArray will not return in this case we will get memory leak with internal
182 // allocator
183 mem::StackFrameAllocator *allocator = thread->GetStackFrameAllocator();
184 auto values_deleter = [allocator](int64_t *values) {
185 if (values != nullptr) {
186 allocator->Free(values);
187 }
188 };
189 auto values = PandaUniquePtr<int64_t, decltype(values_deleter)>(nullptr, values_deleter);
190 if (num_args > 0) {
191 // In the worse case we are calling a dynamic method in which all arguments are pairs ot int64_t
192 // That is why we allocate 2 x num_actual_args
193 size_t capacity = num_args * sizeof(int64_t);
194 // All allocations though FrameAllocator must be aligned
195 capacity = AlignUp(capacity, GetAlignmentInBytes(DEFAULT_FRAME_ALIGNMENT));
196 values.reset(reinterpret_cast<int64_t *>(allocator->Alloc(capacity)));
197 Span<int64_t> values_span(values.get(), capacity);
198 for (uint32_t i = 0; i < num_args; ++i) {
199 if (args_span[i].IsReference()) {
200 values_span[i] = reinterpret_cast<int64_t>(args_span[i].GetAs<ObjectHeader *>());
201 } else {
202 values_span[i] = args_span[i].GetAs<int64_t>();
203 }
204 }
205 }
206
207 ret_value = InvokeCompiledCodeWithArgArray(values.get(), current_frame, this, thread);
208
209 thread->SetCurrentFrameIsCompiled(is_compiled);
210 thread->SetCurrentFrame(current_frame);
211 if (UNLIKELY(thread->HasPendingException())) {
212 ret_value = DecodedTaggedValue(0, 0);
213 }
214 return GetReturnValueFromTaggedValue(ret_value);
215 }
216
217 template <class InvokeHelper, class ValueT>
InvokeInterpretedCode(ManagedThread * thread,uint32_t num_actual_args,ValueT * args)218 ValueT Method::InvokeInterpretedCode(ManagedThread *thread, uint32_t num_actual_args, ValueT *args)
219 {
220 if (!Verify()) {
221 auto ctx = Runtime::GetCurrent()->GetLanguageContext(*this);
222 panda::ThrowVerificationException(ctx, GetFullName());
223 return GetReturnValueFromException<InvokeHelper, ValueT>();
224 }
225
226 Frame *current_frame = thread->GetCurrentFrame();
227 PandaUniquePtr<Frame, FrameDeleter> frame = InitFrame<InvokeHelper>(thread, num_actual_args, args, current_frame);
228 if (UNLIKELY(frame.get() == nullptr)) {
229 panda::ThrowOutOfMemoryError("CreateFrame failed: " + GetFullName());
230 return GetReturnValueFromException<InvokeHelper, ValueT>();
231 }
232
233 LOG(DEBUG, INTERPRETER) << "Invoke entry: " << GetFullName();
234 auto is_compiled = thread->IsCurrentFrameCompiled();
235 thread->SetCurrentFrameIsCompiled(false);
236 thread->SetCurrentFrame(frame.get());
237 if (is_compiled && current_frame != 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>(current_frame)) {
243 bridge = {0, reinterpret_cast<uintptr_t>(current_frame), 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 // TODO(msherstennikov): fix issue
257 frame->DisableOsr();
258 Runtime::GetCurrent()->GetNotificationManager()->MethodEntryEvent(thread, this);
259 // interpreter::Execute(thread, GetInstructions(), frame.get());
260 InvokeHelper::InterpreterExecute(thread, GetInstructions(), frame.get());
261 Runtime::GetCurrent()->GetNotificationManager()->MethodExitEvent(thread, this);
262 thread->SetCurrentFrameIsCompiled(true);
263 } else {
264 Runtime::GetCurrent()->GetNotificationManager()->MethodEntryEvent(thread, this);
265 InvokeHelper::InterpreterExecute(thread, GetInstructions(), frame.get());
266 // interpreter::Execute(thread, GetInstructions(), frame.get());
267 Runtime::GetCurrent()->GetNotificationManager()->MethodExitEvent(thread, this);
268 }
269 thread->SetCurrentFrame(current_frame);
270
271 ValueT res = (UNLIKELY(thread->HasPendingException()))
272 ? GetReturnValueFromException<InvokeHelper, ValueT>()
273 : GetReturnValueFromAcc<InvokeHelper, ValueT>(frame->GetAcc());
274 LOG(DEBUG, INTERPRETER) << "Invoke exit: " << GetFullName();
275 return res;
276 }
277
InvokeDyn(ManagedThread * thread,uint32_t num_args,coretypes::TaggedValue * args)278 inline coretypes::TaggedValue Method::InvokeDyn(ManagedThread *thread, uint32_t num_args, coretypes::TaggedValue *args)
279 {
280 return InvokeDyn<InvokeHelperDynamic>(thread, num_args, args);
281 }
282
283 template <class InvokeHelper>
InvokeDyn(ManagedThread * thread,uint32_t num_args,coretypes::TaggedValue * args)284 inline coretypes::TaggedValue Method::InvokeDyn(ManagedThread *thread, uint32_t num_args, coretypes::TaggedValue *args)
285 {
286 return InvokeImpl<InvokeHelper>(thread, num_args, args, false);
287 }
288
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 return InvokeContext<InvokeHelperDynamic>(thread, pc, acc, nregs, regs);
294 }
295
296 template <class InvokeHelper>
InvokeContext(ManagedThread * thread,const uint8_t * pc,coretypes::TaggedValue acc,uint32_t nregs,coretypes::TaggedValue * regs)297 inline coretypes::TaggedValue Method::InvokeContext(ManagedThread *thread, const uint8_t *pc,
298 coretypes::TaggedValue acc, uint32_t nregs,
299 coretypes::TaggedValue *regs)
300 {
301 static_assert(InvokeHelper::is_dynamic == true);
302 ASSERT(GetReturnType().GetId() == panda_file::Type::TypeId::VOID ||
303 GetReturnType().GetId() == panda_file::Type::TypeId::TAGGED);
304
305 TaggedValue res(TaggedValue::VALUE_UNDEFINED);
306
307 if (!Verify()) {
308 auto ctx = Runtime::GetCurrent()->GetLanguageContext(*this);
309 panda::ThrowVerificationException(ctx, GetFullName());
310 return res;
311 }
312
313 Frame *current_frame = thread->GetCurrentFrame();
314 PandaUniquePtr<Frame, FrameDeleter> frame(
315 interpreter::RuntimeInterface::CreateFrameWithActualArgs<true>(nregs, nregs, this,
316 current_frame),
317 FrameDeleter(thread));
318 if (UNLIKELY(frame.get() == nullptr)) {
319 panda::ThrowOutOfMemoryError("CreateFrame failed: " + GetFullName());
320 return res;
321 }
322
323 frame->SetDynamic();
324
325 DynamicFrameHandler dynamic_frame_helper(frame.get());
326 Span<TaggedValue> args_span(regs, nregs);
327 for (size_t i = 0; i < nregs; ++i) {
328 dynamic_frame_helper.GetVReg(i).SetValue(args_span[i].GetRawData());
329 }
330
331 LOG(DEBUG, INTERPRETER) << "Invoke entry: " << GetFullName();
332
333 dynamic_frame_helper.GetAcc().SetValue(acc.GetRawData());
334 thread->SetCurrentFrame(frame.get());
335
336 Runtime::GetCurrent()->GetNotificationManager()->MethodEntryEvent(thread, this);
337 InvokeHelper::InterpreterExecute(thread, pc, frame.get());
338 Runtime::GetCurrent()->GetNotificationManager()->MethodExitEvent(thread, this);
339
340 thread->SetCurrentFrame(current_frame);
341 res = TaggedValue(dynamic_frame_helper.GetAcc().GetAs<uint64_t>());
342
343 LOG(DEBUG, INTERPRETER) << "Invoke exit: " << GetFullName();
344 return res;
345 }
346
347 template <class InvokeHelper, class ValueT>
EnterNativeMethodFrame(ManagedThread * thread,uint32_t num_vregs,uint32_t num_args,ValueT * args)348 Frame *Method::EnterNativeMethodFrame(ManagedThread *thread, uint32_t num_vregs, uint32_t num_args, ValueT *args)
349 {
350 Frame *current_frame = thread->GetCurrentFrame();
351 PandaUniquePtr<Frame, FrameDeleter> frame =
352 InitFrameWithNumVRegs<InvokeHelper, ValueT, true>(thread, num_vregs, num_args, args, current_frame);
353 if (UNLIKELY(frame.get() == nullptr)) {
354 panda::ThrowOutOfMemoryError("CreateFrame failed: " + GetFullName());
355 return nullptr;
356 }
357
358 LOG(DEBUG, INTERPRETER) << "Enter native frame";
359
360 thread->SetCurrentFrame(frame.get());
361 return frame.release();
362 }
363
ExitNativeMethodFrame(ManagedThread * thread)364 inline void Method::ExitNativeMethodFrame(ManagedThread *thread)
365 {
366 Frame *current_frame = thread->GetCurrentFrame();
367 ASSERT(current_frame != nullptr);
368
369 LOG(DEBUG, INTERPRETER) << "Exit native frame";
370
371 thread->SetCurrentFrame(current_frame->GetPrevFrame());
372 FreeFrame(current_frame);
373 }
374
375 template <class InvokeHelper, class ValueT>
InitFrame(ManagedThread * thread,uint32_t num_actual_args,ValueT * args,Frame * current_frame)376 PandaUniquePtr<Frame, FrameDeleter> Method::InitFrame(ManagedThread *thread, uint32_t num_actual_args, ValueT *args,
377 Frame *current_frame)
378 {
379 ASSERT(code_id_.IsValid());
380 auto num_vregs = panda_file::CodeDataAccessor::GetNumVregs(*(panda_file_), code_id_);
381 return InitFrameWithNumVRegs<InvokeHelper, ValueT, false>(thread, num_vregs, num_actual_args, args, current_frame);
382 }
383
384 template <class InvokeHelper, class ValueT, bool is_native_method>
InitFrameWithNumVRegs(ManagedThread * thread,uint32_t num_vregs,uint32_t num_actual_args,ValueT * args,Frame * current_frame)385 PandaUniquePtr<Frame, FrameDeleter> Method::InitFrameWithNumVRegs(ManagedThread *thread, uint32_t num_vregs,
386 uint32_t num_actual_args, ValueT *args,
387 Frame *current_frame)
388 {
389 Span<ValueT> args_span(args, num_actual_args);
390
391 uint32_t num_declared_args = GetNumArgs();
392 uint32_t frame_size = InvokeHelper::GetFrameSize(num_vregs, num_declared_args, num_actual_args);
393
394 Frame *frame_ptr;
395 // NOLINTNEXTLINE(readability-braces-around-statements)
396 if constexpr (is_native_method) {
397 frame_ptr = interpreter::RuntimeInterface::CreateNativeFrameWithActualArgs<InvokeHelper::is_dynamic>(
398 frame_size, num_actual_args, this, current_frame);
399 } else { // NOLINTNEXTLINE(readability-braces-around-statements)
400 frame_ptr = InvokeHelper::CreateFrame(thread, Frame::GetActualSize<InvokeHelper::is_dynamic>(frame_size), this,
401 current_frame, frame_size, num_actual_args);
402 }
403 PandaUniquePtr<Frame, FrameDeleter> frame(frame_ptr, FrameDeleter(thread));
404 if (UNLIKELY(frame.get() == nullptr)) {
405 return frame;
406 }
407
408 InvokeHelper::InitActualArgs(frame.get(), args_span, num_vregs, num_declared_args);
409
410 frame->SetInvoke();
411 return frame;
412 }
413
414 template <class InvokeHelper, class ValueT>
InvokeImpl(ManagedThread * thread,uint32_t num_actual_args,ValueT * args,bool proxy_call)415 ValueT Method::InvokeImpl(ManagedThread *thread, uint32_t num_actual_args, ValueT *args, bool proxy_call)
416 {
417 IncrementHotnessCounter(0, nullptr);
418
419 // Currently, proxy methods should always be invoked in the interpreter. This constraint should be relaxed once
420 // we support same frame layout for interpreter and compiled methods.
421 // TODO(msherstennikov): remove `proxy_call`
422 bool run_interpreter = !HasCompiledCode() || proxy_call;
423 ASSERT(!(proxy_call && IsNative()));
424 if (!run_interpreter) {
425 if constexpr (InvokeHelper::is_dynamic) { // NOLINT(readability-braces-around-statements)
426 return InvokeHelper::CompiledCodeExecute(thread, this, num_actual_args, args);
427 } else { // NOLINT(readability-misleading-indentation)
428 return InvokeCompiledCode(thread, num_actual_args, args);
429 }
430 }
431 if (!thread->template StackOverflowCheck<true, false>()) {
432 return GetReturnValueFromException<InvokeHelper, ValueT>();
433 }
434
435 return InvokeInterpretedCode<InvokeHelper>(thread, num_actual_args, args);
436 }
437
438 template <class AccVRegisterPtrT>
SetAcc(AccVRegisterPtrT acc)439 inline void Method::SetAcc([[maybe_unused]] AccVRegisterPtrT acc)
440 {
441 if constexpr (!std::is_same_v<AccVRegisterPtrT, std::nullptr_t>) { // NOLINT
442 if (acc != nullptr) {
443 ManagedThread::GetCurrent()->GetCurrentFrame()->SetAcc(*acc);
444 }
445 }
446 }
447
448 /**
449 * Increment method's hotness counter.
450 * @param bytecode_offset Offset of the target bytecode instruction. Used only for OSR.
451 * @param acc Pointer to the accumulator, it is needed because interpreter uses own Frame, not the one in the method.
452 * Used only for OSR.
453 * @return true if OSR has been occurred
454 */
455 template <class AccVRegisterPtrT>
IncrementHotnessCounter(uintptr_t bytecode_offset,AccVRegisterPtrT acc,bool osr)456 inline bool Method::IncrementHotnessCounter(uintptr_t bytecode_offset, [[maybe_unused]] AccVRegisterPtrT acc, bool osr)
457 {
458 bool is_jit_enabled = Runtime::GetCurrent()->IsJitEnabled();
459
460 if (!is_jit_enabled) {
461 // hotness_counter_ may be used as profiling information so it should be incremented even with disabled JIT:
462 stor_16_pair_.hotness_counter_++;
463 return false;
464 }
465 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
466 if constexpr (!ArchTraits<RUNTIME_ARCH>::SUPPORT_OSR) {
467 ASSERT(!osr);
468 }
469
470 auto runtime = Runtime::GetCurrent();
471 auto &options = Runtime::GetOptions();
472 uint32_t threshold = options.GetCompilerHotnessThreshold();
473 if (UNLIKELY(GetHotnessCounter() >= threshold)) {
474 CompilationStage status = GetCompilationStatus();
475 if (!(status == FAILED || status == WAITING || status == COMPILATION)) {
476 ASSERT((!osr) == (acc == nullptr));
477 SetAcc<AccVRegisterPtrT>(acc);
478
479 return runtime->GetPandaVM()->GetCompiler()->CompileMethod(this, bytecode_offset, osr);
480 }
481 if (status == WAITING) {
482 IncrementHotnessCounter();
483 }
484 } else {
485 if (GetHotnessCounter() == options.GetCompilerProfilingThreshold() && !HasCompiledCode() && !IsProfiling()) {
486 StartProfiling();
487 }
488 IncrementHotnessCounter();
489 }
490 return false;
491 }
492
493 template <typename Callback>
EnumerateTypes(Callback handler)494 void Method::EnumerateTypes(Callback handler) const
495 {
496 panda_file::MethodDataAccessor mda(*(panda_file_), file_id_);
497 mda.EnumerateTypesInProto(handler);
498 }
499
500 template <typename Callback>
EnumerateTryBlocks(Callback callback)501 void Method::EnumerateTryBlocks(Callback callback) const
502 {
503 ASSERT(!IsAbstract());
504
505 panda_file::MethodDataAccessor mda(*(panda_file_), file_id_);
506 panda_file::CodeDataAccessor cda(*(panda_file_), mda.GetCodeId().value());
507
508 cda.EnumerateTryBlocks(callback);
509 }
510
511 template <typename Callback>
EnumerateCatchBlocks(Callback callback)512 void Method::EnumerateCatchBlocks(Callback callback) const
513 {
514 ASSERT(!IsAbstract());
515
516 using TryBlock = panda_file::CodeDataAccessor::TryBlock;
517 using CatchBlock = panda_file::CodeDataAccessor::CatchBlock;
518
519 EnumerateTryBlocks([&callback, code = GetInstructions()](const TryBlock &try_block) {
520 bool next = true;
521 const uint8_t *try_start_pc = reinterpret_cast<uint8_t *>(reinterpret_cast<uintptr_t>(code) +
522 static_cast<uintptr_t>(try_block.GetStartPc()));
523 const uint8_t *try_end_pc = reinterpret_cast<uint8_t *>(reinterpret_cast<uintptr_t>(try_start_pc) +
524 static_cast<uintptr_t>(try_block.GetLength()));
525 // ugly, but API of TryBlock is bad designed: enumaration is paired with mutation & updating
526 const_cast<TryBlock &>(try_block).EnumerateCatchBlocks(
527 [&callback, &next, try_start_pc, try_end_pc](const CatchBlock &catch_block) {
528 return next = callback(try_start_pc, try_end_pc, catch_block);
529 });
530 return next;
531 });
532 }
533
534 template <typename Callback>
EnumerateExceptionHandlers(Callback callback)535 void Method::EnumerateExceptionHandlers(Callback callback) const
536 {
537 ASSERT(!IsAbstract());
538
539 using CatchBlock = panda_file::CodeDataAccessor::CatchBlock;
540
541 EnumerateCatchBlocks([this, callback = std::move(callback)](const uint8_t *try_start_pc, const uint8_t *try_end_pc,
542 const CatchBlock &catch_block) {
543 auto type_idx = catch_block.GetTypeIdx();
544 const uint8_t *pc =
545 &GetInstructions()[catch_block.GetHandlerPc()]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
546 size_t size = catch_block.GetCodeSize();
547 const Class *cls = nullptr;
548 if (type_idx != panda_file::INVALID_INDEX) {
549 Runtime *runtime = Runtime::GetCurrent();
550 auto type_id = GetClass()->ResolveClassIndex(type_idx);
551 // todo: remove next code, after solving #1220 '[Runtime] Proposal for class descriptors in panda files'
552 // and clean up of ClassLinker API
553 // cut
554 LanguageContext ctx = runtime->GetLanguageContext(*this);
555 cls = runtime->GetClassLinker()->GetExtension(ctx)->GetClass(*(panda_file_), type_id);
556 // end cut
557 }
558 return callback(try_start_pc, try_end_pc, cls, pc, size);
559 });
560 }
561
562 } // namespace panda
563
564 #endif // !PANDA_RUNTIME_METHOD_INL_H_
565