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 #include "runtime/osr.h"
17
18 #include "libpandabase/events/events.h"
19 #include "libpandafile/shorty_iterator.h"
20 #include "runtime/include/managed_thread.h"
21 #include "runtime/include/method.h"
22 #include "runtime/include/stack_walker.h"
23 #include "code_info/code_info.h"
24 #include "runtime/include/panda_vm.h"
25 #include "compiler/optimizer/ir/runtime_interface.h"
26
27 namespace ark {
28
29 using compiler::CodeInfo;
30 using compiler::VRegInfo;
31
UnpoisonAsanStack(void * ptr)32 static void UnpoisonAsanStack([[maybe_unused]] void *ptr)
33 {
34 #ifdef PANDA_ASAN_ON
35 uint8_t sp;
36 ASAN_UNPOISON_MEMORY_REGION(&sp, reinterpret_cast<uint8_t *>(ptr) - &sp);
37 #endif // PANDA_ASAN_ON
38 }
39
40 #if EVENT_OSR_ENTRY_ENABLED
WriteOsrEventError(Frame * frame,FrameKind kind,uintptr_t loopHeadBc)41 void WriteOsrEventError(Frame *frame, FrameKind kind, uintptr_t loopHeadBc)
42 {
43 events::OsrEntryKind osrKind;
44 switch (kind) {
45 case FrameKind::INTERPRETER:
46 osrKind = events::OsrEntryKind::AFTER_IFRAME;
47 break;
48 case FrameKind::COMPILER:
49 osrKind = events::OsrEntryKind::AFTER_CFRAME;
50 break;
51 case FrameKind::NONE:
52 osrKind = events::OsrEntryKind::TOP_FRAME;
53 break;
54 default:
55 UNREACHABLE();
56 }
57 EVENT_OSR_ENTRY(std::string(frame->GetMethod()->GetFullName()), loopHeadBc, osrKind, events::OsrEntryResult::ERROR);
58 }
59 #endif // EVENT_OSR_ENTRY_ENABLED
60
61 static size_t GetStackParamsSize(const Frame *frame);
62
OsrEntry(uintptr_t loopHeadBc,const void * osrCode)63 bool OsrEntry(uintptr_t loopHeadBc, const void *osrCode)
64 {
65 auto stack = StackWalker::Create(ManagedThread::GetCurrent());
66 Frame *frame = stack.GetIFrame();
67 LOG(DEBUG, INTEROP) << "OSR entry in method '" << stack.GetMethod()->GetFullName() << "': " << osrCode;
68 CodeInfo codeInfo(CodeInfo::GetCodeOriginFromEntryPoint(osrCode));
69 auto stackmap = codeInfo.FindOsrStackMap(loopHeadBc);
70 if (!stackmap.IsValid()) {
71 #if EVENT_OSR_ENTRY_ENABLED
72 WriteOsrEventError(frame, stack.GetPreviousFrameKind(), loopHeadBc);
73 #endif // EVENT_OSR_ENTRY_ENABLED
74 return false;
75 }
76
77 switch (stack.GetPreviousFrameKind()) {
78 case FrameKind::INTERPRETER:
79 LOG(DEBUG, INTEROP) << "OSR: after interpreter frame";
80 EVENT_OSR_ENTRY(std::string(frame->GetMethod()->GetFullName()), loopHeadBc,
81 events::OsrEntryKind::AFTER_IFRAME, events::OsrEntryResult::SUCCESS);
82 OsrEntryAfterIFrame(frame, loopHeadBc, osrCode, codeInfo.GetFrameSize(), GetStackParamsSize(frame));
83 break;
84 case FrameKind::COMPILER:
85 if (frame->IsDynamic() && frame->GetNumActualArgs() < frame->GetMethod()->GetNumArgs()) {
86 frame->DisableOsr();
87 // We need to adjust slot arguments space from previous compiled frame.
88 #if EVENT_OSR_ENTRY_ENABLED
89 WriteOsrEventError(frame, stack.GetPreviousFrameKind(), loopHeadBc);
90 #endif // EVENT_OSR_ENTRY_ENABLED
91 LOG(DEBUG, INTEROP) << "OSR: after compiled frame, fail: num_actual_args < num_args";
92 return false;
93 }
94 UnpoisonAsanStack(frame->GetPrevFrame());
95 LOG(DEBUG, INTEROP) << "OSR: after compiled frame";
96 EVENT_OSR_ENTRY(std::string(frame->GetMethod()->GetFullName()), loopHeadBc,
97 events::OsrEntryKind::AFTER_CFRAME, events::OsrEntryResult::SUCCESS);
98 OsrEntryAfterCFrame(frame, loopHeadBc, osrCode, codeInfo.GetFrameSize());
99 UNREACHABLE();
100 break;
101 case FrameKind::NONE:
102 LOG(DEBUG, INTEROP) << "OSR: after no frame";
103 EVENT_OSR_ENTRY(std::string(frame->GetMethod()->GetFullName()), loopHeadBc, events::OsrEntryKind::TOP_FRAME,
104 events::OsrEntryResult::SUCCESS);
105 OsrEntryTopFrame(frame, loopHeadBc, osrCode, codeInfo.GetFrameSize(), GetStackParamsSize(frame));
106 break;
107 default:
108 break;
109 }
110 return true;
111 }
112
GetValueFromVregAcc(const Frame * iframe,LanguageContext & ctx,VRegInfo & vreg)113 static int64_t GetValueFromVregAcc(const Frame *iframe, LanguageContext &ctx, VRegInfo &vreg)
114 {
115 int64_t value = 0;
116 if (vreg.IsAccumulator()) {
117 value = iframe->GetAcc().GetValue();
118 } else if (!vreg.IsSpecialVReg()) {
119 ASSERT(vreg.GetVRegType() == VRegInfo::VRegType::VREG);
120 value = iframe->GetVReg(vreg.GetIndex()).GetValue();
121 } else {
122 value = static_cast<int64_t>(ctx.GetOsrEnv(iframe, vreg));
123 }
124 #ifdef PANDA_USE_32_BIT_POINTER
125 if (vreg.IsObject()) {
126 value = static_cast<int32_t>(value);
127 }
128 #endif
129 return value;
130 }
131
PrepareOsrEntry(const Frame * iframe,uintptr_t bcOffset,const void * osrCode,void * cframePtr,uintptr_t * regBuffer,uintptr_t * fpRegBuffer)132 extern "C" void *PrepareOsrEntry(const Frame *iframe, uintptr_t bcOffset, const void *osrCode, void *cframePtr,
133 uintptr_t *regBuffer, uintptr_t *fpRegBuffer)
134 {
135 CodeInfo codeInfo(CodeInfo::GetCodeOriginFromEntryPoint(osrCode));
136 CFrame cframe(cframePtr);
137 auto stackmap = codeInfo.FindOsrStackMap(bcOffset);
138
139 ASSERT(stackmap.IsValid() && osrCode != nullptr);
140
141 cframe.SetMethod(iframe->GetMethod());
142 cframe.SetFrameKind(CFrameLayout::FrameKind::OSR);
143 cframe.SetHasFloatRegs(codeInfo.HasFloatRegs());
144
145 auto *thread {ManagedThread::GetCurrent()};
146 ASSERT(thread != nullptr);
147
148 auto *vm {thread->GetVM()};
149
150 auto numSlots {[](size_t bytes) -> size_t {
151 ASSERT(IsAligned(bytes, ArchTraits<RUNTIME_ARCH>::POINTER_SIZE));
152 return bytes / ArchTraits<RUNTIME_ARCH>::POINTER_SIZE;
153 }};
154
155 Span paramSlots(reinterpret_cast<uintptr_t *>(cframe.GetStackArgsStart()), numSlots(GetStackParamsSize(iframe)));
156
157 auto ctx {vm->GetLanguageContext()};
158 ctx.InitializeOsrCframeSlots(paramSlots);
159
160 for (auto vreg : codeInfo.GetVRegList(stackmap, mem::InternalAllocator<>::GetInternalAllocatorFromRuntime())) {
161 if (!vreg.IsLive()) {
162 continue;
163 }
164 int64_t value = GetValueFromVregAcc(iframe, ctx, vreg);
165 switch (vreg.GetLocation()) {
166 case VRegInfo::Location::SLOT:
167 cframe.SetVRegValue(vreg, value, nullptr);
168 break;
169 case VRegInfo::Location::REGISTER:
170 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
171 regBuffer[vreg.GetValue()] = value;
172 break;
173 case VRegInfo::Location::FP_REGISTER:
174 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
175 fpRegBuffer[vreg.GetValue()] = static_cast<uintptr_t>(value);
176 break;
177 // NOLINTNEXTLINE(bugprone-branch-clone)
178 case VRegInfo::Location::CONSTANT:
179 break;
180 default:
181 break;
182 }
183 }
184
185 thread->SetCurrentFrame(reinterpret_cast<Frame *>(cframePtr));
186 thread->SetCurrentFrameIsCompiled(true);
187
188 return bit_cast<void *>(bit_cast<uintptr_t>(osrCode) + stackmap.GetNativePcUnpacked());
189 }
190
SetOsrResult(Frame * frame,uint64_t uval,double fval)191 extern "C" void SetOsrResult(Frame *frame, uint64_t uval, double fval)
192 {
193 ASSERT(frame != nullptr);
194 panda_file::ShortyIterator it(frame->GetMethod()->GetShorty());
195 using panda_file::Type;
196 auto &acc = frame->GetAcc();
197
198 switch ((*it).GetId()) {
199 case Type::TypeId::U1:
200 case Type::TypeId::I8:
201 case Type::TypeId::U8:
202 case Type::TypeId::I16:
203 case Type::TypeId::U16:
204 case Type::TypeId::I32:
205 case Type::TypeId::U32:
206 case Type::TypeId::I64:
207 case Type::TypeId::U64:
208 acc.SetValue(uval);
209 acc.SetTag(interpreter::StaticVRegisterRef::PRIMITIVE_TYPE);
210 break;
211 case Type::TypeId::REFERENCE:
212 acc.SetValue(uval);
213 acc.SetTag(interpreter::StaticVRegisterRef::GC_OBJECT_TYPE);
214 break;
215 case Type::TypeId::F32:
216 case Type::TypeId::F64:
217 acc.SetValue(bit_cast<int64_t>(fval));
218 acc.SetTag(interpreter::StaticVRegisterRef::PRIMITIVE_TYPE);
219 break;
220 case Type::TypeId::VOID:
221 // Interpreter always restores accumulator from the callee method, even if callee method is void. Thus, we
222 // need to reset it here, otherwise it can hold old object, that probably isn't live already.
223 acc.SetValue(0);
224 acc.SetTag(interpreter::StaticVRegisterRef::PRIMITIVE_TYPE);
225 break;
226 case Type::TypeId::TAGGED:
227 acc.SetValue(uval);
228 break;
229 case Type::TypeId::INVALID:
230 UNREACHABLE();
231 default:
232 UNREACHABLE();
233 }
234 }
235
GetStackParamsSize(const Frame * frame)236 static size_t GetStackParamsSize(const Frame *frame)
237 {
238 constexpr auto SLOT_SIZE {ArchTraits<RUNTIME_ARCH>::POINTER_SIZE};
239 auto *method {frame->GetMethod()};
240 if (frame->IsDynamic()) {
241 auto numArgs {std::max(method->GetNumArgs(), frame->GetNumActualArgs())};
242 auto argSize {std::max(sizeof(coretypes::TaggedType), SLOT_SIZE)};
243 return RoundUp(numArgs * argSize, 2U * SLOT_SIZE);
244 }
245
246 arch::ArgCounter<RUNTIME_ARCH> counter;
247 counter.Count<Method *>();
248 if (!method->IsStatic()) {
249 counter.Count<ObjectHeader *>();
250 }
251 panda_file::ShortyIterator it(method->GetShorty());
252 ++it; // skip return type
253 while (it != panda_file::ShortyIterator()) {
254 switch ((*it).GetId()) {
255 case panda_file::Type::TypeId::U1:
256 case panda_file::Type::TypeId::U8:
257 case panda_file::Type::TypeId::I8:
258 case panda_file::Type::TypeId::I16:
259 case panda_file::Type::TypeId::U16:
260 case panda_file::Type::TypeId::I32:
261 case panda_file::Type::TypeId::U32:
262 counter.Count<int32_t>();
263 break;
264 case panda_file::Type::TypeId::F32:
265 counter.Count<float>();
266 break;
267 case panda_file::Type::TypeId::F64:
268 counter.Count<double>();
269 break;
270 case panda_file::Type::TypeId::I64:
271 case panda_file::Type::TypeId::U64:
272 counter.Count<int64_t>();
273 break;
274 case panda_file::Type::TypeId::REFERENCE:
275 counter.Count<ObjectHeader *>();
276 break;
277 case panda_file::Type::TypeId::TAGGED:
278 counter.Count<coretypes::TaggedType>();
279 break;
280 default:
281 UNREACHABLE();
282 }
283 ++it;
284 }
285 return RoundUp(counter.GetOnlyStackSize(), 2U * SLOT_SIZE);
286 }
287
288 } // namespace ark
289