Merge branch 'lang/memory-fence' into lang/leaks2

# Conflicts:
#	selfhost/ludicc.seed.ll
#	selfhost/ludicc.win.seed.ll
#	tools/ludic-cli/test.ludic
This commit is contained in:
Orkun ÇAKILKAYA 2026-09-28 15:46:11 +03:00
commit 84155274c4
32 changed files with 1425 additions and 111 deletions

View file

@ -0,0 +1,32 @@
# alloc_fence.ludic - the allocation fence (docs/plan 25.1) judges frames once play has settled.
# Everything a frame makes here is gone by the frame's end or goes on being reused: text only
# compared (freed once compared), records held from before play written in place. No frame
# keeps anything, so the run - which fails by default when one does - prints
# `frames 300 kept 0 bad 0`.
program AllocFence {
property Box { a: int = 0 }
function round(i: int, kept: []Box) -> int {
var bad = 0
if `{i}-{i * 2}` != string(i) + "-" + string(i * 2) { bad += 1 }
if `lake {i}` == "" { bad += 1 }
for k in 0 .. len(kept) { kept[k].a = i + k } # records reused: List.clear and new ones would keep the old
return bad
}
entry {
var kept = new []Box
for k in 0 .. 8 { push(kept, new Box) }
Mem.play()
Mem.settled()
var bad = 0
var frames = 0
for i in 0 .. 300 {
bad += round(i, kept)
Mem.frame()
frames += 1
}
let kept_now = Mem.kept() # read before the line below makes its text
print(`frames {frames} kept {kept_now} bad {Mem.bad_frames()}`)
}
}

View file

@ -0,0 +1,16 @@
# alloc_fence_auto.ludic - nothing here names the fence but the frame's mark: tracking starts at the
# first frame on its own, judging once R3D_ALLOC_WARM frames (600) in a row kept nothing, and frame
# 1500 keeping a record fails the run with exit 86. Prints nothing when it fails, `through` if not.
program AllocFenceAuto {
property Box { a: int = 0 }
entry {
var held = new []Box
for k in 0 .. 4 { push(held, new Box) }
for f in 0 .. 2000 {
if f < 300 { push(held, new Box { a: f }) } # loading: growing, not judged
if f == 1500 { push(held, new Box { a: f }) }
Mem.frame()
}
print("through")
}
}

View file

@ -0,0 +1,17 @@
# alloc_fence_leak.ludic - frame 50 keeps one record in a list that outlives it. The fence fails
# the run there (exit 86) and names this file's `new Box` line; under R3D_ALLOC_FENCE=count it
# runs on and prints `kept 80 bad 1` (the record and the list's growth, at malloc's own sizes, in one frame).
program AllocFenceLeak {
property Box { a: int = 0, b: int = 0 }
entry {
var held = new []Box
Mem.play()
Mem.settled()
for f in 0 .. 100 {
if f == 50 { push(held, new Box { a: f }) }
Mem.frame()
}
let kept_now = Mem.kept()
print(`kept {kept_now} bad {Mem.bad_frames()}`)
}
}

View file

