cgen: address module-global str/slice pseudo-field stores via the symbol
A module-global base has no frame slot; treating its local-lookup miss as offset zero wrote .ptr/.len/.cap at the caller return address. LEAQ the symbol like the struct-field global arms do. Both stages.
This commit is contained in:
@@ -5378,10 +5378,19 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
else if (strcmp(fld, "cap") == 0) delta = 16;
|
||||
if (delta < 0) goto after_dot_assign;
|
||||
int boff = localfind(locals, base->str);
|
||||
/* A global has no frame slot; treating its lookup miss as
|
||||
* offset zero writes .len at the caller return address. */
|
||||
int is_global = boff == 0 && !via_ptr
|
||||
&& let_islet(base->str);
|
||||
if (n->op != TK_ASSIGN) {
|
||||
if (via_ptr) {
|
||||
ins2(c, A_MOVQ, amem(D_BP, boff), areg(D_BX));
|
||||
ins2(c, A_MOVQ, amem(D_BX, delta), areg(D_BX));
|
||||
} else if (is_global) {
|
||||
ins2(c, A_LEAQ, masym(c, base->str),
|
||||
areg(D_BX));
|
||||
ins2(c, A_MOVQ, amem(D_BX, delta),
|
||||
areg(D_BX));
|
||||
} else {
|
||||
ins2(c, A_MOVQ, amem(D_BP, boff + delta), areg(D_BX));
|
||||
}
|
||||
@@ -5395,6 +5404,11 @@ cgexpr(Cg *c, Node *n, Local *locals)
|
||||
if (via_ptr) {
|
||||
ins2(c, A_MOVQ, amem(D_BP, boff), areg(D_BX));
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BX, delta));
|
||||
} else if (is_global) {
|
||||
ins2(c, A_LEAQ, masym(c, base->str),
|
||||
areg(D_BX));
|
||||
ins2(c, A_MOVQ, areg(D_AX),
|
||||
amem(D_BX, delta));
|
||||
} else {
|
||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, boff + delta));
|
||||
}
|
||||
|
||||
@@ -11170,9 +11170,9 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
};
|
||||
};
|
||||
};
|
||||
// Top-level struct global field assignment: `g.f = expr;` and
|
||||
// `g.f += expr;` for a scalar/str field. Reached when the local
|
||||
// lookup miss but the IDENT base is a registered struct `let`.
|
||||
// Top-level global field assignment: `g.f = expr;` and `g.f += expr;`
|
||||
// for a struct field or str/slice pseudo-field. Reached when the local
|
||||
// lookup misses but the IDENT base is a registered module `let`.
|
||||
// LEAQ name(SB) into BX/CX takes the place of the frame slot
|
||||
// addressing the local branches use. Compound (PLUSEQ/MINUSEQ)
|
||||
// follows the same load → push → eval → combine → store shape
|
||||
@@ -11185,6 +11185,37 @@ fn cgassign(c: *cgen, n: *syntax.node) void = {
|
||||
if (base.kind == syntax.nkind.N_IDENT) {
|
||||
let bn: str = base.str;
|
||||
if (localfindnode(c, bn) == nil) {
|
||||
let btype: *syntax.tinfo = base.type_: *syntax.tinfo;
|
||||
if (isletvar(c, bn) &&
|
||||
(syntax.typeisstr(btype) || syntax.typeisslice(btype))) {
|
||||
let delta: i32 = -1;
|
||||
if (syntax.streq(fld, "ptr")) { delta = 0; };
|
||||
if (syntax.streq(fld, "len")) { delta = 8; };
|
||||
if (syntax.streq(fld, "cap")) { delta = 16; };
|
||||
if (delta >= 0) {
|
||||
if (n.op != syntax.tkind.TK_ASSIGN) {
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
emitline("\tMOVQ\t");
|
||||
emitdispreg(delta: i64, "BX");
|
||||
emitline(", BX\n");
|
||||
emitline("\tPUSHQ\tBX\n");
|
||||
};
|
||||
cgexpr(c, n.rhs);
|
||||
if (n.op != syntax.tkind.TK_ASSIGN) {
|
||||
emitline("\tPOPQ\tBX\n");
|
||||
cgdotfieldcombine(c, n.op, false);
|
||||
};
|
||||
emitline("\tLEAQ\t");
|
||||
emitsymname(c, bn);
|
||||
emitline("(SB), BX\n");
|
||||
emitline("\tMOVQ\tAX, ");
|
||||
emitdispreg(delta: i64, "BX");
|
||||
emitline("\n");
|
||||
return;
|
||||
};
|
||||
};
|
||||
// #31: resolve the global value-struct field OFFSET + type
|
||||
// off the checker-STAMPED receiver tinfo (tichase(base.type_)),
|
||||
// NOT the name-keyed global-struct leaf lookup. A global decl
|
||||
|
||||
19
test/lang/global_slice_pseudofield_store_test.ww
Normal file
19
test/lang/global_slice_pseudofield_store_test.ww
Normal file
@@ -0,0 +1,19 @@
|
||||
// A module-global slice header is not a BP-relative local. Writing its
|
||||
// pseudo-fields through offset zero overwrites the caller's saved control
|
||||
// state instead of the global header.
|
||||
|
||||
package global_slice_pseudofield_store_test;
|
||||
|
||||
let values: []i64 = [10, 20, 30, 40];
|
||||
let replacement: [2]i64 = [70, 80];
|
||||
|
||||
@test fn global_header_store() void = {
|
||||
values.ptr = &replacement[0];
|
||||
values.len = 1;
|
||||
values.cap = 2;
|
||||
values.len += 1;
|
||||
assert(values.len == 2);
|
||||
assert(values.cap == 2);
|
||||
assert(values[0] == 70);
|
||||
assert(values[1] == 80);
|
||||
};
|
||||
Reference in New Issue
Block a user