selfhost+cstage+test: graduate frame growth to first-use+fail-loud (#15)
Subsumes #36. Drop wwstage scanlocals pre-pass; both stages converge on first-use+fail-loud frame growth, rule-10 polarity DOWN to leaner side. #36's surfaces (frame-total divergence on match-arm case-let; sibling offset divergence in variadic+iter+match-prev compositions) close naturally — running-max c.frame includes every first-use binding. selfhost/cmd/wcc: add atlocals persistent @-prefix registry surviving cgblock save/restore; add cgoutbuf/cgoutmode/cgout_enable/disable/flush for deferred prologue (emit body to buffer, finalise c.frame, then TEXT/SUBQ + flush); localadd @-prefix dedups against atlocals + fail-louds on size-grow (rule 7 — no silent truncate); cgreturn-tagged routes through @retscr (was colliding with @tagscr on arg-widen sizes); variadic gather esz uses raw primsize (rune->4) not slotsize (rune->8) — matches cstage and fixes the #36 sibling runtime miscompile in non-leaf variadic+iter+match-prev callees. cmd/w6c/cgen.c: drop the over-allocation hack ("for byte-id with wwstage scanlocals reservation") since wwstage no longer over-reserves; add fail-loud on @sretscr size-grow; @tagscr sites pass actual slot_sz instead of stale c.tagscrsz. 748_size_strategy_convergence: table-driven 4 rows x 2 stages (tag_variadic_runearm, trim_iter_match_prev, variadic_gather_rune_stride, leaf_baseline). Each exercises a #36 surface shape; 8/8 ok. Net -1565 lines. Sister latents filed as cosmetic (cs/ws frame size drift on multiple-variadic-call fns): labelseq drift + varargseq stuck at 0 — both bootstrap-byte-id safe (ww2==ww3==ww4 holds since both ww2 and ww3 are wwstage outputs). make test 122/122; ww2==ww3==ww4 byte-id holds via 995_self_rebuild.
This commit is contained in:
@@ -377,6 +377,13 @@ type structinfo = struct {
|
||||
type local = struct {
|
||||
name: str,
|
||||
off: i32,
|
||||
sz: i32, // allocated slot size; carried so @-prefix reuse can
|
||||
// fail-loud (rule 7) if a later site needs a larger
|
||||
// slot than the first allocation pinned. Per #15/#26c
|
||||
// size-strategy convergence — wwstage dropped its
|
||||
// scanlocals pre-pass, so @tagscr/@retscr/@sretscr/
|
||||
// @tagbase are sized at first-use; subsequent uses
|
||||
// must fit.
|
||||
tnode: *node, // declared type expr (nkind.N_TNAME / nkind.N_TPTR / ...) or nil
|
||||
lnext: *local,
|
||||
};
|
||||
@@ -422,6 +429,17 @@ def DEFER_MAX: i32 = 16;
|
||||
type cgen = struct {
|
||||
a: *arena,
|
||||
locals: *local,
|
||||
// atlocals — persistent registry of `@`-prefix scratch slots
|
||||
// for the current fn. cgblock save/restores c.locals to scope
|
||||
// inner shadows (post-#27); a return/cgindex/cgwidentaggedstore
|
||||
// inside one block must not reallocate @retscr/@tagscr when a
|
||||
// sibling block uses them again. cgblock leaves atlocals alone
|
||||
// so the slot offsets survive. localadd checks here first for
|
||||
// @-prefix names; localfind falls back here when c.locals misses
|
||||
// an @-name. Pre-#15 this was a handful of named offsets on the
|
||||
// cgen (c.retscroff / c.sretargoff / c.sretscroff); post-#15
|
||||
// every @-name flows through the same registry.
|
||||
atlocals: *local,
|
||||
frame: i32,
|
||||
lastwasreturn: i32,
|
||||
labelseq: i32,
|
||||
@@ -451,55 +469,34 @@ type cgen = struct {
|
||||
yieldbuf: *str, // stack of match end labels for yield
|
||||
defertop: i32,
|
||||
deferbuf: **node, // stack of deferred exprs (LIFO at return)
|
||||
// Variadic-call gather state. scanlocals walks the body in pre-
|
||||
// order DFS and assigns per-call scratch names `@vararg_d_N` /
|
||||
// `@vararg_sl_N` using this counter; cgcall resets and walks in
|
||||
// the same order so the names line up at emission time.
|
||||
// Variadic-call gather state. cgcall assigns per-call scratch
|
||||
// names `@vararg_d_N` / `@vararg_sl_N` using this counter;
|
||||
// post #15 the seq is bumped only at emit time so a single
|
||||
// sequence is observed (the scanlocals pre-pass was dropped).
|
||||
varargseq: i32,
|
||||
// Max @tagscr slot_sz across all reservation sites in the current
|
||||
// function. scanlocals bumps; every emit-time `localadd("@tagscr",
|
||||
// ...)` passes this same size so the first allocation lands a slot
|
||||
// big enough for every later user. Single source of truth — pins
|
||||
// rob's "scan + emit lockstep" invariant. Reset per cgfn.
|
||||
tagscrsz: i32,
|
||||
// Live @retscr offset (#14). c.locals-based `@`-prefix dedup in
|
||||
// localadd is unwound by cgblock save/restore (post-#27), so a
|
||||
// second `return` in a sibling/outer block reallocates a fresh
|
||||
// slot — emit grew the frame past what scanlocals reserved, and
|
||||
// the stomp landed below SP. retscroff is the persistent SSoT:
|
||||
// 0 means "not yet allocated"; first emit-site sets it, every
|
||||
// later emit reuses. Mirrors c.tagscrsz pattern (#38) but tracks
|
||||
// offset, not size (per-fn return type is fixed, so size is too).
|
||||
retscroff: i32,
|
||||
// System V AMD64 sret discipline (#23). Plain TY_STRUCT returns
|
||||
// with size > 24B are passed via a hidden first-arg pointer
|
||||
// (RDI) to a caller-prealloc dest; the callee writes through
|
||||
// that pointer and returns it in RAX.
|
||||
//
|
||||
// sretargoff — callee-side @sretarg slot (8B, holds saved RDI).
|
||||
// Set in cgfn prologue when the fn's return type
|
||||
// triggers sret. 0 means N/A.
|
||||
// sretdestoff — caller-side dest BP offset, propagated from a
|
||||
// receive site (cglet / cgassign ident) to the
|
||||
// nested cgexpr → cgcall so the call emits
|
||||
// `LEAQ off(BP), DI` instead of allocating a
|
||||
// scratch. 0 means no receiver wired.
|
||||
// sretscroff — per-fn @sretscr discard slot, used by sret CALLs
|
||||
// whose result has no named receiver. Single-slot
|
||||
// SSoT mirroring c.retscroff; the scanlocals walk
|
||||
// sums c.sretscrsz to pre-reserve.
|
||||
// sretscrsz — max sret discard size in this fn (sums during
|
||||
// scanlocals, consumed by localadd("@sretscr", ...)).
|
||||
// sretforward — set by cgreturn `return f();` from an sret callee to
|
||||
// signal cgcall: source RDI for inner from outer's
|
||||
// saved @sretarg (MOVQ) instead of LEAQ'ing a local
|
||||
// dest. Inner writes into outer's caller-prealloc;
|
||||
// inner's RAX (the dest pointer) is already outer's
|
||||
// return value. Cleared after cgcall consumes it.
|
||||
sretargoff: i32,
|
||||
//
|
||||
// The single-slot caches for @sretarg / @sretscr / @retscr that
|
||||
// used to live here are gone: localadd's `@`-prefix dedup against
|
||||
// c.locals (fail-loud on size grow) is the SSoT now. cgenstmt /
|
||||
// cgenexpr resolve `@sretarg` via localfind when they need the
|
||||
// saved RDI.
|
||||
sretdestoff: i32,
|
||||
sretscroff: i32,
|
||||
sretscrsz: i32,
|
||||
sretforward: i32,
|
||||
};
|
||||
|
||||
@@ -518,16 +515,12 @@ type letvar = struct {
|
||||
fn cgeninit(c: *cgen, a: *arena) void = {
|
||||
c.a = a;
|
||||
c.locals = nil;
|
||||
c.atlocals = nil;
|
||||
c.frame = 0;
|
||||
c.lastwasreturn = 0;
|
||||
c.labelseq = 0;
|
||||
c.varargseq = 0;
|
||||
c.tagscrsz = 0;
|
||||
c.retscroff = 0;
|
||||
c.sretargoff = 0;
|
||||
c.sretdestoff = 0;
|
||||
c.sretscroff = 0;
|
||||
c.sretscrsz = 0;
|
||||
c.sretforward = 0;
|
||||
// Note: strlit_seq, strlits, ffis are *not* reset here; they
|
||||
// persist across cgfn calls within one file. cgfile resets them
|
||||
@@ -542,10 +535,9 @@ fn cgeninit(c: *cgen, a: *arena) void = {
|
||||
};
|
||||
|
||||
// localalloc — append a slot for `name` without dedup. Used for
|
||||
// match-arm bindings, which C cgen allocates via cgexpr's by-value
|
||||
// match-arm bindings, which cstage allocates via cgexpr's by-value
|
||||
// `locals` list — so two separate matches each get fresh slots even
|
||||
// when their bind names collide. scanlocals follows the same rule
|
||||
// for nkind.N_MCASE.
|
||||
// when their bind names collide.
|
||||
fn localalloc(c: *cgen, name: str, sz: i32, tnode: *node) i32 = {
|
||||
let asz: i32 = sz;
|
||||
if (asz < 8) { asz = 8; };
|
||||
@@ -555,6 +547,7 @@ fn localalloc(c: *cgen, name: str, sz: i32, tnode: *node) i32 = {
|
||||
let l: *local = amalloc(c.a, 48u64): *local;
|
||||
l.name = name;
|
||||
l.off = off;
|
||||
l.sz = asz;
|
||||
l.tnode = tnode;
|
||||
l.lnext = c.locals;
|
||||
c.locals = l;
|
||||
@@ -570,6 +563,7 @@ fn localaddstack(c: *cgen, name: str, tnode: *node, off: i32) void = {
|
||||
let l: *local = amalloc(c.a, 48u64): *local;
|
||||
l.name = name;
|
||||
l.off = off;
|
||||
l.sz = 0;
|
||||
l.tnode = tnode;
|
||||
l.lnext = c.locals;
|
||||
c.locals = l;
|
||||
@@ -579,86 +573,60 @@ fn localadd(c: *cgen, name: str, sz: i32, tnode: *node) i32 = {
|
||||
// User-let path (post-#27): always allocate a fresh slot per
|
||||
// binding. Pre-fix this deduped by name to share one slot
|
||||
// across same-name lets in disjoint scopes — inherited from
|
||||
// C cgen's localoff. Both stages had the same silent-stack-
|
||||
// cstage's localoff. Both stages had the same silent-stack-
|
||||
// corruption bug: an inner 8B `let a: i64` allocated first
|
||||
// would force a later outer `let a: [128]u8` onto the 8B slot,
|
||||
// and `a[127]` would write at +119(BP), past the saved RIP.
|
||||
// Localfind walks head-first, so the most-recent binding still
|
||||
// wins lookups inside its scope. Tnode is carried on the
|
||||
// freshly-pushed entry, so type dispatch in cgenutil never
|
||||
// sees a stale predecessor.
|
||||
//
|
||||
// Synthetic scratch slots (`@tagscr`, `@retscr`, `@tagbase`)
|
||||
// keep the per-fn dedup. Each scratch is sized identically
|
||||
// across its call sites and intended to be shared — the
|
||||
// scanlocals pre-pass also dedups via scanseenmark, so frame
|
||||
// reservation and emit-time allocation stay in sync. The
|
||||
// `@`-prefix carve-out preserves that contract; user names
|
||||
// can never start with `@` (lexer-rejected).
|
||||
//
|
||||
// @retscr (#14) routes through c.retscroff instead of c.locals.
|
||||
// The c.locals-based dedup is unwound by cgblock save/restore
|
||||
// (post-#27): a return inside an `if` block adds @retscr to
|
||||
// c.locals; on block exit, c.locals reverts and a sibling/outer
|
||||
// return reallocates a fresh slot. Scan had reserved one slot;
|
||||
// emit grew the frame past the reservation and the second
|
||||
// site's writes landed below SP. c.retscroff is per-fn state
|
||||
// that survives cgblock save/restore and pins single-slot.
|
||||
// `@`-prefix scratch slots (`@tagscr`, `@retscr`, `@tagbase`,
|
||||
// `@sretarg`, `@sretscr`, `@match_spill`, `@vararg_*`) share
|
||||
// one slot per name per fn. Post #15/#26c the slot is sized
|
||||
// at first use and reused by every later caller; a later
|
||||
// caller asking for a larger slot than the first allocation
|
||||
// pinned fatals (rule 7 — surface, don't silently corrupt
|
||||
// the frame: the pinned offset already neighbours other
|
||||
// locals so the slot can't grow in place). Mirrors cstage's
|
||||
// cg_tagscr / cg_retscr / cg_sretscr same-fn caches in
|
||||
// cmd/w6c/cgen.c (#26 / #15).
|
||||
if (name.len > 0) {
|
||||
if (name[0] == 64u8) { // '@'
|
||||
if (streq(name, "@retscr")) {
|
||||
if (c.retscroff != 0) { return c.retscroff; };
|
||||
let off: i32 = localalloc(c, name, sz, tnode);
|
||||
c.retscroff = off;
|
||||
return off;
|
||||
};
|
||||
// @sretarg / @sretscr (#23): same single-slot SSoT
|
||||
// pattern as @retscr. @sretarg holds the saved hidden
|
||||
// RDI for sret callees (8B, set once per fn at the
|
||||
// prologue); @sretscr is the caller-side discard slot
|
||||
// for sret CALLs whose result is dropped.
|
||||
if (streq(name, "@sretarg")) {
|
||||
if (c.sretargoff != 0) { return c.sretargoff; };
|
||||
let off: i32 = localalloc(c, name, sz, tnode);
|
||||
c.sretargoff = off;
|
||||
return off;
|
||||
};
|
||||
if (streq(name, "@sretscr")) {
|
||||
if (c.sretscroff != 0) { return c.sretscroff; };
|
||||
let off: i32 = localalloc(c, name, sz, tnode);
|
||||
c.sretscroff = off;
|
||||
return off;
|
||||
};
|
||||
let cur: *local = c.locals;
|
||||
let asz: i32 = sz;
|
||||
if (asz < 8) { asz = 8; };
|
||||
if ((asz & 7) != 0) { asz = (asz + 7) & ~7; };
|
||||
let cur: *local = c.atlocals;
|
||||
for (cur != nil) {
|
||||
let cn: str = cur.name;
|
||||
if (streq(cn, name)) {
|
||||
if (asz > cur.sz) {
|
||||
// rule-7 surface, post-#15: pinned slot
|
||||
// offset can't grow in place.
|
||||
let msg: str = "localadd: @-prefix slot grew within fn\n";
|
||||
os.write(2, msg.ptr, msg.len: u64);
|
||||
os.exit(1);
|
||||
};
|
||||
cur.tnode = tnode;
|
||||
return cur.off;
|
||||
};
|
||||
cur = cur.lnext;
|
||||
};
|
||||
// First use: allocate via localalloc (bumps c.frame +
|
||||
// pushes to c.locals so localfind sees it within this
|
||||
// block) and pin a parallel entry in c.atlocals so the
|
||||
// allocation survives cgblock save/restore.
|
||||
let off: i32 = localalloc(c, name, sz, tnode);
|
||||
let at: *local = amalloc(c.a, 48u64): *local;
|
||||
at.name = name;
|
||||
at.off = off;
|
||||
at.sz = asz;
|
||||
at.tnode = tnode;
|
||||
at.lnext = c.atlocals;
|
||||
c.atlocals = at;
|
||||
return off;
|
||||
};
|
||||
};
|
||||
return localalloc(c, name, sz, tnode);
|
||||
};
|
||||
|
||||
// scanseenmark — called by scanlocals on every let / match-bind
|
||||
// site. Returns true if `name` is already tracked in c.locals (so
|
||||
// the slot will be shared at emission time — no new frame bump).
|
||||
// Otherwise appends a name-only stub and returns false. Stubs are
|
||||
// thrown away when cgfn resets c.locals before emission.
|
||||
fn scanseenmark(c: *cgen, name: str) bool = {
|
||||
if (localfindnode(c, name) != nil) { return true; };
|
||||
let l: *local = amalloc(c.a, 48u64): *local;
|
||||
l.name = name;
|
||||
l.off = 0;
|
||||
l.tnode = nil;
|
||||
l.lnext = c.locals;
|
||||
c.locals = l;
|
||||
return false;
|
||||
};
|
||||
|
||||
fn localfindnode(c: *cgen, name: str) *local = {
|
||||
let l: *local = c.locals;
|
||||
for (l != nil) {
|
||||
@@ -666,6 +634,18 @@ fn localfindnode(c: *cgen, name: str) *local = {
|
||||
if (streq(ln, name)) { return l; };
|
||||
l = l.lnext;
|
||||
};
|
||||
// @-prefix scratch slots survive cgblock save/restore via
|
||||
// c.atlocals; a localfindnode from a sibling/outer block must
|
||||
// still resolve them.
|
||||
if (name.len > 0) {
|
||||
if (name[0] == 64u8) {
|
||||
let a: *local = c.atlocals;
|
||||
for (a != nil) {
|
||||
if (streq(a.name, name)) { return a; };
|
||||
a = a.lnext;
|
||||
};
|
||||
};
|
||||
};
|
||||
return nil;
|
||||
};
|
||||
|
||||
@@ -684,21 +664,92 @@ fn localfind(c: *cgen, name: str) i32 = {
|
||||
};
|
||||
l = l.lnext;
|
||||
};
|
||||
if (name.len > 0) {
|
||||
if (name[0] == 64u8) {
|
||||
let a: *local = c.atlocals;
|
||||
for (a != nil) {
|
||||
if (streq(a.name, name)) { return a.off; };
|
||||
a = a.lnext;
|
||||
};
|
||||
};
|
||||
};
|
||||
return 0;
|
||||
};
|
||||
|
||||
// ---- emit helpers ---------------------------------------------------
|
||||
|
||||
fn emitline(s: str) void = { os.write(1, s.ptr, s.len: u64); };
|
||||
// Cgfn defers its prologue (TEXT / SUBQ) until after the body so the
|
||||
// frame size reflects every emit-time localadd — the scanlocals pre-
|
||||
// pass that previously pre-computed it was dropped per #15/#26c. The
|
||||
// body is captured into cgoutbuf while cgoutmode != 0, then flushed
|
||||
// after the prologue is written to stdout. Module-level state so the
|
||||
// existing emitline/emitint/emitlabel/emitsymname callers don't have
|
||||
// to thread a *cgen they don't already hold. Mirrors cstage's deferred
|
||||
// Prog-chain emit (cmd/w6c/cgen.c cgfn allocates `subsp`/`text` up
|
||||
// front and patches `from.offset` after the body finishes).
|
||||
let cgoutbuf: *u8 = nil;
|
||||
let cgoutbufcap: i32 = 0;
|
||||
let cgoutbuflen: i32 = 0;
|
||||
let cgoutmode: i32 = 0;
|
||||
let cgoutarena: *arena = nil;
|
||||
|
||||
def CGOUT_INIT_CAP: i32 = 65536;
|
||||
|
||||
fn cgout_grow(need: i32) void = {
|
||||
if (need <= cgoutbufcap) { return; };
|
||||
let want: i32 = cgoutbufcap;
|
||||
if (want == 0) { want = CGOUT_INIT_CAP; };
|
||||
for (want < need) { want = want * 2; };
|
||||
let p: *u8 = amalloc(cgoutarena, want: u64): *u8;
|
||||
let i: i32 = 0;
|
||||
for (i < cgoutbuflen) {
|
||||
p[i] = cgoutbuf[i];
|
||||
i += 1;
|
||||
};
|
||||
cgoutbuf = p;
|
||||
cgoutbufcap = want;
|
||||
};
|
||||
|
||||
fn cgout_enable(a: *arena) void = {
|
||||
cgoutarena = a;
|
||||
cgoutbuflen = 0;
|
||||
cgoutmode = 1;
|
||||
};
|
||||
|
||||
fn cgout_disable() void = { cgoutmode = 0; };
|
||||
|
||||
fn cgout_flush() void = {
|
||||
if (cgoutbuflen > 0) {
|
||||
os.write(1, cgoutbuf, cgoutbuflen: u64);
|
||||
cgoutbuflen = 0;
|
||||
};
|
||||
};
|
||||
|
||||
fn emitbytes(p: *u8, n: u64) void = {
|
||||
if (cgoutmode != 0) {
|
||||
let nn: i32 = n: i32;
|
||||
cgout_grow(cgoutbuflen + nn);
|
||||
let i: i32 = 0;
|
||||
for (i < nn) {
|
||||
cgoutbuf[cgoutbuflen + i] = p[i];
|
||||
i += 1;
|
||||
};
|
||||
cgoutbuflen += nn;
|
||||
} else {
|
||||
os.write(1, p, n);
|
||||
};
|
||||
};
|
||||
|
||||
fn emitline(s: str) void = { emitbytes(s.ptr, s.len: u64); };
|
||||
|
||||
fn emitint(v: i64) void = {
|
||||
let s: str = strconv.i64tos(v, strconv.base.DEC);
|
||||
os.write(1, s.ptr, s.len: u64);
|
||||
emitbytes(s.ptr, s.len: u64);
|
||||
};
|
||||
|
||||
fn emituint(v: u64) void = {
|
||||
let s: str = strconv.u64tos(v, strconv.base.DEC);
|
||||
os.write(1, s.ptr, s.len: u64);
|
||||
emitbytes(s.ptr, s.len: u64);
|
||||
};
|
||||
|
||||
// emitdispreg — print "disp(reg)" or "(reg)" when disp == 0, the
|
||||
@@ -755,7 +806,7 @@ fn mklabel(c: *cgen, prefix: str) str = {
|
||||
};
|
||||
|
||||
fn emitlabel(s: str) void = {
|
||||
os.write(1, s.ptr, s.len: u64);
|
||||
emitbytes(s.ptr, s.len: u64);
|
||||
emitline(":\n");
|
||||
};
|
||||
|
||||
@@ -1076,7 +1127,7 @@ fn emitdatawbyte(b: u8) void = {
|
||||
else { bb[0] = (hi - 10u8) + 97u8; };
|
||||
if (lo < 10u8) { bb[1] = lo + 48u8; }
|
||||
else { bb[1] = (lo - 10u8) + 97u8; };
|
||||
os.write(1, bb.ptr, 2u64);
|
||||
emitbytes( bb.ptr, 2u64);
|
||||
return;
|
||||
};
|
||||
if (b >= 127u8) {
|
||||
@@ -1088,12 +1139,12 @@ fn emitdatawbyte(b: u8) void = {
|
||||
else { bb[0] = (hi - 10u8) + 97u8; };
|
||||
if (lo < 10u8) { bb[1] = lo + 48u8; }
|
||||
else { bb[1] = (lo - 10u8) + 97u8; };
|
||||
os.write(1, bb.ptr, 2u64);
|
||||
emitbytes( bb.ptr, 2u64);
|
||||
return;
|
||||
};
|
||||
let bb: [1]u8;
|
||||
bb[0] = b;
|
||||
os.write(1, bb.ptr, 1u64);
|
||||
emitbytes( bb.ptr, 1u64);
|
||||
};
|
||||
|
||||
// letpreintern — intern strlits referenced from top-level str-let
|
||||
@@ -1258,7 +1309,7 @@ fn emitletdataw(c: *cgen, file: *node) void = {
|
||||
emitline("DATAR ");
|
||||
emitsymname(c, nm);
|
||||
emitline("+0(SB),");
|
||||
os.write(1, lab.ptr, lab.len: u64);
|
||||
emitbytes( lab.ptr, lab.len: u64);
|
||||
emitline("(SB)\n");
|
||||
} else {
|
||||
// zero-init: accept no rhs, nil,
|
||||
@@ -1426,12 +1477,12 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
emitline("DATA ");
|
||||
if (d.exported == 0) {
|
||||
if (d.nmod.len > 0) {
|
||||
os.write(1, d.nmod.ptr, d.nmod.len: u64);
|
||||
os.write(1, ".".ptr, 1u64);
|
||||
emitbytes( d.nmod.ptr, d.nmod.len: u64);
|
||||
emitbytes( ".".ptr, 1u64);
|
||||
};
|
||||
};
|
||||
let nm: str = d.str;
|
||||
os.write(1, nm.ptr, nm.len: u64);
|
||||
emitbytes( nm.ptr, nm.len: u64);
|
||||
emitline("(SB),\"");
|
||||
let i: i32 = 0;
|
||||
let n: u64 = v;
|
||||
@@ -1452,7 +1503,7 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
else { bb[0] = (hi - 10u8) + 97u8; };
|
||||
if (lo < 10u8) { bb[1] = lo + 48u8; }
|
||||
else { bb[1] = (lo - 10u8) + 97u8; };
|
||||
os.write(1, bb.ptr, 2u64);
|
||||
emitbytes( bb.ptr, 2u64);
|
||||
} else {
|
||||
if (b >= 127u8) {
|
||||
emitline("\\x");
|
||||
@@ -1463,11 +1514,11 @@ fn emitdefconstants(c: *cgen, file: *node) void = {
|
||||
else { bb[0] = (hi - 10u8) + 97u8; };
|
||||
if (lo < 10u8) { bb[1] = lo + 48u8; }
|
||||
else { bb[1] = (lo - 10u8) + 97u8; };
|
||||
os.write(1, bb.ptr, 2u64);
|
||||
emitbytes( bb.ptr, 2u64);
|
||||
} else {
|
||||
let bb: [1]u8;
|
||||
bb[0] = b;
|
||||
os.write(1, bb.ptr, 1u64);
|
||||
emitbytes( bb.ptr, 1u64);
|
||||
};
|
||||
};
|
||||
};};
|
||||
@@ -1487,7 +1538,7 @@ fn emitdatasection(c: *cgen) void = {
|
||||
for (s != nil) {
|
||||
emitline("DATA ");
|
||||
let lab: str = s.label;
|
||||
os.write(1, lab.ptr, lab.len: u64);
|
||||
emitbytes( lab.ptr, lab.len: u64);
|
||||
emitline("(SB),\"");
|
||||
let bs: str = s.bytes;
|
||||
let i: i32 = 0;
|
||||
@@ -1508,7 +1559,7 @@ fn emitdatasection(c: *cgen) void = {
|
||||
else { bb[0] = (hi - 10u8) + 97u8; };
|
||||
if (lo < 10u8) { bb[1] = lo + 48u8; }
|
||||
else { bb[1] = (lo - 10u8) + 97u8; };
|
||||
os.write(1, bb.ptr, 2u64);
|
||||
emitbytes( bb.ptr, 2u64);
|
||||
} else {
|
||||
if (b >= 127u8) {
|
||||
emitline("\\x");
|
||||
@@ -1519,11 +1570,11 @@ fn emitdatasection(c: *cgen) void = {
|
||||
else { bb[0] = (hi - 10u8) + 97u8; };
|
||||
if (lo < 10u8) { bb[1] = lo + 48u8; }
|
||||
else { bb[1] = (lo - 10u8) + 97u8; };
|
||||
os.write(1, bb.ptr, 2u64);
|
||||
emitbytes( bb.ptr, 2u64);
|
||||
} else {
|
||||
let bb: [1]u8;
|
||||
bb[0] = b;
|
||||
os.write(1, bb.ptr, 1u64);
|
||||
emitbytes( bb.ptr, 1u64);
|
||||
};
|
||||
};
|
||||
};};};};};
|
||||
@@ -1891,15 +1942,15 @@ fn emitsymname(c: *cgen, ident: str) void = {
|
||||
let resolved: str = ffiresolve(c, ident);
|
||||
if (resolved.ptr != ident.ptr) {
|
||||
// FFI hit — emit the mapped linker symbol verbatim.
|
||||
os.write(1, resolved.ptr, resolved.len: u64);
|
||||
emitbytes( resolved.ptr, resolved.len: u64);
|
||||
return;
|
||||
};
|
||||
let mod: str = modlookup(c, ident);
|
||||
if (mod.len > 0) {
|
||||
os.write(1, mod.ptr, mod.len: u64);
|
||||
os.write(1, ".".ptr, 1u64);
|
||||
emitbytes( mod.ptr, mod.len: u64);
|
||||
emitbytes( ".".ptr, 1u64);
|
||||
};
|
||||
os.write(1, ident.ptr, ident.len: u64);
|
||||
emitbytes( ident.ptr, ident.len: u64);
|
||||
};
|
||||
|
||||
// emitfnname — write the asm symbol name for a fn `ident`, threading
|
||||
@@ -1910,15 +1961,15 @@ fn emitsymname(c: *cgen, ident: str) void = {
|
||||
fn emitfnname(c: *cgen, ident: str, hint: str) void = {
|
||||
let resolved: str = ffiresolve(c, ident);
|
||||
if (resolved.ptr != ident.ptr) {
|
||||
os.write(1, resolved.ptr, resolved.len: u64);
|
||||
emitbytes( resolved.ptr, resolved.len: u64);
|
||||
return;
|
||||
};
|
||||
let mod: str = modlookupforfn(c, ident, hint);
|
||||
if (mod.len > 0) {
|
||||
os.write(1, mod.ptr, mod.len: u64);
|
||||
os.write(1, ".".ptr, 1u64);
|
||||
emitbytes( mod.ptr, mod.len: u64);
|
||||
emitbytes( ".".ptr, 1u64);
|
||||
};
|
||||
os.write(1, ident.ptr, ident.len: u64);
|
||||
emitbytes( ident.ptr, ident.len: u64);
|
||||
};
|
||||
|
||||
// ---- FFI map ---------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user