1 /*
2 * Copyright (c) 2021 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_INCLUDE_METHOD_INL_H_
17 #define PANDA_RUNTIME_INCLUDE_METHOD_INL_H_
18
19 #include "libpandafile/code_data_accessor-inl.h"
20 #include "libpandafile/code_data_accessor.h"
21 #include "libpandafile/file.h"
22 #include "libpandafile/method_data_accessor-inl.h"
23 #include "libpandafile/method_data_accessor.h"
24 #include "libpandafile/proto_data_accessor-inl.h"
25 #include "libpandafile/proto_data_accessor.h"
26 #include "runtime/bridge/bridge.h"
27 #include "runtime/include/class_linker.h"
28 #include "runtime/include/method.h"
29 #include "runtime/include/runtime.h"
30 #include "runtime/include/panda_vm.h"
31 #include "runtime/include/runtime_notification.h"
32 #include "runtime/interpreter/interpreter.h"
33 #include "runtime/interpreter/runtime_interface.h"
34
35 namespace panda {
36
37 template <bool is_dynamic>
InvokeCompiledCode(ManagedThread * thread,uint32_t num_actual_args,Value * args)38 Value Method::InvokeCompiledCode(ManagedThread *thread, uint32_t num_actual_args, Value *args)
39 {
40 Frame *current_frame = thread->GetCurrentFrame();
41 Span<Value> args_span(args, num_actual_args);
42 // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_REDUNDANT_INIT)
43 DecodedTaggedValue ret_value {};
44 bool is_compiled = thread->IsCurrentFrameCompiled();
45 // Use frame allocator to alloc memory for parameters as thread can be terminated and
46 // InvokeCompiledCodeWithArgArray will not return in this case we will get memory leak with internal
47 // allocator
48 mem::StackFrameAllocator *allocator = thread->GetStackFrameAllocator();
49 auto values_deleter = [allocator](int64_t *values) {
50 if (values != nullptr) {
51 allocator->Free(values);
52 }
53 };
54 auto values = PandaUniquePtr<int64_t, decltype(values_deleter)>(nullptr, values_deleter);
55 size_t values_count = 0;
56 if (num_actual_args > 0) {
57 // In the worse case we are calling a dynamic method in which all arguments are pairs ot int64_t
58 // That is why we allocate 2 x num_actual_args
59 size_t capacity = 2 * num_actual_args * sizeof(int64_t);
60 // All allocations through FrameAllocator must be aligned
61 capacity = AlignUp(capacity, GetAlignmentInBytes(DEFAULT_FRAME_ALIGNMENT));
62 values.reset(reinterpret_cast<int64_t *>(allocator->Alloc(capacity)));
63 Span<int64_t> values_span(values.get(), capacity);
64 for (uint32_t i = 0; i < num_actual_args; ++i, ++values_count) {
65 if (args_span[i].IsReference()) {
66 values_span[values_count] = reinterpret_cast<int64_t>(args_span[i].GetAs<ObjectHeader *>());
67 } else if (args_span[i].IsDecodedTaggedValue()) {
68 DecodedTaggedValue v = args_span[i].GetDecodedTaggedValue();
69 values_span[values_count++] = v.value;
70 values_span[values_count] = v.tag;
71 } else {
72 values_span[values_count] = args_span[i].GetAs<int64_t>();
73 }
74 }
75 }
76 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
77 if constexpr (is_dynamic) {
78 ASSERT(values_count >= 2U);
79 ASSERT(values_count % 2U == 0);
80 // All arguments must be pair of int64_t. That is why we divide by 2
81 // -1 means we don't count function object;
82 uint32_t num_args = static_cast<uint32_t>(values_count) / 2 - 1; // 2 - look at the comment above
83 ret_value = InvokeCompiledCodeWithArgArrayDyn(values.get(), num_args, current_frame, this, thread);
84 } else { // NOLINTE(readability-braces-around-statements)
85 ret_value = InvokeCompiledCodeWithArgArray(values.get(), current_frame, this, thread);
86 }
87 thread->SetCurrentFrameIsCompiled(is_compiled);
88 thread->SetCurrentFrame(current_frame);
89 if (UNLIKELY(thread->HasPendingException())) {
90 ret_value = DecodedTaggedValue(0, 0);
91 }
92 return GetReturnValueFromTaggedValue(ret_value);
93 }
94
95 template <bool is_dynamic>
InvokeInterpretedCode(ManagedThread * thread,uint32_t num_actual_args,Value * args,void * data)96 Value Method::InvokeInterpretedCode(ManagedThread *thread, uint32_t num_actual_args, Value *args, void *data)
97 {
98 Frame *current_frame = thread->GetCurrentFrame();
99 // CODECHECK-NOLINTNEXTLINE(C_RULE_ID_REDUNDANT_INIT)
100 Value res(static_cast<int64_t>(0));
101 panda_file::Type ret_type = GetReturnType();
102 if (!Verify()) {
103 auto ctx = Runtime::GetCurrent()->GetLanguageContext(*this);
104 panda::ThrowVerificationException(ctx, GetFullName());
105 if (ret_type.IsReference()) {
106 res = Value(nullptr);
107 } else {
108 res = Value(static_cast<int64_t>(0));
109 }
110 } else {
111 PandaUniquePtr<Frame, FrameDeleter> frame =
112 InitFrame<is_dynamic>(thread, num_actual_args, args, current_frame, data);
113 if (UNLIKELY(frame.get() == nullptr)) {
114 panda::ThrowOutOfMemoryError("CreateFrame failed: " + GetFullName());
115 if (ret_type.IsReference()) {
116 res = Value(nullptr);
117 } else {
118 res = Value(static_cast<int64_t>(0));
119 }
120 return res;
121 }
122
123 LOG(DEBUG, INTERPRETER) << "Invoke entry: " << GetFullName();
124
125 auto is_compiled = thread->IsCurrentFrameCompiled();
126 thread->SetCurrentFrameIsCompiled(false);
127 thread->SetCurrentFrame(frame.get());
128 if (is_compiled && current_frame != nullptr) {
129 // Create C2I bridge frame in case of previous frame is a JNI frame or other compiler frame.
130 // But create only if the previous frame is not a C2I bridge already.
131 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
132 C2IBridge bridge;
133 if (!StackWalker::IsBoundaryFrame<FrameKind::INTERPRETER>(current_frame)) {
134 bridge = {0, reinterpret_cast<uintptr_t>(current_frame), COMPILED_CODE_TO_INTERPRETER,
135 thread->GetNativePc()};
136 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
137 frame->SetPrevFrame(reinterpret_cast<Frame *>(&bridge.v_[1]));
138 }
139 // Workaround for #2888 #2925
140 // We cannot make OSR on the methods called from here, because:
141 // 1. If caller is JNI method, then C2I bridge, created above, is not complete. It can be fixed by
142 // allocating full size boundary frame.
143 // 2. If caller is compiled method, then we got here from entrypoint. But currently compiler creates
144 // boundary frame with pseudo LR value, that doesn't point to the instruction after call, thereby
145 // OSR will fail. It can be fixed by addresses patching, currently codegen hasn't no such mechanism.
146 frame->DisableOsr();
147 Runtime::GetCurrent()->GetNotificationManager()->MethodEntryEvent(thread, this);
148 interpreter::Execute(thread, GetInstructions(), frame.get());
149 Runtime::GetCurrent()->GetNotificationManager()->MethodExitEvent(thread, this);
150 thread->SetCurrentFrameIsCompiled(true);
151 } else {
152 Runtime::GetCurrent()->GetNotificationManager()->MethodEntryEvent(thread, this);
153 interpreter::Execute(thread, GetInstructions(), frame.get());
154 Runtime::GetCurrent()->GetNotificationManager()->MethodExitEvent(thread, this);
155 }
156 thread->SetCurrentFrame(current_frame);
157 res = GetReturnValueFromAcc(ret_type, thread->HasPendingException(), frame->GetAcc());
158
159 LOG(DEBUG, INTERPRETER) << "Invoke exit: " << GetFullName();
160 }
161 return res;
162 }
163
164 template <bool is_dynamic>
InitFrame(ManagedThread * thread,uint32_t num_actual_args,Value * args,Frame * current_frame,void * data)165 PandaUniquePtr<Frame, FrameDeleter> Method::InitFrame(ManagedThread *thread, uint32_t num_actual_args, Value *args,
166 Frame *current_frame, void *data)
167 {
168 ASSERT(code_id_.IsValid());
169 panda_file::CodeDataAccessor cda(*panda_file_, code_id_);
170 auto num_vregs = cda.GetNumVregs();
171
172 Span<Value> args_span(args, num_actual_args);
173
174 uint32_t num_declared_args = GetNumArgs();
175 size_t frame_size;
176 // NOLINTNEXTLINE(readability-braces-around-statements)
177 if constexpr (is_dynamic) {
178 frame_size = num_vregs + std::max(num_declared_args, num_actual_args);
179 } else { // NOLINTE(readability-braces-around-statements)
180 frame_size = num_vregs + num_declared_args;
181 }
182 auto frame_deleter = [](Frame *frame) { FreeFrame(frame); };
183 PandaUniquePtr<Frame, FrameDeleter> frame(
184 CreateFrameWithActualArgs(frame_size, num_actual_args, this, current_frame), frame_deleter);
185 if (UNLIKELY(frame.get() == nullptr)) {
186 return frame;
187 }
188
189 for (size_t i = 0; i < num_actual_args; i++) {
190 if (args_span[i].IsDecodedTaggedValue()) {
191 DecodedTaggedValue decoded = args_span[i].GetDecodedTaggedValue();
192 frame->GetVReg(num_vregs + i).SetValue(decoded.value);
193 frame->GetVReg(num_vregs + i).SetTag(decoded.tag);
194 } else if (args_span[i].IsReference()) {
195 frame->GetVReg(num_vregs + i).SetReference(args_span[i].GetAs<ObjectHeader *>());
196 } else {
197 frame->GetVReg(num_vregs + i).SetPrimitive(args_span[i].GetAs<int64_t>());
198 }
199 }
200 // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
201 if constexpr (is_dynamic) {
202 LanguageContext ctx = thread->GetLanguageContext();
203 DecodedTaggedValue initial_value = ctx.GetInitialDecodedValue();
204 for (size_t i = num_actual_args; i < num_declared_args; i++) {
205 frame->GetVReg(num_vregs + i).SetValue(initial_value.value);
206 frame->GetVReg(num_vregs + i).SetTag(initial_value.tag);
207 }
208 }
209
210 frame->SetData(data);
211 return frame;
212 }
213
214 template <bool is_dynamic>
InvokeImpl(ManagedThread * thread,uint32_t num_actual_args,Value * args,bool proxy_call,void * data)215 Value Method::InvokeImpl(ManagedThread *thread, uint32_t num_actual_args, Value *args, bool proxy_call, void *data)
216 {
217 IncrementHotnessCounter(0, nullptr);
218
219 // Currently, proxy methods should always be invoked in the interpreter. This constraint should be relaxed once
220 // we support same frame layout for interpreter and compiled methods.
221 bool run_interpreter = !HasCompiledCode() || proxy_call;
222 ASSERT(!(proxy_call && IsNative()));
223 if (!run_interpreter) {
224 return InvokeCompiledCode<is_dynamic>(thread, num_actual_args, args);
225 }
226
227 return InvokeInterpretedCode<is_dynamic>(thread, num_actual_args, args, data);
228 }
229
230 template <class AccVRegisterPtrT>
SetAcc(AccVRegisterPtrT acc)231 inline void Method::SetAcc([[maybe_unused]] AccVRegisterPtrT acc)
232 {
233 if constexpr (!std::is_same_v<AccVRegisterPtrT, std::nullptr_t>) { // NOLINT
234 if (acc != nullptr) {
235 ManagedThread::GetCurrent()->GetCurrentFrame()->SetAcc(*acc);
236 }
237 }
238 }
239
240 /**
241 * Increment method's hotness counter.
242 * @param bytecode_offset Offset of the target bytecode instruction. Used only for OSR.
243 * @param acc Pointer to the accumulator, it is needed because interpreter uses own Frame, not the one in the method.
244 * Used only for OSR.
245 * @return true if OSR has been occurred
246 */
247 template <class AccVRegisterPtrT>
IncrementHotnessCounter(uintptr_t bytecode_offset,AccVRegisterPtrT acc,bool osr)248 inline bool Method::IncrementHotnessCounter([[maybe_unused]] uintptr_t bytecode_offset,
249 [[maybe_unused]] AccVRegisterPtrT acc, [[maybe_unused]] bool osr)
250 {
251 ++stor_32_.hotness_counter_;
252 return false;
253 }
254
255 template <typename Callback>
EnumerateTypes(Callback handler)256 void Method::EnumerateTypes(Callback handler) const
257 {
258 panda_file::MethodDataAccessor mda(*panda_file_, file_id_);
259 mda.EnumerateTypesInProto(handler);
260 }
261
262 template <typename Callback>
EnumerateTryBlocks(Callback callback)263 void Method::EnumerateTryBlocks(Callback callback) const
264 {
265 ASSERT(!IsAbstract());
266
267 panda_file::MethodDataAccessor mda(*panda_file_, file_id_);
268 panda_file::CodeDataAccessor cda(*panda_file_, mda.GetCodeId().value());
269
270 cda.EnumerateTryBlocks(callback);
271 }
272
273 template <typename Callback>
EnumerateCatchBlocks(Callback callback)274 void Method::EnumerateCatchBlocks(Callback callback) const
275 {
276 ASSERT(!IsAbstract());
277
278 using TryBlock = panda_file::CodeDataAccessor::TryBlock;
279 using CatchBlock = panda_file::CodeDataAccessor::CatchBlock;
280
281 EnumerateTryBlocks([&callback, code = GetInstructions()](const TryBlock &try_block) {
282 bool next = true;
283 const uint8_t *try_start_pc = reinterpret_cast<uint8_t *>(reinterpret_cast<uintptr_t>(code) +
284 static_cast<uintptr_t>(try_block.GetStartPc()));
285 const uint8_t *try_end_pc = reinterpret_cast<uint8_t *>(reinterpret_cast<uintptr_t>(try_start_pc) +
286 static_cast<uintptr_t>(try_block.GetLength()));
287 // ugly, but API of TryBlock is badly designed: enumaration is paired with mutation & updating
288 const_cast<TryBlock &>(try_block).EnumerateCatchBlocks(
289 [&callback, &next, try_start_pc, try_end_pc](const CatchBlock &catch_block) {
290 return next = callback(try_start_pc, try_end_pc, catch_block);
291 });
292 return next;
293 });
294 }
295
296 template <typename Callback>
EnumerateExceptionHandlers(Callback callback)297 void Method::EnumerateExceptionHandlers(Callback callback) const
298 {
299 ASSERT(!IsAbstract());
300
301 using CatchBlock = panda_file::CodeDataAccessor::CatchBlock;
302
303 EnumerateCatchBlocks([this, callback = std::move(callback)](const uint8_t *try_start_pc, const uint8_t *try_end_pc,
304 const CatchBlock &catch_block) {
305 auto type_idx = catch_block.GetTypeIdx();
306 const uint8_t *pc =
307 &GetInstructions()[catch_block.GetHandlerPc()]; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
308 size_t size = catch_block.GetCodeSize();
309 const Class *cls = nullptr;
310 if (type_idx != panda_file::INVALID_INDEX) {
311 Runtime *runtime = Runtime::GetCurrent();
312 auto type_id = GetClass()->ResolveClassIndex(type_idx);
313 LanguageContext ctx = runtime->GetLanguageContext(*this);
314 cls = runtime->GetClassLinker()->GetExtension(ctx)->GetClass(*panda_file_, type_id);
315 }
316 return callback(try_start_pc, try_end_pc, cls, pc, size);
317 });
318 }
319
320 } // namespace panda
321
322 #endif // PANDA_RUNTIME_INCLUDE_METHOD_INL_H_
323