wcc/cgen: #21 def-str/slice by-value arg pushes all 3 header words (wwstage)
Recognizer flip: nodeisstr/nodeisslice type-key a def ident (was local-keyed → fell to scalar default PUSHQ, dropped len/cap). Mirrors cstage node_isstr/node_isslice; push/pop stay symmetric via the shared recognizer. wwstage-only; cstage already correct.
This commit is contained in:
@@ -17580,6 +17580,19 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
|
||||
let nm: str = n.str;
|
||||
let lc: *local = localfindnode(c, nm);
|
||||
if (lc != nil) { return isslicetype(c, lc.tnode); };
|
||||
// #21: a module-level `def g: []T` ident is not a frame
|
||||
// slot (localfindnode→nil), so the local arm above misses
|
||||
// it and it would fall to the scalar single-PUSHQ default,
|
||||
// dropping len/cap. cstage's node_isslice is type-keyed
|
||||
// (cmd/w6c/cgen.c:226-229 type_isslice(n->type)); align
|
||||
// wwstage UP by reading the checker-stamped .type_ (the def
|
||||
// decl's str/slice type node, check.ww exprtype N_IDENT
|
||||
// arm). A def has no DATA symbol — it rides cgident's
|
||||
// const-fold, so the push site and cgcall's pop sizer flip
|
||||
// together on this shared recognizer (load-bearing).
|
||||
if (deflookup(c, nm)) {
|
||||
return typeisslice(n.type_: *tinfo);
|
||||
};
|
||||
return false;
|
||||
};
|
||||
if (k == nkind.N_SLICE) { return true; };
|
||||
@@ -17657,6 +17670,19 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
||||
// MOVQ BX,CX shuffle on returns of str-aliased locals.
|
||||
if (isstrtype(c, lc.tnode)) { return true; };
|
||||
};
|
||||
// #21: a module-level `def s: str` ident is not a frame slot
|
||||
// (localfindnode→nil), so the local arm above misses it and
|
||||
// it would fall to the scalar single-PUSHQ default, dropping
|
||||
// len/cap. cstage's node_isstr is type-keyed (cmd/w6c/
|
||||
// cgen.c:213-216 type_isstr(n->type)); align wwstage UP by
|
||||
// reading the checker-stamped .type_ (the def decl's str type
|
||||
// node, check.ww exprtype N_IDENT arm). A def has no DATA
|
||||
// symbol — it rides cgident's const-fold (LEAQ _S_n, MOVQ
|
||||
// $len), so the push site and cgcall's pop sizer flip together
|
||||
// on this shared recognizer (load-bearing).
|
||||
if (deflookup(c, nm)) {
|
||||
return typeisstr(n.type_: *tinfo);
|
||||
};
|
||||
return false;
|
||||
};
|
||||
if (k == nkind.N_CALL) {
|
||||
|
||||
@@ -1149,6 +1149,19 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
|
||||
let nm: str = n.str;
|
||||
let lc: *local = localfindnode(c, nm);
|
||||
if (lc != nil) { return isslicetype(c, lc.tnode); };
|
||||
// #21: a module-level `def g: []T` ident is not a frame
|
||||
// slot (localfindnode→nil), so the local arm above misses
|
||||
// it and it would fall to the scalar single-PUSHQ default,
|
||||
// dropping len/cap. cstage's node_isslice is type-keyed
|
||||
// (cmd/w6c/cgen.c:226-229 type_isslice(n->type)); align
|
||||
// wwstage UP by reading the checker-stamped .type_ (the def
|
||||
// decl's str/slice type node, check.ww exprtype N_IDENT
|
||||
// arm). A def has no DATA symbol — it rides cgident's
|
||||
// const-fold, so the push site and cgcall's pop sizer flip
|
||||
// together on this shared recognizer (load-bearing).
|
||||
if (deflookup(c, nm)) {
|
||||
return typeisslice(n.type_: *tinfo);
|
||||
};
|
||||
return false;
|
||||
};
|
||||
if (k == nkind.N_SLICE) { return true; };
|
||||
@@ -1226,6 +1239,19 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
||||
// MOVQ BX,CX shuffle on returns of str-aliased locals.
|
||||
if (isstrtype(c, lc.tnode)) { return true; };
|
||||
};
|
||||
// #21: a module-level `def s: str` ident is not a frame slot
|
||||
// (localfindnode→nil), so the local arm above misses it and
|
||||
// it would fall to the scalar single-PUSHQ default, dropping
|
||||
// len/cap. cstage's node_isstr is type-keyed (cmd/w6c/
|
||||
// cgen.c:213-216 type_isstr(n->type)); align wwstage UP by
|
||||
// reading the checker-stamped .type_ (the def decl's str type
|
||||
// node, check.ww exprtype N_IDENT arm). A def has no DATA
|
||||
// symbol — it rides cgident's const-fold (LEAQ _S_n, MOVQ
|
||||
// $len), so the push site and cgcall's pop sizer flip together
|
||||
// on this shared recognizer (load-bearing).
|
||||
if (deflookup(c, nm)) {
|
||||
return typeisstr(n.type_: *tinfo);
|
||||
};
|
||||
return false;
|
||||
};
|
||||
if (k == nkind.N_CALL) {
|
||||
|
||||
@@ -17580,6 +17580,19 @@ fn nodeisslice(c: *cgen, n: *node) bool = {
|
||||
let nm: str = n.str;
|
||||
let lc: *local = localfindnode(c, nm);
|
||||
if (lc != nil) { return isslicetype(c, lc.tnode); };
|
||||
// #21: a module-level `def g: []T` ident is not a frame
|
||||
// slot (localfindnode→nil), so the local arm above misses
|
||||
// it and it would fall to the scalar single-PUSHQ default,
|
||||
// dropping len/cap. cstage's node_isslice is type-keyed
|
||||
// (cmd/w6c/cgen.c:226-229 type_isslice(n->type)); align
|
||||
// wwstage UP by reading the checker-stamped .type_ (the def
|
||||
// decl's str/slice type node, check.ww exprtype N_IDENT
|
||||
// arm). A def has no DATA symbol — it rides cgident's
|
||||
// const-fold, so the push site and cgcall's pop sizer flip
|
||||
// together on this shared recognizer (load-bearing).
|
||||
if (deflookup(c, nm)) {
|
||||
return typeisslice(n.type_: *tinfo);
|
||||
};
|
||||
return false;
|
||||
};
|
||||
if (k == nkind.N_SLICE) { return true; };
|
||||
@@ -17657,6 +17670,19 @@ fn nodeisstr(c: *cgen, n: *node) bool = {
|
||||
// MOVQ BX,CX shuffle on returns of str-aliased locals.
|
||||
if (isstrtype(c, lc.tnode)) { return true; };
|
||||
};
|
||||
// #21: a module-level `def s: str` ident is not a frame slot
|
||||
// (localfindnode→nil), so the local arm above misses it and
|
||||
// it would fall to the scalar single-PUSHQ default, dropping
|
||||
// len/cap. cstage's node_isstr is type-keyed (cmd/w6c/
|
||||
// cgen.c:213-216 type_isstr(n->type)); align wwstage UP by
|
||||
// reading the checker-stamped .type_ (the def decl's str type
|
||||
// node, check.ww exprtype N_IDENT arm). A def has no DATA
|
||||
// symbol — it rides cgident's const-fold (LEAQ _S_n, MOVQ
|
||||
// $len), so the push site and cgcall's pop sizer flip together
|
||||
// on this shared recognizer (load-bearing).
|
||||
if (deflookup(c, nm)) {
|
||||
return typeisstr(n.type_: *tinfo);
|
||||
};
|
||||
return false;
|
||||
};
|
||||
if (k == nkind.N_CALL) {
|
||||
|
||||
@@ -31,9 +31,22 @@
|
||||
* str_glob_ptr | let s:str="abcd"; take reads x[0] (uses ptr) | 97
|
||||
* slice_glob_len | let g:[]int=[10..50]; take reads x.len | 5
|
||||
* slice_glob_idx | let g:[]int=[10..50]; take reads x[2] | 30
|
||||
* def_str_len | def s:str="abcd"; take reads x.len (#21) | 4
|
||||
* def_str_ptr | def s:str="abcd"; take reads x[0] (#21) | 97
|
||||
* def_str_return | return <def-str>; take reads x.len (#21) | 5
|
||||
* ctrl_local_str | LOCAL str arg (off!=0, unchanged, byte-id) | 2
|
||||
* ctrl_local_sl | LOCAL []int arg (off!=0, unchanged, byte-id) | 8
|
||||
*
|
||||
* The def_* rows are the #21 residual of this by-value-global family: a
|
||||
* module-level `def s: str` (NO DATA symbol) passed BY VALUE. Pre-fix wwstage's
|
||||
* nodeisstr was LOCAL-keyed for an N_IDENT (def→false), so the def-str fell to
|
||||
* the scalar single-PUSHQ default and dropped len/cap; cstage's node_isstr is
|
||||
* TYPE-keyed (n->type) and pushes all 3 words. A def rides cgident's const-fold
|
||||
* (LEAQ _S_n, MOVQ $len) — NOT the let LEAQ name(SB) path — then the now
|
||||
* type-keyed nodeisstr triple push. def_str_return is a SIBLING guard (a
|
||||
* def-str flowed through a fn return, then pushed via nodeisstr's pre-existing
|
||||
* N_CALL arm) — byte-id-identical pre/post-fix, no #21 teeth of its own.
|
||||
*
|
||||
* The *_glob_* rows are mutation-sane: pre-fix wwstage drops len/cap and the
|
||||
* callee reads garbage; cstage is correct. ctrl_* guard the unchanged local
|
||||
* path. A byte-id row pins cstage==wwstage `.s` for each source.
|
||||
@@ -101,6 +114,42 @@ static const struct row rows[] = {
|
||||
"export fn main() i32 = { return take(g): i32; };\n",
|
||||
30 },
|
||||
|
||||
/* def_str_len — THE #21 regression pin: a module-level `def s: str`
|
||||
* passed BY VALUE, callee reads x.len. Pre-fix wwstage drops len/cap
|
||||
* (scalar single-PUSHQ default); cstage type-keyed pushes all 3 words.
|
||||
* A def has NO DATA symbol — it rides cgident's const-fold (LEAQ _S_x,
|
||||
* MOVQ $len BX, $len CX), then the type-keyed str triple push. */
|
||||
{ "def_str_len",
|
||||
"package main;\n"
|
||||
"def s: str = \"abcd\";\n"
|
||||
"fn take(x: str) int = { return x.len: int; };\n"
|
||||
"export fn main() i32 = { return take(s): i32; };\n",
|
||||
4 },
|
||||
|
||||
/* def_str_ptr — def-str by value, callee reads x[0] (proves the ptr
|
||||
* word survived the push). 'a' == 97. */
|
||||
{ "def_str_ptr",
|
||||
"package main;\n"
|
||||
"def s: str = \"abcd\";\n"
|
||||
"fn take(x: str) int = { return x[0]: int; };\n"
|
||||
"export fn main() i32 = { return take(s): i32; };\n",
|
||||
97 },
|
||||
|
||||
/* def_str_return — a def-str flowed through a fn return: give()'s
|
||||
* `return s` leaves the const-folded str header (AX=ptr/BX=len/CX=cap),
|
||||
* and the outer `take(give())` arg rides nodeisstr's pre-existing N_CALL
|
||||
* arm (give returns str) for the 3-word push. A #21 SIBLING, not the
|
||||
* N_IDENT def arm — it is byte-id-IDENTICAL pre- and post-fix (no teeth
|
||||
* of its own), kept as a convergence guard that a def-str surviving a fn
|
||||
* return stays a full str header on both stages. */
|
||||
{ "def_str_return",
|
||||
"package main;\n"
|
||||
"def s: str = \"hello\";\n"
|
||||
"fn give() str = { return s; };\n"
|
||||
"fn take(x: str) int = { return x.len: int; };\n"
|
||||
"export fn main() i32 = { return take(give()): i32; };\n",
|
||||
5 },
|
||||
|
||||
/* ctrl_local_str — a LOCAL str arg (off!=0). cgen unchanged; pins the
|
||||
* local-str push path stays byte-id (no regress). */
|
||||
{ "ctrl_local_str",
|
||||
|
||||
Reference in New Issue
Block a user