commit 645999b8a7c37d67a91aece265d39b44ac68d229
parent 5da5c8eea63865c3655b4fd54f7893bcc6c56fed
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Thu, 18 Jun 2026 16:26:49 -0700
Clean up inline asm constraint binding
Diffstat:
16 files changed, 500 insertions(+), 532 deletions(-)
diff --git a/include/kit/asm_constraints.h b/include/kit/asm_constraints.h
@@ -0,0 +1,37 @@
+#ifndef KIT_ASM_CONSTRAINTS_H
+#define KIT_ASM_CONSTRAINTS_H
+
+/* Shared parsing for the target-neutral part of GCC-style inline-asm operand
+ * constraints. These helpers know only about generic modifiers and matching
+ * digits; target register letters are resolved by the selected backend. */
+
+static inline const char* kit_cg_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 inline int kit_cg_asm_constraint_early(const char* s) {
+ if (!s) return 0;
+ return (s[0] == '=' && s[1] == '&') || s[0] == '&';
+}
+
+static inline int kit_cg_asm_constraint_match_index(const char* s) {
+ int n;
+ if (!s || s[0] < '0' || s[0] > '9') return -1;
+ n = 0;
+ for (; *s >= '0' && *s <= '9'; ++s) n = n * 10 + (*s - '0');
+ return n;
+}
+
+static inline int kit_cg_asm_constraint_is_decimal(const char* s) {
+ if (!s || s[0] < '0' || s[0] > '9') return 0;
+ while (*s) {
+ if (*s < '0' || *s > '9') return 0;
+ ++s;
+ }
+ return 1;
+}
+
+#endif
diff --git a/include/kit/cg.h b/include/kit/cg.h
@@ -3,6 +3,7 @@
#include <kit/core.h>
#include <kit/object.h>
+#include <kit/asm_constraints.h>
/* ============================================================
* Handles
diff --git a/lang/c/parse/cg.c b/lang/c/parse/cg.c
@@ -748,7 +748,7 @@ void c_cg_frame_or_return_address(Parser* p, int is_return, u32 level) {
void c_cg_inline_asm(Parser* p, const char* tmpl, const AsmConstraint* outs,
u32 nout, const AsmConstraint* ins, u32 nin,
- const Sym* clobbers, u32 nclob) {
+ const Sym* clobbers, u32 nclob, u32 flags) {
KitCgInlineAsm a;
KitCgAsmOperand* o = NULL;
KitCgAsmOperand* in = NULL;
@@ -787,6 +787,7 @@ void c_cg_inline_asm(Parser* p, const char* tmpl, const AsmConstraint* outs,
a.ninputs = nin;
a.clobbers = cl;
a.nclobbers = nclob;
+ a.flags = flags;
kit_cg_inline_asm(p->cg, a);
for (u32 i = 0; i < nout; ++i) {
u32 depth = nout - 1u - i;
diff --git a/lang/c/parse/cg.h b/lang/c/parse/cg.h
@@ -279,6 +279,6 @@ void c_cg_syscall(Parser*, u32 nargs, const Type* long_ty);
void c_cg_frame_or_return_address(Parser*, int is_return, u32 level);
void c_cg_readcyclecounter(Parser*);
void c_cg_inline_asm(Parser*, const char*, const AsmConstraint*, u32,
- const AsmConstraint*, u32, const Sym*, u32);
+ const AsmConstraint*, u32, const Sym*, u32, u32);
#endif
diff --git a/lang/c/parse/parse_stmt.c b/lang/c/parse/parse_stmt.c
@@ -647,12 +647,15 @@ static void parse_asm_stmt(Parser* p) {
AsmOutLValue* out_lvs = NULL;
u32 nout = 0, nin = 0, nclob = 0;
u32 cap_out = 0, cap_in = 0, cap_clob = 0;
+ u32 flags = 0;
int saw_goto = 0;
SrcLoc loc = tok_loc_stmt(p, &p->cur);
for (;;) {
- if (accept_kw_stmt(p, KW_VOLATILE))
+ if (accept_kw_stmt(p, KW_VOLATILE)) {
+ flags |= KIT_CG_ASM_VOLATILE;
continue; /* `volatile` or `__volatile__` */
+ }
break;
}
if (accept_kw_stmt(p, KW_GOTO)) saw_goto = 1;
@@ -838,7 +841,7 @@ static void parse_asm_stmt(Parser* p) {
}
c_cg_set_loc(p, loc);
- c_cg_inline_asm(p, tmpl, outs, nout, ins, nin, clobbers, nclob);
+ c_cg_inline_asm(p, tmpl, outs, nout, ins, nin, clobbers, nclob, flags);
if (nout > 0) {
u32 i;
diff --git a/lang/toy/asm.c b/lang/toy/asm.c
@@ -43,51 +43,6 @@ static int toy_asm_append_clobber(ToyParser* p, ToyAsmClobberList* list,
return 1;
}
-static const char* toy_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 toy_asm_is_decimal_constraint(const char* s) {
- if (!s || s[0] < '0' || s[0] > '9') return 0;
- while (*s) {
- if (*s < '0' || *s > '9') return 0;
- ++s;
- }
- return 1;
-}
-
-static int toy_validate_asm_output_constraint(ToyParser* p,
- const KitCgAsmOperand* op) {
- const char* s = kit_sym_str(p->c, op->constraint).s;
- const char* body = toy_asm_constraint_body(s);
- if (op->dir == KIT_CG_ASM_OUT) {
- if (!s || s[0] != '=' || body[0] != 'r' || body[1] != '\0') {
- toy_error(p, p->cur.loc, "unsupported asm output constraint");
- return 0;
- }
- } else if (op->dir == KIT_CG_ASM_INOUT) {
- if (!s || s[0] != '+' || body[0] != 'r' || body[1] != '\0') {
- toy_error(p, p->cur.loc, "unsupported asm output constraint");
- return 0;
- }
- }
- return 1;
-}
-
-static int toy_validate_asm_input_constraint(ToyParser* p,
- const KitCgAsmOperand* op) {
- const char* s = kit_sym_str(p->c, op->constraint).s;
- if ((s && s[0] && !s[1] && (s[0] == 'r' || s[0] == 'i' || s[0] == 'm')) ||
- toy_asm_is_decimal_constraint(s)) {
- return 1;
- }
- toy_error(p, p->cur.loc, "unsupported asm input constraint");
- return 0;
-}
-
static int toy_parse_asm_output_operand(ToyParser* p,
KitCgAsmOperand* operand) {
KitSym op_name;
@@ -206,7 +161,6 @@ static int toy_parse_asm_outputs(ToyParser* p, ToyAsmOperandList* outputs) {
while (p->cur.kind != TOK_RPAREN && p->cur.kind != TOK_EOF) {
KitCgAsmOperand operand;
if (!toy_parse_asm_output_operand(p, &operand) ||
- !toy_validate_asm_output_constraint(p, &operand) ||
!toy_asm_append_operand(p, outputs, &operand)) {
return 0;
}
@@ -227,7 +181,6 @@ static int toy_parse_asm_inputs(ToyParser* p, ToyAsmOperandList* inputs) {
while (p->cur.kind != TOK_RPAREN && p->cur.kind != TOK_EOF) {
KitCgAsmOperand operand;
if (!toy_parse_asm_input_operand(p, &operand) ||
- !toy_validate_asm_input_constraint(p, &operand) ||
!toy_asm_append_operand(p, inputs, &operand)) {
return 0;
}
diff --git a/src/arch/aa64/native.c b/src/arch/aa64/native.c
@@ -5587,6 +5587,20 @@ static Reg aa_asm_native_mem_base(AANativeTarget* a, SrcLoc loc, NativeLoc src,
return dst;
}
+static Reg aa_asm_stage_reg(AANativeTarget* a, SrcLoc loc,
+ NativeAllocClass cls, u32* nint, u32* nfp) {
+ static const Reg int_regs[] = {AA_TMP0, AA_TMP1};
+ static const Reg fp_regs[] = {20u, 21u};
+ if (cls == NATIVE_REG_FP) {
+ if (*nfp >= (u32)(sizeof fp_regs / sizeof fp_regs[0]))
+ aa_asm_panic_at(a->base.c, loc, "too many staged fp asm operands");
+ return fp_regs[(*nfp)++];
+ }
+ if (*nint >= (u32)(sizeof int_regs / sizeof int_regs[0]))
+ aa_asm_panic_at(a->base.c, loc, "too many staged integer asm operands");
+ return int_regs[(*nint)++];
+}
+
static void aa_asm_load_loc_to_reg(AANativeTarget* a, SrcLoc loc, NativeLoc src,
NativeLoc dst) {
NativeTarget* t = &a->base;
@@ -5617,38 +5631,41 @@ static void aa_asm_store_reg_to_loc(AANativeTarget* a, SrcLoc loc,
aa_mem_for_type(t, src.type, loc_size32(t, 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 = native_asm_constraint_body(constraint);
- NativeAsmConstraintInfo info;
- if (native_asm_constraint_reg_info(&a->base, constraint, &info)) {
- if (src.kind != NATIVE_LOC_REG)
- aa_asm_panic_at(a->base.c, loc, "register asm operand not in a register");
- if (info.fixed_reg != REG_NONE && info.fixed_reg != (Reg)src.v.reg)
- aa_asm_panic_at(a->base.c, loc,
- "fixed-register asm operand in wrong register");
- if (info.allowed_mask &&
- ((Reg)src.v.reg >= 32 ||
- (info.allowed_mask & (1u << (Reg)src.v.reg)) == 0))
- compiler_panic(
- a->base.c, loc,
- "aarch64 inline asm: constraint %s got cls%u reg%u outside %08x",
- constraint, (unsigned)info.cls, (unsigned)src.v.reg,
- (unsigned)info.allowed_mask);
- aa_asm_bound_reg(out, type, info.cls, (Reg)src.v.reg);
- } else if (body[0] == 'i') {
- if (src.kind != NATIVE_LOC_IMM)
- aa_asm_panic_at(a->base.c, loc, "immediate asm operand is not immediate");
- memset(out, 0, sizeof *out);
- out->kind = OPK_IMM;
- out->type = type;
- out->v.imm = src.v.imm;
- } else if (body[0] == 'm') {
- aa_asm_bound_mem(out, type, aa_asm_native_mem_base(a, loc, src, ntmp));
- } else {
- aa_asm_panic_at(a->base.c, loc, "unsupported asm constraint");
- }
+static void aa_asm_native_panic(NativeTarget* t, SrcLoc loc, const char* msg) {
+ aa_asm_panic_at(t->c, loc, msg);
+}
+
+static Reg aa_asm_native_mem_base_hook(NativeTarget* t, SrcLoc loc,
+ NativeLoc src, u32* ntmp) {
+ return aa_asm_native_mem_base(aa_of(t), loc, src, ntmp);
+}
+
+static Reg aa_asm_native_stage_reg_hook(NativeTarget* t, SrcLoc loc,
+ NativeAllocClass cls, u32* ntmp,
+ u32* nstage_int, u32* nstage_fp) {
+ (void)nstage_int;
+ return aa_asm_stage_reg(aa_of(t), loc, cls, ntmp, nstage_fp);
+}
+
+static void aa_asm_native_load_loc_hook(NativeTarget* t, SrcLoc loc,
+ NativeLoc src, NativeLoc dst) {
+ aa_asm_load_loc_to_reg(aa_of(t), loc, src, dst);
+}
+
+static void aa_asm_native_store_loc_hook(NativeTarget* t, SrcLoc loc,
+ NativeLoc dst, NativeLoc src) {
+ aa_asm_store_reg_to_loc(aa_of(t), loc, dst, src);
+}
+
+static void aa_asm_native_run_template_hook(
+ NativeTarget* t, const char* tmpl, const AsmConstraint* outs, u32 nout,
+ Operand* bound_outs, const AsmConstraint* ins, u32 nin, Operand* bound_ins,
+ const Sym* clobbers, u32 nclob) {
+ AA64Asm* asmh = aa64_asm_open(t->c);
+ aa64_inline_bind(asmh, outs, nout, bound_outs, ins, nin, bound_ins, clobbers,
+ nclob);
+ aa64_asm_run_template(asmh, t->mc, tmpl);
+ aa64_asm_close(asmh);
}
static void aa_asm_block_native(NativeTarget* t, const char* tmpl,
@@ -5657,105 +5674,21 @@ static void aa_asm_block_native(NativeTarget* t, const char* tmpl,
u32 nin, const NativeLoc* in_locs,
const Sym* clobbers, u32 nclob) {
AANativeTarget* a = aa_of(t);
- Compiler* c = t->c;
SrcLoc loc = a->func ? a->func->loc : (SrcLoc){0, 0, 0};
- Operand* bound_outs = nout ? arena_zarray(c->tu, Operand, nout) : NULL;
- Operand* bound_ins = nin ? arena_zarray(c->tu, Operand, nin) : NULL;
- u8* staged_outs = nout ? arena_zarray(c->tu, u8, nout) : NULL;
- u32 ntmp = 0;
- AA64Asm* asmh;
-
- for (u32 i = 0; i < nout; ++i) {
- KitCgTypeId type = outs[i].type ? outs[i].type : out_locs[i].type;
- NativeLoc outloc = out_locs[i];
- NativeAsmPinnedLoc pinned = native_asm_prepare_pinned_loc(
- t, outs[i].reg, outs[i].str, type, outloc);
- if (pinned.has_pin) {
- if (pinned.pin_status != NATIVE_ASM_REG_PIN_OK)
- aa_asm_panic_at(c, loc,
- native_asm_pin_status_message(pinned.pin_status));
- if (pinned.wrong_reg)
- aa_asm_panic_at(c, loc, "hard-register asm operand in wrong register");
- outloc = pinned.loc;
- if (pinned.needs_stage) {
- staged_outs[i] = 1u;
- if (outs[i].dir == KIT_CG_ASM_INOUT)
- aa_asm_load_loc_to_reg(a, loc, out_locs[i], outloc);
- }
- }
- aa_asm_bind_native(a, loc, &bound_outs[i], outs[i].str, type, outloc,
- &ntmp);
- }
- for (u32 i = 0; i < nin; ++i) {
- 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)
- aa_asm_panic_at(c, loc, "matching constraint out of range");
- bound_ins[i] = bound_outs[matched];
- continue;
- }
- type = ins[i].type ? ins[i].type : in_locs[i].type;
- {
- const char* in_body = native_asm_constraint_body(ins[i].str);
- NativeAsmConstraintInfo info;
- NativeLoc inloc = in_locs[i];
- NativeAsmPinnedLoc pinned =
- native_asm_prepare_pinned_loc(t, ins[i].reg, ins[i].str, type, inloc);
- /* A register-constrained input whose value is an address-taken local
- * arrives in a frame slot: the optimizer cannot keep an address-taken
- * local live in a register across the block, so the "inputs are already
- * in registers" contract does not hold for it. Load it into a reserved
- * scratch register (as the direct path does) before binding. With no
- * hard pin, only unrestricted integer constraints can use this scratch;
- * restricted register sets must already arrive in an allowed hard
- * register. */
- if (pinned.has_pin) {
- if (pinned.pin_status != NATIVE_ASM_REG_PIN_OK)
- aa_asm_panic_at(c, loc,
- native_asm_pin_status_message(pinned.pin_status));
- if (pinned.wrong_reg)
- aa_asm_panic_at(c, loc,
- "hard-register asm operand in wrong register");
- inloc = pinned.loc;
- if (pinned.needs_stage)
- aa_asm_load_loc_to_reg(a, loc, in_locs[i], inloc);
- } else if (native_asm_constraint_reg_info(t, ins[i].str, &info) &&
- info.cls == NATIVE_REG_INT && info.allowed_mask == 0 &&
- inloc.kind != NATIVE_LOC_REG) {
- Reg r;
- if (ntmp >= 2u) aa_asm_panic_at(c, loc, "too many memory asm operands");
- r = (ntmp == 0u) ? AA_TMP0 : AA_TMP1;
- ntmp++;
- inloc = native_loc_reg(type, NATIVE_REG_INT, r);
- aa_emit_mem(a, 1, inloc, aa_asm_loc_to_addr(a, loc, in_locs[i]),
- aa_mem_for_type(t, type, type_size32(t, type)));
- }
- (void)in_body;
- aa_asm_bind_native(a, loc, &bound_ins[i], ins[i].str, type, inloc, &ntmp);
- }
- }
-
- /* No per-block callee-saved spill here: plan_frame forwarded the asm clobber
- * masks and aa_known_callee_saves folded the callee-saved ones into the
- * function's saved set, so the prologue/epilogue already preserve them. */
- asmh = aa64_asm_open(c);
- aa64_inline_bind(asmh, outs, nout, bound_outs, ins, nin, bound_ins, clobbers,
- nclob);
- aa64_asm_run_template(asmh, t->mc, tmpl);
- aa64_asm_close(asmh);
-
- for (u32 i = 0; i < nout; ++i) {
- NativeAllocClass cls;
- NativeLoc src;
- if (!staged_outs || !staged_outs[i]) continue;
- if (bound_outs[i].kind != AA64_INLINE_OPK_REG) continue;
- cls = bound_outs[i].pad[0] == AA64_INLINE_OPCLS_FP ? NATIVE_REG_FP
- : NATIVE_REG_INT;
- src = native_loc_reg(bound_outs[i].type, cls, (Reg)bound_outs[i].v.local);
- aa_asm_store_reg_to_loc(a, loc, out_locs[i], src);
- }
+ static const NativeAsmNativeHooks hooks = {
+ .opk_reg = AA64_INLINE_OPK_REG,
+ .opcls_fp = AA64_INLINE_OPCLS_FP,
+ .panic = aa_asm_native_panic,
+ .bound_reg = aa_asm_bound_reg,
+ .bound_mem = aa_asm_bound_mem,
+ .mem_base = aa_asm_native_mem_base_hook,
+ .stage_reg = aa_asm_native_stage_reg_hook,
+ .load_loc_to_reg = aa_asm_native_load_loc_hook,
+ .store_reg_to_loc = aa_asm_native_store_loc_hook,
+ .run_template = aa_asm_native_run_template_hook,
+ };
+ native_asm_bind_native_operands(t, loc, tmpl, outs, nout, out_locs, ins, nin,
+ in_locs, clobbers, nclob, &hooks);
}
static const NativeOps aa_direct_ops = {
diff --git a/src/arch/arm32/native.c b/src/arch/arm32/native.c
@@ -269,6 +269,8 @@ static int arm_asm_constraint_reg(const NativeRegInfo* ri, const char* body,
if (allowed_mask_out) *allowed_mask_out = 0;
if ((body[0] == 'r' || body[0] == 'l') && body[1] == '\0') {
if (cls_out) *cls_out = NATIVE_REG_INT;
+ if (body[0] == 'l' && allowed_mask_out)
+ *allowed_mask_out = 0x000000ffu; /* r0..r7, filtered by reg_ok. */
return 1;
}
return 0;
@@ -3506,90 +3508,97 @@ static Reg arm_asm_stage_reg(NativeTarget* t, u32* nstage) {
return r;
}
-/* NativeTarget (optimizer / -O1) inline-asm path: bind operands to registers
- * (no self-allocation — the optimizer pre-allocated and forwarded clobbers via
- * plan_frame), then run the template. The minimal runner needs only the bound
- * register numbers, which arrive in out_locs/in_locs. */
+static void arm_asm_native_panic(NativeTarget* t, SrcLoc loc,
+ const char* msg) {
+ arm_asm_panic_at(t->c, loc, msg);
+}
+
+static Reg arm_asm_native_mem_base_hook(NativeTarget* t, SrcLoc loc,
+ NativeLoc src, u32* ntmp) {
+ Reg base = arm_asm_stage_reg(t, ntmp);
+ NativeAddr ma = arm_asm_in_loc_addr(t, src, src.type);
+ (void)loc;
+ arm_load_addr(
+ t, native_loc_reg(builtin_id(KIT_CG_BUILTIN_I32), NATIVE_REG_INT, base),
+ ma);
+ return base;
+}
+
+static Reg arm_asm_native_stage_reg_hook(NativeTarget* t, SrcLoc loc,
+ NativeAllocClass cls, u32* ntmp,
+ u32* nstage_int, u32* nstage_fp) {
+ (void)loc;
+ (void)nstage_int;
+ (void)nstage_fp;
+ if (cls != NATIVE_REG_INT)
+ arm_asm_panic_at(t->c, arm_of(t)->loc,
+ "floating-point asm operands are unsupported");
+ return arm_asm_stage_reg(t, ntmp);
+}
+
+static void arm_asm_native_load_loc_hook(NativeTarget* t, SrcLoc loc,
+ NativeLoc src, NativeLoc dst) {
+ (void)loc;
+ if (dst.cls != NATIVE_REG_INT)
+ arm_asm_panic_at(t->c, arm_of(t)->loc,
+ "floating-point asm operands are unsupported");
+ if (src.kind == NATIVE_LOC_REG) {
+ if (src.v.reg != dst.v.reg || src.cls != dst.cls) arm_move(t, dst, src);
+ return;
+ }
+ if (src.kind == NATIVE_LOC_IMM) {
+ arm_load_imm(t, dst, src.v.imm);
+ return;
+ }
+ arm_load(t, dst, arm_asm_in_loc_addr(t, src, dst.type),
+ native_mem_for_type(t, dst.type, native_type_size(t, dst.type)));
+}
+
+static void arm_asm_native_store_loc_hook(NativeTarget* t, SrcLoc loc,
+ NativeLoc dst, NativeLoc src) {
+ (void)loc;
+ if (src.cls != NATIVE_REG_INT)
+ arm_asm_panic_at(t->c, arm_of(t)->loc,
+ "floating-point asm operands are unsupported");
+ if (dst.kind == NATIVE_LOC_REG) {
+ if (dst.v.reg != src.v.reg || dst.cls != src.cls) arm_move(t, dst, src);
+ return;
+ }
+ arm_store(t, arm_asm_in_loc_addr(t, dst, src.type), src,
+ native_mem_for_type(t, src.type, native_type_size(t, src.type)));
+}
+
+static void arm_asm_native_run_template_hook(
+ NativeTarget* t, const char* tmpl, const AsmConstraint* outs, u32 nout,
+ Operand* bound_outs, const AsmConstraint* ins, u32 nin, Operand* bound_ins,
+ const Sym* clobbers, u32 nclob) {
+ Arm32Asm* asmh = arm32_asm_open(t->c);
+ arm32_inline_bind(asmh, outs, nout, bound_outs, ins, nin, bound_ins, clobbers,
+ nclob);
+ arm32_asm_run_template(asmh, t->mc, tmpl);
+ arm32_asm_close(asmh);
+}
+
static void arm_asm_block_native(NativeTarget* t, const char* tmpl,
const AsmConstraint* outs, u32 nout,
NativeLoc* out_locs, const AsmConstraint* ins,
u32 nin, const NativeLoc* in_locs,
const Sym* clobbers, u32 nclob) {
- Compiler* c = t->c;
- Operand* bound_outs = nout ? arena_zarray(c->tu, Operand, nout) : NULL;
- Operand* bound_ins = nin ? arena_zarray(c->tu, Operand, nin) : NULL;
- u32 i;
- u32 nstage = 0u; /* reserved scratch (IP, then LR) consumed for staged inputs */
- (void)clobbers;
- (void)nclob;
- for (i = 0; i < nout; ++i) {
- KitCgTypeId type = outs[i].type ? outs[i].type : out_locs[i].type;
- if (out_locs[i].kind != NATIVE_LOC_REG)
- arm_asm_panic_at(c, arm_of(t)->loc,
- "optimizer asm output not in a register");
- arm_asm_bound_reg(&bound_outs[i], type, (NativeAllocClass)out_locs[i].cls,
- (Reg)out_locs[i].v.reg);
- }
- for (i = 0; i < nin; ++i) {
- 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)
- arm_asm_panic_at(c, arm_of(t)->loc, "matching constraint out of range");
- bound_ins[i] = bound_outs[matched];
- continue;
- }
- type = ins[i].type ? ins[i].type : in_locs[i].type;
- if (in_locs[i].kind == NATIVE_LOC_REG) {
- arm_asm_bound_reg(&bound_ins[i], type, (NativeAllocClass)in_locs[i].cls,
- (Reg)in_locs[i].v.reg);
- } else if (body[0] == 'i' && in_locs[i].kind == NATIVE_LOC_IMM) {
- /* Immediate ("i"/"n") constraint: bind the literal value, no register. */
- memset(&bound_ins[i], 0, sizeof bound_ins[i]);
- bound_ins[i].kind = OPK_IMM;
- bound_ins[i].type = type;
- bound_ins[i].v.imm = in_locs[i].v.imm;
- } else if (body[0] == 'm') {
- /* Memory ("m") constraint: bind a base register holding the operand's
- * address. The optimizer hands a computed address (NATIVE_LOC_ADDR) or a
- * frame/stack home; either way materialize the address into a reserved
- * staging scratch and bind it as the memory base. */
- Reg base = arm_asm_stage_reg(t, &nstage);
- NativeAddr ma = arm_asm_in_loc_addr(t, in_locs[i], type);
- arm_load_addr(
- t, native_loc_reg(builtin_id(KIT_CG_BUILTIN_I32), NATIVE_REG_INT, base),
- ma);
- arm_asm_bound_mem(&bound_ins[i], type, base);
- } else if (body[0] == 'r' && body[1] == '\0' && !ins[i].reg) {
- /* Plain register ("r") input whose value is NOT already in a register: an
- * address-stable / frame-resident local (the optimizer cannot keep an
- * address-taken local live in a register across the asm block), or a
- * folded immediate. Stage the VALUE into a reserved scratch and bind that
- * register, mirroring the aa64 native asm path. Only the unrestricted "r"
- * class can use IP/LR; the low-reg "l" class (and hard-register pins) must
- * already arrive in an allowed register, so they fall through to the
- * diagnostic below. */
- Reg sr = arm_asm_stage_reg(t, &nstage);
- NativeLoc sd = native_loc_reg(type, NATIVE_REG_INT, sr);
- if (in_locs[i].kind == NATIVE_LOC_IMM)
- arm_load_imm(t, sd, in_locs[i].v.imm);
- else
- arm_load(t, sd, arm_asm_in_loc_addr(t, in_locs[i], type),
- native_mem_for_type(t, type, native_type_size(t, type)));
- arm_asm_bound_reg(&bound_ins[i], type, NATIVE_REG_INT, sr);
- } else {
- arm_asm_panic_at(c, arm_of(t)->loc,
- "optimizer asm input not in a register");
- }
- }
- {
- Arm32Asm* asmh = arm32_asm_open(c);
- arm32_inline_bind(asmh, outs, nout, bound_outs, ins, nin, bound_ins,
- clobbers, nclob);
- arm32_asm_run_template(asmh, t->mc, tmpl);
- arm32_asm_close(asmh);
- }
+ SrcLoc loc = arm_of(t)->loc;
+ static const NativeAsmNativeHooks hooks = {
+ .opk_reg = ARM32_INLINE_OPK_REG,
+ .opcls_fp = ARM32_INLINE_OPCLS_FP,
+ .panic = arm_asm_native_panic,
+ .bound_reg = arm_asm_bound_reg,
+ .bound_mem = arm_asm_bound_mem,
+ .mem_base = arm_asm_native_mem_base_hook,
+ .stage_reg = arm_asm_native_stage_reg_hook,
+ .load_loc_to_reg = arm_asm_native_load_loc_hook,
+ .store_reg_to_loc = arm_asm_native_store_loc_hook,
+ .run_template = arm_asm_native_run_template_hook,
+ };
+ native_asm_bind_native_operands(t, loc, tmpl, outs, nout, out_locs, ins, nin,
+ in_locs, clobbers, nclob, &hooks);
}
/* ============================ construction ============================ */
diff --git a/src/arch/riscv/native.c b/src/arch/riscv/native.c
@@ -3918,35 +3918,41 @@ static void rv_asm_store_reg_to_loc(RvNativeTarget* a, SrcLoc loc,
native_mem_for_type(t, src.type, rv_asm_reg_mem_size(a, cls, src.type)));
}
-static void rv_asm_bind_native(RvNativeTarget* a, SrcLoc loc, Operand* out,
- const char* constraint, KitCgTypeId type,
- NativeLoc src, u32* ntmp) {
- const char* body = native_asm_constraint_body(constraint);
- NativeAsmConstraintInfo info;
- if (native_asm_constraint_reg_info(&a->base, constraint, &info)) {
- if (src.kind != NATIVE_LOC_REG)
- rv_asm_panic_at(a->base.c, loc, "register asm operand not in a register");
- if (info.fixed_reg != REG_NONE && info.fixed_reg != (Reg)src.v.reg)
- rv_asm_panic_at(a->base.c, loc,
- "fixed-register asm operand in wrong register");
- if (info.allowed_mask &&
- ((Reg)src.v.reg >= 32 ||
- (info.allowed_mask & (1u << (Reg)src.v.reg)) == 0))
- rv_asm_panic_at(a->base.c, loc,
- "register asm operand violates constraint register set");
- rv_asm_bound_reg(out, type, info.cls, (Reg)src.v.reg);
- } else if (body[0] == 'i') {
- if (src.kind != NATIVE_LOC_IMM)
- rv_asm_panic_at(a->base.c, loc, "immediate asm operand is not immediate");
- memset(out, 0, sizeof *out);
- out->kind = OPK_IMM;
- out->type = type;
- out->v.imm = src.v.imm;
- } else if (body[0] == 'm') {
- rv_asm_bound_mem(out, type, rv_asm_native_mem_base(a, loc, src, ntmp));
- } else {
- rv_asm_panic_at(a->base.c, loc, "unsupported asm constraint");
- }
+static void rv_asm_native_panic(NativeTarget* t, SrcLoc loc, const char* msg) {
+ rv_asm_panic_at(t->c, loc, msg);
+}
+
+static Reg rv_asm_native_mem_base_hook(NativeTarget* t, SrcLoc loc,
+ NativeLoc src, u32* ntmp) {
+ return rv_asm_native_mem_base(rv_of(t), loc, src, ntmp);
+}
+
+static Reg rv_asm_native_stage_reg_hook(NativeTarget* t, SrcLoc loc,
+ NativeAllocClass cls, u32* ntmp,
+ u32* nstage_int, u32* nstage_fp) {
+ (void)ntmp;
+ return rv_asm_stage_reg(rv_of(t), loc, cls, nstage_int, nstage_fp);
+}
+
+static void rv_asm_native_load_loc_hook(NativeTarget* t, SrcLoc loc,
+ NativeLoc src, NativeLoc dst) {
+ rv_asm_load_loc_to_reg(rv_of(t), loc, src, dst);
+}
+
+static void rv_asm_native_store_loc_hook(NativeTarget* t, SrcLoc loc,
+ NativeLoc dst, NativeLoc src) {
+ rv_asm_store_reg_to_loc(rv_of(t), loc, dst, src);
+}
+
+static void rv_asm_native_run_template_hook(
+ NativeTarget* t, const char* tmpl, const AsmConstraint* outs, u32 nout,
+ Operand* bound_outs, const AsmConstraint* ins, u32 nin, Operand* bound_ins,
+ const Sym* clobbers, u32 nclob) {
+ Rv64Asm* asmh = rv64_asm_open(t->c);
+ rv64_inline_bind(asmh, outs, nout, bound_outs, ins, nin, bound_ins, clobbers,
+ nclob);
+ rv64_asm_run_template(asmh, t->mc, tmpl);
+ rv64_asm_close(asmh);
}
static void rv_asm_block_native(NativeTarget* t, const char* tmpl,
@@ -3955,99 +3961,21 @@ static void rv_asm_block_native(NativeTarget* t, const char* tmpl,
u32 nin, const NativeLoc* in_locs,
const Sym* clobbers, u32 nclob) {
RvNativeTarget* a = rv_of(t);
- Compiler* c = t->c;
SrcLoc loc = a->func ? a->func->loc : (SrcLoc){0, 0, 0};
- Operand* bound_outs = nout ? arena_zarray(c->tu, Operand, nout) : NULL;
- Operand* bound_ins = nin ? arena_zarray(c->tu, Operand, nin) : NULL;
- u8* staged_outs = nout ? arena_zarray(c->tu, u8, nout) : NULL;
- u32 ntmp = 0, nstage_int = 0, nstage_fp = 0, i;
- Rv64Asm* asmh;
-
- for (i = 0; i < nout; ++i) {
- KitCgTypeId type = outs[i].type ? outs[i].type : out_locs[i].type;
- NativeLoc outloc = out_locs[i];
- NativeAsmConstraintInfo info;
- NativeAsmPinnedLoc pinned = native_asm_prepare_pinned_loc(
- t, outs[i].reg, outs[i].str, type, outloc);
- if (pinned.has_pin) {
- if (pinned.pin_status != NATIVE_ASM_REG_PIN_OK)
- rv_asm_panic_at(c, loc,
- native_asm_pin_status_message(pinned.pin_status));
- if (pinned.wrong_reg)
- rv_asm_panic_at(c, loc, "hard-register asm operand in wrong register");
- outloc = pinned.loc;
- if (pinned.needs_stage) {
- staged_outs[i] = 1u;
- if (outs[i].dir == KIT_CG_ASM_INOUT)
- rv_asm_load_loc_to_reg(a, loc, out_locs[i], outloc);
- }
- } else if (native_asm_constraint_reg_info(t, outs[i].str, &info) &&
- info.allowed_mask == 0 && outloc.kind != NATIVE_LOC_REG) {
- Reg r = rv_asm_stage_reg(a, loc, info.cls, &nstage_int, &nstage_fp);
- outloc = native_loc_reg(type, info.cls, r);
- staged_outs[i] = 1u;
- if (outs[i].dir == KIT_CG_ASM_INOUT)
- rv_asm_load_loc_to_reg(a, loc, out_locs[i], outloc);
- }
- rv_asm_bind_native(a, loc, &bound_outs[i], outs[i].str, type, outloc,
- &ntmp);
- }
- for (i = 0; i < nin; ++i) {
- const char* body = native_asm_constraint_body(ins[i].str);
- int matched = native_asm_match_index(body);
- KitCgTypeId type;
- NativeLoc inloc;
- if (matched >= 0) {
- if ((u32)matched >= nout)
- rv_asm_panic_at(c, loc, "matching constraint out of range");
- bound_ins[i] = bound_outs[matched];
- continue;
- }
- type = ins[i].type ? ins[i].type : in_locs[i].type;
- inloc = in_locs[i];
- {
- NativeAsmConstraintInfo info;
- NativeAsmPinnedLoc pinned =
- native_asm_prepare_pinned_loc(t, ins[i].reg, ins[i].str, type, inloc);
- if (pinned.has_pin) {
- if (pinned.pin_status != NATIVE_ASM_REG_PIN_OK)
- rv_asm_panic_at(c, loc,
- native_asm_pin_status_message(pinned.pin_status));
- if (pinned.wrong_reg)
- rv_asm_panic_at(c, loc,
- "hard-register asm operand in wrong register");
- inloc = pinned.loc;
- if (pinned.needs_stage)
- rv_asm_load_loc_to_reg(a, loc, in_locs[i], inloc);
- } else if (native_asm_constraint_reg_info(t, ins[i].str, &info) &&
- info.allowed_mask == 0 && inloc.kind != NATIVE_LOC_REG) {
- Reg r = rv_asm_stage_reg(a, loc, info.cls, &nstage_int, &nstage_fp);
- inloc = native_loc_reg(type, info.cls, r);
- rv_asm_load_loc_to_reg(a, loc, in_locs[i], inloc);
- }
- }
- rv_asm_bind_native(a, loc, &bound_ins[i], ins[i].str, type, inloc, &ntmp);
- }
-
- /* No per-block callee-saved spill here: plan_frame forwarded the asm clobber
- * masks and rv_known_callee_saves folded the callee-saved ones into the
- * function's saved set, so the prologue/epilogue already preserve them. */
- asmh = rv64_asm_open(c);
- rv64_inline_bind(asmh, outs, nout, bound_outs, ins, nin, bound_ins, clobbers,
- nclob);
- rv64_asm_run_template(asmh, t->mc, tmpl);
- rv64_asm_close(asmh);
-
- for (i = 0; i < nout; ++i) {
- NativeAllocClass cls;
- NativeLoc src;
- if (!staged_outs || !staged_outs[i]) continue;
- if (bound_outs[i].kind != RV64_INLINE_OPK_REG) continue;
- cls = bound_outs[i].pad[0] == RV64_INLINE_OPCLS_FP ? NATIVE_REG_FP
- : NATIVE_REG_INT;
- src = native_loc_reg(bound_outs[i].type, cls, (Reg)bound_outs[i].v.local);
- rv_asm_store_reg_to_loc(a, loc, out_locs[i], src);
- }
+ static const NativeAsmNativeHooks hooks = {
+ .opk_reg = RV64_INLINE_OPK_REG,
+ .opcls_fp = RV64_INLINE_OPCLS_FP,
+ .panic = rv_asm_native_panic,
+ .bound_reg = rv_asm_bound_reg,
+ .bound_mem = rv_asm_bound_mem,
+ .mem_base = rv_asm_native_mem_base_hook,
+ .stage_reg = rv_asm_native_stage_reg_hook,
+ .load_loc_to_reg = rv_asm_native_load_loc_hook,
+ .store_reg_to_loc = rv_asm_native_store_loc_hook,
+ .run_template = rv_asm_native_run_template_hook,
+ };
+ native_asm_bind_native_operands(t, loc, tmpl, outs, nout, out_locs, ins, nin,
+ in_locs, clobbers, nclob, &hooks);
}
/* file_scope_asm + finalize are shared (cg/native_asm.h). */
diff --git a/src/arch/x64/native.c b/src/arch/x64/native.c
@@ -4083,6 +4083,20 @@ static Reg x64_asm_native_mem_base(X64NativeTarget* a, SrcLoc loc,
return dst;
}
+static Reg x64_asm_stage_reg(X64NativeTarget* a, SrcLoc loc,
+ NativeAllocClass cls, u32* nint, u32* nfp) {
+ static const Reg int_regs[] = {X64_TMP_INT, X64_TMP_INT2};
+ static const Reg fp_regs[] = {X64_TMP_FP, X64_TMP_FP2};
+ if (cls == NATIVE_REG_FP) {
+ if (*nfp >= (u32)(sizeof fp_regs / sizeof fp_regs[0]))
+ x64_asm_panic_at(a->base.c, loc, "too many staged fp asm operands");
+ return fp_regs[(*nfp)++];
+ }
+ if (*nint >= (u32)(sizeof int_regs / sizeof int_regs[0]))
+ x64_asm_panic_at(a->base.c, loc, "too many staged integer asm operands");
+ return int_regs[(*nint)++];
+}
+
static void x64_asm_load_loc_to_reg(X64NativeTarget* a, SrcLoc loc,
NativeLoc src, NativeLoc dst) {
NativeTarget* t = &a->base;
@@ -4113,37 +4127,42 @@ static void x64_asm_store_reg_to_loc(X64NativeTarget* a, SrcLoc loc,
native_mem_for_type(t, src.type, loc_size32(t, src)));
}
-static void x64_asm_bind_native(X64NativeTarget* a, SrcLoc loc, Operand* out,
- const char* constraint, KitCgTypeId type,
- NativeLoc src, u32* ntmp) {
- const char* body = native_asm_constraint_body(constraint);
- NativeAsmConstraintInfo info;
- if (native_asm_constraint_reg_info(&a->base, constraint, &info)) {
- if (src.kind != NATIVE_LOC_REG)
- x64_asm_panic_at(a->base.c, loc,
- "register asm operand not in a register");
- if (info.fixed_reg != REG_NONE && info.fixed_reg != (Reg)src.v.reg)
- x64_asm_panic_at(a->base.c, loc,
- "fixed-register asm operand in wrong register");
- if (info.allowed_mask &&
- ((Reg)src.v.reg >= 32 ||
- (info.allowed_mask & (1u << (Reg)src.v.reg)) == 0))
- x64_asm_panic_at(a->base.c, loc,
- "register asm operand violates constraint register set");
- x64_asm_bound_reg(out, type, info.cls, (Reg)src.v.reg);
- } else if (body[0] == 'i') {
- if (src.kind != NATIVE_LOC_IMM)
- x64_asm_panic_at(a->base.c, loc,
- "immediate asm operand is not immediate");
- memset(out, 0, sizeof *out);
- out->kind = OPK_IMM;
- out->type = type;
- out->v.imm = src.v.imm;
- } else if (body[0] == 'm') {
- x64_asm_bound_mem(out, type, x64_asm_native_mem_base(a, loc, src, ntmp));
- } else {
- x64_asm_panic_at(a->base.c, loc, "unsupported asm constraint");
- }
+static void x64_asm_native_panic(NativeTarget* t, SrcLoc loc,
+ const char* msg) {
+ x64_asm_panic_at(t->c, loc, msg);
+}
+
+static Reg x64_asm_native_mem_base_hook(NativeTarget* t, SrcLoc loc,
+ NativeLoc src, u32* ntmp) {
+ return x64_asm_native_mem_base(x64_of(t), loc, src, ntmp);
+}
+
+static Reg x64_asm_native_stage_reg_hook(NativeTarget* t, SrcLoc loc,
+ NativeAllocClass cls, u32* ntmp,
+ u32* nstage_int, u32* nstage_fp) {
+ (void)nstage_int;
+ return x64_asm_stage_reg(x64_of(t), loc, cls, ntmp, nstage_fp);
+}
+
+static void x64_asm_native_load_loc_hook(NativeTarget* t, SrcLoc loc,
+ NativeLoc src, NativeLoc dst) {
+ x64_asm_load_loc_to_reg(x64_of(t), loc, src, dst);
+}
+
+static void x64_asm_native_store_loc_hook(NativeTarget* t, SrcLoc loc,
+ NativeLoc dst, NativeLoc src) {
+ x64_asm_store_reg_to_loc(x64_of(t), loc, dst, src);
+}
+
+static void x64_asm_native_run_template_hook(
+ NativeTarget* t, const char* tmpl, const AsmConstraint* outs, u32 nout,
+ Operand* bound_outs, const AsmConstraint* ins, u32 nin, Operand* bound_ins,
+ const Sym* clobbers, u32 nclob) {
+ X64Asm* asmh = x64_asm_open(t->c);
+ x64_inline_bind(asmh, outs, nout, bound_outs, ins, nin, bound_ins, clobbers,
+ nclob);
+ x64_asm_run_template(asmh, t->mc, tmpl);
+ x64_asm_close(asmh);
}
static void x64_asm_block_native(NativeTarget* t, const char* tmpl,
@@ -4152,94 +4171,21 @@ static void x64_asm_block_native(NativeTarget* t, const char* tmpl,
u32 nin, const NativeLoc* in_locs,
const Sym* clobbers, u32 nclob) {
X64NativeTarget* a = x64_of(t);
- Compiler* c = t->c;
SrcLoc loc = a->func ? a->func->loc : (SrcLoc){0, 0, 0};
- Operand* bound_outs = nout ? arena_zarray(c->tu, Operand, nout) : NULL;
- Operand* bound_ins = nin ? arena_zarray(c->tu, Operand, nin) : NULL;
- u8* staged_outs = nout ? arena_zarray(c->tu, u8, nout) : NULL;
- u32 ntmp = 0, i;
- X64Asm* asmh;
-
- for (i = 0; i < nout; ++i) {
- KitCgTypeId type = outs[i].type ? outs[i].type : out_locs[i].type;
- NativeLoc outloc = out_locs[i];
- NativeAsmPinnedLoc pinned = native_asm_prepare_pinned_loc(
- t, outs[i].reg, outs[i].str, type, outloc);
- if (pinned.has_pin) {
- if (pinned.pin_status != NATIVE_ASM_REG_PIN_OK)
- x64_asm_panic_at(c, loc,
- native_asm_pin_status_message(pinned.pin_status));
- if (pinned.wrong_reg)
- x64_asm_panic_at(c, loc, "hard-register asm operand in wrong register");
- outloc = pinned.loc;
- if (pinned.needs_stage) {
- staged_outs[i] = 1u;
- if (outs[i].dir == KIT_CG_ASM_INOUT)
- x64_asm_load_loc_to_reg(a, loc, out_locs[i], outloc);
- }
- }
- x64_asm_bind_native(a, loc, &bound_outs[i], outs[i].str, type, outloc,
- &ntmp);
- }
- for (i = 0; i < nin; ++i) {
- const char* body = native_asm_constraint_body(ins[i].str);
- int matched = native_asm_match_index(body);
- KitCgTypeId type;
- NativeLoc inloc;
- if (matched >= 0) {
- if ((u32)matched >= nout)
- x64_asm_panic_at(c, loc, "matching constraint out of range");
- bound_ins[i] = bound_outs[matched];
- continue;
- }
- type = ins[i].type ? ins[i].type : in_locs[i].type;
- inloc = in_locs[i];
- {
- NativeAsmPinnedLoc pinned =
- native_asm_prepare_pinned_loc(t, ins[i].reg, ins[i].str, type, inloc);
- if (pinned.has_pin) {
- if (pinned.pin_status != NATIVE_ASM_REG_PIN_OK)
- x64_asm_panic_at(c, loc,
- native_asm_pin_status_message(pinned.pin_status));
- if (pinned.wrong_reg)
- x64_asm_panic_at(c, loc,
- "hard-register asm operand in wrong register");
- inloc = pinned.loc;
- if (pinned.needs_stage)
- x64_asm_load_loc_to_reg(a, loc, in_locs[i], inloc);
- } else if ((body[0] == 'r') && inloc.kind != NATIVE_LOC_REG) {
- Reg r;
- if (ntmp >= 2u)
- x64_asm_panic_at(c, loc, "too many memory asm operands");
- r = (ntmp == 0u) ? (Reg)X64_TMP_INT : (Reg)X64_TMP_INT2;
- ntmp++;
- inloc = native_loc_reg(type, NATIVE_REG_INT, r);
- x64_emit_mem(a, 1, inloc, x64_asm_loc_to_addr(a, loc, in_locs[i]),
- native_mem_for_type(t, type, native_type_size(t, type)));
- }
- }
- x64_asm_bind_native(a, loc, &bound_ins[i], ins[i].str, type, inloc, &ntmp);
- }
-
- /* No per-block callee-saved spill here: plan_frame forwarded the asm clobber
- * masks and x64_known_callee_saves folded the callee-saved ones into the
- * function's saved set, so the prologue/epilogue already preserve them. */
- asmh = x64_asm_open(c);
- x64_inline_bind(asmh, outs, nout, bound_outs, ins, nin, bound_ins, clobbers,
- nclob);
- x64_asm_run_template(asmh, t->mc, tmpl);
- x64_asm_close(asmh);
-
- for (i = 0; i < nout; ++i) {
- NativeAllocClass cls;
- NativeLoc src;
- if (!staged_outs || !staged_outs[i]) continue;
- if (bound_outs[i].kind != X64_INLINE_OPK_REG) continue;
- cls = bound_outs[i].pad[0] == X64_INLINE_OPCLS_FP ? NATIVE_REG_FP
- : NATIVE_REG_INT;
- src = native_loc_reg(bound_outs[i].type, cls, (Reg)bound_outs[i].v.local);
- x64_asm_store_reg_to_loc(a, loc, out_locs[i], src);
- }
+ static const NativeAsmNativeHooks hooks = {
+ .opk_reg = X64_INLINE_OPK_REG,
+ .opcls_fp = X64_INLINE_OPCLS_FP,
+ .panic = x64_asm_native_panic,
+ .bound_reg = x64_asm_bound_reg,
+ .bound_mem = x64_asm_bound_mem,
+ .mem_base = x64_asm_native_mem_base_hook,
+ .stage_reg = x64_asm_native_stage_reg_hook,
+ .load_loc_to_reg = x64_asm_native_load_loc_hook,
+ .store_reg_to_loc = x64_asm_native_store_loc_hook,
+ .run_template = x64_asm_native_run_template_hook,
+ };
+ native_asm_bind_native_operands(t, loc, tmpl, outs, nout, out_locs, ins, nin,
+ in_locs, clobbers, nclob, &hooks);
}
/* file_scope_asm + finalize are shared (cg/native_asm.h). */
diff --git a/src/cg/asm.c b/src/cg/asm.c
@@ -8,25 +8,15 @@ const char* api_sym_cstr(KitCg* g, KitSym sym) {
}
int api_asm_parse_match_index(const char* s) {
- int n;
- if (!s || s[0] < '0' || s[0] > '9') return -1;
- n = 0;
- for (const char* p = s; *p >= '0' && *p <= '9'; ++p) {
- n = n * 10 + (*p - '0');
- }
- return n;
+ return kit_cg_asm_constraint_match_index(s);
}
const char* api_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;
+ return kit_cg_asm_constraint_body(s);
}
int api_asm_is_early_clobber(const char* s) {
- if (!s) return 0;
- return (s[0] == '=' && s[1] == '&') || s[0] == '&';
+ return kit_cg_asm_constraint_early(s);
}
/* Does this constraint body name a register operand (one that binds to a temp
@@ -99,8 +89,12 @@ void kit_cg_inline_asm(KitCg* g, KitCgInlineAsm asm_block) {
const KitSym* clobbers = asm_block.clobbers;
uint32_t nclobbers = asm_block.nclobbers;
uint32_t clobber_abi_sets = asm_block.clobber_abi_sets;
- (void)asm_block.flags;
+ uint32_t flags = asm_block.flags;
if (!g) return;
+ if (flags & ~KIT_CG_ASM_VOLATILE) {
+ compiler_panic(g->c, g->cur_loc,
+ "KitCg: unsupported inline asm flags");
+ }
if (api_unevaluated(g)) {
uint32_t ninout_u = 0;
for (u32 i = 0; i < noutputs; ++i)
diff --git a/src/cg/native_asm.c b/src/cg/native_asm.c
@@ -1,5 +1,7 @@
#include "cg/native_asm.h"
+#include <kit/asm_constraints.h>
+
#include "arch/mc.h"
#include "asm/asm.h"
#include "asm/asm_lex.h"
@@ -19,23 +21,15 @@ void native_finalize(NativeTarget* t) {
}
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;
+ return kit_cg_asm_constraint_body(s);
}
int native_asm_constraint_early(const char* s) {
- if (!s) return 0;
- return (s[0] == '=' && s[1] == '&') || s[0] == '&';
+ return kit_cg_asm_constraint_early(s);
}
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;
+ return kit_cg_asm_constraint_match_index(s);
}
void native_asm_abi_clobber_masks(NativeTarget* t, u32 abi_sets, u32* int_mask,
@@ -378,3 +372,128 @@ void native_asm_bind_direct_operands(NativeDirectTarget* d, const char* tmpl,
}
for (i = nsaved; i > 0; --i) h->restore_one(d, saved, i - 1u);
}
+
+static void native_asm_bind_native_one(NativeTarget* t, SrcLoc loc,
+ Operand* out, const char* constraint,
+ KitCgTypeId type, NativeLoc src,
+ u32* ntmp,
+ const NativeAsmNativeHooks* h) {
+ const char* body = native_asm_constraint_body(constraint);
+ NativeAsmConstraintInfo info;
+ if (native_asm_constraint_reg_info(t, constraint, &info)) {
+ if (src.kind != NATIVE_LOC_REG)
+ h->panic(t, loc, "register asm operand not in a register");
+ if (info.fixed_reg != REG_NONE && info.fixed_reg != (Reg)src.v.reg)
+ h->panic(t, loc, "fixed-register asm operand in wrong register");
+ if (info.allowed_mask &&
+ ((Reg)src.v.reg >= 32 ||
+ (info.allowed_mask & (1u << (Reg)src.v.reg)) == 0))
+ h->panic(t, loc, "register asm operand violates constraint register set");
+ h->bound_reg(out, type, info.cls, (Reg)src.v.reg);
+ } else if (body[0] == 'i') {
+ if (src.kind != NATIVE_LOC_IMM)
+ h->panic(t, loc, "immediate asm operand is not immediate");
+ memset(out, 0, sizeof *out);
+ out->kind = OPK_IMM;
+ out->type = type;
+ out->v.imm = src.v.imm;
+ } else if (body[0] == 'm') {
+ h->bound_mem(out, type, h->mem_base(t, loc, src, ntmp));
+ } else {
+ h->panic(t, loc, "unsupported asm constraint");
+ }
+}
+
+void native_asm_bind_native_operands(
+ NativeTarget* t, SrcLoc loc, const char* tmpl, const AsmConstraint* outs,
+ u32 nout, NativeLoc* out_locs, const AsmConstraint* ins, u32 nin,
+ const NativeLoc* in_locs, const Sym* clobbers, u32 nclob,
+ const NativeAsmNativeHooks* h) {
+ Compiler* c = t->c;
+ Operand* bound_outs = nout ? arena_zarray(c->tu, Operand, nout) : NULL;
+ Operand* bound_ins = nin ? arena_zarray(c->tu, Operand, nin) : NULL;
+ u8* staged_outs = nout ? arena_zarray(c->tu, u8, nout) : NULL;
+ u32 ntmp = 0, nstage_int = 0, nstage_fp = 0;
+
+ for (u32 i = 0; i < nout; ++i) {
+ KitCgTypeId type = outs[i].type ? outs[i].type : out_locs[i].type;
+ NativeLoc outloc = out_locs[i];
+ NativeAsmConstraintInfo info;
+ NativeAsmPinnedLoc pinned =
+ native_asm_prepare_pinned_loc(t, outs[i].reg, outs[i].str, type, outloc);
+ if (pinned.has_pin) {
+ if (pinned.pin_status != NATIVE_ASM_REG_PIN_OK)
+ h->panic(t, loc, native_asm_pin_status_message(pinned.pin_status));
+ if (pinned.wrong_reg)
+ h->panic(t, loc, "hard-register asm operand in wrong register");
+ outloc = pinned.loc;
+ if (pinned.needs_stage) {
+ staged_outs[i] = 1u;
+ if (outs[i].dir == KIT_CG_ASM_INOUT)
+ h->load_loc_to_reg(t, loc, out_locs[i], outloc);
+ }
+ } else if (native_asm_constraint_reg_info(t, outs[i].str, &info) &&
+ info.allowed_mask == 0 && outloc.kind != NATIVE_LOC_REG) {
+ Reg r =
+ h->stage_reg(t, loc, info.cls, &ntmp, &nstage_int, &nstage_fp);
+ outloc = native_loc_reg(type, info.cls, r);
+ staged_outs[i] = 1u;
+ if (outs[i].dir == KIT_CG_ASM_INOUT)
+ h->load_loc_to_reg(t, loc, out_locs[i], outloc);
+ }
+ native_asm_bind_native_one(t, loc, &bound_outs[i], outs[i].str, type,
+ outloc, &ntmp, h);
+ }
+
+ for (u32 i = 0; i < nin; ++i) {
+ const char* body = native_asm_constraint_body(ins[i].str);
+ int matched = native_asm_match_index(body);
+ KitCgTypeId type;
+ NativeLoc inloc;
+ if (matched >= 0) {
+ if ((u32)matched >= nout)
+ h->panic(t, loc, "matching constraint out of range");
+ if (native_asm_constraint_early(outs[matched].str))
+ h->panic(t, loc, "matching input names early-clobber output");
+ bound_ins[i] = bound_outs[matched];
+ continue;
+ }
+ type = ins[i].type ? ins[i].type : in_locs[i].type;
+ inloc = in_locs[i];
+ {
+ NativeAsmConstraintInfo info;
+ NativeAsmPinnedLoc pinned =
+ native_asm_prepare_pinned_loc(t, ins[i].reg, ins[i].str, type, inloc);
+ if (pinned.has_pin) {
+ if (pinned.pin_status != NATIVE_ASM_REG_PIN_OK)
+ h->panic(t, loc, native_asm_pin_status_message(pinned.pin_status));
+ if (pinned.wrong_reg)
+ h->panic(t, loc, "hard-register asm operand in wrong register");
+ inloc = pinned.loc;
+ if (pinned.needs_stage)
+ h->load_loc_to_reg(t, loc, in_locs[i], inloc);
+ } else if (native_asm_constraint_reg_info(t, ins[i].str, &info) &&
+ info.allowed_mask == 0 && inloc.kind != NATIVE_LOC_REG) {
+ Reg r =
+ h->stage_reg(t, loc, info.cls, &ntmp, &nstage_int, &nstage_fp);
+ inloc = native_loc_reg(type, info.cls, r);
+ h->load_loc_to_reg(t, loc, in_locs[i], inloc);
+ }
+ }
+ native_asm_bind_native_one(t, loc, &bound_ins[i], ins[i].str, type, inloc,
+ &ntmp, h);
+ }
+
+ h->run_template(t, tmpl, outs, nout, bound_outs, ins, nin, bound_ins, clobbers,
+ nclob);
+
+ for (u32 i = 0; i < nout; ++i) {
+ NativeAllocClass cls;
+ NativeLoc src;
+ if (!staged_outs || !staged_outs[i]) continue;
+ if (bound_outs[i].kind != h->opk_reg) continue;
+ cls = bound_outs[i].pad[0] == h->opcls_fp ? NATIVE_REG_FP : NATIVE_REG_INT;
+ src = native_loc_reg(bound_outs[i].type, cls, (Reg)bound_outs[i].v.local);
+ h->store_reg_to_loc(t, loc, out_locs[i], src);
+ }
+}
diff --git a/src/cg/native_asm.h b/src/cg/native_asm.h
@@ -19,10 +19,9 @@ 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.
+ * Pure, target-neutral parsing of a GCC-style operand constraint string.
+ * Definitions wrap the shared public inline helpers in kit/asm_constraints.h so
+ * frontend, CG, native, and test code all use the same grammar.
*/
/* Skip the leading modifier flags ('=', '+', '&', and the '=&' early-clobber
@@ -167,4 +166,40 @@ void native_asm_bind_direct_operands(NativeDirectTarget* d, const char* tmpl,
u32 clobber_abi_sets,
const NativeAsmDirectHooks* h);
+/* ---- Optimized NativeTarget inline-asm binding ----
+ *
+ * The optimizer allocates the operand locations before final native emission.
+ * This shared driver validates those locations against the same
+ * NativeAsmConstraintInfo used by regalloc, stages frame-resident unrestricted
+ * register operands through reserved scratch registers, binds memory operands to
+ * concrete base registers, runs the arch assembler, and stores staged outputs
+ * back. */
+typedef struct NativeAsmNativeHooks {
+ u8 opk_reg;
+ u8 opcls_fp;
+ u8 pad[2];
+
+ void (*panic)(NativeTarget* t, SrcLoc loc, const char* msg);
+ void (*bound_reg)(Operand* out, KitCgTypeId type, NativeAllocClass cls,
+ Reg reg);
+ void (*bound_mem)(Operand* out, KitCgTypeId type, Reg base);
+ Reg (*mem_base)(NativeTarget* t, SrcLoc loc, NativeLoc src, u32* ntmp);
+ Reg (*stage_reg)(NativeTarget* t, SrcLoc loc, NativeAllocClass cls, u32* ntmp,
+ u32* nstage_int, u32* nstage_fp);
+ void (*load_loc_to_reg)(NativeTarget* t, SrcLoc loc, NativeLoc src,
+ NativeLoc dst);
+ void (*store_reg_to_loc)(NativeTarget* t, SrcLoc loc, NativeLoc dst,
+ NativeLoc src);
+ void (*run_template)(NativeTarget* t, const char* tmpl,
+ const AsmConstraint* outs, u32 nout, Operand* bound_outs,
+ const AsmConstraint* ins, u32 nin, Operand* bound_ins,
+ const Sym* clobbers, u32 nclob);
+} NativeAsmNativeHooks;
+
+void native_asm_bind_native_operands(
+ NativeTarget* t, SrcLoc loc, const char* tmpl, const AsmConstraint* outs,
+ u32 nout, NativeLoc* out_locs, const AsmConstraint* ins, u32 nin,
+ const NativeLoc* in_locs, const Sym* clobbers, u32 nclob,
+ const NativeAsmNativeHooks* h);
+
#endif
diff --git a/src/opt/pass_machinize.c b/src/opt/pass_machinize.c
@@ -54,6 +54,15 @@ static void asm_prepare_constraints(Func* f, NativeTarget* target,
if (native_resolve_reg(target, nm, &r, &cls) != 0) continue;
if ((u32)cls < OPT_REG_CLASSES && r < 32) aux->clobber_mask[cls] |= 1u << r;
}
+ if (aux->clobber_abi_sets) {
+ u32 int_mask, fp_mask;
+ native_asm_abi_clobber_masks(target, aux->clobber_abi_sets, &int_mask,
+ &fp_mask);
+ if (NATIVE_REG_INT < OPT_REG_CLASSES)
+ aux->clobber_mask[NATIVE_REG_INT] |= int_mask;
+ if (NATIVE_REG_FP < OPT_REG_CLASSES)
+ aux->clobber_mask[NATIVE_REG_FP] |= fp_mask;
+ }
for (u32 i = 0; i < aux->nout; ++i) {
NativeAsmRegPin pin;
NativeAsmRegPinStatus st = native_asm_resolve_pin(target, aux->outs[i].reg,
diff --git a/src/opt/pass_native_emit.c b/src/opt/pass_native_emit.c
@@ -1521,9 +1521,9 @@ static void plan_frame(NativeEmitCtx* e, const CGFuncDesc* fd) {
}
}
/* Gather the union of every asm block's clobber names and hard-register
- * operand pins. The backend resolves them with its own clobber parser
- * (machinize's resolve_name is unset on every backend, so aux->clobber_mask
- * is unreliable here). */
+ * operand pins for known-frame callee-save reservation. Keep the raw names:
+ * each backend's frame planner uses its clobber parser so diagnostics and
+ * ABI exclusions match final emission. */
if (nasm_clob) {
u32 n = 0;
asm_clobbers = arena_array(e->f->arena, Sym, nasm_clob);
diff --git a/test/toy/err/invalid_asm_input_constraint.toy b/test/toy/err/invalid_asm_input_constraint.toy
@@ -1,4 +1,4 @@
fn main(): i64 {
- @asm<void>("", outputs(), inputs(in("x", 1)));
+ @asm<void>("", outputs(), inputs(in("?", 1)));
return 0;
}