Phase 7i: byte indexing p[i] / p[i] = v (retire peek8/poke8)
Raw byte access is now indexing, not C-style peek/poke: peek8(src, i) -> src[i] (reads a byte, widened to int) poke8(out, j, r) -> out[j] = r (narrows the int to a byte) E_INDEX on a non-slice pointer/string lowers to a `getelementptr i8` + load/zext (read) or trunc/store (write) — byte-identical to the old peek8/poke8, so the migration reproduces the compiler exactly. A "byte" element type (llty i8) drives the widen/narrow. The compiler's own byte work now reads naturally, e.g. `is_slice_ty` is `t[0] == 91 and t[1] == 93`. Subtlety fixed on the way: g_addr_ty (the out-param carrying the indexed element type) must be set AFTER evaluating the index expression, since a member/index in the index would otherwise clobber it — doing it early made a byte read load a full pointer from a byte address and crash the self-compile. Two reseeds: add byte-index support keeping peek8/poke8, then migrate 148 call sites and delete the intrinsics (+ the now-dead emit_gep_i8). Vocabulary drops peek8/poke8. Reseeded (22604 lines); C-free fixpoint holds; goldens identical; 18/18; vocab clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
3aaf983cf5
commit
feb3a71e56
25 changed files with 3309 additions and 3388 deletions
|
|
@ -18,12 +18,12 @@ fn buf_ensure(b: Buf, extra: int) -> void {
|
|||
}
|
||||
fn buf_putc(b: Buf, c: int) -> void {
|
||||
buf_ensure(b, 1)
|
||||
poke8(b.data, b.len, c)
|
||||
b.data[b.len] = c
|
||||
b.len = b.len + 1
|
||||
}
|
||||
fn buf_puts(b: Buf, s: ptr) -> void {
|
||||
var i = 0
|
||||
while peek8(s, i) != 0 { buf_putc(b, peek8(s, i)); i = i + 1 }
|
||||
while s[i] != 0 { buf_putc(b, s[i]); i = i + 1 }
|
||||
}
|
||||
fn buf_puti(b: Buf, n: int) -> void { buf_puts(b, itoa(n)) }
|
||||
fn buf_str(b: Buf) -> ptr { poke8(b.data, b.len, 0); return b.data }
|
||||
fn buf_str(b: Buf) -> ptr { b.data[b.len] = 0; return b.data }
|
||||
|
|
|
|||
|
|
@ -21,7 +21,12 @@ fn emit_member_addr(e: Node) -> ptr {
|
|||
# address of `base[index]` (slices only in this subset)
|
||||
fn emit_index_addr(e: Node) -> ptr {
|
||||
let base = emit_expr(e.a)
|
||||
if not is_slice_ty(base.ty) { perr("indexing a non-slice") }
|
||||
if not is_slice_ty(base.ty) { # a raw ptr/str: address of byte i.
|
||||
let bi = emit_expr(e.b) # (evaluate index first — it may set g_addr_ty)
|
||||
let r = emit_bind(`getelementptr inbounds i8, ptr {base.code}, i32 {bi.code}`)
|
||||
g_addr_ty = "byte" # set last so the caller sees it
|
||||
return r
|
||||
}
|
||||
let el = slice_elem(base.ty)
|
||||
g_addr_ty = el
|
||||
# load the data pointer from the slice header (field 0)
|
||||
|
|
|
|||
|
|
@ -53,11 +53,12 @@ fn lbl(pfx: ptr) -> ptr { let r = (pfx + itoa(ll_lbl)); ll_lbl = ll_lbl + 1; r
|
|||
# slice) is a pointer; void is void.
|
||||
fn llty(t: ptr) -> ptr {
|
||||
if (t == "int") or (t == "bool") or (t == "fixed") { return "i32" }
|
||||
if (t == "byte") { return "i8" } # a single byte (p[i] on a raw ptr)
|
||||
if (t == "void") { return "void" }
|
||||
return "ptr"
|
||||
}
|
||||
|
||||
fn is_slice_ty(t: ptr) -> bool { return peek8(t, 0) == 91 and peek8(t, 1) == 93 } # "[]"
|
||||
fn is_slice_ty(t: ptr) -> bool { return t[0] == 91 and t[1] == 93 } # "[]"
|
||||
fn slice_elem(t: ptr) -> ptr { return t[2..len(t)] }
|
||||
|
||||
fn find_arch(name: ptr) -> Node {
|
||||
|
|
|
|||
|
|
@ -215,7 +215,14 @@ fn emit_expr(e: Node) -> Val {
|
|||
}
|
||||
let a = emit_member_addr(e); return emit_load_at(a, g_addr_ty)
|
||||
}
|
||||
if e.kind == E_INDEX { let a = emit_index_addr(e); return emit_load_at(a, g_addr_ty) }
|
||||
if e.kind == E_INDEX {
|
||||
let a = emit_index_addr(e)
|
||||
if (g_addr_ty == "byte") { # a byte read, widened to int
|
||||
let b = emit_bind(`load i8, ptr {a}`)
|
||||
return val(emit_bind(`zext i8 {b} to i32`), "int")
|
||||
}
|
||||
return emit_load_at(a, g_addr_ty)
|
||||
}
|
||||
if e.kind == E_CALL { return emit_call(e) }
|
||||
if e.kind == E_BIN { return emit_bin(e) }
|
||||
if e.kind == E_UN {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ fn emit_str_const(s: ptr) -> ptr {
|
|||
emith(itoa(n + 1)); emith(" x i8] c\"")
|
||||
var i = 0
|
||||
while i < n {
|
||||
let c = peek8(s, i)
|
||||
let c = s[i]
|
||||
if c == 34 or c == 92 or c < 32 or c > 126 {
|
||||
buf_putc(head, 92) # backslash
|
||||
buf_putc(head, hexdig(c / 16))
|
||||
|
|
@ -24,14 +24,6 @@ fn emit_str_const(s: ptr) -> ptr {
|
|||
return name
|
||||
}
|
||||
|
||||
# a fresh SSA register bound to a `getelementptr`, returned as its name
|
||||
fn emit_gep_i8(base: ptr, idx: ptr) -> ptr {
|
||||
let r = nreg()
|
||||
emit(" "); emit(r); emit(" = getelementptr inbounds i8, ptr ")
|
||||
emit(base); emit(", i32 "); emit(idx); emit("\n")
|
||||
return r
|
||||
}
|
||||
|
||||
# the constant initializer for a global var: a literal, or 0/null
|
||||
fn global_init(d: Node) -> ptr {
|
||||
if (d.a == null) { if (llty(d.ty) == "ptr") { return "null" }; return "0" }
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ var g_intrin_ok: bool = false
|
|||
# without evaluating arguments (which could clobber shared state).
|
||||
fn is_intrinsic(name: ptr) -> bool {
|
||||
if (name == "mem_alloc") or (name == "mem_realloc") { return true }
|
||||
if (name == "peek8") or (name == "poke8") { return true }
|
||||
if (name == "file_open") or (name == "file_read") or (name == "file_write") { return true }
|
||||
if (name == "file_seek") or (name == "file_tell") or (name == "file_close") { return true }
|
||||
if (name == "os_argc") or (name == "os_arg") or (name == "os_exit") or (name == "file_stderr") { return true }
|
||||
|
|
@ -35,19 +34,6 @@ fn emit_intrinsic(name: ptr, e: Node) -> Val {
|
|||
let w = emit_bind(`zext i32 {n} to i64`)
|
||||
return val(emit_bind(`call ptr @realloc(ptr {p}, i64 {w})`), "ptr")
|
||||
}
|
||||
if (name == "peek8") {
|
||||
let p = arg_code(e, 0); let i = arg_code(e, 1)
|
||||
let a = emit_gep_i8(p, i)
|
||||
let b = emit_bind(`load i8, ptr {a}`)
|
||||
return val(emit_bind(`zext i8 {b} to i32`), "int")
|
||||
}
|
||||
if (name == "poke8") {
|
||||
let p = arg_code(e, 0); let i = arg_code(e, 1); let v = arg_code(e, 2)
|
||||
let a = emit_gep_i8(p, i)
|
||||
let t = emit_bind(`trunc i32 {v} to i8`)
|
||||
emit(" store i8 "); emit(t); emit(", ptr "); emit(a); emit("\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
if (name == "file_open") {
|
||||
let p = arg_code(e, 0); let m = arg_code(e, 1)
|
||||
return val(emit_bind(`call ptr @fopen(ptr {p}, ptr {m})`), "ptr")
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ fn emit_assign(st: Node) -> void {
|
|||
if (st.s == ("/=")) { opc = "sdiv" }
|
||||
v = emit_bind(`{opc} i32 {cur}, {v}`)
|
||||
}
|
||||
if (lt == "i8") { v = emit_bind(`trunc i32 {v} to i8`) } # narrow to a byte for p[i] = v
|
||||
store_at(lt, v, addr)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ fn ui_index_of(nm: ptr) -> int {
|
|||
return 0
|
||||
}
|
||||
fn is_ui_ident(nm: ptr) -> bool {
|
||||
return len(nm) > 3 and peek8(nm, 0) == 85 and peek8(nm, 1) == 73 and peek8(nm, 2) == 95 # "UI_"
|
||||
return len(nm) > 3 and nm[0] == 85 and nm[1] == 73 and nm[2] == 95 # "UI_"
|
||||
}
|
||||
|
||||
fn ll_ui_set(idx: int, key: int, val: ptr) -> void {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ fn read_file(path: str) -> ptr {
|
|||
file_seek(f, 0, 0)
|
||||
let buf = mem_alloc(n + 1)
|
||||
file_read(f, buf, n)
|
||||
poke8(buf, n, 0)
|
||||
buf[n] = 0
|
||||
file_close(f)
|
||||
return buf
|
||||
}
|
||||
|
|
@ -17,6 +17,6 @@ fn read_file(path: str) -> ptr {
|
|||
# length of a NUL-terminated buffer
|
||||
fn cstr_len(s: ptr) -> int {
|
||||
var n = 0
|
||||
while peek8(s, n) != 0 { n = n + 1 }
|
||||
while s[n] != 0 { n = n + 1 }
|
||||
return n
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ fn tok_push(kind: int, text: ptr, ival: int, line: int) -> void {
|
|||
|
||||
# does src match the 2-char operator op at position i?
|
||||
fn two_at(src: ptr, i: int, a: int, b: int) -> bool {
|
||||
return peek8(src, i) == a and peek8(src, i + 1) == b
|
||||
return src[i] == a and src[i + 1] == b
|
||||
}
|
||||
|
||||
fn is_op1(c: int) -> bool {
|
||||
|
|
@ -43,11 +43,11 @@ fn lex(src: ptr) -> void {
|
|||
var line = 1
|
||||
let n = len(src)
|
||||
while i < n {
|
||||
let c = peek8(src, i)
|
||||
let c = src[i]
|
||||
if c == 10 { tok_push(TK_NL, null, 0, line); line = line + 1; i = i + 1; continue }
|
||||
if c == 32 or c == 9 or c == 13 { i = i + 1; continue }
|
||||
if c == 35 { # '#' comment to end of line
|
||||
while i < n and peek8(src, i) != 10 { i = i + 1 }
|
||||
while i < n and src[i] != 10 { i = i + 1 }
|
||||
continue
|
||||
}
|
||||
if c == 34 { # "string"
|
||||
|
|
@ -55,18 +55,18 @@ fn lex(src: ptr) -> void {
|
|||
let start = i
|
||||
let out = mem_alloc(n)
|
||||
var j = 0
|
||||
while i < n and peek8(src, i) != 34 {
|
||||
if peek8(src, i) == 92 { # backslash escape
|
||||
let e = peek8(src, i + 1)
|
||||
while i < n and src[i] != 34 {
|
||||
if src[i] == 92 { # backslash escape
|
||||
let e = src[i + 1]
|
||||
var r = e
|
||||
if e == 110 { r = 10 }
|
||||
if e == 116 { r = 9 }
|
||||
if e == 48 { r = 0 }
|
||||
poke8(out, j, r); j = j + 1; i = i + 2
|
||||
} else { poke8(out, j, peek8(src, i)); j = j + 1; i = i + 1 }
|
||||
out[j] = r; j = j + 1; i = i + 2
|
||||
} else { out[j] = src[i]; j = j + 1; i = i + 1 }
|
||||
}
|
||||
i = i + 1
|
||||
poke8(out, j, 0)
|
||||
out[j] = 0
|
||||
tok_push(TK_STR, out, 0, line)
|
||||
continue
|
||||
}
|
||||
|
|
@ -74,32 +74,32 @@ fn lex(src: ptr) -> void {
|
|||
i = i + 1
|
||||
let out = mem_alloc(n)
|
||||
var j = 0
|
||||
while i < n and peek8(src, i) != 96 { poke8(out, j, peek8(src, i)); j = j + 1; i = i + 1 }
|
||||
while i < n and src[i] != 96 { out[j] = src[i]; j = j + 1; i = i + 1 }
|
||||
i = i + 1
|
||||
poke8(out, j, 0)
|
||||
out[j] = 0
|
||||
tok_push(TK_INTERP, out, 0, line)
|
||||
continue
|
||||
}
|
||||
if c == 39 { # 'c' char literal -> int
|
||||
i = i + 1
|
||||
var v = 0
|
||||
if peek8(src, i) == 92 {
|
||||
let e = peek8(src, i + 1)
|
||||
if src[i] == 92 {
|
||||
let e = src[i + 1]
|
||||
if e == 110 { v = 10 }
|
||||
if e == 116 { v = 9 }
|
||||
if e == 48 { v = 0 }
|
||||
i = i + 2
|
||||
} else { v = peek8(src, i); i = i + 1 }
|
||||
if peek8(src, i) == 39 { i = i + 1 }
|
||||
} else { v = src[i]; i = i + 1 }
|
||||
if src[i] == 39 { i = i + 1 }
|
||||
tok_push(TK_INT, null, v, line)
|
||||
continue
|
||||
}
|
||||
if char_is_digit(c) {
|
||||
if c == 48 and peek8(src, i + 1) == 120 { # 0x hex
|
||||
if c == 48 and src[i + 1] == 120 { # 0x hex
|
||||
var v = 0
|
||||
i = i + 2
|
||||
while i < n {
|
||||
let h = peek8(src, i)
|
||||
let h = src[i]
|
||||
var d = 0
|
||||
if char_is_digit(h) { d = h - 48 }
|
||||
else { if h >= 97 and h <= 102 { d = h - 87 }
|
||||
|
|
@ -111,12 +111,12 @@ fn lex(src: ptr) -> void {
|
|||
continue
|
||||
}
|
||||
var v = 0
|
||||
while i < n and char_is_digit(peek8(src, i)) { v = v * 10 + (peek8(src, i) - 48); i = i + 1 }
|
||||
while i < n and char_is_digit(src[i]) { v = v * 10 + (src[i] - 48); i = i + 1 }
|
||||
# a fractional part makes it a Q16.16 fixed literal
|
||||
if i < n and peek8(src, i) == 46 and char_is_digit(peek8(src, i + 1)) {
|
||||
if i < n and src[i] == 46 and char_is_digit(src[i + 1]) {
|
||||
i = i + 1
|
||||
var fnum = 0; var fden = 1
|
||||
while i < n and char_is_digit(peek8(src, i)) { fnum = fnum * 10 + (peek8(src, i) - 48); fden = fden * 10; i = i + 1 }
|
||||
while i < n and char_is_digit(src[i]) { fnum = fnum * 10 + (src[i] - 48); fden = fden * 10; i = i + 1 }
|
||||
let bits = (v << 16) + (fnum << 16) / fden
|
||||
tok_push(TK_FLOAT, null, bits, line)
|
||||
continue
|
||||
|
|
@ -126,7 +126,7 @@ fn lex(src: ptr) -> void {
|
|||
}
|
||||
if char_is_alpha(c) {
|
||||
let start = i
|
||||
while i < n and char_is_alnum(peek8(src, i)) { i = i + 1 }
|
||||
while i < n and char_is_alnum(src[i]) { i = i + 1 }
|
||||
tok_push(TK_ID, src[start..i], 0, line)
|
||||
continue
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -18,7 +18,7 @@
|
|||
fn base_name(path: ptr) -> ptr {
|
||||
var last = 0 - 1
|
||||
var i = 0
|
||||
while peek8(path, i) != 0 { if peek8(path, i) == 47 { last = i }; i = i + 1 }
|
||||
while path[i] != 0 { if path[i] == 47 { last = i }; i = i + 1 }
|
||||
return path[last + 1..i]
|
||||
}
|
||||
|
||||
|
|
@ -42,7 +42,7 @@ fn getenv_or(name: ptr, dflt: ptr) -> ptr {
|
|||
fn ensure_slash(d: ptr) -> ptr {
|
||||
let n = len(d)
|
||||
if n == 0 { return d }
|
||||
if peek8(d, n - 1) == 47 { return d }
|
||||
if d[n - 1] == 47 { return d }
|
||||
return (d + ("/"))
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ entry {
|
|||
else {
|
||||
# an unknown flag is ignored (with a note) rather than mistaken for the
|
||||
# input file — '-' is ASCII 45
|
||||
if peek8(a, 0) == 45 {
|
||||
if a[0] == 45 {
|
||||
let m = `ludicc: ignoring unknown flag {a}\n`
|
||||
file_write(file_stderr(), m, len(m))
|
||||
} else { path = a }
|
||||
|
|
|
|||
|
|
@ -36,10 +36,10 @@ fn ptype() -> ptr {
|
|||
eat_op("]")
|
||||
let el = ptype()
|
||||
let out = mem_alloc(len(el) + 3)
|
||||
poke8(out, 0, 91); poke8(out, 1, 93) # "[]"
|
||||
out[0] = 91; out[1] = 93 # "[]"
|
||||
var i = 0
|
||||
while peek8(el, i) != 0 { poke8(out, 2 + i, peek8(el, i)); i = i + 1 }
|
||||
poke8(out, 2 + i, 0)
|
||||
while el[i] != 0 { out[2 + i] = el[i]; i = i + 1 }
|
||||
out[2 + i] = 0
|
||||
return out
|
||||
}
|
||||
return eat_id()
|
||||
|
|
@ -79,15 +79,15 @@ fn parse_interp(raw: ptr) -> Node {
|
|||
var lj = 0
|
||||
var i = 0
|
||||
while i < n {
|
||||
let c = peek8(raw, i)
|
||||
let c = raw[i]
|
||||
if c == 123 { # '{'
|
||||
if peek8(raw, i + 1) == 123 { poke8(lit, lj, 123); lj = lj + 1; i = i + 2; continue } # {{ -> {
|
||||
if raw[i + 1] == 123 { lit[lj] = 123; lj = lj + 1; i = i + 2; continue } # {{ -> {
|
||||
if lj > 0 { acc = interp_add(acc, interp_lit(lit, lj)); lj = 0 }
|
||||
i = i + 1
|
||||
let hs = i
|
||||
var depth = 1
|
||||
while i < n and depth > 0 {
|
||||
let d = peek8(raw, i)
|
||||
let d = raw[i]
|
||||
if d == 123 { depth = depth + 1 }
|
||||
else { if d == 125 { depth = depth - 1; if depth == 0 { break } } }
|
||||
i = i + 1
|
||||
|
|
@ -95,14 +95,14 @@ fn parse_interp(raw: ptr) -> Node {
|
|||
acc = interp_add(acc, interp_str(parse_hole(raw[hs..i])))
|
||||
i = i + 1 # skip the closing '}'
|
||||
} else {
|
||||
if c == 125 and peek8(raw, i + 1) == 125 { poke8(lit, lj, 125); lj = lj + 1; i = i + 2; continue } # }} -> }
|
||||
if c == 125 and raw[i + 1] == 125 { lit[lj] = 125; lj = lj + 1; i = i + 2; continue } # }} -> }
|
||||
if c == 92 { # backslash escape in literal text
|
||||
let e = peek8(raw, i + 1); var r = e
|
||||
let e = raw[i + 1]; var r = e
|
||||
if e == 110 { r = 10 }
|
||||
if e == 116 { r = 9 }
|
||||
poke8(lit, lj, r); lj = lj + 1; i = i + 2; continue
|
||||
lit[lj] = r; lj = lj + 1; i = i + 2; continue
|
||||
}
|
||||
poke8(lit, lj, c); lj = lj + 1; i = i + 1
|
||||
lit[lj] = c; lj = lj + 1; i = i + 1
|
||||
}
|
||||
}
|
||||
if lj > 0 or (acc == null) { acc = interp_add(acc, interp_lit(lit, lj)) }
|
||||
|
|
@ -311,12 +311,12 @@ fn parse_main() -> Node { pi = pi + 1; let n = node(N_MAIN); n.a = block(); retu
|
|||
fn dir_of(path: ptr) -> ptr {
|
||||
var last = 0 - 1
|
||||
var i = 0
|
||||
while peek8(path, i) != 0 { if peek8(path, i) == 47 { last = i }; i = i + 1 }
|
||||
while path[i] != 0 { if path[i] == 47 { last = i }; i = i + 1 }
|
||||
if last < 0 { return "" }
|
||||
return path[0..0 + (last + 1)]
|
||||
}
|
||||
fn path_join(dir: ptr, rel: ptr) -> ptr {
|
||||
if peek8(rel, 0) == 47 { return rel } # absolute
|
||||
if rel[0] == 47 { return rel } # absolute
|
||||
return (dir + rel)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -14,20 +14,20 @@ fn char_is_alnum(c: int) -> bool { return char_is_alpha(c) or char_is_digit(c) }
|
|||
|
||||
# integer -> fresh decimal string
|
||||
fn itoa(v: int) -> ptr {
|
||||
if v == 0 { let z = mem_alloc(2); poke8(z, 0, 48); poke8(z, 1, 0); return z }
|
||||
if v == 0 { let z = mem_alloc(2); z[0] = 48; z[1] = 0; return z }
|
||||
var neg = false
|
||||
var x = v
|
||||
if x < 0 { neg = true; x = 0 - x }
|
||||
let tmp = mem_alloc(16)
|
||||
var n = 0
|
||||
while x > 0 { poke8(tmp, n, 48 + x % 10); x = x / 10; n = n + 1 }
|
||||
while x > 0 { tmp[n] = 48 + x % 10; x = x / 10; n = n + 1 }
|
||||
var total = n
|
||||
if neg { total = total + 1 }
|
||||
let out = mem_alloc(total + 1)
|
||||
var k = 0
|
||||
if neg { poke8(out, 0, 45); k = 1 }
|
||||
if neg { out[0] = 45; k = 1 }
|
||||
var i = 0
|
||||
while i < n { poke8(out, k + i, peek8(tmp, n - 1 - i)); i = i + 1 }
|
||||
poke8(out, total, 0)
|
||||
while i < n { out[k + i] = tmp[n - 1 - i]; i = i + 1 }
|
||||
out[total] = 0
|
||||
return out
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue