kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

native_frame.h (7377B)


      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   NativeFrameSlot free_next; /* next free slot in its size/align bin, when in_free */
     40   u8 kind;                   /* NativeFrameSlotKind */
     41   u8 in_free;                /* on the free list (guards double-release) */
     42   u8 pad[2];
     43 } NativeFrameSlotEntry;
     44 
     45 /* A callee-saved register the function body used and must preserve. `slot` is
     46  * the reserved save slot when the backend asked native_frame to allocate one
     47  * (aa64), else NATIVE_FRAME_SLOT_NONE (rv64/x64 compute the save offset). */
     48 typedef struct NativeFrameCalleeSave {
     49   NativeFrameSlot slot;
     50   KitCgTypeId type;
     51   u8 cls; /* NativeAllocClass */
     52   Reg reg;
     53 } NativeFrameCalleeSave;
     54 
     55 /* Per-class save-slot shape, used only when native_frame_set_callee_saves is
     56  * asked to allocate save slots (alloc_slots != 0). Indexed by NativeAllocClass.
     57  */
     58 typedef struct NativeFrameSaveSpec {
     59   u32 size;
     60   u32 align;
     61   KitCgTypeId type;
     62 } NativeFrameSaveSpec;
     63 
     64 /* x19..x28 (10) + v8..v15 (8) on aa64; s0..s11 + fs0..fs11 on rv64; the Win64
     65  * GPR + XMM callee-saved set on x64. 48 covers every target with headroom. */
     66 #define NATIVE_FRAME_MAX_CALLEE_SAVES 48u
     67 
     68 typedef struct NativeFrame {
     69   Compiler* c;
     70 
     71   NativeFrameSlotEntry* slots; /* arena-grown; 1-indexed handles */
     72   u32 nslots;
     73   u32 slots_cap;
     74   u32 cum_off; /* running below-anchor slot bytes (sum of reservations) */
     75 
     76   u32 max_outgoing; /* max outgoing-arg bytes across all calls */
     77 
     78   NativeFrameCalleeSave callee_saves[NATIVE_FRAME_MAX_CALLEE_SAVES];
     79   u32 ncallee_saves;
     80 
     81   /* Free list of recyclable transient temp slots, bucketed by exact
     82    * (size,align). A NATIVE_FRAME_SLOT_TRANSIENT allocation pops a same-bucket
     83    * slot before bumping cum_off; native_frame_release_slot pushes a dead temp's
     84    * slot back. Buckets are LIFO stacks threaded through
     85    * NativeFrameSlotEntry.free_next; the per-function distinct (size,align) set is
     86    * tiny (scalar temps: 4/4, 8/8, 16/16, ...), so a linear bucket scan is O(1) in
     87    * practice. Overflow past NATIVE_FRAME_FREE_BINS simply doesn't recycle. */
     88   struct {
     89     u32 size;
     90     u32 align;
     91     NativeFrameSlot head;
     92   } free_bins[16];
     93   u32 nfree_bins;
     94 
     95   u8 frame_final; /* set once the prologue is emitted; bars further slots */
     96   u8 known_frame; /* optimizer (known-frame) path vs single-pass path */
     97   u8 has_alloca;  /* body contains a dynamic alloca */
     98   u8 pad;
     99 } NativeFrame;
    100 
    101 /* One-time setup (call from <arch>_native_target_new). */
    102 void native_frame_init(NativeFrame* f, Compiler* c);
    103 
    104 /* Per-function reset: clears the slot table, outgoing size, callee-save set and
    105  * the frame flags. Keeps the slots buffer for reuse across functions. */
    106 void native_frame_reset(NativeFrame* f);
    107 
    108 /* Allocate a frame slot, advancing the cumulative offset by `size` aligned to
    109  * `align` (defaults: size 8, align 1). Returns a 1-indexed handle. Panics if
    110  * the frame is already final. Identical arithmetic to the per-arch allocators.
    111  */
    112 NativeFrameSlot native_frame_slot_alloc(NativeFrame* f,
    113                                         const NativeFrameSlotDesc* d);
    114 
    115 /* Return a previously-allocated transient slot to the free list so a later
    116  * NATIVE_FRAME_SLOT_TRANSIENT allocation of the same (size,align) reuses it.
    117  * Idempotent (a slot already on the free list is ignored). The slot's home
    118  * offset is preserved; only the next-temp ownership is recycled. */
    119 void native_frame_release_slot(NativeFrame* f, NativeFrameSlot slot);
    120 
    121 /* Resolve a 1-indexed handle to its entry. Panics on an out-of-range handle. */
    122 NativeFrameSlotEntry* native_frame_slot_at(NativeFrame* f,
    123                                            NativeFrameSlot slot);
    124 
    125 /* Grow max_outgoing to at least `bytes`. */
    126 void native_frame_note_outgoing(NativeFrame* f, u32 bytes);
    127 
    128 /* Mark the frame final (no further native_frame_slot_alloc allowed). */
    129 void native_frame_set_final(NativeFrame* f);
    130 
    131 static inline u32 native_frame_slot_bytes(const NativeFrame* f) {
    132   return f->cum_off;
    133 }
    134 
    135 /* Derive the used-callee-save set from the optimizer's per-class masks (one
    136  * bitmask of hard registers per NativeAllocClass, already restricted to the
    137  * callee-saved set by the caller). Appends one NativeFrameCalleeSave per set
    138  * bit. When alloc_slots is non-zero, also reserves an 8/size-byte save slot per
    139  * register (aa64); otherwise slot is left NONE and the backend computes the
    140  * save offset itself (rv64/x64). `spec_by_class` (indexed by NativeAllocClass)
    141  * gives the save-slot shape per class and may be NULL when alloc_slots == 0.
    142  * Resets the set first; panics if it would exceed
    143  * NATIVE_FRAME_MAX_CALLEE_SAVES. */
    144 void native_frame_set_callee_saves(NativeFrame* f, const u32* used_by_class,
    145                                    u32 nclasses,
    146                                    const NativeFrameSaveSpec* spec_by_class,
    147                                    u32 nspec, int alloc_slots);
    148 
    149 /* Fill `out` with the registers in the callee-save set belonging to `cls`, in
    150  * the order they were derived. Returns the count written (capped at `cap`). */
    151 u32 native_frame_collect_saves(const NativeFrame* f, NativeAllocClass cls,
    152                                Reg* out, u32 cap);
    153 
    154 /* Bytes of the vararg register-save area for `abi`, derived from its va_list
    155  * layout: gp_reg_count*gp_slot_size + fp_reg_count*fp_slot_size. 0 for ABIs
    156  * with no register-save area (e.g. Win64). The one ABI-consulting frame query.
    157  */
    158 u32 native_frame_va_save_bytes(TargetABI* abi);
    159 
    160 #endif