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