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 #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 panda {
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
PrepareOsrEntry(const Frame * iframe,uintptr_t bcOffset,const void * osrCode,void * cframePtr,uintptr_t * regBuffer,uintptr_t * fpRegBuffer)113 extern "C" void *PrepareOsrEntry(const Frame *iframe, uintptr_t bcOffset, const void *osrCode, void *cframePtr,
114 uintptr_t *regBuffer, uintptr_t *fpRegBuffer)
115 {
116 CodeInfo codeInfo(CodeInfo::GetCodeOriginFromEntryPoint(osrCode));
117 CFrame cframe(cframePtr);
118 auto stackmap = codeInfo.FindOsrStackMap(bcOffset);
119
120 ASSERT(stackmap.IsValid() && osrCode != nullptr);
121
122 cframe.SetMethod(iframe->GetMethod());
123 cframe.SetFrameKind(CFrameLayout::FrameKind::OSR);
124 cframe.SetHasFloatRegs(codeInfo.HasFloatRegs());
125
126 auto *thread {ManagedThread::GetCurrent()};
127 ASSERT(thread != nullptr);
128
129 auto *vm {thread->GetVM()};
130
131 auto numSlots {[](size_t bytes) -> size_t {
132 ASSERT(IsAligned(bytes, ArchTraits<RUNTIME_ARCH>::POINTER_SIZE));
133 return bytes / ArchTraits<RUNTIME_ARCH>::POINTER_SIZE;
134 }};
135
136 Span paramSlots(reinterpret_cast<uintptr_t *>(cframe.GetStackArgsStart()), numSlots(GetStackParamsSize(iframe)));
137
138 auto ctx {vm->GetLanguageContext()};
139 ctx.InitializeOsrCframeSlots(paramSlots);
140
141 for (auto vreg : codeInfo.GetVRegList(stackmap, mem::InternalAllocator<>::GetInternalAllocatorFromRuntime())) {
142 if (!vreg.IsLive()) {
143 continue;
144 }
145 int64_t value = 0;
146 if (vreg.IsAccumulator()) {
147 value = iframe->GetAcc().GetValue();
148 } else if (!vreg.IsSpecialVReg()) {
149 ASSERT(vreg.GetVRegType() == VRegInfo::VRegType::VREG);
150 value = iframe->GetVReg(vreg.GetIndex()).GetValue();
151 } else {
152 value = ctx.GetOsrEnv(iframe, vreg);
153 }
154 #ifdef PANDA_USE_32_BIT_POINTER
155 if (vreg.IsObject()) {
156 value = static_cast<int32_t>(value);
157 }
158 #endif
159 switch (vreg.GetLocation()) {
160 case VRegInfo::Location::SLOT:
161 cframe.SetVRegValue(vreg, value, nullptr);
162 break;
163 case VRegInfo::Location::REGISTER:
164 regBuffer[vreg.GetValue()] = value; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
165 break;
166 case VRegInfo::Location::FP_REGISTER:
167 fpRegBuffer[vreg.GetValue()] = value; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
168 break;
169 // NOLINTNEXTLINE(bugprone-branch-clone)
170 case VRegInfo::Location::CONSTANT:
171 break;
172 default:
173 break;
174 }
175 }
176
177 thread->SetCurrentFrame(reinterpret_cast<Frame *>(cframePtr));
178 thread->SetCurrentFrameIsCompiled(true);
179
180 return bit_cast<void *>(bit_cast<uintptr_t>(osrCode) + stackmap.GetNativePcUnpacked());
181 }
182
SetOsrResult(Frame * frame,uint64_t uval,double fval)183 extern "C" void SetOsrResult(Frame *frame, uint64_t uval, double fval)
184 {
185 ASSERT(frame != nullptr);
186 panda_file::ShortyIterator it(frame->GetMethod()->GetShorty());
187 using panda_file::Type;
188 auto &acc = frame->GetAcc();
189
190 switch ((*it).GetId()) {
191 case Type::TypeId::U1:
192 case Type::TypeId::I8:
193 case Type::TypeId::U8:
194 case Type::TypeId::I16:
195 case Type::TypeId::U16:
196 case Type::TypeId::I32:
197 case Type::TypeId::U32:
198 case Type::TypeId::I64:
199 case Type::TypeId::U64:
200 acc.SetValue(uval);
201 acc.SetTag(interpreter::StaticVRegisterRef::PRIMITIVE_TYPE);
202 break;
203 case Type::TypeId::REFERENCE:
204 acc.SetValue(uval);
205 acc.SetTag(interpreter::StaticVRegisterRef::GC_OBJECT_TYPE);
206 break;
207 case Type::TypeId::F32:
208 case Type::TypeId::F64:
209 acc.SetValue(bit_cast<int64_t>(fval));
210 acc.SetTag(interpreter::StaticVRegisterRef::PRIMITIVE_TYPE);
211 break;
212 case Type::TypeId::VOID:
213 // Interpreter always restores accumulator from the callee method, even if callee method is void. Thus, we
214 // need to reset it here, otherwise it can hold old object, that probably isn't live already.
215 acc.SetValue(0);
216 acc.SetTag(interpreter::StaticVRegisterRef::PRIMITIVE_TYPE);
217 break;
218 case Type::TypeId::TAGGED:
219 acc.SetValue(uval);
220 break;
221 case Type::TypeId::INVALID:
222 UNREACHABLE();
223 default:
224 UNREACHABLE();
225 }
226 }
227
GetStackParamsSize(const Frame * frame)228 static size_t GetStackParamsSize(const Frame *frame)
229 {
230 constexpr auto SLOT_SIZE {ArchTraits<RUNTIME_ARCH>::POINTER_SIZE};
231 auto *method {frame->GetMethod()};
232 if (frame->IsDynamic()) {
233 auto numArgs {std::max(method->GetNumArgs(), frame->GetNumActualArgs())};
234 auto argSize {std::max(sizeof(coretypes::TaggedType), SLOT_SIZE)};
235 return RoundUp(numArgs * argSize, 2U * SLOT_SIZE);
236 }
237
238 arch::ArgCounter<RUNTIME_ARCH> counter;
239 counter.Count<Method *>();
240 if (!method->IsStatic()) {
241 counter.Count<ObjectHeader *>();
242 }
243 panda_file::ShortyIterator it(method->GetShorty());
244 ++it; // skip return type
245 while (it != panda_file::ShortyIterator()) {
246 switch ((*it).GetId()) {
247 case panda_file::Type::TypeId::U1:
248 case panda_file::Type::TypeId::U8:
249 case panda_file::Type::TypeId::I8:
250 case panda_file::Type::TypeId::I16:
251 case panda_file::Type::TypeId::U16:
252 case panda_file::Type::TypeId::I32:
253 case panda_file::Type::TypeId::U32:
254 counter.Count<int32_t>();
255 break;
256 case panda_file::Type::TypeId::F32:
257 counter.Count<float>();
258 break;
259 case panda_file::Type::TypeId::F64:
260 counter.Count<double>();
261 break;
262 case panda_file::Type::TypeId::I64:
263 case panda_file::Type::TypeId::U64:
264 counter.Count<int64_t>();
265 break;
266 case panda_file::Type::TypeId::REFERENCE:
267 counter.Count<ObjectHeader *>();
268 break;
269 case panda_file::Type::TypeId::TAGGED:
270 counter.Count<coretypes::TaggedType>();
271 break;
272 default:
273 UNREACHABLE();
274 }
275 ++it;
276 }
277 return RoundUp(counter.GetOnlyStackSize(), 2U * SLOT_SIZE);
278 }
279
280 } // namespace panda
281