diff --git a/examples/lang/alloc_fence.ludic b/examples/lang/alloc_fence.ludic new file mode 100644 index 00000000..e52fb8fd --- /dev/null +++ b/examples/lang/alloc_fence.ludic @@ -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()}`) + } +} diff --git a/examples/lang/alloc_fence_auto.ludic b/examples/lang/alloc_fence_auto.ludic new file mode 100644 index 00000000..1307a390 --- /dev/null +++ b/examples/lang/alloc_fence_auto.ludic @@ -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") + } +} diff --git a/examples/lang/alloc_fence_leak.ludic b/examples/lang/alloc_fence_leak.ludic new file mode 100644 index 00000000..d245bd3b --- /dev/null +++ b/examples/lang/alloc_fence_leak.ludic @@ -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()}`) + } +} diff --git a/selfhost/backend/emit_call.ludic b/selfhost/backend/emit_call.ludic index 82b3a51c..2f750a8d 100644 --- a/selfhost/backend/emit_call.ludic +++ b/selfhost/backend/emit_call.ludic @@ -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`) diff --git a/selfhost/backend/emit_decl.ludic b/selfhost/backend/emit_decl.ludic index fe09bae2..d4c80243 100644 --- a/selfhost/backend/emit_decl.ludic +++ b/selfhost/backend/emit_decl.ludic @@ -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_ (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 # ` ` row per instrumented line, and close. diff --git a/selfhost/backend/emit_expr.ludic b/selfhost/backend/emit_expr.ludic index 1937a96d..c99fc75c 100644 --- a/selfhost/backend/emit_expr.ludic +++ b/selfhost/backend/emit_expr.ludic @@ -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") diff --git a/selfhost/backend/emit_fence.ludic b/selfhost/backend/emit_fence.ludic new file mode 100644 index 00000000..922064c6 --- /dev/null +++ b/selfhost/backend/emit_fence.ludic @@ -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`) +} diff --git a/selfhost/backend/emit_fence_ir.ludic b/selfhost/backend/emit_fence_ir.ludic new file mode 100644 index 00000000..5854587a --- /dev/null +++ b/selfhost/backend/emit_fence_ir.ludic @@ -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=, 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=); 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") +} diff --git a/selfhost/backend/emit_float.ludic b/selfhost/backend/emit_float.ludic index e0809fcf..b4409888 100644 --- a/selfhost/backend/emit_float.ludic +++ b/selfhost/backend/emit_float.ludic @@ -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 --------------------------------------------------------- diff --git a/selfhost/backend/emit_head.ludic b/selfhost/backend/emit_head.ludic index 98fbe9ed..56283ccd 100644 --- a/selfhost/backend/emit_head.ludic +++ b/selfhost/backend/emit_head.ludic @@ -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") diff --git a/selfhost/backend/emit_intrin.ludic b/selfhost/backend/emit_intrin.ludic index ef33a771..dcb29365 100644 --- a/selfhost/backend/emit_intrin.ludic +++ b/selfhost/backend/emit_intrin.ludic @@ -17,7 +17,7 @@ function is_intrinsic(name: pointer) -> bool { return false } # emit " = \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 diff --git a/selfhost/backend/emit_intrin2.ludic b/selfhost/backend/emit_intrin2.ludic index 61514fa0..b341a83c 100644 --- a/selfhost/backend/emit_intrin2.ludic +++ b/selfhost/backend/emit_intrin2.ludic @@ -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") { diff --git a/selfhost/backend/emit_mem.ludic b/selfhost/backend/emit_mem.ludic index 3d6007d6..f4d87cce 100644 --- a/selfhost/backend/emit_mem.ludic +++ b/selfhost/backend/emit_mem.ludic @@ -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]) diff --git a/selfhost/backend/emit_new.ludic b/selfhost/backend/emit_new.ludic index 1bc48a58..867a7e40 100644 --- a/selfhost/backend/emit_new.ludic +++ b/selfhost/backend/emit_new.ludic @@ -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") diff --git a/selfhost/backend/game/emit_ecs.ludic b/selfhost/backend/game/emit_ecs.ludic index 87d86245..8acd42ed 100644 --- a/selfhost/backend/game/emit_ecs.ludic +++ b/selfhost/backend/game/emit_ecs.ludic @@ -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`) } diff --git a/selfhost/backend/game/emit_game.ludic b/selfhost/backend/game/emit_game.ludic index 621b3f3f..6b131fce 100644 --- a/selfhost/backend/game/emit_game.ludic +++ b/selfhost/backend/game/emit_game.ludic @@ -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_, 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`) } diff --git a/selfhost/backend/game/emit_world.ludic b/selfhost/backend/game/emit_world.ludic index cb3a01f0..4aa71f5b 100644 --- a/selfhost/backend/game/emit_world.ludic +++ b/selfhost/backend/game/emit_world.ludic @@ -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") diff --git a/selfhost/backend/stdlib/emit_crypto.ludic b/selfhost/backend/stdlib/emit_crypto.ludic index 37a26ae4..bdd0c5e1 100644 --- a/selfhost/backend/stdlib/emit_crypto.ludic +++ b/selfhost/backend/stdlib/emit_crypto.ludic @@ -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") } diff --git a/selfhost/backend/stdlib/emit_datetime.ludic b/selfhost/backend/stdlib/emit_datetime.ludic index 3e4ac830..d5144a14 100644 --- a/selfhost/backend/stdlib/emit_datetime.ludic +++ b/selfhost/backend/stdlib/emit_datetime.ludic @@ -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") diff --git a/selfhost/backend/stdlib/emit_fs.ludic b/selfhost/backend/stdlib/emit_fs.ludic index 0c283ff6..1715b65b 100644 --- a/selfhost/backend/stdlib/emit_fs.ludic +++ b/selfhost/backend/stdlib/emit_fs.ludic @@ -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 "

