Baseline: Ludic compiler + toolchain, Phase 1 syntax fixes complete
Self-hosted compiler (selfhost/*.ludic), runtime, examples, editor tooling, and docs. Phase 1 of the syntax-redesign cohesion pass has landed: edge-system fix, signature-query, when-alias, and the documentation truth-pass. Suite green (14/14), C-free bootstrap fixpoint holds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
commit
985f9ad8f2
418 changed files with 39065 additions and 0 deletions
142
selfhost/emit_intrin.ludic
Normal file
142
selfhost/emit_intrin.ludic
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
# emit_intrin.ludic — the low-level intrinsics the self-host source uses, each
|
||||
# lowered to the same libc/inline IR the C backend emits. Returns a Val via
|
||||
# g_intrin_val and sets g_intrin_ok when `name` was an intrinsic.
|
||||
|
||||
var g_intrin_ok: bool = false
|
||||
|
||||
# is `name` a low-level intrinsic? A pure name check, so it can gate dispatch
|
||||
# without evaluating arguments (which could clobber shared state).
|
||||
fn is_intrinsic(name: ptr) -> bool {
|
||||
if streq(name,"ptr_null") or streq(name,"mem_alloc") or streq(name,"mem_realloc") { return true }
|
||||
if streq(name,"peek8") or streq(name,"poke8") or streq(name,"ptr_is_null") { return true }
|
||||
if streq(name,"file_open") or streq(name,"file_read") or streq(name,"file_write") { return true }
|
||||
if streq(name,"file_seek") or streq(name,"file_tell") or streq(name,"file_close") { return true }
|
||||
if streq(name,"print_str") or streq(name,"print_int") { return true }
|
||||
if streq(name,"os_argc") or streq(name,"os_arg") or streq(name,"os_exit") or streq(name,"file_stderr") { return true }
|
||||
if streq(name,"os_system") or streq(name,"os_getenv") { return true }
|
||||
if streq(name,"shl") or streq(name,"shr") or streq(name,"band") or streq(name,"bor") or streq(name,"bxor") or streq(name,"bnot") { return true }
|
||||
if streq(name,"peek32") or streq(name,"poke32") { return true }
|
||||
return false
|
||||
}
|
||||
# emit " <r> = <rest>\n" and return r
|
||||
fn emit_bind(rest: ptr) -> ptr { let r = nreg() emit(" ") emit(r) emit(" = ") emit(rest) emit("\n") return r }
|
||||
|
||||
fn arg_code(e: Node, i: int) -> ptr { let v = emit_expr(e.kids[i]) return v.code }
|
||||
|
||||
fn emit_intrinsic(name: ptr, e: Node) -> Val {
|
||||
g_intrin_ok = true
|
||||
if streq(name, "ptr_null") { return val("null", "ptr") }
|
||||
if streq(name, "mem_alloc") {
|
||||
let n = arg_code(e, 0)
|
||||
let w = emit_bind(sconcat("zext i32 ", sconcat(n, " to i64")))
|
||||
return val(emit_bind(sconcat("call ptr @malloc(i64 ", sconcat(w, ")"))), "ptr")
|
||||
}
|
||||
if streq(name, "mem_realloc") {
|
||||
let p = arg_code(e, 0) let n = arg_code(e, 1)
|
||||
let w = emit_bind(sconcat("zext i32 ", sconcat(n, " to i64")))
|
||||
return val(emit_bind(sconcat("call ptr @realloc(ptr ", sconcat(p, sconcat(", i64 ", sconcat(w, ")"))))), "ptr")
|
||||
}
|
||||
if streq(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(sconcat("load i8, ptr ", a))
|
||||
return val(emit_bind(sconcat("zext i8 ", sconcat(b, " to i32"))), "int")
|
||||
}
|
||||
if streq(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(sconcat("trunc i32 ", sconcat(v, " to i8")))
|
||||
emit(" store i8 ") emit(t) emit(", ptr ") emit(a) emit("\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
if streq(name, "ptr_is_null") {
|
||||
let p = arg_code(e, 0)
|
||||
let c = emit_bind(sconcat("icmp eq ptr ", sconcat(p, ", null")))
|
||||
return val(emit_bind(sconcat("zext i1 ", sconcat(c, " to i32"))), "bool")
|
||||
}
|
||||
if streq(name, "file_open") {
|
||||
let p = arg_code(e, 0) let m = arg_code(e, 1)
|
||||
return val(emit_bind(sconcat("call ptr @fopen(ptr ", sconcat(p, sconcat(", ptr ", sconcat(m, ")"))))), "ptr")
|
||||
}
|
||||
if streq(name, "file_read") or streq(name, "file_write") {
|
||||
let f = arg_code(e, 0) let b = arg_code(e, 1) let n = arg_code(e, 2)
|
||||
let w = emit_bind(sconcat("zext i32 ", sconcat(n, " to i64")))
|
||||
let fn2 = "@fread"
|
||||
if streq(name, "file_write") { fn2 = "@fwrite" }
|
||||
let r = emit_bind(sconcat("call i64 ", sconcat(fn2, sconcat("(ptr ", sconcat(b, sconcat(", i64 1, i64 ", sconcat(w, sconcat(", ptr ", sconcat(f, ")")))))))))
|
||||
return val(emit_bind(sconcat("trunc i64 ", sconcat(r, " to i32"))), "int")
|
||||
}
|
||||
if streq(name, "file_seek") {
|
||||
let f = arg_code(e, 0) let off = arg_code(e, 1) let wh = arg_code(e, 2)
|
||||
let o = emit_bind(sconcat("sext i32 ", sconcat(off, " to i64")))
|
||||
return val(emit_bind(sconcat("call i32 @fseek(ptr ", sconcat(f, sconcat(", i64 ", sconcat(o, sconcat(", i32 ", sconcat(wh, ")"))))))), "int")
|
||||
}
|
||||
if streq(name, "file_tell") {
|
||||
let f = arg_code(e, 0)
|
||||
let r = emit_bind(sconcat("call i64 @ftell(ptr ", sconcat(f, ")")))
|
||||
return val(emit_bind(sconcat("trunc i64 ", sconcat(r, " to i32"))), "int")
|
||||
}
|
||||
if streq(name, "file_close") {
|
||||
let f = arg_code(e, 0)
|
||||
emit(" call i32 @fclose(ptr ") emit(f) emit(")\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
if streq(name, "print_str") {
|
||||
let s = arg_code(e, 0)
|
||||
emit(" call i32 (ptr, ...) @printf(ptr @.fmt_str, ptr ") emit(s) emit(")\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
if streq(name, "print_int") {
|
||||
let n = arg_code(e, 0)
|
||||
emit(" call i32 (ptr, ...) @printf(ptr @.fmt_int, i32 ") emit(n) emit(")\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
if streq(name, "os_argc") { return val(emit_bind("load i32, ptr @L_argc"), "int") }
|
||||
if streq(name, "os_arg") {
|
||||
let i = arg_code(e, 0)
|
||||
let v = emit_bind("load ptr, ptr @L_argv")
|
||||
let q = emit_bind(sconcat("getelementptr ptr, ptr ", sconcat(v, sconcat(", i32 ", i))))
|
||||
return val(emit_bind(sconcat("load ptr, ptr ", q)), "str")
|
||||
}
|
||||
if streq(name, "os_exit") {
|
||||
let n = arg_code(e, 0)
|
||||
emit(" call void @exit(i32 ") emit(n) emit(")\n")
|
||||
emit(" unreachable\n")
|
||||
g_term = true
|
||||
return val("0", "void")
|
||||
}
|
||||
if streq(name, "file_stderr") { return val(emit_bind("load ptr, ptr @__stderrp"), "ptr") }
|
||||
if streq(name, "os_system") {
|
||||
let c = arg_code(e, 0)
|
||||
return val(emit_bind(sconcat("call i32 @system(ptr ", sconcat(c, ")"))), "int")
|
||||
}
|
||||
if streq(name, "os_getenv") {
|
||||
let n = arg_code(e, 0)
|
||||
return val(emit_bind(sconcat("call ptr @getenv(ptr ", sconcat(n, ")"))), "str")
|
||||
}
|
||||
if streq(name, "bnot") { let a = arg_code(e, 0) return val(emit_bind(sconcat("xor i32 ", sconcat(a, ", -1"))), "int") }
|
||||
if streq(name,"shl") or streq(name,"shr") or streq(name,"band") or streq(name,"bor") or streq(name,"bxor") {
|
||||
let a = arg_code(e, 0) let b = arg_code(e, 1)
|
||||
let opc = "shl"
|
||||
if streq(name,"shr") { opc = "lshr" }
|
||||
if streq(name,"band") { opc = "and" }
|
||||
if streq(name,"bor") { opc = "or" }
|
||||
if streq(name,"bxor") { opc = "xor" }
|
||||
return val(emit_bind(sconcat(opc, sconcat(" i32 ", sconcat(a, sconcat(", ", b))))), "int")
|
||||
}
|
||||
if streq(name, "peek32") {
|
||||
let p = arg_code(e, 0) let i = arg_code(e, 1)
|
||||
let g = nreg()
|
||||
emit(" ") emit(g) emit(" = getelementptr inbounds i32, ptr ") emit(p) emit(", i32 ") emit(i) emit("\n")
|
||||
return val(emit_bind(sconcat("load i32, ptr ", g)), "int")
|
||||
}
|
||||
if streq(name, "poke32") {
|
||||
let p = arg_code(e, 0) let i = arg_code(e, 1) let v = arg_code(e, 2)
|
||||
let g = nreg()
|
||||
emit(" ") emit(g) emit(" = getelementptr inbounds i32, ptr ") emit(p) emit(", i32 ") emit(i) emit("\n")
|
||||
emit(" store i32 ") emit(v) emit(", ptr ") emit(g) emit("\n")
|
||||
return val("0", "void")
|
||||
}
|
||||
g_intrin_ok = false
|
||||
return val("0", "void")
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue