cgen: sub-slice ptr = base + lo*esz (both stages, #76)
A sub-slice base[lo:hi] advanced its data pointer by lo (element COUNT) instead of lo*esz (BYTES), so the base pointer was wrong for any esz>1 element. Pointer arithmetic is membsz-unit per the rt invariant (ref/hare/rt/ensure.ha:30); esz==1 (u8/str) is unchanged. Four emission sites, fixed byte-identically across stages (rule 10): - value path: cmd/w6c/cgen.c N_SLICE <-> cgenexpr.ww cgslice - call-arg: cmd/w6c/cgen.c:4646 <-> cgenutil.ww pushargsrev Scaling mirrors the cgindex idiom: esz from the type table (rule 13; cstage bu->sub->size, wwstage elemsizeofc) gated to an N_IDENT base, uniform IMULQ (no SHL special-case, no immediate form -- w6a is reg-reg only). The live lo reg is the multiplicand so the one free GP (DX value / BX arg) holds esz*lo; lo is preserved for len (hi-lo) and cap (base_cap-lo, #20). The esz==1 path keeps the single ADDQ, byte-identical to before (#75/#20/str unaffected). Non-ident bases stay unscaled in both stages (wwstage has no tnode there), tracked as a #76 residual alongside #74. New 943_subslice_ptresz_run: table-driven, dual-driver (ww/ww_ww), esz in {2,4,8} array+slice base, lo>0, let-form + call-arg form; asserts s[0]==base[lo] & s[1]==base[lo+1]. Fails on every fixture pre-fix on both stages, passes post-fix. Registered in Makefile (TESTS + target) so test/run builds and runs it.
This commit is contained in:
@@ -10863,6 +10863,15 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// esz from the type table for an N_IDENT base (#76; mirrors
|
||||
// the cgindex idiom). Non-ident base stays esz=1 -> ptr
|
||||
// unscaled, matching cstage's base->kind==N_IDENT gate.
|
||||
let esz: i32 = 1;
|
||||
if (baselocal != nil) {
|
||||
esz = elemsizeofc(c, baselocal.tnode);
|
||||
} else { if (globaltn != nil) {
|
||||
esz = elemsizeofc(c, globaltn);
|
||||
};};
|
||||
// base address → push
|
||||
if (baselocal != nil) {
|
||||
let tn: *node = baselocal.tnode;
|
||||
@@ -10949,7 +10958,18 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
emitline("\tPOPQ\tCX\n"); // base
|
||||
emitline("\tMOVQ\tBX, DX\n"); // DX = hi
|
||||
emitline("\tSUBQ\tAX, DX\n"); // DX = hi - lo = len
|
||||
emitline("\tADDQ\tAX, CX\n"); // CX = base + lo = ptr
|
||||
// ptr = base + lo*esz (#76; ensure.ha:30 membsz-unit).
|
||||
// BX=lo*esz; AX=lo PRESERVED for cap. BX (dead hi) reloaded
|
||||
// by cgbasecap below.
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", BX\n");
|
||||
emitline("\tIMULQ\tAX, BX\n");
|
||||
emitline("\tADDQ\tBX, CX\n");
|
||||
} else {
|
||||
emitline("\tADDQ\tAX, CX\n"); // CX = base + lo = ptr
|
||||
};
|
||||
// cap = base_cap - lo (#20); AX=lo, BX free.
|
||||
if (cgbasecap(c, base, "BX")) {
|
||||
emitline("\tSUBQ\tAX, BX\n");
|
||||
@@ -14861,11 +14881,12 @@ fn cgbasecap(c: *cgen, base: *node, dst: str) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
// cgslice — `base[lo:hi]` as a slice value. Leaves (AX=base+lo,
|
||||
// cgslice — `base[lo:hi]` as a slice value. Leaves (AX=base+lo*esz,
|
||||
// BX=hi-lo, CX=base_cap-lo) so callers can route to a slice slot,
|
||||
// return, or arg with the same triple ABI. cap is the storage
|
||||
// remaining to the base's end (#20, Go/Hare-identical) via cgbasecap.
|
||||
// Element scaling on the ptr isn't wired (lo*esz is #76).
|
||||
// ptr advances by BYTES (lo*esz, #76; ref/hare/rt/ensure.ha:30
|
||||
// membsz-unit); esz from the type table, mirroring the cgindex idiom.
|
||||
fn cgslice(c: *cgen, n: *node) void = {
|
||||
let base: *node = n.lhs;
|
||||
let lo: *node = n.rhs;
|
||||
@@ -14886,6 +14907,15 @@ fn cgslice(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// esz from the type table for an N_IDENT base (#76; mirrors the
|
||||
// cgindex idiom). Non-ident base stays esz=1 -> ptr unscaled,
|
||||
// matching cstage's base->kind==N_IDENT gate.
|
||||
let esz: i32 = 1;
|
||||
if (baselocal != nil) {
|
||||
esz = elemsizeofc(c, baselocal.tnode);
|
||||
} else { if (globaltn != nil) {
|
||||
esz = elemsizeofc(c, globaltn);
|
||||
};};
|
||||
// base address
|
||||
if (baselocal != nil) {
|
||||
let tn: *node = baselocal.tnode;
|
||||
@@ -14981,7 +15011,17 @@ fn cgslice(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
emitline("\tPOPQ\tCX\n");
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tADDQ\tCX, AX\n");
|
||||
// ptr = base + lo*esz (#76; ensure.ha:30 membsz-unit).
|
||||
// DX=lo*esz; CX=lo PRESERVED for len + cap (#20).
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", DX\n");
|
||||
emitline("\tIMULQ\tCX, DX\n");
|
||||
emitline("\tADDQ\tDX, AX\n");
|
||||
} else {
|
||||
emitline("\tADDQ\tCX, AX\n");
|
||||
};
|
||||
emitline("\tSUBQ\tCX, BX\n");
|
||||
// cap = base_cap - lo (#20); CX=lo, BX=len here.
|
||||
if (cgbasecap(c, base, "DX")) {
|
||||
|
||||
@@ -1022,11 +1022,12 @@ fn cgbasecap(c: *cgen, base: *node, dst: str) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
// cgslice — `base[lo:hi]` as a slice value. Leaves (AX=base+lo,
|
||||
// cgslice — `base[lo:hi]` as a slice value. Leaves (AX=base+lo*esz,
|
||||
// BX=hi-lo, CX=base_cap-lo) so callers can route to a slice slot,
|
||||
// return, or arg with the same triple ABI. cap is the storage
|
||||
// remaining to the base's end (#20, Go/Hare-identical) via cgbasecap.
|
||||
// Element scaling on the ptr isn't wired (lo*esz is #76).
|
||||
// ptr advances by BYTES (lo*esz, #76; ref/hare/rt/ensure.ha:30
|
||||
// membsz-unit); esz from the type table, mirroring the cgindex idiom.
|
||||
fn cgslice(c: *cgen, n: *node) void = {
|
||||
let base: *node = n.lhs;
|
||||
let lo: *node = n.rhs;
|
||||
@@ -1047,6 +1048,15 @@ fn cgslice(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// esz from the type table for an N_IDENT base (#76; mirrors the
|
||||
// cgindex idiom). Non-ident base stays esz=1 -> ptr unscaled,
|
||||
// matching cstage's base->kind==N_IDENT gate.
|
||||
let esz: i32 = 1;
|
||||
if (baselocal != nil) {
|
||||
esz = elemsizeofc(c, baselocal.tnode);
|
||||
} else { if (globaltn != nil) {
|
||||
esz = elemsizeofc(c, globaltn);
|
||||
};};
|
||||
// base address
|
||||
if (baselocal != nil) {
|
||||
let tn: *node = baselocal.tnode;
|
||||
@@ -1142,7 +1152,17 @@ fn cgslice(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
emitline("\tPOPQ\tCX\n");
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tADDQ\tCX, AX\n");
|
||||
// ptr = base + lo*esz (#76; ensure.ha:30 membsz-unit).
|
||||
// DX=lo*esz; CX=lo PRESERVED for len + cap (#20).
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", DX\n");
|
||||
emitline("\tIMULQ\tCX, DX\n");
|
||||
emitline("\tADDQ\tDX, AX\n");
|
||||
} else {
|
||||
emitline("\tADDQ\tCX, AX\n");
|
||||
};
|
||||
emitline("\tSUBQ\tCX, BX\n");
|
||||
// cap = base_cap - lo (#20); CX=lo, BX=len here.
|
||||
if (cgbasecap(c, base, "DX")) {
|
||||
|
||||
@@ -302,6 +302,15 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// esz from the type table for an N_IDENT base (#76; mirrors
|
||||
// the cgindex idiom). Non-ident base stays esz=1 -> ptr
|
||||
// unscaled, matching cstage's base->kind==N_IDENT gate.
|
||||
let esz: i32 = 1;
|
||||
if (baselocal != nil) {
|
||||
esz = elemsizeofc(c, baselocal.tnode);
|
||||
} else { if (globaltn != nil) {
|
||||
esz = elemsizeofc(c, globaltn);
|
||||
};};
|
||||
// base address → push
|
||||
if (baselocal != nil) {
|
||||
let tn: *node = baselocal.tnode;
|
||||
@@ -388,7 +397,18 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
emitline("\tPOPQ\tCX\n"); // base
|
||||
emitline("\tMOVQ\tBX, DX\n"); // DX = hi
|
||||
emitline("\tSUBQ\tAX, DX\n"); // DX = hi - lo = len
|
||||
emitline("\tADDQ\tAX, CX\n"); // CX = base + lo = ptr
|
||||
// ptr = base + lo*esz (#76; ensure.ha:30 membsz-unit).
|
||||
// BX=lo*esz; AX=lo PRESERVED for cap. BX (dead hi) reloaded
|
||||
// by cgbasecap below.
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", BX\n");
|
||||
emitline("\tIMULQ\tAX, BX\n");
|
||||
emitline("\tADDQ\tBX, CX\n");
|
||||
} else {
|
||||
emitline("\tADDQ\tAX, CX\n"); // CX = base + lo = ptr
|
||||
};
|
||||
// cap = base_cap - lo (#20); AX=lo, BX free.
|
||||
if (cgbasecap(c, base, "BX")) {
|
||||
emitline("\tSUBQ\tAX, BX\n");
|
||||
|
||||
@@ -10863,6 +10863,15 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// esz from the type table for an N_IDENT base (#76; mirrors
|
||||
// the cgindex idiom). Non-ident base stays esz=1 -> ptr
|
||||
// unscaled, matching cstage's base->kind==N_IDENT gate.
|
||||
let esz: i32 = 1;
|
||||
if (baselocal != nil) {
|
||||
esz = elemsizeofc(c, baselocal.tnode);
|
||||
} else { if (globaltn != nil) {
|
||||
esz = elemsizeofc(c, globaltn);
|
||||
};};
|
||||
// base address → push
|
||||
if (baselocal != nil) {
|
||||
let tn: *node = baselocal.tnode;
|
||||
@@ -10949,7 +10958,18 @@ fn pushargsrev(c: *cgen, arg: *node, param: *node) i32 = {
|
||||
emitline("\tPOPQ\tCX\n"); // base
|
||||
emitline("\tMOVQ\tBX, DX\n"); // DX = hi
|
||||
emitline("\tSUBQ\tAX, DX\n"); // DX = hi - lo = len
|
||||
emitline("\tADDQ\tAX, CX\n"); // CX = base + lo = ptr
|
||||
// ptr = base + lo*esz (#76; ensure.ha:30 membsz-unit).
|
||||
// BX=lo*esz; AX=lo PRESERVED for cap. BX (dead hi) reloaded
|
||||
// by cgbasecap below.
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", BX\n");
|
||||
emitline("\tIMULQ\tAX, BX\n");
|
||||
emitline("\tADDQ\tBX, CX\n");
|
||||
} else {
|
||||
emitline("\tADDQ\tAX, CX\n"); // CX = base + lo = ptr
|
||||
};
|
||||
// cap = base_cap - lo (#20); AX=lo, BX free.
|
||||
if (cgbasecap(c, base, "BX")) {
|
||||
emitline("\tSUBQ\tAX, BX\n");
|
||||
@@ -14861,11 +14881,12 @@ fn cgbasecap(c: *cgen, base: *node, dst: str) bool = {
|
||||
return false;
|
||||
};
|
||||
|
||||
// cgslice — `base[lo:hi]` as a slice value. Leaves (AX=base+lo,
|
||||
// cgslice — `base[lo:hi]` as a slice value. Leaves (AX=base+lo*esz,
|
||||
// BX=hi-lo, CX=base_cap-lo) so callers can route to a slice slot,
|
||||
// return, or arg with the same triple ABI. cap is the storage
|
||||
// remaining to the base's end (#20, Go/Hare-identical) via cgbasecap.
|
||||
// Element scaling on the ptr isn't wired (lo*esz is #76).
|
||||
// ptr advances by BYTES (lo*esz, #76; ref/hare/rt/ensure.ha:30
|
||||
// membsz-unit); esz from the type table, mirroring the cgindex idiom.
|
||||
fn cgslice(c: *cgen, n: *node) void = {
|
||||
let base: *node = n.lhs;
|
||||
let lo: *node = n.rhs;
|
||||
@@ -14886,6 +14907,15 @@ fn cgslice(c: *cgen, n: *node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// esz from the type table for an N_IDENT base (#76; mirrors the
|
||||
// cgindex idiom). Non-ident base stays esz=1 -> ptr unscaled,
|
||||
// matching cstage's base->kind==N_IDENT gate.
|
||||
let esz: i32 = 1;
|
||||
if (baselocal != nil) {
|
||||
esz = elemsizeofc(c, baselocal.tnode);
|
||||
} else { if (globaltn != nil) {
|
||||
esz = elemsizeofc(c, globaltn);
|
||||
};};
|
||||
// base address
|
||||
if (baselocal != nil) {
|
||||
let tn: *node = baselocal.tnode;
|
||||
@@ -14981,7 +15011,17 @@ fn cgslice(c: *cgen, n: *node) void = {
|
||||
emitline("\tMOVQ\tAX, BX\n");
|
||||
emitline("\tPOPQ\tCX\n");
|
||||
emitline("\tPOPQ\tAX\n");
|
||||
emitline("\tADDQ\tCX, AX\n");
|
||||
// ptr = base + lo*esz (#76; ensure.ha:30 membsz-unit).
|
||||
// DX=lo*esz; CX=lo PRESERVED for len + cap (#20).
|
||||
if (esz > 1) {
|
||||
emitline("\tMOVQ\t$");
|
||||
emitint(esz: i64);
|
||||
emitline(", DX\n");
|
||||
emitline("\tIMULQ\tCX, DX\n");
|
||||
emitline("\tADDQ\tDX, AX\n");
|
||||
} else {
|
||||
emitline("\tADDQ\tCX, AX\n");
|
||||
};
|
||||
emitline("\tSUBQ\tCX, BX\n");
|
||||
// cap = base_cap - lo (#20); CX=lo, BX=len here.
|
||||
if (cgbasecap(c, base, "DX")) {
|
||||
|
||||
Reference in New Issue
Block a user