• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
111 // when AOT trigger deopt, frame layout as the following
112 // * OptimizedJSFunctionFrame layout description as the following:
113 //               +--------------------------+ ---------------
114 //               |        ......            |               ^
115 //               |        ......            |       callerFunction
116 //               |        ......            |               |
117 //               |--------------------------|               |
118 //               |        args              |               v
119 //               +--------------------------+ ---------------
120 //               |       returnAddr         |               ^
121 //               |--------------------------|               |
122 //               |       callsiteFp         |               |
123 //               |--------------------------|   OptimizedJSFunction  FrameType:OPTIMIZED_JS_FUNCTION_FRAME
124 //               |       frameType          |               |
125 //               |--------------------------|               |
126 //               |       call-target        |               |
127 //               |--------------------------|               |
128 //               |       lexEnv             |               |
129 //               |--------------------------|               |
130 //               |       ...........        |               v
131 //               +--------------------------+ ---------------
132 //               |       returnAddr         |               ^
133 //               |--------------------------|               |
134 //               |       callsiteFp         |               |
135 //               |--------------------------|   __llvm_deoptimize  FrameType:OPTIMIZED_FRAME
136 //               |       frameType          |               |
137 //               |--------------------------|               |
138 //               |       No CalleeSave      |               |
139 //               |       Registers          |               v
140 //               +--------------------------+ ---------------
141 //               |       returnAddr         |               ^
142 //               |--------------------------|               |
143 //               |       callsiteFp         |               |
144 //               |--------------------------|   DeoptHandlerAsm  FrameType:ASM_BRIDGE_FRAME
145 //               |       frameType          |               |
146 //               |--------------------------|               |
147 //               |       glue               |               |
148 //               |--------------------------|               |
149 //               | CalleeSave Registers     |               v
150 //               +--------------------------+ ---------------
151 //               |       .........          |               ^
152 //               |       .........          |     CallRuntime   FrameType:LEAVE_FRAME
153 //               |       .........          |               |
154 //               |       .........          |               v
155 //               |--------------------------| ---------------
156 
CollectDeoptBundleVec(std::vector<kungfu::ARKDeopt> & deoptBundle)157 void Deoptimizier::CollectDeoptBundleVec(std::vector<kungfu::ARKDeopt>& deoptBundle)
158 {
159     JSTaggedType *lastLeave = const_cast<JSTaggedType *>(thread_->GetLastLeaveFrame());
160     FrameIterator it(lastLeave, thread_);
161     // note: last deopt bridge frame is generated by DeoptHandlerAsm, callee Regs is grow from this frame
162     for (; !it.Done() && deoptBundle.empty(); it.Advance<GCVisitedFlag::VISITED>()) {
163         FrameType type = it.GetFrameType();
164         switch (type) {
165             case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: {
166                 auto frame = it.GetFrame<OptimizedJSFunctionFrame>();
167                 frame->GetDeoptBundleInfo(it, deoptBundle);
168                 kungfu::CalleeRegAndOffsetVec calleeRegInfo;
169                 frame->GetFuncCalleeRegAndOffset(it, calleeRegInfo);
170                 context_.calleeRegAndOffset = calleeRegInfo;
171                 context_.callsiteSp = it.GetCallSiteSp();
172                 context_.callsiteFp = reinterpret_cast<uintptr_t>(it.GetSp());
173                 auto preFrameSp = frame->ComputePrevFrameSp(it);
174                 frameArgc_ = frame->GetArgc(preFrameSp);
175                 frameArgvs_ = frame->GetArgv(preFrameSp);
176                 stackContext_.callFrameTop_ = it.GetPrevFrameCallSiteSp();
177                 stackContext_.returnAddr_ = frame->GetReturnAddr();
178                 stackContext_.callerFp_ = reinterpret_cast<uintptr_t>(frame->GetPrevFrameFp());
179                 break;
180             }
181             case FrameType::ASM_BRIDGE_FRAME: {
182                 auto sp = reinterpret_cast<uintptr_t*>(it.GetSp());
183                 static constexpr size_t TYPE_GLUE_SLOT = 2; // 2: skip type & glue
184                 sp -= TYPE_GLUE_SLOT;
185                 calleeRegAddr_ = sp - numCalleeRegs_;
186                 break;
187             }
188             case FrameType::OPTIMIZED_FRAME:
189             case FrameType::LEAVE_FRAME:
190                 break;
191             default: {
192                 LOG_FULL(FATAL) << "frame type error!";
193                 UNREACHABLE();
194             }
195         }
196     }
197     ASSERT(!it.Done());
198 }
199 
GetMethod(JSTaggedValue & target)200 Method* Deoptimizier::GetMethod(JSTaggedValue &target)
201 {
202     ECMAObject *callTarget = reinterpret_cast<ECMAObject*>(target.GetTaggedObject());
203     ASSERT(callTarget != nullptr);
204     Method *method = callTarget->GetCallTarget();
205     return method;
206 }
207 
RelocateCalleeSave()208 void Deoptimizier::RelocateCalleeSave()
209 {
210     kungfu::CalleeReg callreg;
211     for (auto &it: context_.calleeRegAndOffset) {
212         auto reg = it.first;
213         auto offset = it.second;
214         uintptr_t value = *(reinterpret_cast<uintptr_t *>(context_.callsiteFp + offset));
215         int order = callreg.FindCallRegOrder(reg);
216         calleeRegAddr_[order] = value;
217     }
218 }
219 
CollectVirtualRegisters(Method * method,FrameWriter * frameWriter)220 bool Deoptimizier::CollectVirtualRegisters(Method* method, FrameWriter *frameWriter)
221 {
222     int32_t actualNumArgs = static_cast<int32_t>(frameArgc_) - NUM_MANDATORY_JSFUNC_ARGS;
223     bool haveExtra = method->HaveExtraWithCallField();
224     int32_t declaredNumArgs = static_cast<int32_t>(method->GetNumArgsWithCallField());
225     int32_t callFieldNumVregs = static_cast<int32_t>(method->GetNumVregsWithCallField());
226     // layout of frame:
227     // [maybe argc] [actual args] [reserved args] [call field virtual regs]
228 
229     // [maybe argc]
230     if (declaredNumArgs != actualNumArgs && haveExtra) {
231         auto value = JSTaggedValue(actualNumArgs);
232         frameWriter->PushValue(value.GetRawData());
233     }
234     int32_t reservedCount = std::max(actualNumArgs, declaredNumArgs);
235     int32_t virtualIndex = reservedCount + callFieldNumVregs +
236         static_cast<int32_t>(method->GetNumRevervedArgs()) - 1;
237     if (!frameWriter->Reserve(static_cast<size_t>(virtualIndex))) {
238         return false;
239     }
240     // [actual args]
241     if (declaredNumArgs > actualNumArgs) {
242         for (int32_t i = 0; i < declaredNumArgs - actualNumArgs; i++) {
243             frameWriter->PushValue(JSTaggedValue::Undefined().GetRawData());
244             virtualIndex--;
245         }
246     }
247     for (int32_t i = actualNumArgs - 1; i >= 0; i--) {
248         JSTaggedValue value = JSTaggedValue::Undefined();
249         // deopt value
250         if (HasDeoptValue(virtualIndex)) {
251             value = deoptVregs_.at(static_cast<kungfu::OffsetType>(virtualIndex));
252         } else {
253             value = GetActualFrameArgs(i);
254         }
255         frameWriter->PushValue(value.GetRawData());
256         virtualIndex--;
257     }
258 
259     // [reserved args]
260     if (method->HaveThisWithCallField()) {
261         JSTaggedValue value = GetFrameArgv(kungfu::CommonArgIdx::THIS_OBJECT);
262         frameWriter->PushValue(value.GetRawData());
263         virtualIndex--;
264     }
265     if (method->HaveNewTargetWithCallField()) {
266         JSTaggedValue value = GetFrameArgv(kungfu::CommonArgIdx::NEW_TARGET);
267         frameWriter->PushValue(value.GetRawData());
268         virtualIndex--;
269     }
270     if (method->HaveFuncWithCallField()) {
271         JSTaggedValue value = GetFrameArgv(kungfu::CommonArgIdx::FUNC);
272         frameWriter->PushValue(value.GetRawData());
273         virtualIndex--;
274     }
275 
276     // [call field virtual regs]
277     for (int32_t i = virtualIndex; i >= 0; i--) {
278         JSTaggedValue value = GetDeoptValue(virtualIndex);
279         frameWriter->PushValue(value.GetRawData());
280         virtualIndex--;
281     }
282     return true;
283 }
284 
Dump(Method * method,kungfu::DeoptType type)285 void Deoptimizier::Dump(Method* method, kungfu::DeoptType type)
286 {
287     if (traceDeopt_) {
288         std::string checkType = DisplayItems(type);
289         LOG_COMPILER(INFO) << "Check Type: " << checkType;
290         std::string data = JsStackInfo::BuildMethodTrace(method, pc_);
291         LOG_COMPILER(INFO) << "Deoptimize" << data;
292         const uint8_t *pc = method->GetBytecodeArray() + pc_;
293         BytecodeInstruction inst(pc);
294         LOG_COMPILER(INFO) << inst;
295     }
296 }
297 
DisplayItems(kungfu::DeoptType type)298 std::string Deoptimizier::DisplayItems(kungfu::DeoptType type)
299 {
300     switch (type) {
301         case kungfu::DeoptType::NOTINT:
302             return "NOT INT";
303         case kungfu::DeoptType::NOTDOUBLE:
304             return "NOT DOUBLE";
305         case kungfu::DeoptType::NOTNUMBER:
306             return "NOT NUMBER";
307         case kungfu::DeoptType::NOTBOOL:
308             return "NOT BOOL";
309         case kungfu::DeoptType::NOTARRAY:
310             return "NOT ARRAY";
311         case kungfu::DeoptType::NOTSARRAY:
312             return "NOT SARRAY";
313         case kungfu::DeoptType::NOTF32ARRAY:
314             return "NOT F32ARRAY";
315         case kungfu::DeoptType::WRONGHCLASS:
316             return "WRONG HCLASS";
317         case kungfu::DeoptType::NOTNEWOBJ:
318             return "NOT NEWOBJ TYPE";
319         case kungfu::DeoptType::NOTARRAYIDX:
320             return "NOT ARRAY IDX";
321         case kungfu::DeoptType::NOTF32ARRAYIDX:
322             return "NOT F32 ARRAY IDX";
323         case kungfu::DeoptType::NOTINCOV:
324             return "NOT INC OVERFLOW";
325         case kungfu::DeoptType::NOTDECOV:
326             return "NOT DEC OVERFLOW";
327         case kungfu::DeoptType::NOTNEGOV:
328             return "NOT NEG OVERFLOW";
329         case kungfu::DeoptType::NOTCALLTGT:
330             return "NOT CALL TARGET";
331         default: {
332             return "NOT CHECK";
333         }
334     }
335 }
336 
ConstructAsmInterpretFrame(kungfu::DeoptType type)337 JSTaggedType Deoptimizier::ConstructAsmInterpretFrame(kungfu::DeoptType type)
338 {
339     JSTaggedValue callTarget = GetFrameArgv(kungfu::CommonArgIdx::FUNC);
340     auto method = GetMethod(callTarget);
341     Dump(method, type);
342     ASSERT(thread_ != nullptr);
343     uint8_t deoptThreshold = method->GetDeoptThreshold();
344     if (deoptThreshold > 0) {
345         method->SetDeoptType(type);
346         method->SetDeoptThreshold(--deoptThreshold);
347     } else {
348         method->SetAotCodeBit(false);
349         method->SetDeoptType(kungfu::DeoptType::NOTCHECK);
350         method->SetCodeEntryOrLiteral(reinterpret_cast<uintptr_t>(nullptr));
351     }
352 
353     FrameWriter frameWriter(this);
354     // Push asm interpreter frame
355     if (!CollectVirtualRegisters(method, &frameWriter)) {
356         return JSTaggedValue::Exception().GetRawData();
357     }
358     AsmInterpretedFrame *statePtr = frameWriter.ReserveAsmInterpretedFrame();
359     const uint8_t *resumePc = method->GetBytecodeArray() + pc_;
360 
361     JSTaggedValue thisObj = GetFrameArgv(kungfu::CommonArgIdx::THIS_OBJECT);
362     auto acc = GetDeoptValue(static_cast<int32_t>(SpecVregIndex::ACC_INDEX));
363     statePtr->function = callTarget;
364     statePtr->acc = acc;
365     statePtr->env = env_;
366     statePtr->callSize = 0;
367     statePtr->fp = 0;  // need update
368     statePtr->thisObj = thisObj;
369     statePtr->pc = resumePc;
370     // -uintptr_t skip lr
371     statePtr->base.prev = reinterpret_cast<JSTaggedType *>(
372         stackContext_.callFrameTop_ - sizeof(uintptr_t));
373     statePtr->base.type = FrameType::ASM_INTERPRETER_FRAME;
374 
375     // construct stack context
376     auto start = frameWriter.GetStart();
377     auto end = frameWriter.GetTop();
378     auto outputCount = start - end;
379 
380     RelocateCalleeSave();
381 
382     frameWriter.PushRawValue(stackContext_.callerFp_);
383     frameWriter.PushRawValue(stackContext_.returnAddr_);
384     frameWriter.PushRawValue(stackContext_.callFrameTop_);
385     frameWriter.PushRawValue(outputCount);
386     return reinterpret_cast<JSTaggedType>(frameWriter.GetTop());
387 }
388 }  // namespace panda::ecmascript