.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 "

.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 ".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 diff --git a/selfhost/backend/stdlib/emit_list.ludic b/selfhost/backend/stdlib/emit_list.ludic index b2013e15..84c0b979 100644 --- a/selfhost/backend/stdlib/emit_list.ludic +++ b/selfhost/backend/stdlib/emit_list.ludic @@ -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 diff --git a/selfhost/backend/stdlib/emit_log.ludic b/selfhost/backend/stdlib/emit_log.ludic index ac1886b0..20a95f93 100644 --- a/selfhost/backend/stdlib/emit_log.ludic +++ b/selfhost/backend/stdlib/emit_log.ludic @@ -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") diff --git a/selfhost/backend/stdlib/emit_os.ludic b/selfhost/backend/stdlib/emit_os.ludic index 74dad2be..66f6d3ff 100644 --- a/selfhost/backend/stdlib/emit_os.ludic +++ b/selfhost/backend/stdlib/emit_os.ludic @@ -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") # $ when it is set and non-empty, else $HOME; 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") diff --git a/selfhost/backend/stdlib/emit_pak.ludic b/selfhost/backend/stdlib/emit_pak.ludic index 799695ab..81301685 100644 --- a/selfhost/backend/stdlib/emit_pak.ludic +++ b/selfhost/backend/stdlib/emit_pak.ludic @@ -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") } diff --git a/selfhost/backend/stdlib/emit_text.ludic b/selfhost/backend/stdlib/emit_text.ludic index 728a29e1..61d60478 100644 --- a/selfhost/backend/stdlib/emit_text.ludic +++ b/selfhost/backend/stdlib/emit_text.ludic @@ -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 { diff --git a/selfhost/backend/stdlib/emit_text_prelude.ludic b/selfhost/backend/stdlib/emit_text_prelude.ludic index 795de997..750eb8ff 100644 --- a/selfhost/backend/stdlib/emit_text_prelude.ludic +++ b/selfhost/backend/stdlib/emit_text_prelude.ludic @@ -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") diff --git a/selfhost/backend/stdlib/emit_unicode.ludic b/selfhost/backend/stdlib/emit_unicode.ludic index dec48d8e..6b25d8ac 100644 --- a/selfhost/backend/stdlib/emit_unicode.ludic +++ b/selfhost/backend/stdlib/emit_unicode.ludic @@ -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() diff --git a/selfhost/backend/stdlib/emit_uuid.ludic b/selfhost/backend/stdlib/emit_uuid.ludic index 68d49cac..1b232c9f 100644 --- a/selfhost/backend/stdlib/emit_uuid.ludic +++ b/selfhost/backend/stdlib/emit_uuid.ludic @@ -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") diff --git a/selfhost/backend/stdlib/emit_win.ludic b/selfhost/backend/stdlib/emit_win.ludic index cef45a8b..9dfe5f23 100644 --- a/selfhost/backend/stdlib/emit_win.ludic +++ b/selfhost/backend/stdlib/emit_win.ludic @@ -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") diff --git a/selfhost/main.ludic b/selfhost/main.ludic index d4658371..0fcd1823 100644 --- a/selfhost/main.ludic +++ b/selfhost/main.ludic @@ -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 diff --git a/tools/ludic-cli/selfhost.ludic b/tools/ludic-cli/selfhost.ludic index c0144202..ca518737 100644 --- a/tools/ludic-cli/selfhost.ludic +++ b/tools/ludic-cli/selfhost.ludic @@ -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") diff --git a/tools/ludic-cli/test.ludic b/tools/ludic-cli/test.ludic index ab92c19d..a1ba5597 100644 --- a/tools/ludic-cli/test.ludic +++ b/tools/ludic-cli/test.ludic @@ -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")