kit

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

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:
Msrc/arch/native_target.h | 31++++++++++++-------------------
Msrc/cg/native_direct_target.c | 189+++++++++++++++++++++++++++++++------------------------------------------------
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);