1 /**
2  * Copyright (c) 2021-2024 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 #ifndef PANDA_INTERPRETER_FRAME_H_
16 #define PANDA_INTERPRETER_FRAME_H_
17 
18 #include <cstddef>
19 #include <cstdint>
20 
21 #include "libpandabase/macros.h"
22 #include "libpandabase/utils/bit_helpers.h"
23 #include "libpandabase/utils/bit_utils.h"
24 #include "libpandabase/utils/logger.h"
25 #include "runtime/interpreter/acc_vregister.h"
26 #include "runtime/mem/frame_allocator-inl.h"
27 #include "libpandafile/bytecode_instruction-inl.h"
28 
29 namespace ark {
30 
31 // ========== Compatible Frame Layout ==========
32 // Now we have a variable vregisters list, it's compatible with static language(Java etc.) and dynamic language(JS etc.)
33 // Frame layout - Static Languages
34 // +---------------------------------+
35 // |          ark::Frame           |
36 // +---------------------------------+ <-------- payload
37 // |           vregs[0]: v_          |
38 // +---------------------------------+
39 // |              ...                |
40 // +---------------------------------+
41 // |         vregs[nregs-1] : v_     |
42 // +---------------------------------+ <-------- mirror
43 // |         vregs[0]: v_            |
44 // +---------------------------------+
45 // |               ...               |
46 // +---------------------------------+
47 // |         vregs[nregs-1] : v_     |
48 // +---------------------------------+
49 //
50 // Frame layout - Dynamic Languages
51 // +---------------------------------+
52 // |          ark::Frame           |
53 // +---------------------------------+ <-------- payload
54 // |           vregs[0]: v_          |
55 // +---------------------------------+
56 // |              ...                |
57 // +---------------------------------+
58 // |         vregs[nregs-1] : v_     |
59 // +---------------------------------+
60 //
61 // Vregister has no type info now, called tagless vregister. For dynamic Language, vregister's value is Tagged with type
62 // info, which does not need the mirror part to store extra tag. For static Language, we alloc a mirror part for every
63 // vregister to retain the tag info. And the mirror part offset will be `nregs_ * sizeof(Vregister)`.
64 // You can use the `FrameHandler` to access the vregisters list and GetVReg will return `VRegisterRef`
65 // AccVRegister is different from VRegister, it contains both value and tag like before.
66 
67 class Method;
68 class ObjectHeader;
69 template <class ExtData>
70 class ExtFrame;
71 
72 class Frame {
73 public:
74     // Instrumentation: indicate what the frame must be force poped
75     static constexpr size_t FORCE_POP = 1U;
76     // Instrumentation: indicate what the frame must retry last instruction
77     static constexpr size_t RETRY_INSTRUCTION = 2U;
78     // Instrumentation: indicate what the frame must notify when poped
79     static constexpr size_t NOTIFY_POP = 4U;
80     // Indicate that the frame was created after deoptimization.
81     // This flag is needed to avoid OSR for deoptimized frames. Because the OSR consumes stack that isn't released after
82     // deoptimization, stack overflow can be occurred. This constrain may be removed once asm interpreter is introduced.
83     static constexpr size_t IS_DEOPTIMIZED = 8U;
84     // Indicate whether this frame is stackless frame, only take effects under stackless interpreter mode.
85     static constexpr size_t IS_STACKLESS = 16U;
86     // Indicate whether this frame is initobj frame, only take effects under stackless interpreter mode.
87     static constexpr size_t IS_INITOBJ = 32U;
88 
89     // Indicate whether this frame is created in Method::Invoke
90     static constexpr size_t IS_INVOKE = 64U;
91 
92     // Indicate whether this frame is static or dynamic, which decides the frame layout
93     static constexpr size_t IS_DYNAMIC = 128U;
94 
Frame(void *ext, Method *method, Frame *prev, uint32_t nregs)95     ALWAYS_INLINE inline Frame(void *ext, Method *method, Frame *prev, uint32_t nregs)
96         : prev_(prev),
97           method_(method),
98           nregs_(nregs),
99           numActualArgs_(0),
100           bcOffset_(0),
101           flags_(0),
102           ext_(ext),
103           nextInst_(nullptr),
104           inst_(nullptr)
105     {
106     }
Frame(void *ext, Method *method, Frame *prev, uint32_t nregs, uint32_t numActualArgs)107     ALWAYS_INLINE inline Frame(void *ext, Method *method, Frame *prev, uint32_t nregs, uint32_t numActualArgs)
108         : prev_(prev),
109           method_(method),
110           nregs_(nregs),
111           numActualArgs_(numActualArgs),
112           bcOffset_(0),
113           flags_(0),
114           ext_(ext),
115           nextInst_(nullptr),
116           inst_(nullptr)
117     {
118     }
119 
ToExt(Frame *frame, size_t extSz)120     ALWAYS_INLINE static void *ToExt(Frame *frame, size_t extSz)
121     {
122         return reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(frame) - extSz);
123     }
124 
FromExt(void *ext, size_t extSz)125     ALWAYS_INLINE static Frame *FromExt(void *ext, size_t extSz)
126     {
127         return reinterpret_cast<Frame *>(reinterpret_cast<uintptr_t>(ext) + extSz);
128     }
129 
GetExt() const130     ALWAYS_INLINE inline void *GetExt() const
131     {
132         return ext_;
133     }
134 
GetVReg(size_t i) const135     ALWAYS_INLINE inline const interpreter::VRegister &GetVReg(size_t i) const
136     {
137         return vregs_[i];
138     }
139 
GetVReg(size_t i)140     ALWAYS_INLINE inline interpreter::VRegister &GetVReg(size_t i)
141     {
142         return vregs_[i];
143     }
144 
SetAcc(const interpreter::AccVRegister &acc)145     ALWAYS_INLINE inline void SetAcc(const interpreter::AccVRegister &acc)
146     {
147         acc_ = acc;
148     }
149 
GetAcc()150     ALWAYS_INLINE inline interpreter::AccVRegister &GetAcc()
151     {
152         return acc_;
153     }
154 
GetAcc() const155     ALWAYS_INLINE inline const interpreter::AccVRegister &GetAcc() const
156     {
157         return acc_;
158     }
159 
SetMethod(Method *method)160     ALWAYS_INLINE inline void SetMethod(Method *method)
161     {
162         method_ = method;
163     }
164 
GetMethod() const165     ALWAYS_INLINE inline Method *GetMethod() const
166     {
167         return method_;
168     }
169 
170     ALWAYS_INLINE const uint8_t *GetInstrOffset();
171 
SetPrevFrame(Frame *prev)172     ALWAYS_INLINE inline void SetPrevFrame(Frame *prev)
173     {
174         prev_ = prev;
175     }
176 
GetPrevFrame() const177     ALWAYS_INLINE inline Frame *GetPrevFrame() const
178     {
179         return prev_;
180     }
181 
GetSize() const182     ALWAYS_INLINE inline uint32_t GetSize() const
183     {
184         return nregs_;
185     }
186 
GetNumActualArgs() const187     ALWAYS_INLINE inline uint32_t GetNumActualArgs() const
188     {
189         return numActualArgs_;
190     }
191 
SetBytecodeOffset(uint32_t bcOffset)192     ALWAYS_INLINE inline void SetBytecodeOffset(uint32_t bcOffset)
193     {
194         bcOffset_ = bcOffset;
195     }
196 
GetBytecodeOffset() const197     ALWAYS_INLINE inline uint32_t GetBytecodeOffset() const
198     {
199         return bcOffset_;
200     }
201 
SetNextInstruction(BytecodeInstruction inst)202     ALWAYS_INLINE inline void SetNextInstruction(BytecodeInstruction inst)
203     {
204         nextInst_ = inst;
205     }
206 
GetNextInstruction() const207     ALWAYS_INLINE inline BytecodeInstruction GetNextInstruction() const
208     {
209         return nextInst_;
210     }
211 
SetInstruction(const uint8_t *inst)212     ALWAYS_INLINE inline void SetInstruction(const uint8_t *inst)
213     {
214         inst_ = inst;
215     }
216 
GetInstruction() const217     ALWAYS_INLINE inline const uint8_t *GetInstruction() const
218     {
219         return inst_;
220     }
221 
GetAllocSize(size_t size, uint32_t extSz)222     ALWAYS_INLINE static inline size_t GetAllocSize(size_t size, uint32_t extSz)
223     {
224         return AlignUp(sizeof(Frame) + sizeof(interpreter::VRegister) * size + extSz,
225                        GetAlignmentInBytes(DEFAULT_FRAME_ALIGNMENT));
226     }
227 
IsForcePop() const228     ALWAYS_INLINE inline bool IsForcePop() const
229     {
230         return (flags_ & FORCE_POP) != 0;
231     }
232 
ClearForcePop()233     ALWAYS_INLINE inline void ClearForcePop()
234     {
235         flags_ = flags_ & ~FORCE_POP;
236     }
237 
SetForcePop()238     ALWAYS_INLINE inline void SetForcePop()
239     {
240         flags_ = flags_ | FORCE_POP;
241     }
242 
IsRetryInstruction() const243     ALWAYS_INLINE inline bool IsRetryInstruction() const
244     {
245         return (flags_ & RETRY_INSTRUCTION) != 0;
246     }
247 
ClearRetryInstruction()248     ALWAYS_INLINE inline void ClearRetryInstruction()
249     {
250         flags_ = flags_ & ~RETRY_INSTRUCTION;
251     }
252 
SetRetryInstruction()253     ALWAYS_INLINE inline void SetRetryInstruction()
254     {
255         flags_ = flags_ | RETRY_INSTRUCTION;
256     }
257 
IsNotifyPop() const258     ALWAYS_INLINE inline bool IsNotifyPop() const
259     {
260         return (flags_ & NOTIFY_POP) != 0;
261     }
262 
ClearNotifyPop()263     ALWAYS_INLINE inline void ClearNotifyPop()
264     {
265         flags_ = flags_ & ~NOTIFY_POP;
266     }
267 
SetNotifyPop()268     ALWAYS_INLINE inline void SetNotifyPop()
269     {
270         flags_ = flags_ | NOTIFY_POP;
271     }
272 
IsDeoptimized() const273     ALWAYS_INLINE inline bool IsDeoptimized() const
274     {
275         return (flags_ & IS_DEOPTIMIZED) != 0;
276     }
277 
SetDeoptimized()278     ALWAYS_INLINE inline void SetDeoptimized()
279     {
280         flags_ |= IS_DEOPTIMIZED;
281     }
282 
DisableOsr()283     ALWAYS_INLINE inline void DisableOsr()
284     {
285         SetDeoptimized();
286     }
287 
IsStackless() const288     ALWAYS_INLINE inline bool IsStackless() const
289     {
290         return (flags_ & IS_STACKLESS) != 0;
291     }
292 
SetStackless()293     ALWAYS_INLINE inline void SetStackless()
294     {
295         flags_ = flags_ | IS_STACKLESS;
296     }
297 
IsInitobj() const298     ALWAYS_INLINE inline bool IsInitobj() const
299     {
300         return (flags_ & IS_INITOBJ) != 0;
301     }
302 
SetInitobj()303     ALWAYS_INLINE inline void SetInitobj()
304     {
305         flags_ = flags_ | IS_INITOBJ;
306     }
307 
SetInvoke()308     ALWAYS_INLINE inline void SetInvoke()
309     {
310         flags_ = flags_ | IS_INVOKE;
311     }
312 
IsInvoke() const313     ALWAYS_INLINE inline bool IsInvoke() const
314     {
315         return (flags_ & IS_INVOKE) != 0;
316     }
317 
IsDynamic() const318     ALWAYS_INLINE inline bool IsDynamic() const
319     {
320         return (flags_ & IS_DYNAMIC) != 0;
321     }
322 
SetDynamic()323     ALWAYS_INLINE inline void SetDynamic()
324     {
325         flags_ = flags_ | IS_DYNAMIC;
326     }
327 
328     template <bool IS_DYNAMIC = false>
GetAccAsVReg()329     ALWAYS_INLINE inline typename std::enable_if<IS_DYNAMIC, interpreter::DynamicVRegisterRef>::type GetAccAsVReg()
330     {
331         return GetAcc().template AsVRegRef<true>();
332     }
333 
334     template <bool IS_DYNAMIC = false>
GetAccAsVReg()335     ALWAYS_INLINE inline typename std::enable_if<!IS_DYNAMIC, interpreter::StaticVRegisterRef>::type GetAccAsVReg()
336     {
337         return GetAcc().template AsVRegRef<false>();
338     }
339 
340     template <bool IS_DYNAMIC = false>
GetActualSize(uint32_t nregs)341     ALWAYS_INLINE static inline uint32_t GetActualSize(uint32_t nregs)
342     {
343         // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon)
344         if constexpr (IS_DYNAMIC) {
345             return nregs;
346         }
347         return nregs * 2U;
348     }
349 
GetMethodOffset()350     ALWAYS_INLINE static inline constexpr uint32_t GetMethodOffset()
351     {
352         return MEMBER_OFFSET(Frame, method_);
353     }
354 
GetPrevFrameOffset()355     ALWAYS_INLINE static inline constexpr uint32_t GetPrevFrameOffset()
356     {
357         return MEMBER_OFFSET(Frame, prev_);
358     }
359 
GetNumVregsOffset()360     ALWAYS_INLINE static inline constexpr uint32_t GetNumVregsOffset()
361     {
362         return MEMBER_OFFSET(Frame, nregs_);
363     }
364 
GetVregsOffset()365     ALWAYS_INLINE static inline constexpr uint32_t GetVregsOffset()
366     {
367         return MEMBER_OFFSET(Frame, vregs_);
368     }
369 
GetAccOffset()370     ALWAYS_INLINE static inline constexpr uint32_t GetAccOffset()
371     {
372         return MEMBER_OFFSET(Frame, acc_);
373     }
374 
GetNumActualArgsOffset()375     ALWAYS_INLINE static inline constexpr uint32_t GetNumActualArgsOffset()
376     {
377         return MEMBER_OFFSET(Frame, numActualArgs_);
378     }
379 
GetFlagsOffset()380     ALWAYS_INLINE static inline constexpr uint32_t GetFlagsOffset()
381     {
382         return MEMBER_OFFSET(Frame, flags_);
383     }
384 
GetNextInstructionOffset()385     ALWAYS_INLINE static inline constexpr uint32_t GetNextInstructionOffset()
386     {
387         return MEMBER_OFFSET(Frame, nextInst_);
388     }
389 
GetInstructionsOffset()390     ALWAYS_INLINE static inline constexpr uint32_t GetInstructionsOffset()
391     {
392         return MEMBER_OFFSET(Frame, inst_);
393     }
394 
GetBytecodeOffsetOffset()395     ALWAYS_INLINE static constexpr uint32_t GetBytecodeOffsetOffset()
396     {
397         return MEMBER_OFFSET(Frame, bcOffset_);
398     }
399 
GetExtOffset()400     ALWAYS_INLINE static constexpr uint32_t GetExtOffset()
401     {
402         return MEMBER_OFFSET(Frame, ext_);
403     }
404 
405     ~Frame() = default;
406 
407     NO_COPY_SEMANTIC(Frame);
408     NO_MOVE_SEMANTIC(Frame);
409 
410 private:
411     Frame *prev_;
412     Method *method_;
413     uint32_t nregs_;
414     uint32_t numActualArgs_;
415     uint32_t bcOffset_;
416     size_t flags_;
417 
418     // ExtFrame<Ext> allocation ptr
419     void *ext_;
420 
421     interpreter::AccVRegister acc_;
422     BytecodeInstruction nextInst_;
423     const uint8_t *inst_;
424 
425     __extension__ interpreter::VRegister vregs_[0];  // NOLINT(modernize-avoid-c-arrays)
426 };
427 
428 class FrameHandler {
429 public:
FrameHandler(Frame *frame)430     ALWAYS_INLINE inline explicit FrameHandler(Frame *frame) : frame_(frame) {}
431 
SetAcc(const interpreter::AccVRegister &acc)432     ALWAYS_INLINE inline void SetAcc(const interpreter::AccVRegister &acc)
433     {
434         frame_->SetAcc(acc);
435     }
436 
GetAcc()437     ALWAYS_INLINE inline interpreter::AccVRegister &GetAcc()
438     {
439         return frame_->GetAcc();
440     }
441 
GetAcc() const442     ALWAYS_INLINE inline const interpreter::AccVRegister &GetAcc() const
443     {
444         return frame_->GetAcc();
445     }
446 
SetMethod(Method *method)447     ALWAYS_INLINE inline void SetMethod(Method *method)
448     {
449         frame_->SetMethod(method);
450     }
451 
GetMethod() const452     ALWAYS_INLINE inline Method *GetMethod() const
453     {
454         return frame_->GetMethod();
455     }
456 
GetInstrOffset() const457     ALWAYS_INLINE const uint8_t *GetInstrOffset() const
458     {
459         return frame_->GetInstrOffset();
460     }
461 
SetPrevFrame(Frame *prev)462     ALWAYS_INLINE inline void SetPrevFrame(Frame *prev)
463     {
464         frame_->SetPrevFrame(prev);
465     }
466 
GetPrevFrame() const467     ALWAYS_INLINE inline Frame *GetPrevFrame() const
468     {
469         return frame_->GetPrevFrame();
470     }
471 
GetSize() const472     ALWAYS_INLINE inline uint32_t GetSize() const
473     {
474         return frame_->GetSize();
475     }
476 
GetNumActualArgs() const477     ALWAYS_INLINE inline uint32_t GetNumActualArgs() const
478     {
479         return frame_->GetNumActualArgs();
480     }
481 
SetBytecodeOffset(uint32_t bcOffset)482     ALWAYS_INLINE inline void SetBytecodeOffset(uint32_t bcOffset)
483     {
484         frame_->SetBytecodeOffset(bcOffset);
485     }
486 
GetBytecodeOffset() const487     ALWAYS_INLINE inline uint32_t GetBytecodeOffset() const
488     {
489         return frame_->GetBytecodeOffset();
490     }
491 
SetNextInstruction(BytecodeInstruction inst)492     ALWAYS_INLINE inline void SetNextInstruction(BytecodeInstruction inst)
493     {
494         frame_->SetNextInstruction(inst);
495     }
496 
GetNextInstruction() const497     ALWAYS_INLINE inline BytecodeInstruction GetNextInstruction() const
498     {
499         return frame_->GetNextInstruction();
500     }
501 
SetInstruction(const uint8_t *inst)502     ALWAYS_INLINE inline void SetInstruction(const uint8_t *inst)
503     {
504         frame_->SetInstruction(inst);
505     }
506 
GetInstruction() const507     ALWAYS_INLINE inline const uint8_t *GetInstruction() const
508     {
509         return frame_->GetInstruction();
510     }
511 
IsForcePop() const512     ALWAYS_INLINE inline bool IsForcePop() const
513     {
514         return frame_->IsForcePop();
515     }
516 
ClearForcePop()517     ALWAYS_INLINE inline void ClearForcePop()
518     {
519         frame_->ClearForcePop();
520     }
521 
SetForcePop()522     ALWAYS_INLINE inline void SetForcePop()
523     {
524         frame_->SetForcePop();
525     }
526 
IsRetryInstruction() const527     ALWAYS_INLINE inline bool IsRetryInstruction() const
528     {
529         return frame_->IsRetryInstruction();
530     }
531 
ClearRetryInstruction()532     ALWAYS_INLINE inline void ClearRetryInstruction()
533     {
534         frame_->ClearRetryInstruction();
535     }
536 
SetRetryInstruction()537     ALWAYS_INLINE inline void SetRetryInstruction()
538     {
539         frame_->SetRetryInstruction();
540     }
541 
IsNotifyPop() const542     ALWAYS_INLINE inline bool IsNotifyPop() const
543     {
544         return frame_->IsNotifyPop();
545     }
546 
ClearNotifyPop()547     ALWAYS_INLINE inline void ClearNotifyPop()
548     {
549         frame_->ClearNotifyPop();
550     }
551 
SetNotifyPop()552     ALWAYS_INLINE inline void SetNotifyPop()
553     {
554         frame_->SetNotifyPop();
555     }
556 
IsDeoptimized() const557     ALWAYS_INLINE inline bool IsDeoptimized() const
558     {
559         return frame_->IsDeoptimized();
560     }
561 
SetDeoptimized()562     ALWAYS_INLINE inline void SetDeoptimized()
563     {
564         frame_->SetDeoptimized();
565     }
566 
DisableOsr()567     ALWAYS_INLINE inline void DisableOsr()
568     {
569         frame_->SetDeoptimized();
570     }
571 
IsStackless() const572     ALWAYS_INLINE inline bool IsStackless() const
573     {
574         return frame_->IsStackless();
575     }
576 
SetStackless()577     ALWAYS_INLINE inline void SetStackless()
578     {
579         frame_->SetStackless();
580     }
581 
IsInitobj() const582     ALWAYS_INLINE inline bool IsInitobj() const
583     {
584         return frame_->IsInitobj();
585     }
586 
SetInitobj()587     ALWAYS_INLINE inline void SetInitobj()
588     {
589         frame_->SetInitobj();
590     }
591 
IsInvoke() const592     ALWAYS_INLINE inline bool IsInvoke() const
593     {
594         return frame_->IsInvoke();
595     }
596 
SetInvoke()597     ALWAYS_INLINE inline void SetInvoke()
598     {
599         frame_->SetInvoke();
600     }
601 
IsDynamic() const602     ALWAYS_INLINE inline bool IsDynamic() const
603     {
604         return frame_->IsDynamic();
605     }
606 
SetDynamic()607     ALWAYS_INLINE inline void SetDynamic()
608     {
609         frame_->SetDynamic();
610     }
611 
operator *() const612     ALWAYS_INLINE inline Frame *operator*() const
613     {
614         return frame_;
615     }
616 
617     ~FrameHandler() = default;
618 
619     DEFAULT_COPY_SEMANTIC(FrameHandler);
620     DEFAULT_MOVE_SEMANTIC(FrameHandler);
621 
622 protected:
GetVRegisters()623     ALWAYS_INLINE inline interpreter::VRegister *GetVRegisters()
624     {
625         return reinterpret_cast<interpreter::VRegister *>(reinterpret_cast<uintptr_t>(frame_) +
626                                                           Frame::GetVregsOffset());
627     }
628 
629     Frame *frame_ {nullptr};  // NOLINT(misc-non-private-member-variables-in-classes)
630 };
631 
632 class StaticFrameHandler : public FrameHandler {
633 public:
StaticFrameHandler(Frame *frame)634     ALWAYS_INLINE inline explicit StaticFrameHandler(Frame *frame) : FrameHandler(frame) {}
635 
GetVReg(size_t i)636     ALWAYS_INLINE inline interpreter::StaticVRegisterRef GetVReg(size_t i)
637     {
638         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
639         return interpreter::StaticVRegisterRef(&GetVRegisters()[i], &GetMirrorVRegisters()[i]);
640     }
641 
GetAccAsVReg()642     ALWAYS_INLINE inline interpreter::StaticVRegisterRef GetAccAsVReg()
643     {
644         return GetAcc().template AsVRegRef<false>();
645     }
646 
647 private:
GetMirrorVRegisters()648     ALWAYS_INLINE inline interpreter::VRegister *GetMirrorVRegisters()
649     {
650         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
651         return &GetVRegisters()[frame_->GetSize()];
652     }
653 };
654 
655 class DynamicFrameHandler : public FrameHandler {
656 public:
DynamicFrameHandler(Frame *frame)657     ALWAYS_INLINE inline explicit DynamicFrameHandler(Frame *frame) : FrameHandler(frame) {}
658 
GetVReg(size_t i)659     ALWAYS_INLINE inline interpreter::DynamicVRegisterRef GetVReg(size_t i)
660     {
661         // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
662         return interpreter::DynamicVRegisterRef(&GetVRegisters()[i]);
663     }
664 
GetAccAsVReg()665     ALWAYS_INLINE inline interpreter::DynamicVRegisterRef GetAccAsVReg()
666     {
667         return GetAcc().template AsVRegRef<true>();
668     }
669 };
670 
671 template <bool IS_DYNAMIC = false>
GetFrameHandler(Frame *frame)672 ALWAYS_INLINE inline typename std::enable_if<IS_DYNAMIC, DynamicFrameHandler>::type GetFrameHandler(Frame *frame)
673 {
674     return DynamicFrameHandler(frame);
675 }
676 
677 template <bool IS_DYNAMIC = false>
GetFrameHandler(Frame *frame)678 ALWAYS_INLINE inline typename std::enable_if<!IS_DYNAMIC, StaticFrameHandler>::type GetFrameHandler(Frame *frame)
679 {
680     return StaticFrameHandler(frame);
681 }
682 
683 // ExtFrame<ExtData> is an extended ark::Frame. It's used to hold any
684 // language-specific data, which is required to be associated with ark::Frame.
685 //
686 //                      Allocated ExtFrame looks like that:
687 //
688 //   ExtFrame<ExtData>*  ---->  | ExtData.field1 |  <---- ExtFrame::ExtData*
689 //                              | ExtData.field2 |
690 //                              | ExtData.field3 |
691 //    inserted by compiler ---- |     padding    |
692 //                              |  Frame.field1  |  <---- ExtFame::Frame*
693 //                              |  Frame.field2  |
694 //               VRegs VLA --|--|     vreg[0]    |
695 //                           |  |     vreg[1]    |
696 //                           |  |      ....      |
697 //                           |--|  vreg[nregs-1] |
698 //
699 // Generic panda interpreter operates ark::Frames, while any language extension
700 // may access its LangSpecData via ark::Frame *base_frame
701 //     ExtFrame<LangSpecData>::FromFrame(base_frame)->GetExtData()
702 // if it's known that this frame was properly allocated.
703 template <class ExtData>
704 class ExtFrame {
705 public:
706     ExtFrame() = delete;
707     ~ExtFrame() = delete;
708     NO_COPY_SEMANTIC(ExtFrame);
709     NO_MOVE_SEMANTIC(ExtFrame);
710 
GetFrame()711     ALWAYS_INLINE inline Frame *GetFrame()
712     {
713         return &frame_;
714     }
715 
GetExtData()716     ALWAYS_INLINE inline ExtData *GetExtData()
717     {
718         return &extData_;
719     }
720 
GetExtSize()721     ALWAYS_INLINE static inline constexpr uint32_t GetExtSize()
722     {
723         return FRAME_OFFSET;
724     }
725 
GetFrameOffset()726     ALWAYS_INLINE static inline constexpr uint32_t GetFrameOffset()
727     {
728         return FRAME_OFFSET;
729     }
730 
GetExtDataOffset()731     ALWAYS_INLINE static inline constexpr uint32_t GetExtDataOffset()
732     {
733         return EXT_DATA_OFFSET;
734     }
735 
FromFrame(const Frame *base)736     ALWAYS_INLINE static inline ExtFrame<ExtData> *FromFrame(const Frame *base)
737     {
738         auto *res = reinterpret_cast<ExtFrame<ExtData> *>(reinterpret_cast<uintptr_t>(base) - FRAME_OFFSET);
739         ASSERT(res == base->GetExt());
740         return res;
741     }
742 
743 private:
744     static constexpr uint32_t EXT_DATA_OFFSET = MEMBER_OFFSET(ExtFrame<ExtData>, extData_);
745     static constexpr uint32_t FRAME_OFFSET = MEMBER_OFFSET(ExtFrame<ExtData>, frame_);
746 
747 #pragma GCC diagnostic push
748 #pragma GCC diagnostic ignored "-Wpedantic"
749     ExtData extData_;
750     __extension__ Frame frame_;
751 #pragma GCC diagnostic pop
752 };
753 
754 #pragma GCC diagnostic push
755 #pragma GCC diagnostic ignored "-Wpedantic"
756 // Zero-size template parameter for ExtFrame in case where no language
757 // extension required
758 union EmptyExtFrameData {
759     __extension__ int v[0];  // NOLINT(modernize-avoid-c-arrays)
760 };
761 constexpr uint32_t EMPTY_EXT_FRAME_DATA_SIZE = ExtFrame<EmptyExtFrameData>::GetExtSize();
762 constexpr uint32_t CORE_EXT_FRAME_DATA_SIZE = 0;
763 static_assert(EMPTY_EXT_FRAME_DATA_SIZE == 0, "Nonzero EMPTY_EXT_FRAME_DATA_SIZE");
764 #pragma GCC diagnostic pop
765 
766 template <class ExtT = EmptyExtFrameData>
CreateFrame(mem::StackFrameAllocator *stackFrameAllocator, uint32_t nregsSize, Method *method, Frame *prev, uint32_t nregs, uint32_t numActualArgs)767 ALWAYS_INLINE inline Frame *CreateFrame(mem::StackFrameAllocator *stackFrameAllocator, uint32_t nregsSize,
768                                         Method *method, Frame *prev, uint32_t nregs, uint32_t numActualArgs)
769 {
770     constexpr uint32_t EXT_SIZE = ExtFrame<ExtT>::GetExtSize();
771 
772     size_t extFrameSize = Frame::GetAllocSize(nregsSize, EXT_SIZE);
773     void *mem = stackFrameAllocator->Alloc(extFrameSize);
774     if (UNLIKELY(mem == nullptr)) {
775         return nullptr;
776     }
777     // CODECHECK-NOLINTNEXTLINE(CPP_RULE_ID_SMARTPOINTER_INSTEADOF_ORIGINPOINTER)
778     return new (Frame::FromExt(mem, EXT_SIZE)) Frame(mem, method, prev, nregs, numActualArgs);
779 }
780 
DestroyFrame(mem::StackFrameAllocator *stackFrameAllocator, Frame *frame)781 ALWAYS_INLINE inline void DestroyFrame(mem::StackFrameAllocator *stackFrameAllocator, Frame *frame)
782 {
783     ASSERT(frame->GetExt() != nullptr);
784     stackFrameAllocator->Free(frame->GetExt());
785 }
786 
787 }  // namespace ark
788 
789 #endif  // PANDA_INTERPRETER_FRAME_H_
790