• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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