1 /*
2 * Copyright (c) 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 #include "deoptimizer.h"
16 #include "ecmascript/compiler/assembler/assembler.h"
17 #include "ecmascript/compiler/gate_meta_data.h"
18 #include "ecmascript/dfx/stackinfo/js_stackinfo.h"
19 #include "ecmascript/frames.h"
20 #include "ecmascript/interpreter/interpreter.h"
21 #include "ecmascript/js_thread.h"
22 #include "ecmascript/stubs/runtime_stubs-inl.h"
23
24 namespace panda::ecmascript {
25 class FrameWriter {
26 public:
FrameWriter(Deoptimizier * deoptimizier)27 explicit FrameWriter(Deoptimizier *deoptimizier) : thread_(deoptimizier->GetThread())
28 {
29 JSTaggedType *prevSp = const_cast<JSTaggedType *>(thread_->GetCurrentSPFrame());
30 start_ = top_ = EcmaInterpreter::GetInterpreterFrameEnd(thread_, prevSp);
31 }
32
PushValue(JSTaggedType value)33 void PushValue(JSTaggedType value)
34 {
35 *(--top_) = value;
36 }
37
PushRawValue(uintptr_t value)38 void PushRawValue(uintptr_t value)
39 {
40 *(--top_) = value;
41 }
42
Reserve(size_t size)43 bool Reserve(size_t size)
44 {
45 return !thread_->DoStackOverflowCheck(top_ - size);
46 }
47
ReserveAsmInterpretedFrame()48 AsmInterpretedFrame *ReserveAsmInterpretedFrame()
49 {
50 auto frame = AsmInterpretedFrame::GetFrameFromSp(top_);
51 top_ = reinterpret_cast<JSTaggedType *>(frame);
52 return frame;
53 }
54
GetStart() const55 JSTaggedType *GetStart() const
56 {
57 return start_;
58 }
59
GetTop() const60 JSTaggedType *GetTop() const
61 {
62 return top_;
63 }
64
65 private:
66 JSThread *thread_ {nullptr};
67 JSTaggedType *start_;
68 JSTaggedType *top_;
69 };
70
CollectVregs(const std::vector<kungfu::ARKDeopt> & deoptBundle)71 void Deoptimizier::CollectVregs(const std::vector<kungfu::ARKDeopt>& deoptBundle)
72 {
73 deoptVregs_.clear();
74 for (size_t i = 0; i < deoptBundle.size(); i++) {
75 kungfu::ARKDeopt deopt = deoptBundle.at(i);
76 JSTaggedType v;
77 kungfu::OffsetType id = deopt.Id;
78 if (std::holds_alternative<kungfu::DwarfRegAndOffsetType>(deopt.value)) {
79 ASSERT(deopt.kind == kungfu::LocationTy::Kind::INDIRECT);
80 auto value = std::get<kungfu::DwarfRegAndOffsetType>(deopt.value);
81 kungfu::DwarfRegType dwarfReg = value.first;
82 kungfu::OffsetType offset = value.second;
83 ASSERT (dwarfReg == GCStackMapRegisters::FP || dwarfReg == GCStackMapRegisters::SP);
84 uintptr_t addr;
85 if (dwarfReg == GCStackMapRegisters::SP) {
86 addr = context_.callsiteSp + offset;
87 } else {
88 addr = context_.callsiteFp + offset;
89 }
90 v = *(reinterpret_cast<JSTaggedType *>(addr));
91 } else if (std::holds_alternative<kungfu::LargeInt>(deopt.value)) {
92 ASSERT(deopt.kind == kungfu::LocationTy::Kind::CONSTANTNDEX);
93 v = JSTaggedType(static_cast<int64_t>(std::get<kungfu::LargeInt>(deopt.value)));
94 } else {
95 ASSERT(std::holds_alternative<kungfu::OffsetType>(deopt.value));
96 ASSERT(deopt.kind == kungfu::LocationTy::Kind::CONSTANT);
97 v = JSTaggedType(static_cast<int64_t>(std::get<kungfu::OffsetType>(deopt.value)));
98 }
99 if (id != static_cast<kungfu::OffsetType>(SpecVregIndex::PC_INDEX)) {
100 if (id == static_cast<kungfu::OffsetType>(SpecVregIndex::ENV_INDEX)) {
101 env_ = JSTaggedValue(v);
102 } else {
103 deoptVregs_[id] = JSTaggedValue(v);
104 }
105 } else {
106 pc_ = static_cast<uint32_t>(v);
107 }
108 }
109 }
110
CollectDeoptBundleVec(std::vector<kungfu::ARKDeopt> & deoptBundle)111 void Deoptimizier::CollectDeoptBundleVec(std::vector<kungfu::ARKDeopt>& deoptBundle)
112 {
113 JSTaggedType *lastLeave = const_cast<JSTaggedType *>(thread_->GetLastLeaveFrame());
114 FrameIterator it(lastLeave, thread_);
115 for (; !it.Done() && deoptBundle.empty(); it.Advance<GCVisitedFlag::VISITED>()) {
116 FrameType type = it.GetFrameType();
117 switch (type) {
118 case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: {
119 auto frame = it.GetFrame<OptimizedJSFunctionFrame>();
120 frame->GetDeoptBundleInfo(it, deoptBundle);
121 kungfu::CalleeRegAndOffsetVec calleeRegInfo;
122 frame->GetFuncCalleeRegAndOffset(it, calleeRegInfo);
123 context_.calleeRegAndOffset = calleeRegInfo;
124 context_.callsiteSp = it.GetCallSiteSp();
125 context_.callsiteFp = reinterpret_cast<uintptr_t>(it.GetSp());
126 auto preFrameSp = frame->ComputePrevFrameSp(it);
127 frameArgc_ = frame->GetArgc(preFrameSp);
128 frameArgvs_ = frame->GetArgv(preFrameSp);
129 stackContext_.callFrameTop_ = it.GetPrevFrameCallSiteSp();
130 stackContext_.returnAddr_ = frame->GetReturnAddr();
131 stackContext_.callerFp_ = reinterpret_cast<uintptr_t>(frame->GetPrevFrameFp());
132 break;
133 }
134 case FrameType::OPTIMIZED_FRAME: {
135 auto sp = reinterpret_cast<uintptr_t*>(it.GetSp());
136 sp -= 2; // 2: skip type & glue
137 calleeRegAddr_ = sp - numCalleeRegs_;
138 break;
139 }
140 case FrameType::LEAVE_FRAME:
141 break;
142 default: {
143 LOG_FULL(FATAL) << "frame type error!";
144 UNREACHABLE();
145 }
146 }
147 }
148 ASSERT(!it.Done());
149 }
150
GetMethod(JSTaggedValue & target)151 Method* Deoptimizier::GetMethod(JSTaggedValue &target)
152 {
153 ECMAObject *callTarget = reinterpret_cast<ECMAObject*>(target.GetTaggedObject());
154 ASSERT(callTarget != nullptr);
155 Method *method = callTarget->GetCallTarget();
156 return method;
157 }
158
RelocateCalleeSave()159 void Deoptimizier::RelocateCalleeSave()
160 {
161 kungfu::CalleeReg callreg;
162 for (auto &it: context_.calleeRegAndOffset) {
163 auto reg = it.first;
164 auto offset = it.second;
165 uintptr_t value = *(reinterpret_cast<uintptr_t *>(context_.callsiteFp + offset));
166 int order = callreg.FindCallRegOrder(reg);
167 calleeRegAddr_[order] = value;
168 }
169 }
170
CollectVirtualRegisters(Method * method,FrameWriter * frameWriter)171 bool Deoptimizier::CollectVirtualRegisters(Method* method, FrameWriter *frameWriter)
172 {
173 int32_t actualNumArgs = static_cast<int32_t>(frameArgc_) - NUM_MANDATORY_JSFUNC_ARGS;
174 bool haveExtra = method->HaveExtraWithCallField();
175 int32_t declaredNumArgs = static_cast<int32_t>(method->GetNumArgsWithCallField());
176 int32_t callFieldNumVregs = static_cast<int32_t>(method->GetNumVregsWithCallField());
177 // layout of frame:
178 // [maybe argc] [actual args] [reserved args] [call field virtual regs]
179
180 // [maybe argc]
181 if (declaredNumArgs != actualNumArgs && haveExtra) {
182 auto value = JSTaggedValue(actualNumArgs);
183 frameWriter->PushValue(value.GetRawData());
184 }
185 int32_t reservedCount = std::max(actualNumArgs, declaredNumArgs);
186 int32_t virtualIndex = reservedCount + callFieldNumVregs +
187 static_cast<int32_t>(method->GetNumRevervedArgs()) - 1;
188 if (!frameWriter->Reserve(static_cast<size_t>(virtualIndex))) {
189 return false;
190 }
191 // [actual args]
192 if (declaredNumArgs > actualNumArgs) {
193 for (int32_t i = 0; i < declaredNumArgs - actualNumArgs; i++) {
194 frameWriter->PushValue(JSTaggedValue::Undefined().GetRawData());
195 virtualIndex--;
196 }
197 }
198 for (int32_t i = actualNumArgs - 1; i >= 0; i--) {
199 JSTaggedValue value = JSTaggedValue::Undefined();
200 // deopt value
201 if (HasDeoptValue(virtualIndex)) {
202 value = deoptVregs_.at(static_cast<kungfu::OffsetType>(virtualIndex));
203 } else {
204 value = GetActualFrameArgs(i);
205 }
206 frameWriter->PushValue(value.GetRawData());
207 virtualIndex--;
208 }
209
210 // [reserved args]
211 if (method->HaveThisWithCallField()) {
212 JSTaggedValue value = GetFrameArgv(kungfu::CommonArgIdx::THIS_OBJECT);
213 frameWriter->PushValue(value.GetRawData());
214 virtualIndex--;
215 }
216 if (method->HaveNewTargetWithCallField()) {
217 JSTaggedValue value = GetFrameArgv(kungfu::CommonArgIdx::NEW_TARGET);
218 frameWriter->PushValue(value.GetRawData());
219 virtualIndex--;
220 }
221 if (method->HaveFuncWithCallField()) {
222 JSTaggedValue value = GetFrameArgv(kungfu::CommonArgIdx::FUNC);
223 frameWriter->PushValue(value.GetRawData());
224 virtualIndex--;
225 }
226
227 // [call field virtual regs]
228 for (int32_t i = virtualIndex; i >= 0; i--) {
229 JSTaggedValue value = GetDeoptValue(virtualIndex);
230 frameWriter->PushValue(value.GetRawData());
231 virtualIndex--;
232 }
233 return true;
234 }
235
Dump(Method * method,kungfu::DeoptType type)236 void Deoptimizier::Dump(Method* method, kungfu::DeoptType type)
237 {
238 if (traceDeopt_) {
239 std::string checkType = DisplayItems(type);
240 LOG_COMPILER(INFO) << "Check Type: " << checkType;
241 std::string data = JsStackInfo::BuildMethodTrace(method, pc_);
242 LOG_COMPILER(INFO) << "Deoptimize" << data;
243 const uint8_t *pc = method->GetBytecodeArray() + pc_;
244 BytecodeInstruction inst(pc);
245 LOG_COMPILER(INFO) << inst;
246 }
247 }
248
DisplayItems(kungfu::DeoptType type)249 std::string Deoptimizier::DisplayItems(kungfu::DeoptType type)
250 {
251 switch (type) {
252 case kungfu::DeoptType::NOTINT:
253 return "NOT INT";
254 case kungfu::DeoptType::NOTDOUBLE:
255 return "NOT DOUBLE";
256 case kungfu::DeoptType::NOTNUMBER:
257 return "NOT NUMBER";
258 case kungfu::DeoptType::NOTBOOL:
259 return "NOT BOOL";
260 case kungfu::DeoptType::NOTARRAY:
261 return "NOT ARRAY";
262 case kungfu::DeoptType::NOTSARRAY:
263 return "NOT SARRAY";
264 case kungfu::DeoptType::NOTF32ARRAY:
265 return "NOT F32ARRAY";
266 case kungfu::DeoptType::WRONGHCLASS:
267 return "WRONG HCLASS";
268 case kungfu::DeoptType::NOTNEWOBJ:
269 return "NOT NEWOBJ TYPE";
270 case kungfu::DeoptType::NOTARRAYIDX:
271 return "NOT ARRAY IDX";
272 case kungfu::DeoptType::NOTF32ARRAYIDX:
273 return "NOT F32 ARRAY IDX";
274 case kungfu::DeoptType::NOTINCOV:
275 return "NOT INC OVERFLOW";
276 case kungfu::DeoptType::NOTDECOV:
277 return "NOT DEC OVERFLOW";
278 case kungfu::DeoptType::NOTNEGOV:
279 return "NOT NEG OVERFLOW";
280 case kungfu::DeoptType::NOTCALLTGT:
281 return "NOT CALL TARGET";
282 default: {
283 return "NOT CHECK";
284 }
285 }
286 }
287
ConstructAsmInterpretFrame(kungfu::DeoptType type)288 JSTaggedType Deoptimizier::ConstructAsmInterpretFrame(kungfu::DeoptType type)
289 {
290 JSTaggedValue callTarget = GetFrameArgv(kungfu::CommonArgIdx::FUNC);
291 auto method = GetMethod(callTarget);
292 Dump(method, type);
293 ASSERT(thread_ != nullptr);
294 uint8_t deoptThreshold = method->GetDeoptThreshold();
295 if (deoptThreshold > 0) {
296 method->SetDeoptType(type);
297 method->SetDeoptThreshold(--deoptThreshold);
298 } else {
299 method->SetAotCodeBit(false);
300 method->SetDeoptType(kungfu::DeoptType::NOTCHECK);
301 method->SetCodeEntryOrLiteral(reinterpret_cast<uintptr_t>(nullptr));
302 }
303
304 FrameWriter frameWriter(this);
305 // Push asm interpreter frame
306 if (!CollectVirtualRegisters(method, &frameWriter)) {
307 return JSTaggedValue::Exception().GetRawData();
308 }
309 AsmInterpretedFrame *statePtr = frameWriter.ReserveAsmInterpretedFrame();
310 const uint8_t *resumePc = method->GetBytecodeArray() + pc_;
311
312 JSTaggedValue thisObj = GetFrameArgv(kungfu::CommonArgIdx::THIS_OBJECT);
313 auto acc = GetDeoptValue(static_cast<int32_t>(SpecVregIndex::ACC_INDEX));
314 statePtr->function = callTarget;
315 statePtr->acc = acc;
316 statePtr->env = env_;
317 statePtr->callSize = 0;
318 statePtr->fp = 0; // need update
319 statePtr->thisObj = thisObj;
320 statePtr->pc = resumePc;
321 // -uintptr_t skip lr
322 statePtr->base.prev = reinterpret_cast<JSTaggedType *>(
323 stackContext_.callFrameTop_ - sizeof(uintptr_t));
324 statePtr->base.type = FrameType::ASM_INTERPRETER_FRAME;
325
326 // construct stack context
327 auto start = frameWriter.GetStart();
328 auto end = frameWriter.GetTop();
329 auto outputCount = start - end;
330
331 RelocateCalleeSave();
332
333 frameWriter.PushRawValue(stackContext_.callerFp_);
334 frameWriter.PushRawValue(stackContext_.returnAddr_);
335 frameWriter.PushRawValue(stackContext_.callFrameTop_);
336 frameWriter.PushRawValue(outputCount);
337 return reinterpret_cast<JSTaggedType>(frameWriter.GetTop());
338 }
339 } // namespace panda::ecmascript