commit 7ea57a999f6e38192ca781416198d64c778cc276
parent d33227bb2f34d7317626dd64e31ce3e96f455bf8
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 27 May 2026 05:49:32 -0700
opt: handle aggregate-typed COPY/LOAD/STORE via byte copy
Aggregate values flow through IR_COPY/IR_LOAD/IR_STORE (e.g. assigning a
struct-returning call result, copying a record local). These were emitted as
scalar moves, truncating to one register width and reading wrong field
offsets. Route aggregate / >8-byte value types through copy_bytes via a new
emit_agg_move helper.
Fixes record-by-value return and slice cases (130, 124) on the optimizer
path. Default test path remains 408/408. Remaining bypass-off failures: tail
+ sret (36, 37) and inline asm (7 cases).
Diffstat:
1 file changed, 37 insertions(+), 0 deletions(-)
diff --git a/src/opt/pass_native_emit.c b/src/opt/pass_native_emit.c
@@ -464,6 +464,25 @@ static void write_loc(NativeEmitCtx* e, NativeLoc dst, NativeLoc src,
e->target->store(e->target, addr, src, mem);
}
+static int type_is_aggregate_or_large(NativeEmitCtx* e, CfreeCgTypeId type) {
+ return type &&
+ (cg_type_is_aggregate(e->c, type) || type_size_or(e->c, type, 8u) > 8u);
+}
+
+/* Copy an aggregate / oversized value between two memory locations. dst and
+ * src must be addressable (frame/global/indirect/reg-as-pointer); used for
+ * IR_COPY/IR_LOAD/IR_STORE whose value type cannot move through one register. */
+static void emit_agg_move(NativeEmitCtx* e, NativeAddr da, NativeAddr sa,
+ CfreeCgTypeId type) {
+ AggregateAccess acc;
+ memset(&acc, 0, sizeof acc);
+ acc.type = type;
+ acc.size = type_size_or(e->c, type, 8u);
+ acc.align = type_align_or(e->c, type, 8u);
+ acc.mem = mem_for_type(e->c, type);
+ e->target->copy_bytes(e->target, da, sa, acc);
+}
+
static CGFuncDesc semantic_func_desc(NativeEmitCtx* e) {
OptCGFuncDesc* in = &e->f->desc;
CGFuncDesc out;
@@ -733,11 +752,23 @@ static void emit_inst(NativeEmitCtx* e, u32 block, u32 order_index, Inst* in,
e->target->load_const(e->target, dst, in->extra.cbytes);
return;
case IR_COPY:
+ if (type_is_aggregate_or_large(e, in->opnds[0].type)) {
+ emit_agg_move(e, addr_from_operand(e, &in->opnds[0], in->loc),
+ addr_from_operand(e, &in->opnds[1], in->loc),
+ in->opnds[0].type);
+ return;
+ }
dst = loc_from_operand(e, &in->opnds[0], in->loc);
src = loc_from_operand(e, &in->opnds[1], in->loc);
write_loc(e, dst, src, mem_for_type(e->c, in->opnds[0].type), in->loc);
return;
case IR_LOAD:
+ if (type_is_aggregate_or_large(e, in->opnds[0].type)) {
+ addr = addr_from_operand(e, &in->opnds[1], in->loc);
+ emit_agg_move(e, addr_from_operand(e, &in->opnds[0], in->loc), addr,
+ in->opnds[0].type);
+ return;
+ }
dst = loc_from_operand(e, &in->opnds[0], in->loc);
addr = addr_from_operand(e, &in->opnds[1], in->loc);
legalize_addr(e, &addr, in->extra.mem, in->loc);
@@ -755,6 +786,12 @@ static void emit_inst(NativeEmitCtx* e, u32 block, u32 order_index, Inst* in,
}
return;
case IR_STORE:
+ if (type_is_aggregate_or_large(e, in->opnds[1].type)) {
+ emit_agg_move(e, addr_from_operand(e, &in->opnds[0], in->loc),
+ addr_from_operand(e, &in->opnds[1], in->loc),
+ in->opnds[1].type);
+ return;
+ }
addr = addr_from_operand(e, &in->opnds[0], in->loc);
legalize_addr(e, &addr, in->extra.mem, in->loc);
src = loc_from_operand(e, &in->opnds[1], in->loc);