@ -270,6 +270,10 @@ function emit_ns_call(ns: pointer, meth: pointer, e: Node) -> Val {
if is_log_ns(meth) { return emit_log_ns(meth, e) }
return emit_alias_or_fail("Log", meth, e)
}
if (ns == "Mem") {
if is_mem_fence_ns(meth) { return emit_mem_fence_ns(meth, e) }
return emit_alias_or_fail("Mem", meth, e)
}
if (ns == "Os") {
if is_os_ns(meth) { return emit_os_ns(meth, e) }
return emit_alias_or_fail("Os", meth, e)
@ -587,7 +591,7 @@ function emit_variant_new(en: Node, ord: int, args: []Node) -> Val {
let variant = en.kids[ord]
let arity = len(variant.kids)
if (len(args) != arity) { perr(`enum variant {variant.s} takes {itoa(arity)} payload(s), got {itoa(len(args))}`) }
let box = emit_bind(`call ptr @malloc(i64 {itoa(enum_box_size(en))})`)
let box = emit_bind(`call ptr @lp_malloc(i64 {itoa(enum_box_size(en))})`)
emit(" store i32 "); emit(itoa(ord)); emit(", ptr "); emit(box); emit("\n")
var k = 0
while k < arity {
@ -705,13 +709,13 @@ function emit_call(e: Node) -> Val {
if (llty(a.ty) == "ptr") { emit(" call i32 (ptr, ...) @printf(ptr @.fmt_line, ptr " + `{a.code})\n`) }
else { if (llty(a.ty) == "i64") { emit(" call i32 (ptr, ...) @printf(ptr @.fmt_long, i64 " + `{a.code})\n`) }
else { emit(" call i32 (ptr, ...) @printf(ptr @.fmt_int, i32 " + `{a.code})\n`) } }
if a.fresh { emit(` call void @free(ptr {a.code})\n`) } # a template printed is held by nothing
if a.fresh { emit(` call void @lp_free(ptr {a.code})\n`) } # a template printed is held by nothing
return val("0", "void")
}
if (name == "bytes") { # bytes(n): allocate n bytes -> a byte buffer
let n = emit_expr(e.kids[0])
let w = emit_bind(`zext i32 {n.code} to i64`)
return val(emit_bind(`call ptr @malloc(i64 {w})`), "pointer")
return val(emit_bind(`call ptr @lp_malloc(i64 {w})`), "pointer")
}
if (name == "words") { return emit_sized_slice("int", emit_expr(e.kids[0])) } # words(n): n zeroed ints
if (name == "buffer") and (find_fn(name) == null) { return emit_sized_slice("byte", emit_expr(e.kids[0])) } # buffer(n): n zeroed bytes (L7)
@ -833,7 +837,7 @@ function emit_call(e: Node) -> Val {
if (name == "ok") and (find_fn("ok") == null) {
g_uses_result = true
let v = emit_expr(e.kids[0])
let p = emit_bind("call ptr @malloc(i64 16)")
let p = emit_bind("call ptr @lp_malloc(i64 16)")
let okp = emit_bind(`getelementptr inbounds %Result, ptr {p}, i32 0, i32 0`)
emit(` store i32 1, ptr {okp}\n`)
let vp = emit_bind(`getelementptr inbounds %Result, ptr {p}, i32 0, i32 1`)
@ -845,7 +849,7 @@ function emit_call(e: Node) -> Val {
if (name == "err") and (find_fn("err") == null) {
g_uses_result = true
let m = emit_expr(e.kids[0])
let p = emit_bind("call ptr @malloc(i64 16)")
let p = emit_bind("call ptr @lp_malloc(i64 16)")
let okp = emit_bind(`getelementptr inbounds %Result, ptr {p}, i32 0, i32 0`)
emit(` store i32 0, ptr {okp}\n`)
let vp = emit_bind(`getelementptr inbounds %Result, ptr {p}, i32 0, i32 1`)
@ -877,7 +881,7 @@ function emit_call(e: Node) -> Val {
if (name == "some") and (find_fn("some") == null) {
g_uses_option = true
let v = emit_expr(e.kids[0])
let p = emit_bind("call ptr @malloc(i64 8)")
let p = emit_bind("call ptr @lp_malloc(i64 8)")
let pp = emit_bind(`getelementptr inbounds %Option, ptr {p}, i32 0, i32 0`)
emit(` store i32 1, ptr {pp}\n`)
let vp = emit_bind(`getelementptr inbounds %Option, ptr {p}, i32 0, i32 1`)
@ -886,7 +890,7 @@ function emit_call(e: Node) -> Val {
}
if (name == "none") and (find_fn("none") == null) {
g_uses_option = true
let p = emit_bind("call ptr @malloc(i64 8)")
let p = emit_bind("call ptr @lp_malloc(i64 8)")
let pp = emit_bind(`getelementptr inbounds %Option, ptr {p}, i32 0, i32 0`)
emit(` store i32 0, ptr {pp}\n`)
let vp = emit_bind(`getelementptr inbounds %Option, ptr {p}, i32 0, i32 1`)

View file

@ -14,6 +14,7 @@ function emit_params_sig(d: Node) -> void {
function emit_fn(d: Node) -> void {
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0
det_enter(d.s)
fence_enter(d.s)
g_cur_scene = null # a function belongs to no scene: `become` leaves the live one
ret_ty = d.ty
let fbody = buf_new()
@ -46,6 +47,7 @@ function emit_fn(d: Node) -> void {
}
function emit_main(d: Node) -> void {
fence_enter("entry")
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0
ret_ty = "int"
let fbody = buf_new()
@ -85,6 +87,7 @@ function emit_main(d: Node) -> void {
# one test block -> a void function @fn__test_<idx> (mirrors emit_fn's shape).
function emit_test_fn(t: Node, idx: int) -> void {
fence_enter(`test {t.s}`)
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0
ret_ty = "void"
let fbody = buf_new()
@ -295,6 +298,7 @@ function emit_program() -> void {
g_uses_world_despawn = false # #84: set when a world_despawn call is emitted (below)
g_cov_lines = new []int
g_cov_active = true
fence_reset()
loc_name = new []pointer; loc_reg = new []pointer; loc_ty = new []pointer; loc_mut = new []int
brk_lbl = new []pointer; cnt_lbl = new []pointer
self_stk = new []pointer
@ -356,8 +360,7 @@ function emit_program() -> void {
if g_uses_pak { emit_pak_prelude() } # @lp_pak_* asset packs + the pre-main mount ctor
if g_uses_datert { emit_datetime_prelude() } # @lp_days_from_civil / @lp_civil_from_days conversions
if g_uses_panic { # panic/assert: located abort to stderr
emith("declare i32 @fprintf(ptr, ptr, ...)\n")
g_fprintf_declared = true
if not g_fprintf_declared { emith("declare i32 @fprintf(ptr, ptr, ...)\n"); g_fprintf_declared = true }
emith("@.fmt_panic = private unnamed_addr constant [6 x i8] c\"%s%s\\0A\\00\"\n")
}
if g_uses_bounds { # a slice index out of range: located abort
@ -366,6 +369,7 @@ function emit_program() -> void {
}
if g_uses_result { emith("%Result = type { i32, i32, ptr }\n") } # issue #46: ok/err/try value
if g_uses_option { emith("%Option = type { i32, i32 }\n") } # issue #53: some/none value
emit_fence_runtime() # 25.1: the allocation fence and its site tables
emit_cov_runtime() # issue #45: --coverage tables + exit dump
if g_emit_module { emit_module_glue() } # issue #64: binary-module host-ABI declares + load-time registration ctor
}
@ -400,7 +404,7 @@ function emit_cov_runtime() -> void {
emith("@.cov_filefmt = private unnamed_addr constant [9 x i8] c\"FILE %s\\0A\\00\"\n")
emith("@.cov_rowfmt = private unnamed_addr constant [7 x i8] c\"%d %d\\0A\\00\"\n")
if not g_fprintf_declared { emith("declare i32 @fprintf(ptr, ptr, ...)\n"); g_fprintf_declared = true }
emith("declare i32 @atexit(ptr)\n")
if not g_atexit_declared { emith("declare i32 @atexit(ptr)\n"); g_atexit_declared = true }
# @cov_dump: open $LUDIC_COVERAGE (or "ludic.cov"), write a FILE header then one
# `<line> <hits>` row per instrumented line, and close.

View file

@ -136,14 +136,14 @@ function emit_str_op(op: pointer, a: Val, b: Val) -> Val {
let r = fresh_val(emit_bind(`call ptr @lp_str_concat(ptr {a.code}, ptr {b.code})`), "string")
# a half this expression made is copied now and held by nothing: `a + "/" + b` and every
# template's pieces once left one string behind per `+`
if a.fresh { emit(` call void @free(ptr {a.code})\n`) }
if b.fresh and b.code != a.code { emit(` call void @free(ptr {b.code})\n`) }
if a.fresh { emit(` call void @lp_free(ptr {a.code})\n`) }
if b.fresh and b.code != a.code { emit(` call void @lp_free(ptr {b.code})\n`) }
return r
}
let r = emit_bind(`call i32 @lp_str_eq(ptr {a.code}, ptr {b.code})`)
# a side made just to be compared is held by nothing once it has been: `` `{x}` == "..." ``
if a.fresh { emit(` call void @free(ptr {a.code})\n`) }
if b.fresh and b.code != a.code { emit(` call void @free(ptr {b.code})\n`) }
if a.fresh { emit(` call void @lp_free(ptr {a.code})\n`) }
if b.fresh and b.code != a.code { emit(` call void @lp_free(ptr {b.code})\n`) }
if (op == ("!=")) {
let c = emit_bind(`icmp eq i32 {r}, 0`)
return val(emit_bind(`zext i1 {c} to i32`), "bool")

View file

@ -0,0 +1,203 @@
# emit_fence.ludic — the allocation fence (docs/plan 25.1). Every allocation the emitter writes
# goes through @lp_malloc / @lp_calloc / @lp_realloc / @lp_free, and before a Ludic-level one it
# stores the site's number in @lp_site. The runtime (emit_fence_ir.ludic) counts nothing until
# Mem.play(); then it tracks every block by site in a side table and, once play has settled,
# fails a frame that ends holding more than it began with (R3D_ALLOC_FENCE=off|count|warn|fail).
#
# A site is (function, file, line, kind). Site 0 is "unknown": an allocation inside a runtime
# helper the emitter reached without a site of its own inherits the last one set.
var g_site_fn: []pointer = new []pointer # per site: the function name's constant
var g_site_file: []pointer = new []pointer # the file name's constant
var g_site_line: []int = new []int
var g_site_kind: []pointer = new []pointer # the kind's constant
var g_cur_fn_name: pointer = "?" # the function being emitted
var g_site_fnc: pointer = null # its name's constant, made at its first site
var g_site_filen: pointer = null # the last file a site named, and its constant
var g_site_filec: pointer = null
var g_site_kinds: []pointer = new []pointer # kinds seen, and their constants
var g_site_kindc: []pointer = new []pointer
var g_site_next: pointer = null # a kind the next allocation takes (`new Foo`)
var g_atexit_declared: bool = false # @atexit is declared once: by the fence or by --coverage
var g_mzs_declared: bool = false # @malloc_zone_statistics: by the fence or by Os.heap_bytes
# the build's defaults (--fence=, --fence-warm=, --fence-census=, else a `fence` line in the nearest
# package.ludic); the environment overrides each at run time (R3D_ALLOC_FENCE, _WARM, _CENSUS)
var g_fence_mode: int = 0 # 0: headless fails, windowed fails only under R3D_DEV
var g_fence_warm: int = 600
var g_fence_census: pointer = null
var g_fence_flagged: bool = false # a flag said so: the manifest does not override it
function fence_reset() -> void {
g_site_fn = new []pointer; g_site_file = new []pointer; g_site_line = new []int; g_site_kind = new []pointer
g_site_kinds = new []pointer; g_site_kindc = new []pointer
g_atexit_declared = false
g_mzs_declared = false
g_cur_fn_name = "?"; g_site_fnc = null; g_site_filen = null; g_site_filec = null; g_site_next = null
push(g_site_fn, null); push(g_site_file, null); push(g_site_line, 0); push(g_site_kind, null) # site 0, named at the end
}
# the function the next sites belong to (emit_fn, emit_system_fn)
function fence_enter(name: pointer) -> void {
g_cur_fn_name = name
g_site_fnc = null
}
function fence_kind_const(kind: pointer) -> pointer {
var i = 0
while i < len(g_site_kinds) { if (g_site_kinds[i] == kind) { return g_site_kindc[i] }; i += 1 }
push(g_site_kinds, kind)
push(g_site_kindc, emit_str_const(kind))
return g_site_kindc[len(g_site_kindc) - 1]
}
# a new site for the allocation about to be emitted, stored into @lp_site
function emit_site(kind: pointer) -> void {
var k = kind
if g_site_next != null { k = g_site_next; g_site_next = null }
if g_site_fnc == null { g_site_fnc = emit_str_const(g_cur_fn_name) }
var file = g_err_file
if file == null { file = "?" }
if (g_site_filen == null) or not (g_site_filen == file) {
g_site_filen = file
g_site_filec = emit_str_const(file)
}
let id = len(g_site_line)
push(g_site_fn, g_site_fnc)
push(g_site_file, g_site_filec)
push(g_site_line, g_err_line)
push(g_site_kind, fence_kind_const(k))
emit(` store i32 {itoa(id)}, ptr @lp_site\n`)
}
# emit_bind's hook: a call that allocates gets a site first, its kind read off the callee
function fence_bind(rest: pointer) -> void {
if not str_starts(rest, "call ptr @lp_") { return }
if str_starts(rest, "call ptr @lp_malloc(") { emit_site("alloc"); return }
if str_starts(rest, "call ptr @lp_calloc(") { emit_site("slice data"); return }
if str_starts(rest, "call ptr @lp_realloc(") { emit_site("grow"); return }
if str_starts(rest, "call ptr @lp_str_concat(") { emit_site("concat"); return }
if str_starts(rest, "call ptr @lp_int_str(") or str_starts(rest, "call ptr @lp_long_str(") { emit_site("string(n)"); return }
if str_starts(rest, "call ptr @lp_fp_str(") { emit_site("string(x)"); return }
if str_starts(rest, "call ptr @lp_str_slice(") { emit_site("substring"); return }
if str_starts(rest, "call ptr @lp_text_") { emit_site("Text"); return }
}
function fence_int(s: pointer) -> int {
var n = 0
var neg = false
var i = 0
if (len(s) > 0) and (s[0] == '-') { neg = true; i = 1 }
while (i < len(s)) and char_is_digit(s[i]) { n = n * 10 + (s[i] - '0'); i += 1 }
if neg { return 0 - n }
return n
}
function fence_mode_of(s: pointer) -> int {
if (s == "off") { return 1 }
if (s == "count") { return 2 }
if (s == "warn") { return 3 }
if (s == "fail") { return 4 }
perr(`the fence is off, count, warn or fail, not {s}`)
return 0
}
# ludicc's --fence flags; true when `a` was one of them
function fence_flag(a: pointer) -> bool {
if str_starts(a, "--fence=") { g_fence_mode = fence_mode_of(a[8..len(a)]); g_fence_flagged = true; return true }
if str_starts(a, "--fence-warm=") { g_fence_warm = fence_int(a[13..len(a)]); g_fence_flagged = true; return true }
if str_starts(a, "--fence-census=") { g_fence_census = a[15..len(a)]; g_fence_flagged = true; return true }
return false
}
# `fence fail`, `fence warm 900`, `fence census "build/fence.txt"` in the package.ludic nearest the
# program, looked for up to four directories above it
function fence_manifest(entry: pointer) -> void {
if g_fence_flagged { return }
var dir = dir_of(entry)
var up = 0
while up < 5 {
var root = dir
if len(root) > 0 { root = root + "/" }
let txt = read_file(root + "package.ludic")
if txt != null { fence_manifest_read(txt); return }
if len(dir) == 0 { dir = ".." } else { dir = dir + "/.." }
up += 1
}
}
function fence_manifest_read(txt: pointer) -> void {
var i = 0
let tn = len(txt)
while i < tn {
var e = i
while (e < tn) and (txt[e] != '\n') { e += 1 }
var le = e
if (le > i) and (txt[le - 1] == 13) { le -= 1 }
let ws = native_words(txt[i..le])
if (len(ws) == 2) and (ws[0] == "fence") { g_fence_mode = fence_mode_of(ws[1]) }
if (len(ws) == 3) and (ws[0] == "fence") and (ws[1] == "warm") { g_fence_warm = fence_int(ws[2]) }
if (len(ws) == 3) and (ws[0] == "fence") and (ws[1] == "census") { g_fence_census = ws[2] }
i = e + 1
}
}
# Mem.play() / Mem.rewarm() / Mem.settled() / Mem.frame() / Mem.kept() / Mem.bad_frames()
function is_mem_fence_ns(meth: pointer) -> bool {
return (meth == "play") or (meth == "rewarm") or (meth == "settled") or (meth == "frame") or (meth == "kept") or (meth == "bad_frames")
}
function emit_mem_fence_ns(meth: pointer, e: Node) -> Val {
if (meth == "kept") { return val(emit_bind("call i64 @lp_mem_kept()"), "long") }
if (meth == "bad_frames") { return val(emit_bind("call i64 @lp_mem_bad_frames()"), "long") }
if (meth == "rewarm") { emit(" call void @lp_mem_play()\n"); return val("0", "void") }
emit(` call void @lp_mem_{meth}()\n`)
return val("0", "void")
}
function fence_table(name: pointer, ty: pointer, vals: []pointer) -> void {
emith(`{name} = global [{itoa(len(vals))} x {ty}] [`)
var i = 0
while i < len(vals) {
if i > 0 { emith(", ") }
emith(ty); emith(" "); emith(vals[i])
i += 1
}
emith("]\n")
}
# the runtime, and the tables this program's sites size
function emit_fence_runtime() -> void {
if g_target_win { emit_fence_win(); return }
if not g_fprintf_declared { emith("declare i32 @fprintf(ptr, ptr, ...)\n"); g_fprintf_declared = true }
if not g_atexit_declared { emith("declare i32 @atexit(ptr)\n"); g_atexit_declared = true }
if not g_mzs_declared { emith("declare extern_weak void @malloc_zone_statistics(ptr, ptr)\n"); g_mzs_declared = true }
emit_fence_globals()
emit_fence_entry()
emit_fence_table()
emit_fence_control()
emit_fence_report()
emit_fence_frame()
let unknown = emit_str_const("?")
g_site_fn[0] = unknown; g_site_file[0] = unknown; g_site_kind[0] = unknown
let n = len(g_site_line)
let sn = itoa(n)
var dflt = itoa(g_fence_mode)
if (g_fence_mode == 0) and not g_windowed { dflt = "4" }
emith(`@lp_fdef = global i8 {dflt}\n`)
emith(`@lp_warm = global i64 {itoa(g_fence_warm)}\n`)
if g_fence_census == null { emith("@lp_census_def = global ptr null\n") }
else { emith(`@lp_census_def = global ptr {emit_str_const(g_fence_census)}\n`) }
emith(`@lp_site_n = global i32 {sn}\n`)
fence_table("@lp_site_fn", "ptr", g_site_fn)
fence_table("@lp_site_file", "ptr", g_site_file)
fence_table("@lp_site_kind", "ptr", g_site_kind)
let lines = new []pointer
var i = 0
while i < n { push(lines, itoa(g_site_line[i])); i += 1 }
fence_table("@lp_site_line", "i32", lines)
emith(`@lp_site_live = global [{sn} x i64] zeroinitializer\n`)
emith(`@lp_site_mark = global [{sn} x i64] zeroinitializer\n`)
emith(`@lp_site_stamp = global [{sn} x i64] zeroinitializer\n`)
emith(`@lp_site_base = global [{sn} x i64] zeroinitializer\n`)
emith(`@lp_site_ra = global [{itoa(n * 6)} x ptr] zeroinitializer\n`)
}

View file

@ -0,0 +1,995 @@
# emit_fence_ir.ludic - the allocation fence's runtime (25.1), as IR. Generated from a
# template (docs/plan 25); the tables sized by the program are emit_fence.ludic's.
function emit_fence_globals() -> void {
emith("@lp_site = global i32 0\n")
emith("@lp_ft = global i8 0\n")
emith("@lp_fm = global i8 0\n")
emith("@lp_fj = global i8 0\n")
emith("@lp_fl = global i32 0\n")
emith("@lp_tab = global ptr null\n")
emith("@lp_tabmask = global i64 0\n")
emith("@lp_tabn = global i64 0\n")
emith("@lp_tabmax = global i64 0\n")
emith("@lp_live = global i64 0\n")
emith("@lp_pre = global i64 0\n")
emith("@lp_made = global i64 0\n")
emith("@lp_freed = global i64 0\n")
emith("@lp_over = global i64 0\n")
emith("@lp_overrep = global i8 0\n")
emith("@lp_frame = global i64 0\n")
emith("@lp_flat = global i64 0\n")
emith("@lp_playf = global i64 0\n")
emith("@lp_warm_max = global i64 -1\n")
emith("@lp_fauto = global i8 1\n")
emith("@lp_autoplay = global i64 -1\n")
emith("@lp_mlive = global i64 0\n")
emith("@lp_mpre = global i64 0\n")
emith("@lp_mmade = global i64 0\n")
emith("@lp_mfreed = global i64 0\n")
emith("@lp_slive = global i64 0\n")
emith("@lp_spre = global i64 0\n")
emith("@lp_touch = global [512 x i32] zeroinitializer\n")
emith("@lp_ntouch = global i32 0\n")
emith("@lp_fkept = global i64 0\n")
emith("@lp_fbad = global i64 0\n")
emith("@lp_fjudged = global i64 0\n")
emith("@lp_zset = global i64 0\n")
emith("@lp_census = global ptr null\n")
emith("declare extern_weak i64 @malloc_size(ptr)\n")
emith("declare i32 @dprintf(i32, ptr, ...)\n")
emith("declare i32 @_Unwind_Backtrace(ptr, ptr)\n")
emith("declare i64 @_Unwind_GetIP(ptr)\n")
emith("declare i32 @dladdr(ptr, ptr)\n")
emith("@.lpf_env = private unnamed_addr constant [16 x i8] c\"R3D_ALLOC_FENCE\\00\"\n")
emith("@.lpf_warm = private unnamed_addr constant [15 x i8] c\"R3D_ALLOC_WARM\\00\"\n")
emith("@.lpf_play = private unnamed_addr constant [15 x i8] c\"R3D_ALLOC_PLAY\\00\"\n")
emith("@.lpf_tab = private unnamed_addr constant [16 x i8] c\"R3D_ALLOC_TABLE\\00\"\n")
emith("@.lpf_dev = private unnamed_addr constant [8 x i8] c\"R3D_DEV\\00\"\n")
emith("@.lpf_off = private unnamed_addr constant [4 x i8] c\"off\\00\"\n")
emith("@.lpf_count = private unnamed_addr constant [6 x i8] c\"count\\00\"\n")
emith("@.lpf_warn = private unnamed_addr constant [5 x i8] c\"warn\\00\"\n")
emith("@.lpf_fail = private unnamed_addr constant [5 x i8] c\"fail\\00\"\n")
emith("@.lpf_hdr = private unnamed_addr constant [62 x i8] c\"alloc-fence: frame %lld kept %+lld B (%lld made, %lld freed)\\0A\\00\"\n")
emith("@.lpf_row = private unnamed_addr constant [26 x i8] c\" %+lld B %s %s:%d %s\\0A\\00\"\n")
emith("@.lpf_more = private unnamed_addr constant [21 x i8] c\" ... and %lld more\\0A\\00\"\n")
emith("@.lpf_full = private unnamed_addr constant [99 x i8] c\"alloc-fence: the table is full (%lld blocks) - raise R3D_ALLOC_TABLE; from here it judges nothing\\0A\\00\"\n")
emith("@.lpf_failing = private unnamed_addr constant [53 x i8] c\"alloc-fence: failing the run (R3D_ALLOC_FENCE=fail)\\0A\\00\"\n")
emith("@.lpf_census = private unnamed_addr constant [17 x i8] c\"R3D_ALLOC_CENSUS\\00\"\n")
emith("@.lpf_warmmax = private unnamed_addr constant [19 x i8] c\"R3D_ALLOC_WARM_MAX\\00\"\n")
emith("@.lpf_auto = private unnamed_addr constant [15 x i8] c\"R3D_ALLOC_AUTO\\00\"\n")
emith("@.lpf_w = private unnamed_addr constant [2 x i8] c\"w\\00\"\n")
emith("@.lpf_chdr = private unnamed_addr constant [76 x i8] c\"alloc-census frames %lld bad %lld kept %lld made %lld freed %lld over %lld\\0A\\00\"\n")
emith("@.lpf_cnat = private unnamed_addr constant [91 x i8] c\"native %lld (the heap outside Ludic's blocks, since judging began: libraries and drivers)\\0A\\00\"\n")
emith("@.lpf_crow = private unnamed_addr constant [23 x i8] c\"site %lld %s %s:%d %s\\0A\\00\"\n")
emith("@.lpf_from = private unnamed_addr constant [15 x i8] c\" from %s\\0A\\00\"\n")
emith("@.lpf_cfrom = private unnamed_addr constant [11 x i8] c\" from %s\\0A\\00\"\n")
emith("@.lpf_lp = private unnamed_addr constant [4 x i8] c\"lp_\\00\"\n")
}
function emit_fence_entry() -> void {
emith("define ptr @lp_malloc(i64 %n) {\n")
emith("entry:\n")
emith(" %t = load i8, ptr @lp_ft\n")
emith(" %on = icmp ne i8 %t, 0\n")
emith(" br i1 %on, label %slow, label %fast\n")
emith("fast:\n")
emith(" %p = tail call ptr @malloc(i64 %n)\n")
emith(" ret ptr %p\n")
emith("slow:\n")
emith(" %q = call ptr @malloc(i64 %n)\n")
emith(" call void @lp_fadd(ptr %q, i64 %n)\n")
emith(" ret ptr %q\n")
emith("}\n")
emith("define ptr @lp_calloc(i64 %n, i64 %s) {\n")
emith("entry:\n")
emith(" %t = load i8, ptr @lp_ft\n")
emith(" %on = icmp ne i8 %t, 0\n")
emith(" br i1 %on, label %slow, label %fast\n")
emith("fast:\n")
emith(" %p = tail call ptr @calloc(i64 %n, i64 %s)\n")
emith(" ret ptr %p\n")
emith("slow:\n")
emith(" %q = call ptr @calloc(i64 %n, i64 %s)\n")
emith(" %b = mul i64 %n, %s\n")
emith(" call void @lp_fadd(ptr %q, i64 %b)\n")
emith(" ret ptr %q\n")
emith("}\n")
emith("define ptr @lp_realloc(ptr %o, i64 %n) {\n")
emith("entry:\n")
emith(" %t = load i8, ptr @lp_ft\n")
emith(" %on = icmp ne i8 %t, 0\n")
emith(" br i1 %on, label %slow, label %fast\n")
emith("fast:\n")
emith(" %p = tail call ptr @realloc(ptr %o, i64 %n)\n")
emith(" ret ptr %p\n")
emith("slow:\n")
emith(" call void @lp_fdel(ptr %o)\n")
emith(" %q = call ptr @realloc(ptr %o, i64 %n)\n")
emith(" call void @lp_fadd(ptr %q, i64 %n)\n")
emith(" ret ptr %q\n")
emith("}\n")
emith("define void @lp_free(ptr %o) {\n")
emith("entry:\n")
emith(" %t = load i8, ptr @lp_ft\n")
emith(" %on = icmp ne i8 %t, 0\n")
emith(" br i1 %on, label %slow, label %fast\n")
emith("fast:\n")
emith(" tail call void @free(ptr %o)\n")
emith(" ret void\n")
emith("slow:\n")
emith(" call void @lp_fdel(ptr %o)\n")
emith(" call void @free(ptr %o)\n")
emith(" ret void\n")
emith("}\n")
}
function emit_fence_table() -> void {
emith("define internal void @lp_flock() {\n")
emith("entry:\n")
emith(" br label %spin\n")
emith("spin:\n")
emith(" %o = atomicrmw xchg ptr @lp_fl, i32 1 acquire\n")
emith(" %b = icmp eq i32 %o, 0\n")
emith(" br i1 %b, label %done, label %spin\n")
emith("done:\n")
emith(" ret void\n")
emith("}\n")
emith("define internal void @lp_funlock() {\n")
emith("entry:\n")
emith(" store atomic i32 0, ptr @lp_fl release, align 4\n")
emith(" ret void\n")
emith("}\n")
emith("define internal i64 @lp_fhash(i64 %k) {\n")
emith("entry:\n")
emith(" %a = lshr i64 %k, 4\n")
emith(" %b = mul i64 %a, -7046029254386353131\n")
emith(" %c = lshr i64 %b, 29\n")
emith(" %d = xor i64 %b, %c\n")
emith(" ret i64 %d\n")
emith("}\n")
emith("define internal void @lp_ftouch(i64 %s) {\n")
emith("entry:\n")
emith(" %sp = getelementptr inbounds i64, ptr @lp_site_stamp, i64 %s\n")
emith(" %st = load i64, ptr %sp\n")
emith(" %f = load i64, ptr @lp_frame\n")
emith(" %f1 = add i64 %f, 1\n")
emith(" %seen = icmp eq i64 %st, %f1\n")
emith(" br i1 %seen, label %out, label %mark\n")
emith("mark:\n")
emith(" store i64 %f1, ptr %sp\n")
emith(" %n = load i32, ptr @lp_ntouch\n")
emith(" %room = icmp ult i32 %n, 512\n")
emith(" %n1 = add i32 %n, 1\n")
emith(" store i32 %n1, ptr @lp_ntouch\n")
emith(" br i1 %room, label %put, label %out\n")
emith("put:\n")
emith(" %tp = getelementptr inbounds [512 x i32], ptr @lp_touch, i32 0, i32 %n\n")
emith(" %s32 = trunc i64 %s to i32\n")
emith(" store i32 %s32, ptr %tp\n")
emith(" br label %out\n")
emith("out:\n")
emith(" ret void\n")
emith("}\n")
emith("define internal void @lp_fsite_add(i64 %s, i64 %n) {\n")
emith("entry:\n")
emith(" %sp = getelementptr inbounds i64, ptr @lp_site_live, i64 %s\n")
emith(" %v = load i64, ptr %sp\n")
emith(" %v1 = add i64 %v, %n\n")
emith(" store i64 %v1, ptr %sp\n")
emith(" %lv = load i64, ptr @lp_live\n")
emith(" %lv1 = add i64 %lv, %n\n")
emith(" store i64 %lv1, ptr @lp_live\n")
emith(" call void @lp_ftouch(i64 %s)\n")
emith(" ret void\n")
emith("}\n")
emith("; the last six return addresses above an allocation of site s, from the unwinder (the emitted\n")
emith("; functions keep no frame-pointer chain), taken only once judging has begun: what is made then is\n")
emith("; what a report names, and it is rare\n")
emith("define internal i32 @lp_funw(ptr %ctx, ptr %st) {\n")
emith("entry:\n")
emith(" %n = load i64, ptr %st\n")
emith(" %full = icmp uge i64 %n, 6\n")
emith(" br i1 %full, label %stop, label %take\n")
emith("take:\n")
emith(" %ip = call i64 @_Unwind_GetIP(ptr %ctx)\n")
emith(" %bp = getelementptr inbounds i64, ptr %st, i64 1\n")
emith(" %base = load i64, ptr %bp\n")
emith(" %slot = add i64 %base, %n\n")
emith(" %dst = getelementptr inbounds i64, ptr @lp_site_ra, i64 %slot\n")
emith(" store i64 %ip, ptr %dst\n")
emith(" %n1 = add i64 %n, 1\n")
emith(" store i64 %n1, ptr %st\n")
emith(" ret i32 0\n")
emith("stop:\n")
emith(" ret i32 5\n")
emith("}\n")
emith("define internal void @lp_fcallers(i64 %s) noinline {\n")
emith("entry:\n")
emith(" %j = load i8, ptr @lp_fj\n")
emith(" %judging = icmp ne i8 %j, 0\n")
emith(" br i1 %judging, label %walk, label %out\n")
emith("walk:\n")
emith(" %st = alloca [2 x i64], align 8\n")
emith(" store i64 0, ptr %st\n")
emith(" %bp = getelementptr inbounds i64, ptr %st, i64 1\n")
emith(" %base = mul i64 %s, 6\n")
emith(" store i64 %base, ptr %bp\n")
emith(" %z = getelementptr inbounds i64, ptr @lp_site_ra, i64 %base\n")
emith(" call ptr @memset(ptr %z, i32 0, i64 48)\n")
emith(" call i32 @_Unwind_Backtrace(ptr @lp_funw, ptr %st)\n")
emith(" br label %out\n")
emith("out:\n")
emith(" ret void\n")
emith("}\n")
emith("; the Ludic functions a site was last reached from: its callers' names, those of the fence and of\n")
emith("; the site's own function left out\n")
emith("define internal void @lp_fsite_from(ptr %fp, i32 %s, ptr %own) {\n")
emith("entry:\n")
emith(" %info = alloca [4 x ptr], align 8\n")
emith(" %s64 = sext i32 %s to i64\n")
emith(" %base = mul i64 %s64, 6\n")
emith(" br label %loop\n")
emith("loop:\n")
emith(" %i = phi i64 [ 0, %entry ], [ %i1, %next ]\n")
emith(" %shown = phi i32 [ 0, %entry ], [ %shown1, %next ]\n")
emith(" %more = icmp ult i64 %i, 6\n")
emith(" %room = icmp ult i32 %shown, 3\n")
emith(" %go = and i1 %more, %room\n")
emith(" br i1 %go, label %body, label %out\n")
emith("body:\n")
emith(" %slot = add i64 %base, %i\n")
emith(" %rp = getelementptr inbounds ptr, ptr @lp_site_ra, i64 %slot\n")
emith(" %ra0 = load ptr, ptr %rp\n")
emith(" %ra = getelementptr i8, ptr %ra0, i64 -1\n")
emith(" %nul = icmp eq ptr %ra0, null\n")
emith(" br i1 %nul, label %skip, label %look\n")
emith("look:\n")
emith(" %ok = call i32 @dladdr(ptr %ra, ptr %info)\n")
emith(" %okq = icmp ne i32 %ok, 0\n")
emith(" br i1 %okq, label %named, label %skip\n")
emith("named:\n")
emith(" %snp = getelementptr inbounds [4 x ptr], ptr %info, i32 0, i32 2\n")
emith(" %sn = load ptr, ptr %snp\n")
emith(" %snnul = icmp eq ptr %sn, null\n")
emith(" br i1 %snnul, label %skip, label %filter\n")
emith("filter:\n")
emith(" %c1 = call i32 @strncmp(ptr %sn, ptr @.lpf_lp, i64 3)\n")
emith(" %islp = icmp eq i32 %c1, 0\n")
emith(" %own3 = getelementptr inbounds i8, ptr %sn, i64 3\n")
emith(" %c2 = call i32 @strcmp(ptr %own3, ptr %own)\n")
emith(" %isown = icmp eq i32 %c2, 0\n")
emith(" %drop = or i1 %islp, %isown\n")
emith(" br i1 %drop, label %skip, label %print\n")
emith("print:\n")
emith(" %tofile = icmp ne ptr %fp, null\n")
emith(" br i1 %tofile, label %pf, label %pe\n")
emith("pf:\n")
emith(" call i32 (ptr, ptr, ...) @fprintf(ptr %fp, ptr @.lpf_cfrom, ptr %sn)\n")
emith(" br label %counted\n")
emith("pe:\n")
emith(" call i32 (i32, ptr, ...) @dprintf(i32 2, ptr @.lpf_from, ptr %sn)\n")
emith(" br label %counted\n")
emith("counted:\n")
emith(" br label %next\n")
emith("skip:\n")
emith(" br label %next\n")
emith("next:\n")
emith(" %add = phi i32 [ 1, %counted ], [ 0, %skip ]\n")
emith(" %shown1 = add i32 %shown, %add\n")
emith(" %i1 = add i64 %i, 1\n")
emith(" br label %loop\n")
emith("out:\n")
emith(" ret void\n")
emith("}\n")
emith("define internal void @lp_fadd(ptr %p, i64 %asked) {\n")
emith("entry:\n")
emith(" %isnull = icmp eq ptr %p, null\n")
emith(" br i1 %isnull, label %out, label %size\n")
emith("size:\n")
emith(" %hasms = icmp ne ptr @malloc_size, null\n")
emith(" br i1 %hasms, label %real, label %go\n")
emith("real:\n")
emith(" %ms = call i64 @malloc_size(ptr %p)\n")
emith(" br label %go\n")
emith("go:\n")
emith(" %n = phi i64 [ %asked, %size ], [ %ms, %real ]\n")
emith(" call void @lp_flock()\n")
emith(" %cnt = load i64, ptr @lp_tabn\n")
emith(" %max = load i64, ptr @lp_tabmax\n")
emith(" %full = icmp sge i64 %cnt, %max\n")
emith(" br i1 %full, label %over, label %ins\n")
emith("over:\n")
emith(" %ov = load i64, ptr @lp_over\n")
emith(" %ov1 = add i64 %ov, 1\n")
emith(" store i64 %ov1, ptr @lp_over\n")
emith(" br label %unlock\n")
emith("ins:\n")
emith(" %s0 = load i32, ptr @lp_site\n")
emith(" %sn = load i32, ptr @lp_site_n\n")
emith(" %sok = icmp ult i32 %s0, %sn\n")
emith(" %s = select i1 %sok, i32 %s0, i32 0\n")
emith(" %s64 = zext i32 %s to i64\n")
emith(" %k = ptrtoint ptr %p to i64\n")
emith(" %h = call i64 @lp_fhash(i64 %k)\n")
emith(" %mask = load i64, ptr @lp_tabmask\n")
emith(" %tab = load ptr, ptr @lp_tab\n")
emith(" br label %probe\n")
emith("probe:\n")
emith(" %i = phi i64 [ %h, %ins ], [ %i1, %next ]\n")
emith(" %im = and i64 %i, %mask\n")
emith(" %e = getelementptr inbounds [2 x i64], ptr %tab, i64 %im\n")
emith(" %ek = load i64, ptr %e\n")
emith(" %empty = icmp eq i64 %ek, 0\n")
emith(" br i1 %empty, label %put, label %chk\n")
emith("chk:\n")
emith(" %same = icmp eq i64 %ek, %k\n")
emith(" br i1 %same, label %dup, label %next\n")
emith("next:\n")
emith(" %i1 = add i64 %im, 1\n")
emith(" br label %probe\n")
emith("dup:\n")
emith(" %dv = getelementptr inbounds i64, ptr %e, i64 1\n")
emith(" %dold = load i64, ptr %dv\n")
emith(" %dn = and i64 %dold, 1099511627775\n")
emith(" %ds = lshr i64 %dold, 40\n")
emith(" %dneg = sub i64 0, %dn\n")
emith(" call void @lp_fsite_add(i64 %ds, i64 %dneg)\n")
emith(" %dtn = load i64, ptr @lp_tabn\n")
emith(" %dtn1 = sub i64 %dtn, 1\n")
emith(" store i64 %dtn1, ptr @lp_tabn\n")
emith(" %dfr = load i64, ptr @lp_freed\n")
emith(" %dfr1 = add i64 %dfr, 1\n")
emith(" store i64 %dfr1, ptr @lp_freed\n")
emith(" br label %put\n")
emith("put:\n")
emith(" store i64 %k, ptr %e\n")
emith(" %ev = getelementptr inbounds i64, ptr %e, i64 1\n")
emith(" %sh = shl i64 %s64, 40\n")
emith(" %v = or i64 %sh, %n\n")
emith(" store i64 %v, ptr %ev\n")
emith(" %tn = load i64, ptr @lp_tabn\n")
emith(" %tn1 = add i64 %tn, 1\n")
emith(" store i64 %tn1, ptr @lp_tabn\n")
emith(" %md = load i64, ptr @lp_made\n")
emith(" %md1 = add i64 %md, 1\n")
emith(" store i64 %md1, ptr @lp_made\n")
emith(" call void @lp_fsite_add(i64 %s64, i64 %n)\n")
emith(" call void @lp_fcallers(i64 %s64)\n")
emith(" br label %unlock\n")
emith("unlock:\n")
emith(" call void @lp_funlock()\n")
emith(" br label %out\n")
emith("out:\n")
emith(" ret void\n")
emith("}\n")
emith("define internal void @lp_fdel(ptr %p) {\n")
emith("entry:\n")
emith(" %isnull = icmp eq ptr %p, null\n")
emith(" br i1 %isnull, label %out, label %go\n")
emith("go:\n")
emith(" call void @lp_flock()\n")
emith(" %k = ptrtoint ptr %p to i64\n")
emith(" %h = call i64 @lp_fhash(i64 %k)\n")
emith(" %mask = load i64, ptr @lp_tabmask\n")
emith(" %tab = load ptr, ptr @lp_tab\n")
emith(" br label %probe\n")
emith("probe:\n")
emith(" %i = phi i64 [ %h, %go ], [ %i1, %next ]\n")
emith(" %im = and i64 %i, %mask\n")
emith(" %e = getelementptr inbounds [2 x i64], ptr %tab, i64 %im\n")
emith(" %ek = load i64, ptr %e\n")
emith(" %empty = icmp eq i64 %ek, 0\n")
emith(" br i1 %empty, label %miss, label %cmp\n")
emith("cmp:\n")
emith(" %hit = icmp eq i64 %ek, %k\n")
emith(" br i1 %hit, label %found, label %next\n")
emith("next:\n")
emith(" %i1 = add i64 %im, 1\n")
emith(" br label %probe\n")
emith("miss:\n")
emith(" %has = icmp ne ptr @malloc_size, null\n")
emith(" br i1 %has, label %msz, label %unlock\n")
emith("msz:\n")
emith(" %ms = call i64 @malloc_size(ptr %p)\n")
emith(" %pr = load i64, ptr @lp_pre\n")
emith(" %pr1 = add i64 %pr, %ms\n")
emith(" store i64 %pr1, ptr @lp_pre\n")
emith(" br label %unlock\n")
emith("found:\n")
emith(" %ev = getelementptr inbounds i64, ptr %e, i64 1\n")
emith(" %v = load i64, ptr %ev\n")
emith(" %n = and i64 %v, 1099511627775\n")
emith(" %s = lshr i64 %v, 40\n")
emith(" %neg = sub i64 0, %n\n")
emith(" call void @lp_fsite_add(i64 %s, i64 %neg)\n")
emith(" %fr = load i64, ptr @lp_freed\n")
emith(" %fr1 = add i64 %fr, 1\n")
emith(" store i64 %fr1, ptr @lp_freed\n")
emith(" %tn = load i64, ptr @lp_tabn\n")
emith(" %tn1 = sub i64 %tn, 1\n")
emith(" store i64 %tn1, ptr @lp_tabn\n")
emith(" br label %sh\n")
emith("sh:\n")
emith(" %hole = phi i64 [ %im, %found ], [ %hole, %sh_skip ], [ %j, %sh_move ]\n")
emith(" %jp = phi i64 [ %im, %found ], [ %j, %sh_skip ], [ %j, %sh_move ]\n")
emith(" %j0 = add i64 %jp, 1\n")
emith(" %j = and i64 %j0, %mask\n")
emith(" %ej = getelementptr inbounds [2 x i64], ptr %tab, i64 %j\n")
emith(" %kj = load i64, ptr %ej\n")
emith(" %endq = icmp eq i64 %kj, 0\n")
emith(" br i1 %endq, label %sh_end, label %sh_test\n")
emith("sh_test:\n")
emith(" %hj = call i64 @lp_fhash(i64 %kj)\n")
emith(" %home = and i64 %hj, %mask\n")
emith(" %jgt = icmp ugt i64 %j, %hole\n")
emith(" %a1 = icmp ugt i64 %home, %hole\n")
emith(" %a2 = icmp ule i64 %home, %j\n")
emith(" %in1 = and i1 %a1, %a2\n")
emith(" %in2 = or i1 %a1, %a2\n")
emith(" %in = select i1 %jgt, i1 %in1, i1 %in2\n")
emith(" br i1 %in, label %sh_skip, label %sh_move\n")
emith("sh_skip:\n")
emith(" br label %sh\n")
emith("sh_move:\n")
emith(" %eh = getelementptr inbounds [2 x i64], ptr %tab, i64 %hole\n")
emith(" store i64 %kj, ptr %eh\n")
emith(" %ejv = getelementptr inbounds i64, ptr %ej, i64 1\n")
emith(" %vj = load i64, ptr %ejv\n")
emith(" %ehv = getelementptr inbounds i64, ptr %eh, i64 1\n")
emith(" store i64 %vj, ptr %ehv\n")
emith(" br label %sh\n")
emith("sh_end:\n")
emith(" %ehe = getelementptr inbounds [2 x i64], ptr %tab, i64 %hole\n")
emith(" store i64 0, ptr %ehe\n")
emith(" br label %unlock\n")
emith("unlock:\n")
emith(" call void @lp_funlock()\n")
emith(" br label %out\n")
emith("out:\n")
emith(" ret void\n")
emith("}\n")
}
function emit_fence_control() -> void {
emith("define internal i64 @lp_fenv(ptr %name, i64 %dflt) {\n")
emith("entry:\n")
emith(" %e = call ptr @getenv(ptr %name)\n")
emith(" %has = icmp ne ptr %e, null\n")
emith(" br i1 %has, label %read, label %none\n")
emith("read:\n")
emith(" %v = call i32 @atoi(ptr %e)\n")
emith(" %v64 = sext i32 %v to i64\n")
emith(" ret i64 %v64\n")
emith("none:\n")
emith(" ret i64 %dflt\n")
emith("}\n")
emith("define internal void @lp_finit() {\n")
emith("entry:\n")
emith(" %m0 = load i8, ptr @lp_fm\n")
emith(" %done = icmp ne i8 %m0, 0\n")
emith(" br i1 %done, label %out, label %go\n")
emith("go:\n")
emith(" %e = call ptr @getenv(ptr @.lpf_env)\n")
emith(" %has = icmp ne ptr %e, null\n")
emith(" br i1 %has, label %parse, label %dflt\n")
emith("parse:\n")
emith(" %c1 = call i32 @strcmp(ptr %e, ptr @.lpf_off)\n")
emith(" %is1 = icmp eq i32 %c1, 0\n")
emith(" %c2 = call i32 @strcmp(ptr %e, ptr @.lpf_count)\n")
emith(" %is2 = icmp eq i32 %c2, 0\n")
emith(" %c3 = call i32 @strcmp(ptr %e, ptr @.lpf_warn)\n")
emith(" %is3 = icmp eq i32 %c3, 0\n")
emith(" %ma = select i1 %is3, i8 3, i8 4\n")
emith(" %mb = select i1 %is2, i8 2, i8 %ma\n")
emith(" %mc = select i1 %is1, i8 1, i8 %mb\n")
emith(" br label %set\n")
emith("dflt:\n")
emith(" %d = load i8, ptr @lp_fdef\n")
emith(" %dz = icmp eq i8 %d, 0\n")
emith(" br i1 %dz, label %dev, label %set\n")
emith("dev:\n")
emith(" %dv = call ptr @getenv(ptr @.lpf_dev)\n")
emith(" %hd = icmp ne ptr %dv, null\n")
emith(" %md = select i1 %hd, i8 4, i8 1\n")
emith(" br label %set\n")
emith("set:\n")
emith(" %mode = phi i8 [ %mc, %parse ], [ %d, %dflt ], [ %md, %dev ]\n")
emith(" store i8 %mode, ptr @lp_fm\n")
emith(" %wd = load i64, ptr @lp_warm\n")
emith(" %w = call i64 @lp_fenv(ptr @.lpf_warm, i64 %wd)\n")
emith(" store i64 %w, ptr @lp_warm\n")
emith(" %wx5 = mul i64 %w, 5\n")
emith(" %wm = call i64 @lp_fenv(ptr @.lpf_warmmax, i64 %wx5)\n")
emith(" store i64 %wm, ptr @lp_warm_max\n")
emith(" %au = call i64 @lp_fenv(ptr @.lpf_auto, i64 1)\n")
emith(" %au8 = trunc i64 %au to i8\n")
emith(" store i8 %au8, ptr @lp_fauto\n")
emith(" %pl = call i64 @lp_fenv(ptr @.lpf_play, i64 -1)\n")
emith(" store i64 %pl, ptr @lp_autoplay\n")
emith(" %want = call i64 @lp_fenv(ptr @.lpf_tab, i64 4194304)\n")
emith(" br label %cap\n")
emith("cap:\n")
emith(" %c = phi i64 [ 1024, %set ], [ %c2x, %grow ]\n")
emith(" %big = icmp sge i64 %c, %want\n")
emith(" br i1 %big, label %capped, label %grow\n")
emith("grow:\n")
emith(" %c2x = shl i64 %c, 1\n")
emith(" br label %cap\n")
emith("capped:\n")
emith(" %mask = sub i64 %c, 1\n")
emith(" store i64 %mask, ptr @lp_tabmask\n")
emith(" %q = lshr i64 %c, 2\n")
emith(" %max = mul i64 %q, 3\n")
emith(" store i64 %max, ptr @lp_tabmax\n")
emith(" br label %out\n")
emith("out:\n")
emith(" ret void\n")
emith("}\n")
emith("define internal void @lp_fsnap() {\n")
emith("entry:\n")
emith(" %lv = load i64, ptr @lp_live\n")
emith(" store i64 %lv, ptr @lp_mlive\n")
emith(" %pr = load i64, ptr @lp_pre\n")
emith(" store i64 %pr, ptr @lp_mpre\n")
emith(" %md = load i64, ptr @lp_made\n")
emith(" store i64 %md, ptr @lp_mmade\n")
emith(" %fr = load i64, ptr @lp_freed\n")
emith(" store i64 %fr, ptr @lp_mfreed\n")
emith(" store i32 0, ptr @lp_ntouch\n")
emith(" ret void\n")
emith("}\n")
emith("define internal void @lp_fsnap_all() {\n")
emith("entry:\n")
emith(" %n = load i32, ptr @lp_site_n\n")
emith(" %n64 = zext i32 %n to i64\n")
emith(" %b = shl i64 %n64, 3\n")
emith(" call ptr @memcpy(ptr @lp_site_mark, ptr @lp_site_live, i64 %b)\n")
emith(" call void @lp_fsnap()\n")
emith(" ret void\n")
emith("}\n")
emith("define void @lp_mem_play() {\n")
emith("entry:\n")
emith(" call void @lp_finit()\n")
emith(" %m = load i8, ptr @lp_fm\n")
emith(" %off = icmp ule i8 %m, 1\n")
emith(" br i1 %off, label %out, label %chk\n")
emith("chk:\n")
emith(" %f = load i64, ptr @lp_frame\n")
emith(" %t = load i8, ptr @lp_ft\n")
emith(" %on = icmp ne i8 %t, 0\n")
emith(" br i1 %on, label %rewarm, label %start\n")
emith("rewarm:\n")
emith(" call void @lp_flock()\n")
emith(" store i8 0, ptr @lp_fj\n")
emith(" store i64 0, ptr @lp_flat\n")
emith(" store i64 %f, ptr @lp_playf\n")
emith(" call void @lp_funlock()\n")
emith(" br label %out\n")
emith("start:\n")
emith(" %tab = load ptr, ptr @lp_tab\n")
emith(" %none = icmp eq ptr %tab, null\n")
emith(" br i1 %none, label %make, label %arm\n")
emith("make:\n")
emith(" %mask = load i64, ptr @lp_tabmask\n")
emith(" %c = add i64 %mask, 1\n")
emith(" %p = call ptr @calloc(i64 %c, i64 16)\n")
emith(" store ptr %p, ptr @lp_tab\n")
emith(" br label %arm\n")
emith("arm:\n")
emith(" store i64 %f, ptr @lp_playf\n")
emith(" store i64 0, ptr @lp_flat\n")
emith(" %cp0 = call ptr @getenv(ptr @.lpf_census)\n")
emith(" %cpd = load ptr, ptr @lp_census_def\n")
emith(" %hasenv = icmp ne ptr %cp0, null\n")
emith(" %cp = select i1 %hasenv, ptr %cp0, ptr %cpd\n")
emith(" store ptr %cp, ptr @lp_census\n")
emith(" %hascp = icmp ne ptr %cp, null\n")
emith(" br i1 %hascp, label %reg, label %go\n")
emith("reg:\n")
emith(" call i32 @atexit(ptr @lp_fcensus)\n")
emith(" br label %go\n")
emith("go:\n")
emith(" call void @lp_fsnap_all()\n")
emith(" store i8 1, ptr @lp_ft\n")
emith(" br label %out\n")
emith("out:\n")
emith(" ret void\n")
emith("}\n")
emith("define void @lp_mem_settled() {\n")
emith("entry:\n")
emith(" %t = load i8, ptr @lp_ft\n")
emith(" %on = icmp ne i8 %t, 0\n")
emith(" br i1 %on, label %chk, label %out\n")
emith("chk:\n")
emith(" %j = load i8, ptr @lp_fj\n")
emith(" %already = icmp ne i8 %j, 0\n")
emith(" br i1 %already, label %out, label %go\n")
emith("go:\n")
emith(" call void @lp_flock()\n")
emith(" call void @lp_fsnap_all()\n")
emith(" call void @lp_fsettle_locked()\n")
emith(" call void @lp_funlock()\n")
emith(" br label %out\n")
emith("out:\n")
emith(" ret void\n")
emith("}\n")
emith("define i64 @lp_mem_kept() {\n")
emith("entry:\n")
emith(" %j = load i8, ptr @lp_fj\n")
emith(" %on = icmp ne i8 %j, 0\n")
emith(" br i1 %on, label %read, label %none\n")
emith("none:\n")
emith(" ret i64 0\n")
emith("read:\n")
emith(" call void @lp_flock()\n")
emith(" %lv = load i64, ptr @lp_live\n")
emith(" %sl = load i64, ptr @lp_slive\n")
emith(" %pr = load i64, ptr @lp_pre\n")
emith(" %sp = load i64, ptr @lp_spre\n")
emith(" %a = sub i64 %lv, %sl\n")
emith(" %b = sub i64 %pr, %sp\n")
emith(" %k = sub i64 %a, %b\n")
emith(" call void @lp_funlock()\n")
emith(" ret i64 %k\n")
emith("}\n")
emith("define i64 @lp_mem_bad_frames() {\n")
emith("entry:\n")
emith(" %b = load i64, ptr @lp_fbad\n")
emith(" ret i64 %b\n")
emith("}\n")
}
function emit_fence_report() -> void {
emith("define internal i64 @lp_fdelta(i32 %s, ptr %base) {\n")
emith("entry:\n")
emith(" %sp = getelementptr inbounds i64, ptr @lp_site_live, i32 %s\n")
emith(" %v = load i64, ptr %sp\n")
emith(" %mp = getelementptr inbounds i64, ptr %base, i32 %s\n")
emith(" %m = load i64, ptr %mp\n")
emith(" %d = sub i64 %v, %m\n")
emith(" ret i64 %d\n")
emith("}\n")
emith("define internal i32 @lp_fsite_at(i32 %i, i1 %all) {\n")
emith("entry:\n")
emith(" br i1 %all, label %every, label %touched\n")
emith("every:\n")
emith(" ret i32 %i\n")
emith("touched:\n")
emith(" %tp = getelementptr inbounds [512 x i32], ptr @lp_touch, i32 0, i32 %i\n")
emith(" %s = load i32, ptr %tp\n")
emith(" ret i32 %s\n")
emith("}\n")
emith("define internal { i64, i32 } @lp_fpick(ptr %base, i1 %all, i32 %cnt, i64 %pb, i32 %ps) {\n")
emith("entry:\n")
emith(" br label %loop\n")
emith("loop:\n")
emith(" %i = phi i32 [ 0, %entry ], [ %i1, %body ]\n")
emith(" %bb0 = phi i64 [ 0, %entry ], [ %bb1, %body ]\n")
emith(" %bs0 = phi i32 [ -1, %entry ], [ %bs1, %body ]\n")
emith(" %go = icmp ult i32 %i, %cnt\n")
emith(" br i1 %go, label %body, label %done\n")
emith("body:\n")
emith(" %s = call i32 @lp_fsite_at(i32 %i, i1 %all)\n")
emith(" %d = call i64 @lp_fdelta(i32 %s, ptr %base)\n")
emith(" %pos = icmp sgt i64 %d, 0\n")
emith(" %lt = icmp slt i64 %d, %pb\n")
emith(" %eqb = icmp eq i64 %d, %pb\n")
emith(" %gts = icmp sgt i32 %s, %ps\n")
emith(" %tie = and i1 %eqb, %gts\n")
emith(" %below = or i1 %lt, %tie\n")
emith(" %gtb = icmp sgt i64 %d, %bb0\n")
emith(" %eqbb = icmp eq i64 %d, %bb0\n")
emith(" %lts = icmp slt i32 %s, %bs0\n")
emith(" %tie2 = and i1 %eqbb, %lts\n")
emith(" %better = or i1 %gtb, %tie2\n")
emith(" %c1 = and i1 %pos, %below\n")
emith(" %take = and i1 %c1, %better\n")
emith(" %bb1 = select i1 %take, i64 %d, i64 %bb0\n")
emith(" %bs1 = select i1 %take, i32 %s, i32 %bs0\n")
emith(" %i1 = add i32 %i, 1\n")
emith(" br label %loop\n")
emith("done:\n")
emith(" %r0 = insertvalue { i64, i32 } undef, i64 %bb0, 0\n")
emith(" %r1 = insertvalue { i64, i32 } %r0, i32 %bs0, 1\n")
emith(" ret { i64, i32 } %r1\n")
emith("}\n")
emith("define internal void @lp_fsite_row(ptr %fp, i32 %fd, i64 %bytes, i32 %s) {\n")
emith("entry:\n")
emith(" %fnp = getelementptr inbounds ptr, ptr @lp_site_fn, i32 %s\n")
emith(" %fn = load ptr, ptr %fnp\n")
emith(" %flp = getelementptr inbounds ptr, ptr @lp_site_file, i32 %s\n")
emith(" %fl = load ptr, ptr %flp\n")
emith(" %lnp = getelementptr inbounds i32, ptr @lp_site_line, i32 %s\n")
emith(" %ln = load i32, ptr %lnp\n")
emith(" %kdp = getelementptr inbounds ptr, ptr @lp_site_kind, i32 %s\n")
emith(" %kd = load ptr, ptr %kdp\n")
emith(" %tofile = icmp ne ptr %fp, null\n")
emith(" br i1 %tofile, label %file, label %err\n")
emith("file:\n")
emith(" call i32 (ptr, ptr, ...) @fprintf(ptr %fp, ptr @.lpf_crow, i64 %bytes, ptr %fn, ptr %fl, i32 %ln, ptr %kd)\n")
emith(" br label %from\n")
emith("err:\n")
emith(" call i32 (i32, ptr, ...) @dprintf(i32 %fd, ptr @.lpf_row, i64 %bytes, ptr %fn, ptr %fl, i32 %ln, ptr %kd)\n")
emith(" br label %from\n")
emith("from:\n")
emith(" call void @lp_fsite_from(ptr %fp, i32 %s, ptr %fn)\n")
emith(" ret void\n")
emith("}\n")
emith("; the largest `rows` sites by what they hold over `base`, largest first\n")
emith("define internal void @lp_franked(ptr %fp, ptr %base, i1 %all, i32 %cnt, i32 %rows) {\n")
emith("entry:\n")
emith(" br label %pass\n")
emith("pass:\n")
emith(" %row = phi i32 [ 0, %entry ], [ %row1, %show ]\n")
emith(" %pb = phi i64 [ 9223372036854775807, %entry ], [ %bb, %show ]\n")
emith(" %ps = phi i32 [ -1, %entry ], [ %bs, %show ]\n")
emith(" %more = icmp ult i32 %row, %rows\n")
emith(" br i1 %more, label %scan, label %tail\n")
emith("scan:\n")
emith(" %r = call { i64, i32 } @lp_fpick(ptr %base, i1 %all, i32 %cnt, i64 %pb, i32 %ps)\n")
emith(" %bb = extractvalue { i64, i32 } %r, 0\n")
emith(" %bs = extractvalue { i64, i32 } %r, 1\n")
emith(" %found = icmp sge i32 %bs, 0\n")
emith(" br i1 %found, label %show, label %tail\n")
emith("show:\n")
emith(" call void @lp_fsite_row(ptr %fp, i32 2, i64 %bb, i32 %bs)\n")
emith(" %row1 = add i32 %row, 1\n")
emith(" br label %pass\n")
emith("tail:\n")
emith(" ret void\n")
emith("}\n")
emith("define internal void @lp_freport(i64 %f, i64 %kept) {\n")
emith("entry:\n")
emith(" %md = load i64, ptr @lp_made\n")
emith(" %mm = load i64, ptr @lp_mmade\n")
emith(" %made = sub i64 %md, %mm\n")
emith(" %fr = load i64, ptr @lp_freed\n")
emith(" %mf = load i64, ptr @lp_mfreed\n")
emith(" %freed = sub i64 %fr, %mf\n")
emith(" call i32 (i32, ptr, ...) @dprintf(i32 2, ptr @.lpf_hdr, i64 %f, i64 %kept, i64 %made, i64 %freed)\n")
emith(" %nt = load i32, ptr @lp_ntouch\n")
emith(" %all = icmp ugt i32 %nt, 512\n")
emith(" %sn = load i32, ptr @lp_site_n\n")
emith(" %cnt = select i1 %all, i32 %sn, i32 %nt\n")
emith(" call void @lp_franked(ptr null, ptr @lp_site_mark, i1 %all, i32 %cnt, i32 12)\n")
emith(" ret void\n")
emith("}\n")
emith("; R3D_ALLOC_CENSUS=<file>, written at exit: the run's totals since play settled, and every site\n")
emith("; that holds more than it did then, largest first\n")
emith("define void @lp_fcensus() {\n")
emith("entry:\n")
emith(" %path = load ptr, ptr @lp_census\n")
emith(" %none = icmp eq ptr %path, null\n")
emith(" br i1 %none, label %out, label %open\n")
emith("open:\n")
emith(" %zn = call i64 @lp_fzone()\n")
emith(" %fp = call ptr @fopen(ptr %path, ptr @.lpf_w)\n")
emith(" %bad = icmp eq ptr %fp, null\n")
emith(" br i1 %bad, label %out, label %write\n")
emith("write:\n")
emith(" store i8 0, ptr @lp_ft\n")
emith(" %j = load i64, ptr @lp_fjudged\n")
emith(" %b = load i64, ptr @lp_fbad\n")
emith(" %k = call i64 @lp_mem_kept()\n")
emith(" %md = load i64, ptr @lp_made\n")
emith(" %fr = load i64, ptr @lp_freed\n")
emith(" %ov = load i64, ptr @lp_over\n")
emith(" call i32 (ptr, ptr, ...) @fprintf(ptr %fp, ptr @.lpf_chdr, i64 %j, i64 %b, i64 %k, i64 %md, i64 %fr, i64 %ov)\n")
emith(" %jq = icmp ne i64 %j, 0\n")
emith(" %zs = load i64, ptr @lp_zset\n")
emith(" %zd = sub i64 %zn, %zs\n")
emith(" %nat = sub i64 %zd, %k\n")
emith(" %nat1 = select i1 %jq, i64 %nat, i64 0\n")
emith(" call i32 (ptr, ptr, ...) @fprintf(ptr %fp, ptr @.lpf_cnat, i64 %nat1)\n")
emith(" %sn = load i32, ptr @lp_site_n\n")
emith(" call void @lp_franked(ptr %fp, ptr @lp_site_base, i1 1, i32 %sn, i32 40)\n")
emith(" call i32 @fclose(ptr %fp)\n")
emith(" br label %out\n")
emith("out:\n")
emith(" ret void\n")
emith("}\n")
}
function emit_fence_frame() -> void {
emith("define internal void @lp_fmark_touched() {\n")
emith("entry:\n")
emith(" %nt = load i32, ptr @lp_ntouch\n")
emith(" %all = icmp ugt i32 %nt, 512\n")
emith(" br i1 %all, label %every, label %some\n")
emith("every:\n")
emith(" call void @lp_fsnap_all()\n")
emith(" ret void\n")
emith("some:\n")
emith(" br label %loop\n")
emith("loop:\n")
emith(" %i = phi i32 [ 0, %some ], [ %i1, %body ]\n")
emith(" %go = icmp ult i32 %i, %nt\n")
emith(" br i1 %go, label %body, label %done\n")
emith("body:\n")
emith(" %tp = getelementptr inbounds [512 x i32], ptr @lp_touch, i32 0, i32 %i\n")
emith(" %s = load i32, ptr %tp\n")
emith(" %sp = getelementptr inbounds i64, ptr @lp_site_live, i32 %s\n")
emith(" %v = load i64, ptr %sp\n")
emith(" %mp = getelementptr inbounds i64, ptr @lp_site_mark, i32 %s\n")
emith(" store i64 %v, ptr %mp\n")
emith(" %i1 = add i32 %i, 1\n")
emith(" br label %loop\n")
emith("done:\n")
emith(" call void @lp_fsnap()\n")
emith(" ret void\n")
emith("}\n")
emith("; malloc's live bytes over every zone (0 where there is no malloc_zone_statistics)\n")
emith("define internal i64 @lp_fzone() {\n")
emith("entry:\n")
emith(" %has = icmp ne ptr @malloc_zone_statistics, null\n")
emith(" br i1 %has, label %read, label %none\n")
emith("none:\n")
emith(" ret i64 0\n")
emith("read:\n")
emith(" %st = alloca [4 x i64], align 8\n")
emith(" call void @malloc_zone_statistics(ptr null, ptr %st)\n")
emith(" %p = getelementptr [4 x i64], ptr %st, i32 0, i32 1\n")
emith(" %n = load i64, ptr %p\n")
emith(" ret i64 %n\n")
emith("}\n")
emith("define internal void @lp_fsettle_locked() {\n")
emith("entry:\n")
emith(" %n = load i32, ptr @lp_site_n\n")
emith(" %n64 = zext i32 %n to i64\n")
emith(" %nb = shl i64 %n64, 3\n")
emith(" call ptr @memcpy(ptr @lp_site_base, ptr @lp_site_live, i64 %nb)\n")
emith(" %lv = load i64, ptr @lp_live\n")
emith(" store i64 %lv, ptr @lp_slive\n")
emith(" %pr = load i64, ptr @lp_pre\n")
emith(" store i64 %pr, ptr @lp_spre\n")
emith(" %z = call i64 @lp_fzone()\n")
emith(" store i64 %z, ptr @lp_zset\n")
emith(" store i8 1, ptr @lp_fj\n")
emith(" ret void\n")
emith("}\n")
emith("; the frame's mark. Tracking starts at the first one (R3D_ALLOC_AUTO=0: at Mem.play() or\n")
emith("; R3D_ALLOC_PLAY=<frame>); judging starts once R3D_ALLOC_WARM frames in a row kept nothing, or\n")
emith("; R3D_ALLOC_WARM_MAX frames after tracking (re)started, whichever is first\n")
emith("define void @lp_mem_frame() {\n")
emith("entry:\n")
emith(" call void @lp_finit()\n")
emith(" %f = load i64, ptr @lp_frame\n")
emith(" %f1 = add i64 %f, 1\n")
emith(" %m = load i8, ptr @lp_fm\n")
emith(" %off = icmp ule i8 %m, 1\n")
emith(" br i1 %off, label %tick, label %live\n")
emith("live:\n")
emith(" %t = load i8, ptr @lp_ft\n")
emith(" %on = icmp ne i8 %t, 0\n")
emith(" br i1 %on, label %tracking, label %maybe_play\n")
emith("maybe_play:\n")
emith(" %au = load i8, ptr @lp_fauto\n")
emith(" %auq = icmp ne i8 %au, 0\n")
emith(" %ap = load i64, ptr @lp_autoplay\n")
emith(" %apset = icmp sge i64 %ap, 0\n")
emith(" %apdue = icmp sge i64 %f1, %ap\n")
emith(" %apgo = and i1 %apset, %apdue\n")
emith(" %go = or i1 %auq, %apgo\n")
emith(" br i1 %go, label %play, label %tick\n")
emith("play:\n")
emith(" call void @lp_mem_play()\n")
emith(" br label %tick\n")
emith("tracking:\n")
emith(" call void @lp_flock()\n")
emith(" %ov = load i64, ptr @lp_over\n")
emith(" %ovq = icmp sgt i64 %ov, 0\n")
emith(" %rep = load i8, ptr @lp_overrep\n")
emith(" %repq = icmp eq i8 %rep, 0\n")
emith(" %fullnew = and i1 %ovq, %repq\n")
emith(" br i1 %fullnew, label %full, label %weigh\n")
emith("full:\n")
emith(" %tn = load i64, ptr @lp_tabn\n")
emith(" call i32 (i32, ptr, ...) @dprintf(i32 2, ptr @.lpf_full, i64 %tn)\n")
emith(" store i8 1, ptr @lp_overrep\n")
emith(" store i8 0, ptr @lp_fj\n")
emith(" br label %unlock\n")
emith("weigh:\n")
emith(" %lv = load i64, ptr @lp_live\n")
emith(" %ml = load i64, ptr @lp_mlive\n")
emith(" %pr = load i64, ptr @lp_pre\n")
emith(" %mp = load i64, ptr @lp_mpre\n")
emith(" %a = sub i64 %lv, %ml\n")
emith(" %b = sub i64 %pr, %mp\n")
emith(" %kept = sub i64 %a, %b\n")
emith(" %bad = icmp sgt i64 %kept, 0\n")
emith(" %j = load i8, ptr @lp_fj\n")
emith(" %judging = icmp ne i8 %j, 0\n")
emith(" br i1 %judging, label %judge, label %warming\n")
emith("warming:\n")
emith(" %fl = load i64, ptr @lp_flat\n")
emith(" %fl1 = add i64 %fl, 1\n")
emith(" %fl2 = select i1 %bad, i64 0, i64 %fl1\n")
emith(" store i64 %fl2, ptr @lp_flat\n")
emith(" %w = load i64, ptr @lp_warm\n")
emith(" %wset = icmp sge i64 %w, 0\n")
emith(" %wdone = icmp sge i64 %fl2, %w\n")
emith(" %byflat = and i1 %wset, %wdone\n")
emith(" %pf = load i64, ptr @lp_playf\n")
emith(" %since = sub i64 %f1, %pf\n")
emith(" %wm = load i64, ptr @lp_warm_max\n")
emith(" %wmset = icmp sge i64 %wm, 0\n")
emith(" %wmdone = icmp sge i64 %since, %wm\n")
emith(" %bycap = and i1 %wmset, %wmdone\n")
emith(" %settle = or i1 %byflat, %bycap\n")
emith(" br i1 %settle, label %settle_now, label %mark\n")
emith("settle_now:\n")
emith(" call void @lp_fsettle_locked()\n")
emith(" br label %mark\n")
emith("judge:\n")
emith(" %jd = load i64, ptr @lp_fjudged\n")
emith(" %jd1 = add i64 %jd, 1\n")
emith(" store i64 %jd1, ptr @lp_fjudged\n")
emith(" br i1 %bad, label %kept_some, label %mark\n")
emith("kept_some:\n")
emith(" %fk = load i64, ptr @lp_fkept\n")
emith(" %fk1 = add i64 %fk, %kept\n")
emith(" store i64 %fk1, ptr @lp_fkept\n")
emith(" %fb = load i64, ptr @lp_fbad\n")
emith(" %fb1 = add i64 %fb, 1\n")
emith(" store i64 %fb1, ptr @lp_fbad\n")
emith(" %say = icmp uge i8 %m, 3\n")
emith(" br i1 %say, label %say_it, label %mark\n")
emith("say_it:\n")
emith(" call void @lp_freport(i64 %f, i64 %kept)\n")
emith(" %fail = icmp eq i8 %m, 4\n")
emith(" br i1 %fail, label %die, label %mark\n")
emith("die:\n")
emith(" call i32 (i32, ptr, ...) @dprintf(i32 2, ptr @.lpf_failing)\n")
emith(" store i8 0, ptr @lp_ft\n")
emith(" call void @lp_funlock()\n")
emith(" call void @exit(i32 86)\n")
emith(" unreachable\n")
emith("mark:\n")
emith(" call void @lp_fmark_touched()\n")
emith(" br label %unlock\n")
emith("unlock:\n")
emith(" call void @lp_funlock()\n")
emith(" br label %tick\n")
emith("tick:\n")
emith(" store i64 %f1, ptr @lp_frame\n")
emith(" ret void\n")
emith("}\n")
}
function emit_fence_win() -> void {
emith("@lp_site = global i32 0\n")
emith("define ptr @lp_malloc(i64 %n) {\n")
emith("entry:\n")
emith(" %p = tail call ptr @malloc(i64 %n)\n")
emith(" ret ptr %p\n")
emith("}\n")
emith("define ptr @lp_calloc(i64 %n, i64 %s) {\n")
emith("entry:\n")
emith(" %p = tail call ptr @calloc(i64 %n, i64 %s)\n")
emith(" ret ptr %p\n")
emith("}\n")
emith("define ptr @lp_realloc(ptr %o, i64 %n) {\n")
emith("entry:\n")
emith(" %p = tail call ptr @realloc(ptr %o, i64 %n)\n")
emith(" ret ptr %p\n")
emith("}\n")
emith("define void @lp_free(ptr %o) {\n")
emith("entry:\n")
emith(" tail call void @free(ptr %o)\n")
emith(" ret void\n")
emith("}\n")
emith("define void @lp_mem_play() {\n")
emith("entry:\n")
emith(" ret void\n")
emith("}\n")
emith("define void @lp_mem_settled() {\n")
emith("entry:\n")
emith(" ret void\n")
emith("}\n")
emith("define void @lp_mem_frame() {\n")
emith("entry:\n")
emith(" ret void\n")
emith("}\n")
emith("define i64 @lp_mem_kept() {\n")
emith("entry:\n")
emith(" ret i64 0\n")
emith("}\n")
emith("define i64 @lp_mem_bad_frames() {\n")
emith("entry:\n")
emith(" ret i64 0\n")
emith("}\n")
}

View file

@ -259,7 +259,7 @@ function emit_fp_str_fn() -> void {
emith("@.fmt_fpg = private unnamed_addr constant [5 x i8] c\"%.*g\\00\"\n")
emith("@.fp_marks = private unnamed_addr constant [8 x i8] c\".eEnNiI\\00\"\n")
emith("define ptr @lp_fp_str(double %v, i32 %single) {\n")
emith("entry:\n %buf = call ptr @malloc(i64 40)\n br label %try\n")
emith("entry:\n %buf = call ptr @lp_malloc(i64 40)\n br label %try\n")
emith("try:\n %p = phi i32 [ 6, %entry ], [ %p1, %again ]\n")
emith(" %w = call i32 (ptr, i64, ptr, ...) @snprintf(ptr %buf, i64 36, ptr @.fmt_fpg, i32 %p, double %v)\n")
emith(" %r = call double @strtod(ptr %buf, ptr null)\n")
@ -285,7 +285,7 @@ function emit_fp_buffer(t: pointer, n: Val) -> Val {
if (t == "double") { sz = "8"; ty = "doubles" }
let by = emit_bind(`mul i32 {n.code}, {sz}`)
let w = emit_bind(`zext i32 {by} to i64`)
return val(emit_bind(`call ptr @malloc(i64 {w})`), ty)
return val(emit_bind(`call ptr @lp_malloc(i64 {w})`), ty)
}
# ---- Math.* on floats ---------------------------------------------------------

View file

@ -70,6 +70,7 @@ function emit_global_init_fn() -> void {
# 0.S: the runtime's states are made first, before it boots (rt_init reads them); the rest after
function is_runtime_state_var(d: Node) -> bool { return d.uns == 1 and is_state_ty(d.ty) and is_runtime_file(d.file) }
function emit_init_fn(name: pointer, runtime: bool) -> void {
fence_enter(name)
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0; nself = 0
ret_ty = "void"
let fbody = buf_new()
@ -276,7 +277,7 @@ function emit_str_prelude() -> void {
emith(" %zb = icmp eq i8 %ccb, 0\n br i1 %zb, label %alloc, label %blb\n")
emith("blb:\n %ib1 = add i32 %ib, 1\n br label %bl\n")
emith("alloc:\n %sum = add i32 %ia, %ib\n %sz = add i32 %sum, 1\n")
emith(" %sz64 = sext i32 %sz to i64\n %out = call ptr @malloc(i64 %sz64)\n br label %c1\n")
emith(" %sz64 = sext i32 %sz to i64\n %out = call ptr @lp_malloc(i64 %sz64)\n br label %c1\n")
emith("c1:\n %i = phi i32 [ 0, %alloc ], [ %i1, %c1b ]\n")
emith(" %d1 = icmp slt i32 %i, %ia\n br i1 %d1, label %c1b, label %c2i\n")
emith("c1b:\n %s1 = getelementptr inbounds i8, ptr %a, i32 %i\n %v1 = load i8, ptr %s1\n")
@ -296,7 +297,7 @@ function emit_str_prelude() -> void {
# buffer, prepends '-' for negatives, and moves them to the buffer's start.
function emit_int_str() -> void {
emith("define ptr @lp_int_str(i32 %n0) {\n")
emith("entry:\n %buf = call ptr @malloc(i64 24)\n")
emith("entry:\n %buf = call ptr @lp_malloc(i64 24)\n")
emith(" %isneg = icmp slt i32 %n0, 0\n %neg = sub i32 0, %n0\n")
emith(" %n = select i1 %isneg, i32 %neg, i32 %n0\n")
emith(" %term = getelementptr inbounds i8, ptr %buf, i32 23\n store i8 0, ptr %term\n")
@ -321,7 +322,7 @@ function emit_int_str() -> void {
# at most 20 digits plus sign and NUL, so the 24-byte scratch buffer still fits.
function emit_long_str() -> void {
emith("define ptr @lp_long_str(i64 %n0) {\n")
emith("entry:\n %buf = call ptr @malloc(i64 24)\n")
emith("entry:\n %buf = call ptr @lp_malloc(i64 24)\n")
emith(" %isneg = icmp slt i64 %n0, 0\n %neg = sub i64 0, %n0\n")
emith(" %n = select i1 %isneg, i64 %neg, i64 %n0\n")
emith(" %term = getelementptr inbounds i8, ptr %buf, i32 23\n store i8 0, ptr %term\n")
@ -346,7 +347,7 @@ function emit_long_str() -> void {
function emit_str_slice() -> void {
emith("define ptr @lp_str_slice(ptr %s, i32 %start, i32 %end) {\n")
emith("entry:\n %len = sub i32 %end, %start\n %sz = add i32 %len, 1\n")
emith(" %sz64 = sext i32 %sz to i64\n %out = call ptr @malloc(i64 %sz64)\n br label %loop\n")
emith(" %sz64 = sext i32 %sz to i64\n %out = call ptr @lp_malloc(i64 %sz64)\n br label %loop\n")
emith("loop:\n %i = phi i32 [ 0, %entry ], [ %i1, %body ]\n")
emith(" %d = icmp slt i32 %i, %len\n br i1 %d, label %body, label %fin\n")
emith("body:\n %si = add i32 %start, %i\n %sp = getelementptr inbounds i8, ptr %s, i32 %si\n %c = load i8, ptr %sp\n")

View file

@ -17,7 +17,7 @@ function is_intrinsic(name: pointer) -> bool {
return false
}
# emit " <r> = <rest>\n" and return r
function emit_bind(rest: pointer) -> pointer { let r = nreg(); emit(" "); emit(r); emit(" = "); emit(rest); emit("\n"); return r }
function emit_bind(rest: pointer) -> pointer { fence_bind(rest); let r = nreg(); emit(" "); emit(r); emit(" = "); emit(rest); emit("\n"); return r }
function arg_code(e: Node, i: int) -> pointer { let v = emit_expr(e.kids[i]); return v.code }
# a buffer handed to the platform layer: a slice's elements, never its header (as an extern's are)
@ -34,7 +34,7 @@ function emit_intrinsic(name: pointer, e: Node) -> Val {
if (name == "resize") {
let p = arg_code(e, 0); let n = arg_code(e, 1)
let w = emit_bind(`zext i32 {n} to i64`)
return val(emit_bind(`call ptr @realloc(ptr {p}, i64 {w})`), "pointer")
return val(emit_bind(`call ptr @lp_realloc(ptr {p}, i64 {w})`), "pointer")
}
# Every asset a Ludic program loads arrives through here — gltf_load, tex_load,
# Audio.load, Fs.read_text and the renderer's own shader loads all bottom out at

View file

@ -39,9 +39,9 @@ function emit_intrinsic2(name: pointer, e: Node) -> Val {
# a slice is its elements and its header: both go (L7)
if is_slice_ty(fv.ty) {
let fd = emit_bind(`load ptr, ptr {slice_field(fv.code, 0)}`)
emit(` call void @free(ptr {fd})\n`)
emit(` call void @lp_free(ptr {fd})\n`)
}
emit(` call void @free(ptr {fv.code})\n`)
emit(` call void @lp_free(ptr {fv.code})\n`)
return val("0", "void")
}
if (name == "fill") {

View file

@ -13,7 +13,7 @@ function emit_mem_ns(meth: pointer, e: Node) -> Val {
if (meth == "bytes") { # allocate n bytes -> a byte buffer
let n = emit_expr(e.kids[0])
let w = emit_bind(`zext i32 {n.code} to i64`)
return val(emit_bind(`call ptr @malloc(i64 {w})`), "pointer")
return val(emit_bind(`call ptr @lp_malloc(i64 {w})`), "pointer")
}
if (meth == "words") { # allocate n 32-bit words
let n = emit_expr(e.kids[0])

View file

@ -32,7 +32,7 @@ function emit_new_struct(name: pointer, rec: Node, into: Node) -> Val {
let lty = layout_ty(name)
let sz = emit_sizeof(lty)
var obj = ""
if into != null { obj = emit_expr(into).code } else { obj = emit_bind(`call ptr @malloc(i64 {sz})`) }
if into != null { obj = emit_expr(into).code } else { g_site_next = `new {name}`; obj = emit_bind(`call ptr @lp_malloc(i64 {sz})`) }
var f = 0
while f < len(s.kids) {
let fd = s.kids[f]
@ -52,7 +52,8 @@ function emit_new_struct(name: pointer, rec: Node, into: Node) -> Val {
function emit_new_slice(ty: pointer) -> Val {
let sz = emit_sizeof("%LSlice")
let h = emit_bind(`call ptr @malloc(i64 {sz})`)
g_site_next = `new {ty}`
let h = emit_bind(`call ptr @lp_malloc(i64 {sz})`)
let d0 = nreg(); emit(" "); emit(d0); emit(" = getelementptr inbounds %LSlice, ptr "); emit(h); emit(", i32 0, i32 0\n")
emit(" store ptr null, ptr "); emit(d0); emit("\n")
let d1 = nreg(); emit(" "); emit(d1); emit(" = getelementptr inbounds %LSlice, ptr "); emit(h); emit(", i32 0, i32 1\n")
@ -67,7 +68,7 @@ function emit_sized_slice(el: pointer, n: Val) -> Val {
let h = emit_new_slice("[]" + el)
let esz = emit_sizeof(llty(el))
let nn = emit_bind(`zext i32 {n.code} to i64`)
let data = emit_bind(`call ptr @calloc(i64 {nn}, i64 {esz})`)
let data = emit_bind(`call ptr @lp_calloc(i64 {nn}, i64 {esz})`)
emit(` store ptr {data}, ptr {slice_field(h.code, 0)}\n`)
emit(` store i32 {n.code}, ptr {slice_field(h.code, 1)}\n`)
emit(` store i32 {n.code}, ptr {slice_field(h.code, 2)}\n`)
@ -183,7 +184,7 @@ function emit_push_into(h: pointer, elt: pointer, v: Val) -> void {
let ncw = emit_bind(`zext i32 {nc} to i64`)
let bytes = emit_bind(`mul i64 {ncw}, {esz}`)
let old = emit_bind(`load ptr, ptr {dp}`)
let nd = emit_bind(`call ptr @realloc(ptr {old}, i64 {bytes})`)
let nd = emit_bind(`call ptr @lp_realloc(ptr {old}, i64 {bytes})`)
emit(" store ptr "); emit(nd); emit(", ptr "); emit(dp); emit("\n")
emit(" store i32 "); emit(nc); emit(", ptr "); emit(cp); emit("\n")
emit(" br label %"); emit(put); emit("\n")

View file

@ -200,7 +200,7 @@ function emit_ecs_grow() -> void {
function emit_grow_store(store: pointer, esz: pointer, k: int) -> void {
let n = itoa(k)
emit(` %gp{n} = load ptr, ptr @{store}\n %gb{n} = mul i64 %nc64, {esz}\n %gr{n} = add i64 %gb{n}, 8\n`)
emit(` %gn{n} = call ptr @realloc(ptr %gp{n}, i64 %gr{n})\n %go{n} = mul i64 %cap64, {esz}\n`)
emit(` %gn{n} = call ptr @lp_realloc(ptr %gp{n}, i64 %gr{n})\n %go{n} = mul i64 %cap64, {esz}\n`)
emit(` %gt{n} = getelementptr i8, ptr %gn{n}, i64 %go{n}\n %gz{n} = sub i64 %gr{n}, %go{n}\n`)
emit(` %gm{n} = call ptr @memset(ptr %gt{n}, i32 0, i64 %gz{n})\n store ptr %gn{n}, ptr @{store}\n`)
}
@ -218,7 +218,7 @@ function emit_grow_dyn() -> void {
function emit_grow_slot(table: pointer, esz: pointer, t: pointer) -> void {
emit(` %d{t}p = getelementptr inbounds [32 x ptr], ptr @{table}, i32 0, i32 %di\n %d{t}o = load ptr, ptr %d{t}p\n`)
emit(` %d{t}b = mul i64 %nc64, {esz}\n %d{t}r = add i64 %d{t}b, 8\n %d{t}n = call ptr @realloc(ptr %d{t}o, i64 %d{t}r)\n`)
emit(` %d{t}b = mul i64 %nc64, {esz}\n %d{t}r = add i64 %d{t}b, 8\n %d{t}n = call ptr @lp_realloc(ptr %d{t}o, i64 %d{t}r)\n`)
emit(` %d{t}f = mul i64 %cap64, {esz}\n %d{t}t = getelementptr i8, ptr %d{t}n, i64 %d{t}f\n %d{t}z = sub i64 %d{t}r, %d{t}f\n`)
emit(` %d{t}m = call ptr @memset(ptr %d{t}t, i32 0, i64 %d{t}z)\n store ptr %d{t}n, ptr %d{t}p\n`)
}

View file

@ -5,6 +5,7 @@
function emit_system_fn(sys: Node) -> void {
det_enter(sys.s)
fence_enter(sys.s)
g_cur_scene = sys.c # scene owning this handler (null if global) — for `become`
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0; nself = 0
ret_ty = "void"
@ -376,6 +377,7 @@ function emit_scene_menu_render() -> void {
# @scene_exit_<Name>, called at the transition point (and enter at boot for the
# start scene). Emitted for every scene, empty body when the hook is absent.
function emit_scene_fn(name: pointer, kind: pointer, body: Node, menu: pointer) -> void {
fence_enter(`scene {name}`)
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0; nself = 0
ret_ty = "void"
let fbody = buf_new()
@ -670,6 +672,7 @@ function emit_event_fns() -> void {
g_ret_label = next
g_err_file = g_onlisten[i].file
vis_check(ev, en)
fence_enter(`@On({en})`)
emit_block(g_onlisten[i].a)
g_ret_label = "ret"
if not g_term { emit(` br label %{next}\n`) }

View file

@ -205,9 +205,9 @@ function emit_world_table() -> void {
emit("define i32 @ludic_register_prop(ptr %name, i32 %nfields) {\nentry:\n")
emit(" %dc = load i32, ptr @dyn_count\n %full = icmp slt i32 %dc, 32\n br i1 %full, label %do, label %rej\n")
emit("do:\n %rcap = load i32, ptr @L_cap\n %nf4 = mul i32 %nfields, 4\n %sz = mul i32 %nf4, %rcap\n %szl = sext i32 %sz to i64\n")
emit(" %buf = call ptr @malloc(i64 %szl)\n call ptr @memset(ptr %buf, i32 0, i64 %szl)\n")
emit(" %buf = call ptr @lp_malloc(i64 %szl)\n call ptr @memset(ptr %buf, i32 0, i64 %szl)\n")
emit(" %sp = getelementptr inbounds [32 x ptr], ptr @dynS, i32 0, i32 %dc\n store ptr %buf, ptr %sp\n")
emit(" %hcap = sext i32 %rcap to i64\n %hbuf = call ptr @malloc(i64 %hcap)\n call ptr @memset(ptr %hbuf, i32 0, i64 %hcap)\n")
emit(" %hcap = sext i32 %rcap to i64\n %hbuf = call ptr @lp_malloc(i64 %hcap)\n call ptr @memset(ptr %hbuf, i32 0, i64 %hcap)\n")
emit(" %hp = getelementptr inbounds [32 x ptr], ptr @dynH, i32 0, i32 %dc\n store ptr %hbuf, ptr %hp\n")
emit(" %fp = getelementptr inbounds [32 x i32], ptr @dynF, i32 0, i32 %dc\n store i32 %nfields, ptr %fp\n")
emit(" %np = getelementptr inbounds [32 x ptr], ptr @dynName, i32 0, i32 %dc\n store ptr %name, ptr %np\n")
@ -520,6 +520,7 @@ function emit_game_main() -> void {
if g_has_clear_color and (find_fn("rt_present") != null) {
emit(" call void " + fn_sym("rt_present") + "()\n")
}
emit(" call void @lp_mem_frame()\n") # 25.1: the fence judges the frame that ended
let fcur = emit_bind("load i32, ptr @L_frame") # Time.frame(): count completed frames
let fnext = emit_bind(`add i32 {fcur}, 1`)
emit(" store i32 "); emit(fnext); emit(", ptr @L_frame\n")

View file

@ -104,7 +104,7 @@ function emit_crypto_prelude() -> void {
emith(" %ip = alloca i64\n %bp = alloca i64\n")
# padded length = ((len + 8) / 64 + 1) * 64
emith(" %e0 = add i64 %len, 8\n %e1 = lshr i64 %e0, 6\n %e2 = add i64 %e1, 1\n %pl = shl i64 %e2, 6\n")
emith(" %buf = call ptr @malloc(i64 %pl)\n")
emith(" %buf = call ptr @lp_malloc(i64 %pl)\n")
emith(" call ptr @memset(ptr %buf, i32 0, i64 %pl)\n")
emith(" call ptr @memcpy(ptr %buf, ptr %msg, i64 %len)\n")
emith(" %pmark = getelementptr i8, ptr %buf, i64 %len\n store i8 -128, ptr %pmark\n") # 0x80
@ -215,7 +215,7 @@ function emit_crypto_prelude() -> void {
emith(" %ob8 = lshr i32 %hval, 8\n %obb8 = trunc i32 %ob8 to i8\n %oo2 = add i64 %oi4, 2\n %op2 = getelementptr i8, ptr %out, i64 %oo2\n store i8 %obb8, ptr %op2\n")
emith(" %obb0 = trunc i32 %hval to i8\n %oo3 = add i64 %oi4, 3\n %op3 = getelementptr i8, ptr %out, i64 %oo3\n store i8 %obb0, ptr %op3\n")
emith(" %oin = add i64 %oi, 1\n store i64 %oin, ptr %ip\n br label %oc\n")
emith("freeb:\n call void @free(ptr %buf)\n ret void\n}\n")
emith("freeb:\n call void @lp_free(ptr %buf)\n ret void\n}\n")
# one hex digit (0..15) -> its lowercase ASCII byte
emith("define i8 @lp_hex_digit(i32 %d) {\n")
@ -223,7 +223,7 @@ function emit_crypto_prelude() -> void {
# hex-encode %n bytes at %in -> a fresh null-terminated 2n-char string
emith("define ptr @lp_hex_encode(ptr %in, i64 %n) {\n")
emith("entry:\n %ip = alloca i64\n %olen = shl i64 %n, 1\n %olen1 = add i64 %olen, 1\n %s = call ptr @malloc(i64 %olen1)\n store i64 0, ptr %ip\n br label %c\n")
emith("entry:\n %ip = alloca i64\n %olen = shl i64 %n, 1\n %olen1 = add i64 %olen, 1\n %s = call ptr @lp_malloc(i64 %olen1)\n store i64 0, ptr %ip\n br label %c\n")
emith("c:\n %i = load i64, ptr %ip\n %lt = icmp ult i64 %i, %n\n br i1 %lt, label %bdy, label %done\n")
emith("bdy:\n %pp = getelementptr i8, ptr %in, i64 %i\n %byte = load i8, ptr %pp\n %bz = zext i8 %byte to i32\n")
emith(" %hi = lshr i32 %bz, 4\n %lo = and i32 %bz, 15\n %hc = call i8 @lp_hex_digit(i32 %hi)\n %lc = call i8 @lp_hex_digit(i32 %lo)\n")
@ -260,10 +260,10 @@ function emit_crypto_prelude() -> void {
emith("copyk:\n call ptr @memcpy(ptr %k0p, ptr %key, i64 %klen)\n br label %pads\n")
emith("pads:\n")
# inner = SHA-256( (K0 ^ ipad) || msg ), ipad = 0x36
emith(" %inlen = add i64 64, %mlen\n %inbuf = call ptr @malloc(i64 %inlen)\n")
emith(" %inlen = add i64 64, %mlen\n %inbuf = call ptr @lp_malloc(i64 %inlen)\n")
emith(" call void @lp_xor64(ptr %inbuf, ptr %k0p, i32 54)\n")
emith(" %inmsg = getelementptr i8, ptr %inbuf, i64 64\n call ptr @memcpy(ptr %inmsg, ptr %msg, i64 %mlen)\n")
emith(" %innerp = getelementptr [32 x i8], ptr %inner, i64 0, i64 0\n call void @lp_sha256_buf(ptr %inbuf, i64 %inlen, ptr %innerp)\n call void @free(ptr %inbuf)\n")
emith(" %innerp = getelementptr [32 x i8], ptr %inner, i64 0, i64 0\n call void @lp_sha256_buf(ptr %inbuf, i64 %inlen, ptr %innerp)\n call void @lp_free(ptr %inbuf)\n")
# digest = SHA-256( (K0 ^ opad) || inner ), opad = 0x5c
emith(" %outp = getelementptr [96 x i8], ptr %outbuf, i64 0, i64 0\n call void @lp_xor64(ptr %outp, ptr %k0p, i32 92)\n")
emith(" %outmsg = getelementptr i8, ptr %outbuf, i64 64\n call ptr @memcpy(ptr %outmsg, ptr %innerp, i64 32)\n")
@ -318,8 +318,8 @@ function emit_secure_rand_prelude() -> void {
# %n secure bytes -> a fresh 2n-char lowercase hex string
emith("define ptr @lp_random_hex(i64 %n) {\n")
emith("entry:\n %buf = call ptr @malloc(i64 %n)\n call void @lp_secure_bytes(ptr %buf, i64 %n)\n")
emith(" %hex = call ptr @lp_hex_encode(ptr %buf, i64 %n)\n call void @free(ptr %buf)\n ret ptr %hex\n}\n")
emith("entry:\n %buf = call ptr @lp_malloc(i64 %n)\n call void @lp_secure_bytes(ptr %buf, i64 %n)\n")
emith(" %hex = call ptr @lp_hex_encode(ptr %buf, i64 %n)\n call void @lp_free(ptr %buf)\n ret ptr %hex\n}\n")
# one CSPRNG-drawn i32 (little-endian assembly of four secure bytes)
emith("define i32 @lp_random_u32() {\n")
@ -336,8 +336,8 @@ function emit_secure_rand_prelude() -> void {
emith("define ptr @lp_base64(ptr %s) {\n")
emith("entry:\n %n = call i64 @strlen(ptr %s)\n")
emith(" %n2 = add i64 %n, 2\n %grp = udiv i64 %n2, 3\n %bufn = mul i64 %grp, 3\n")
emith(" %olen = mul i64 %grp, 4\n %olen1 = add i64 %olen, 1\n %out = call ptr @malloc(i64 %olen1)\n")
emith(" %inbuf = call ptr @malloc(i64 %bufn)\n call ptr @memset(ptr %inbuf, i32 0, i64 %bufn)\n call ptr @memcpy(ptr %inbuf, ptr %s, i64 %n)\n")
emith(" %olen = mul i64 %grp, 4\n %olen1 = add i64 %olen, 1\n %out = call ptr @lp_malloc(i64 %olen1)\n")
emith(" %inbuf = call ptr @lp_malloc(i64 %bufn)\n call ptr @memset(ptr %inbuf, i32 0, i64 %bufn)\n call ptr @memcpy(ptr %inbuf, ptr %s, i64 %n)\n")
emith(" %gp = alloca i64\n store i64 0, ptr %gp\n br label %cond\n")
emith("cond:\n %gi = load i64, ptr %gp\n %lt = icmp ult i64 %gi, %grp\n br i1 %lt, label %body, label %pad\n")
emith("body:\n %i3 = mul i64 %gi, 3\n")
@ -361,5 +361,5 @@ function emit_secure_rand_prelude() -> void {
emith("pad1:\n %pm1 = sub i64 %olen, 1\n %pp1 = getelementptr i8, ptr %out, i64 %pm1\n store i8 61, ptr %pp1\n %pm2 = sub i64 %olen, 2\n %pp2 = getelementptr i8, ptr %out, i64 %pm2\n store i8 61, ptr %pp2\n br label %done\n")
emith("maybe2:\n br i1 %r2, label %pad2, label %done\n")
emith("pad2:\n %qm1 = sub i64 %olen, 1\n %qp1 = getelementptr i8, ptr %out, i64 %qm1\n store i8 61, ptr %qp1\n br label %done\n")
emith("done:\n call void @free(ptr %inbuf)\n ret ptr %out\n}\n")
emith("done:\n call void @lp_free(ptr %inbuf)\n ret ptr %out\n}\n")
}

View file

@ -384,7 +384,7 @@ function emit_datetime_prelude() -> void {
emith("define ptr @lp_dt_pad0(i32 %v, i32 %w) {\n")
emith(" %we = zext i32 %w to i64\n")
emith(" %sz = add i64 %we, 1\n")
emith(" %buf = call ptr @malloc(i64 %sz)\n")
emith(" %buf = call ptr @lp_malloc(i64 %sz)\n")
emith(" %endp = getelementptr i8, ptr %buf, i32 %w\n")
emith(" store i8 0, ptr %endp\n")
emith(" %kp = alloca i32\n %vp = alloca i32\n")

View file

@ -112,7 +112,7 @@ function emit_fs_prelude() -> void {
function emit_fs_common() -> void {
# duplicate %n bytes of %s into a fresh NUL-terminated buffer
emith("define ptr @lp_fs_dup(ptr %s, i32 %n) {\n")
emith("entry:\n %nz = zext i32 %n to i64\n %t = add i64 %nz, 1\n %m = call ptr @malloc(i64 %t)\n call ptr @memcpy(ptr %m, ptr %s, i64 %nz)\n")
emith("entry:\n %nz = zext i32 %n to i64\n %t = add i64 %nz, 1\n %m = call ptr @lp_malloc(i64 %t)\n call ptr @memcpy(ptr %m, ptr %s, i64 %nz)\n")
emith(" %end = getelementptr i8, ptr %m, i32 %n\n store i8 0, ptr %end\n ret ptr %m\n}\n")
emith("define ptr @lp_fs_strdup(ptr %s) {\n")
@ -142,7 +142,7 @@ function emit_fs_path() -> void {
emith("kb:\n %bemp = icmp eq i32 %lb, 0\n br i1 %bemp, label %reta, label %chk\n")
emith("reta:\n %ra = call ptr @lp_fs_strdup(ptr %a)\n ret ptr %ra\n")
emith("chk:\n %lam1 = sub i32 %la, 1\n %pe = getelementptr i8, ptr %a, i32 %lam1\n %ae = load i8, ptr %pe\n %aei = zext i8 %ae to i32\n %ends = icmp eq i32 %aei, 47\n")
emith(" %sep = select i1 %ends, i32 0, i32 1\n %tot = add i32 %la, %lb\n %tot2 = add i32 %tot, %sep\n %tot3 = add i32 %tot2, 1\n %totz = zext i32 %tot3 to i64\n %m = call ptr @malloc(i64 %totz)\n")
emith(" %sep = select i1 %ends, i32 0, i32 1\n %tot = add i32 %la, %lb\n %tot2 = add i32 %tot, %sep\n %tot3 = add i32 %tot2, 1\n %totz = zext i32 %tot3 to i64\n %m = call ptr @lp_malloc(i64 %totz)\n")
emith(" %laz = zext i32 %la to i64\n call ptr @memcpy(ptr %m, ptr %a, i64 %laz)\n")
# write separator if needed
emith(" br i1 %ends, label %nosep, label %wsep\n")
@ -185,7 +185,7 @@ function emit_fs_path() -> void {
emith("entry:\n %base = call ptr @lp_path_base(ptr %s)\n %ext = call ptr @lp_path_ext(ptr %s)\n")
emith(" %bl64 = call i64 @strlen(ptr %base)\n %bl = trunc i64 %bl64 to i32\n %el64 = call i64 @strlen(ptr %ext)\n %el = trunc i64 %el64 to i32\n")
# the base and the extension are its own copies: freed once the answer is cut out of them
emith(" %keep = sub i32 %bl, %el\n %r = call ptr @lp_fs_dup(ptr %base, i32 %keep)\n call void @free(ptr %base)\n call void @free(ptr %ext)\n ret ptr %r\n}\n")
emith(" %keep = sub i32 %bl, %el\n %r = call ptr @lp_fs_dup(ptr %base, i32 %keep)\n call void @lp_free(ptr %base)\n call void @lp_free(ptr %ext)\n ret ptr %r\n}\n")
emit_fs_normalize()
}
@ -196,9 +196,9 @@ function emit_fs_path() -> void {
function emit_fs_normalize() -> void {
emith("define ptr @lp_path_norm(ptr %s) {\n")
emith("entry:\n %len64 = call i64 @strlen(ptr %s)\n %len = trunc i64 %len64 to i32\n %cap = add i32 %len, 2\n %capz = zext i32 %cap to i64\n")
emith(" %out = call ptr @malloc(i64 %capz)\n")
emith(" %out = call ptr @lp_malloc(i64 %capz)\n")
# segst holds output offsets (i32) of each kept segment's start; size cap ints
emith(" %stz = mul i32 %cap, 4\n %stzz = zext i32 %stz to i64\n %segst = call ptr @malloc(i64 %stzz)\n")
emith(" %stz = mul i32 %cap, 4\n %stzz = zext i32 %stz to i64\n %segst = call ptr @lp_malloc(i64 %stzz)\n")
emith(" %b0 = load i8, ptr %s\n %b0i = zext i8 %b0 to i32\n %abs = icmp eq i32 %b0i, 47\n")
emith(" %ip = alloca i32\n %op = alloca i32\n %scp = alloca i32\n store i32 0, ptr %ip\n store i32 0, ptr %scp\n")
emith(" br i1 %abs, label %ldr, label %noldr\n")
@ -239,8 +239,8 @@ function emit_fs_normalize() -> void {
emith("fdefault:\n br label %term\n")
emith("term:\n %eo = load i32, ptr %op\n %ez = icmp eq i32 %eo, 0\n br i1 %ez, label %emptyout, label %putnul\n")
# the segment table is this call's own: freed on both ways out
emith("emptyout:\n store i8 46, ptr %out\n %e1 = getelementptr i8, ptr %out, i32 1\n store i8 0, ptr %e1\n call void @free(ptr %segst)\n ret ptr %out\n")
emith("putnul:\n %ep = getelementptr i8, ptr %out, i32 %eo\n store i8 0, ptr %ep\n call void @free(ptr %segst)\n ret ptr %out\n}\n")
emith("emptyout:\n store i8 46, ptr %out\n %e1 = getelementptr i8, ptr %out, i32 1\n store i8 0, ptr %e1\n call void @lp_free(ptr %segst)\n ret ptr %out\n")
emith("putnul:\n %ep = getelementptr i8, ptr %out, i32 %eo\n store i8 0, ptr %ep\n call void @lp_free(ptr %segst)\n ret ptr %out\n}\n")
}
# ---- Fs.* (libc) -----------------------------------------------------------
@ -274,7 +274,7 @@ function emit_fs_io() -> void {
emith("entry:\n store i32 0, ptr %lenout\n %f = call ptr @lp_pak_open(ptr %p, ptr @fn_str_rb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %bad\n")
emith("bad:\n ret ptr null\n")
emith("ok:\n call i32 @fseek(ptr %f, i64 0, i32 2)\n %n64 = call i64 @ftell(ptr %f)\n call i32 @fseek(ptr %f, i64 0, i32 0)\n %n = trunc i64 %n64 to i32\n")
emith(" %cap = add i64 %n64, 1\n %m = call ptr @malloc(i64 %cap)\n %rd = call i64 @fread(ptr %m, i64 1, i64 %n64, ptr %f)\n call i32 @fclose(ptr %f)\n")
emith(" %cap = add i64 %n64, 1\n %m = call ptr @lp_malloc(i64 %cap)\n %rd = call i64 @fread(ptr %m, i64 1, i64 %n64, ptr %f)\n call i32 @fclose(ptr %f)\n")
emith(" %rdi = trunc i64 %rd to i32\n %endp = getelementptr i8, ptr %m, i32 %rdi\n store i8 0, ptr %endp\n store i32 %rdi, ptr %lenout\n ret ptr %m\n}\n")
emith("define ptr @lp_fs_read_text(ptr %p) {\n")
@ -285,14 +285,14 @@ function emit_fs_io() -> void {
emith("define i32 @lp_fs_write_text(ptr %p, ptr %s) {\n")
# the "<p>.tmp" name is this call's own and goes on both ways out
emith("entry:\n %tmp = call ptr @lp_path_join_ext(ptr %p, ptr @fn_str_dottmp)\n %f = call ptr @fopen(ptr %tmp, ptr @fn_str_wb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %bad\n")
emith("bad:\n call void @free(ptr %tmp)\n ret i32 0\n")
emith("bad:\n call void @lp_free(ptr %tmp)\n ret i32 0\n")
emith("ok:\n %n = call i64 @strlen(ptr %s)\n %w = call i64 @fwrite(ptr %s, i64 1, i64 %n, ptr %f)\n call i32 @fclose(ptr %f)\n")
emith(" %rr = call i32 @rename(ptr %tmp, ptr %p)\n call void @free(ptr %tmp)\n %ok2 = icmp eq i32 %rr, 0\n %z = zext i1 %ok2 to i32\n ret i32 %z\n}\n")
emith(" %rr = call i32 @rename(ptr %tmp, ptr %p)\n call void @lp_free(ptr %tmp)\n %ok2 = icmp eq i32 %rr, 0\n %z = zext i1 %ok2 to i32\n ret i32 %z\n}\n")
# concat two strings (used to build "<p>.tmp"); local so write_text needs no
# dependency on the Os prelude
emith("define ptr @lp_path_join_ext(ptr %a, ptr %b) {\n")
emith("entry:\n %la = call i64 @strlen(ptr %a)\n %lb = call i64 @strlen(ptr %b)\n %sum = add i64 %la, %lb\n %tot = add i64 %sum, 1\n %m = call ptr @malloc(i64 %tot)\n")
emith("entry:\n %la = call i64 @strlen(ptr %a)\n %lb = call i64 @strlen(ptr %b)\n %sum = add i64 %la, %lb\n %tot = add i64 %sum, 1\n %m = call ptr @lp_malloc(i64 %tot)\n")
emith(" call ptr @memcpy(ptr %m, ptr %a, i64 %la)\n %m2 = getelementptr i8, ptr %m, i64 %la\n call ptr @memcpy(ptr %m2, ptr %b, i64 %lb)\n %ep = getelementptr i8, ptr %m, i64 %sum\n store i8 0, ptr %ep\n ret ptr %m\n}\n")
emith("define i32 @lp_fs_append_text(ptr %p, ptr %s) {\n")
@ -309,8 +309,8 @@ function emit_fs_io() -> void {
emith("bad:\n ret i32 0\n")
# the whole file read and the "<dst>.tmp" name are this call's own: both go on every way out
emith("ok:\n %tmp = call ptr @lp_path_join_ext(ptr %dst, ptr @fn_str_dottmp)\n %f = call ptr @fopen(ptr %tmp, ptr @fn_str_wb)\n %fnz = icmp ne ptr %f, null\n br i1 %fnz, label %w, label %badw\n")
emith("badw:\n call void @free(ptr %buf)\n call void @free(ptr %tmp)\n ret i32 0\n")
emith("w:\n %n = load i32, ptr %lp\n %nz64 = zext i32 %n to i64\n call i64 @fwrite(ptr %buf, i64 1, i64 %nz64, ptr %f)\n call i32 @fclose(ptr %f)\n %rr = call i32 @rename(ptr %tmp, ptr %dst)\n call void @free(ptr %buf)\n call void @free(ptr %tmp)\n %ok2 = icmp eq i32 %rr, 0\n %z = zext i1 %ok2 to i32\n ret i32 %z\n}\n")
emith("badw:\n call void @lp_free(ptr %buf)\n call void @lp_free(ptr %tmp)\n ret i32 0\n")
emith("w:\n %n = load i32, ptr %lp\n %nz64 = zext i32 %n to i64\n call i64 @fwrite(ptr %buf, i64 1, i64 %nz64, ptr %f)\n call i32 @fclose(ptr %f)\n %rr = call i32 @rename(ptr %tmp, ptr %dst)\n call void @lp_free(ptr %buf)\n call void @lp_free(ptr %tmp)\n %ok2 = icmp eq i32 %rr, 0\n %z = zext i1 %ok2 to i32\n ret i32 %z\n}\n")
# mkdir: create %p and any missing parents (mkdir -p). Returns 1 if the
# directory exists afterwards. Intermediate EEXIST errors are ignored.
@ -320,7 +320,7 @@ function emit_fs_io() -> void {
emith("go:\n %sl = icmp eq i32 %ci, 47\n br i1 %sl, label %cut, label %nx\n")
emith("cut:\n store i8 0, ptr %pp\n call i32 @mkdir(ptr %dup, i32 493)\n store i8 47, ptr %pp\n br label %nx\n")
emith("nx:\n %i1 = add i32 %i, 1\n store i32 %i1, ptr %ip\n br label %lp\n")
emith("fin:\n call i32 @mkdir(ptr %dup, i32 493)\n call void @free(ptr %dup)\n %r = call i32 @lp_fs_is_dir(ptr %p)\n ret i32 %r\n}\n")
emith("fin:\n call i32 @mkdir(ptr %dup, i32 493)\n call void @lp_free(ptr %dup)\n %r = call i32 @lp_fs_is_dir(ptr %p)\n ret i32 %r\n}\n")
}
# ---- Fs.list (+ sort) ------------------------------------------------------
@ -330,8 +330,8 @@ function emit_fs_dir() -> void {
# the readdir buffer before the next call. macOS/BSD dirent: d_name at offset
# 21 (documented native layout).
emith("define ptr @lp_fs_list(ptr %path) {\n")
emith("entry:\n %h = call ptr @malloc(i64 16)\n %d0 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 0\n %d1 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 1\n %d2 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 2\n")
emith(" %datap = alloca ptr\n %cntp = alloca i32\n %capp = alloca i32\n %init = call ptr @malloc(i64 128)\n store ptr %init, ptr %datap\n store i32 0, ptr %cntp\n store i32 16, ptr %capp\n")
emith("entry:\n %h = call ptr @lp_malloc(i64 16)\n %d0 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 0\n %d1 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 1\n %d2 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 2\n")
emith(" %datap = alloca ptr\n %cntp = alloca i32\n %capp = alloca i32\n %init = call ptr @lp_malloc(i64 128)\n store ptr %init, ptr %datap\n store i32 0, ptr %cntp\n store i32 16, ptr %capp\n")
emith(" %dir = call ptr @opendir(ptr %path)\n %dnz = icmp ne ptr %dir, null\n br i1 %dnz, label %rd, label %empty\n")
emith("rd:\n %de = call ptr @readdir(ptr %dir)\n %denz = icmp ne ptr %de, null\n br i1 %denz, label %ent, label %closed\n")
emith("ent:\n %namep = getelementptr i8, ptr %de, i32 21\n")
@ -343,7 +343,7 @@ function emit_fs_dir() -> void {
emith("keep:\n %nm = call ptr @lp_fs_strdup(ptr %namep)\n")
# grow if full
emith(" %cnt = load i32, ptr %cntp\n %cap = load i32, ptr %capp\n %full = icmp sge i32 %cnt, %cap\n br i1 %full, label %grow, label %put\n")
emith("grow:\n %nc = mul i32 %cap, 2\n store i32 %nc, ptr %capp\n %ncz = zext i32 %nc to i64\n %nb = mul i64 %ncz, 8\n %old = load ptr, ptr %datap\n %new = call ptr @realloc(ptr %old, i64 %nb)\n store ptr %new, ptr %datap\n br label %put\n")
emith("grow:\n %nc = mul i32 %cap, 2\n store i32 %nc, ptr %capp\n %ncz = zext i32 %nc to i64\n %nb = mul i64 %ncz, 8\n %old = load ptr, ptr %datap\n %new = call ptr @lp_realloc(ptr %old, i64 %nb)\n store ptr %new, ptr %datap\n br label %put\n")
emith("put:\n %data = load ptr, ptr %datap\n %cnt2 = load i32, ptr %cntp\n %slot = getelementptr ptr, ptr %data, i32 %cnt2\n store ptr %nm, ptr %slot\n %cnt3 = add i32 %cnt2, 1\n store i32 %cnt3, ptr %cntp\n br label %rd\n")
emith("closed:\n call i32 @closedir(ptr %dir)\n br label %sortit\n")
emith("empty:\n br label %sortit\n")
@ -410,13 +410,13 @@ function emit_fs_mime() -> void {
emith("entry:\n %ext = call ptr @lp_path_ext(ptr %path)\n %lc = call ptr @lp_mime_lc(ptr %ext)\n %ip = alloca i32\n store i32 0, ptr %ip\n br label %lp\n")
emith(`lp:\n %i = load i32, ptr %ip\n %lt = icmp slt i32 %i, {itoa(n)}\n br i1 %lt, label %body, label %def\n`)
emith(`body:\n %kp = getelementptr [{itoa(n)} x {{ ptr, ptr }}], ptr @mime_tbl, i32 0, i32 %i, i32 0\n %k = load ptr, ptr %kp\n %c = call i32 @strcmp(ptr %lc, ptr %k)\n %eq = icmp eq i32 %c, 0\n br i1 %eq, label %hit, label %nx\n`)
emith(`hit:\n %vp = getelementptr [{itoa(n)} x {{ ptr, ptr }}], ptr @mime_tbl, i32 0, i32 %i, i32 1\n %v = load ptr, ptr %vp\n %r = call ptr @lp_fs_strdup(ptr %v)\n call void @free(ptr %ext)\n call void @free(ptr %lc)\n ret ptr %r\n`)
emith(`hit:\n %vp = getelementptr [{itoa(n)} x {{ ptr, ptr }}], ptr @mime_tbl, i32 0, i32 %i, i32 1\n %v = load ptr, ptr %vp\n %r = call ptr @lp_fs_strdup(ptr %v)\n call void @lp_free(ptr %ext)\n call void @lp_free(ptr %lc)\n ret ptr %r\n`)
emith("nx:\n %i1 = add i32 %i, 1\n store i32 %i1, ptr %ip\n br label %lp\n")
emith(`def:\n %d = call ptr @lp_fs_strdup(ptr {k_octet})\n call void @free(ptr %ext)\n call void @free(ptr %lc)\n ret ptr %d\n}}\n`)
emith(`def:\n %d = call ptr @lp_fs_strdup(ptr {k_octet})\n call void @lp_free(ptr %ext)\n call void @lp_free(ptr %lc)\n ret ptr %d\n}}\n`)
# lowercase an extension, dropping a leading '.' (ASCII only, for table lookup)
emith("define ptr @lp_mime_lc(ptr %s) {\n")
emith("entry:\n %l64 = call i64 @strlen(ptr %s)\n %l = trunc i64 %l64 to i32\n %cap = add i64 %l64, 1\n %out = call ptr @malloc(i64 %cap)\n")
emith("entry:\n %l64 = call i64 @strlen(ptr %s)\n %l = trunc i64 %l64 to i32\n %cap = add i64 %l64, 1\n %out = call ptr @lp_malloc(i64 %cap)\n")
emith(" %b0 = load i8, ptr %s\n %b0i = zext i8 %b0 to i32\n %isdot = icmp eq i32 %b0i, 46\n %start = select i1 %isdot, i32 1, i32 0\n")
emith(" %ip = alloca i32\n %op = alloca i32\n store i32 %start, ptr %ip\n store i32 0, ptr %op\n br label %lp\n")
emith("lp:\n %i = load i32, ptr %ip\n %pp = getelementptr i8, ptr %s, i32 %i\n %c = load i8, ptr %pp\n %ci = zext i8 %c to i32\n %z = icmp eq i32 %ci, 0\n br i1 %z, label %done, label %go\n")
@ -431,22 +431,22 @@ function emit_fs_mime() -> void {
function emit_fs_sniff() -> void {
emith("define ptr @lp_mime_sniff(ptr %path) {\n")
emith("entry:\n %f = call ptr @fopen(ptr %path, ptr @fn_str_rb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %fallback\n")
emith("ok:\n %buf = call ptr @malloc(i64 16)\n %rd = call i64 @fread(ptr %buf, i64 1, i64 8, ptr %f)\n call i32 @fclose(ptr %f)\n %rdi = trunc i64 %rd to i32\n %has4 = icmp sge i32 %rdi, 4\n br i1 %has4, label %chk, label %nomatch\n")
emith("ok:\n %buf = call ptr @lp_malloc(i64 16)\n %rd = call i64 @fread(ptr %buf, i64 1, i64 8, ptr %f)\n call i32 @fclose(ptr %f)\n %rdi = trunc i64 %rd to i32\n %has4 = icmp sge i32 %rdi, 4\n br i1 %has4, label %chk, label %nomatch\n")
emith("chk:\n %b0p = getelementptr i8, ptr %buf, i32 0\n %b0 = load i8, ptr %b0p\n %b0i = zext i8 %b0 to i32\n %b1p = getelementptr i8, ptr %buf, i32 1\n %b1 = load i8, ptr %b1p\n %b1i = zext i8 %b1 to i32\n %b2p = getelementptr i8, ptr %buf, i32 2\n %b2 = load i8, ptr %b2p\n %b2i = zext i8 %b2 to i32\n %b3p = getelementptr i8, ptr %buf, i32 3\n %b3 = load i8, ptr %b3p\n %b3i = zext i8 %b3 to i32\n")
# PNG: 89 50 4E 47
emith(" %p0 = icmp eq i32 %b0i, 137\n %p1 = icmp eq i32 %b1i, 80\n %p2 = icmp eq i32 %b2i, 78\n %p3 = icmp eq i32 %b3i, 71\n %pa = and i1 %p0, %p1\n %pb = and i1 %pa, %p2\n %pc = and i1 %pb, %p3\n br i1 %pc, label %png, label %cj\n")
emith("png:\n %rpng = call ptr @lp_fs_strdup(ptr @fn_sig_png)\n call void @free(ptr %buf)\n ret ptr %rpng\n")
emith("png:\n %rpng = call ptr @lp_fs_strdup(ptr @fn_sig_png)\n call void @lp_free(ptr %buf)\n ret ptr %rpng\n")
# JPEG: FF D8 FF
emith("cj:\n %j0 = icmp eq i32 %b0i, 255\n %j1 = icmp eq i32 %b1i, 216\n %j2 = icmp eq i32 %b2i, 255\n %ja = and i1 %j0, %j1\n %jb = and i1 %ja, %j2\n br i1 %jb, label %jpg, label %cg\n")
emith("jpg:\n %rjpg = call ptr @lp_fs_strdup(ptr @fn_sig_jpg)\n call void @free(ptr %buf)\n ret ptr %rjpg\n")
emith("jpg:\n %rjpg = call ptr @lp_fs_strdup(ptr @fn_sig_jpg)\n call void @lp_free(ptr %buf)\n ret ptr %rjpg\n")
# GIF: 47 49 46
emith("cg:\n %g0 = icmp eq i32 %b0i, 71\n %g1 = icmp eq i32 %b1i, 73\n %g2 = icmp eq i32 %b2i, 70\n %ga = and i1 %g0, %g1\n %gb = and i1 %ga, %g2\n br i1 %gb, label %gif, label %cp\n")
emith("gif:\n %rgif = call ptr @lp_fs_strdup(ptr @fn_sig_gif)\n call void @free(ptr %buf)\n ret ptr %rgif\n")
emith("gif:\n %rgif = call ptr @lp_fs_strdup(ptr @fn_sig_gif)\n call void @lp_free(ptr %buf)\n ret ptr %rgif\n")
# PDF: 25 50 44 46
emith("cp:\n %q0 = icmp eq i32 %b0i, 37\n %q1 = icmp eq i32 %b1i, 80\n %q2 = icmp eq i32 %b2i, 68\n %q3 = icmp eq i32 %b3i, 70\n %qa = and i1 %q0, %q1\n %qb = and i1 %qa, %q2\n %qc = and i1 %qb, %q3\n br i1 %qc, label %pdf, label %nomatch\n")
emith("pdf:\n %rpdf = call ptr @lp_fs_strdup(ptr @fn_sig_pdf)\n call void @free(ptr %buf)\n ret ptr %rpdf\n")
emith("pdf:\n %rpdf = call ptr @lp_fs_strdup(ptr @fn_sig_pdf)\n call void @lp_free(ptr %buf)\n ret ptr %rpdf\n")
# the 16 bytes read are this call's own and go on every way out
emith("nomatch:\n call void @free(ptr %buf)\n br label %fallback\n")
emith("nomatch:\n call void @lp_free(ptr %buf)\n br label %fallback\n")
emith("fallback:\n %r = call ptr @lp_mime_of(ptr %path)\n ret ptr %r\n}\n")
# the small string constants the Fs/Path/Mime runtime references

View file

@ -37,7 +37,7 @@ function list_grow_if_full(h: pointer, elt: pointer) -> void {
let ncw = emit_bind(`zext i32 {nc} to i64`)
let byts = emit_bind(`mul i64 {ncw}, {esz}`)
let old = emit_bind(`load ptr, ptr {dp}`)
let nd = emit_bind(`call ptr @realloc(ptr {old}, i64 {byts})`)
let nd = emit_bind(`call ptr @lp_realloc(ptr {old}, i64 {byts})`)
emit(" store ptr "); emit(nd); emit(", ptr "); emit(dp); emit("\n")
emit(" store i32 "); emit(nc); emit(", ptr "); emit(cp); emit("\n")
emit(" br label %"); emit(done); emit("\n")
@ -93,7 +93,7 @@ function emit_merge_sort(h: pointer, elt: pointer, mode: int, fnsym: pointer, ke
let esz = emit_sizeof(elt)
let nz = emit_bind(`zext i32 {n} to i64`)
let byts = emit_bind(`mul i64 {nz}, {esz}`)
let scratch = emit_bind(`call ptr @malloc(i64 {byts})`)
let scratch = emit_bind(`call ptr @lp_malloc(i64 {byts})`)
# outer: for (width = 1; width < n; width *= 2)
let wp = emit_alloca("i32"); store_at("i32", "1", wp)
@ -212,7 +212,7 @@ function emit_merge_sort(h: pointer, elt: pointer, mode: int, fnsym: pointer, ke
store_at("i32", w2, wp) # width *= 2
emit(" br label %"); emit(wc); emit("\n")
emit(we); emit(":\n")
emit(" call void @free(ptr "); emit(scratch); emit(")\n")
emit(" call void @lp_free(ptr "); emit(scratch); emit(")\n")
}
# resolve a sort_by/sort_with function argument (a bare identifier naming a

View file

@ -82,7 +82,7 @@ function emit_log_ns(meth: pointer, e: Node) -> Val {
i += 2
}
emit(` call void @lp_log_emit(i32 {lvl}, ptr {line.code})\n`)
emit(` call void @free(ptr {line.code})\n`)
emit(` call void @lp_free(ptr {line.code})\n`)
emit(` br label %{lend}\n`)
emit(`{lend}:\n`)
return val("0", "void")

View file

@ -164,13 +164,13 @@ function emit_os_prelude() -> void {
# concatenate two NUL-terminated strings into a fresh malloc'd buffer
emith("define ptr @lp_os_join2(ptr %a, ptr %b) {\n")
emith("entry:\n %la = call i64 @strlen(ptr %a)\n %lb = call i64 @strlen(ptr %b)\n")
emith(" %sum = add i64 %la, %lb\n %tot = add i64 %sum, 1\n %m = call ptr @malloc(i64 %tot)\n")
emith(" %sum = add i64 %la, %lb\n %tot = add i64 %sum, 1\n %m = call ptr @lp_malloc(i64 %tot)\n")
emith(" call ptr @memcpy(ptr %m, ptr %a, i64 %la)\n")
emith(" %m2 = getelementptr i8, ptr %m, i64 %la\n call ptr @memcpy(ptr %m2, ptr %b, i64 %lb)\n")
emith(" %end = getelementptr i8, ptr %m, i64 %sum\n store i8 0, ptr %end\n ret ptr %m\n}\n")
emith("define ptr @lp_os_join3(ptr %a, ptr %b, ptr %c) {\n")
emith("entry:\n %ab = call ptr @lp_os_join2(ptr %a, ptr %b)\n %r = call ptr @lp_os_join2(ptr %ab, ptr %c)\n call void @free(ptr %ab)\n ret ptr %r\n}\n")
emith("entry:\n %ab = call ptr @lp_os_join2(ptr %a, ptr %b)\n %r = call ptr @lp_os_join2(ptr %ab, ptr %c)\n call void @lp_free(ptr %ab)\n ret ptr %r\n}\n")
# Windows has its own known folders and no uname; emit_win.ludic supplies them
if g_target_win { emit_os_win_known(k_dot, k_slash); return }
@ -204,9 +204,9 @@ function emit_os_prelude() -> void {
emith("define i32 @lp_os_is_darwin() {\n")
emith("entry:\n %c = load i32, ptr @L_os_darwin\n %known = icmp sge i32 %c, 0\n br i1 %known, label %done, label %probe\n")
emith("done:\n ret i32 %c\n")
emith("probe:\n %buf = call ptr @malloc(i64 8192)\n call i32 @uname(ptr %buf)\n")
emith("probe:\n %buf = call ptr @lp_malloc(i64 8192)\n call i32 @uname(ptr %buf)\n")
emith(` %cd = call i32 @strncmp(ptr %buf, ptr {k_darw}, i64 6)\n %isd = icmp eq i32 %cd, 0\n`)
emith(" %r = select i1 %isd, i32 1, i32 0\n call void @free(ptr %buf)\n store i32 %r, ptr @L_os_darwin\n ret i32 %r\n}\n")
emith(" %r = select i1 %isd, i32 1, i32 0\n call void @lp_free(ptr %buf)\n store i32 %r, ptr @L_os_darwin\n ret i32 %r\n}\n")
# $<var> when it is set and non-empty, else $HOME<rel>; the result always ends in '/'
emith("define ptr @lp_os_xdg(ptr %var, ptr %rel) {\n")
@ -219,7 +219,7 @@ function emit_os_prelude() -> void {
emith("define ptr @lp_os_known(ptr %app, ptr %mac, ptr %var, ptr %rel) {\n")
emith("entry:\n %d = call i32 @lp_os_is_darwin()\n %isd = icmp ne i32 %d, 0\n br i1 %isd, label %m, label %x\n")
emith("m:\n %h = call ptr @lp_os_home()\n %r1 = call ptr @lp_os_join3(ptr %h, ptr %mac, ptr %app)\n ret ptr %r1\n")
emith("x:\n %b = call ptr @lp_os_xdg(ptr %var, ptr %rel)\n %r2 = call ptr @lp_os_join2(ptr %b, ptr %app)\n call void @free(ptr %b)\n ret ptr %r2\n}\n")
emith("x:\n %b = call ptr @lp_os_xdg(ptr %var, ptr %rel)\n %r2 = call ptr @lp_os_join2(ptr %b, ptr %app)\n call void @lp_free(ptr %b)\n ret ptr %r2\n}\n")
emith(`define ptr @lp_os_save_dir(ptr %app) {{\n %r = call ptr @lp_os_known(ptr %app, ptr {k_appsp}, ptr {k_xdg_data}, ptr {k_dot_share})\n ret ptr %r\n}}\n`)
emith(`define ptr @lp_os_config_dir(ptr %app) {{\n %r = call ptr @lp_os_known(ptr %app, ptr {k_appsp}, ptr {k_xdg_cfg}, ptr {k_dot_config})\n ret ptr %r\n}}\n`)
@ -230,7 +230,7 @@ function emit_os_prelude() -> void {
# snapshot the caller may iterate or index like any other []string.
emith("define ptr @lp_os_args() {\n")
emith("entry:\n %c = load i32, ptr @L_argc\n %v = load ptr, ptr @L_argv\n")
emith(" %h = call ptr @malloc(i64 16)\n")
emith(" %h = call ptr @lp_malloc(i64 16)\n")
emith(" %d0 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 0\n store ptr %v, ptr %d0\n")
emith(" %d1 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 1\n store i32 %c, ptr %d1\n")
emith(" %d2 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 2\n store i32 %c, ptr %d2\n")
@ -242,7 +242,7 @@ function emit_os_prelude() -> void {
emith("define ptr @lp_os_uts() {\n")
emith("entry:\n %c = load ptr, ptr @L_os_uts\n %have = icmp ne ptr %c, null\n br i1 %have, label %done, label %make\n")
emith("done:\n ret ptr %c\n")
emith("make:\n %buf = call ptr @malloc(i64 8192)\n call i32 @uname(ptr %buf)\n store ptr %buf, ptr @L_os_uts\n ret ptr %buf\n}\n")
emith("make:\n %buf = call ptr @lp_malloc(i64 8192)\n call i32 @uname(ptr %buf)\n store ptr %buf, ptr @L_os_uts\n ret ptr %buf\n}\n")
# platform(): uname sysname (field 0, portable) mapped to a short id
emith("define ptr @lp_os_platform() {\n")
@ -264,7 +264,7 @@ function emit_os_prelude() -> void {
# Os.heap_bytes: malloc_zone_statistics' in-use bytes, all zones. The symbol is weak, so a libc
# without it links and reads 0.
function emit_os_heap() -> void {
emith("declare extern_weak void @malloc_zone_statistics(ptr, ptr)\n")
if not g_mzs_declared { emith("declare extern_weak void @malloc_zone_statistics(ptr, ptr)\n"); g_mzs_declared = true }
emith("define i64 @lp_os_heap_bytes() {\n")
emith("entry:\n %has = icmp ne ptr @malloc_zone_statistics, null\n br i1 %has, label %read, label %none\n")
emith("none:\n ret i64 0\n")

View file

@ -234,7 +234,7 @@ function emit_pak_boot() -> void {
emith("define ptr @lp_pak_slurp(ptr %path) {\n")
emith("entry:\n %f = call ptr @fopen(ptr %path, ptr @.pak_rb)\n %bad = icmp eq ptr %f, null\n br i1 %bad, label %no, label %go\n")
emith("go:\n %s1 = call i32 @fseek(ptr %f, i64 0, i32 2)\n %n64 = call i64 @ftell(ptr %f)\n %s2 = call i32 @fseek(ptr %f, i64 0, i32 0)\n")
emith(" %cap = add i64 %n64, 1\n %buf = call ptr @malloc(i64 %cap)\n %rd = call i64 @fread(ptr %buf, i64 1, i64 %n64, ptr %f)\n")
emith(" %cap = add i64 %n64, 1\n %buf = call ptr @lp_malloc(i64 %cap)\n %rd = call i64 @fread(ptr %buf, i64 1, i64 %n64, ptr %f)\n")
emith(" %e = getelementptr i8, ptr %buf, i64 %rd\n store i8 0, ptr %e\n %c = call i32 @fclose(ptr %f)\n ret ptr %buf\n")
emith("no:\n ret ptr null\n}\n")
@ -324,9 +324,9 @@ function emit_pak_boot() -> void {
emith("entry:\n %f = call ptr @lp_pak_open(ptr %path, ptr @.pak_rb)\n %bad = icmp eq ptr %f, null\n br i1 %bad, label %out, label %go\n")
emith("go:\n %s1 = call i32 @fseek(ptr %f, i64 0, i32 2)\n %n64 = call i64 @ftell(ptr %f)\n %s2 = call i32 @fseek(ptr %f, i64 0, i32 0)\n")
emith(" %tiny = icmp sle i64 %n64, 0\n br i1 %tiny, label %shut, label %read\n")
emith("read:\n %buf = call ptr @malloc(i64 %n64)\n %rd = call i64 @fread(ptr %buf, i64 1, i64 %n64, ptr %f)\n")
emith("read:\n %buf = call ptr @lp_malloc(i64 %n64)\n %rd = call i64 @fread(ptr %buf, i64 1, i64 %n64, ptr %f)\n")
emith(" %c1 = call i32 @fclose(ptr %f)\n %n32 = trunc i64 %rd to i32\n")
emith(" call void @app_set_icon(ptr %buf, i32 %n32)\n call void @free(ptr %buf)\n ret void\n")
emith(" call void @app_set_icon(ptr %buf, i32 %n32)\n call void @lp_free(ptr %buf)\n ret void\n")
emith("shut:\n %c2 = call i32 @fclose(ptr %f)\n ret void\n")
emith("out:\n ret void\n}\n")
}

View file

@ -19,7 +19,7 @@ function is_text_ns(meth: pointer) -> bool {
# read: Text.to_int(string(n)) kept n's text. Every string a Text call returns is a copy of its own,
# so it is fresh and goes once a +, == or print has read it.
function text_gone(v: Val) -> void {
if v.fresh { emit(` call void @free(ptr {v.code})\n`) }
if v.fresh { emit(` call void @lp_free(ptr {v.code})\n`) }
}
function emit_text_ns(meth: pointer, e: Node) -> Val {

View file

@ -7,7 +7,7 @@ function emit_text_prelude() -> void {
emith("entry:\n")
emith(" %n = call i64 @strlen(ptr %s)\n")
emith(" %sz = add i64 %n, 1\n")
emith(" %out = call ptr @malloc(i64 %sz)\n")
emith(" %out = call ptr @lp_malloc(i64 %sz)\n")
emith(" %ip = alloca i64\n")
emith(" store i64 0, ptr %ip\n")
emith(" br label %cond\n")
@ -37,7 +37,7 @@ function emit_text_prelude() -> void {
emith("entry:\n")
emith(" %n = call i64 @strlen(ptr %s)\n")
emith(" %sz = add i64 %n, 1\n")
emith(" %out = call ptr @malloc(i64 %sz)\n")
emith(" %out = call ptr @lp_malloc(i64 %sz)\n")
emith(" %ip = alloca i64\n")
emith(" store i64 0, ptr %ip\n")
emith(" br label %cond\n")
@ -104,7 +104,7 @@ function emit_text_prelude() -> void {
emith(" %en = load i64, ptr %ep\n")
emith(" %len = sub i64 %en, %st\n")
emith(" %sz = add i64 %len, 1\n")
emith(" %out = call ptr @malloc(i64 %sz)\n")
emith(" %out = call ptr @lp_malloc(i64 %sz)\n")
emith(" %src = getelementptr i8, ptr %s, i64 %st\n")
emith(" call ptr @memcpy(ptr %out, ptr %src, i64 %len)\n")
emith(" %tp = getelementptr i8, ptr %out, i64 %len\n")
@ -129,7 +129,7 @@ function emit_text_prelude() -> void {
emith(" %L = call i64 @strlen(ptr %s)\n")
emith(" %tot = mul i64 %L, %n\n")
emith(" %sz = add i64 %tot, 1\n")
emith(" %out = call ptr @malloc(i64 %sz)\n")
emith(" %out = call ptr @lp_malloc(i64 %sz)\n")
emith(" %ip = alloca i64\n")
emith(" store i64 0, ptr %ip\n")
emith(" br label %cond\n")
@ -157,7 +157,7 @@ function emit_text_prelude() -> void {
emith(" br i1 %need, label %do, label %copy\n")
emith("copy:\n")
emith(" %sz0 = add i64 %L, 1\n")
emith(" %o0 = call ptr @malloc(i64 %sz0)\n")
emith(" %o0 = call ptr @lp_malloc(i64 %sz0)\n")
emith(" %e0 = add i64 %L, 0\n")
emith(" call ptr @memcpy(ptr %o0, ptr %s, i64 %L)\n")
emith(" %t0 = getelementptr i8, ptr %o0, i64 %L\n")
@ -166,7 +166,7 @@ function emit_text_prelude() -> void {
emith("do:\n")
emith(" %pad = sub i64 %w0, %L\n")
emith(" %sz = add i64 %w0, 1\n")
emith(" %out = call ptr @malloc(i64 %sz)\n")
emith(" %out = call ptr @lp_malloc(i64 %sz)\n")
emith(" br i1 %left, label %padleft, label %padright\n")
emith("padleft:\n")
emith(" call void @lp_fill_sp(ptr %out, i64 0, i64 %pad)\n")
@ -216,7 +216,7 @@ function emit_text2_prelude() -> void {
emith("copyall:\n")
emith(" %ls0 = call i64 @strlen(ptr %s)\n")
emith(" %sz0 = add i64 %ls0, 1\n")
emith(" %o0 = call ptr @malloc(i64 %sz0)\n")
emith(" %o0 = call ptr @lp_malloc(i64 %sz0)\n")
emith(" call ptr @memcpy(ptr %o0, ptr %s, i64 %sz0)\n")
emith(" ret ptr %o0\n")
emith("scan:\n")
@ -245,7 +245,7 @@ function emit_text2_prelude() -> void {
emith(" %grow = mul i64 %cnt, %delta\n")
emith(" %newlen = add i64 %ls, %grow\n")
emith(" %osz = add i64 %newlen, 1\n")
emith(" %out = call ptr @malloc(i64 %osz)\n")
emith(" %out = call ptr @lp_malloc(i64 %osz)\n")
emith(" %srcp = alloca ptr\n")
emith(" store ptr %s, ptr %srcp\n")
emith(" %dstp = alloca ptr\n")
@ -312,7 +312,7 @@ function emit_text2_prelude() -> void {
emith(" %tot0 = load i64, ptr %totp\n")
emith(" %tot = add i64 %tot0, %sepsz\n")
emith(" %osz = add i64 %tot, 1\n")
emith(" %out = call ptr @malloc(i64 %osz)\n")
emith(" %out = call ptr @lp_malloc(i64 %osz)\n")
emith(" %dstp = alloca ptr\n")
emith(" store ptr %out, ptr %dstp\n")
emith(" %jp = alloca i64\n")
@ -369,9 +369,9 @@ function emit_text2_prelude() -> void {
emith(" br label %ccond\n")
emith("mk:\n")
emith(" %cnt = load i64, ptr %cntp\n")
emith(" %hdr = call ptr @malloc(i64 16)\n")
emith(" %hdr = call ptr @lp_malloc(i64 16)\n")
emith(" %arrsz = mul i64 %cnt, 8\n")
emith(" %arr = call ptr @malloc(i64 %arrsz)\n")
emith(" %arr = call ptr @lp_malloc(i64 %arrsz)\n")
emith(" %d0 = getelementptr inbounds %LSlice, ptr %hdr, i32 0, i32 0\n")
emith(" store ptr %arr, ptr %d0\n")
emith(" %cnt32 = trunc i64 %cnt to i32\n")
@ -404,7 +404,7 @@ function emit_text2_prelude() -> void {
emith("store:\n")
emith(" %seg = phi i64 [ %seglen, %mid ], [ %rem, %last ]\n")
emith(" %ssz = add i64 %seg, 1\n")
emith(" %sub = call ptr @malloc(i64 %ssz)\n")
emith(" %sub = call ptr @lp_malloc(i64 %ssz)\n")
emith(" %src2 = load ptr, ptr %srcp\n")
emith(" call ptr @memcpy(ptr %sub, ptr %src2, i64 %seg)\n")
emith(" %tp = getelementptr i8, ptr %sub, i64 %seg\n")

View file

@ -248,8 +248,8 @@ function emit_uni_str_fns() -> void {
# chars(s) -> []int : a fresh %LSlice of every code point, in order
emith("define ptr @lp_uni_chars(ptr %s) {\n")
emith("entry:\n %n = call i32 @lp_uni_len(ptr %s)\n %h = call ptr @malloc(i64 16)\n")
emith(" %nz = zext i32 %n to i64\n %bytes = mul i64 %nz, 4\n %data = call ptr @malloc(i64 %bytes)\n")
emith("entry:\n %n = call i32 @lp_uni_len(ptr %s)\n %h = call ptr @lp_malloc(i64 16)\n")
emith(" %nz = zext i32 %n to i64\n %bytes = mul i64 %nz, 4\n %data = call ptr @lp_malloc(i64 %bytes)\n")
emith(" %d0 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 0\n store ptr %data, ptr %d0\n")
emith(" %d1 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 1\n store i32 %n, ptr %d1\n")
emith(" %d2 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 2\n store i32 %n, ptr %d2\n")
@ -264,7 +264,7 @@ function emit_uni_str_fns() -> void {
# maps each code point, and re-encodes into a fresh buffer (worst case 4 bytes
# per code point, though ASCII/Latin-1 mapping preserves byte length).
emith("define ptr @lp_uni_case(ptr %s, i32 %up) {\n")
emith("entry:\n %bl = call i64 @strlen(ptr %s)\n %cap0 = mul i64 %bl, 4\n %cap = add i64 %cap0, 4\n %out = call ptr @malloc(i64 %cap)\n")
emith("entry:\n %bl = call i64 @strlen(ptr %s)\n %cap0 = mul i64 %bl, 4\n %cap = add i64 %cap0, 4\n %out = call ptr @lp_malloc(i64 %cap)\n")
emith(" %ip = alloca i32\n %op = alloca i32\n %cp = alloca i32\n store i32 0, ptr %ip\n store i32 0, ptr %op\n br label %lp\n")
emith("lp:\n %i = load i32, ptr %ip\n %p = getelementptr i8, ptr %s, i32 %i\n %b = load i8, ptr %p\n %c = zext i8 %b to i32\n")
emith(" %z = icmp eq i32 %c, 0\n br i1 %z, label %done, label %go\n")
@ -281,7 +281,7 @@ function emit_uni_str_fns() -> void {
emith("chk:\n %i = load i32, ptr %ip\n %p = getelementptr i8, ptr %s, i32 %i\n %b = load i8, ptr %p\n %c = zext i8 %b to i32\n")
emith(" %z = icmp eq i32 %c, 0\n br i1 %z, label %cut, label %go\n")
emith("go:\n %ni = call i32 @lp_uni_decode(ptr %s, i32 %i, ptr %cp)\n %k1 = add i32 %k, 1\n store i32 %k1, ptr %kp\n store i32 %ni, ptr %ip\n br label %lp\n")
emith("cut:\n %len = load i32, ptr %ip\n %lz = zext i32 %len to i64\n %cap = add i64 %lz, 1\n %out = call ptr @malloc(i64 %cap)\n %lz2 = zext i32 %len to i64\n call ptr @memcpy(ptr %out, ptr %s, i64 %lz2)\n")
emith("cut:\n %len = load i32, ptr %ip\n %lz = zext i32 %len to i64\n %cap = add i64 %lz, 1\n %out = call ptr @lp_malloc(i64 %cap)\n %lz2 = zext i32 %len to i64\n call ptr @memcpy(ptr %out, ptr %s, i64 %lz2)\n")
emith(" %endp = getelementptr i8, ptr %out, i32 %len\n store i8 0, ptr %endp\n ret ptr %out\n}\n")
emit_uni_grapheme()

View file

@ -72,7 +72,7 @@ function emit_uuid_prelude() -> void {
# 16 raw bytes -> a fresh canonical 36-char string. hex-encode all 16 bytes,
# then splice the four hyphens between the 8/4/4/4/12 groups.
emith("define ptr @lp_uuid_format(ptr %b16) {\n")
emith("entry:\n %hex = call ptr @lp_hex_encode(ptr %b16, i64 16)\n %out = call ptr @malloc(i64 37)\n")
emith("entry:\n %hex = call ptr @lp_hex_encode(ptr %b16, i64 16)\n %out = call ptr @lp_malloc(i64 37)\n")
emith(" call ptr @memcpy(ptr %out, ptr %hex, i64 8)\n")
emith(" %o8 = getelementptr i8, ptr %out, i64 8\n store i8 45, ptr %o8\n")
emith(" %h8 = getelementptr i8, ptr %hex, i64 8\n %o9 = getelementptr i8, ptr %out, i64 9\n call ptr @memcpy(ptr %o9, ptr %h8, i64 4)\n")
@ -83,7 +83,7 @@ function emit_uuid_prelude() -> void {
emith(" %o23 = getelementptr i8, ptr %out, i64 23\n store i8 45, ptr %o23\n")
emith(" %h20 = getelementptr i8, ptr %hex, i64 20\n %o24 = getelementptr i8, ptr %out, i64 24\n call ptr @memcpy(ptr %o24, ptr %h20, i64 12)\n")
emith(" %o36 = getelementptr i8, ptr %out, i64 36\n store i8 0, ptr %o36\n")
emith(" call void @free(ptr %hex)\n ret ptr %out\n}\n")
emith(" call void @lp_free(ptr %hex)\n ret ptr %out\n}\n")
# v4: 16 CSPRNG bytes, then set version (0x4x in byte 6) and variant (0b10xx in
# byte 8). 0x80 does not fit an i8 immediate, so it is written as -128.
@ -112,7 +112,7 @@ function emit_uuid_prelude() -> void {
# the nil UUID: 36 '0' with hyphens spliced in
emith("define ptr @lp_uuid_nil() {\n")
emith("entry:\n %out = call ptr @malloc(i64 37)\n call ptr @memset(ptr %out, i32 48, i64 36)\n")
emith("entry:\n %out = call ptr @lp_malloc(i64 37)\n call ptr @memset(ptr %out, i32 48, i64 36)\n")
emith(" %o8 = getelementptr i8, ptr %out, i64 8\n store i8 45, ptr %o8\n")
emith(" %o13 = getelementptr i8, ptr %out, i64 13\n store i8 45, ptr %o13\n")
emith(" %o18 = getelementptr i8, ptr %out, i64 18\n store i8 45, ptr %o18\n")
@ -153,7 +153,7 @@ function emit_uuid_prelude() -> void {
# parse: lowercase-normalise a valid UUID, else return the nil UUID.
emith("define ptr @lp_uuid_parse(ptr %s) {\n")
emith("entry:\n %ok = call i32 @lp_uuid_valid(ptr %s)\n %isok = icmp ne i32 %ok, 0\n br i1 %isok, label %dup, label %nilb\n")
emith("dup:\n %out = call ptr @malloc(i64 37)\n %ip = alloca i64\n store i64 0, ptr %ip\n br label %cond\n")
emith("dup:\n %out = call ptr @lp_malloc(i64 37)\n %ip = alloca i64\n store i64 0, ptr %ip\n br label %cond\n")
emith("cond:\n %i = load i64, ptr %ip\n %lt = icmp ult i64 %i, 36\n br i1 %lt, label %body, label %fin\n")
emith("body:\n %p = getelementptr i8, ptr %s, i64 %i\n %c = load i8, ptr %p\n")
emith(" %cA = icmp uge i8 %c, 65\n %cZ = icmp ule i8 %c, 90\n %cup = and i1 %cA, %cZ\n %c32 = add i8 %c, 32\n %cl = select i1 %cup, i8 %c32, i8 %c\n")

View file

@ -146,11 +146,11 @@ function emit_win_files() -> void {
# returns block + 39 so that d_name, read at +21, is cFileName itself.
function emit_win_dirs() -> void {
emith("define internal ptr @opendir(ptr %path) {\n")
emith("entry:\n %l = call i64 @strlen(ptr %path)\n %cap = add i64 %l, 3\n %pat = call ptr @malloc(i64 %cap)\n call ptr @memcpy(ptr %pat, ptr %path, i64 %l)\n")
emith("entry:\n %l = call i64 @strlen(ptr %path)\n %cap = add i64 %l, 3\n %pat = call ptr @lp_malloc(i64 %cap)\n call ptr @memcpy(ptr %pat, ptr %path, i64 %l)\n")
emith(" %e0 = getelementptr i8, ptr %pat, i64 %l\n store i8 47, ptr %e0\n %e1 = getelementptr i8, ptr %e0, i64 1\n store i8 42, ptr %e1\n %e2 = getelementptr i8, ptr %e0, i64 2\n store i8 0, ptr %e2\n")
emith(" %d = call ptr @malloc(i64 368)\n %fd = getelementptr i8, ptr %d, i64 16\n %h = call ptr @FindFirstFileA(ptr %pat, ptr %fd)\n call void @free(ptr %pat)\n")
emith(" %d = call ptr @lp_malloc(i64 368)\n %fd = getelementptr i8, ptr %d, i64 16\n %h = call ptr @FindFirstFileA(ptr %pat, ptr %fd)\n call void @lp_free(ptr %pat)\n")
emith(" %bad = icmp eq ptr %h, inttoptr (i64 -1 to ptr)\n br i1 %bad, label %no, label %ok\n")
emith("no:\n call void @free(ptr %d)\n ret ptr null\n")
emith("no:\n call void @lp_free(ptr %d)\n ret ptr null\n")
emith("ok:\n store ptr %h, ptr %d\n %fl = getelementptr i8, ptr %d, i64 8\n store i32 1, ptr %fl\n ret ptr %d\n}\n")
emith("define internal ptr @readdir(ptr %d) {\n")
@ -160,7 +160,7 @@ function emit_win_dirs() -> void {
emith("give:\n %de = getelementptr i8, ptr %d, i64 39\n ret ptr %de\n")
emith("end:\n ret ptr null\n}\n")
emith("define internal i32 @closedir(ptr %d) {\nentry:\n %h = load ptr, ptr %d\n %r = call i32 @FindClose(ptr %h)\n call void @free(ptr %d)\n ret i32 0\n}\n")
emith("define internal i32 @closedir(ptr %d) {\nentry:\n %h = load ptr, ptr %d\n %r = call i32 @FindClose(ptr %h)\n call void @lp_free(ptr %d)\n ret i32 0\n}\n")
}
# ---- mapped files and memory streams -------------------------------------------
@ -247,7 +247,7 @@ function emit_os_win_known(k_dot: pointer, k_slash: pointer) -> void {
# Os.args(), as emit_os_prelude defines it
emith("define ptr @lp_os_args() {\n")
emith("entry:\n %c = load i32, ptr @L_argc\n %v = load ptr, ptr @L_argv\n")
emith(" %h = call ptr @malloc(i64 16)\n")
emith(" %h = call ptr @lp_malloc(i64 16)\n")
emith(" %d0 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 0\n store ptr %v, ptr %d0\n")
emith(" %d1 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 1\n store i32 %c, ptr %d1\n")
emith(" %d2 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 2\n store i32 %c, ptr %d2\n")

View file

@ -174,6 +174,7 @@ entry {
else if a == "--natives-out" { ai += 1; if ai < arg_count() { natives_out = arg(ai) } }
else if a == "--link" { ai += 1; if ai < arg_count() { push(links, arg(ai)) } }
else if a == "-o" { ai += 1; if ai < arg_count() { out = arg(ai) } }
else if fence_flag(a) { } # 25.1: --fence=, --fence-warm=, --fence-census=
else if a[0] == '-' {
# an unknown flag is ignored (with a note) rather than mistaken for the input file
let m = `ludicc: ignoring unknown flag {a}\n`
@ -209,6 +210,7 @@ entry {
if fmt { exit(0) }
maybe_splice_runtime()
fence_manifest(path) # 25.1: a `fence` line in the program's package.ludic
ui_register_gotos() # `goto:` buttons -> generated UiClicked listeners
# a game gets a window by default; a plain program stays headless. An explicit

View file

@ -42,6 +42,8 @@ function selfhost_frags() -> []pointer {
push(f, "selfhost/frontend/component_gen2.ludic")
push(f, "selfhost/frontend/component_class.ludic")
push(f, "selfhost/backend/emit_core.ludic")
push(f, "selfhost/backend/emit_fence.ludic")
push(f, "selfhost/backend/emit_fence_ir.ludic")
push(f, "selfhost/backend/emit_head.ludic")
push(f, "selfhost/backend/emit_deps.ludic")
push(f, "selfhost/backend/emit_addr.ludic")

View file

@ -787,6 +787,36 @@ function panic_case() -> void {
else { bad2("panic", `rc={string(rc)} err=[{msg}]`) }
}
# 25.1: the fence fails a frame that keeps memory once play has settled - exit 86, naming the line and
# the kind - and under R3D_ALLOC_FENCE=count runs on and says what was kept
function alloc_fence_leak_case() -> void {
let lbl = "alloc_fence_leak.ludic (a frame that keeps a record fails the run with exit 86 and names its line; count mode runs on)"
let b = `{tmp_dir()}/e_alloc_fence_leak`
if not game_build_ok("bin/ludicc", "examples/lang/alloc_fence_leak.ludic", b) { bad2(lbl, "did not build"); return }
let rc = sh(`{b} < /dev/null > {tmp_dir()}/afl.out 2>{tmp_dir()}/afl.err`)
let head = capture_line(`grep 'alloc-fence: frame' {tmp_dir()}/afl.err | head -1`)
let named = shq(`grep -q 'alloc_fence_leak.ludic:11 new Box' {tmp_dir()}/afl.err`)
let counted = capture_line(`R3D_ALLOC_FENCE=count {b} < /dev/null`)
if (rc == 86) and (head == "alloc-fence: frame 50 kept +80 B (2 made, 0 freed)") and named and (counted == "kept 80 bad 1") { ok(lbl) }
else { bad2(lbl, `rc={string(rc)} head=[{head}] named={string(named)} count=[{counted}]`) }
}
# 25.1: with nothing but the frame's mark the fence starts itself, waits out a loading that grows,
# judges once 600 frames in a row kept nothing, and fails the frame that keeps a record; --fence=count
# builds a run that goes on
function alloc_fence_auto_case() -> void {
let lbl = "alloc_fence_auto.ludic (the fence starts at the first frame, judges once play is flat, fails frame 1500; --fence=count runs on)"
let b = `{tmp_dir()}/e_alloc_fence_auto`
if not game_build_ok("bin/ludicc", "examples/lang/alloc_fence_auto.ludic", b) { bad2(lbl, "did not build"); return }
let rc = sh(`{b} < /dev/null > {tmp_dir()}/afa.out 2>{tmp_dir()}/afa.err`)
let head = capture_line(`grep 'alloc-fence: frame' {tmp_dir()}/afa.err | head -1`)
let bc = `{tmp_dir()}/e_alloc_fence_auto_c`
var through = ""
if shq(`bin/ludicc --fence=count examples/lang/alloc_fence_auto.ludic -o {bc} 2>/dev/null`) { through = capture_line(`{bc} < /dev/null`) }
if (rc == 86) and (head == "alloc-fence: frame 1500 kept +16 B (1 made, 0 freed)") and (through == "through") { ok(lbl) }
else { bad2(lbl, `rc={string(rc)} head=[{head}] count=[{through}]`) }
}
# issue #45: `bin/ludic-dev test --coverage`. Compile each test-spec with `--coverage`,
# run it with LUDIC_COVERAGE pointed at a per-file dump, then aggregate the dumps
# into a clean per-file line-coverage report. The instrumentation is flag-gated,
@ -1082,6 +1112,9 @@ function cmd_dev_test() -> int {
feat_case("lang/runtime_temps", "", "1 2 3 4 5 grew 0 files grew 0", "runtime_temps.ludic (Log, DateTime.format, Path, Mime, Text, Os and Fs keep nothing a call does not hand back)")
feat_case("lang/string_temps", "", "1 2 3 4 5 6 7 grew 0", "string_temps.ludic (a concatenation's pieces, a template's holes and a compared side are freed once used; a kept one is not)")
feat_case("lang/json_saves", "", "1 2 3 saves grew 0", "json_saves.ludic (Json.write_file saves through a kept buffer: the text Json.encode gives, 1000 saves keep nothing)")
feat_case("lang/alloc_fence", "", "frames 300 kept 0 bad 0", "alloc_fence.ludic (25.1: a frame that makes only what it frees or reuses keeps nothing, and the fence passes it)")
alloc_fence_leak_case()
alloc_fence_auto_case()
feat_case("lang/nested_templates", "", "outer [inner 3 {x}] \"`}\" end", "nested_templates.ludic (a template literal inside another's hole)")
feat_case("lang/aliases", "", "10 5 3", "aliases.ludic (L6: a namespace method declared as an alias of a function, labelled or by its parameters)")
reject_case("rejected/alias_arity", "this call to Trail.length leaves out to, which has no default", "an alias's arguments are checked against its target")