abi.c (4876B)
1 /* wasm32 BasicCABI classifier. 2 * 3 * Reference: 4 * https://github.com/WebAssembly/tool-conventions/blob/main/BasicCABI.md 5 * 6 * Coverage for v1: 7 * void -> IGNORE 8 * scalar ≤ 8B -> DIRECT, one part (i32/i64/f32/f64 per scalar kind) 9 * pointer -> DIRECT, one INT part (4 bytes, ILP32) 10 * empty struct -> IGNORE 11 * singleton struct (one scalar field, size matches) -> DIRECT, one part 12 * other aggregate -> INDIRECT (sret/byval) 13 * array -> INDIRECT 14 * 15 * Wasm has no native 128-bit integer or x87 long-double; those panic on 16 * classification rather than silently routing through memory. */ 17 18 #include <string.h> 19 20 #include "abi/abi_internal.h" 21 #include "cg/type.h" 22 #include "core/arena.h" 23 #include "core/core.h" 24 25 static void classify_void(ABIArgInfo* out) { 26 memset(out, 0, sizeof *out); 27 out->kind = ABI_ARG_IGNORE; 28 } 29 30 static void classify_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out) { 31 ABITypeInfo ti = abi_internal_type_info(a, t); 32 ABIArgPart* parts; 33 34 if (ti.size > 8) { 35 SrcLoc loc = {0, 0, 0}; 36 /* A >8-byte float is binary128 long double (the only such C scalar); 37 * report it specifically. Wider integers are __int128. */ 38 if (ti.scalar_kind == ABI_SC_FLOAT) { 39 compiler_panic(a->c, loc, "wasm: long double not supported"); 40 } 41 compiler_panic(a->c, loc, 42 "wasm32 ABI: scalar %u-byte values are not supported", 43 (unsigned)ti.size); 44 } 45 46 out->kind = ABI_ARG_DIRECT; 47 out->flags = ABI_AF_NONE; 48 out->indirect_align = 0; 49 50 parts = arena_new(a->c->tu, ABIArgPart); 51 memset(parts, 0, sizeof *parts); 52 parts->cls = (ti.scalar_kind == ABI_SC_FLOAT) ? ABI_CLASS_FP : ABI_CLASS_INT; 53 parts->loc = ABI_LOC_REG; 54 parts->size = ti.size; 55 parts->align = ti.align; 56 parts->src_offset = 0; 57 58 out->parts = parts; 59 out->nparts = 1; 60 } 61 62 /* BasicCABI singleton rule: a record with exactly one scalar field whose size 63 * equals the record's size is passed as that scalar. Otherwise the aggregate 64 * is passed indirectly. */ 65 static int try_classify_singleton(TargetABI* a, KitCgTypeId t, 66 ABIArgInfo* out) { 67 const CgType* ty = cg_type_get(a->c, t); 68 ABITypeInfo ti; 69 KitCgTypeId field_ty; 70 const CgType* field; 71 if (!ty || ty->kind != KIT_CG_TYPE_RECORD || ty->record.nfields != 1) 72 return 0; 73 if (ty->record.is_union) return 0; 74 field_ty = ty->record.fields[0].type; 75 field = cg_type_get(a->c, field_ty); 76 if (!field) return 0; 77 if (field->kind == KIT_CG_TYPE_RECORD || field->kind == KIT_CG_TYPE_ARRAY) 78 return 0; 79 ti = abi_internal_type_info(a, field_ty); 80 if (ti.size != (u32)ty->size) return 0; 81 classify_scalar(a, field_ty, out); 82 return 1; 83 } 84 85 static void classify_aggregate(TargetABI* a, KitCgTypeId t, ABIArgInfo* out, 86 int is_return) { 87 ABITypeInfo ti = abi_internal_type_info(a, t); 88 if (ti.size == 0) { 89 classify_void(out); 90 return; 91 } 92 if (try_classify_singleton(a, t, out)) return; 93 out->kind = ABI_ARG_INDIRECT; 94 out->flags = is_return ? ABI_AF_SRET : ABI_AF_BYVAL; 95 out->indirect_align = ti.align ? ti.align : 4; 96 out->parts = NULL; 97 out->nparts = 0; 98 } 99 100 static void classify_one(TargetABI* a, KitCgTypeId t, ABIArgInfo* out, 101 int is_return) { 102 const CgType* ty = cg_type_get(a->c, t); 103 if (!ty || ty->kind == KIT_CG_TYPE_VOID) { 104 classify_void(out); 105 return; 106 } 107 switch (ty->kind) { 108 case KIT_CG_TYPE_RECORD: 109 classify_aggregate(a, t, out, is_return); 110 return; 111 case KIT_CG_TYPE_ARRAY: 112 /* Arrays decay to pointers in C function signatures; if one reaches 113 * the classifier, route indirect. */ 114 classify_aggregate(a, t, out, is_return); 115 return; 116 case KIT_CG_TYPE_ALIAS: 117 classify_one(a, ty->alias.base, out, is_return); 118 return; 119 default: 120 classify_scalar(a, t, out); 121 return; 122 } 123 } 124 125 static ABIFuncInfo* wasm32_compute_func_info(TargetABI* a, KitCgTypeId fn) { 126 ABIFuncInfo* info = arena_new(a->c->tu, ABIFuncInfo); 127 const CgType* fnty = cg_type_get(a->c, fn); 128 memset(info, 0, sizeof *info); 129 130 classify_one(a, cg_func_ret_type(fnty), &info->ret, /*is_return=*/1); 131 info->has_sret = (info->ret.kind == ABI_ARG_INDIRECT) ? 1 : 0; 132 info->variadic = fnty->func.abi_variadic ? 1u : 0u; 133 134 info->nparams = (u16)fnty->func.nparams; 135 if (fnty->func.nparams) { 136 ABIArgInfo* arr = arena_array(a->c->tu, ABIArgInfo, fnty->func.nparams); 137 memset(arr, 0, sizeof(ABIArgInfo) * fnty->func.nparams); 138 for (u32 i = 0; i < fnty->func.nparams; ++i) { 139 classify_one(a, fnty->func.params[i].type, &arr[i], /*is_return=*/0); 140 } 141 info->params = arr; 142 } else { 143 info->params = NULL; 144 } 145 return info; 146 } 147 148 const ABIVtable wasm32_vtable = { 149 .compute_func_info = wasm32_compute_func_info, 150 .va_list_info = {4, 4, ABI_SC_PTR, 0, 0, 0}, 151 };