w6c+wwstage: aggregate arg from any non-ident source via the closed addr machinery (#271) — close aggregate-arg family
Passing an aggregate BY VALUE as a call argument worked ONLY for a ≤16B struct from an IDENT source; every non-ident source — CALL mk(), N_DOT o.f, N_INDEX a[i], DEREF *p — and every array / >24B-struct (even as an ident) fell to the scalar default: one PUSHQ for a multi-word aggregate, stack-imbalancing against the type-based multi-word drain. cs!=ww, both garbage (f(mk()) cs4/ww236, f(o.f) cs8/ww108, f(a[i]) cs4/ww28, f(*p) cs4/ww140; arrays + 32B sret struct same). The arg-pass twin of the #265/#268 let-init copy. A new aggregate-arg push arm materialises the source into the arg convention: the source ADDRESS in SI (ident LEAQ / deref operand / dotchainaddr #253 / &base[i] spine #252-270) then its ceil(sz/8) words pushed high→low; a CALL receives first — ≤24B in AX/DX/CX pushed straight, >24B sret'd into a per-fn @aggargscr then pushed from there. The pop-forward drain gained a matching array / >16B-struct arm and the callee prologue an is_bigagg receive (ceil(sz/8) GP eightbytes), so caller and callee agree on the multi-word layout. The ≤16B-struct-IDENT fast path is untouched (byte-id preserved). The new-arm exclusion is TYPE-keyed (the stamped tinfo, mirroring cstage node_isstructarg over args[i]->type), not the name-keyed structparamsize — a name-keyed gate re-opened the #211/#13 cross-module same-leaf collision (784 symmetric: an 8B `sa.s` struct whose name-resolution collides with `sb.s = *vtable` would miss the struct fast path and wrongly enter the new arm, diverging from cstage's 1-word push). A float-bearing ≤16B struct from a non-ident source loud-stops in both stages (the #165 SSE eightbyte transport the GP push/drain can't model; out of scope). A const array/struct `def` global as an aggregate arg is aligned DOWN to the leaner wwstage (both loud-stop) per rule-10. #110: cgen is compiler-imported by w6c + wwdump — main.combined.ww regen'd for both. 949 rows: arg_{struct16,arr16,struct32}_{call,dot,idx,deref,ident}, full member readback (struct 16B reg-class + 32B sret-class + array [4]u32, each non-ident source + ident control); byteid=1 throughout (master both-broken-and-divergent → converge on the correct full push, #263). All 111 dotbaseaddr + 3/3 784 pass; test-unit 241 green; sizelint + smoke OK; the full w6c compiler source (214705 asm lines) self-compiles cs==ww byte-id.
This commit is contained in:
228
cmd/w6c/cgen.c
228
cmd/w6c/cgen.c
@@ -57,6 +57,11 @@ static int cg_tupfscr;
|
||||
* arg); 0 means "not yet allocated", cg_tupargscr_sz the cached width. */
|
||||
static int cg_tupargscr;
|
||||
static int cg_tupargscr_sz;
|
||||
/* #271: per-fn @aggargscr scratch for a >24B (sret-class) aggregate
|
||||
* arg sourced from a CALL — the result is sret'd here, then pushed
|
||||
* word-by-word into the arg convention. 0 = not yet allocated. */
|
||||
static int cg_aggargscr;
|
||||
static int cg_aggargscr_sz;
|
||||
/* Per-fn @-prefix scratch SSoT (task #26, follow-up to #15-cstage's
|
||||
* @retscr). Pre-#26 each site allocated a labelseq-stamped fresh slot
|
||||
* per call (mklabel "tagbase" / "tagscr" / "argscr" / "idxscr"); the
|
||||
@@ -598,6 +603,28 @@ node_isstructarg(Node *n)
|
||||
return sz > 0 && sz <= 16;
|
||||
}
|
||||
|
||||
/* aggarg_size — byte size of a by-value aggregate (struct OR array)
|
||||
* call arg, else 0. The size axis the ≤16B-struct node_isstructarg
|
||||
* carve-out doesn't cover: arrays of any size and structs > 16B (#271).
|
||||
* Pure-int transport only; a float-bearing struct keeps the #165 SSE
|
||||
* eightbyte path (gated separately at the push/drain sites). */
|
||||
static int
|
||||
aggarg_size(Type *t)
|
||||
{
|
||||
if (t == NULL) return 0;
|
||||
if (t->kind == TY_NAMED) t = t->under;
|
||||
if (t == NULL) return 0;
|
||||
if (t->kind == TY_STRUCT || t->kind == TY_ARRAY)
|
||||
return (int)t->size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
node_isaggarg(Node *n)
|
||||
{
|
||||
return n && aggarg_size(n->type) > 0;
|
||||
}
|
||||
|
||||
/* Pick the appropriate scalar SSE opcode (SS vs SD) for a node's
|
||||
* float type. Untyped float defaults to SD. */
|
||||
static int
|
||||
@@ -1852,6 +1879,70 @@ cg_dotbase_addr(Cg *c, Node *base, int dst_reg, Local *locals)
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
/* aggarg_srcaddr — land the ADDRESS of an addressable aggregate arg
|
||||
* source in `dst`, reusing the closed #265/#268 let-init-copy dispatch:
|
||||
* ident/global slot (LEAQ), deref operand (cgexpr of the pointer),
|
||||
* N_DOT field (cg_dotchain_addr, #253), N_INDEX element (the &base[i]
|
||||
* spine, #252/#270). Returns 0 for a source kind not covered (caller
|
||||
* loud-stops, rule 7). The CALL source is handled separately at the
|
||||
* push site (receive-to-regs / sret-to-scratch). */
|
||||
static int
|
||||
aggarg_srcaddr(Cg *c, Node *src, int dst, Local *locals)
|
||||
{
|
||||
if (src->kind == N_UN && src->op == TK_STAR) {
|
||||
cgexpr(c, src->lhs, locals);
|
||||
if (dst != D_AX)
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(dst));
|
||||
return 1;
|
||||
}
|
||||
if (src->kind == N_IDENT) {
|
||||
int soff = localfind(locals, src->str);
|
||||
if (soff != 0) {
|
||||
ins2(c, A_LEAQ, amem(D_BP, soff), areg(dst));
|
||||
return 1;
|
||||
}
|
||||
/* global value source. Gated to a module-`let` (let_islet,
|
||||
* the wwstage letvartnode twin); a const array/struct `def`
|
||||
* aggregate ARG is untested and out of scope (#274) — both
|
||||
* stages fall through to the caller's loud-stop, aligned DOWN
|
||||
* to the leaner wwstage per rule-10. */
|
||||
if (let_islet(src->str)) {
|
||||
ins2(c, A_LEAQ, masym(c, src->str), areg(dst));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (src->kind == N_DOT)
|
||||
return cg_dotchain_addr(c, src, dst, locals);
|
||||
if (src->kind == N_INDEX) {
|
||||
Node *base = src->lhs;
|
||||
Node *idx = src->rhs;
|
||||
Type *bt = base ? base->type : NULL;
|
||||
Type *bu = (bt && bt->kind == TY_NAMED) ? bt->under : bt;
|
||||
if (base && base->kind == N_IDENT && bu
|
||||
&& bu->kind == TY_ARRAY) {
|
||||
int esz = (bu->sub) ? (int)bu->sub->size : 1;
|
||||
cgexpr(c, idx, locals);
|
||||
if (esz > 1) {
|
||||
ins2(c, A_MOVQ, aimm(esz), areg(D_CX));
|
||||
ins2(c, A_IMULQ, areg(D_CX), areg(D_AX));
|
||||
}
|
||||
int boff = localfind(locals, base->str);
|
||||
if (boff != 0)
|
||||
ins2(c, A_LEAQ, amem(D_BP, boff), areg(D_BX));
|
||||
else
|
||||
ins2(c, A_LEAQ, masym(c, base->str),
|
||||
areg(D_BX));
|
||||
ins2(c, A_ADDQ, areg(D_BX), areg(D_AX));
|
||||
if (dst != D_AX)
|
||||
ins2(c, A_MOVQ, areg(D_AX), areg(dst));
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* cg_structlit_fill modes — see helper docstring. */
|
||||
enum {
|
||||
DST_BP = 0,
|
||||
@@ -6397,6 +6488,103 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/* #271: aggregate (struct/array) arg from any source the
|
||||
* ≤16B-struct-IDENT fast path above doesn't cover — a
|
||||
* 16B struct from a non-ident source, OR any array, OR a
|
||||
* struct > 16B. The arg twin of the #265/#268 let-init
|
||||
* copy: materialise the source's ADDRESS in SI and push
|
||||
* its ceil(sz/8) words high→low (the pop drains word0
|
||||
* into the first arg reg). A CALL source receives first —
|
||||
* ≤24B in AX/DX/CX pushed straight, >24B sret'd into
|
||||
* @aggargscr then pushed from there. Pre-fix every such
|
||||
* source fell to the scalar default (one PUSHQ for a
|
||||
* multi-word aggregate) and stack-imbalanced against the
|
||||
* type-based multi-word drain. */
|
||||
if (!widen[i] && node_isaggarg(args[i])
|
||||
&& !(node_isstructarg(args[i])
|
||||
&& args[i]->kind == N_IDENT)) {
|
||||
int aggsz = aggarg_size(args[i]->type);
|
||||
int nwords = (aggsz + 7) / 8;
|
||||
/* A float-bearing ≤16B struct from a non-ident
|
||||
* source would need the #165 SSE eightbyte
|
||||
* transport the GP push/drain here can't model —
|
||||
* loud-stop rather than silently GP-pass it (a
|
||||
* ≤16B struct with any float field; the wwstage
|
||||
* tinfo mirror uses the same predicate). */
|
||||
{
|
||||
Type *st = args[i]->type;
|
||||
if (st && st->kind == TY_NAMED)
|
||||
st = st->under;
|
||||
if (st && st->kind == TY_STRUCT
|
||||
&& st->size <= 16) {
|
||||
int f32;
|
||||
for (Tfield *f = st->fields; f;
|
||||
f = f->next)
|
||||
if (fld_isfloat(f->type,
|
||||
&f32))
|
||||
fatal("#271/#165: "
|
||||
"float-bearing "
|
||||
"struct arg from a "
|
||||
"non-ident source "
|
||||
"needs SSE eightbyte "
|
||||
"transport (out of "
|
||||
"scope)");
|
||||
}
|
||||
}
|
||||
if (args[i]->kind == N_CALL) {
|
||||
if (cg_sret_retsize(args[i]->type) > 0) {
|
||||
if (cg_aggargscr == 0) {
|
||||
cg_aggargscr =
|
||||
local_alloc(c, &locals,
|
||||
"@aggargscr", aggsz,
|
||||
cg_frame);
|
||||
cg_aggargscr_sz = aggsz;
|
||||
} else if (aggsz >
|
||||
cg_aggargscr_sz) {
|
||||
fatal("cgcall: @aggargscr "
|
||||
"cached sz %d, need %d "
|
||||
"(#271 pinned offset "
|
||||
"can't grow)",
|
||||
cg_aggargscr_sz,
|
||||
aggsz);
|
||||
}
|
||||
cg_sret_dest_off = cg_aggargscr;
|
||||
cgexpr(c, args[i], locals);
|
||||
cg_sret_dest_off = 0;
|
||||
for (int k = nwords - 1; k >= 0;
|
||||
k--) {
|
||||
ins2(c, A_MOVQ,
|
||||
amem(D_BP,
|
||||
cg_aggargscr + k*8),
|
||||
areg(D_AX));
|
||||
ins1(c, A_PUSHQ,
|
||||
areg(D_AX));
|
||||
}
|
||||
} else {
|
||||
/* ≤24B: producer left AX=word0,
|
||||
* DX=word1, CX=word2. Push
|
||||
* high→low so the pop drains
|
||||
* word0 first. */
|
||||
int rr[3] = { D_AX, D_DX, D_CX };
|
||||
cgexpr(c, args[i], locals);
|
||||
for (int k = nwords - 1; k >= 0;
|
||||
k--)
|
||||
ins1(c, A_PUSHQ,
|
||||
areg(rr[k]));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (!aggarg_srcaddr(c, args[i], D_SI, locals))
|
||||
fatal("#271: aggregate arg from "
|
||||
"unsupported source kind %d",
|
||||
args[i]->kind);
|
||||
for (int k = nwords - 1; k >= 0; k--) {
|
||||
ins2(c, A_MOVQ, amem(D_SI, k*8),
|
||||
areg(D_AX));
|
||||
ins1(c, A_PUSHQ, areg(D_AX));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (widen[i]) {
|
||||
/* Concrete → tagged-union widening at the call
|
||||
* site. Mirrors the let/assign/return widening:
|
||||
@@ -6687,6 +6875,24 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
stackslots++;
|
||||
}
|
||||
}
|
||||
} else if (node_isaggarg(args[i])
|
||||
&& !node_isstructarg(args[i])) {
|
||||
/* #271: array / >16B-struct aggregate arg —
|
||||
* drain its ceil(sz/8) staged words into the
|
||||
* INTEGER arg cursor (overflow spills to the
|
||||
* stack, reached by the callee via positive BP
|
||||
* offsets). The ≤16B struct case stays in
|
||||
* node_isstructarg above (SSE class path
|
||||
* intact). */
|
||||
int aggsz = aggarg_size(args[i]->type);
|
||||
int nw = (aggsz + 7) / 8;
|
||||
for (int k = 0; k < nw; k++) {
|
||||
if (ii < 6)
|
||||
ins1(c, A_POPQ,
|
||||
areg(sysv_argregs[ii++]));
|
||||
else
|
||||
stackslots++;
|
||||
}
|
||||
} else if (node_istaggedarg(args[i])) {
|
||||
int sz = tagged_arg_size(args[i]->type);
|
||||
int eb = sz / 8;
|
||||
@@ -10284,6 +10490,8 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
cg_tupfscr = 0;
|
||||
cg_tupargscr = 0;
|
||||
cg_tupargscr_sz = 0;
|
||||
cg_aggargscr = 0;
|
||||
cg_aggargscr_sz = 0;
|
||||
cg_tagbase = 0;
|
||||
cg_tagbase_sz = 0;
|
||||
cg_tagscr = 0;
|
||||
@@ -10343,6 +10551,13 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
int slice = (pu && pu->kind == TY_SLICE);
|
||||
int is_str = type_isstr(pt);
|
||||
int is_struct = pu && pu->kind == TY_STRUCT && pu->size <= 16;
|
||||
/* #271: a by-value array param, or a struct param > 16B —
|
||||
* received as ceil(sz/8) GP eightbytes, the callee twin of the
|
||||
* generalised aggregate-arg push. The ≤16B struct keeps its own
|
||||
* (possibly SSE-classified) path above. */
|
||||
int is_bigagg = pu && ((pu->kind == TY_ARRAY)
|
||||
|| (pu->kind == TY_STRUCT && pu->size > 16));
|
||||
int agg_eb = is_bigagg ? (int)((pu->size + 7) / 8) : 0;
|
||||
int tagged_sz = tagged_arg_size(pt);
|
||||
int is_tagged = tagged_sz > 0;
|
||||
int isf = cg_isfloat(pt);
|
||||
@@ -10448,7 +10663,8 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
* — the caller pushes the triple (#1/Phase 3). */
|
||||
int eightbytes = (slice || is_str) ? 3 :
|
||||
(is_struct ? struct_eb :
|
||||
(is_tagged ? tagged_eb : 1));
|
||||
(is_bigagg ? agg_eb :
|
||||
(is_tagged ? tagged_eb : 1)));
|
||||
int regs_left = isf ? (8 - fargi) : (6 - argi);
|
||||
if (regs_left >= eightbytes) {
|
||||
/* #60: route slice/str slot widths through Type.size SSoT
|
||||
@@ -10456,9 +10672,10 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
* this site (or its stack-stitch mirror below). */
|
||||
int sz = (slice || is_str) ? (int)pu->size :
|
||||
(is_struct ? (int)pu->size :
|
||||
(is_tagged ? tagged_sz : 8));
|
||||
(is_bigagg ? (int)pu->size :
|
||||
(is_tagged ? tagged_sz : 8)));
|
||||
int off = localoff(c, &locals, p->str, sz, &frame);
|
||||
if (slice || is_str || is_struct || is_tagged) {
|
||||
if (slice || is_str || is_struct || is_bigagg || is_tagged) {
|
||||
for (int k = 0; k < eightbytes; k++, argi++)
|
||||
ins2(c, A_MOVQ,
|
||||
areg(sysv_argregs[argi]),
|
||||
@@ -10476,7 +10693,7 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
argi++;
|
||||
}
|
||||
} else if (eightbytes > 1 && regs_left > 0 &&
|
||||
(slice || is_str || is_struct || is_tagged)) {
|
||||
(slice || is_str || is_struct || is_bigagg || is_tagged)) {
|
||||
/* Multi-word arg that partially fits in regs: caller
|
||||
* filled (regs_left) registers greedily, the rest spilled
|
||||
* to stack at positive BP offsets. Stitch a single local
|
||||
@@ -10486,7 +10703,8 @@ cgfn(Cg *c, FILE *out, Node *fn)
|
||||
/* #60: same SSoT routing as the regs-fit arm above. */
|
||||
int sz = (slice || is_str) ? (int)pu->size :
|
||||
(is_struct ? (int)pu->size :
|
||||
(is_tagged ? tagged_sz : 8));
|
||||
(is_bigagg ? (int)pu->size :
|
||||
(is_tagged ? tagged_sz : 8)));
|
||||
int off = localoff(c, &locals, p->str, sz, &frame);
|
||||
extern int cg_stack_arg_cursor;
|
||||
int k = 0;
|
||||
|
||||
@@ -15976,6 +15976,93 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// #271: aggregate (struct/array) arg from any source the ≤16B
|
||||
// struct-IDENT fast path above doesn't cover — a 16B struct from a
|
||||
// non-ident source, OR any array, OR a struct > 16B. The arg twin
|
||||
// of the #265/#268 let-init copy (mirror of cstage cgen.c #271 push
|
||||
// arm): materialise the source ADDRESS in SI and push its ceil(sz/8)
|
||||
// words high→low (the pop drains word0 into the first arg reg). A
|
||||
// CALL source receives first — ≤24B in AX/DX/CX pushed straight,
|
||||
// >24B sret'd into @aggargscr then pushed from there. Pre-fix every
|
||||
// such source fell to the scalar default (one PUSHQ for a multi-word
|
||||
// aggregate) and stack-imbalanced against the type-based drain.
|
||||
let aggsz: i32 = aggargsizetn(arg.type_: *tinfo);
|
||||
if (aggsz > 0) {
|
||||
// Exclude a ≤16B-struct IDENT — it owns the structparamsize
|
||||
// fast path above (or, when a cross-module same-leaf collision
|
||||
// makes the name-keyed structparamsize miss it, the scalar
|
||||
// default below, byte-id with cstage's 1-word struct push;
|
||||
// #784/#223). The exclusion is TYPE-keyed via the stamped
|
||||
// tinfo, mirroring cstage node_isstructarg (struct_arg_size on
|
||||
// args[i]->type) — a name-keyed gate here re-opens the #211/#13
|
||||
// name-keyed divergence the cstage type gate doesn't have.
|
||||
let structident: bool = false;
|
||||
if (arg.kind == nkind.N_IDENT) {
|
||||
let st: *tinfo = arg.type_: *tinfo;
|
||||
for (st != nil && st.kind == tykind.TY_NAMED) {
|
||||
st = st.under;
|
||||
};
|
||||
if (st != nil) { if (st.kind == tykind.TY_STRUCT) {
|
||||
if (st.size: i32 <= 16) { structident = true; };
|
||||
}; };
|
||||
};
|
||||
if (!structident) {
|
||||
if (aggargfloatstop(arg)) {
|
||||
let msg: str = "#271/#165: float-bearing struct arg from a non-ident source needs SSE eightbyte transport (out of scope)\n";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let nwords: i32 = (aggsz + 7) / 8;
|
||||
if (arg.kind == nkind.N_CALL) {
|
||||
if (callsretsize(c, arg) > 0) {
|
||||
let scr: i32 = localadd(c, "@aggargscr",
|
||||
aggsz, nil);
|
||||
c.sretdestoff = scr;
|
||||
cgexpr(c, arg);
|
||||
c.sretdestoff = 0;
|
||||
let k: i32 = nwords - 1;
|
||||
for (k >= 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scr + k*8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
k -= 1;
|
||||
};
|
||||
} else {
|
||||
// ≤24B: producer left AX=word0,
|
||||
// DX=word1, CX=word2. Push high→low so
|
||||
// the pop drains word0 first.
|
||||
cgexpr(c, arg);
|
||||
let k: i32 = nwords - 1;
|
||||
for (k >= 0) {
|
||||
if (k == 2) {
|
||||
emitline("\tPUSHQ\tCX\n");
|
||||
} else { if (k == 1) {
|
||||
emitline("\tPUSHQ\tDX\n");
|
||||
} else {
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
}; };
|
||||
k -= 1;
|
||||
};
|
||||
};
|
||||
return rest + nwords;
|
||||
};
|
||||
if (!aggargsrcaddr(c, arg, "SI")) {
|
||||
let msg: str = "#271: aggregate arg from unsupported source kind\n";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let k: i32 = nwords - 1;
|
||||
for (k >= 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((k*8): i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
k -= 1;
|
||||
};
|
||||
return rest + nwords;
|
||||
};
|
||||
};
|
||||
// Float arg: cgexpr leaves the value in X0. Push 8 bytes from
|
||||
// X0 via SUBQ+MOVSD so cgcall's pop side can drain into the
|
||||
// XMM stream (X0..X7). f32 still occupies 8B on the stack —
|
||||
@@ -17562,6 +17649,47 @@ fn structparamsize(c: *cgen, t: *node) i32 = {
|
||||
return si.totsize;
|
||||
};
|
||||
|
||||
// aggargsize — byte size of a by-value aggregate (struct OR array) call
|
||||
// arg, else 0 (#271, mirror of cstage aggarg_size). The size axis the
|
||||
// ≤16B-struct structparamsize carve-out doesn't cover: arrays of any
|
||||
// size and structs > 16B. Reads the stamped tinfo size (the type table,
|
||||
// byte-id with cstage Type.size).
|
||||
fn aggargsizetn(t: *tinfo) i32 = {
|
||||
if (t == nil) { return 0; };
|
||||
let u: *tinfo = t;
|
||||
for (u != nil && u.kind == tykind.TY_NAMED) { u = u.under; };
|
||||
if (u == nil) { return 0; };
|
||||
if (u.kind == tykind.TY_STRUCT || u.kind == tykind.TY_ARRAY) {
|
||||
return u.size: i32;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
fn nodeisaggarg(n: *node) bool = {
|
||||
if (n == nil) { return false; };
|
||||
return aggargsizetn(n.type_: *tinfo) > 0;
|
||||
};
|
||||
|
||||
// aggargfloatstop — true iff the arg is a ≤16B struct with any float
|
||||
// field (#271/#165). Such a struct from a non-ident source would need
|
||||
// the SSE eightbyte transport the GP aggregate push/drain can't model;
|
||||
// both stages loud-stop on it. Same predicate as the cstage Tfield
|
||||
// fld_isfloat walk (cmd/w6c/cgen.c #271 push arm).
|
||||
fn aggargfloatstop(n: *node) bool = {
|
||||
if (n == nil) { return false; };
|
||||
let st: *tinfo = n.type_: *tinfo;
|
||||
for (st != nil && st.kind == tykind.TY_NAMED) { st = st.under; };
|
||||
if (st == nil) { return false; };
|
||||
if (st.kind != tykind.TY_STRUCT) { return false; };
|
||||
if (st.size: i32 > 16) { return false; };
|
||||
let f: *tfield = st.fields;
|
||||
for (f != nil) {
|
||||
if (typeisfloat(f.type_)) { return true; };
|
||||
f = f.tnext;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// structfloatclass — SysV per-eightbyte classification for the #165
|
||||
// float-bearing-struct param case (param twin of #171's struct return;
|
||||
// classifies per-eightbyte, not #163's per-element). Returns 0 when the
|
||||
@@ -20626,6 +20754,92 @@ fn dotbaseaddr(c: *cgen, base: *node, dstreg: str) bool = {
|
||||
return true;
|
||||
};
|
||||
|
||||
// aggargsrcaddr — land the ADDRESS of an addressable aggregate (struct/
|
||||
// array) call-arg source in dstreg (#271, mirror of cstage
|
||||
// aggarg_srcaddr). Reuses the closed #265/#268 let-init-copy dispatch:
|
||||
// local ident slot (LEAQ off(BP)), module-let global (LEAQ name(SB)),
|
||||
// deref operand (cgexpr of the pointer), N_DOT field (dotchainaddr,
|
||||
// #253), N_INDEX element of an N_IDENT array base (the &base[i] spine,
|
||||
// #252/#270). Returns false for an uncovered source kind (caller loud-
|
||||
// stops, rule 7). The CALL source is handled at the push site.
|
||||
fn aggargsrcaddr(c: *cgen, src: *node, dst: str) bool = {
|
||||
if (src.kind == nkind.N_UN) {
|
||||
if (src.op == tkind.TK_STAR) {
|
||||
cgexpr(c, src.lhs);
|
||||
if (!streq(dst, "AX")) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
};
|
||||
return true;
|
||||
};
|
||||
};
|
||||
if (src.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, src.str);
|
||||
if (lc != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
// global value source. Gated to a module-`let` (letvartnode,
|
||||
// the cstage let_islet twin); a const array/struct `def`
|
||||
// aggregate ARG is untested + out of scope (#274; both stages
|
||||
// loud-stop, rule-10 aligned).
|
||||
if (letvartnode(c, src.str) != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, src.str);
|
||||
emitline("(SB), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
if (src.kind == nkind.N_DOT) {
|
||||
return dotchainaddr(c, src, dst);
|
||||
};
|
||||
if (src.kind == nkind.N_INDEX) {
|
||||
let base: *node = src.lhs;
|
||||
let idx: *node = src.rhs;
|
||||
if (base == nil) { return false; };
|
||||
if (base.kind != nkind.N_IDENT) { return false; };
|
||||
let bu: *tinfo = base.type_: *tinfo;
|
||||
for (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
|
||||
if (bu == nil) { return false; };
|
||||
if (bu.kind != tykind.TY_ARRAY) { return false; };
|
||||
let esz: i32 = 1;
|
||||
if (bu.sub != nil) { esz = bu.sub.size: i32; };
|
||||
cgexpr(c, idx);
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
let boff: *local = localfindnode(c, base.str);
|
||||
if (boff != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(boff.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, base.str);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
emitline("\tADDQ\tBX, AX\n");
|
||||
if (!streq(dst, "AX")) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
};
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn cgindex(c: *cgen, n: *node) void = {
|
||||
// Element-size-aware load: u8 → MOVZBQ, i32 → MOVSXD, u32 → MOVL,
|
||||
// str → (ptr, len) into (AX, BX), everything else → MOVQ. Fast
|
||||
@@ -24004,6 +24218,13 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
// words and shift intidx out of sync.
|
||||
let tcs: i32 = taggedcallslot(c, a);
|
||||
if (tcs > 0) { extra = tcs / 8 - 1; };
|
||||
// #271: array / >16B-struct / non-ident 16B-struct
|
||||
// aggregate arg — pushargsrev staged ceil(sz/8) words;
|
||||
// drain exactly that many so intidx tracks per-arg
|
||||
// (the ≤16B struct IDENT case is the stfc branch
|
||||
// above). Mirror of cstage node_isaggarg drain arm.
|
||||
let aggsz: i32 = aggargsizetn(a.type_: *tinfo);
|
||||
if (aggsz > 0) { extra = (aggsz + 7) / 8 - 1; };
|
||||
let words: i32 = 1 + extra;
|
||||
let w: i32 = 0;
|
||||
for (w < words) {
|
||||
@@ -30870,18 +31091,66 @@ fn cgfnparams(c: *cgen, params: *node) void = {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += nw;
|
||||
};};
|
||||
} else {
|
||||
if (idx < 6) {
|
||||
let off: i32 = localadd(c, nm, 8, p.lhs);
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
} else { let aggsz2: i32 = aggargsizetn(p.lhs.type_: *tinfo);
|
||||
if (aggsz2 > 0) {
|
||||
// #271: array / >16B-struct by-value param —
|
||||
// received as ceil(sz/8) GP eightbytes, the
|
||||
// callee twin of the generalised aggregate-arg
|
||||
// push. Mirror of the cstage is_bigagg arm
|
||||
// (regs-fit / partial-stitch / stack-spill).
|
||||
let nw2: i32 = (aggsz2 + 7) / 8;
|
||||
if (idx + nw2 <= 6) {
|
||||
let off: i32 = localadd(c, nm, aggsz2, p.lhs);
|
||||
let w: i32 = 0;
|
||||
for (w < nw2) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + w*8): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
w += 1;
|
||||
};
|
||||
} else { if (idx < 6) {
|
||||
let off: i32 = localadd(c, nm, aggsz2, p.lhs);
|
||||
let regs_left: i32 = 6 - idx;
|
||||
let w: i32 = 0;
|
||||
for (w < regs_left) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + w*8): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
w += 1;
|
||||
};
|
||||
for (w < nw2) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((16 + stkcursor*8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + w*8): i64);
|
||||
emitline("(BP)\n");
|
||||
stkcursor += 1;
|
||||
w += 1;
|
||||
};
|
||||
} else {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += nw2;
|
||||
};};
|
||||
} else {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += 1;
|
||||
if (idx < 6) {
|
||||
let off: i32 = localadd(c, nm, 8, p.lhs);
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
} else {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};};};
|
||||
|
||||
@@ -424,18 +424,66 @@ fn cgfnparams(c: *cgen, params: *node) void = {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += nw;
|
||||
};};
|
||||
} else {
|
||||
if (idx < 6) {
|
||||
let off: i32 = localadd(c, nm, 8, p.lhs);
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
} else { let aggsz2: i32 = aggargsizetn(p.lhs.type_: *tinfo);
|
||||
if (aggsz2 > 0) {
|
||||
// #271: array / >16B-struct by-value param —
|
||||
// received as ceil(sz/8) GP eightbytes, the
|
||||
// callee twin of the generalised aggregate-arg
|
||||
// push. Mirror of the cstage is_bigagg arm
|
||||
// (regs-fit / partial-stitch / stack-spill).
|
||||
let nw2: i32 = (aggsz2 + 7) / 8;
|
||||
if (idx + nw2 <= 6) {
|
||||
let off: i32 = localadd(c, nm, aggsz2, p.lhs);
|
||||
let w: i32 = 0;
|
||||
for (w < nw2) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + w*8): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
w += 1;
|
||||
};
|
||||
} else { if (idx < 6) {
|
||||
let off: i32 = localadd(c, nm, aggsz2, p.lhs);
|
||||
let regs_left: i32 = 6 - idx;
|
||||
let w: i32 = 0;
|
||||
for (w < regs_left) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + w*8): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
w += 1;
|
||||
};
|
||||
for (w < nw2) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((16 + stkcursor*8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + w*8): i64);
|
||||
emitline("(BP)\n");
|
||||
stkcursor += 1;
|
||||
w += 1;
|
||||
};
|
||||
} else {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += nw2;
|
||||
};};
|
||||
} else {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += 1;
|
||||
if (idx < 6) {
|
||||
let off: i32 = localadd(c, nm, 8, p.lhs);
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
} else {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};};};
|
||||
|
||||
@@ -1128,6 +1128,92 @@ fn dotbaseaddr(c: *cgen, base: *node, dstreg: str) bool = {
|
||||
return true;
|
||||
};
|
||||
|
||||
// aggargsrcaddr — land the ADDRESS of an addressable aggregate (struct/
|
||||
// array) call-arg source in dstreg (#271, mirror of cstage
|
||||
// aggarg_srcaddr). Reuses the closed #265/#268 let-init-copy dispatch:
|
||||
// local ident slot (LEAQ off(BP)), module-let global (LEAQ name(SB)),
|
||||
// deref operand (cgexpr of the pointer), N_DOT field (dotchainaddr,
|
||||
// #253), N_INDEX element of an N_IDENT array base (the &base[i] spine,
|
||||
// #252/#270). Returns false for an uncovered source kind (caller loud-
|
||||
// stops, rule 7). The CALL source is handled at the push site.
|
||||
fn aggargsrcaddr(c: *cgen, src: *node, dst: str) bool = {
|
||||
if (src.kind == nkind.N_UN) {
|
||||
if (src.op == tkind.TK_STAR) {
|
||||
cgexpr(c, src.lhs);
|
||||
if (!streq(dst, "AX")) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
};
|
||||
return true;
|
||||
};
|
||||
};
|
||||
if (src.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, src.str);
|
||||
if (lc != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
// global value source. Gated to a module-`let` (letvartnode,
|
||||
// the cstage let_islet twin); a const array/struct `def`
|
||||
// aggregate ARG is untested + out of scope (#274; both stages
|
||||
// loud-stop, rule-10 aligned).
|
||||
if (letvartnode(c, src.str) != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, src.str);
|
||||
emitline("(SB), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
if (src.kind == nkind.N_DOT) {
|
||||
return dotchainaddr(c, src, dst);
|
||||
};
|
||||
if (src.kind == nkind.N_INDEX) {
|
||||
let base: *node = src.lhs;
|
||||
let idx: *node = src.rhs;
|
||||
if (base == nil) { return false; };
|
||||
if (base.kind != nkind.N_IDENT) { return false; };
|
||||
let bu: *tinfo = base.type_: *tinfo;
|
||||
for (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
|
||||
if (bu == nil) { return false; };
|
||||
if (bu.kind != tykind.TY_ARRAY) { return false; };
|
||||
let esz: i32 = 1;
|
||||
if (bu.sub != nil) { esz = bu.sub.size: i32; };
|
||||
cgexpr(c, idx);
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
let boff: *local = localfindnode(c, base.str);
|
||||
if (boff != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(boff.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, base.str);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
emitline("\tADDQ\tBX, AX\n");
|
||||
if (!streq(dst, "AX")) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
};
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn cgindex(c: *cgen, n: *node) void = {
|
||||
// Element-size-aware load: u8 → MOVZBQ, i32 → MOVSXD, u32 → MOVL,
|
||||
// str → (ptr, len) into (AX, BX), everything else → MOVQ. Fast
|
||||
@@ -4506,6 +4592,13 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
// words and shift intidx out of sync.
|
||||
let tcs: i32 = taggedcallslot(c, a);
|
||||
if (tcs > 0) { extra = tcs / 8 - 1; };
|
||||
// #271: array / >16B-struct / non-ident 16B-struct
|
||||
// aggregate arg — pushargsrev staged ceil(sz/8) words;
|
||||
// drain exactly that many so intidx tracks per-arg
|
||||
// (the ≤16B struct IDENT case is the stfc branch
|
||||
// above). Mirror of cstage node_isaggarg drain arm.
|
||||
let aggsz: i32 = aggargsizetn(a.type_: *tinfo);
|
||||
if (aggsz > 0) { extra = (aggsz + 7) / 8 - 1; };
|
||||
let words: i32 = 1 + extra;
|
||||
let w: i32 = 0;
|
||||
for (w < words) {
|
||||
|
||||
@@ -466,6 +466,93 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// #271: aggregate (struct/array) arg from any source the ≤16B
|
||||
// struct-IDENT fast path above doesn't cover — a 16B struct from a
|
||||
// non-ident source, OR any array, OR a struct > 16B. The arg twin
|
||||
// of the #265/#268 let-init copy (mirror of cstage cgen.c #271 push
|
||||
// arm): materialise the source ADDRESS in SI and push its ceil(sz/8)
|
||||
// words high→low (the pop drains word0 into the first arg reg). A
|
||||
// CALL source receives first — ≤24B in AX/DX/CX pushed straight,
|
||||
// >24B sret'd into @aggargscr then pushed from there. Pre-fix every
|
||||
// such source fell to the scalar default (one PUSHQ for a multi-word
|
||||
// aggregate) and stack-imbalanced against the type-based drain.
|
||||
let aggsz: i32 = aggargsizetn(arg.type_: *tinfo);
|
||||
if (aggsz > 0) {
|
||||
// Exclude a ≤16B-struct IDENT — it owns the structparamsize
|
||||
// fast path above (or, when a cross-module same-leaf collision
|
||||
// makes the name-keyed structparamsize miss it, the scalar
|
||||
// default below, byte-id with cstage's 1-word struct push;
|
||||
// #784/#223). The exclusion is TYPE-keyed via the stamped
|
||||
// tinfo, mirroring cstage node_isstructarg (struct_arg_size on
|
||||
// args[i]->type) — a name-keyed gate here re-opens the #211/#13
|
||||
// name-keyed divergence the cstage type gate doesn't have.
|
||||
let structident: bool = false;
|
||||
if (arg.kind == nkind.N_IDENT) {
|
||||
let st: *tinfo = arg.type_: *tinfo;
|
||||
for (st != nil && st.kind == tykind.TY_NAMED) {
|
||||
st = st.under;
|
||||
};
|
||||
if (st != nil) { if (st.kind == tykind.TY_STRUCT) {
|
||||
if (st.size: i32 <= 16) { structident = true; };
|
||||
}; };
|
||||
};
|
||||
if (!structident) {
|
||||
if (aggargfloatstop(arg)) {
|
||||
let msg: str = "#271/#165: float-bearing struct arg from a non-ident source needs SSE eightbyte transport (out of scope)\n";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let nwords: i32 = (aggsz + 7) / 8;
|
||||
if (arg.kind == nkind.N_CALL) {
|
||||
if (callsretsize(c, arg) > 0) {
|
||||
let scr: i32 = localadd(c, "@aggargscr",
|
||||
aggsz, nil);
|
||||
c.sretdestoff = scr;
|
||||
cgexpr(c, arg);
|
||||
c.sretdestoff = 0;
|
||||
let k: i32 = nwords - 1;
|
||||
for (k >= 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scr + k*8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
k -= 1;
|
||||
};
|
||||
} else {
|
||||
// ≤24B: producer left AX=word0,
|
||||
// DX=word1, CX=word2. Push high→low so
|
||||
// the pop drains word0 first.
|
||||
cgexpr(c, arg);
|
||||
let k: i32 = nwords - 1;
|
||||
for (k >= 0) {
|
||||
if (k == 2) {
|
||||
emitline("\tPUSHQ\tCX\n");
|
||||
} else { if (k == 1) {
|
||||
emitline("\tPUSHQ\tDX\n");
|
||||
} else {
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
}; };
|
||||
k -= 1;
|
||||
};
|
||||
};
|
||||
return rest + nwords;
|
||||
};
|
||||
if (!aggargsrcaddr(c, arg, "SI")) {
|
||||
let msg: str = "#271: aggregate arg from unsupported source kind\n";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let k: i32 = nwords - 1;
|
||||
for (k >= 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((k*8): i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
k -= 1;
|
||||
};
|
||||
return rest + nwords;
|
||||
};
|
||||
};
|
||||
// Float arg: cgexpr leaves the value in X0. Push 8 bytes from
|
||||
// X0 via SUBQ+MOVSD so cgcall's pop side can drain into the
|
||||
// XMM stream (X0..X7). f32 still occupies 8B on the stack —
|
||||
@@ -2052,6 +2139,47 @@ fn structparamsize(c: *cgen, t: *node) i32 = {
|
||||
return si.totsize;
|
||||
};
|
||||
|
||||
// aggargsize — byte size of a by-value aggregate (struct OR array) call
|
||||
// arg, else 0 (#271, mirror of cstage aggarg_size). The size axis the
|
||||
// ≤16B-struct structparamsize carve-out doesn't cover: arrays of any
|
||||
// size and structs > 16B. Reads the stamped tinfo size (the type table,
|
||||
// byte-id with cstage Type.size).
|
||||
fn aggargsizetn(t: *tinfo) i32 = {
|
||||
if (t == nil) { return 0; };
|
||||
let u: *tinfo = t;
|
||||
for (u != nil && u.kind == tykind.TY_NAMED) { u = u.under; };
|
||||
if (u == nil) { return 0; };
|
||||
if (u.kind == tykind.TY_STRUCT || u.kind == tykind.TY_ARRAY) {
|
||||
return u.size: i32;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
fn nodeisaggarg(n: *node) bool = {
|
||||
if (n == nil) { return false; };
|
||||
return aggargsizetn(n.type_: *tinfo) > 0;
|
||||
};
|
||||
|
||||
// aggargfloatstop — true iff the arg is a ≤16B struct with any float
|
||||
// field (#271/#165). Such a struct from a non-ident source would need
|
||||
// the SSE eightbyte transport the GP aggregate push/drain can't model;
|
||||
// both stages loud-stop on it. Same predicate as the cstage Tfield
|
||||
// fld_isfloat walk (cmd/w6c/cgen.c #271 push arm).
|
||||
fn aggargfloatstop(n: *node) bool = {
|
||||
if (n == nil) { return false; };
|
||||
let st: *tinfo = n.type_: *tinfo;
|
||||
for (st != nil && st.kind == tykind.TY_NAMED) { st = st.under; };
|
||||
if (st == nil) { return false; };
|
||||
if (st.kind != tykind.TY_STRUCT) { return false; };
|
||||
if (st.size: i32 > 16) { return false; };
|
||||
let f: *tfield = st.fields;
|
||||
for (f != nil) {
|
||||
if (typeisfloat(f.type_)) { return true; };
|
||||
f = f.tnext;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// structfloatclass — SysV per-eightbyte classification for the #165
|
||||
// float-bearing-struct param case (param twin of #171's struct return;
|
||||
// classifies per-eightbyte, not #163's per-element). Returns 0 when the
|
||||
|
||||
@@ -15976,6 +15976,93 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// #271: aggregate (struct/array) arg from any source the ≤16B
|
||||
// struct-IDENT fast path above doesn't cover — a 16B struct from a
|
||||
// non-ident source, OR any array, OR a struct > 16B. The arg twin
|
||||
// of the #265/#268 let-init copy (mirror of cstage cgen.c #271 push
|
||||
// arm): materialise the source ADDRESS in SI and push its ceil(sz/8)
|
||||
// words high→low (the pop drains word0 into the first arg reg). A
|
||||
// CALL source receives first — ≤24B in AX/DX/CX pushed straight,
|
||||
// >24B sret'd into @aggargscr then pushed from there. Pre-fix every
|
||||
// such source fell to the scalar default (one PUSHQ for a multi-word
|
||||
// aggregate) and stack-imbalanced against the type-based drain.
|
||||
let aggsz: i32 = aggargsizetn(arg.type_: *tinfo);
|
||||
if (aggsz > 0) {
|
||||
// Exclude a ≤16B-struct IDENT — it owns the structparamsize
|
||||
// fast path above (or, when a cross-module same-leaf collision
|
||||
// makes the name-keyed structparamsize miss it, the scalar
|
||||
// default below, byte-id with cstage's 1-word struct push;
|
||||
// #784/#223). The exclusion is TYPE-keyed via the stamped
|
||||
// tinfo, mirroring cstage node_isstructarg (struct_arg_size on
|
||||
// args[i]->type) — a name-keyed gate here re-opens the #211/#13
|
||||
// name-keyed divergence the cstage type gate doesn't have.
|
||||
let structident: bool = false;
|
||||
if (arg.kind == nkind.N_IDENT) {
|
||||
let st: *tinfo = arg.type_: *tinfo;
|
||||
for (st != nil && st.kind == tykind.TY_NAMED) {
|
||||
st = st.under;
|
||||
};
|
||||
if (st != nil) { if (st.kind == tykind.TY_STRUCT) {
|
||||
if (st.size: i32 <= 16) { structident = true; };
|
||||
}; };
|
||||
};
|
||||
if (!structident) {
|
||||
if (aggargfloatstop(arg)) {
|
||||
let msg: str = "#271/#165: float-bearing struct arg from a non-ident source needs SSE eightbyte transport (out of scope)\n";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let nwords: i32 = (aggsz + 7) / 8;
|
||||
if (arg.kind == nkind.N_CALL) {
|
||||
if (callsretsize(c, arg) > 0) {
|
||||
let scr: i32 = localadd(c, "@aggargscr",
|
||||
aggsz, nil);
|
||||
c.sretdestoff = scr;
|
||||
cgexpr(c, arg);
|
||||
c.sretdestoff = 0;
|
||||
let k: i32 = nwords - 1;
|
||||
for (k >= 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((scr + k*8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
k -= 1;
|
||||
};
|
||||
} else {
|
||||
// ≤24B: producer left AX=word0,
|
||||
// DX=word1, CX=word2. Push high→low so
|
||||
// the pop drains word0 first.
|
||||
cgexpr(c, arg);
|
||||
let k: i32 = nwords - 1;
|
||||
for (k >= 0) {
|
||||
if (k == 2) {
|
||||
emitline("\tPUSHQ\tCX\n");
|
||||
} else { if (k == 1) {
|
||||
emitline("\tPUSHQ\tDX\n");
|
||||
} else {
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
}; };
|
||||
k -= 1;
|
||||
};
|
||||
};
|
||||
return rest + nwords;
|
||||
};
|
||||
if (!aggargsrcaddr(c, arg, "SI")) {
|
||||
let msg: str = "#271: aggregate arg from unsupported source kind\n";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
let k: i32 = nwords - 1;
|
||||
for (k >= 0) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((k*8): i64);
|
||||
emitline("(SI), AX\n");
|
||||
emitline("\tPUSHQ\tAX\n");
|
||||
k -= 1;
|
||||
};
|
||||
return rest + nwords;
|
||||
};
|
||||
};
|
||||
// Float arg: cgexpr leaves the value in X0. Push 8 bytes from
|
||||
// X0 via SUBQ+MOVSD so cgcall's pop side can drain into the
|
||||
// XMM stream (X0..X7). f32 still occupies 8B on the stack —
|
||||
@@ -17562,6 +17649,47 @@ fn structparamsize(c: *cgen, t: *node) i32 = {
|
||||
return si.totsize;
|
||||
};
|
||||
|
||||
// aggargsize — byte size of a by-value aggregate (struct OR array) call
|
||||
// arg, else 0 (#271, mirror of cstage aggarg_size). The size axis the
|
||||
// ≤16B-struct structparamsize carve-out doesn't cover: arrays of any
|
||||
// size and structs > 16B. Reads the stamped tinfo size (the type table,
|
||||
// byte-id with cstage Type.size).
|
||||
fn aggargsizetn(t: *tinfo) i32 = {
|
||||
if (t == nil) { return 0; };
|
||||
let u: *tinfo = t;
|
||||
for (u != nil && u.kind == tykind.TY_NAMED) { u = u.under; };
|
||||
if (u == nil) { return 0; };
|
||||
if (u.kind == tykind.TY_STRUCT || u.kind == tykind.TY_ARRAY) {
|
||||
return u.size: i32;
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
fn nodeisaggarg(n: *node) bool = {
|
||||
if (n == nil) { return false; };
|
||||
return aggargsizetn(n.type_: *tinfo) > 0;
|
||||
};
|
||||
|
||||
// aggargfloatstop — true iff the arg is a ≤16B struct with any float
|
||||
// field (#271/#165). Such a struct from a non-ident source would need
|
||||
// the SSE eightbyte transport the GP aggregate push/drain can't model;
|
||||
// both stages loud-stop on it. Same predicate as the cstage Tfield
|
||||
// fld_isfloat walk (cmd/w6c/cgen.c #271 push arm).
|
||||
fn aggargfloatstop(n: *node) bool = {
|
||||
if (n == nil) { return false; };
|
||||
let st: *tinfo = n.type_: *tinfo;
|
||||
for (st != nil && st.kind == tykind.TY_NAMED) { st = st.under; };
|
||||
if (st == nil) { return false; };
|
||||
if (st.kind != tykind.TY_STRUCT) { return false; };
|
||||
if (st.size: i32 > 16) { return false; };
|
||||
let f: *tfield = st.fields;
|
||||
for (f != nil) {
|
||||
if (typeisfloat(f.type_)) { return true; };
|
||||
f = f.tnext;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
// structfloatclass — SysV per-eightbyte classification for the #165
|
||||
// float-bearing-struct param case (param twin of #171's struct return;
|
||||
// classifies per-eightbyte, not #163's per-element). Returns 0 when the
|
||||
@@ -20626,6 +20754,92 @@ fn dotbaseaddr(c: *cgen, base: *node, dstreg: str) bool = {
|
||||
return true;
|
||||
};
|
||||
|
||||
// aggargsrcaddr — land the ADDRESS of an addressable aggregate (struct/
|
||||
// array) call-arg source in dstreg (#271, mirror of cstage
|
||||
// aggarg_srcaddr). Reuses the closed #265/#268 let-init-copy dispatch:
|
||||
// local ident slot (LEAQ off(BP)), module-let global (LEAQ name(SB)),
|
||||
// deref operand (cgexpr of the pointer), N_DOT field (dotchainaddr,
|
||||
// #253), N_INDEX element of an N_IDENT array base (the &base[i] spine,
|
||||
// #252/#270). Returns false for an uncovered source kind (caller loud-
|
||||
// stops, rule 7). The CALL source is handled at the push site.
|
||||
fn aggargsrcaddr(c: *cgen, src: *node, dst: str) bool = {
|
||||
if (src.kind == nkind.N_UN) {
|
||||
if (src.op == tkind.TK_STAR) {
|
||||
cgexpr(c, src.lhs);
|
||||
if (!streq(dst, "AX")) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
};
|
||||
return true;
|
||||
};
|
||||
};
|
||||
if (src.kind == nkind.N_IDENT) {
|
||||
let lc: *local = localfindnode(c, src.str);
|
||||
if (lc != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(lc.off: i64);
|
||||
emitline("(BP), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
// global value source. Gated to a module-`let` (letvartnode,
|
||||
// the cstage let_islet twin); a const array/struct `def`
|
||||
// aggregate ARG is untested + out of scope (#274; both stages
|
||||
// loud-stop, rule-10 aligned).
|
||||
if (letvartnode(c, src.str) != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, src.str);
|
||||
emitline("(SB), ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
if (src.kind == nkind.N_DOT) {
|
||||
return dotchainaddr(c, src, dst);
|
||||
};
|
||||
if (src.kind == nkind.N_INDEX) {
|
||||
let base: *node = src.lhs;
|
||||
let idx: *node = src.rhs;
|
||||
if (base == nil) { return false; };
|
||||
if (base.kind != nkind.N_IDENT) { return false; };
|
||||
let bu: *tinfo = base.type_: *tinfo;
|
||||
for (bu != nil && bu.kind == tykind.TY_NAMED) { bu = bu.under; };
|
||||
if (bu == nil) { return false; };
|
||||
if (bu.kind != tykind.TY_ARRAY) { return false; };
|
||||
let esz: i32 = 1;
|
||||
if (bu.sub != nil) { esz = bu.sub.size: i32; };
|
||||
cgexpr(c, idx);
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", CX\n");
|
||||
emitline("\tIMULQ\tCX, AX\n");
|
||||
};
|
||||
let boff: *local = localfindnode(c, base.str);
|
||||
if (boff != nil) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitoff(boff.off: i64);
|
||||
emitline("(BP), BX\n");
|
||||
} else {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, base.str);
|
||||
emitline("(SB), BX\n");
|
||||
};
|
||||
emitline("\tADDQ\tBX, AX\n");
|
||||
if (!streq(dst, "AX")) {
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitline(dst);
|
||||
emitline("\n");
|
||||
};
|
||||
return true;
|
||||
};
|
||||
return false;
|
||||
};
|
||||
|
||||
fn cgindex(c: *cgen, n: *node) void = {
|
||||
// Element-size-aware load: u8 → MOVZBQ, i32 → MOVSXD, u32 → MOVL,
|
||||
// str → (ptr, len) into (AX, BX), everything else → MOVQ. Fast
|
||||
@@ -24004,6 +24218,13 @@ fn cgcall(c: *cgen, n: *node) void = {
|
||||
// words and shift intidx out of sync.
|
||||
let tcs: i32 = taggedcallslot(c, a);
|
||||
if (tcs > 0) { extra = tcs / 8 - 1; };
|
||||
// #271: array / >16B-struct / non-ident 16B-struct
|
||||
// aggregate arg — pushargsrev staged ceil(sz/8) words;
|
||||
// drain exactly that many so intidx tracks per-arg
|
||||
// (the ≤16B struct IDENT case is the stfc branch
|
||||
// above). Mirror of cstage node_isaggarg drain arm.
|
||||
let aggsz: i32 = aggargsizetn(a.type_: *tinfo);
|
||||
if (aggsz > 0) { extra = (aggsz + 7) / 8 - 1; };
|
||||
let words: i32 = 1 + extra;
|
||||
let w: i32 = 0;
|
||||
for (w < words) {
|
||||
@@ -30870,18 +31091,66 @@ fn cgfnparams(c: *cgen, params: *node) void = {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += nw;
|
||||
};};
|
||||
} else {
|
||||
if (idx < 6) {
|
||||
let off: i32 = localadd(c, nm, 8, p.lhs);
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
} else { let aggsz2: i32 = aggargsizetn(p.lhs.type_: *tinfo);
|
||||
if (aggsz2 > 0) {
|
||||
// #271: array / >16B-struct by-value param —
|
||||
// received as ceil(sz/8) GP eightbytes, the
|
||||
// callee twin of the generalised aggregate-arg
|
||||
// push. Mirror of the cstage is_bigagg arm
|
||||
// (regs-fit / partial-stitch / stack-spill).
|
||||
let nw2: i32 = (aggsz2 + 7) / 8;
|
||||
if (idx + nw2 <= 6) {
|
||||
let off: i32 = localadd(c, nm, aggsz2, p.lhs);
|
||||
let w: i32 = 0;
|
||||
for (w < nw2) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + w*8): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
w += 1;
|
||||
};
|
||||
} else { if (idx < 6) {
|
||||
let off: i32 = localadd(c, nm, aggsz2, p.lhs);
|
||||
let regs_left: i32 = 6 - idx;
|
||||
let w: i32 = 0;
|
||||
for (w < regs_left) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff((off + w*8): i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
w += 1;
|
||||
};
|
||||
for (w < nw2) {
|
||||
emitline("\tMOVQ\t");
|
||||
emitoff((16 + stkcursor*8): i64);
|
||||
emitline("(BP), AX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitoff((off + w*8): i64);
|
||||
emitline("(BP)\n");
|
||||
stkcursor += 1;
|
||||
w += 1;
|
||||
};
|
||||
} else {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += nw2;
|
||||
};};
|
||||
} else {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += 1;
|
||||
if (idx < 6) {
|
||||
let off: i32 = localadd(c, nm, 8, p.lhs);
|
||||
emitline("\tMOVQ\t");
|
||||
emitline(argregname(idx));
|
||||
emitline(", ");
|
||||
emitoff(off: i64);
|
||||
emitline("(BP)\n");
|
||||
idx += 1;
|
||||
} else {
|
||||
localaddstack(c, nm, p.lhs, 16 + stkcursor*8);
|
||||
stkcursor += 1;
|
||||
};
|
||||
};
|
||||
};
|
||||
};};};
|
||||
|
||||
@@ -1180,6 +1180,147 @@ static const struct row rows[] = {
|
||||
" let x: [2]inner = [p, q];\n"
|
||||
" return (x[0].a + x[0].b + x[1].a + x[1].b): i32;\n"
|
||||
"};\n", 18, 1 },
|
||||
/* #271 aggregate ARG from any NON-IDENT source — the arg-pass twin
|
||||
* of the #265/#268 let-init copy. Passing an aggregate BY VALUE as a
|
||||
* call argument worked ONLY for an IDENT source (≤16B struct); every
|
||||
* non-ident source (CALL mk(), N_DOT o.f, N_INDEX a[i], DEREF *p) and
|
||||
* every array / >16B-struct (even as an IDENT) fell to the scalar
|
||||
* default — one PUSHQ for a multi-word aggregate — stack-imbalancing
|
||||
* against the multi-word drain (cs!=ww, both garbage). The fix
|
||||
* materialises the source into the arg convention: the source ADDRESS
|
||||
* in SI (ident LEAQ / deref / dotchainaddr #253 / &base[i] #252-270)
|
||||
* then ceil(sz/8) words pushed; a CALL receives first (≤24B in
|
||||
* AX/DX/CX, >24B sret'd into @aggargscr). The callee prologue gained a
|
||||
* matching array / >16B-struct receive. Each callee reads back ALL
|
||||
* members (full sum) so a dropped word fails. Covered: struct 16B
|
||||
* (reg-class) AND struct 32B (sret-class) AND array [4]u32, from each
|
||||
* non-ident source + an ident control. byteid=1 throughout: both
|
||||
* stages converge on the correct full-aggregate push (master both-
|
||||
* broken-and-divergent → fix correct, #263 lesson). */
|
||||
{ "arg_struct16_call",
|
||||
"package main;\n"
|
||||
"type t = struct { x: i64, y: i64 };\n"
|
||||
"fn mk() t = { let a: t; a.x=3i64; a.y=7i64; return a; };\n"
|
||||
"fn sum(b: t) i64 = { return b.x + b.y; };\n"
|
||||
"export fn main() i32 = { return sum(mk()): i32; };\n", 10, 1 },
|
||||
{ "arg_struct16_dot",
|
||||
"package main;\n"
|
||||
"type t = struct { x: i64, y: i64 };\n"
|
||||
"type o = struct { f: t };\n"
|
||||
"fn sum(b: t) i64 = { return b.x + b.y; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let q: o; q.f.x=3i64; q.f.y=7i64;\n"
|
||||
" return sum(q.f): i32;\n"
|
||||
"};\n", 10, 1 },
|
||||
{ "arg_struct16_idx",
|
||||
"package main;\n"
|
||||
"type t = struct { x: i64, y: i64 };\n"
|
||||
"fn sum(b: t) i64 = { return b.x + b.y; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [2]t; a[1].x=3i64; a[1].y=7i64;\n"
|
||||
" return sum(a[1]): i32;\n"
|
||||
"};\n", 10, 1 },
|
||||
{ "arg_struct16_deref",
|
||||
"package main;\n"
|
||||
"type t = struct { x: i64, y: i64 };\n"
|
||||
"fn sum(b: t) i64 = { return b.x + b.y; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: t; v.x=3i64; v.y=7i64; let p: *t = &v;\n"
|
||||
" return sum(*p): i32;\n"
|
||||
"};\n", 10, 1 },
|
||||
{ "arg_struct16_ident",
|
||||
"package main;\n"
|
||||
"type t = struct { x: i64, y: i64 };\n"
|
||||
"fn sum(b: t) i64 = { return b.x + b.y; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: t; v.x=3i64; v.y=7i64;\n"
|
||||
" return sum(v): i32;\n"
|
||||
"};\n", 10, 1 },
|
||||
{ "arg_arr16_call",
|
||||
"package main;\n"
|
||||
"fn mk() [4]u32 = { let a: [4]u32; a[0]=1u32;a[1]=2u32;a[2]=3u32;a[3]=4u32; return a; };\n"
|
||||
"fn sum(b: [4]u32) i32 = { return (b[0]+b[1]+b[2]+b[3]): i32; };\n"
|
||||
"export fn main() i32 = { return sum(mk()); };\n", 10, 1 },
|
||||
{ "arg_arr16_dot",
|
||||
"package main;\n"
|
||||
"type o = struct { f: [4]u32 };\n"
|
||||
"fn sum(b: [4]u32) i32 = { return (b[0]+b[1]+b[2]+b[3]): i32; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let q: o; q.f[0]=1u32; q.f[1]=2u32; q.f[2]=3u32; q.f[3]=4u32;\n"
|
||||
" return sum(q.f);\n"
|
||||
"};\n", 10, 1 },
|
||||
{ "arg_arr16_idx",
|
||||
"package main;\n"
|
||||
"fn sum(b: [4]u32) i32 = { return (b[0]+b[1]+b[2]+b[3]): i32; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [2][4]u32;\n"
|
||||
" a[1][0]=1u32; a[1][1]=2u32; a[1][2]=3u32; a[1][3]=4u32;\n"
|
||||
" return sum(a[1]);\n"
|
||||
"};\n", 10, 1 },
|
||||
{ "arg_arr16_deref",
|
||||
"package main;\n"
|
||||
"fn sum(b: [4]u32) i32 = { return (b[0]+b[1]+b[2]+b[3]): i32; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: [4]u32; v[0]=1u32; v[1]=2u32; v[2]=3u32; v[3]=4u32;\n"
|
||||
" let p: *[4]u32 = &v;\n"
|
||||
" return sum(*p);\n"
|
||||
"};\n", 10, 1 },
|
||||
{ "arg_arr16_ident",
|
||||
"package main;\n"
|
||||
"fn sum(b: [4]u32) i32 = { return (b[0]+b[1]+b[2]+b[3]): i32; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: [4]u32; v[0]=1u32; v[1]=2u32; v[2]=3u32; v[3]=4u32;\n"
|
||||
" return sum(v);\n"
|
||||
"};\n", 10, 1 },
|
||||
{ "arg_struct32_call",
|
||||
"package main;\n"
|
||||
"type t = struct { h: [8]u32 };\n"
|
||||
"fn mk() t = { let a: t; a.h[0]=1u32;a.h[1]=2u32;a.h[2]=3u32;a.h[3]=4u32;a.h[4]=5u32;a.h[5]=6u32;a.h[6]=7u32;a.h[7]=8u32; return a; };\n"
|
||||
"fn sum(b: t) i32 = { return (b.h[0]+b.h[1]+b.h[2]+b.h[3]+b.h[4]+b.h[5]+b.h[6]+b.h[7]): i32; };\n"
|
||||
"export fn main() i32 = { return sum(mk()); };\n", 36, 1 },
|
||||
{ "arg_struct32_dot",
|
||||
"package main;\n"
|
||||
"type t = struct { h: [8]u32 };\n"
|
||||
"type o = struct { f: t };\n"
|
||||
"fn sum(b: t) i32 = { return (b.h[0]+b.h[1]+b.h[2]+b.h[3]+b.h[4]+b.h[5]+b.h[6]+b.h[7]): i32; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let q: o;\n"
|
||||
" q.f.h[0]=1u32;q.f.h[1]=2u32;q.f.h[2]=3u32;q.f.h[3]=4u32;\n"
|
||||
" q.f.h[4]=5u32;q.f.h[5]=6u32;q.f.h[6]=7u32;q.f.h[7]=8u32;\n"
|
||||
" return sum(q.f);\n"
|
||||
"};\n", 36, 1 },
|
||||
{ "arg_struct32_idx",
|
||||
"package main;\n"
|
||||
"type t = struct { h: [8]u32 };\n"
|
||||
"fn sum(b: t) i32 = { return (b.h[0]+b.h[1]+b.h[2]+b.h[3]+b.h[4]+b.h[5]+b.h[6]+b.h[7]): i32; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let a: [2]t;\n"
|
||||
" let p: *t = &a[1];\n"
|
||||
" p.h[0]=1u32;p.h[1]=2u32;p.h[2]=3u32;p.h[3]=4u32;\n"
|
||||
" p.h[4]=5u32;p.h[5]=6u32;p.h[6]=7u32;p.h[7]=8u32;\n"
|
||||
" return sum(a[1]);\n"
|
||||
"};\n", 36, 1 },
|
||||
{ "arg_struct32_deref",
|
||||
"package main;\n"
|
||||
"type t = struct { h: [8]u32 };\n"
|
||||
"fn sum(b: t) i32 = { return (b.h[0]+b.h[1]+b.h[2]+b.h[3]+b.h[4]+b.h[5]+b.h[6]+b.h[7]): i32; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: t;\n"
|
||||
" v.h[0]=1u32;v.h[1]=2u32;v.h[2]=3u32;v.h[3]=4u32;\n"
|
||||
" v.h[4]=5u32;v.h[5]=6u32;v.h[6]=7u32;v.h[7]=8u32;\n"
|
||||
" let p: *t = &v;\n"
|
||||
" return sum(*p);\n"
|
||||
"};\n", 36, 1 },
|
||||
{ "arg_struct32_ident",
|
||||
"package main;\n"
|
||||
"type t = struct { h: [8]u32 };\n"
|
||||
"fn sum(b: t) i32 = { return (b.h[0]+b.h[1]+b.h[2]+b.h[3]+b.h[4]+b.h[5]+b.h[6]+b.h[7]): i32; };\n"
|
||||
"export fn main() i32 = {\n"
|
||||
" let v: t;\n"
|
||||
" v.h[0]=1u32;v.h[1]=2u32;v.h[2]=3u32;v.h[3]=4u32;\n"
|
||||
" v.h[4]=5u32;v.h[5]=6u32;v.h[6]=7u32;v.h[7]=8u32;\n"
|
||||
" return sum(v);\n"
|
||||
"};\n", 36, 1 },
|
||||
{ NULL, NULL, 0, 0 }
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user