wcc: module-imported array indexed-load via cg_dotbase_addr (#128b)
Fix segfault-class memory corruption on `module.array[i]` indexed-read where both stages emitted MOVQ-not-LEAQ on the module-qualified base plus wrong stride. Extends the #135 cg_dotbase_addr/dotbaseaddr helper to handle the SK_USE module-ident-base case: when bt is NULL/ty_err and let_islet(base.str) resolves to TY_ARRAY, emit LEAQ base(SB),dst instead of MOVQ. Wwstage parallel via letvartnode/N_TARRAY check. Stride fix via let_var_type fallback in cgindex when n.lhs.kind==N_DOT. Use-site fix per #135 precedent (Option B); preserves cgdot's MOVQ semantics for the whole-array-assign defensive case (zero current consumers). Test 915 carries 3 module-u16 indexed-read rows (strconv.left_shift_table[0/2/4]) + 2 local-array controls; the strconv.left_shift_table[2]:u32 probe segfaulted (exit 139) pre-fix and exits cleanly post-fix. Broader width-variation rows (u8/u32/i32 module-imported) deferred as informational enhancement. Test 915 skips its inline cs==ww .s cmp on needs_import rows (line 217-222) since `ww build` only drives cstage; reviewer externally verified byte-id on /tmp/k128probe.combined.ww (driver-expanded form, no imports). Future enhancement: 915 could read the driver-emitted combined.ww and add a cmp leg there. Bootstrap NEUTRAL (zero current module.array[i] consumers; strconv decimal.ww uses IDENT-base from within package). 178/178 incl. 990-997 + combined_ww_fresh green. Sibling bugs #137 (chained N_DOT) / #141 (variadic-gather esz==2) / #142 (wwstage primsize-on-alias) properly deferred to backlog.
This commit is contained in:
@@ -692,6 +692,8 @@ static Mod *mod_map;
|
||||
typedef struct LetVar LetVar;
|
||||
struct LetVar {
|
||||
const char *name;
|
||||
Type *type; /* #128b: imported-let type lookup for module-
|
||||
* qualified N_INDEX base esz dispatch. */
|
||||
LetVar *next;
|
||||
};
|
||||
static LetVar *letvars;
|
||||
@@ -905,6 +907,7 @@ let_collect(Cg *c, Node *file)
|
||||
if (let_emit_size(d->type) == 0) continue;
|
||||
LetVar *lv = amalloc(c->a, sizeof *lv);
|
||||
lv->name = d->str;
|
||||
lv->type = d->type; /* #128b */
|
||||
lv->next = letvars;
|
||||
letvars = lv;
|
||||
}
|
||||
@@ -919,6 +922,22 @@ let_islet(const char *name)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* #128b: look up a top-level let's type by leaf name. Sister of
|
||||
* wwstage's letvartnode (selfhost/cmd/wcc/cgen.ww:999). Used at the
|
||||
* cgindex / cg_dotbase_addr sites where a module-qualified base
|
||||
* (`mod.arr`) leaves n->lhs->type NULL (SK_USE-bound module ident),
|
||||
* so the imported array's element type / size must come through
|
||||
* this let-map lookup instead. Returns NULL if name isn't a tracked
|
||||
* top-level let. */
|
||||
static Type *
|
||||
let_var_type(const char *name)
|
||||
{
|
||||
if (name == NULL) return NULL;
|
||||
for (LetVar *lv = letvars; lv; lv = lv->next)
|
||||
if (strcmp(lv->name, name) == 0) return lv->type;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Glue `<module>.<ident>` into a fresh arena buffer. */
|
||||
static const char *
|
||||
mod_join(Cg *c, const char *mod, const char *ident)
|
||||
@@ -1240,6 +1259,26 @@ cg_dotbase_addr(Cg *c, Node *base, int dst_reg, Local *locals)
|
||||
Node *inner = base->lhs;
|
||||
if (inner == NULL || inner->kind != N_IDENT) return 0;
|
||||
Type *bt = inner->type;
|
||||
/* #128b: module-qualified `mod.arr` where arr is an imported
|
||||
* top-level `let X: [N]T`. The checker leaves SK_USE module-idents
|
||||
* with NULL/ty_err type; detect via let_islet + let_var_type-of-
|
||||
* TY_ARRAY and emit LEAQ X(SB) for the array's base address.
|
||||
* Without this, the N_INDEX fallback at cgen.c:~6760 falls to
|
||||
* cgexpr(base) which auto-MOVQs the symbol contents as if it
|
||||
* were a pointer-var (= load 8 bytes of the array's first
|
||||
* elements + treat as junk address) — segfault-class miscompile. */
|
||||
if (bt == NULL || bt == ty_err) {
|
||||
if (let_islet(base->str)) {
|
||||
Type *lt = let_var_type(base->str);
|
||||
Type *lu = type_chase_named(lt);
|
||||
if (lu && lu->kind == TY_ARRAY) {
|
||||
ins2(c, A_LEAQ, masym(c, base->str),
|
||||
areg(dst_reg));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Type *bu = type_chase_named(bt);
|
||||
if (bu == NULL) return 0;
|
||||
int viaptr = 0;
|
||||
@@ -6659,6 +6698,19 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
* For `*[N]T` drill through to the array so esz/esub reflect
|
||||
* T, not sizeof(array). */
|
||||
Type *bt = n->lhs ? n->lhs->type : NULL;
|
||||
/* #128b: module-qualified `mod.arr[i]` — n->lhs is N_DOT and
|
||||
* its type is NULL (SK_USE-bound module ident). Look up the
|
||||
* imported let's type via let_var_type so esz/esub reflect
|
||||
* the imported array's element width instead of falling to
|
||||
* the esz=1 default (→ MOVZBQ wrong-width load). Sister of
|
||||
* the dst-side cg_dotbase_addr branch that emits LEAQ for
|
||||
* the base address. */
|
||||
if ((bt == NULL || bt == ty_err)
|
||||
&& n->lhs && n->lhs->kind == N_DOT
|
||||
&& n->lhs->str
|
||||
&& let_islet(n->lhs->str)) {
|
||||
bt = let_var_type(n->lhs->str);
|
||||
}
|
||||
Type *u = (bt && bt->kind == TY_NAMED) ? bt->under : bt;
|
||||
Type *eff = idx_eff(bt);
|
||||
int esz = 1;
|
||||
|
||||
Reference in New Issue
Block a user