native_frame.c (5827B)
1 #include "cg/native_frame.h" 2 3 #include <string.h> 4 5 #include "core/arena.h" 6 7 static u32 nf_align_up(u32 v, u32 align) { 8 u32 mask = align ? align - 1u : 0u; 9 return (v + mask) & ~mask; 10 } 11 12 static void nf_panic(NativeFrame* f, const char* msg) { 13 compiler_panic(f->c, (SrcLoc){0, 0, 0}, "native frame: %s", msg); 14 } 15 16 void native_frame_init(NativeFrame* f, Compiler* c) { 17 memset(f, 0, sizeof *f); 18 f->c = c; 19 } 20 21 void native_frame_reset(NativeFrame* f) { 22 /* Keep the slots buffer (slots/slots_cap) for reuse across functions in the 23 * translation unit; nslots = 0 logically clears it. The transient free list is 24 * per-function: dropping nfree_bins discards any stale buckets, and each fresh 25 * slot_alloc reinitializes its entry's free_next/in_free, so no stale handle 26 * from the previous function can be served. */ 27 f->nslots = 0; 28 f->cum_off = 0; 29 f->max_outgoing = 0; 30 f->ncallee_saves = 0; 31 f->nfree_bins = 0; 32 f->frame_final = 0; 33 f->known_frame = 0; 34 f->has_alloca = 0; 35 } 36 37 /* Find the free bucket for (size,align), or NULL. */ 38 static u32* nf_free_bin_find(NativeFrame* f, u32 size, u32 align) { 39 for (u32 i = 0; i < f->nfree_bins; ++i) 40 if (f->free_bins[i].size == size && f->free_bins[i].align == align) 41 return &f->free_bins[i].head; 42 return NULL; 43 } 44 45 NativeFrameSlot native_frame_slot_alloc(NativeFrame* f, 46 const NativeFrameSlotDesc* d) { 47 NativeFrameSlotEntry* s; 48 u32 size = d->size ? d->size : 8u; 49 u32 align = d->align ? d->align : 1u; 50 if (f->frame_final) nf_panic(f, "frame slot requested after prologue"); 51 /* A transient temp first tries to reuse a dead slot of the exact same 52 * (size,align): its home offset already satisfies the alignment, so handing 53 * the same handle back is safe and leaves cum_off (the frame high-water mark) 54 * unchanged. */ 55 if (d->flags & NATIVE_FRAME_SLOT_TRANSIENT) { 56 u32* head = nf_free_bin_find(f, size, align); 57 if (head && *head != NATIVE_FRAME_SLOT_NONE) { 58 NativeFrameSlot slot = *head; 59 s = &f->slots[slot - 1u]; 60 *head = s->free_next; 61 s->free_next = NATIVE_FRAME_SLOT_NONE; 62 s->in_free = 0; 63 s->kind = d->kind; 64 return slot; 65 } 66 } 67 if (f->nslots == f->slots_cap) { 68 u32 cap = f->slots_cap ? f->slots_cap * 2u : 16u; 69 NativeFrameSlotEntry* nb = 70 arena_zarray(f->c->tu, NativeFrameSlotEntry, cap); 71 if (f->slots) memcpy(nb, f->slots, sizeof(*nb) * f->nslots); 72 f->slots = nb; 73 f->slots_cap = cap; 74 } 75 f->cum_off = nf_align_up(f->cum_off + size, align); 76 s = &f->slots[f->nslots++]; 77 s->off = f->cum_off; 78 s->size = size; 79 s->align = align; 80 s->free_next = NATIVE_FRAME_SLOT_NONE; 81 s->kind = d->kind; 82 s->in_free = 0; 83 return (NativeFrameSlot)f->nslots; 84 } 85 86 void native_frame_release_slot(NativeFrame* f, NativeFrameSlot slot) { 87 NativeFrameSlotEntry* s; 88 u32* head; 89 if (slot == NATIVE_FRAME_SLOT_NONE || slot > f->nslots) return; 90 s = &f->slots[slot - 1u]; 91 if (s->in_free) return; /* already released */ 92 head = nf_free_bin_find(f, s->size, s->align); 93 if (!head) { 94 if (f->nfree_bins >= (u32)(sizeof f->free_bins / sizeof f->free_bins[0])) 95 return; /* bucket table full: don't recycle this shape (still correct) */ 96 f->free_bins[f->nfree_bins].size = s->size; 97 f->free_bins[f->nfree_bins].align = s->align; 98 f->free_bins[f->nfree_bins].head = NATIVE_FRAME_SLOT_NONE; 99 head = &f->free_bins[f->nfree_bins].head; 100 f->nfree_bins++; 101 } 102 s->free_next = *head; 103 *head = slot; 104 s->in_free = 1; 105 } 106 107 NativeFrameSlotEntry* native_frame_slot_at(NativeFrame* f, 108 NativeFrameSlot slot) { 109 if (slot == NATIVE_FRAME_SLOT_NONE || slot > f->nslots) 110 nf_panic(f, "bad frame slot"); 111 return &f->slots[slot - 1u]; 112 } 113 114 void native_frame_note_outgoing(NativeFrame* f, u32 bytes) { 115 if (bytes > f->max_outgoing) f->max_outgoing = bytes; 116 } 117 118 void native_frame_set_final(NativeFrame* f) { f->frame_final = 1; } 119 120 void native_frame_set_callee_saves(NativeFrame* f, const u32* used_by_class, 121 u32 nclasses, 122 const NativeFrameSaveSpec* spec_by_class, 123 u32 nspec, int alloc_slots) { 124 f->ncallee_saves = 0; 125 if (!used_by_class) return; 126 for (u32 cls = 0; cls < nclasses; ++cls) { 127 u32 mask = used_by_class[cls]; 128 for (Reg r = 0; r < 32u && mask; ++r) { 129 NativeFrameCalleeSave* cs; 130 if ((mask & (1u << r)) == 0) continue; 131 mask &= ~(1u << r); 132 if (f->ncallee_saves >= NATIVE_FRAME_MAX_CALLEE_SAVES) 133 nf_panic(f, "too many callee-saved registers"); 134 cs = &f->callee_saves[f->ncallee_saves++]; 135 cs->cls = (u8)cls; 136 cs->reg = r; 137 cs->slot = NATIVE_FRAME_SLOT_NONE; 138 cs->type = 0; 139 if (cls < nspec && spec_by_class) cs->type = spec_by_class[cls].type; 140 if (alloc_slots) { 141 NativeFrameSlotDesc sd; 142 const NativeFrameSaveSpec* sp = 143 (cls < nspec && spec_by_class) ? &spec_by_class[cls] : NULL; 144 memset(&sd, 0, sizeof sd); 145 sd.type = cs->type; 146 sd.size = sp && sp->size ? sp->size : 8u; 147 sd.align = sp && sp->align ? sp->align : 8u; 148 sd.kind = NATIVE_FRAME_SLOT_SAVE; 149 cs->slot = native_frame_slot_alloc(f, &sd); 150 } 151 } 152 } 153 } 154 155 u32 native_frame_collect_saves(const NativeFrame* f, NativeAllocClass cls, 156 Reg* out, u32 cap) { 157 u32 n = 0; 158 for (u32 i = 0; i < f->ncallee_saves; ++i) { 159 if (f->callee_saves[i].cls != (u8)cls) continue; 160 if (n >= cap) break; 161 out[n++] = f->callee_saves[i].reg; 162 } 163 return n; 164 } 165 166 u32 native_frame_va_save_bytes(TargetABI* abi) { 167 ABIVaListInfo va = abi_va_list_layout(abi); 168 return va.gp_reg_count * va.gp_slot_size + va.fp_reg_count * va.fp_slot_size; 169 }