kit

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

commit 82b58f328c50dd20e30489b5f331384ee727d1ac
parent 0282ae36cdb7c754f96c931b6c0101d5ee56d31f
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Wed,  3 Jun 2026 10:45:07 -0700

econ(native-shared): share inline-asm constraint + abi-clobber helpers

Sub-refactor (C), partial: the inline-asm constraint-string parsers
(constraint_body / constraint_early / match_index) were byte-identical across
aa64/rv64/x64, and abi_clobber_masks differed only in which static register
table it read. Hoist all four into cg/native_asm as native_asm_constraint_body
/early/match_index and native_asm_abi_clobber_masks (the latter reading the
masks from t->regs->classes, so it needs no per-arch table). Each backend now
calls the shared versions.

The full native_asm_block_direct/optimizer vtable extraction is NOT done (see
report): the arch-private Operand-binding ABI (*_INLINE_OPK/OPCLS), per-arch
*_inline_bind / *_asm_run_template / *_emit_mem / *_addr_base, and the
save/restore predicates make it XL/high-risk on the -O0 codegen path.

(cherry picked from commit f1a7f310da6d07faf407242e49a8a32df1d0c9f3)

Diffstat:
Msrc/arch/aa64/native.c | 65+++++++++++++++--------------------------------------------------
Msrc/arch/rv64/native.c | 52++++++++++++++--------------------------------------
Msrc/arch/x64/native.c | 54++++++++++++++----------------------------------------
Msrc/cg/native_asm.c | 35+++++++++++++++++++++++++++++++++++
Msrc/cg/native_asm.h | 29+++++++++++++++++++++++++++++
5 files changed, 107 insertions(+), 128 deletions(-)

