kit

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

abi.c (11344B)


      1 /* TargetABI dispatch and shared codegen type layout.
      2  *
      3  * The single authority for target-dependent storage layout and calling
      4  * convention decisions. Frontends lower source-language types to CgType
      5  * before calling into this layer.
      6  *
      7  * Per-ABI bits (function classification, __va_list shape) live in
      8  * abi_aapcs64.c, abi_sysv_x64.c, ... The ABI registry selects the vtable
      9  * for (target.arch, target.obj). The C-standard-driven scalar profile and
     10  * record layout stay here so all ABIs share one impl. */
     11 
     12 #include "abi/abi.h"
     13 
     14 #include <string.h>
     15 
     16 #include "abi/abi_internal.h"
     17 #include "cg/type.h"
     18 #include "core/arena.h"
     19 #include "core/core.h"
     20 
     21 /* ---- scalar profile ----
     22  *
     23  * Shared by all currently supported ABIs (LP64 on Linux for both
     24  * aarch64 and x86_64). When a Windows-x64 (LLP64) or 32-bit ABI lands,
     25  * promote prim_info into the vtable. */
     26 
     27 static ABITypeInfo abi_cg_type_info_compute(TargetABI* a, KitCgTypeId id) {
     28   ABITypeInfo r = {0, 0, ABI_SC_VOID, 0, 0, 0};
     29   const CgType* t;
     30   t = cg_type_get(a->c, id);
     31   if (!t) return r;
     32   switch (t->kind) {
     33     case KIT_CG_TYPE_PTR:
     34       r.size = a->c->target.ptr_size ? a->c->target.ptr_size : 8;
     35       r.align = a->c->target.ptr_align ? a->c->target.ptr_align : 8;
     36       r.scalar_kind = ABI_SC_PTR;
     37       return r;
     38     case KIT_CG_TYPE_ARRAY: {
     39       ABITypeInfo e = abi_cg_type_info(a, t->array.elem);
     40       r.size = e.size * t->array.count;
     41       r.align = e.align;
     42       return r;
     43     }
     44     case KIT_CG_TYPE_RECORD: {
     45       const ABIRecordLayout* L = abi_cg_record_layout(a, id);
     46       if (L) {
     47         r.size = L->size;
     48         r.align = L->align;
     49       }
     50       return r;
     51     }
     52     case KIT_CG_TYPE_ENUM:
     53       return abi_cg_type_info(a, t->enum_.base);
     54     case KIT_CG_TYPE_FUNC:
     55       /* sizeof(function) is undefined in C; use 1 for arithmetic. */
     56       r.size = 1;
     57       r.align = 1;
     58       return r;
     59     case KIT_CG_TYPE_VOID:
     60       r.align = 1;
     61       r.scalar_kind = ABI_SC_VOID;
     62       return r;
     63     case KIT_CG_TYPE_BOOL:
     64       r.size = t->size;
     65       r.align = t->align;
     66       r.scalar_kind = ABI_SC_BOOL;
     67       return r;
     68     case KIT_CG_TYPE_INT:
     69       r.size = t->size;
     70       r.align = t->align;
     71       r.scalar_kind = ABI_SC_INT;
     72       return r;
     73     case KIT_CG_TYPE_FLOAT:
     74       r.size = t->size;
     75       r.align = t->align;
     76       r.scalar_kind = ABI_SC_FLOAT;
     77       return r;
     78     case KIT_CG_TYPE_VARARG_STATE:
     79       r.size = t->size;
     80       r.align = t->align;
     81       return r;
     82     default:
     83       return r;
     84   }
     85 }
     86 
     87 ABITypeInfo abi_cg_type_info(TargetABI* a, KitCgTypeId id) {
     88   const ABITypeInfo* p;
     89   ABITypeInfo r;
     90   if (!id) {
     91     ABITypeInfo z = {0, 0, ABI_SC_VOID, 0, 0, 0};
     92     return z;
     93   }
     94   /* Builtins are the overwhelming majority of type queries: index the
     95    * per-compiler layout table inline (one load, no cross-TU call) once cg_api
     96    * has published it. Before that, fall through to the general path, which
     97    * lazily creates the table. */
     98   if (id <= KIT_CG_BUILTIN_COUNT && a->c->cg_builtin_layout)
     99     return ((const ABITypeInfo*)a->c->cg_builtin_layout)[id - 1u];
    100   /* User types: a borrowed pointer to the cached layout (no field-by-field
    101    * copy) collapses the alias/enum/array recursion to one load; a miss computes
    102    * once and caches. */
    103   p = api_type_layout_ref(a->c, id);
    104   if (p) return *p;
    105   r = abi_cg_type_info_compute(a, id);
    106   api_type_layout_put(a->c, id, r);
    107   return r;
    108 }
    109 
    110 u32 abi_cg_sizeof(TargetABI* a, KitCgTypeId id) {
    111   return abi_cg_type_info(a, id).size;
    112 }
    113 u32 abi_cg_alignof(TargetABI* a, KitCgTypeId id) {
    114   return abi_cg_type_info(a, id).align;
    115 }
    116 
    117 u32 abi_cg_scalar_split_lane_size(TargetABI* a, KitCgTypeId id) {
    118   if (!a || !a->vt || !a->vt->scalar_split_lane_size) return 0;
    119   return a->vt->scalar_split_lane_size(a, id);
    120 }
    121 
    122 /* ---- record layout (struct/union) ----
    123  *
    124  * The CG type constructor computes the shared source-facing record layout.
    125  * The ABI cache exposes that immutable layout to codegen passes. When an ABI
    126  * with different source bit-field rules lands, record construction should be
    127  * routed through an ABI-specific layout hook before the type is committed. */
    128 
    129 static ABIRecordLayout* compute_record_layout(TargetABI* a, KitCgTypeId id) {
    130   ABIRecordLayout* L = arena_new(a->c->tu, ABIRecordLayout);
    131   const CgType* t = cg_type_get(a->c, id);
    132   if (!L) return NULL;
    133   if (!t || t->kind != KIT_CG_TYPE_RECORD ||
    134       !(t->record.flags & CG_TYPE_RECORD_COMPLETE))
    135     return NULL;
    136   memset(L, 0, sizeof *L);
    137   ABIFieldLayout* fl = NULL;
    138   if (t->record.nfields) {
    139     fl = arena_array(a->c->tu, ABIFieldLayout, t->record.nfields);
    140     memset(fl, 0, sizeof(ABIFieldLayout) * t->record.nfields);
    141   }
    142 
    143   for (u32 i = 0; i < t->record.nfields; ++i) {
    144     const CgTypeField* f = &t->record.fields[i];
    145     fl[i].offset = (u32)f->offset;
    146     fl[i].bit_offset = f->bit_offset;
    147     fl[i].bit_width = (f->flags & KIT_CG_FIELD_BITFIELD) ? f->bit_width : 0;
    148     fl[i].storage_size = f->bit_storage_size ? f->bit_storage_size
    149                                              : (u32)abi_cg_sizeof(a, f->type);
    150   }
    151   L->size = (u32)t->size;
    152   L->align = t->align;
    153   L->nfields = t->record.nfields;
    154   L->fields = fl;
    155   return L;
    156 }
    157 
    158 const ABIRecordLayout* abi_cg_record_layout(TargetABI* a, KitCgTypeId id) {
    159   const CgType* t = cg_type_get(a->c, id);
    160   ABIRecordLayout** hit;
    161   ABIRecordLayout* L;
    162   if (!t || t->kind != KIT_CG_TYPE_RECORD ||
    163       !(t->record.flags & CG_TYPE_RECORD_COMPLETE))
    164     return NULL;
    165   hit = AbiRecLayoutMap_get(&a->rec_cache, id);
    166   if (hit) return *hit;
    167   L = compute_record_layout(a, id);
    168   if (!L) return NULL;
    169   AbiRecLayoutMap_set(&a->rec_cache, id, L);
    170   return L;
    171 }
    172 
    173 /* ---- shared classifier primitives ----
    174  *
    175  * The per-ABI classifiers (abi_sysv_x64.c, abi_win64_x64.c, abi_aapcs64.c,
    176  * abi_rv64.c) share these byte-identical building blocks; the ABI-specific
    177  * scalar/aggregate rules stay in their own TUs. */
    178 
    179 /* A void / zero-size argument is ignored (no parts, no register/stack slot). */
    180 void abi_classify_void(ABIArgInfo* out) {
    181   memset(out, 0, sizeof *out);
    182   out->kind = ABI_ARG_IGNORE;
    183 }
    184 
    185 /* A 16-byte integer scalar (__int128 / __uint128) passed/returned as two
    186  * INTEGER eightbytes: low half in the lower-numbered register, high in the
    187  * next. Used by the SysV-x64, Win64 (mingw), and AAPCS64 classifiers, which
    188  * all agree on this shape. */
    189 void abi_classify_int128_pair(TargetABI* a, ABIArgInfo* out) {
    190   ABIArgPart* parts = arena_array(a->c->tu, ABIArgPart, 2);
    191   memset(parts, 0, sizeof(ABIArgPart) * 2);
    192   for (u32 i = 0; i < 2; ++i) {
    193     parts[i].cls = ABI_CLASS_INT;
    194     parts[i].loc = ABI_LOC_REG;
    195     parts[i].size = 8;
    196     parts[i].align = 8;
    197     parts[i].src_offset = i * 8;
    198   }
    199   out->kind = ABI_ARG_DIRECT;
    200   out->flags = ABI_AF_NONE;
    201   out->parts = parts;
    202   out->nparts = 2;
    203   out->indirect_align = 0;
    204 }
    205 
    206 /* The single-register scalar tail shared by every per-ABI classify_scalar: a
    207  * DIRECT argument with exactly one register part carrying the whole scalar.
    208  * `is_fp` selects the part class; the caller owns the FP-eligibility decision.
    209  */
    210 void abi_classify_scalar_reg_part(TargetABI* a, ABIArgInfo* out, ABITypeInfo ti,
    211                                   int is_fp) {
    212   out->kind = ABI_ARG_DIRECT;
    213   out->flags = ABI_AF_NONE;
    214   out->indirect_align = 0;
    215 
    216   ABIArgPart* parts = arena_new(a->c->tu, ABIArgPart);
    217   memset(parts, 0, sizeof *parts);
    218   parts->cls = is_fp ? ABI_CLASS_FP : ABI_CLASS_INT;
    219   parts->loc = ABI_LOC_REG;
    220   parts->size = ti.size;
    221   parts->align = ti.align;
    222   parts->src_offset = 0;
    223 
    224   out->parts = parts;
    225   out->nparts = 1;
    226 }
    227 
    228 /* Generic compute_func_info scaffold shared by the per-ABI vtables. Classifies
    229  * the result then every parameter through `classify_one`, filling the shared
    230  * has_sret / sret_consumes_int_arg / variadic / nparams fields. The per-ABI
    231  * vararg metadata pass (SysV-x64 gp/fp offsets) runs in the caller on the
    232  * returned info; all vararg_* fields are left zero here. */
    233 ABIFuncInfo* abi_compute_func_info_generic(TargetABI* a, KitCgTypeId fn,
    234                                            ABIClassifyOneFn classify_one,
    235                                            int sret_consumes_int_arg) {
    236   ABIFuncInfo* info = arena_new(a->c->tu, ABIFuncInfo);
    237   const CgType* fnty = cg_type_get(a->c, fn);
    238   memset(info, 0, sizeof *info);
    239 
    240   classify_one(a, cg_func_ret_type(fnty), &info->ret, /*is_return=*/1);
    241   info->has_sret = (info->ret.kind == ABI_ARG_INDIRECT) ? 1 : 0;
    242   info->sret_consumes_int_arg =
    243       (sret_consumes_int_arg && info->has_sret) ? 1 : 0;
    244   info->variadic = fnty->func.abi_variadic;
    245 
    246   info->nparams = (u16)fnty->func.nparams;
    247   if (fnty->func.nparams) {
    248     ABIArgInfo* arr = arena_array(a->c->tu, ABIArgInfo, fnty->func.nparams);
    249     memset(arr, 0, sizeof(ABIArgInfo) * fnty->func.nparams);
    250     for (u32 i = 0; i < fnty->func.nparams; ++i) {
    251       classify_one(a, fnty->func.params[i].type, &arr[i], /*is_return=*/0);
    252     }
    253     info->params = arr;
    254   } else {
    255     info->params = NULL;
    256   }
    257   return info;
    258 }
    259 
    260 /* ---- function classification (vtabled) ---- */
    261 
    262 const ABIFuncInfo* abi_cg_func_info(TargetABI* a, KitCgTypeId fn_type) {
    263   const CgType* fn = cg_type_get(a->c, fn_type);
    264   ABIFuncInfo** hit;
    265   ABIFuncInfo* info;
    266   if (!fn || fn->kind != KIT_CG_TYPE_FUNC) return NULL;
    267   hit = AbiFnInfoMap_get(&a->fn_cache, fn_type);
    268   if (hit) return *hit;
    269   info = a->vt->compute_func_info(a, fn_type);
    270   if (!info) return NULL;
    271   AbiFnInfoMap_set(&a->fn_cache, fn_type, info);
    272   return info;
    273 }
    274 
    275 u32 abi_stack_probe_interval(TargetABI* a) {
    276   return a->vt->stack_probe_interval;
    277 }
    278 
    279 ABITypeInfo abi_va_list_info(TargetABI* a) { return a->vt->va_list_info; }
    280 
    281 ABIVaListInfo abi_va_list_layout(TargetABI* a) {
    282   /* va_list_info is the single source of truth for the va_list ABITypeInfo;
    283    * the layout's .type is always derived from it so the two cannot drift. */
    284   ABIVaListInfo out = a->vt->va_list_layout;
    285   out.type = a->vt->va_list_info;
    286   if (out.kind == ABI_VA_LIST_OPAQUE && out.type.scalar_kind == ABI_SC_PTR &&
    287       out.type.size == 8u)
    288     out.kind = ABI_VA_LIST_POINTER;
    289   return out;
    290 }
    291 
    292 /* ---- lifecycle ---- */
    293 
    294 static const ABIVtable* select_vtable(Compiler* c) {
    295   const ABIVtable* vt = abi_vtable_lookup(c->target.arch, c->target.obj);
    296   if (vt) return vt;
    297   {
    298     SrcLoc loc = {0, 0, 0};
    299     compiler_panic(c, loc, "abi_init: unsupported target arch/obj %d/%d",
    300                    (int)c->target.arch, (int)c->target.obj);
    301   }
    302 }
    303 
    304 void abi_init(TargetABI* a, Compiler* c) {
    305   memset(a, 0, sizeof *a);
    306   a->c = c;
    307   a->vt = select_vtable(c);
    308   /* The cached values stay on c->tu (per-TU arena); only the index lives on the
    309    * compiler heap, freed in abi_fini. */
    310   AbiFnInfoMap_init(&a->fn_cache, (Heap*)c->ctx->heap);
    311   AbiRecLayoutMap_init(&a->rec_cache, (Heap*)c->ctx->heap);
    312 }
    313 
    314 void abi_fini(TargetABI* a) {
    315   if (!a) return;
    316   AbiFnInfoMap_fini(&a->fn_cache);
    317   AbiRecLayoutMap_fini(&a->rec_cache);
    318   a->vt = NULL;
    319   a->c = NULL;
    320 }
    321 
    322 TargetABI* abi_new(Compiler* c) {
    323   Heap* h = (Heap*)c->ctx->heap;
    324   TargetABI* a =
    325       (TargetABI*)h->alloc(h, sizeof(TargetABI), _Alignof(TargetABI));
    326   if (!a) return NULL;
    327   abi_init(a, c);
    328   return a;
    329 }
    330 
    331 void abi_free(TargetABI* a) {
    332   if (!a) return;
    333   Heap* h = (Heap*)a->c->ctx->heap;
    334   abi_fini(a);
    335   h->free(h, a, sizeof(TargetABI));
    336 }
    337 
    338 Compiler* abi_compiler(TargetABI* a) { return a ? a->c : NULL; }