commit bc5ef9d8e29b2fb214e596198202d19cc21c956d
parent 8e9dc6c4c12726f18150535ec45a800b6659ee67
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 10 Jun 2026 22:12:05 -0700
perf(cg): build native descriptors via compound literals, not memset
The -O0 native-direct codegen built NativeLoc (~64B, its union embeds the 56B
NativeAddr), NativeAddr, MemAccess and the frame-slot descriptors with
memset(&x,0,sizeof x) + field stores on the hottest per-operand/per-access path
— and the full-struct zero compiled to an out-of-line _memset call (the #1
self-time cost on codegen-heavy axes). Replace with C99 designated compound
literals: value-identical (unnamed fields zero-fill, same as the memset minus
padding, which nothing reads) but the compiler emits only the live stores.
Covers native_loc_{none,reg,stack}, nd_loc_{frame,imm,global,operand},
nd_addr_{storage,pointer}, nd_scalar_mem, the frame load/store address builders,
the load/store STACK/GLOBAL address builds, and the per-local/param slot
descriptors. body-size 2096->1921ms (-8%), locals-per-fn 1060->972ms (-8%),
ref-density 883->848ms (-4%). Object output byte-identical on aa64/x64/rv64
(incl. the s->a++ + ++s->b value-stack-aliasing case). Verified: test-cg-api/
toy/opt/isa/aa64-inline/smoke-x64/smoke-rv64 all green.
Diffstat:
2 files changed, 87 insertions(+), 133 deletions(-)
diff --git a/src/arch/native_target.h b/src/arch/native_target.h
@@ -562,11 +562,13 @@ static inline u32 native_target_callee_saved_mask(NativeTarget* t,
return ci ? ci->callee_saved_mask : 0u;
}
+/* Location constructors. A designated compound literal initializes the named
+ * fields and zero-fills the rest (so it is value-identical to the former
+ * memset + field stores) but lets the compiler emit only the needed stores
+ * instead of an out-of-line memset of the ~64-byte struct — these run on the
+ * hottest per-operand codegen path. */
static inline NativeLoc native_loc_none(void) {
- NativeLoc loc;
- memset(&loc, 0, sizeof loc);
- loc.kind = NATIVE_LOC_NONE;
- return loc;
+ return (NativeLoc){.kind = NATIVE_LOC_NONE};
}
/* Target-neutral location constructors and scalar queries. These are
@@ -575,25 +577,16 @@ static inline NativeLoc native_loc_none(void) {
* per-backend.) */
static inline NativeLoc native_loc_reg(KitCgTypeId type, NativeAllocClass cls,
Reg reg) {
- NativeLoc loc;
- memset(&loc, 0, sizeof loc);
- loc.kind = NATIVE_LOC_REG;
- loc.cls = (u8)cls;
- loc.type = type;
- loc.v.reg = reg;
- return loc;
+ return (NativeLoc){
+ .kind = NATIVE_LOC_REG, .cls = (u8)cls, .type = type, .v.reg = reg};
}
static inline NativeLoc native_loc_stack(KitCgTypeId type, NativeFrameSlot slot,
i32 offset) {
- NativeLoc loc;
- memset(&loc, 0, sizeof loc);
- loc.kind = NATIVE_LOC_STACK;
- loc.cls = NATIVE_REG_INT;
- loc.type = type;
- loc.v.stack.slot = slot;
- loc.v.stack.offset = offset;
- return loc;
+ return (NativeLoc){.kind = NATIVE_LOC_STACK,
+ .cls = NATIVE_REG_INT,
+ .type = type,
+ .v.stack = {.slot = slot, .offset = offset}};
}
static inline int native_loc_is_fp(NativeLoc loc) {
diff --git a/src/cg/native_direct_target.c b/src/cg/native_direct_target.c
@@ -205,14 +205,12 @@ static NativeFrameSlot nd_alloc_frame_slot(NativeDirectTarget* d,
}
static NativeFrameSlotDesc nd_slot_desc_local(const CGLocalDesc* in) {
- NativeFrameSlotDesc out;
- memset(&out, 0, sizeof out);
- out.type = in->type;
- out.name = in->name;
- out.loc = in->loc;
- out.size = in->size;
- out.align = in->align;
- out.kind = NATIVE_FRAME_SLOT_LOCAL;
+ NativeFrameSlotDesc out = {.type = in->type,
+ .name = in->name,
+ .loc = in->loc,
+ .size = in->size,
+ .align = in->align,
+ .kind = NATIVE_FRAME_SLOT_LOCAL};
if (in->flags & CG_LOCAL_ADDR_TAKEN)
out.flags |= NATIVE_FRAME_SLOT_ADDR_TAKEN;
if (in->flags & CG_LOCAL_MEMORY_REQUIRED)
@@ -221,14 +219,12 @@ static NativeFrameSlotDesc nd_slot_desc_local(const CGLocalDesc* in) {
}
static NativeFrameSlotDesc nd_slot_desc_param(const CGParamDesc* in) {
- NativeFrameSlotDesc out;
- memset(&out, 0, sizeof out);
- out.type = in->type;
- out.name = in->name;
- out.loc = in->loc;
- out.size = in->size;
- out.align = in->align;
- out.kind = NATIVE_FRAME_SLOT_PARAM;
+ NativeFrameSlotDesc out = {.type = in->type,
+ .name = in->name,
+ .loc = in->loc,
+ .size = in->size,
+ .align = in->align,
+ .kind = NATIVE_FRAME_SLOT_PARAM};
if (in->flags & CG_LOCAL_ADDR_TAKEN)
out.flags |= NATIVE_FRAME_SLOT_ADDR_TAKEN;
if (in->flags & CG_LOCAL_MEMORY_REQUIRED)
@@ -274,35 +270,27 @@ static Label nd_label_new_raw(NativeDirectTarget* d) {
return id;
}
+/* Designated compound literals: value-identical to the former memset + field
+ * stores (unnamed fields zero-fill), but the compiler emits only the live
+ * stores rather than an out-of-line memset of the ~64-byte descriptor on this
+ * per-operand path. */
static NativeLoc nd_loc_frame(NativeDirectTarget* d, CGLocal local,
KitCgTypeId type) {
NativeDirectLocal* l = nd_local(d, local);
- NativeLoc out;
- memset(&out, 0, sizeof out);
- out.kind = NATIVE_LOC_FRAME;
- out.cls = l->cls;
- out.type = type ? type : l->type;
- out.v.frame = l->home;
- return out;
+ return (NativeLoc){.kind = NATIVE_LOC_FRAME,
+ .cls = l->cls,
+ .type = type ? type : l->type,
+ .v.frame = l->home};
}
static NativeLoc nd_loc_imm(i64 imm, KitCgTypeId type) {
- NativeLoc out;
- memset(&out, 0, sizeof out);
- out.kind = NATIVE_LOC_IMM;
- out.type = type;
- out.v.imm = imm;
- return out;
+ return (NativeLoc){.kind = NATIVE_LOC_IMM, .type = type, .v.imm = imm};
}
static NativeLoc nd_loc_global(ObjSymId sym, i64 addend, KitCgTypeId type) {
- NativeLoc out;
- memset(&out, 0, sizeof out);
- out.kind = NATIVE_LOC_GLOBAL;
- out.type = type;
- out.v.global.sym = sym;
- out.v.global.addend = addend;
- return out;
+ return (NativeLoc){.kind = NATIVE_LOC_GLOBAL,
+ .type = type,
+ .v.global = {.sym = sym, .addend = addend}};
}
static NativeLoc nd_loc_operand(NativeDirectTarget* d, Operand op) {
@@ -315,14 +303,15 @@ static NativeLoc nd_loc_operand(NativeDirectTarget* d, Operand op) {
return nd_loc_global(op.v.global.sym, op.v.global.addend, op.type);
case OPK_INDIRECT: {
NativeDirectLocal* bl = nd_local(d, op.v.ind.base);
- NativeLoc out;
- memset(&out, 0, sizeof out);
- out.kind = NATIVE_LOC_ADDR;
- out.type = op.type;
- out.v.addr.base_kind = NATIVE_ADDR_BASE_FRAME_VALUE;
- out.v.addr.base.frame = bl->home;
- out.v.addr.cls = bl->cls;
- out.v.addr.base_type = bl->type;
+ NativeLoc out = (NativeLoc){
+ .kind = NATIVE_LOC_ADDR,
+ .type = op.type,
+ .v.addr = {.base_kind = NATIVE_ADDR_BASE_FRAME_VALUE,
+ .base.frame = bl->home,
+ .cls = bl->cls,
+ .base_type = bl->type,
+ .log2_scale = op.v.ind.log2_scale,
+ .offset = op.v.ind.ofs}};
if (op.v.ind.index != CG_LOCAL_NONE) {
NativeDirectLocal* il = nd_local(d, op.v.ind.index);
out.v.addr.index_kind = NATIVE_ADDR_INDEX_FRAME_VALUE;
@@ -330,8 +319,6 @@ static NativeLoc nd_loc_operand(NativeDirectTarget* d, Operand op) {
out.v.addr.index_cls = il->cls;
out.v.addr.index_type = il->type;
}
- out.v.addr.log2_scale = op.v.ind.log2_scale;
- out.v.addr.offset = op.v.ind.ofs;
return out;
}
default:
@@ -340,8 +327,6 @@ static NativeLoc nd_loc_operand(NativeDirectTarget* d, Operand op) {
}
static NativeAddr nd_addr_storage(NativeDirectTarget* d, Operand op) {
- NativeAddr out;
- memset(&out, 0, sizeof out);
switch ((OpKind)op.kind) {
case OPK_LOCAL: {
/* The local's home is addressed directly (a memory access reads/writes
@@ -351,23 +336,23 @@ static NativeAddr nd_addr_storage(NativeDirectTarget* d, Operand op) {
NativeDirectLocal* l;
nd_flush_local(d, op.v.local);
l = nd_local(d, op.v.local);
- out.base_kind = NATIVE_ADDR_BASE_FRAME;
- out.base.frame = l->home;
- out.cls = l->cls;
- out.base_type = l->type;
- return out;
+ return (NativeAddr){.base_kind = NATIVE_ADDR_BASE_FRAME,
+ .base.frame = l->home,
+ .cls = l->cls,
+ .base_type = l->type};
}
case OPK_GLOBAL:
- out.base_kind = NATIVE_ADDR_BASE_GLOBAL;
- out.base.global.sym = op.v.global.sym;
- out.base.global.addend = op.v.global.addend;
- out.base_type = op.type;
- return out;
+ return (NativeAddr){
+ .base_kind = NATIVE_ADDR_BASE_GLOBAL,
+ .base.global = {.sym = op.v.global.sym, .addend = op.v.global.addend},
+ .base_type = op.type};
case OPK_INDIRECT: {
NativeDirectLocal* bl = nd_local(d, op.v.ind.base);
Reg br = nd_cache_reg_for(d, op.v.ind.base, bl->type);
- out.cls = bl->cls;
- out.base_type = bl->type;
+ NativeAddr out = (NativeAddr){.cls = bl->cls,
+ .base_type = bl->type,
+ .log2_scale = op.v.ind.log2_scale,
+ .offset = op.v.ind.ofs};
if (br != REG_NONE) {
out.base_kind = NATIVE_ADDR_BASE_REG;
out.base.reg = br;
@@ -391,8 +376,6 @@ static NativeAddr nd_addr_storage(NativeDirectTarget* d, Operand op) {
out.index.frame = il->home;
}
}
- out.log2_scale = op.v.ind.log2_scale;
- out.offset = op.v.ind.ofs;
return out;
}
default:
@@ -401,13 +384,10 @@ static NativeAddr nd_addr_storage(NativeDirectTarget* d, Operand op) {
}
static NativeAddr nd_addr_pointer(NativeDirectTarget* d, Operand op) {
- NativeAddr out;
- memset(&out, 0, sizeof out);
switch ((OpKind)op.kind) {
case OPK_LOCAL: {
NativeDirectLocal* l = nd_local(d, op.v.local);
- out.cls = l->cls;
- out.base_type = l->type;
+ NativeAddr out = (NativeAddr){.cls = l->cls, .base_type = l->type};
if (cg_type_is_ptr(d->base.c, op.type)) {
/* Pointer value lives in the local: use its live register if cached
* (a dirty cached pointer is a valid base), else load from the home. */
@@ -430,11 +410,10 @@ static NativeAddr nd_addr_pointer(NativeDirectTarget* d, Operand op) {
return out;
}
case OPK_GLOBAL:
- out.base_kind = NATIVE_ADDR_BASE_GLOBAL;
- out.base.global.sym = op.v.global.sym;
- out.base.global.addend = op.v.global.addend;
- out.base_type = op.type;
- return out;
+ return (NativeAddr){
+ .base_kind = NATIVE_ADDR_BASE_GLOBAL,
+ .base.global = {.sym = op.v.global.sym, .addend = op.v.global.addend},
+ .base_type = op.type};
case OPK_INDIRECT:
return nd_addr_storage(d, op);
default:
@@ -458,12 +437,7 @@ static void nd_addr_temps_release(NativeDirectTarget* d,
const NdAddrTemps* temps);
static MemAccess nd_scalar_mem(KitCgTypeId type, u32 size, u32 align) {
- MemAccess mem;
- memset(&mem, 0, sizeof mem);
- mem.type = type;
- mem.size = size;
- mem.align = align;
- return mem;
+ return (MemAccess){.type = type, .size = size, .align = align};
}
static MemAccess nd_type_mem(NativeDirectTarget* d, KitCgTypeId type) {
@@ -480,26 +454,20 @@ static void nd_barrier(NativeDirectTarget* d, u32 flags) {
static void nd_load_frame_to_reg(NativeDirectTarget* d, NativeLoc dst,
NativeFrameSlot frame, KitCgTypeId type) {
- NativeAddr addr;
- MemAccess mem;
- memset(&addr, 0, sizeof addr);
- addr.base_kind = NATIVE_ADDR_BASE_FRAME;
- addr.base.frame = frame;
- addr.base_type = type;
- mem = nd_type_mem(d, type);
+ NativeAddr addr = {.base_kind = NATIVE_ADDR_BASE_FRAME,
+ .base.frame = frame,
+ .base_type = type};
+ MemAccess mem = nd_type_mem(d, type);
ND_REQUIRE_NATIVE(d, load, "target does not emit loads");
d->native->load(d->native, dst, addr, mem);
}
static void nd_store_reg_to_frame(NativeDirectTarget* d, NativeFrameSlot frame,
KitCgTypeId type, NativeLoc src) {
- NativeAddr addr;
- MemAccess mem;
- memset(&addr, 0, sizeof addr);
- addr.base_kind = NATIVE_ADDR_BASE_FRAME;
- addr.base.frame = frame;
- addr.base_type = type;
- mem = nd_type_mem(d, type);
+ NativeAddr addr = {.base_kind = NATIVE_ADDR_BASE_FRAME,
+ .base.frame = frame,
+ .base_type = type};
+ MemAccess mem = nd_type_mem(d, type);
ND_REQUIRE_NATIVE(d, store, "target does not emit stores");
d->native->store(d->native, addr, src, mem);
}
@@ -710,11 +678,10 @@ static NativeAddr nd_addr_materialize(NativeDirectTarget* d, NativeAddr in,
temps->base = r;
temps->index = REG_NONE;
temps->base_cls = cls;
- memset(&out, 0, sizeof out);
- out.base_kind = NATIVE_ADDR_BASE_REG;
- out.base.reg = r;
- out.cls = (u8)cls;
- out.base_type = dst.type;
+ out = (NativeAddr){.base_kind = NATIVE_ADDR_BASE_REG,
+ .base.reg = r,
+ .cls = (u8)cls,
+ .base_type = dst.type};
if (d->native && d->native->addr_legal &&
!d->native->addr_legal(d->native, &out, mem))
nd_panic(d, "native address is not legal");
@@ -755,13 +722,11 @@ static void nd_copy_to_reg(NativeDirectTarget* d, NativeLoc dst,
nd_load_frame_to_reg(d, dst, src.v.frame, dst.type);
break;
case NATIVE_LOC_STACK: {
- NativeAddr addr;
MemAccess mem = nd_type_mem(d, dst.type);
- memset(&addr, 0, sizeof addr);
- addr.base_kind = NATIVE_ADDR_BASE_FRAME;
- addr.base.frame = src.v.stack.slot;
- addr.base_type = dst.type;
- addr.offset = src.v.stack.offset;
+ NativeAddr addr = {.base_kind = NATIVE_ADDR_BASE_FRAME,
+ .base.frame = src.v.stack.slot,
+ .base_type = dst.type,
+ .offset = src.v.stack.offset};
ND_REQUIRE_NATIVE(d, load, "target does not emit loads");
d->native->load(d->native, dst, addr, mem);
break;
@@ -771,12 +736,10 @@ static void nd_copy_to_reg(NativeDirectTarget* d, NativeLoc dst,
d->native->load_imm(d->native, dst, src.v.imm);
break;
case NATIVE_LOC_GLOBAL: {
- NativeAddr addr;
- memset(&addr, 0, sizeof addr);
- addr.base_kind = NATIVE_ADDR_BASE_GLOBAL;
- addr.base.global.sym = src.v.global.sym;
- addr.base.global.addend = src.v.global.addend;
- addr.base_type = dst.type;
+ NativeAddr addr = {.base_kind = NATIVE_ADDR_BASE_GLOBAL,
+ .base.global = {.sym = src.v.global.sym,
+ .addend = src.v.global.addend},
+ .base_type = dst.type};
ND_REQUIRE_NATIVE(d, load_addr, "target does not materialize addresses");
d->native->load_addr(d->native, dst, addr);
break;
@@ -810,14 +773,12 @@ static void nd_write_loc(NativeDirectTarget* d, NativeLoc dst, NativeLoc src,
break;
}
case NATIVE_LOC_STACK: {
- NativeAddr addr;
NativeLoc val =
nd_materialize_loc(d, src, (NativeAllocClass)dst.cls, dst.type);
- memset(&addr, 0, sizeof addr);
- addr.base_kind = NATIVE_ADDR_BASE_FRAME;
- addr.base.frame = dst.v.stack.slot;
- addr.base_type = dst.type;
- addr.offset = dst.v.stack.offset;
+ NativeAddr addr = {.base_kind = NATIVE_ADDR_BASE_FRAME,
+ .base.frame = dst.v.stack.slot,
+ .base_type = dst.type,
+ .offset = dst.v.stack.offset};
ND_REQUIRE_NATIVE(d, store, "target does not emit stores");
d->native->store(d->native, addr, val, mem);
nd_release_materialized(d, val);