selfhost: fix 4B array load/store width + 8B uninit zero-init
This commit is contained in:
@@ -5281,7 +5281,24 @@ fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
|||||||
if (k == nkind.N_TFN) { return true; };
|
if (k == nkind.N_TFN) { return true; };
|
||||||
if (k == nkind.N_TCHAN) { return true; };
|
if (k == nkind.N_TCHAN) { return true; };
|
||||||
if (k == nkind.N_TSLICE) { return false; };
|
if (k == nkind.N_TSLICE) { return false; };
|
||||||
if (k == nkind.N_TARRAY) { return false; };
|
if (k == nkind.N_TARRAY) {
|
||||||
|
// C cgen (cmd/w6c/cgen.c:3317) zero-inits TY_ARRAY whenever
|
||||||
|
// its raw byte size is 8 — e.g. `[8]bool`, `[2]i32`, `[4]i16`,
|
||||||
|
// `[1]i64`. Mirror that here so the wwstage matches.
|
||||||
|
let lenn: *node = t.rhs;
|
||||||
|
let elemn: *node = t.lhs;
|
||||||
|
if (lenn == nil) { return false; };
|
||||||
|
if (lenn.kind != nkind.N_INTLIT) { return false; };
|
||||||
|
let elen: i64 = lenn.uval: i64;
|
||||||
|
let esz: i32 = 8;
|
||||||
|
if (elemn != nil) {
|
||||||
|
if (elemn.kind == nkind.N_TNAME) {
|
||||||
|
let ps: i32 = primsize(elemn.str);
|
||||||
|
if (ps > 0) { esz = ps; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return (esz: i64 * elen) == 8i64;
|
||||||
|
};
|
||||||
if (k == nkind.N_TTUPLE) { return false; };
|
if (k == nkind.N_TTUPLE) { return false; };
|
||||||
if (k == nkind.N_TTAGGED){ return false; };
|
if (k == nkind.N_TTAGGED){ return false; };
|
||||||
if (k == nkind.N_TNAME) {
|
if (k == nkind.N_TNAME) {
|
||||||
@@ -5297,6 +5314,22 @@ fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// elemissigned — given an indexable type (`*T`, `[]T`, `[N]T`), is
|
||||||
|
// its element a signed narrow primitive (i8/i16/i32/rune)? Used by
|
||||||
|
// cgindex to pick MOVSXD vs MOVL at esz=4. Mirrors C cgen's
|
||||||
|
// `signed_elem` check.
|
||||||
|
fn elemissigned(t: *node) bool = {
|
||||||
|
if (t == nil) { return false; };
|
||||||
|
let elem: *node = nil;
|
||||||
|
let k: nkind = t.kind;
|
||||||
|
if (k == nkind.N_TPTR) { elem = t.lhs; };
|
||||||
|
if (k == nkind.N_TSLICE) { elem = t.lhs; };
|
||||||
|
if (k == nkind.N_TARRAY) { elem = t.lhs; };
|
||||||
|
if (elem == nil) { return false; };
|
||||||
|
if (elem.kind != nkind.N_TNAME) { return false; };
|
||||||
|
return typenameissigned(elem.str);
|
||||||
|
};
|
||||||
|
|
||||||
// typenameissigned — true for i8/i16/i32/i64/int/rune.
|
// typenameissigned — true for i8/i16/i32/i64/int/rune.
|
||||||
fn typenameissigned(nm: str) bool = {
|
fn typenameissigned(nm: str) bool = {
|
||||||
if (streq(nm, "i8")) { return true; };
|
if (streq(nm, "i8")) { return true; };
|
||||||
@@ -6644,18 +6677,22 @@ fn cgident(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn cgindex(c: *cgen, n: *node) void = {
|
fn cgindex(c: *cgen, n: *node) void = {
|
||||||
// Element-size-aware load: u8-element bases use MOVZBQ,
|
// Element-size-aware load: u8 → MOVZBQ, i32 → MOVSXD, u32 → MOVL,
|
||||||
// everything else MOVQ. Fast path when the base is a bare
|
// str → (ptr, len) into (AX, BX), everything else → MOVQ. Fast
|
||||||
// ident (mem.ww shape).
|
// path when the base is a bare ident (mem.ww shape).
|
||||||
let base: *node = n.lhs;
|
let base: *node = n.lhs;
|
||||||
let idx: *node = n.rhs;
|
let idx: *node = n.rhs;
|
||||||
let esz: i32 = 8;
|
let esz: i32 = 8;
|
||||||
|
let signed_elem: bool = false;
|
||||||
let baselocal: *local = nil;
|
let baselocal: *local = nil;
|
||||||
if (base != nil) {
|
if (base != nil) {
|
||||||
if (base.kind == nkind.N_IDENT) {
|
if (base.kind == nkind.N_IDENT) {
|
||||||
let bn: str = base.str;
|
let bn: str = base.str;
|
||||||
baselocal = localfindnode(c, bn);
|
baselocal = localfindnode(c, bn);
|
||||||
if (baselocal != nil) { esz = elemsizeof(baselocal.tnode); };
|
if (baselocal != nil) {
|
||||||
|
esz = elemsizeof(baselocal.tnode);
|
||||||
|
signed_elem = elemissigned(baselocal.tnode);
|
||||||
|
};
|
||||||
} else { if (base.kind == nkind.N_DOT) {
|
} else { if (base.kind == nkind.N_DOT) {
|
||||||
esz = indexbaseesz(c, base);
|
esz = indexbaseesz(c, base);
|
||||||
};};
|
};};
|
||||||
@@ -6690,7 +6727,11 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
||||||
else { emitline("\tMOVQ\t(BX), AX\n"); };
|
else { if (esz == 4) {
|
||||||
|
if (signed_elem) { emitline("\tMOVSXD\t(BX), AX\n"); }
|
||||||
|
else { emitline("\tMOVL\t(BX), AX\n"); };
|
||||||
|
}
|
||||||
|
else { emitline("\tMOVQ\t(BX), AX\n"); };};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// Generic fallback when base isn't a plain ident.
|
// Generic fallback when base isn't a plain ident.
|
||||||
@@ -6704,7 +6745,11 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVZBQ\t(AX), AX\n"); }
|
if (esz == 1) { emitline("\tMOVZBQ\t(AX), AX\n"); }
|
||||||
else { emitline("\tMOVQ\t(AX), AX\n"); };
|
else { if (esz == 4) {
|
||||||
|
if (signed_elem) { emitline("\tMOVSXD\t(AX), AX\n"); }
|
||||||
|
else { emitline("\tMOVL\t(AX), AX\n"); };
|
||||||
|
}
|
||||||
|
else { emitline("\tMOVQ\t(AX), AX\n"); };};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -7951,7 +7996,8 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVB\tAX, (BX)\n"); }
|
if (esz == 1) { emitline("\tMOVB\tAX, (BX)\n"); }
|
||||||
else { emitline("\tMOVQ\tAX, (BX)\n"); };
|
else { if (esz == 4) { emitline("\tMOVL\tAX, (BX)\n"); }
|
||||||
|
else { emitline("\tMOVQ\tAX, (BX)\n"); };};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -8976,13 +9022,11 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Bare `let x: T;` with no initializer. C cgen
|
// Bare `let x: T;` with no initializer. C cgen
|
||||||
// (cmd/w6c/cgen.c:2181-2183) zero-inits only when
|
// (cmd/w6c/cgen.c:3317) zero-inits whenever the raw type size
|
||||||
// the underlying type's natural size is 8 — pointers,
|
// is 8: scalar primitives, pointers, fn/chan handles, plus 8B
|
||||||
// i64/u64, function pointers, ints. Structs/arrays/
|
// composites like `[8]bool`, `[2]i32`, `[4]i16`, `[1]i64`.
|
||||||
// slices/strings/tagged/tuples are left for per-field
|
// Larger composites and `[N]T` with size != 8 are left for
|
||||||
// writes. ww's slotsize pads struct slots up to 8,
|
// per-field writes.
|
||||||
// so we can't just check sz == 8: walk the type AST
|
|
||||||
// directly to make the same call.
|
|
||||||
if (typeis8byteprimitive(c, n.lhs)) {
|
if (typeis8byteprimitive(c, n.lhs)) {
|
||||||
emitline("\tMOVQ\t$0, ");
|
emitline("\tMOVQ\t$0, ");
|
||||||
emitoff(off: i64);
|
emitoff(off: i64);
|
||||||
@@ -9271,15 +9315,12 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
let ioff: i32 = localalloc(c, iname, 8, nil);
|
let ioff: i32 = localalloc(c, iname, 8, nil);
|
||||||
let loff: i32 = localalloc(c, lname, 8, nil);
|
let loff: i32 = localalloc(c, lname, 8, nil);
|
||||||
|
|
||||||
// Per-binding (up to 8 — matches the C array). Parallel i64 arrays
|
// Per-binding (up to 8 — matches the C array). Parallel arrays so
|
||||||
// keep every elem at 8B so the indexed-store hits the working MOVQ
|
// we don't depend on local-struct cgen.
|
||||||
// path (selfhost cgen doesn't yet emit MOVL for i32-array writes,
|
let bind_off: [8]i32;
|
||||||
// and doesn't zero-init `[8]bool` uninit slots — both byte-diverge
|
let bind_sz: [8]i32;
|
||||||
// from C w6c on the wwstage rebuild).
|
let bind_foff: [8]i32;
|
||||||
let bind_off: [8]i64;
|
let bind_signed: [8]bool;
|
||||||
let bind_sz: [8]i64;
|
|
||||||
let bind_foff: [8]i64;
|
|
||||||
let bind_signed: [8]i64; // 0 / 1
|
|
||||||
let nbinds: i32 = 0;
|
let nbinds: i32 = 0;
|
||||||
|
|
||||||
if (destruct) {
|
if (destruct) {
|
||||||
@@ -9300,15 +9341,14 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
let slot_sz: i32 = fsz;
|
let slot_sz: i32 = fsz;
|
||||||
if (slot_sz < 8) { slot_sz = 8; };
|
if (slot_sz < 8) { slot_sz = 8; };
|
||||||
bind_sz[nbinds] = fsz: i64;
|
bind_sz[nbinds] = fsz;
|
||||||
bind_foff[nbinds] = field_off: i64;
|
bind_foff[nbinds] = field_off;
|
||||||
if (signf) { bind_signed[nbinds] = 1i64; }
|
bind_signed[nbinds] = signf;
|
||||||
else { bind_signed[nbinds] = 0i64; };
|
|
||||||
let bnm: str = m.str;
|
let bnm: str = m.str;
|
||||||
if (bnm.len > 0) {
|
if (bnm.len > 0) {
|
||||||
bind_off[nbinds] = localadd(c, bnm, slot_sz, nil): i64;
|
bind_off[nbinds] = localadd(c, bnm, slot_sz, nil);
|
||||||
} else {
|
} else {
|
||||||
bind_off[nbinds] = localalloc(c, mkscratchname(c, "fr"), slot_sz, nil): i64;
|
bind_off[nbinds] = localalloc(c, mkscratchname(c, "fr"), slot_sz, nil);
|
||||||
};
|
};
|
||||||
field_off += fsz;
|
field_off += fsz;
|
||||||
nbinds += 1;
|
nbinds += 1;
|
||||||
@@ -9319,18 +9359,18 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
} else {
|
} else {
|
||||||
let slot_sz: i32 = esz;
|
let slot_sz: i32 = esz;
|
||||||
if (slot_sz < 8) { slot_sz = 8; };
|
if (slot_sz < 8) { slot_sz = 8; };
|
||||||
bind_sz[0] = esz: i64;
|
bind_sz[0] = esz;
|
||||||
bind_foff[0] = 0i64;
|
bind_foff[0] = 0;
|
||||||
// Single-binding signed-narrow detection: mirror C which
|
// Single-binding signed-narrow detection: mirror C which
|
||||||
// reads `u->sub->kind` for the elem type.
|
// reads `u->sub->kind` for the elem type.
|
||||||
let signf0: bool = false;
|
bind_signed[0] = false;
|
||||||
if (elemt != nil) { signf0 = paramissigned(elemt); };
|
if (elemt != nil) {
|
||||||
if (signf0) { bind_signed[0] = 1i64; }
|
bind_signed[0] = paramissigned(elemt);
|
||||||
else { bind_signed[0] = 0i64; };
|
};
|
||||||
if (n.str.len > 0) {
|
if (n.str.len > 0) {
|
||||||
bind_off[0] = localadd(c, n.str, slot_sz, nil): i64;
|
bind_off[0] = localadd(c, n.str, slot_sz, nil);
|
||||||
} else {
|
} else {
|
||||||
bind_off[0] = localalloc(c, mkscratchname(c, "fr"), slot_sz, nil): i64;
|
bind_off[0] = localalloc(c, mkscratchname(c, "fr"), slot_sz, nil);
|
||||||
};
|
};
|
||||||
nbinds = 1;
|
nbinds = 1;
|
||||||
};
|
};
|
||||||
@@ -9424,18 +9464,18 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
let b: i32 = 0;
|
let b: i32 = 0;
|
||||||
for (b < nbinds) {
|
for (b < nbinds) {
|
||||||
let op: str = "MOVQ";
|
let op: str = "MOVQ";
|
||||||
if (bind_sz[b] == 1i64) { op = "MOVZBQ"; }
|
if (bind_sz[b] == 1) { op = "MOVZBQ"; }
|
||||||
else { if (bind_sz[b] == 4i64) {
|
else { if (bind_sz[b] == 4) {
|
||||||
if (bind_signed[b] != 0i64) { op = "MOVSXD"; }
|
if (bind_signed[b]) { op = "MOVSXD"; }
|
||||||
else { op = "MOVL"; };
|
else { op = "MOVL"; };
|
||||||
};};
|
};};
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitoff(bind_foff[b]);
|
emitoff(bind_foff[b]: i64);
|
||||||
emitline("(BX), AX\n");
|
emitline("(BX), AX\n");
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitoff(bind_off[b]);
|
emitoff(bind_off[b]: i64);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
b += 1;
|
b += 1;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -516,18 +516,22 @@ fn cgident(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn cgindex(c: *cgen, n: *node) void = {
|
fn cgindex(c: *cgen, n: *node) void = {
|
||||||
// Element-size-aware load: u8-element bases use MOVZBQ,
|
// Element-size-aware load: u8 → MOVZBQ, i32 → MOVSXD, u32 → MOVL,
|
||||||
// everything else MOVQ. Fast path when the base is a bare
|
// str → (ptr, len) into (AX, BX), everything else → MOVQ. Fast
|
||||||
// ident (mem.ww shape).
|
// path when the base is a bare ident (mem.ww shape).
|
||||||
let base: *node = n.lhs;
|
let base: *node = n.lhs;
|
||||||
let idx: *node = n.rhs;
|
let idx: *node = n.rhs;
|
||||||
let esz: i32 = 8;
|
let esz: i32 = 8;
|
||||||
|
let signed_elem: bool = false;
|
||||||
let baselocal: *local = nil;
|
let baselocal: *local = nil;
|
||||||
if (base != nil) {
|
if (base != nil) {
|
||||||
if (base.kind == nkind.N_IDENT) {
|
if (base.kind == nkind.N_IDENT) {
|
||||||
let bn: str = base.str;
|
let bn: str = base.str;
|
||||||
baselocal = localfindnode(c, bn);
|
baselocal = localfindnode(c, bn);
|
||||||
if (baselocal != nil) { esz = elemsizeof(baselocal.tnode); };
|
if (baselocal != nil) {
|
||||||
|
esz = elemsizeof(baselocal.tnode);
|
||||||
|
signed_elem = elemissigned(baselocal.tnode);
|
||||||
|
};
|
||||||
} else { if (base.kind == nkind.N_DOT) {
|
} else { if (base.kind == nkind.N_DOT) {
|
||||||
esz = indexbaseesz(c, base);
|
esz = indexbaseesz(c, base);
|
||||||
};};
|
};};
|
||||||
@@ -562,7 +566,11 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
||||||
else { emitline("\tMOVQ\t(BX), AX\n"); };
|
else { if (esz == 4) {
|
||||||
|
if (signed_elem) { emitline("\tMOVSXD\t(BX), AX\n"); }
|
||||||
|
else { emitline("\tMOVL\t(BX), AX\n"); };
|
||||||
|
}
|
||||||
|
else { emitline("\tMOVQ\t(BX), AX\n"); };};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// Generic fallback when base isn't a plain ident.
|
// Generic fallback when base isn't a plain ident.
|
||||||
@@ -576,7 +584,11 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVZBQ\t(AX), AX\n"); }
|
if (esz == 1) { emitline("\tMOVZBQ\t(AX), AX\n"); }
|
||||||
else { emitline("\tMOVQ\t(AX), AX\n"); };
|
else { if (esz == 4) {
|
||||||
|
if (signed_elem) { emitline("\tMOVSXD\t(AX), AX\n"); }
|
||||||
|
else { emitline("\tMOVL\t(AX), AX\n"); };
|
||||||
|
}
|
||||||
|
else { emitline("\tMOVQ\t(AX), AX\n"); };};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1823,7 +1835,8 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVB\tAX, (BX)\n"); }
|
if (esz == 1) { emitline("\tMOVB\tAX, (BX)\n"); }
|
||||||
else { emitline("\tMOVQ\tAX, (BX)\n"); };
|
else { if (esz == 4) { emitline("\tMOVL\tAX, (BX)\n"); }
|
||||||
|
else { emitline("\tMOVQ\tAX, (BX)\n"); };};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -504,13 +504,11 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Bare `let x: T;` with no initializer. C cgen
|
// Bare `let x: T;` with no initializer. C cgen
|
||||||
// (cmd/w6c/cgen.c:2181-2183) zero-inits only when
|
// (cmd/w6c/cgen.c:3317) zero-inits whenever the raw type size
|
||||||
// the underlying type's natural size is 8 — pointers,
|
// is 8: scalar primitives, pointers, fn/chan handles, plus 8B
|
||||||
// i64/u64, function pointers, ints. Structs/arrays/
|
// composites like `[8]bool`, `[2]i32`, `[4]i16`, `[1]i64`.
|
||||||
// slices/strings/tagged/tuples are left for per-field
|
// Larger composites and `[N]T` with size != 8 are left for
|
||||||
// writes. ww's slotsize pads struct slots up to 8,
|
// per-field writes.
|
||||||
// so we can't just check sz == 8: walk the type AST
|
|
||||||
// directly to make the same call.
|
|
||||||
if (typeis8byteprimitive(c, n.lhs)) {
|
if (typeis8byteprimitive(c, n.lhs)) {
|
||||||
emitline("\tMOVQ\t$0, ");
|
emitline("\tMOVQ\t$0, ");
|
||||||
emitoff(off: i64);
|
emitoff(off: i64);
|
||||||
@@ -799,15 +797,12 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
let ioff: i32 = localalloc(c, iname, 8, nil);
|
let ioff: i32 = localalloc(c, iname, 8, nil);
|
||||||
let loff: i32 = localalloc(c, lname, 8, nil);
|
let loff: i32 = localalloc(c, lname, 8, nil);
|
||||||
|
|
||||||
// Per-binding (up to 8 — matches the C array). Parallel i64 arrays
|
// Per-binding (up to 8 — matches the C array). Parallel arrays so
|
||||||
// keep every elem at 8B so the indexed-store hits the working MOVQ
|
// we don't depend on local-struct cgen.
|
||||||
// path (selfhost cgen doesn't yet emit MOVL for i32-array writes,
|
let bind_off: [8]i32;
|
||||||
// and doesn't zero-init `[8]bool` uninit slots — both byte-diverge
|
let bind_sz: [8]i32;
|
||||||
// from C w6c on the wwstage rebuild).
|
let bind_foff: [8]i32;
|
||||||
let bind_off: [8]i64;
|
let bind_signed: [8]bool;
|
||||||
let bind_sz: [8]i64;
|
|
||||||
let bind_foff: [8]i64;
|
|
||||||
let bind_signed: [8]i64; // 0 / 1
|
|
||||||
let nbinds: i32 = 0;
|
let nbinds: i32 = 0;
|
||||||
|
|
||||||
if (destruct) {
|
if (destruct) {
|
||||||
@@ -828,15 +823,14 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
let slot_sz: i32 = fsz;
|
let slot_sz: i32 = fsz;
|
||||||
if (slot_sz < 8) { slot_sz = 8; };
|
if (slot_sz < 8) { slot_sz = 8; };
|
||||||
bind_sz[nbinds] = fsz: i64;
|
bind_sz[nbinds] = fsz;
|
||||||
bind_foff[nbinds] = field_off: i64;
|
bind_foff[nbinds] = field_off;
|
||||||
if (signf) { bind_signed[nbinds] = 1i64; }
|
bind_signed[nbinds] = signf;
|
||||||
else { bind_signed[nbinds] = 0i64; };
|
|
||||||
let bnm: str = m.str;
|
let bnm: str = m.str;
|
||||||
if (bnm.len > 0) {
|
if (bnm.len > 0) {
|
||||||
bind_off[nbinds] = localadd(c, bnm, slot_sz, nil): i64;
|
bind_off[nbinds] = localadd(c, bnm, slot_sz, nil);
|
||||||
} else {
|
} else {
|
||||||
bind_off[nbinds] = localalloc(c, mkscratchname(c, "fr"), slot_sz, nil): i64;
|
bind_off[nbinds] = localalloc(c, mkscratchname(c, "fr"), slot_sz, nil);
|
||||||
};
|
};
|
||||||
field_off += fsz;
|
field_off += fsz;
|
||||||
nbinds += 1;
|
nbinds += 1;
|
||||||
@@ -847,18 +841,18 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
} else {
|
} else {
|
||||||
let slot_sz: i32 = esz;
|
let slot_sz: i32 = esz;
|
||||||
if (slot_sz < 8) { slot_sz = 8; };
|
if (slot_sz < 8) { slot_sz = 8; };
|
||||||
bind_sz[0] = esz: i64;
|
bind_sz[0] = esz;
|
||||||
bind_foff[0] = 0i64;
|
bind_foff[0] = 0;
|
||||||
// Single-binding signed-narrow detection: mirror C which
|
// Single-binding signed-narrow detection: mirror C which
|
||||||
// reads `u->sub->kind` for the elem type.
|
// reads `u->sub->kind` for the elem type.
|
||||||
let signf0: bool = false;
|
bind_signed[0] = false;
|
||||||
if (elemt != nil) { signf0 = paramissigned(elemt); };
|
if (elemt != nil) {
|
||||||
if (signf0) { bind_signed[0] = 1i64; }
|
bind_signed[0] = paramissigned(elemt);
|
||||||
else { bind_signed[0] = 0i64; };
|
};
|
||||||
if (n.str.len > 0) {
|
if (n.str.len > 0) {
|
||||||
bind_off[0] = localadd(c, n.str, slot_sz, nil): i64;
|
bind_off[0] = localadd(c, n.str, slot_sz, nil);
|
||||||
} else {
|
} else {
|
||||||
bind_off[0] = localalloc(c, mkscratchname(c, "fr"), slot_sz, nil): i64;
|
bind_off[0] = localalloc(c, mkscratchname(c, "fr"), slot_sz, nil);
|
||||||
};
|
};
|
||||||
nbinds = 1;
|
nbinds = 1;
|
||||||
};
|
};
|
||||||
@@ -952,18 +946,18 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
let b: i32 = 0;
|
let b: i32 = 0;
|
||||||
for (b < nbinds) {
|
for (b < nbinds) {
|
||||||
let op: str = "MOVQ";
|
let op: str = "MOVQ";
|
||||||
if (bind_sz[b] == 1i64) { op = "MOVZBQ"; }
|
if (bind_sz[b] == 1) { op = "MOVZBQ"; }
|
||||||
else { if (bind_sz[b] == 4i64) {
|
else { if (bind_sz[b] == 4) {
|
||||||
if (bind_signed[b] != 0i64) { op = "MOVSXD"; }
|
if (bind_signed[b]) { op = "MOVSXD"; }
|
||||||
else { op = "MOVL"; };
|
else { op = "MOVL"; };
|
||||||
};};
|
};};
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitoff(bind_foff[b]);
|
emitoff(bind_foff[b]: i64);
|
||||||
emitline("(BX), AX\n");
|
emitline("(BX), AX\n");
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitoff(bind_off[b]);
|
emitoff(bind_off[b]: i64);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
b += 1;
|
b += 1;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -299,7 +299,24 @@ fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
|||||||
if (k == nkind.N_TFN) { return true; };
|
if (k == nkind.N_TFN) { return true; };
|
||||||
if (k == nkind.N_TCHAN) { return true; };
|
if (k == nkind.N_TCHAN) { return true; };
|
||||||
if (k == nkind.N_TSLICE) { return false; };
|
if (k == nkind.N_TSLICE) { return false; };
|
||||||
if (k == nkind.N_TARRAY) { return false; };
|
if (k == nkind.N_TARRAY) {
|
||||||
|
// C cgen (cmd/w6c/cgen.c:3317) zero-inits TY_ARRAY whenever
|
||||||
|
// its raw byte size is 8 — e.g. `[8]bool`, `[2]i32`, `[4]i16`,
|
||||||
|
// `[1]i64`. Mirror that here so the wwstage matches.
|
||||||
|
let lenn: *node = t.rhs;
|
||||||
|
let elemn: *node = t.lhs;
|
||||||
|
if (lenn == nil) { return false; };
|
||||||
|
if (lenn.kind != nkind.N_INTLIT) { return false; };
|
||||||
|
let elen: i64 = lenn.uval: i64;
|
||||||
|
let esz: i32 = 8;
|
||||||
|
if (elemn != nil) {
|
||||||
|
if (elemn.kind == nkind.N_TNAME) {
|
||||||
|
let ps: i32 = primsize(elemn.str);
|
||||||
|
if (ps > 0) { esz = ps; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return (esz: i64 * elen) == 8i64;
|
||||||
|
};
|
||||||
if (k == nkind.N_TTUPLE) { return false; };
|
if (k == nkind.N_TTUPLE) { return false; };
|
||||||
if (k == nkind.N_TTAGGED){ return false; };
|
if (k == nkind.N_TTAGGED){ return false; };
|
||||||
if (k == nkind.N_TNAME) {
|
if (k == nkind.N_TNAME) {
|
||||||
@@ -315,6 +332,22 @@ fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// elemissigned — given an indexable type (`*T`, `[]T`, `[N]T`), is
|
||||||
|
// its element a signed narrow primitive (i8/i16/i32/rune)? Used by
|
||||||
|
// cgindex to pick MOVSXD vs MOVL at esz=4. Mirrors C cgen's
|
||||||
|
// `signed_elem` check.
|
||||||
|
fn elemissigned(t: *node) bool = {
|
||||||
|
if (t == nil) { return false; };
|
||||||
|
let elem: *node = nil;
|
||||||
|
let k: nkind = t.kind;
|
||||||
|
if (k == nkind.N_TPTR) { elem = t.lhs; };
|
||||||
|
if (k == nkind.N_TSLICE) { elem = t.lhs; };
|
||||||
|
if (k == nkind.N_TARRAY) { elem = t.lhs; };
|
||||||
|
if (elem == nil) { return false; };
|
||||||
|
if (elem.kind != nkind.N_TNAME) { return false; };
|
||||||
|
return typenameissigned(elem.str);
|
||||||
|
};
|
||||||
|
|
||||||
// typenameissigned — true for i8/i16/i32/i64/int/rune.
|
// typenameissigned — true for i8/i16/i32/i64/int/rune.
|
||||||
fn typenameissigned(nm: str) bool = {
|
fn typenameissigned(nm: str) bool = {
|
||||||
if (streq(nm, "i8")) { return true; };
|
if (streq(nm, "i8")) { return true; };
|
||||||
|
|||||||
@@ -5281,7 +5281,24 @@ fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
|||||||
if (k == nkind.N_TFN) { return true; };
|
if (k == nkind.N_TFN) { return true; };
|
||||||
if (k == nkind.N_TCHAN) { return true; };
|
if (k == nkind.N_TCHAN) { return true; };
|
||||||
if (k == nkind.N_TSLICE) { return false; };
|
if (k == nkind.N_TSLICE) { return false; };
|
||||||
if (k == nkind.N_TARRAY) { return false; };
|
if (k == nkind.N_TARRAY) {
|
||||||
|
// C cgen (cmd/w6c/cgen.c:3317) zero-inits TY_ARRAY whenever
|
||||||
|
// its raw byte size is 8 — e.g. `[8]bool`, `[2]i32`, `[4]i16`,
|
||||||
|
// `[1]i64`. Mirror that here so the wwstage matches.
|
||||||
|
let lenn: *node = t.rhs;
|
||||||
|
let elemn: *node = t.lhs;
|
||||||
|
if (lenn == nil) { return false; };
|
||||||
|
if (lenn.kind != nkind.N_INTLIT) { return false; };
|
||||||
|
let elen: i64 = lenn.uval: i64;
|
||||||
|
let esz: i32 = 8;
|
||||||
|
if (elemn != nil) {
|
||||||
|
if (elemn.kind == nkind.N_TNAME) {
|
||||||
|
let ps: i32 = primsize(elemn.str);
|
||||||
|
if (ps > 0) { esz = ps; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
return (esz: i64 * elen) == 8i64;
|
||||||
|
};
|
||||||
if (k == nkind.N_TTUPLE) { return false; };
|
if (k == nkind.N_TTUPLE) { return false; };
|
||||||
if (k == nkind.N_TTAGGED){ return false; };
|
if (k == nkind.N_TTAGGED){ return false; };
|
||||||
if (k == nkind.N_TNAME) {
|
if (k == nkind.N_TNAME) {
|
||||||
@@ -5297,6 +5314,22 @@ fn typeis8byteprimitive(c: *cgen, t: *node) bool = {
|
|||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// elemissigned — given an indexable type (`*T`, `[]T`, `[N]T`), is
|
||||||
|
// its element a signed narrow primitive (i8/i16/i32/rune)? Used by
|
||||||
|
// cgindex to pick MOVSXD vs MOVL at esz=4. Mirrors C cgen's
|
||||||
|
// `signed_elem` check.
|
||||||
|
fn elemissigned(t: *node) bool = {
|
||||||
|
if (t == nil) { return false; };
|
||||||
|
let elem: *node = nil;
|
||||||
|
let k: nkind = t.kind;
|
||||||
|
if (k == nkind.N_TPTR) { elem = t.lhs; };
|
||||||
|
if (k == nkind.N_TSLICE) { elem = t.lhs; };
|
||||||
|
if (k == nkind.N_TARRAY) { elem = t.lhs; };
|
||||||
|
if (elem == nil) { return false; };
|
||||||
|
if (elem.kind != nkind.N_TNAME) { return false; };
|
||||||
|
return typenameissigned(elem.str);
|
||||||
|
};
|
||||||
|
|
||||||
// typenameissigned — true for i8/i16/i32/i64/int/rune.
|
// typenameissigned — true for i8/i16/i32/i64/int/rune.
|
||||||
fn typenameissigned(nm: str) bool = {
|
fn typenameissigned(nm: str) bool = {
|
||||||
if (streq(nm, "i8")) { return true; };
|
if (streq(nm, "i8")) { return true; };
|
||||||
@@ -6644,18 +6677,22 @@ fn cgident(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn cgindex(c: *cgen, n: *node) void = {
|
fn cgindex(c: *cgen, n: *node) void = {
|
||||||
// Element-size-aware load: u8-element bases use MOVZBQ,
|
// Element-size-aware load: u8 → MOVZBQ, i32 → MOVSXD, u32 → MOVL,
|
||||||
// everything else MOVQ. Fast path when the base is a bare
|
// str → (ptr, len) into (AX, BX), everything else → MOVQ. Fast
|
||||||
// ident (mem.ww shape).
|
// path when the base is a bare ident (mem.ww shape).
|
||||||
let base: *node = n.lhs;
|
let base: *node = n.lhs;
|
||||||
let idx: *node = n.rhs;
|
let idx: *node = n.rhs;
|
||||||
let esz: i32 = 8;
|
let esz: i32 = 8;
|
||||||
|
let signed_elem: bool = false;
|
||||||
let baselocal: *local = nil;
|
let baselocal: *local = nil;
|
||||||
if (base != nil) {
|
if (base != nil) {
|
||||||
if (base.kind == nkind.N_IDENT) {
|
if (base.kind == nkind.N_IDENT) {
|
||||||
let bn: str = base.str;
|
let bn: str = base.str;
|
||||||
baselocal = localfindnode(c, bn);
|
baselocal = localfindnode(c, bn);
|
||||||
if (baselocal != nil) { esz = elemsizeof(baselocal.tnode); };
|
if (baselocal != nil) {
|
||||||
|
esz = elemsizeof(baselocal.tnode);
|
||||||
|
signed_elem = elemissigned(baselocal.tnode);
|
||||||
|
};
|
||||||
} else { if (base.kind == nkind.N_DOT) {
|
} else { if (base.kind == nkind.N_DOT) {
|
||||||
esz = indexbaseesz(c, base);
|
esz = indexbaseesz(c, base);
|
||||||
};};
|
};};
|
||||||
@@ -6690,7 +6727,11 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
if (esz == 1) { emitline("\tMOVZBQ\t(BX), AX\n"); }
|
||||||
else { emitline("\tMOVQ\t(BX), AX\n"); };
|
else { if (esz == 4) {
|
||||||
|
if (signed_elem) { emitline("\tMOVSXD\t(BX), AX\n"); }
|
||||||
|
else { emitline("\tMOVL\t(BX), AX\n"); };
|
||||||
|
}
|
||||||
|
else { emitline("\tMOVQ\t(BX), AX\n"); };};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
// Generic fallback when base isn't a plain ident.
|
// Generic fallback when base isn't a plain ident.
|
||||||
@@ -6704,7 +6745,11 @@ fn cgindex(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVZBQ\t(AX), AX\n"); }
|
if (esz == 1) { emitline("\tMOVZBQ\t(AX), AX\n"); }
|
||||||
else { emitline("\tMOVQ\t(AX), AX\n"); };
|
else { if (esz == 4) {
|
||||||
|
if (signed_elem) { emitline("\tMOVSXD\t(AX), AX\n"); }
|
||||||
|
else { emitline("\tMOVL\t(AX), AX\n"); };
|
||||||
|
}
|
||||||
|
else { emitline("\tMOVQ\t(AX), AX\n"); };};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -7951,7 +7996,8 @@ fn cgassign(c: *cgen, n: *node) void = {
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
if (esz == 1) { emitline("\tMOVB\tAX, (BX)\n"); }
|
if (esz == 1) { emitline("\tMOVB\tAX, (BX)\n"); }
|
||||||
else { emitline("\tMOVQ\tAX, (BX)\n"); };
|
else { if (esz == 4) { emitline("\tMOVL\tAX, (BX)\n"); }
|
||||||
|
else { emitline("\tMOVQ\tAX, (BX)\n"); };};
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -8976,13 +9022,11 @@ fn cglet(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
// Bare `let x: T;` with no initializer. C cgen
|
// Bare `let x: T;` with no initializer. C cgen
|
||||||
// (cmd/w6c/cgen.c:2181-2183) zero-inits only when
|
// (cmd/w6c/cgen.c:3317) zero-inits whenever the raw type size
|
||||||
// the underlying type's natural size is 8 — pointers,
|
// is 8: scalar primitives, pointers, fn/chan handles, plus 8B
|
||||||
// i64/u64, function pointers, ints. Structs/arrays/
|
// composites like `[8]bool`, `[2]i32`, `[4]i16`, `[1]i64`.
|
||||||
// slices/strings/tagged/tuples are left for per-field
|
// Larger composites and `[N]T` with size != 8 are left for
|
||||||
// writes. ww's slotsize pads struct slots up to 8,
|
// per-field writes.
|
||||||
// so we can't just check sz == 8: walk the type AST
|
|
||||||
// directly to make the same call.
|
|
||||||
if (typeis8byteprimitive(c, n.lhs)) {
|
if (typeis8byteprimitive(c, n.lhs)) {
|
||||||
emitline("\tMOVQ\t$0, ");
|
emitline("\tMOVQ\t$0, ");
|
||||||
emitoff(off: i64);
|
emitoff(off: i64);
|
||||||
@@ -9271,15 +9315,12 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
let ioff: i32 = localalloc(c, iname, 8, nil);
|
let ioff: i32 = localalloc(c, iname, 8, nil);
|
||||||
let loff: i32 = localalloc(c, lname, 8, nil);
|
let loff: i32 = localalloc(c, lname, 8, nil);
|
||||||
|
|
||||||
// Per-binding (up to 8 — matches the C array). Parallel i64 arrays
|
// Per-binding (up to 8 — matches the C array). Parallel arrays so
|
||||||
// keep every elem at 8B so the indexed-store hits the working MOVQ
|
// we don't depend on local-struct cgen.
|
||||||
// path (selfhost cgen doesn't yet emit MOVL for i32-array writes,
|
let bind_off: [8]i32;
|
||||||
// and doesn't zero-init `[8]bool` uninit slots — both byte-diverge
|
let bind_sz: [8]i32;
|
||||||
// from C w6c on the wwstage rebuild).
|
let bind_foff: [8]i32;
|
||||||
let bind_off: [8]i64;
|
let bind_signed: [8]bool;
|
||||||
let bind_sz: [8]i64;
|
|
||||||
let bind_foff: [8]i64;
|
|
||||||
let bind_signed: [8]i64; // 0 / 1
|
|
||||||
let nbinds: i32 = 0;
|
let nbinds: i32 = 0;
|
||||||
|
|
||||||
if (destruct) {
|
if (destruct) {
|
||||||
@@ -9300,15 +9341,14 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
};
|
};
|
||||||
let slot_sz: i32 = fsz;
|
let slot_sz: i32 = fsz;
|
||||||
if (slot_sz < 8) { slot_sz = 8; };
|
if (slot_sz < 8) { slot_sz = 8; };
|
||||||
bind_sz[nbinds] = fsz: i64;
|
bind_sz[nbinds] = fsz;
|
||||||
bind_foff[nbinds] = field_off: i64;
|
bind_foff[nbinds] = field_off;
|
||||||
if (signf) { bind_signed[nbinds] = 1i64; }
|
bind_signed[nbinds] = signf;
|
||||||
else { bind_signed[nbinds] = 0i64; };
|
|
||||||
let bnm: str = m.str;
|
let bnm: str = m.str;
|
||||||
if (bnm.len > 0) {
|
if (bnm.len > 0) {
|
||||||
bind_off[nbinds] = localadd(c, bnm, slot_sz, nil): i64;
|
bind_off[nbinds] = localadd(c, bnm, slot_sz, nil);
|
||||||
} else {
|
} else {
|
||||||
bind_off[nbinds] = localalloc(c, mkscratchname(c, "fr"), slot_sz, nil): i64;
|
bind_off[nbinds] = localalloc(c, mkscratchname(c, "fr"), slot_sz, nil);
|
||||||
};
|
};
|
||||||
field_off += fsz;
|
field_off += fsz;
|
||||||
nbinds += 1;
|
nbinds += 1;
|
||||||
@@ -9319,18 +9359,18 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
} else {
|
} else {
|
||||||
let slot_sz: i32 = esz;
|
let slot_sz: i32 = esz;
|
||||||
if (slot_sz < 8) { slot_sz = 8; };
|
if (slot_sz < 8) { slot_sz = 8; };
|
||||||
bind_sz[0] = esz: i64;
|
bind_sz[0] = esz;
|
||||||
bind_foff[0] = 0i64;
|
bind_foff[0] = 0;
|
||||||
// Single-binding signed-narrow detection: mirror C which
|
// Single-binding signed-narrow detection: mirror C which
|
||||||
// reads `u->sub->kind` for the elem type.
|
// reads `u->sub->kind` for the elem type.
|
||||||
let signf0: bool = false;
|
bind_signed[0] = false;
|
||||||
if (elemt != nil) { signf0 = paramissigned(elemt); };
|
if (elemt != nil) {
|
||||||
if (signf0) { bind_signed[0] = 1i64; }
|
bind_signed[0] = paramissigned(elemt);
|
||||||
else { bind_signed[0] = 0i64; };
|
};
|
||||||
if (n.str.len > 0) {
|
if (n.str.len > 0) {
|
||||||
bind_off[0] = localadd(c, n.str, slot_sz, nil): i64;
|
bind_off[0] = localadd(c, n.str, slot_sz, nil);
|
||||||
} else {
|
} else {
|
||||||
bind_off[0] = localalloc(c, mkscratchname(c, "fr"), slot_sz, nil): i64;
|
bind_off[0] = localalloc(c, mkscratchname(c, "fr"), slot_sz, nil);
|
||||||
};
|
};
|
||||||
nbinds = 1;
|
nbinds = 1;
|
||||||
};
|
};
|
||||||
@@ -9424,18 +9464,18 @@ fn cgforrange(c: *cgen, n: *node) void = {
|
|||||||
let b: i32 = 0;
|
let b: i32 = 0;
|
||||||
for (b < nbinds) {
|
for (b < nbinds) {
|
||||||
let op: str = "MOVQ";
|
let op: str = "MOVQ";
|
||||||
if (bind_sz[b] == 1i64) { op = "MOVZBQ"; }
|
if (bind_sz[b] == 1) { op = "MOVZBQ"; }
|
||||||
else { if (bind_sz[b] == 4i64) {
|
else { if (bind_sz[b] == 4) {
|
||||||
if (bind_signed[b] != 0i64) { op = "MOVSXD"; }
|
if (bind_signed[b]) { op = "MOVSXD"; }
|
||||||
else { op = "MOVL"; };
|
else { op = "MOVL"; };
|
||||||
};};
|
};};
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitline(op);
|
emitline(op);
|
||||||
emitline("\t");
|
emitline("\t");
|
||||||
emitoff(bind_foff[b]);
|
emitoff(bind_foff[b]: i64);
|
||||||
emitline("(BX), AX\n");
|
emitline("(BX), AX\n");
|
||||||
emitline("\tMOVQ\tAX, ");
|
emitline("\tMOVQ\tAX, ");
|
||||||
emitoff(bind_off[b]);
|
emitoff(bind_off[b]: i64);
|
||||||
emitline("(BP)\n");
|
emitline("(BP)\n");
|
||||||
b += 1;
|
b += 1;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -345,6 +345,24 @@ probe_ww_compile(const char *bin)
|
|||||||
" dec(&c); dec(&c); dec(&c);\n"
|
" dec(&c); dec(&c); dec(&c);\n"
|
||||||
" return c.x;\n"
|
" return c.x;\n"
|
||||||
"};", 2 },
|
"};", 2 },
|
||||||
|
/* Uninit `[8]u8` local — raw size 8B, C cgen zero-inits via
|
||||||
|
* `MOVQ $0, off(BP)`. Pre-fix the wwstage left the slot
|
||||||
|
* holding stack junk and `buf[0]` returned non-zero. */
|
||||||
|
{ "fn main() i32 = {\n"
|
||||||
|
" let buf: [8]u8;\n"
|
||||||
|
" return buf[0]: i32;\n"
|
||||||
|
"};", 0 },
|
||||||
|
/* [N]i32 indexed write + read: MOVL store, MOVSXD load.
|
||||||
|
* Pre-fix the wwstage emitted MOVQ in both directions —
|
||||||
|
* overwrites adjacent slots and loads the wrong width.
|
||||||
|
* arr[0..3] = -7, 3, 11, sum = 7. */
|
||||||
|
{ "fn main() i32 = {\n"
|
||||||
|
" let arr: [3]i32;\n"
|
||||||
|
" arr[0] = 0 - 7;\n"
|
||||||
|
" arr[1] = 3;\n"
|
||||||
|
" arr[2] = 11;\n"
|
||||||
|
" return arr[0] + arr[1] + arr[2];\n"
|
||||||
|
"};", 7 },
|
||||||
/* Hare-style for-range over a slice: `for (let b .. s)`.
|
/* Hare-style for-range over a slice: `for (let b .. s)`.
|
||||||
* Allocates `.rgi`/`.rgl` scratch slots, walks i=0..s.len
|
* Allocates `.rgi`/`.rgl` scratch slots, walks i=0..s.len
|
||||||
* loading s.ptr[i] into the binding. esz=1 here so the
|
* loading s.ptr[i] into the binding. esz=1 here so the
|
||||||
|
|||||||
Reference in New Issue
Block a user