diff --git a/src/arch/aa64/native.c b/src/arch/aa64/native.c @@ -1557,9 +1557,8 @@ static int aa_frame_slot_debug_loc(NativeTarget* t, NativeFrameSlot slot, static void aa_asm_clobber_masks(Compiler* c, SrcLoc loc, const Sym* clobbers, u32 nclob, u32* int_mask, u32* fp_mask); -/* Defined after aa_classes (below); forward-declared so the frame helper can - * use it. Expands KitCgAsmClobberAbiSet bits into per-class register masks. */ -static void aa_abi_clobber_masks(u32 abi_sets, u32* int_mask, u32* fp_mask); +/* abi_clobber_masks is shared as native_asm_abi_clobber_masks + * (cg/native_asm.h); it reads the masks from t->regs->classes. */ /* Build the callee-saved set the prologue must preserve: the allocator-assigned * callee-saved registers (frame->callee_saved_used) plus any an inline-asm @@ -1584,7 +1583,8 @@ static u32 aa_known_callee_saves(NativeTarget* t, aa_asm_clobber_masks(t->c, loc, frame->asm_clobbers, frame->nasm_clobbers, &clob_int, &clob_fp); } - aa_abi_clobber_masks(frame->asm_clobber_abi_sets, &abi_int, &abi_fp); + native_asm_abi_clobber_masks(t, frame->asm_clobber_abi_sets, &abi_int, + &abi_fp); clob_int |= abi_int; clob_fp |= abi_fp; for (Reg r = 0; r < 32u; ++r) { @@ -3666,23 +3666,6 @@ static const NativeRegInfo aa_reg_info = { .nclasses = sizeof aa_classes / sizeof aa_classes[0], }; -/* Expand the arch-neutral clobber-ABI sets (KitCgAsmClobberAbiSet bits) into - * this target's per-class caller/callee-saved register masks. Forward-declared - * earlier for aa_known_callee_saves; defined here where aa_classes is in scope. - */ -static void aa_abi_clobber_masks(u32 abi_sets, u32* int_mask, u32* fp_mask) { - *int_mask = 0; - *fp_mask = 0; - if (abi_sets & KIT_CG_ASM_CLOBBER_ABI_CALLER_SAVED) { - *int_mask |= aa_classes[NATIVE_REG_INT].caller_saved_mask; - *fp_mask |= aa_classes[NATIVE_REG_FP].caller_saved_mask; - } - if (abi_sets & KIT_CG_ASM_CLOBBER_ABI_CALLEE_SAVED) { - *int_mask |= aa_classes[NATIVE_REG_INT].callee_saved_mask; - *fp_mask |= aa_classes[NATIVE_REG_FP].callee_saved_mask; - } -} - static void aa_va_start_native(NativeTarget* t, NativeLoc ap_ptr); static void aa_va_arg_native(NativeTarget* t, NativeLoc dst, NativeLoc ap_ptr, KitCgTypeId type); @@ -4216,26 +4199,8 @@ static void aa_va_copy_native(NativeTarget* t, NativeLoc dst_ap_ptr, aa_va_addr_from_ptr(src_ap_ptr)); } -AA_UNUSED_FN static const char* aa_asm_constraint_body(const char* s) { - if (!s) return ""; - if (s[0] == '=' && s[1] == '&') return s + 2; - if (s[0] == '=' || s[0] == '+' || s[0] == '&') return s + 1; - return s; -} - -AA_UNUSED_FN static int aa_asm_constraint_early(const char* s) { - if (!s) return 0; - return (s[0] == '=' && s[1] == '&') || s[0] == '&'; -} - -AA_UNUSED_FN static int aa_asm_match_index(const char* s) { - int n = 0; - if (!s || s[0] < '0' || s[0] > '9') return -1; - for (const char* p = s; *p >= '0' && *p <= '9'; ++p) { - n = n * 10 + (*p - '0'); - } - return n; -} +/* constraint_body / constraint_early / match_index are shared + * (cg/native_asm.h). */ _Noreturn static void aa_asm_panic_at(Compiler* c, SrcLoc loc, const char* msg) { @@ -4470,7 +4435,7 @@ static void aa_direct_asm_block(NativeDirectTarget* d, const char* tmpl, AA64Asm* a; aa_asm_clobber_masks(d->base.c, d->loc, clobbers, nclob, &clob_int, &clob_fp); - aa_abi_clobber_masks(clobber_abi_sets, &abi_int, &abi_fp); + native_asm_abi_clobber_masks(d->native, clobber_abi_sets, &abi_int, &abi_fp); clob_int |= abi_int; clob_fp |= abi_fp; used_int = clob_int | (1u << AA_TMP0) | (1u << AA_TMP1) | (1u << 18u) | @@ -4478,7 +4443,7 @@ static void aa_direct_asm_block(NativeDirectTarget* d, const char* tmpl, used_fp = clob_fp | (1u << 20u) | (1u << 21u); for (u32 i = 0; i < nout; ++i) { - const char* body = aa_asm_constraint_body(outs[i].str); + const char* body = native_asm_constraint_body(outs[i].str); if (body[0] == 'r' || body[0] == 'w') { NativeAllocClass cls = aa_asm_constraint_class(d, body); Reg reg = aa_asm_alloc_reg(d, cls, &used_int, &used_fp); @@ -4501,12 +4466,12 @@ static void aa_direct_asm_block(NativeDirectTarget* d, const char* tmpl, } for (u32 i = 0; i < nin; ++i) { - const char* body = aa_asm_constraint_body(ins[i].str); - int matched = aa_asm_match_index(body); + const char* body = native_asm_constraint_body(ins[i].str); + int matched = native_asm_match_index(body); if (matched >= 0) { if ((u32)matched >= nout) aa_asm_panic(d, "matching constraint out of range"); - if (aa_asm_constraint_early(outs[matched].str)) + if (native_asm_constraint_early(outs[matched].str)) aa_asm_panic(d, "matching input names early-clobber output"); if (bound_outs[matched].kind != AA64_INLINE_OPK_REG) aa_asm_panic(d, "matching constraint requires register output"); @@ -4626,7 +4591,7 @@ static Reg aa_asm_native_mem_base(AANativeTarget* a, SrcLoc loc, NativeLoc src, static void aa_asm_bind_native(AANativeTarget* a, SrcLoc loc, Operand* out, const char* constraint, KitCgTypeId type, NativeLoc src, u32* ntmp) { - const char* body = aa_asm_constraint_body(constraint); + const char* body = native_asm_constraint_body(constraint); if (body[0] == 'r' || body[0] == 'w') { NativeAllocClass cls = (body[0] == 'w') ? NATIVE_REG_FP : NATIVE_REG_INT; if (src.kind != NATIVE_LOC_REG) @@ -4665,8 +4630,8 @@ static void aa_asm_block_native(NativeTarget* t, const char* tmpl, &ntmp); } for (u32 i = 0; i < nin; ++i) { - const char* body = aa_asm_constraint_body(ins[i].str); - int matched = aa_asm_match_index(body); + const char* body = native_asm_constraint_body(ins[i].str); + int matched = native_asm_match_index(body); KitCgTypeId type; if (matched >= 0) { if ((u32)matched >= nout) @@ -4676,7 +4641,7 @@ static void aa_asm_block_native(NativeTarget* t, const char* tmpl, } type = ins[i].type ? ins[i].type : in_locs[i].type; { - const char* in_body = aa_asm_constraint_body(ins[i].str); + const char* in_body = native_asm_constraint_body(ins[i].str); NativeLoc inloc = in_locs[i]; /* A register-constrained input whose value is an address-taken local * arrives in a frame slot: the optimizer cannot keep an address-taken diff --git a/src/arch/rv64/native.c b/src/arch/rv64/native.c @@ -1588,18 +1588,8 @@ static void rv_asm_clobber_masks(Compiler* c, SrcLoc loc, const Sym* clobbers, /* Expand the arch-neutral clobber-ABI sets (KitCgAsmClobberAbiSet bits) into * this target's per-class caller/callee-saved register masks. */ -static void rv_abi_clobber_masks(u32 abi_sets, u32* int_mask, u32* fp_mask) { - *int_mask = 0; - *fp_mask = 0; - if (abi_sets & KIT_CG_ASM_CLOBBER_ABI_CALLER_SAVED) { - *int_mask |= rv_classes[NATIVE_REG_INT].caller_saved_mask; - *fp_mask |= rv_classes[NATIVE_REG_FP].caller_saved_mask; - } - if (abi_sets & KIT_CG_ASM_CLOBBER_ABI_CALLEE_SAVED) { - *int_mask |= rv_classes[NATIVE_REG_INT].callee_saved_mask; - *fp_mask |= rv_classes[NATIVE_REG_FP].callee_saved_mask; - } -} +/* abi_clobber_masks is shared as native_asm_abi_clobber_masks + * (cg/native_asm.h); it reads the masks from t->regs->classes. */ /* Build the callee-saved set the prologue must preserve: the allocator-assigned * callee-saved registers (frame->callee_saved_used) plus any an inline-asm @@ -1625,7 +1615,8 @@ static u32 rv_known_callee_saves(NativeTarget* t, rv_asm_clobber_masks(t->c, loc, frame->asm_clobbers, frame->nasm_clobbers, &clob_int, &clob_fp); } - rv_abi_clobber_masks(frame->asm_clobber_abi_sets, &abi_int, &abi_fp); + native_asm_abi_clobber_masks(t, frame->asm_clobber_abi_sets, &abi_int, + &abi_fp); clob_int |= abi_int; clob_fp |= abi_fp; for (Reg r = 0; r < 32u; ++r) { @@ -3045,23 +3036,8 @@ _Noreturn static void rv_asm_panic(NativeDirectTarget* d, const char* msg) { rv_asm_panic_at(d->base.c, d->loc, msg); } -static const char* rv_asm_constraint_body(const char* s) { - if (!s) return ""; - if (s[0] == '=' && s[1] == '&') return s + 2; - if (s[0] == '=' || s[0] == '+' || s[0] == '&') return s + 1; - return s; -} -static int rv_asm_constraint_early(const char* s) { - if (!s) return 0; - return (s[0] == '=' && s[1] == '&') || s[0] == '&'; -} -static int rv_asm_match_index(const char* s) { - int n = 0; - const char* p; - if (!s || s[0] < '0' || s[0] > '9') return -1; - for (p = s; *p >= '0' && *p <= '9'; ++p) n = n * 10 + (*p - '0'); - return n; -} +/* constraint_body / constraint_early / match_index are shared + * (cg/native_asm.h). */ /* Build a bound register pseudo-operand in the rv64 inline shape. */ static void rv_asm_bound_reg(Operand* out, KitCgTypeId type, @@ -3383,7 +3359,7 @@ static Reg rv_asm_native_mem_base(RvNativeTarget* a, SrcLoc loc, NativeLoc src, static void rv_asm_bind_native(RvNativeTarget* a, SrcLoc loc, Operand* out, const char* constraint, KitCgTypeId type, NativeLoc src, u32* ntmp) { - const char* body = rv_asm_constraint_body(constraint); + const char* body = native_asm_constraint_body(constraint); if (body[0] == 'r' || body[0] == 'f') { NativeAllocClass cls = (body[0] == 'f') ? NATIVE_REG_FP : NATIVE_REG_INT; if (src.kind != NATIVE_LOC_REG) @@ -3422,8 +3398,8 @@ static void rv_asm_block_native(NativeTarget* t, const char* tmpl, &ntmp); } for (i = 0; i < nin; ++i) { - const char* body = rv_asm_constraint_body(ins[i].str); - int matched = rv_asm_match_index(body); + const char* body = native_asm_constraint_body(ins[i].str); + int matched = native_asm_match_index(body); KitCgTypeId type; NativeLoc inloc; if (matched >= 0) { @@ -3691,7 +3667,7 @@ static void rv_direct_asm_block(NativeDirectTarget* d, const char* tmpl, Rv64Asm* asmh; rv_asm_clobber_masks(c, d->loc, clobbers, nclob, &clob_int, &clob_fp); - rv_abi_clobber_masks(clobber_abi_sets, &abi_int, &abi_fp); + native_asm_abi_clobber_masks(d->native, clobber_abi_sets, &abi_int, &abi_fp); clob_int |= abi_int; clob_fp |= abi_fp; /* Reserve emit scratch (t0/t1/t2/t3), sp/gp/tp/zero/ra and the frame pointer @@ -3703,7 +3679,7 @@ static void rv_direct_asm_block(NativeDirectTarget* d, const char* tmpl, clob_fp | (1u << RV_FTMP0) | (1u << RV_FTMP1) | (1u << 2u) | (1u << 3u); for (i = 0; i < nout; ++i) { - const char* body = rv_asm_constraint_body(outs[i].str); + const char* body = native_asm_constraint_body(outs[i].str); KitCgTypeId type = outs[i].type ? outs[i].type : out_ops[i].type; if (body[0] == 'r' || body[0] == 'f') { NativeAllocClass cls = rv_asm_constraint_class(d, body); @@ -3724,13 +3700,13 @@ static void rv_direct_asm_block(NativeDirectTarget* d, const char* tmpl, } for (i = 0; i < nin; ++i) { - const char* body = rv_asm_constraint_body(ins[i].str); - int matched = rv_asm_match_index(body); + const char* body = native_asm_constraint_body(ins[i].str); + int matched = native_asm_match_index(body); KitCgTypeId type = ins[i].type ? ins[i].type : in_ops[i].type; if (matched >= 0) { if ((u32)matched >= nout) rv_asm_panic(d, "matching constraint out of range"); - if (rv_asm_constraint_early(outs[matched].str)) + if (native_asm_constraint_early(outs[matched].str)) rv_asm_panic(d, "matching input names early-clobber output"); if (bound_outs[matched].kind != RV64_INLINE_OPK_REG) rv_asm_panic(d, "matching constraint requires register output"); diff --git a/src/arch/x64/native.c b/src/arch/x64/native.c @@ -1680,20 +1680,8 @@ static int x64_reg_is_callee_fp(const X64ABIRegs* abi, Reg r); static void x64_asm_clobber_masks(Compiler* c, SrcLoc loc, const Sym* clobbers, u32 nclob, u32* int_mask, u32* fp_mask); -/* Expand the arch-neutral clobber-ABI sets (KitCgAsmClobberAbiSet bits) into - * this target's per-class caller/callee-saved register masks. */ -static void x64_abi_clobber_masks(u32 abi_sets, u32* int_mask, u32* fp_mask) { - *int_mask = 0; - *fp_mask = 0; - if (abi_sets & KIT_CG_ASM_CLOBBER_ABI_CALLER_SAVED) { - *int_mask |= x64_classes[NATIVE_REG_INT].caller_saved_mask; - *fp_mask |= x64_classes[NATIVE_REG_FP].caller_saved_mask; - } - if (abi_sets & KIT_CG_ASM_CLOBBER_ABI_CALLEE_SAVED) { - *int_mask |= x64_classes[NATIVE_REG_INT].callee_saved_mask; - *fp_mask |= x64_classes[NATIVE_REG_FP].callee_saved_mask; - } -} +/* abi_clobber_masks is shared as native_asm_abi_clobber_masks + * (cg/native_asm.h); it reads the masks from t->regs->classes. */ /* Build the callee-saved set the prologue must preserve: the allocator-assigned * callee-saved registers (frame->callee_saved_used) plus any an inline-asm @@ -1719,7 +1707,8 @@ static u32 x64_known_callee_saves(NativeTarget* t, const X64ABIRegs* abi, x64_asm_clobber_masks(t->c, loc, frame->asm_clobbers, frame->nasm_clobbers, &clob_int, &clob_fp); } - x64_abi_clobber_masks(frame->asm_clobber_abi_sets, &abi_int, &abi_fp); + native_asm_abi_clobber_masks(t, frame->asm_clobber_abi_sets, &abi_int, + &abi_fp); clob_int |= abi_int; clob_fp |= abi_fp; for (Reg r = 0; r < 16u; ++r) { @@ -3519,23 +3508,8 @@ _Noreturn static void x64_asm_panic(NativeDirectTarget* d, const char* msg) { x64_asm_panic_at(d->base.c, d->loc, msg); } -static const char* x64_asm_constraint_body(const char* s) { - if (!s) return ""; - if (s[0] == '=' && s[1] == '&') return s + 2; - if (s[0] == '=' || s[0] == '+' || s[0] == '&') return s + 1; - return s; -} -static int x64_asm_constraint_early(const char* s) { - if (!s) return 0; - return (s[0] == '=' && s[1] == '&') || s[0] == '&'; -} -static int x64_asm_match_index(const char* s) { - int n = 0; - const char* p; - if (!s || s[0] < '0' || s[0] > '9') return -1; - for (p = s; *p >= '0' && *p <= '9'; ++p) n = n * 10 + (*p - '0'); - return n; -} +/* constraint_body / constraint_early / match_index are shared + * (cg/native_asm.h). */ static void x64_asm_bound_reg(Operand* out, KitCgTypeId type, NativeAllocClass cls, Reg reg) { @@ -3850,7 +3824,7 @@ static Reg x64_asm_native_mem_base(X64NativeTarget* a, SrcLoc loc, static void x64_asm_bind_native(X64NativeTarget* a, SrcLoc loc, Operand* out, const char* constraint, KitCgTypeId type, NativeLoc src, u32* ntmp) { - const char* body = x64_asm_constraint_body(constraint); + const char* body = native_asm_constraint_body(constraint); if (body[0] == 'r' || body[0] == 'x') { NativeAllocClass cls = (body[0] == 'x') ? NATIVE_REG_FP : NATIVE_REG_INT; if (src.kind != NATIVE_LOC_REG) @@ -3891,8 +3865,8 @@ static void x64_asm_block_native(NativeTarget* t, const char* tmpl, &ntmp); } for (i = 0; i < nin; ++i) { - const char* body = x64_asm_constraint_body(ins[i].str); - int matched = x64_asm_match_index(body); + const char* body = native_asm_constraint_body(ins[i].str); + int matched = native_asm_match_index(body); KitCgTypeId type; NativeLoc inloc; if (matched >= 0) { @@ -4203,7 +4177,7 @@ static void x64_direct_asm_block(NativeDirectTarget* d, const char* tmpl, X64Asm* asmh; x64_asm_clobber_masks(c, d->loc, clobbers, nclob, &clob_int, &clob_fp); - x64_abi_clobber_masks(clobber_abi_sets, &abi_int, &abi_fp); + native_asm_abi_clobber_masks(d->native, clobber_abi_sets, &abi_int, &abi_fp); clob_int |= abi_int; clob_fp |= abi_fp; /* Reserve emit scratch (rax,r11), driver scratch, sp/bp, and clobbers. */ @@ -4214,7 +4188,7 @@ static void x64_direct_asm_block(NativeDirectTarget* d, const char* tmpl, (1u << (X64_XMM0 + 14)) | (1u << X64_XMM15); for (i = 0; i < nout; ++i) { - const char* body = x64_asm_constraint_body(outs[i].str); + const char* body = native_asm_constraint_body(outs[i].str); KitCgTypeId type = outs[i].type ? outs[i].type : out_ops[i].type; if (body[0] == 'r' || body[0] == 'x') { NativeAllocClass cls = x64_asm_constraint_class(d, body); @@ -4235,13 +4209,13 @@ static void x64_direct_asm_block(NativeDirectTarget* d, const char* tmpl, } for (i = 0; i < nin; ++i) { - const char* body = x64_asm_constraint_body(ins[i].str); - int matched = x64_asm_match_index(body); + const char* body = native_asm_constraint_body(ins[i].str); + int matched = native_asm_match_index(body); KitCgTypeId type = ins[i].type ? ins[i].type : in_ops[i].type; if (matched >= 0) { if ((u32)matched >= nout) x64_asm_panic(d, "matching constraint out of range"); - if (x64_asm_constraint_early(outs[matched].str)) + if (native_asm_constraint_early(outs[matched].str)) x64_asm_panic(d, "matching input names early-clobber output"); if (bound_outs[matched].kind != X64_INLINE_OPK_REG) x64_asm_panic(d, "matching constraint requires register output"); diff --git a/src/cg/native_asm.c b/src/cg/native_asm.c @@ -13,3 +13,38 @@ void native_file_scope_asm(NativeTarget* t, const char* src, size_t len) { void native_finalize(NativeTarget* t) { if (t->mc) mc_emit_eh_frame(t->mc); } + +const char* native_asm_constraint_body(const char* s) { + if (!s) return ""; + if (s[0] == '=' && s[1] == '&') return s + 2; + if (s[0] == '=' || s[0] == '+' || s[0] == '&') return s + 1; + return s; +} + +int native_asm_constraint_early(const char* s) { + if (!s) return 0; + return (s[0] == '=' && s[1] == '&') || s[0] == '&'; +} + +int native_asm_match_index(const char* s) { + int n = 0; + const char* p; + if (!s || s[0] < '0' || s[0] > '9') return -1; + for (p = s; *p >= '0' && *p <= '9'; ++p) n = n * 10 + (*p - '0'); + return n; +} + +void native_asm_abi_clobber_masks(NativeTarget* t, u32 abi_sets, u32* int_mask, + u32* fp_mask) { + const NativeAllocClassInfo* classes = t->regs->classes; + *int_mask = 0; + *fp_mask = 0; + if (abi_sets & KIT_CG_ASM_CLOBBER_ABI_CALLER_SAVED) { + *int_mask |= classes[NATIVE_REG_INT].caller_saved_mask; + *fp_mask |= classes[NATIVE_REG_FP].caller_saved_mask; + } + if (abi_sets & KIT_CG_ASM_CLOBBER_ABI_CALLEE_SAVED) { + *int_mask |= classes[NATIVE_REG_INT].callee_saved_mask; + *fp_mask |= classes[NATIVE_REG_FP].callee_saved_mask; + } +} diff --git a/src/cg/native_asm.h b/src/cg/native_asm.h @@ -17,4 +17,33 @@ void native_file_scope_asm(NativeTarget* t, const char* src, size_t len); /* Emit the function's eh-frame (CFI) once code emission is complete. */ void native_finalize(NativeTarget* t); +/* ---- Inline-asm constraint-string helpers ---- + * + * Pure, target-neutral parsing of a GCC-style operand constraint string. These + * were byte-identical across the aa64/rv64/x64 inline-asm lowering paths, so + * they live here as the single source of truth. They read only the string, + * never any target state. + */ + +/* Skip the leading modifier flags ('=', '+', '&', and the '=&' early-clobber + * pair) and return a pointer to the constraint body (e.g. "r", "w", "m", "i", + * or a matching-constraint digit run). Returns "" for a NULL string. */ +const char* native_asm_constraint_body(const char* s); + +/* Whether the constraint marks an early-clobber output ('=&' or a leading + * '&'). */ +int native_asm_constraint_early(const char* s); + +/* For a matching constraint (a leading decimal digit run naming an output + * operand index), return that index; -1 when the constraint is not a matching + * constraint. */ +int native_asm_match_index(const char* s); + +/* Expand the arch-neutral clobber-ABI sets (KitCgAsmClobberAbiSet bits) into + * this target's per-class caller/callee-saved register masks, read straight + * from the target's register file (t->regs->classes). Byte-identical across the + * backends apart from which register table they consulted, so it lives here. */ +void native_asm_abi_clobber_masks(NativeTarget* t, u32 abi_sets, u32* int_mask, + u32* fp_mask); + #endif