native_frame.h (6192B)
1 /* see also: cg/native_argmove.h (shared parallel-copy register shuffle) */ 2 #ifndef KIT_CG_NATIVE_FRAME_H 3 #define KIT_CG_NATIVE_FRAME_H 4 5 /* Shared native-frame bookkeeping for the aa64/rv64/x64 NativeTarget backends. 6 * 7 * Every native backend lays out a stack frame the same way at the bookkeeping 8 * level: a table of frame slots (locals, spills, sret/variadic homes, and — on 9 * aa64 — callee-save homes) accumulated below the frame anchor, a running 10 * max-outgoing-arg size, and a set of callee-saved registers the allocator 11 * touched. The *arithmetic* of assigning each slot a cumulative offset, the 12 * frame-final gate that forbids growing the frame after the prologue is 13 * emitted, and the derivation of the used-callee-save set from the optimizer's 14 * per-class masks are identical across all three. They live here. 15 * 16 * What stays in each backend is everything ISA/ABI-specific: the coordinate 17 * transform from a slot's cumulative `off` to an anchor-relative displacement 18 * (fp/s0/rbp-relative, and aa64's top- vs bottom-record choice), the 19 * prologue/epilogue instruction encoding, callee-save placement (aa64 reserves 20 * slots here; rv64/x64 compute offsets below the locals), the slim-prologue 21 * variants, deferred-patch application, and the variadic register-save stores. 22 * 23 * The frame-relevant ABI facts are consulted through this module too: 24 * native_frame_va_save_bytes derives the vararg register-save-area size from 25 * the target ABI's va_list layout, so the per-arch magic numbers (rv64 64, x64 26 * 176, aa64 64+128) all come from one ABI-driven query. */ 27 28 #include "abi/abi.h" 29 #include "arch/native_target.h" 30 #include "core/core.h" 31 32 /* One allocated frame slot. `off` is the cumulative byte offset below the frame 33 * anchor (positive); the backend converts it to an anchor-relative 34 * displacement. Layout matches the per-arch slot structs it replaces. */ 35 typedef struct NativeFrameSlotEntry { 36 u32 off; 37 u32 size; 38 u32 align; 39 u8 kind; /* NativeFrameSlotKind */ 40 u8 pad[3]; 41 } NativeFrameSlotEntry; 42 43 /* A callee-saved register the function body used and must preserve. `slot` is 44 * the reserved save slot when the backend asked native_frame to allocate one 45 * (aa64), else NATIVE_FRAME_SLOT_NONE (rv64/x64 compute the save offset). */ 46 typedef struct NativeFrameCalleeSave { 47 NativeFrameSlot slot; 48 KitCgTypeId type; 49 u8 cls; /* NativeAllocClass */ 50 Reg reg; 51 } NativeFrameCalleeSave; 52 53 /* Per-class save-slot shape, used only when native_frame_set_callee_saves is 54 * asked to allocate save slots (alloc_slots != 0). Indexed by NativeAllocClass. 55 */ 56 typedef struct NativeFrameSaveSpec { 57 u32 size; 58 u32 align; 59 KitCgTypeId type; 60 } NativeFrameSaveSpec; 61 62 /* x19..x28 (10) + v8..v15 (8) on aa64; s0..s11 + fs0..fs11 on rv64; the Win64 63 * GPR + XMM callee-saved set on x64. 48 covers every target with headroom. */ 64 #define NATIVE_FRAME_MAX_CALLEE_SAVES 48u 65 66 typedef struct NativeFrame { 67 Compiler* c; 68 69 NativeFrameSlotEntry* slots; /* arena-grown; 1-indexed handles */ 70 u32 nslots; 71 u32 slots_cap; 72 u32 cum_off; /* running below-anchor slot bytes (sum of reservations) */ 73 74 u32 max_outgoing; /* max outgoing-arg bytes across all calls */ 75 76 NativeFrameCalleeSave callee_saves[NATIVE_FRAME_MAX_CALLEE_SAVES]; 77 u32 ncallee_saves; 78 79 u8 frame_final; /* set once the prologue is emitted; bars further slots */ 80 u8 known_frame; /* optimizer (known-frame) path vs single-pass path */ 81 u8 has_alloca; /* body contains a dynamic alloca */ 82 u8 pad; 83 } NativeFrame; 84 85 /* One-time setup (call from <arch>_native_target_new). */ 86 void native_frame_init(NativeFrame* f, Compiler* c); 87 88 /* Per-function reset: clears the slot table, outgoing size, callee-save set and 89 * the frame flags. Keeps the slots buffer for reuse across functions. */ 90 void native_frame_reset(NativeFrame* f); 91 92 /* Allocate a frame slot, advancing the cumulative offset by `size` aligned to 93 * `align` (defaults: size 8, align 1). Returns a 1-indexed handle. Panics if 94 * the frame is already final. Identical arithmetic to the per-arch allocators. 95 */ 96 NativeFrameSlot native_frame_slot_alloc(NativeFrame* f, 97 const NativeFrameSlotDesc* d); 98 99 /* Resolve a 1-indexed handle to its entry. Panics on an out-of-range handle. */ 100 NativeFrameSlotEntry* native_frame_slot_at(NativeFrame* f, 101 NativeFrameSlot slot); 102 103 /* Grow max_outgoing to at least `bytes`. */ 104 void native_frame_note_outgoing(NativeFrame* f, u32 bytes); 105 106 /* Mark the frame final (no further native_frame_slot_alloc allowed). */ 107 void native_frame_set_final(NativeFrame* f); 108 109 static inline u32 native_frame_slot_bytes(const NativeFrame* f) { 110 return f->cum_off; 111 } 112 113 /* Derive the used-callee-save set from the optimizer's per-class masks (one 114 * bitmask of hard registers per NativeAllocClass, already restricted to the 115 * callee-saved set by the caller). Appends one NativeFrameCalleeSave per set 116 * bit. When alloc_slots is non-zero, also reserves an 8/size-byte save slot per 117 * register (aa64); otherwise slot is left NONE and the backend computes the 118 * save offset itself (rv64/x64). `spec_by_class` (indexed by NativeAllocClass) 119 * gives the save-slot shape per class and may be NULL when alloc_slots == 0. 120 * Resets the set first; panics if it would exceed 121 * NATIVE_FRAME_MAX_CALLEE_SAVES. */ 122 void native_frame_set_callee_saves(NativeFrame* f, const u32* used_by_class, 123 u32 nclasses, 124 const NativeFrameSaveSpec* spec_by_class, 125 u32 nspec, int alloc_slots); 126 127 /* Fill `out` with the registers in the callee-save set belonging to `cls`, in 128 * the order they were derived. Returns the count written (capped at `cap`). */ 129 u32 native_frame_collect_saves(const NativeFrame* f, NativeAllocClass cls, 130 Reg* out, u32 cap); 131 132 /* Bytes of the vararg register-save area for `abi`, derived from its va_list 133 * layout: gp_reg_count*gp_slot_size + fp_reg_count*fp_slot_size. 0 for ABIs 134 * with no register-save area (e.g. Win64). The one ABI-consulting frame query. 135 */ 136 u32 native_frame_va_save_bytes(TargetABI* abi); 137 138 #endif