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;
|
else if (strcmp(fld, "cap") == 0) delta = 16;
|
||||||
if (delta < 0) goto after_dot_assign;
|
if (delta < 0) goto after_dot_assign;
|
||||||
int boff = localfind(locals, base->str);
|
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 (n->op != TK_ASSIGN) {
|
||||||
if (via_ptr) {
|
if (via_ptr) {
|
||||||
ins2(c, A_MOVQ, amem(D_BP, boff), areg(D_BX));
|
ins2(c, A_MOVQ, amem(D_BP, boff), areg(D_BX));
|
||||||
ins2(c, A_MOVQ, amem(D_BX, delta), 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 {
|
} else {
|
||||||
ins2(c, A_MOVQ, amem(D_BP, boff + delta), areg(D_BX));
|
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) {
|
if (via_ptr) {
|
||||||
ins2(c, A_MOVQ, amem(D_BP, boff), areg(D_BX));
|
ins2(c, A_MOVQ, amem(D_BP, boff), areg(D_BX));
|
||||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BX, delta));
|
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 {
|
} else {
|
||||||
ins2(c, A_MOVQ, areg(D_AX), amem(D_BP, boff + delta));
|
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
|
// Top-level global field assignment: `g.f = expr;` and `g.f += expr;`
|
||||||
// `g.f += expr;` for a scalar/str field. Reached when the local
|
// for a struct field or str/slice pseudo-field. Reached when the local
|
||||||
// lookup miss but the IDENT base is a registered struct `let`.
|
// lookup misses but the IDENT base is a registered module `let`.
|
||||||
// LEAQ name(SB) into BX/CX takes the place of the frame slot
|
// LEAQ name(SB) into BX/CX takes the place of the frame slot
|
||||||
// addressing the local branches use. Compound (PLUSEQ/MINUSEQ)
|
// addressing the local branches use. Compound (PLUSEQ/MINUSEQ)
|
||||||
// follows the same load → push → eval → combine → store shape
|
// 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) {
|
if (base.kind == syntax.nkind.N_IDENT) {
|
||||||
let bn: str = base.str;
|
let bn: str = base.str;
|
||||||
if (localfindnode(c, bn) == nil) {
|
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
|
// #31: resolve the global value-struct field OFFSET + type
|
||||||
// off the checker-STAMPED receiver tinfo (tichase(base.type_)),
|
// off the checker-STAMPED receiver tinfo (tichase(base.type_)),
|
||||||
// NOT the name-keyed global-struct leaf lookup. A global decl
|
// 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