• 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 
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