abi_win64_x64.c (6524B)
1 /* Win64 (Microsoft x86_64) ABI classifier. 2 * 3 * Selected when (target.arch == X86_64, target.os == WINDOWS). 4 * 5 * Win64 vs SysV-x64 deltas: 6 * - Arg slots: RCX/RDX/R8/R9 share index with XMM0..3 (codegen 7 * assigns by index; classifier emits per-slot INT or FP parts). 8 * - Aggregates: pass-by-value only for sizes in {1,2,4,8}; otherwise 9 * hidden-pointer (byval for args, sret for returns). 10 * - __int128: passed as two INTEGER eightbytes (mingw convention; 11 * differs from MSVC spec which says by reference). 12 * - long double: 64-bit double (no x87). 13 * - va_list: void* (single pointer; no struct). 14 * - varargs: still in regs; FP-args duplicated in matching GPR slot 15 * by the call-site codegen (not encoded here). 16 * 17 * Shadow-space (32 B above return addr) is a call-site reservation, 18 * not an ABI classifier concern -- see arch/x64/call.c. 19 */ 20 21 #include <string.h> 22 23 #include "abi/abi_internal.h" 24 #include "cg/type.h" 25 #include "core/arena.h" 26 #include "core/core.h" 27 28 static void classify_void(ABIArgInfo* out) { 29 memset(out, 0, sizeof *out); 30 out->kind = ABI_ARG_IGNORE; 31 } 32 33 static void classify_scalar(TargetABI* a, KitCgTypeId t, ABIArgInfo* out, 34 int is_return) { 35 ABITypeInfo ti = abi_internal_type_info(a, t); 36 (void)is_return; 37 /* __int128 / __uint128: mingw/GCC convention emits two INTEGER 38 * eightbytes (rcx+rdx for args, rax+rdx for return) -- same shape 39 * as SysV. MSVC's official spec says "by reference" for 16-byte 40 * aggregates, but mingw is kit's interop target on Windows and 41 * mingw matches SysV here. */ 42 if (ti.scalar_kind == ABI_SC_INT && ti.size == 16) { 43 ABIArgPart* parts = arena_array(a->c->tu, ABIArgPart, 2); 44 memset(parts, 0, sizeof(ABIArgPart) * 2); 45 for (u32 i = 0; i < 2; ++i) { 46 parts[i].cls = ABI_CLASS_INT; 47 parts[i].loc = ABI_LOC_REG; 48 parts[i].size = 8; 49 parts[i].align = 8; 50 parts[i].src_offset = i * 8; 51 } 52 out->kind = ABI_ARG_DIRECT; 53 out->flags = ABI_AF_NONE; 54 out->parts = parts; 55 out->nparts = 2; 56 out->indirect_align = 0; 57 return; 58 } 59 /* long double on Win64 is 64-bit double (both MSVC and mingw, unless 60 * mingw's -mlong-double-80 is in effect -- not supported in v1). 61 * The front end should already have lowered `long double` to a size-8 62 * float for Windows targets. Defensive path: if a size-16 FP slips 63 * through, treat it as a size-8 double (one FP part) -- this stays 64 * register-passed, unlike SysV which routes long double through 65 * memory. */ 66 if (ti.scalar_kind == ABI_SC_FLOAT && ti.size == 16) { 67 ABIArgPart* parts = arena_new(a->c->tu, ABIArgPart); 68 memset(parts, 0, sizeof *parts); 69 parts->cls = ABI_CLASS_FP; 70 parts->loc = ABI_LOC_REG; 71 parts->size = 8; 72 parts->align = 8; 73 parts->src_offset = 0; 74 out->kind = ABI_ARG_DIRECT; 75 out->flags = ABI_AF_NONE; 76 out->parts = parts; 77 out->nparts = 1; 78 out->indirect_align = 0; 79 return; 80 } 81 82 out->kind = ABI_ARG_DIRECT; 83 out->flags = ABI_AF_NONE; 84 out->indirect_align = 0; 85 86 ABIArgPart* parts = arena_new(a->c->tu, ABIArgPart); 87 memset(parts, 0, sizeof *parts); 88 parts->cls = (ti.scalar_kind == ABI_SC_FLOAT) ? ABI_CLASS_FP : ABI_CLASS_INT; 89 parts->loc = ABI_LOC_REG; 90 parts->size = ti.size; 91 parts->align = ti.align; 92 parts->src_offset = 0; 93 94 out->parts = parts; 95 out->nparts = 1; 96 } 97 98 static void classify_aggregate(TargetABI* a, KitCgTypeId t, ABIArgInfo* out, 99 int is_return) { 100 ABITypeInfo ti = abi_internal_type_info(a, t); 101 if (ti.size == 0) { 102 classify_void(out); 103 return; 104 } 105 /* Win64: aggregates pass by value only when the size is exactly one 106 * of {1, 2, 4, 8}. A 3-byte struct is NOT a 3-byte INT part -- it 107 * goes by hidden pointer. A 16-byte struct is also hidden-pointer 108 * (no two-register pair, unlike SysV's <=16B path). */ 109 if (ti.size == 1 || ti.size == 2 || ti.size == 4 || ti.size == 8) { 110 ABIArgPart* parts = arena_new(a->c->tu, ABIArgPart); 111 memset(parts, 0, sizeof *parts); 112 parts->cls = ABI_CLASS_INT; 113 parts->loc = ABI_LOC_REG; 114 parts->size = ti.size; 115 parts->align = ti.align ? ti.align : ti.size; 116 parts->src_offset = 0; 117 out->kind = ABI_ARG_DIRECT; 118 out->flags = ABI_AF_NONE; 119 out->parts = parts; 120 out->nparts = 1; 121 out->indirect_align = 0; 122 } else { 123 out->kind = ABI_ARG_INDIRECT; 124 out->flags = is_return ? ABI_AF_SRET : ABI_AF_BYVAL; 125 out->indirect_align = ti.align ? ti.align : 8; 126 out->parts = NULL; 127 out->nparts = 0; 128 } 129 } 130 131 static void classify_one(TargetABI* a, KitCgTypeId t, ABIArgInfo* out, 132 int is_return) { 133 const CgType* ty = cg_type_get(a->c, t); 134 if (!ty || ty->kind == KIT_CG_TYPE_VOID) { 135 classify_void(out); 136 return; 137 } 138 switch (ty->kind) { 139 case KIT_CG_TYPE_RECORD: 140 classify_aggregate(a, t, out, is_return); 141 return; 142 case KIT_CG_TYPE_ALIAS: 143 classify_one(a, ty->alias.base, out, is_return); 144 return; 145 default: 146 classify_scalar(a, t, out, is_return); 147 return; 148 } 149 } 150 151 static ABIFuncInfo* win64_x64_compute_func_info(TargetABI* a, KitCgTypeId fn) { 152 ABIFuncInfo* info = arena_new(a->c->tu, ABIFuncInfo); 153 const CgType* fnty = cg_type_get(a->c, fn); 154 memset(info, 0, sizeof *info); 155 156 classify_one(a, cg_func_ret_type(fnty), &info->ret, /*is_return=*/1); 157 info->has_sret = (info->ret.kind == ABI_ARG_INDIRECT) ? 1 : 0; 158 /* Win64 passes the sret pointer in rcx (the first integer arg register), 159 * consuming that slot. */ 160 info->sret_consumes_int_arg = info->has_sret; 161 info->variadic = fnty->func.abi_variadic; 162 163 info->nparams = (u16)fnty->func.nparams; 164 if (fnty->func.nparams) { 165 ABIArgInfo* arr = arena_array(a->c->tu, ABIArgInfo, fnty->func.nparams); 166 memset(arr, 0, sizeof(ABIArgInfo) * fnty->func.nparams); 167 for (u32 i = 0; i < fnty->func.nparams; ++i) { 168 classify_one(a, fnty->func.params[i].type, &arr[i], /*is_return=*/0); 169 } 170 info->params = arr; 171 } else { 172 info->params = NULL; 173 } 174 return info; 175 } 176 177 const ABIVtable win64_x64_vtable = { 178 .compute_func_info = win64_x64_compute_func_info, 179 /* Windows commits stack one guard page at a time; the x64 backend gates its 180 * __chkstk call on this (frame_size > one page). See x64_build_prologue. */ 181 .stack_probe_interval = 4096, 182 .va_list_info = {8, 8, ABI_SC_PTR, 0, 0, 0}, 183 .va_list_layout = {.type = {8, 8, ABI_SC_PTR, 0, 0, 0}, 184 .kind = ABI_VA_LIST_POINTER}, 185 };