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 "ecmascript/deoptimizer/deoptimizer.h"
16
17 #include <cmath>
18
19 #include "ecmascript/compiler/assembler/assembler.h"
20 #include "ecmascript/compiler/gate_meta_data.h"
21 #include "ecmascript/dfx/stackinfo/js_stackinfo.h"
22 #include "ecmascript/frames.h"
23 #include "ecmascript/interpreter/interpreter.h"
24 #include "ecmascript/js_thread.h"
25 #include "ecmascript/stubs/runtime_stubs-inl.h"
26
27 namespace panda::ecmascript {
28 class FrameWriter {
29 public:
FrameWriter(Deoptimizier * deoptimizier)30 explicit FrameWriter(Deoptimizier *deoptimizier) : thread_(deoptimizier->GetThread())
31 {
32 JSTaggedType *prevSp = const_cast<JSTaggedType *>(thread_->GetCurrentSPFrame());
33 start_ = top_ = EcmaInterpreter::GetInterpreterFrameEnd(thread_, prevSp);
34 }
35
PushValue(JSTaggedType value)36 void PushValue(JSTaggedType value)
37 {
38 *(--top_) = value;
39 }
40
PushRawValue(uintptr_t value)41 void PushRawValue(uintptr_t value)
42 {
43 *(--top_) = value;
44 }
45
Reserve(size_t size)46 bool Reserve(size_t size)
47 {
48 return !thread_->DoStackOverflowCheck(top_ - size);
49 }
50
ReserveAsmInterpretedFrame()51 AsmInterpretedFrame *ReserveAsmInterpretedFrame()
52 {
53 auto frame = AsmInterpretedFrame::GetFrameFromSp(top_);
54 top_ = reinterpret_cast<JSTaggedType *>(frame);
55 return frame;
56 }
57
GetStart() const58 JSTaggedType *GetStart() const
59 {
60 return start_;
61 }
62
GetTop() const63 JSTaggedType *GetTop() const
64 {
65 return top_;
66 }
67
GetFirstFrame() const68 JSTaggedType *GetFirstFrame() const
69 {
70 return firstFrame_;
71 }
72
RecordFirstFrame()73 void RecordFirstFrame()
74 {
75 firstFrame_ = top_;
76 }
77
ReviseValueByIndex(JSTaggedType value,size_t index)78 void ReviseValueByIndex(JSTaggedType value, size_t index)
79 {
80 ASSERT(index < static_cast<size_t>(start_ - top_));
81 *(top_ + index) = value;
82 }
83
84 private:
85 JSThread *thread_ {nullptr};
86 JSTaggedType *start_ {nullptr};
87 JSTaggedType *top_ {nullptr};
88 JSTaggedType *firstFrame_ {nullptr};
89 };
90
CollectVregs(const std::vector<kungfu::ARKDeopt> & deoptBundle,size_t shift)91 void Deoptimizier::CollectVregs(const std::vector<kungfu::ARKDeopt>& deoptBundle, size_t shift)
92 {
93 deoptVregs_.clear();
94 for (size_t i = 0; i < deoptBundle.size(); i++) {
95 ARKDeopt deopt = deoptBundle.at(i);
96 JSTaggedType v;
97 VRegId id = deopt.id;
98 if (std::holds_alternative<DwarfRegAndOffsetType>(deopt.value)) {
99 ASSERT(deopt.kind == LocationTy::Kind::INDIRECT);
100 auto value = std::get<DwarfRegAndOffsetType>(deopt.value);
101 DwarfRegType dwarfReg = value.first;
102 OffsetType offset = value.second;
103 ASSERT (dwarfReg == GCStackMapRegisters::FP || dwarfReg == GCStackMapRegisters::SP);
104 uintptr_t addr;
105 if (dwarfReg == GCStackMapRegisters::SP) {
106 addr = context_.callsiteSp + offset;
107 } else {
108 addr = context_.callsiteFp + offset;
109 }
110 v = *(reinterpret_cast<JSTaggedType *>(addr));
111 } else if (std::holds_alternative<LargeInt>(deopt.value)) {
112 ASSERT(deopt.kind == LocationTy::Kind::CONSTANTNDEX);
113 v = JSTaggedType(static_cast<int64_t>(std::get<LargeInt>(deopt.value)));
114 } else {
115 ASSERT(std::holds_alternative<IntType>(deopt.value));
116 ASSERT(deopt.kind == LocationTy::Kind::CONSTANT);
117 v = JSTaggedType(static_cast<int64_t>(std::get<IntType>(deopt.value)));
118 }
119 size_t curDepth = DecodeDeoptDepth(id, shift);
120 OffsetType vregId = static_cast<OffsetType>(DecodeVregIndex(id, shift));
121 if (vregId != static_cast<OffsetType>(SpecVregIndex::PC_OFFSET_INDEX)) {
122 deoptVregs_.insert({{curDepth, vregId}, JSHandle<JSTaggedValue>(thread_, JSTaggedValue(v))});
123 } else {
124 pc_.insert({curDepth, static_cast<size_t>(v)});
125 }
126 }
127 }
128
129 // when AOT trigger deopt, frame layout as the following
130 // * OptimizedJSFunctionFrame layout description as the following:
131 // +--------------------------+ ---------------
132 // | ...... | ^
133 // | ...... | callerFunction
134 // | ...... | |
135 // |--------------------------| |
136 // | args | v
137 // +--------------------------+ ---------------
138 // | returnAddr | ^
139 // |--------------------------| |
140 // | callsiteFp | |
141 // |--------------------------| OptimizedJSFunction FrameType:OPTIMIZED_JS_FUNCTION_FRAME
142 // | frameType | |
143 // |--------------------------| |
144 // | call-target | |
145 // |--------------------------| |
146 // | lexEnv | |
147 // |--------------------------| |
148 // | ........... | v
149 // +--------------------------+ ---------------
150 // | returnAddr | ^
151 // |--------------------------| |
152 // | callsiteFp | |
153 // |--------------------------| __llvm_deoptimize FrameType:OPTIMIZED_FRAME
154 // | frameType | |
155 // |--------------------------| |
156 // | No CalleeSave | |
157 // | Registers | v
158 // +--------------------------+ ---------------
159 // | returnAddr | ^
160 // |--------------------------| |
161 // | callsiteFp | |
162 // |--------------------------| DeoptHandlerAsm FrameType:ASM_BRIDGE_FRAME
163 // | frameType | |
164 // |--------------------------| |
165 // | glue | |
166 // |--------------------------| |
167 // | CalleeSave Registers | v
168 // +--------------------------+ ---------------
169 // | ......... | ^
170 // | ......... | CallRuntime FrameType:LEAVE_FRAME
171 // | ......... | |
172 // | ......... | v
173 // |--------------------------| ---------------
174
175 // After gathering the necessary information(After Call Runtime), frame layout after constructing
176 // asminterpreterframe is shown as the following:
177 // +----------------------------------+---------+
178 // | ...... | ^
179 // | ...... | callerFunction
180 // | ...... | |
181 // |----------------------------------| |
182 // | args | v
183 // +----------------------------------+---------+
184 // | returnAddr | ^
185 // |----------------------------------| |
186 // | frameType | |
187 // |----------------------------------| ASM_INTERPRETER_BRIDGE_FRAME
188 // | callsiteFp | |
189 // |----------------------------------| |
190 // | ........... | v
191 // +----------------------------------+---------+
192 // | returnAddr |
193 // |----------------------------------|
194 // | argv[n-1] |
195 // |----------------------------------|
196 // | ...... |
197 // |----------------------------------|
198 // | thisArg [maybe not exist] |
199 // |----------------------------------|
200 // | newTarget [maybe not exist] |
201 // |----------------------------------|
202 // | ...... |
203 // |----------------------------------|
204 // | Vregs [not exist in native] |
205 // +----------------------------------+--------+
206 // | . . . . | ^
207 // | InterpretedFrameBase | |
208 // | . . . . | |
209 // |----------------------------------| |
210 // | pc(bytecode addr) | |
211 // |----------------------------------| |
212 // | sp(current stack pointer) | |
213 // |----------------------------------| AsmInterpretedFrame 0
214 // | callSize | |
215 // |----------------------------------| |
216 // | env | |
217 // |----------------------------------| |
218 // | acc | |
219 // |----------------------------------| |
220 // | thisObj | |
221 // |----------------------------------| |
222 // | call-target | v
223 // +----------------------------------+--------+
224 // | argv[n-1] |
225 // |----------------------------------|
226 // | ...... |
227 // |----------------------------------|
228 // | thisArg [maybe not exist] |
229 // |----------------------------------|
230 // | newTarget [maybe not exist] |
231 // |----------------------------------|
232 // | ...... |
233 // |----------------------------------|
234 // | Vregs [not exist in native] |
235 // +----------------------------------+--------+
236 // | . . . . | ^
237 // | InterpretedFrameBase | |
238 // | . . . . | |
239 // |----------------------------------| |
240 // | pc(bytecode addr) | |
241 // |----------------------------------| |
242 // | sp(current stack pointer) | |
243 // |----------------------------------| AsmInterpretedFrame 1
244 // | callSize | |
245 // |----------------------------------| |
246 // | env | |
247 // |----------------------------------| |
248 // | acc | |
249 // |----------------------------------| |
250 // | thisObj | |
251 // |----------------------------------| |
252 // | call-target | v
253 // +----------------------------------+--------+
254 // | . . . . | ^
255 // | . . . . | AsmInterpretedFrame n
256 // | . . . . | v
257 // +----------------------------------+--------+
258
CollectDeoptBundleVec(std::vector<ARKDeopt> & deoptBundle)259 void Deoptimizier::CollectDeoptBundleVec(std::vector<ARKDeopt>& deoptBundle)
260 {
261 JSTaggedType *lastLeave = const_cast<JSTaggedType *>(thread_->GetLastLeaveFrame());
262 FrameIterator it(lastLeave, thread_);
263 // note: last deopt bridge frame is generated by DeoptHandlerAsm, callee Regs is grow from this frame
264 for (; !it.Done() && deoptBundle.empty(); it.Advance<GCVisitedFlag::VISITED>()) {
265 FrameType type = it.GetFrameType();
266 switch (type) {
267 case FrameType::OPTIMIZED_JS_FAST_CALL_FUNCTION_FRAME:
268 case FrameType::OPTIMIZED_JS_FUNCTION_FRAME: {
269 auto frame = it.GetFrame<OptimizedJSFunctionFrame>();
270 frame->GetDeoptBundleInfo(it, deoptBundle);
271 CalleeRegAndOffsetVec calleeRegInfo;
272 frame->GetFuncCalleeRegAndOffset(it, calleeRegInfo);
273 context_.calleeRegAndOffset = calleeRegInfo;
274 context_.callsiteSp = it.GetCallSiteSp();
275 context_.callsiteFp = reinterpret_cast<uintptr_t>(it.GetSp());
276 auto preFrameSp = frame->ComputePrevFrameSp(it);
277 frameArgc_ = frame->GetArgc(preFrameSp);
278 frameArgvs_ = frame->GetArgv(preFrameSp);
279 stackContext_.callFrameTop_ = it.GetPrevFrameCallSiteSp();
280 stackContext_.returnAddr_ = frame->GetReturnAddr();
281 stackContext_.callerFp_ = reinterpret_cast<uintptr_t>(frame->GetPrevFrameFp());
282 break;
283 }
284 case FrameType::ASM_BRIDGE_FRAME: {
285 auto sp = reinterpret_cast<uintptr_t*>(it.GetSp());
286 static constexpr size_t TYPE_GLUE_SLOT = 2; // 2: skip type & glue
287 sp -= TYPE_GLUE_SLOT;
288 calleeRegAddr_ = sp - numCalleeRegs_;
289 break;
290 }
291 case FrameType::OPTIMIZED_FRAME:
292 case FrameType::LEAVE_FRAME:
293 break;
294 default: {
295 LOG_FULL(FATAL) << "frame type error!";
296 UNREACHABLE();
297 }
298 }
299 }
300 ASSERT(!it.Done());
301 }
302
GetMethod(JSTaggedValue & target)303 Method* Deoptimizier::GetMethod(JSTaggedValue &target)
304 {
305 ECMAObject *callTarget = reinterpret_cast<ECMAObject*>(target.GetTaggedObject());
306 ASSERT(callTarget != nullptr);
307 Method *method = callTarget->GetCallTarget();
308 return method;
309 }
310
RelocateCalleeSave()311 void Deoptimizier::RelocateCalleeSave()
312 {
313 CalleeReg callreg;
314 for (auto &it: context_.calleeRegAndOffset) {
315 auto reg = it.first;
316 auto offset = it.second;
317 uintptr_t value = *(reinterpret_cast<uintptr_t *>(context_.callsiteFp + offset));
318 int order = callreg.FindCallRegOrder(reg);
319 calleeRegAddr_[order] = value;
320 }
321 }
322
CollectVirtualRegisters(Method * method,FrameWriter * frameWriter,size_t curDepth)323 bool Deoptimizier::CollectVirtualRegisters(Method* method, FrameWriter *frameWriter, size_t curDepth)
324 {
325 int32_t actualNumArgs = 0;
326 int32_t declaredNumArgs = 0;
327 if (curDepth == 0) {
328 actualNumArgs = static_cast<int32_t>(GetDeoptValue(curDepth,
329 static_cast<int32_t>(SpecVregIndex::ACTUAL_ARGC_INDEX)).GetInt());
330 declaredNumArgs = static_cast<int32_t>(method->GetNumArgsWithCallField());
331 } else {
332 // inline method actualNumArgs equal to declaredNumArgs
333 actualNumArgs = static_cast<int32_t>(method->GetNumArgsWithCallField());
334 declaredNumArgs = static_cast<int32_t>(method->GetNumArgsWithCallField());
335 }
336
337 int32_t callFieldNumVregs = static_cast<int32_t>(method->GetNumVregsWithCallField());
338
339 // layout of frame:
340 // [maybe argc] [actual args] [reserved args] [call field virtual regs]
341
342 // [maybe argc]
343 if (!method->IsFastCall() && declaredNumArgs != actualNumArgs) {
344 auto value = JSTaggedValue(actualNumArgs);
345 frameWriter->PushValue(value.GetRawData());
346 }
347 int32_t virtualIndex = declaredNumArgs + callFieldNumVregs +
348 static_cast<int32_t>(method->GetNumRevervedArgs()) - 1;
349 if (!frameWriter->Reserve(static_cast<size_t>(virtualIndex))) {
350 return false;
351 }
352 for (int32_t i = static_cast<int32_t>(declaredNumArgs - 1); i >= 0; i--) {
353 JSTaggedValue value = JSTaggedValue::Undefined();
354 // deopt value
355 if (HasDeoptValue(curDepth, virtualIndex)) {
356 value = GetDeoptValue(curDepth, virtualIndex);
357 }
358 frameWriter->PushValue(value.GetRawData());
359 virtualIndex--;
360 }
361
362 // [reserved args]
363 if (method->HaveThisWithCallField()) {
364 JSTaggedValue value = deoptVregs_.at(
365 {curDepth, static_cast<OffsetType>(SpecVregIndex::THIS_OBJECT_INDEX)}).GetTaggedValue();
366 frameWriter->PushValue(value.GetRawData());
367 virtualIndex--;
368 }
369 if (method->HaveNewTargetWithCallField()) {
370 JSTaggedValue value = deoptVregs_.at(
371 {curDepth, static_cast<OffsetType>(SpecVregIndex::NEWTARGET_INDEX)}).GetTaggedValue();
372 frameWriter->PushValue(value.GetRawData());
373 virtualIndex--;
374 }
375 if (method->HaveFuncWithCallField()) {
376 JSTaggedValue value = deoptVregs_.at(
377 {curDepth, static_cast<OffsetType>(SpecVregIndex::FUNC_INDEX)}).GetTaggedValue();
378 frameWriter->PushValue(value.GetRawData());
379 virtualIndex--;
380 }
381
382 // [call field virtual regs]
383 for (int32_t i = virtualIndex; i >= 0; i--) {
384 JSTaggedValue value = GetDeoptValue(curDepth, virtualIndex);
385 frameWriter->PushValue(value.GetRawData());
386 virtualIndex--;
387 }
388 // revise correct a0 - aN virtual regs , for example: ldobjbyname key; sta a2; update value to a2
389 // +--------------------------+ ^
390 // | aN | |
391 // +--------------------------+ |
392 // | ... | |
393 // +--------------------------+ |
394 // | a2(this) | |
395 // +--------------------------+ revise correct vreg
396 // | a1(newtarget) | |
397 // +--------------------------+ |
398 // | a0(func) | |
399 // |--------------------------| v
400 // | v0 - vN |
401 // sp --> |--------------------------|
402 int32_t vregsAndArgsNum = declaredNumArgs + callFieldNumVregs +
403 static_cast<int32_t>(method->GetNumRevervedArgs());
404 for (int32_t i = callFieldNumVregs; i < vregsAndArgsNum; i++) {
405 JSTaggedValue value = JSTaggedValue::Undefined();
406 if (HasDeoptValue(curDepth, i)) {
407 value = GetDeoptValue(curDepth, i);
408 frameWriter->ReviseValueByIndex(value.GetRawData(), i);
409 }
410 }
411 return true;
412 }
413
Dump(Method * method,kungfu::DeoptType type,size_t depth)414 void Deoptimizier::Dump(Method* method, kungfu::DeoptType type, size_t depth)
415 {
416 if (traceDeopt_) {
417 std::string checkType = DisplayItems(type);
418 LOG_COMPILER(INFO) << "Check Type: " << checkType;
419 std::string data = JsStackInfo::BuildJsStackTrace(thread_, true);
420 LOG_COMPILER(INFO) << "Deoptimize" << data;
421 const uint8_t *pc = method->GetBytecodeArray() + pc_.at(depth);
422 BytecodeInstruction inst(pc);
423 LOG_COMPILER(INFO) << inst;
424 }
425 }
426
DisplayItems(DeoptType type)427 std::string Deoptimizier::DisplayItems(DeoptType type)
428 {
429 const std::map<DeoptType, const char *> strMap = {
430 #define DEOPT_NAME_MAP(NAME, TYPE) {DeoptType::TYPE, #NAME},
431 GATE_META_DATA_DEOPT_REASON(DEOPT_NAME_MAP)
432 #undef DEOPT_NAME_MAP
433 };
434 if (strMap.count(type) > 0) {
435 return strMap.at(type);
436 }
437 return "DeoptType-" + std::to_string(static_cast<uint8_t>(type));
438 }
439
440 // layout of frameWriter
441 // |--------------------------| --------------> start(n)
442 // | args |
443 // | this |
444 // | newTarget |
445 // | callTarget |
446 // | vregs |
447 // |---------------------------
448 // | ASM Interpreter |
449 // +--------------------------+ --------------> end(n)
450 // | outputcounts | outputcounts = end(n) - start(n)
451 // |--------------------------| --------------> start(n-1)
452 // | args |
453 // | this |
454 // | newTarget |
455 // | callTarget |
456 // | vregs |
457 // |-------------------------------------------
458 // | ASM Interpreter |
459 // +--------------------------+ --------------> end(n-1)
460 // | outputcounts | outputcounts = end(n-1) - start(n-1)
461 // |--------------------------| --------------> start(n-1)
462 // | ...... |
463 // +--------------------------+ ---------------
464 // | callerFp_ | ^
465 // | returnAddr_ | stackContext
466 // | callFrameTop_ | |
467 // | inlineDepth | v
468 // |--------------------------| ---------------
469
ConstructAsmInterpretFrame()470 JSTaggedType Deoptimizier::ConstructAsmInterpretFrame()
471 {
472 FrameWriter frameWriter(this);
473 // Push asm interpreter frame
474 for (int32_t curDepth = static_cast<int32_t>(inlineDepth_); curDepth >= 0; curDepth--) {
475 auto start = frameWriter.GetTop();
476 JSTaggedValue callTarget = GetDeoptValue(curDepth, static_cast<int32_t>(SpecVregIndex::FUNC_INDEX));
477 auto method = GetMethod(callTarget);
478 if (!CollectVirtualRegisters(method, &frameWriter, curDepth)) {
479 return JSTaggedValue::Exception().GetRawData();
480 }
481 AsmInterpretedFrame *statePtr = frameWriter.ReserveAsmInterpretedFrame();
482 const uint8_t *resumePc = method->GetBytecodeArray() + pc_.at(curDepth);
483 JSTaggedValue thisObj = GetDeoptValue(curDepth, static_cast<int32_t>(SpecVregIndex::THIS_OBJECT_INDEX));
484 auto acc = GetDeoptValue(curDepth, static_cast<int32_t>(SpecVregIndex::ACC_INDEX));
485 statePtr->function = callTarget;
486 statePtr->acc = acc;
487 statePtr->env = GetDeoptValue(curDepth, static_cast<int32_t>(SpecVregIndex::ENV_INDEX));
488 statePtr->callSize = GetCallSize(curDepth, resumePc);
489 statePtr->fp = 0; // need update
490 statePtr->thisObj = thisObj;
491 statePtr->pc = resumePc;
492 // -uintptr_t skip lr
493 if (curDepth == 0) {
494 statePtr->base.prev = reinterpret_cast<JSTaggedType *>(stackContext_.callFrameTop_ - sizeof(uintptr_t));
495 } else {
496 statePtr->base.prev = 0; // need update
497 }
498
499 statePtr->base.type = FrameType::ASM_INTERPRETER_FRAME;
500
501 // construct stack context
502 auto end = frameWriter.GetTop();
503 auto outputCount = start - end;
504 frameWriter.PushRawValue(outputCount);
505 }
506
507 RelocateCalleeSave();
508
509 frameWriter.PushRawValue(stackContext_.callerFp_);
510 frameWriter.PushRawValue(stackContext_.returnAddr_);
511 frameWriter.PushRawValue(stackContext_.callFrameTop_);
512 frameWriter.PushRawValue(inlineDepth_);
513 return reinterpret_cast<JSTaggedType>(frameWriter.GetTop());
514 }
515
UpdateAndDumpDeoptInfo(kungfu::DeoptType type)516 void Deoptimizier::UpdateAndDumpDeoptInfo(kungfu::DeoptType type)
517 {
518 // depth records the number of layers of nested calls when deopt occurs
519 for (size_t i = 0; i <= inlineDepth_; i++) {
520 JSTaggedValue CallTarget = GetDeoptValue(i, static_cast<int32_t>(SpecVregIndex::FUNC_INDEX));
521 auto method = GetMethod(CallTarget);
522 if (i == inlineDepth_) {
523 Dump(method, type, i);
524 }
525 ASSERT(thread_ != nullptr);
526 uint8_t deoptThreshold = method->GetDeoptThreshold();
527 if (deoptThreshold > 0) {
528 method->SetDeoptType(type);
529 method->SetDeoptThreshold(--deoptThreshold);
530 } else {
531 FunctionKind kind = method->GetFunctionKind();
532 ObjectFactory *factory = thread_->GetEcmaVM()->GetFactory();
533 JSHandle<ECMAObject> jsFunc(thread_, CallTarget);
534 JSHandle<JSHClass> oldHclass(thread_, jsFunc->GetClass());
535 // instead of hclass by non_optimized hclass when method ClearAOTFlags
536 JSHandle<JSHClass> newHClass = factory->GetNonOptimizedHclass(oldHclass, kind);
537 jsFunc->SynchronizedSetClass(*newHClass);
538 }
539 }
540 }
541
542 // call instructions need compute jumpSize
GetCallSize(size_t curDepth,const uint8_t * resumePc)543 size_t Deoptimizier::GetCallSize(size_t curDepth, const uint8_t *resumePc)
544 {
545 if (inlineDepth_ > 0 && curDepth != inlineDepth_) {
546 auto op = BytecodeInstruction(resumePc).GetOpcode();
547 size_t jumpSize = BytecodeInstruction::Size(op);
548 return jumpSize;
549 }
550 return 0;
551 }
552
EncodeDeoptVregIndex(int32_t index,size_t depth,size_t shift)553 int32_t Deoptimizier::EncodeDeoptVregIndex(int32_t index, size_t depth, size_t shift)
554 {
555 if (index >= 0) {
556 return (index << shift) | depth;
557 }
558 return -((-index << shift) | depth);
559 }
560
ComputeShift(size_t depth)561 size_t Deoptimizier::ComputeShift(size_t depth)
562 {
563 size_t shift = 0;
564 if (depth != 0) {
565 shift = std::floor(std::log2(depth)) + 1;
566 }
567 return shift;
568 }
569
DecodeVregIndex(OffsetType id,size_t shift)570 int32_t Deoptimizier::DecodeVregIndex(OffsetType id, size_t shift)
571 {
572 if (id >= 0) {
573 return id >> shift;
574 }
575 return -((-id) >> shift);
576 }
577
DecodeDeoptDepth(OffsetType id,size_t shift)578 size_t Deoptimizier::DecodeDeoptDepth(OffsetType id, size_t shift)
579 {
580 size_t mask = (1 << shift) - 1;
581 if (id >= 0) {
582 return id & mask;
583 }
584 return (-id) & mask;
585 }
586 } // namespace panda::ecmascript
587