fence (25.1): every allocation goes through the fence - sites, frame judging, census, callers
Every allocation the compiler emits goes through @lp_malloc/@lp_calloc/@lp_realloc/@lp_free, and a Ludic-level one first stores its site (function, file, line, kind) in @lp_site. Off, that is one load and a predictable branch (30 M allocations: 0.87-0.91 s against 0.87-0.90 s on leaks2). On (the default in a headless build, and windowed under R3D_DEV), tracking starts at the first frame on its own and judging once R3D_ALLOC_WARM frames in a row kept nothing (600) or R3D_ALLOC_WARM_MAX after (re)start; Mem.play()/Mem.rewarm() sends a load back to its warm-up. A judged frame that ends holding more than it began with is reported by site with its callers (the unwinder, taken only once judging) and fails the run with exit 86 (R3D_ALLOC_FENCE=off|count|warn|fail). R3D_ALLOC_CENSUS writes the totals and top sites at exit. The build's defaults are --fence=, --fence-warm=, --fence-census= or a fence line in the program's package.ludic; the environment overrides them. The runtime is IR (emit_fence_ir.ludic, generated from a template); tracking is a side table in one calloc'd region, so no block carries a header and pointers crossing to natives stay safe. Examples alloc_fence, alloc_fence_leak and alloc_fence_auto with cases in ludic-dev test; reseeded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
fc0f3d3790
commit
a8d54e9878
34 changed files with 106868 additions and 82677 deletions
32
examples/lang/alloc_fence.ludic
Normal file
32
examples/lang/alloc_fence.ludic
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
# alloc_fence.ludic - the allocation fence (docs/plan 25.1) judges frames once play has settled.
|
||||
# Everything a frame makes here is gone by the frame's end or goes on being reused: text only
|
||||
# compared (freed once compared), records held from before play written in place. No frame
|
||||
# keeps anything, so the run - which fails by default when one does - prints
|
||||
# `frames 300 kept 0 bad 0`.
|
||||
program AllocFence {
|
||||
property Box { a: int = 0 }
|
||||
|
||||
function round(i: int, kept: []Box) -> int {
|
||||
var bad = 0
|
||||
if `{i}-{i * 2}` != string(i) + "-" + string(i * 2) { bad += 1 }
|
||||
if `lake {i}` == "" { bad += 1 }
|
||||
for k in 0 .. len(kept) { kept[k].a = i + k } # records reused: List.clear and new ones would keep the old
|
||||
return bad
|
||||
}
|
||||
|
||||
entry {
|
||||
var kept = new []Box
|
||||
for k in 0 .. 8 { push(kept, new Box) }
|
||||
Mem.play()
|
||||
Mem.settled()
|
||||
var bad = 0
|
||||
var frames = 0
|
||||
for i in 0 .. 300 {
|
||||
bad += round(i, kept)
|
||||
Mem.frame()
|
||||
frames += 1
|
||||
}
|
||||
let kept_now = Mem.kept() # read before the line below makes its text
|
||||
print(`frames {frames} kept {kept_now} bad {Mem.bad_frames()}`)
|
||||
}
|
||||
}
|
||||
16
examples/lang/alloc_fence_auto.ludic
Normal file
16
examples/lang/alloc_fence_auto.ludic
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
# alloc_fence_auto.ludic - nothing here names the fence but the frame's mark: tracking starts at the
|
||||
# first frame on its own, judging once R3D_ALLOC_WARM frames (600) in a row kept nothing, and frame
|
||||
# 1500 keeping a record fails the run with exit 86. Prints nothing when it fails, `through` if not.
|
||||
program AllocFenceAuto {
|
||||
property Box { a: int = 0 }
|
||||
entry {
|
||||
var held = new []Box
|
||||
for k in 0 .. 4 { push(held, new Box) }
|
||||
for f in 0 .. 2000 {
|
||||
if f < 300 { push(held, new Box { a: f }) } # loading: growing, not judged
|
||||
if f == 1500 { push(held, new Box { a: f }) }
|
||||
Mem.frame()
|
||||
}
|
||||
print("through")
|
||||
}
|
||||
}
|
||||
17
examples/lang/alloc_fence_leak.ludic
Normal file
17
examples/lang/alloc_fence_leak.ludic
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
# alloc_fence_leak.ludic - frame 50 keeps one record in a list that outlives it. The fence fails
|
||||
# the run there (exit 86) and names this file's `new Box` line; under R3D_ALLOC_FENCE=count it
|
||||
# runs on and prints `kept 72 bad 1` (the record and the list's first growth, 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()}`)
|
||||
}
|
||||
}
|
||||
|
|
@ -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`)
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ function emit_params_sig(d: Node) -> void {
|
|||
function emit_fn(d: Node) -> void {
|
||||
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0
|
||||
det_enter(d.s)
|
||||
fence_enter(d.s)
|
||||
g_cur_scene = null # a function belongs to no scene: `become` leaves the live one
|
||||
ret_ty = d.ty
|
||||
let fbody = buf_new()
|
||||
|
|
@ -46,6 +47,7 @@ function emit_fn(d: Node) -> void {
|
|||
}
|
||||
|
||||
function emit_main(d: Node) -> void {
|
||||
fence_enter("entry")
|
||||
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0
|
||||
ret_ty = "int"
|
||||
let fbody = buf_new()
|
||||
|
|
@ -85,6 +87,7 @@ function emit_main(d: Node) -> void {
|
|||
|
||||
# one test block -> a void function @fn__test_<idx> (mirrors emit_fn's shape).
|
||||
function emit_test_fn(t: Node, idx: int) -> void {
|
||||
fence_enter(`test {t.s}`)
|
||||
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0
|
||||
ret_ty = "void"
|
||||
let fbody = buf_new()
|
||||
|
|
@ -295,6 +298,7 @@ function emit_program() -> void {
|
|||
g_uses_world_despawn = false # #84: set when a world_despawn call is emitted (below)
|
||||
g_cov_lines = new []int
|
||||
g_cov_active = true
|
||||
fence_reset()
|
||||
loc_name = new []pointer; loc_reg = new []pointer; loc_ty = new []pointer; loc_mut = new []int
|
||||
brk_lbl = new []pointer; cnt_lbl = new []pointer
|
||||
self_stk = new []pointer
|
||||
|
|
@ -356,8 +360,7 @@ function emit_program() -> void {
|
|||
if g_uses_pak { emit_pak_prelude() } # @lp_pak_* asset packs + the pre-main mount ctor
|
||||
if g_uses_datert { emit_datetime_prelude() } # @lp_days_from_civil / @lp_civil_from_days conversions
|
||||
if g_uses_panic { # panic/assert: located abort to stderr
|
||||
emith("declare i32 @fprintf(ptr, ptr, ...)\n")
|
||||
g_fprintf_declared = true
|
||||
if not g_fprintf_declared { emith("declare i32 @fprintf(ptr, ptr, ...)\n"); g_fprintf_declared = true }
|
||||
emith("@.fmt_panic = private unnamed_addr constant [6 x i8] c\"%s%s\\0A\\00\"\n")
|
||||
}
|
||||
if g_uses_bounds { # a slice index out of range: located abort
|
||||
|
|
@ -366,6 +369,7 @@ function emit_program() -> void {
|
|||
}
|
||||
if g_uses_result { emith("%Result = type { i32, i32, ptr }\n") } # issue #46: ok/err/try value
|
||||
if g_uses_option { emith("%Option = type { i32, i32 }\n") } # issue #53: some/none value
|
||||
emit_fence_runtime() # 25.1: the allocation fence and its site tables
|
||||
emit_cov_runtime() # issue #45: --coverage tables + exit dump
|
||||
if g_emit_module { emit_module_glue() } # issue #64: binary-module host-ABI declares + load-time registration ctor
|
||||
}
|
||||
|
|
@ -400,7 +404,7 @@ function emit_cov_runtime() -> void {
|
|||
emith("@.cov_filefmt = private unnamed_addr constant [9 x i8] c\"FILE %s\\0A\\00\"\n")
|
||||
emith("@.cov_rowfmt = private unnamed_addr constant [7 x i8] c\"%d %d\\0A\\00\"\n")
|
||||
if not g_fprintf_declared { emith("declare i32 @fprintf(ptr, ptr, ...)\n"); g_fprintf_declared = true }
|
||||
emith("declare i32 @atexit(ptr)\n")
|
||||
if not g_atexit_declared { emith("declare i32 @atexit(ptr)\n"); g_atexit_declared = true }
|
||||
|
||||
# @cov_dump: open $LUDIC_COVERAGE (or "ludic.cov"), write a FILE header then one
|
||||
# `<line> <hits>` row per instrumented line, and close.
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
200
selfhost/backend/emit_fence.ludic
Normal file
200
selfhost/backend/emit_fence.ludic
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
# 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
|
||||
# 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_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 }
|
||||
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`)
|
||||
}
|
||||
963
selfhost/backend/emit_fence_ir.ludic
Normal file
963
selfhost/backend/emit_fence_ir.ludic
Normal file
|
|
@ -0,0 +1,963 @@
|
|||
# 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_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_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 %n) {\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(" %cnt = load i64, ptr @lp_tabn\n")
|
||||
emith(" %max = load i64, ptr @lp_tabmax\n")
|
||||
emith(" %full = icmp sge i64 %cnt, %max\n")
|
||||
emith(" br i1 %full, label %over, label %ins\n")
|
||||
emith("over:\n")
|
||||
emith(" %ov = load i64, ptr @lp_over\n")
|
||||
emith(" %ov1 = add i64 %ov, 1\n")
|
||||
emith(" store i64 %ov1, ptr @lp_over\n")
|
||||
emith(" br label %unlock\n")
|
||||
emith("ins:\n")
|
||||
emith(" %s0 = load i32, ptr @lp_site\n")
|
||||
emith(" %sn = load i32, ptr @lp_site_n\n")
|
||||
emith(" %sok = icmp ult i32 %s0, %sn\n")
|
||||
emith(" %s = select i1 %sok, i32 %s0, i32 0\n")
|
||||
emith(" %s64 = zext i32 %s to i64\n")
|
||||
emith(" %k = ptrtoint ptr %p to i64\n")
|
||||
emith(" %h = call i64 @lp_fhash(i64 %k)\n")
|
||||
emith(" %mask = load i64, ptr @lp_tabmask\n")
|
||||
emith(" %tab = load ptr, ptr @lp_tab\n")
|
||||
emith(" br label %probe\n")
|
||||
emith("probe:\n")
|
||||
emith(" %i = phi i64 [ %h, %ins ], [ %i1, %next ]\n")
|
||||
emith(" %im = and i64 %i, %mask\n")
|
||||
emith(" %e = getelementptr inbounds [2 x i64], ptr %tab, i64 %im\n")
|
||||
emith(" %ek = load i64, ptr %e\n")
|
||||
emith(" %empty = icmp eq i64 %ek, 0\n")
|
||||
emith(" br i1 %empty, label %put, label %chk\n")
|
||||
emith("chk:\n")
|
||||
emith(" %same = icmp eq i64 %ek, %k\n")
|
||||
emith(" br i1 %same, label %dup, label %next\n")
|
||||
emith("next:\n")
|
||||
emith(" %i1 = add i64 %im, 1\n")
|
||||
emith(" br label %probe\n")
|
||||
emith("dup:\n")
|
||||
emith(" %dv = getelementptr inbounds i64, ptr %e, i64 1\n")
|
||||
emith(" %dold = load i64, ptr %dv\n")
|
||||
emith(" %dn = and i64 %dold, 1099511627775\n")
|
||||
emith(" %ds = lshr i64 %dold, 40\n")
|
||||
emith(" %dneg = sub i64 0, %dn\n")
|
||||
emith(" call void @lp_fsite_add(i64 %ds, i64 %dneg)\n")
|
||||
emith(" %dtn = load i64, ptr @lp_tabn\n")
|
||||
emith(" %dtn1 = sub i64 %dtn, 1\n")
|
||||
emith(" store i64 %dtn1, ptr @lp_tabn\n")
|
||||
emith(" %dfr = load i64, ptr @lp_freed\n")
|
||||
emith(" %dfr1 = add i64 %dfr, 1\n")
|
||||
emith(" store i64 %dfr1, ptr @lp_freed\n")
|
||||
emith(" br label %put\n")
|
||||
emith("put:\n")
|
||||
emith(" store i64 %k, ptr %e\n")
|
||||
emith(" %ev = getelementptr inbounds i64, ptr %e, i64 1\n")
|
||||
emith(" %sh = shl i64 %s64, 40\n")
|
||||
emith(" %v = or i64 %sh, %n\n")
|
||||
emith(" store i64 %v, ptr %ev\n")
|
||||
emith(" %tn = load i64, ptr @lp_tabn\n")
|
||||
emith(" %tn1 = add i64 %tn, 1\n")
|
||||
emith(" store i64 %tn1, ptr @lp_tabn\n")
|
||||
emith(" %md = load i64, ptr @lp_made\n")
|
||||
emith(" %md1 = add i64 %md, 1\n")
|
||||
emith(" store i64 %md1, ptr @lp_made\n")
|
||||
emith(" call void @lp_fsite_add(i64 %s64, i64 %n)\n")
|
||||
emith(" call void @lp_fcallers(i64 %s64)\n")
|
||||
emith(" br label %unlock\n")
|
||||
emith("unlock:\n")
|
||||
emith(" call void @lp_funlock()\n")
|
||||
emith(" br label %out\n")
|
||||
emith("out:\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
emith("define internal void @lp_fdel(ptr %p) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %isnull = icmp eq ptr %p, null\n")
|
||||
emith(" br i1 %isnull, label %out, label %go\n")
|
||||
emith("go:\n")
|
||||
emith(" call void @lp_flock()\n")
|
||||
emith(" %k = ptrtoint ptr %p to i64\n")
|
||||
emith(" %h = call i64 @lp_fhash(i64 %k)\n")
|
||||
emith(" %mask = load i64, ptr @lp_tabmask\n")
|
||||
emith(" %tab = load ptr, ptr @lp_tab\n")
|
||||
emith(" br label %probe\n")
|
||||
emith("probe:\n")
|
||||
emith(" %i = phi i64 [ %h, %go ], [ %i1, %next ]\n")
|
||||
emith(" %im = and i64 %i, %mask\n")
|
||||
emith(" %e = getelementptr inbounds [2 x i64], ptr %tab, i64 %im\n")
|
||||
emith(" %ek = load i64, ptr %e\n")
|
||||
emith(" %empty = icmp eq i64 %ek, 0\n")
|
||||
emith(" br i1 %empty, label %miss, label %cmp\n")
|
||||
emith("cmp:\n")
|
||||
emith(" %hit = icmp eq i64 %ek, %k\n")
|
||||
emith(" br i1 %hit, label %found, label %next\n")
|
||||
emith("next:\n")
|
||||
emith(" %i1 = add i64 %im, 1\n")
|
||||
emith(" br label %probe\n")
|
||||
emith("miss:\n")
|
||||
emith(" %has = icmp ne ptr @malloc_size, null\n")
|
||||
emith(" br i1 %has, label %msz, label %unlock\n")
|
||||
emith("msz:\n")
|
||||
emith(" %ms = call i64 @malloc_size(ptr %p)\n")
|
||||
emith(" %pr = load i64, ptr @lp_pre\n")
|
||||
emith(" %pr1 = add i64 %pr, %ms\n")
|
||||
emith(" store i64 %pr1, ptr @lp_pre\n")
|
||||
emith(" br label %unlock\n")
|
||||
emith("found:\n")
|
||||
emith(" %ev = getelementptr inbounds i64, ptr %e, i64 1\n")
|
||||
emith(" %v = load i64, ptr %ev\n")
|
||||
emith(" %n = and i64 %v, 1099511627775\n")
|
||||
emith(" %s = lshr i64 %v, 40\n")
|
||||
emith(" %neg = sub i64 0, %n\n")
|
||||
emith(" call void @lp_fsite_add(i64 %s, i64 %neg)\n")
|
||||
emith(" %fr = load i64, ptr @lp_freed\n")
|
||||
emith(" %fr1 = add i64 %fr, 1\n")
|
||||
emith(" store i64 %fr1, ptr @lp_freed\n")
|
||||
emith(" %tn = load i64, ptr @lp_tabn\n")
|
||||
emith(" %tn1 = sub i64 %tn, 1\n")
|
||||
emith(" store i64 %tn1, ptr @lp_tabn\n")
|
||||
emith(" br label %sh\n")
|
||||
emith("sh:\n")
|
||||
emith(" %hole = phi i64 [ %im, %found ], [ %hole, %sh_skip ], [ %j, %sh_move ]\n")
|
||||
emith(" %jp = phi i64 [ %im, %found ], [ %j, %sh_skip ], [ %j, %sh_move ]\n")
|
||||
emith(" %j0 = add i64 %jp, 1\n")
|
||||
emith(" %j = and i64 %j0, %mask\n")
|
||||
emith(" %ej = getelementptr inbounds [2 x i64], ptr %tab, i64 %j\n")
|
||||
emith(" %kj = load i64, ptr %ej\n")
|
||||
emith(" %endq = icmp eq i64 %kj, 0\n")
|
||||
emith(" br i1 %endq, label %sh_end, label %sh_test\n")
|
||||
emith("sh_test:\n")
|
||||
emith(" %hj = call i64 @lp_fhash(i64 %kj)\n")
|
||||
emith(" %home = and i64 %hj, %mask\n")
|
||||
emith(" %jgt = icmp ugt i64 %j, %hole\n")
|
||||
emith(" %a1 = icmp ugt i64 %home, %hole\n")
|
||||
emith(" %a2 = icmp ule i64 %home, %j\n")
|
||||
emith(" %in1 = and i1 %a1, %a2\n")
|
||||
emith(" %in2 = or i1 %a1, %a2\n")
|
||||
emith(" %in = select i1 %jgt, i1 %in1, i1 %in2\n")
|
||||
emith(" br i1 %in, label %sh_skip, label %sh_move\n")
|
||||
emith("sh_skip:\n")
|
||||
emith(" br label %sh\n")
|
||||
emith("sh_move:\n")
|
||||
emith(" %eh = getelementptr inbounds [2 x i64], ptr %tab, i64 %hole\n")
|
||||
emith(" store i64 %kj, ptr %eh\n")
|
||||
emith(" %ejv = getelementptr inbounds i64, ptr %ej, i64 1\n")
|
||||
emith(" %vj = load i64, ptr %ejv\n")
|
||||
emith(" %ehv = getelementptr inbounds i64, ptr %eh, i64 1\n")
|
||||
emith(" store i64 %vj, ptr %ehv\n")
|
||||
emith(" br label %sh\n")
|
||||
emith("sh_end:\n")
|
||||
emith(" %ehe = getelementptr inbounds [2 x i64], ptr %tab, i64 %hole\n")
|
||||
emith(" store i64 0, ptr %ehe\n")
|
||||
emith(" br label %unlock\n")
|
||||
emith("unlock:\n")
|
||||
emith(" call void @lp_funlock()\n")
|
||||
emith(" br label %out\n")
|
||||
emith("out:\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
}
|
||||
function emit_fence_control() -> void {
|
||||
emith("define internal i64 @lp_fenv(ptr %name, i64 %dflt) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %e = call ptr @getenv(ptr %name)\n")
|
||||
emith(" %has = icmp ne ptr %e, null\n")
|
||||
emith(" br i1 %has, label %read, label %none\n")
|
||||
emith("read:\n")
|
||||
emith(" %v = call i32 @atoi(ptr %e)\n")
|
||||
emith(" %v64 = sext i32 %v to i64\n")
|
||||
emith(" ret i64 %v64\n")
|
||||
emith("none:\n")
|
||||
emith(" ret i64 %dflt\n")
|
||||
emith("}\n")
|
||||
emith("define internal void @lp_finit() {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %m0 = load i8, ptr @lp_fm\n")
|
||||
emith(" %done = icmp ne i8 %m0, 0\n")
|
||||
emith(" br i1 %done, label %out, label %go\n")
|
||||
emith("go:\n")
|
||||
emith(" %e = call ptr @getenv(ptr @.lpf_env)\n")
|
||||
emith(" %has = icmp ne ptr %e, null\n")
|
||||
emith(" br i1 %has, label %parse, label %dflt\n")
|
||||
emith("parse:\n")
|
||||
emith(" %c1 = call i32 @strcmp(ptr %e, ptr @.lpf_off)\n")
|
||||
emith(" %is1 = icmp eq i32 %c1, 0\n")
|
||||
emith(" %c2 = call i32 @strcmp(ptr %e, ptr @.lpf_count)\n")
|
||||
emith(" %is2 = icmp eq i32 %c2, 0\n")
|
||||
emith(" %c3 = call i32 @strcmp(ptr %e, ptr @.lpf_warn)\n")
|
||||
emith(" %is3 = icmp eq i32 %c3, 0\n")
|
||||
emith(" %ma = select i1 %is3, i8 3, i8 4\n")
|
||||
emith(" %mb = select i1 %is2, i8 2, i8 %ma\n")
|
||||
emith(" %mc = select i1 %is1, i8 1, i8 %mb\n")
|
||||
emith(" br label %set\n")
|
||||
emith("dflt:\n")
|
||||
emith(" %d = load i8, ptr @lp_fdef\n")
|
||||
emith(" %dz = icmp eq i8 %d, 0\n")
|
||||
emith(" br i1 %dz, label %dev, label %set\n")
|
||||
emith("dev:\n")
|
||||
emith(" %dv = call ptr @getenv(ptr @.lpf_dev)\n")
|
||||
emith(" %hd = icmp ne ptr %dv, null\n")
|
||||
emith(" %md = select i1 %hd, i8 4, i8 1\n")
|
||||
emith(" br label %set\n")
|
||||
emith("set:\n")
|
||||
emith(" %mode = phi i8 [ %mc, %parse ], [ %d, %dflt ], [ %md, %dev ]\n")
|
||||
emith(" store i8 %mode, ptr @lp_fm\n")
|
||||
emith(" %wd = load i64, ptr @lp_warm\n")
|
||||
emith(" %w = call i64 @lp_fenv(ptr @.lpf_warm, i64 %wd)\n")
|
||||
emith(" store i64 %w, ptr @lp_warm\n")
|
||||
emith(" %wx5 = mul i64 %w, 5\n")
|
||||
emith(" %wm = call i64 @lp_fenv(ptr @.lpf_warmmax, i64 %wx5)\n")
|
||||
emith(" store i64 %wm, ptr @lp_warm_max\n")
|
||||
emith(" %au = call i64 @lp_fenv(ptr @.lpf_auto, i64 1)\n")
|
||||
emith(" %au8 = trunc i64 %au to i8\n")
|
||||
emith(" store i8 %au8, ptr @lp_fauto\n")
|
||||
emith(" %pl = call i64 @lp_fenv(ptr @.lpf_play, i64 -1)\n")
|
||||
emith(" store i64 %pl, ptr @lp_autoplay\n")
|
||||
emith(" %want = call i64 @lp_fenv(ptr @.lpf_tab, i64 4194304)\n")
|
||||
emith(" br label %cap\n")
|
||||
emith("cap:\n")
|
||||
emith(" %c = phi i64 [ 1024, %set ], [ %c2x, %grow ]\n")
|
||||
emith(" %big = icmp sge i64 %c, %want\n")
|
||||
emith(" br i1 %big, label %capped, label %grow\n")
|
||||
emith("grow:\n")
|
||||
emith(" %c2x = shl i64 %c, 1\n")
|
||||
emith(" br label %cap\n")
|
||||
emith("capped:\n")
|
||||
emith(" %mask = sub i64 %c, 1\n")
|
||||
emith(" store i64 %mask, ptr @lp_tabmask\n")
|
||||
emith(" %q = lshr i64 %c, 2\n")
|
||||
emith(" %max = mul i64 %q, 3\n")
|
||||
emith(" store i64 %max, ptr @lp_tabmax\n")
|
||||
emith(" br label %out\n")
|
||||
emith("out:\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
emith("define internal void @lp_fsnap() {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %lv = load i64, ptr @lp_live\n")
|
||||
emith(" store i64 %lv, ptr @lp_mlive\n")
|
||||
emith(" %pr = load i64, ptr @lp_pre\n")
|
||||
emith(" store i64 %pr, ptr @lp_mpre\n")
|
||||
emith(" %md = load i64, ptr @lp_made\n")
|
||||
emith(" store i64 %md, ptr @lp_mmade\n")
|
||||
emith(" %fr = load i64, ptr @lp_freed\n")
|
||||
emith(" store i64 %fr, ptr @lp_mfreed\n")
|
||||
emith(" store i32 0, ptr @lp_ntouch\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
emith("define internal void @lp_fsnap_all() {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %n = load i32, ptr @lp_site_n\n")
|
||||
emith(" %n64 = zext i32 %n to i64\n")
|
||||
emith(" %b = shl i64 %n64, 3\n")
|
||||
emith(" call ptr @memcpy(ptr @lp_site_mark, ptr @lp_site_live, i64 %b)\n")
|
||||
emith(" call void @lp_fsnap()\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
emith("define void @lp_mem_play() {\n")
|
||||
emith("entry:\n")
|
||||
emith(" call void @lp_finit()\n")
|
||||
emith(" %m = load i8, ptr @lp_fm\n")
|
||||
emith(" %off = icmp ule i8 %m, 1\n")
|
||||
emith(" br i1 %off, label %out, label %chk\n")
|
||||
emith("chk:\n")
|
||||
emith(" %f = load i64, ptr @lp_frame\n")
|
||||
emith(" %t = load i8, ptr @lp_ft\n")
|
||||
emith(" %on = icmp ne i8 %t, 0\n")
|
||||
emith(" br i1 %on, label %rewarm, label %start\n")
|
||||
emith("rewarm:\n")
|
||||
emith(" call void @lp_flock()\n")
|
||||
emith(" store i8 0, ptr @lp_fj\n")
|
||||
emith(" store i64 0, ptr @lp_flat\n")
|
||||
emith(" store i64 %f, ptr @lp_playf\n")
|
||||
emith(" call void @lp_funlock()\n")
|
||||
emith(" br label %out\n")
|
||||
emith("start:\n")
|
||||
emith(" %tab = load ptr, ptr @lp_tab\n")
|
||||
emith(" %none = icmp eq ptr %tab, null\n")
|
||||
emith(" br i1 %none, label %make, label %arm\n")
|
||||
emith("make:\n")
|
||||
emith(" %mask = load i64, ptr @lp_tabmask\n")
|
||||
emith(" %c = add i64 %mask, 1\n")
|
||||
emith(" %p = call ptr @calloc(i64 %c, i64 16)\n")
|
||||
emith(" store ptr %p, ptr @lp_tab\n")
|
||||
emith(" br label %arm\n")
|
||||
emith("arm:\n")
|
||||
emith(" store i64 %f, ptr @lp_playf\n")
|
||||
emith(" store i64 0, ptr @lp_flat\n")
|
||||
emith(" %cp0 = call ptr @getenv(ptr @.lpf_census)\n")
|
||||
emith(" %cpd = load ptr, ptr @lp_census_def\n")
|
||||
emith(" %hasenv = icmp ne ptr %cp0, null\n")
|
||||
emith(" %cp = select i1 %hasenv, ptr %cp0, ptr %cpd\n")
|
||||
emith(" store ptr %cp, ptr @lp_census\n")
|
||||
emith(" %hascp = icmp ne ptr %cp, null\n")
|
||||
emith(" br i1 %hascp, label %reg, label %go\n")
|
||||
emith("reg:\n")
|
||||
emith(" call i32 @atexit(ptr @lp_fcensus)\n")
|
||||
emith(" br label %go\n")
|
||||
emith("go:\n")
|
||||
emith(" call void @lp_fsnap_all()\n")
|
||||
emith(" store i8 1, ptr @lp_ft\n")
|
||||
emith(" br label %out\n")
|
||||
emith("out:\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
emith("define void @lp_mem_settled() {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %t = load i8, ptr @lp_ft\n")
|
||||
emith(" %on = icmp ne i8 %t, 0\n")
|
||||
emith(" br i1 %on, label %chk, label %out\n")
|
||||
emith("chk:\n")
|
||||
emith(" %j = load i8, ptr @lp_fj\n")
|
||||
emith(" %already = icmp ne i8 %j, 0\n")
|
||||
emith(" br i1 %already, label %out, label %go\n")
|
||||
emith("go:\n")
|
||||
emith(" call void @lp_flock()\n")
|
||||
emith(" call void @lp_fsnap_all()\n")
|
||||
emith(" call void @lp_fsettle_locked()\n")
|
||||
emith(" call void @lp_funlock()\n")
|
||||
emith(" br label %out\n")
|
||||
emith("out:\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
emith("define i64 @lp_mem_kept() {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %j = load i8, ptr @lp_fj\n")
|
||||
emith(" %on = icmp ne i8 %j, 0\n")
|
||||
emith(" br i1 %on, label %read, label %none\n")
|
||||
emith("none:\n")
|
||||
emith(" ret i64 0\n")
|
||||
emith("read:\n")
|
||||
emith(" call void @lp_flock()\n")
|
||||
emith(" %lv = load i64, ptr @lp_live\n")
|
||||
emith(" %sl = load i64, ptr @lp_slive\n")
|
||||
emith(" %pr = load i64, ptr @lp_pre\n")
|
||||
emith(" %sp = load i64, ptr @lp_spre\n")
|
||||
emith(" %a = sub i64 %lv, %sl\n")
|
||||
emith(" %b = sub i64 %pr, %sp\n")
|
||||
emith(" %k = sub i64 %a, %b\n")
|
||||
emith(" call void @lp_funlock()\n")
|
||||
emith(" ret i64 %k\n")
|
||||
emith("}\n")
|
||||
emith("define i64 @lp_mem_bad_frames() {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %b = load i64, ptr @lp_fbad\n")
|
||||
emith(" ret i64 %b\n")
|
||||
emith("}\n")
|
||||
}
|
||||
function emit_fence_report() -> void {
|
||||
emith("define internal i64 @lp_fdelta(i32 %s, ptr %base) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %sp = getelementptr inbounds i64, ptr @lp_site_live, i32 %s\n")
|
||||
emith(" %v = load i64, ptr %sp\n")
|
||||
emith(" %mp = getelementptr inbounds i64, ptr %base, i32 %s\n")
|
||||
emith(" %m = load i64, ptr %mp\n")
|
||||
emith(" %d = sub i64 %v, %m\n")
|
||||
emith(" ret i64 %d\n")
|
||||
emith("}\n")
|
||||
emith("define internal i32 @lp_fsite_at(i32 %i, i1 %all) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" br i1 %all, label %every, label %touched\n")
|
||||
emith("every:\n")
|
||||
emith(" ret i32 %i\n")
|
||||
emith("touched:\n")
|
||||
emith(" %tp = getelementptr inbounds [512 x i32], ptr @lp_touch, i32 0, i32 %i\n")
|
||||
emith(" %s = load i32, ptr %tp\n")
|
||||
emith(" ret i32 %s\n")
|
||||
emith("}\n")
|
||||
emith("define internal { i64, i32 } @lp_fpick(ptr %base, i1 %all, i32 %cnt, i64 %pb, i32 %ps) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" br label %loop\n")
|
||||
emith("loop:\n")
|
||||
emith(" %i = phi i32 [ 0, %entry ], [ %i1, %body ]\n")
|
||||
emith(" %bb0 = phi i64 [ 0, %entry ], [ %bb1, %body ]\n")
|
||||
emith(" %bs0 = phi i32 [ -1, %entry ], [ %bs1, %body ]\n")
|
||||
emith(" %go = icmp ult i32 %i, %cnt\n")
|
||||
emith(" br i1 %go, label %body, label %done\n")
|
||||
emith("body:\n")
|
||||
emith(" %s = call i32 @lp_fsite_at(i32 %i, i1 %all)\n")
|
||||
emith(" %d = call i64 @lp_fdelta(i32 %s, ptr %base)\n")
|
||||
emith(" %pos = icmp sgt i64 %d, 0\n")
|
||||
emith(" %lt = icmp slt i64 %d, %pb\n")
|
||||
emith(" %eqb = icmp eq i64 %d, %pb\n")
|
||||
emith(" %gts = icmp sgt i32 %s, %ps\n")
|
||||
emith(" %tie = and i1 %eqb, %gts\n")
|
||||
emith(" %below = or i1 %lt, %tie\n")
|
||||
emith(" %gtb = icmp sgt i64 %d, %bb0\n")
|
||||
emith(" %eqbb = icmp eq i64 %d, %bb0\n")
|
||||
emith(" %lts = icmp slt i32 %s, %bs0\n")
|
||||
emith(" %tie2 = and i1 %eqbb, %lts\n")
|
||||
emith(" %better = or i1 %gtb, %tie2\n")
|
||||
emith(" %c1 = and i1 %pos, %below\n")
|
||||
emith(" %take = and i1 %c1, %better\n")
|
||||
emith(" %bb1 = select i1 %take, i64 %d, i64 %bb0\n")
|
||||
emith(" %bs1 = select i1 %take, i32 %s, i32 %bs0\n")
|
||||
emith(" %i1 = add i32 %i, 1\n")
|
||||
emith(" br label %loop\n")
|
||||
emith("done:\n")
|
||||
emith(" %r0 = insertvalue { i64, i32 } undef, i64 %bb0, 0\n")
|
||||
emith(" %r1 = insertvalue { i64, i32 } %r0, i32 %bs0, 1\n")
|
||||
emith(" ret { i64, i32 } %r1\n")
|
||||
emith("}\n")
|
||||
emith("define internal void @lp_fsite_row(ptr %fp, i32 %fd, i64 %bytes, i32 %s) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %fnp = getelementptr inbounds ptr, ptr @lp_site_fn, i32 %s\n")
|
||||
emith(" %fn = load ptr, ptr %fnp\n")
|
||||
emith(" %flp = getelementptr inbounds ptr, ptr @lp_site_file, i32 %s\n")
|
||||
emith(" %fl = load ptr, ptr %flp\n")
|
||||
emith(" %lnp = getelementptr inbounds i32, ptr @lp_site_line, i32 %s\n")
|
||||
emith(" %ln = load i32, ptr %lnp\n")
|
||||
emith(" %kdp = getelementptr inbounds ptr, ptr @lp_site_kind, i32 %s\n")
|
||||
emith(" %kd = load ptr, ptr %kdp\n")
|
||||
emith(" %tofile = icmp ne ptr %fp, null\n")
|
||||
emith(" br i1 %tofile, label %file, label %err\n")
|
||||
emith("file:\n")
|
||||
emith(" call i32 (ptr, ptr, ...) @fprintf(ptr %fp, ptr @.lpf_crow, i64 %bytes, ptr %fn, ptr %fl, i32 %ln, ptr %kd)\n")
|
||||
emith(" br label %from\n")
|
||||
emith("err:\n")
|
||||
emith(" call i32 (i32, ptr, ...) @dprintf(i32 %fd, ptr @.lpf_row, i64 %bytes, ptr %fn, ptr %fl, i32 %ln, ptr %kd)\n")
|
||||
emith(" br label %from\n")
|
||||
emith("from:\n")
|
||||
emith(" call void @lp_fsite_from(ptr %fp, i32 %s, ptr %fn)\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
emith("; the largest `rows` sites by what they hold over `base`, largest first\n")
|
||||
emith("define internal void @lp_franked(ptr %fp, ptr %base, i1 %all, i32 %cnt, i32 %rows) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" br label %pass\n")
|
||||
emith("pass:\n")
|
||||
emith(" %row = phi i32 [ 0, %entry ], [ %row1, %show ]\n")
|
||||
emith(" %pb = phi i64 [ 9223372036854775807, %entry ], [ %bb, %show ]\n")
|
||||
emith(" %ps = phi i32 [ -1, %entry ], [ %bs, %show ]\n")
|
||||
emith(" %more = icmp ult i32 %row, %rows\n")
|
||||
emith(" br i1 %more, label %scan, label %tail\n")
|
||||
emith("scan:\n")
|
||||
emith(" %r = call { i64, i32 } @lp_fpick(ptr %base, i1 %all, i32 %cnt, i64 %pb, i32 %ps)\n")
|
||||
emith(" %bb = extractvalue { i64, i32 } %r, 0\n")
|
||||
emith(" %bs = extractvalue { i64, i32 } %r, 1\n")
|
||||
emith(" %found = icmp sge i32 %bs, 0\n")
|
||||
emith(" br i1 %found, label %show, label %tail\n")
|
||||
emith("show:\n")
|
||||
emith(" call void @lp_fsite_row(ptr %fp, i32 2, i64 %bb, i32 %bs)\n")
|
||||
emith(" %row1 = add i32 %row, 1\n")
|
||||
emith(" br label %pass\n")
|
||||
emith("tail:\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
emith("define internal void @lp_freport(i64 %f, i64 %kept) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %md = load i64, ptr @lp_made\n")
|
||||
emith(" %mm = load i64, ptr @lp_mmade\n")
|
||||
emith(" %made = sub i64 %md, %mm\n")
|
||||
emith(" %fr = load i64, ptr @lp_freed\n")
|
||||
emith(" %mf = load i64, ptr @lp_mfreed\n")
|
||||
emith(" %freed = sub i64 %fr, %mf\n")
|
||||
emith(" call i32 (i32, ptr, ...) @dprintf(i32 2, ptr @.lpf_hdr, i64 %f, i64 %kept, i64 %made, i64 %freed)\n")
|
||||
emith(" %nt = load i32, ptr @lp_ntouch\n")
|
||||
emith(" %all = icmp ugt i32 %nt, 512\n")
|
||||
emith(" %sn = load i32, ptr @lp_site_n\n")
|
||||
emith(" %cnt = select i1 %all, i32 %sn, i32 %nt\n")
|
||||
emith(" call void @lp_franked(ptr null, ptr @lp_site_mark, i1 %all, i32 %cnt, i32 12)\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
emith("; R3D_ALLOC_CENSUS=<file>, written at exit: the run's totals since play settled, and every site\n")
|
||||
emith("; that holds more than it did then, largest first\n")
|
||||
emith("define void @lp_fcensus() {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %path = load ptr, ptr @lp_census\n")
|
||||
emith(" %none = icmp eq ptr %path, null\n")
|
||||
emith(" br i1 %none, label %out, label %open\n")
|
||||
emith("open:\n")
|
||||
emith(" %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(" %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("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(" store i8 1, ptr @lp_fj\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
emith("; the frame's mark. Tracking starts at the first one (R3D_ALLOC_AUTO=0: at Mem.play() or\n")
|
||||
emith("; R3D_ALLOC_PLAY=<frame>); judging starts once R3D_ALLOC_WARM frames in a row kept nothing, or\n")
|
||||
emith("; R3D_ALLOC_WARM_MAX frames after tracking (re)started, whichever is first\n")
|
||||
emith("define void @lp_mem_frame() {\n")
|
||||
emith("entry:\n")
|
||||
emith(" call void @lp_finit()\n")
|
||||
emith(" %f = load i64, ptr @lp_frame\n")
|
||||
emith(" %f1 = add i64 %f, 1\n")
|
||||
emith(" %m = load i8, ptr @lp_fm\n")
|
||||
emith(" %off = icmp ule i8 %m, 1\n")
|
||||
emith(" br i1 %off, label %tick, label %live\n")
|
||||
emith("live:\n")
|
||||
emith(" %t = load i8, ptr @lp_ft\n")
|
||||
emith(" %on = icmp ne i8 %t, 0\n")
|
||||
emith(" br i1 %on, label %tracking, label %maybe_play\n")
|
||||
emith("maybe_play:\n")
|
||||
emith(" %au = load i8, ptr @lp_fauto\n")
|
||||
emith(" %auq = icmp ne i8 %au, 0\n")
|
||||
emith(" %ap = load i64, ptr @lp_autoplay\n")
|
||||
emith(" %apset = icmp sge i64 %ap, 0\n")
|
||||
emith(" %apdue = icmp sge i64 %f1, %ap\n")
|
||||
emith(" %apgo = and i1 %apset, %apdue\n")
|
||||
emith(" %go = or i1 %auq, %apgo\n")
|
||||
emith(" br i1 %go, label %play, label %tick\n")
|
||||
emith("play:\n")
|
||||
emith(" call void @lp_mem_play()\n")
|
||||
emith(" br label %tick\n")
|
||||
emith("tracking:\n")
|
||||
emith(" call void @lp_flock()\n")
|
||||
emith(" %ov = load i64, ptr @lp_over\n")
|
||||
emith(" %ovq = icmp sgt i64 %ov, 0\n")
|
||||
emith(" %rep = load i8, ptr @lp_overrep\n")
|
||||
emith(" %repq = icmp eq i8 %rep, 0\n")
|
||||
emith(" %fullnew = and i1 %ovq, %repq\n")
|
||||
emith(" br i1 %fullnew, label %full, label %weigh\n")
|
||||
emith("full:\n")
|
||||
emith(" %tn = load i64, ptr @lp_tabn\n")
|
||||
emith(" call i32 (i32, ptr, ...) @dprintf(i32 2, ptr @.lpf_full, i64 %tn)\n")
|
||||
emith(" store i8 1, ptr @lp_overrep\n")
|
||||
emith(" store i8 0, ptr @lp_fj\n")
|
||||
emith(" br label %unlock\n")
|
||||
emith("weigh:\n")
|
||||
emith(" %lv = load i64, ptr @lp_live\n")
|
||||
emith(" %ml = load i64, ptr @lp_mlive\n")
|
||||
emith(" %pr = load i64, ptr @lp_pre\n")
|
||||
emith(" %mp = load i64, ptr @lp_mpre\n")
|
||||
emith(" %a = sub i64 %lv, %ml\n")
|
||||
emith(" %b = sub i64 %pr, %mp\n")
|
||||
emith(" %kept = sub i64 %a, %b\n")
|
||||
emith(" %bad = icmp sgt i64 %kept, 0\n")
|
||||
emith(" %j = load i8, ptr @lp_fj\n")
|
||||
emith(" %judging = icmp ne i8 %j, 0\n")
|
||||
emith(" br i1 %judging, label %judge, label %warming\n")
|
||||
emith("warming:\n")
|
||||
emith(" %fl = load i64, ptr @lp_flat\n")
|
||||
emith(" %fl1 = add i64 %fl, 1\n")
|
||||
emith(" %fl2 = select i1 %bad, i64 0, i64 %fl1\n")
|
||||
emith(" store i64 %fl2, ptr @lp_flat\n")
|
||||
emith(" %w = load i64, ptr @lp_warm\n")
|
||||
emith(" %wset = icmp sge i64 %w, 0\n")
|
||||
emith(" %wdone = icmp sge i64 %fl2, %w\n")
|
||||
emith(" %byflat = and i1 %wset, %wdone\n")
|
||||
emith(" %pf = load i64, ptr @lp_playf\n")
|
||||
emith(" %since = sub i64 %f1, %pf\n")
|
||||
emith(" %wm = load i64, ptr @lp_warm_max\n")
|
||||
emith(" %wmset = icmp sge i64 %wm, 0\n")
|
||||
emith(" %wmdone = icmp sge i64 %since, %wm\n")
|
||||
emith(" %bycap = and i1 %wmset, %wmdone\n")
|
||||
emith(" %settle = or i1 %byflat, %bycap\n")
|
||||
emith(" br i1 %settle, label %settle_now, label %mark\n")
|
||||
emith("settle_now:\n")
|
||||
emith(" call void @lp_fsettle_locked()\n")
|
||||
emith(" br label %mark\n")
|
||||
emith("judge:\n")
|
||||
emith(" %jd = load i64, ptr @lp_fjudged\n")
|
||||
emith(" %jd1 = add i64 %jd, 1\n")
|
||||
emith(" store i64 %jd1, ptr @lp_fjudged\n")
|
||||
emith(" br i1 %bad, label %kept_some, label %mark\n")
|
||||
emith("kept_some:\n")
|
||||
emith(" %fk = load i64, ptr @lp_fkept\n")
|
||||
emith(" %fk1 = add i64 %fk, %kept\n")
|
||||
emith(" store i64 %fk1, ptr @lp_fkept\n")
|
||||
emith(" %fb = load i64, ptr @lp_fbad\n")
|
||||
emith(" %fb1 = add i64 %fb, 1\n")
|
||||
emith(" store i64 %fb1, ptr @lp_fbad\n")
|
||||
emith(" %say = icmp uge i8 %m, 3\n")
|
||||
emith(" br i1 %say, label %say_it, label %mark\n")
|
||||
emith("say_it:\n")
|
||||
emith(" call void @lp_freport(i64 %f, i64 %kept)\n")
|
||||
emith(" %fail = icmp eq i8 %m, 4\n")
|
||||
emith(" br i1 %fail, label %die, label %mark\n")
|
||||
emith("die:\n")
|
||||
emith(" call i32 (i32, ptr, ...) @dprintf(i32 2, ptr @.lpf_failing)\n")
|
||||
emith(" store i8 0, ptr @lp_ft\n")
|
||||
emith(" call void @lp_funlock()\n")
|
||||
emith(" call void @exit(i32 86)\n")
|
||||
emith(" unreachable\n")
|
||||
emith("mark:\n")
|
||||
emith(" call void @lp_fmark_touched()\n")
|
||||
emith(" br label %unlock\n")
|
||||
emith("unlock:\n")
|
||||
emith(" call void @lp_funlock()\n")
|
||||
emith(" br label %tick\n")
|
||||
emith("tick:\n")
|
||||
emith(" store i64 %f1, ptr @lp_frame\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
}
|
||||
function emit_fence_win() -> void {
|
||||
emith("@lp_site = global i32 0\n")
|
||||
emith("define ptr @lp_malloc(i64 %n) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %p = tail call ptr @malloc(i64 %n)\n")
|
||||
emith(" ret ptr %p\n")
|
||||
emith("}\n")
|
||||
emith("define ptr @lp_calloc(i64 %n, i64 %s) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %p = tail call ptr @calloc(i64 %n, i64 %s)\n")
|
||||
emith(" ret ptr %p\n")
|
||||
emith("}\n")
|
||||
emith("define ptr @lp_realloc(ptr %o, i64 %n) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" %p = tail call ptr @realloc(ptr %o, i64 %n)\n")
|
||||
emith(" ret ptr %p\n")
|
||||
emith("}\n")
|
||||
emith("define void @lp_free(ptr %o) {\n")
|
||||
emith("entry:\n")
|
||||
emith(" tail call void @free(ptr %o)\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
emith("define void @lp_mem_play() {\n")
|
||||
emith("entry:\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
emith("define void @lp_mem_settled() {\n")
|
||||
emith("entry:\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
emith("define void @lp_mem_frame() {\n")
|
||||
emith("entry:\n")
|
||||
emith(" ret void\n")
|
||||
emith("}\n")
|
||||
emith("define i64 @lp_mem_kept() {\n")
|
||||
emith("entry:\n")
|
||||
emith(" ret i64 0\n")
|
||||
emith("}\n")
|
||||
emith("define i64 @lp_mem_bad_frames() {\n")
|
||||
emith("entry:\n")
|
||||
emith(" ret i64 0\n")
|
||||
emith("}\n")
|
||||
}
|
||||
|
|
@ -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 ---------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ function is_intrinsic(name: pointer) -> bool {
|
|||
return false
|
||||
}
|
||||
# emit " <r> = <rest>\n" and return r
|
||||
function emit_bind(rest: pointer) -> pointer { let r = nreg(); emit(" "); emit(r); emit(" = "); emit(rest); emit("\n"); return r }
|
||||
function emit_bind(rest: pointer) -> pointer { fence_bind(rest); let r = nreg(); emit(" "); emit(r); emit(" = "); emit(rest); emit("\n"); return r }
|
||||
|
||||
function arg_code(e: Node, i: int) -> pointer { let v = emit_expr(e.kids[i]); return v.code }
|
||||
# a buffer handed to the platform layer: a slice's elements, never its header (as an extern's are)
|
||||
|
|
@ -34,7 +34,7 @@ function emit_intrinsic(name: pointer, e: Node) -> Val {
|
|||
if (name == "resize") {
|
||||
let p = arg_code(e, 0); let n = arg_code(e, 1)
|
||||
let w = emit_bind(`zext i32 {n} to i64`)
|
||||
return val(emit_bind(`call ptr @realloc(ptr {p}, i64 {w})`), "pointer")
|
||||
return val(emit_bind(`call ptr @lp_realloc(ptr {p}, i64 {w})`), "pointer")
|
||||
}
|
||||
# Every asset a Ludic program loads arrives through here — gltf_load, tex_load,
|
||||
# Audio.load, Fs.read_text and the renderer's own shader loads all bottom out at
|
||||
|
|
|
|||
|
|
@ -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") {
|
||||
|
|
|
|||
|
|
@ -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])
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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`)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
function emit_system_fn(sys: Node) -> void {
|
||||
det_enter(sys.s)
|
||||
fence_enter(sys.s)
|
||||
g_cur_scene = sys.c # scene owning this handler (null if global) — for `become`
|
||||
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0; nself = 0
|
||||
ret_ty = "void"
|
||||
|
|
@ -376,6 +377,7 @@ function emit_scene_menu_render() -> void {
|
|||
# @scene_exit_<Name>, called at the transition point (and enter at boot for the
|
||||
# start scene). Emitted for every scene, empty body when the hook is absent.
|
||||
function emit_scene_fn(name: pointer, kind: pointer, body: Node, menu: pointer) -> void {
|
||||
fence_enter(`scene {name}`)
|
||||
ll_t = 0; ll_lbl = 0; g_term = false; loc_reset(); nloop = 0; nself = 0
|
||||
ret_ty = "void"
|
||||
let fbody = buf_new()
|
||||
|
|
@ -670,6 +672,7 @@ function emit_event_fns() -> void {
|
|||
g_ret_label = next
|
||||
g_err_file = g_onlisten[i].file
|
||||
vis_check(ev, en)
|
||||
fence_enter(`@On({en})`)
|
||||
emit_block(g_onlisten[i].a)
|
||||
g_ret_label = "ret"
|
||||
if not g_term { emit(` br label %{next}\n`) }
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -112,7 +112,7 @@ function emit_fs_prelude() -> void {
|
|||
function emit_fs_common() -> void {
|
||||
# duplicate %n bytes of %s into a fresh NUL-terminated buffer
|
||||
emith("define ptr @lp_fs_dup(ptr %s, i32 %n) {\n")
|
||||
emith("entry:\n %nz = zext i32 %n to i64\n %t = add i64 %nz, 1\n %m = call ptr @malloc(i64 %t)\n call ptr @memcpy(ptr %m, ptr %s, i64 %nz)\n")
|
||||
emith("entry:\n %nz = zext i32 %n to i64\n %t = add i64 %nz, 1\n %m = call ptr @lp_malloc(i64 %t)\n call ptr @memcpy(ptr %m, ptr %s, i64 %nz)\n")
|
||||
emith(" %end = getelementptr i8, ptr %m, i32 %n\n store i8 0, ptr %end\n ret ptr %m\n}\n")
|
||||
|
||||
emith("define ptr @lp_fs_strdup(ptr %s) {\n")
|
||||
|
|
@ -142,7 +142,7 @@ function emit_fs_path() -> void {
|
|||
emith("kb:\n %bemp = icmp eq i32 %lb, 0\n br i1 %bemp, label %reta, label %chk\n")
|
||||
emith("reta:\n %ra = call ptr @lp_fs_strdup(ptr %a)\n ret ptr %ra\n")
|
||||
emith("chk:\n %lam1 = sub i32 %la, 1\n %pe = getelementptr i8, ptr %a, i32 %lam1\n %ae = load i8, ptr %pe\n %aei = zext i8 %ae to i32\n %ends = icmp eq i32 %aei, 47\n")
|
||||
emith(" %sep = select i1 %ends, i32 0, i32 1\n %tot = add i32 %la, %lb\n %tot2 = add i32 %tot, %sep\n %tot3 = add i32 %tot2, 1\n %totz = zext i32 %tot3 to i64\n %m = call ptr @malloc(i64 %totz)\n")
|
||||
emith(" %sep = select i1 %ends, i32 0, i32 1\n %tot = add i32 %la, %lb\n %tot2 = add i32 %tot, %sep\n %tot3 = add i32 %tot2, 1\n %totz = zext i32 %tot3 to i64\n %m = call ptr @lp_malloc(i64 %totz)\n")
|
||||
emith(" %laz = zext i32 %la to i64\n call ptr @memcpy(ptr %m, ptr %a, i64 %laz)\n")
|
||||
# write separator if needed
|
||||
emith(" br i1 %ends, label %nosep, label %wsep\n")
|
||||
|
|
@ -185,7 +185,7 @@ function emit_fs_path() -> void {
|
|||
emith("entry:\n %base = call ptr @lp_path_base(ptr %s)\n %ext = call ptr @lp_path_ext(ptr %s)\n")
|
||||
emith(" %bl64 = call i64 @strlen(ptr %base)\n %bl = trunc i64 %bl64 to i32\n %el64 = call i64 @strlen(ptr %ext)\n %el = trunc i64 %el64 to i32\n")
|
||||
# the base and the extension are its own copies: freed once the answer is cut out of them
|
||||
emith(" %keep = sub i32 %bl, %el\n %r = call ptr @lp_fs_dup(ptr %base, i32 %keep)\n call void @free(ptr %base)\n call void @free(ptr %ext)\n ret ptr %r\n}\n")
|
||||
emith(" %keep = sub i32 %bl, %el\n %r = call ptr @lp_fs_dup(ptr %base, i32 %keep)\n call void @lp_free(ptr %base)\n call void @lp_free(ptr %ext)\n ret ptr %r\n}\n")
|
||||
|
||||
emit_fs_normalize()
|
||||
}
|
||||
|
|
@ -196,9 +196,9 @@ function emit_fs_path() -> void {
|
|||
function emit_fs_normalize() -> void {
|
||||
emith("define ptr @lp_path_norm(ptr %s) {\n")
|
||||
emith("entry:\n %len64 = call i64 @strlen(ptr %s)\n %len = trunc i64 %len64 to i32\n %cap = add i32 %len, 2\n %capz = zext i32 %cap to i64\n")
|
||||
emith(" %out = call ptr @malloc(i64 %capz)\n")
|
||||
emith(" %out = call ptr @lp_malloc(i64 %capz)\n")
|
||||
# segst holds output offsets (i32) of each kept segment's start; size cap ints
|
||||
emith(" %stz = mul i32 %cap, 4\n %stzz = zext i32 %stz to i64\n %segst = call ptr @malloc(i64 %stzz)\n")
|
||||
emith(" %stz = mul i32 %cap, 4\n %stzz = zext i32 %stz to i64\n %segst = call ptr @lp_malloc(i64 %stzz)\n")
|
||||
emith(" %b0 = load i8, ptr %s\n %b0i = zext i8 %b0 to i32\n %abs = icmp eq i32 %b0i, 47\n")
|
||||
emith(" %ip = alloca i32\n %op = alloca i32\n %scp = alloca i32\n store i32 0, ptr %ip\n store i32 0, ptr %scp\n")
|
||||
emith(" br i1 %abs, label %ldr, label %noldr\n")
|
||||
|
|
@ -239,8 +239,8 @@ function emit_fs_normalize() -> void {
|
|||
emith("fdefault:\n br label %term\n")
|
||||
emith("term:\n %eo = load i32, ptr %op\n %ez = icmp eq i32 %eo, 0\n br i1 %ez, label %emptyout, label %putnul\n")
|
||||
# the segment table is this call's own: freed on both ways out
|
||||
emith("emptyout:\n store i8 46, ptr %out\n %e1 = getelementptr i8, ptr %out, i32 1\n store i8 0, ptr %e1\n call void @free(ptr %segst)\n ret ptr %out\n")
|
||||
emith("putnul:\n %ep = getelementptr i8, ptr %out, i32 %eo\n store i8 0, ptr %ep\n call void @free(ptr %segst)\n ret ptr %out\n}\n")
|
||||
emith("emptyout:\n store i8 46, ptr %out\n %e1 = getelementptr i8, ptr %out, i32 1\n store i8 0, ptr %e1\n call void @lp_free(ptr %segst)\n ret ptr %out\n")
|
||||
emith("putnul:\n %ep = getelementptr i8, ptr %out, i32 %eo\n store i8 0, ptr %ep\n call void @lp_free(ptr %segst)\n ret ptr %out\n}\n")
|
||||
}
|
||||
|
||||
# ---- Fs.* (libc) -----------------------------------------------------------
|
||||
|
|
@ -274,7 +274,7 @@ function emit_fs_io() -> void {
|
|||
emith("entry:\n store i32 0, ptr %lenout\n %f = call ptr @lp_pak_open(ptr %p, ptr @fn_str_rb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("bad:\n ret ptr null\n")
|
||||
emith("ok:\n call i32 @fseek(ptr %f, i64 0, i32 2)\n %n64 = call i64 @ftell(ptr %f)\n call i32 @fseek(ptr %f, i64 0, i32 0)\n %n = trunc i64 %n64 to i32\n")
|
||||
emith(" %cap = add i64 %n64, 1\n %m = call ptr @malloc(i64 %cap)\n %rd = call i64 @fread(ptr %m, i64 1, i64 %n64, ptr %f)\n call i32 @fclose(ptr %f)\n")
|
||||
emith(" %cap = add i64 %n64, 1\n %m = call ptr @lp_malloc(i64 %cap)\n %rd = call i64 @fread(ptr %m, i64 1, i64 %n64, ptr %f)\n call i32 @fclose(ptr %f)\n")
|
||||
emith(" %rdi = trunc i64 %rd to i32\n %endp = getelementptr i8, ptr %m, i32 %rdi\n store i8 0, ptr %endp\n store i32 %rdi, ptr %lenout\n ret ptr %m\n}\n")
|
||||
|
||||
emith("define ptr @lp_fs_read_text(ptr %p) {\n")
|
||||
|
|
@ -285,14 +285,14 @@ function emit_fs_io() -> void {
|
|||
emith("define i32 @lp_fs_write_text(ptr %p, ptr %s) {\n")
|
||||
# the "<p>.tmp" name is this call's own and goes on both ways out
|
||||
emith("entry:\n %tmp = call ptr @lp_path_join_ext(ptr %p, ptr @fn_str_dottmp)\n %f = call ptr @fopen(ptr %tmp, ptr @fn_str_wb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %bad\n")
|
||||
emith("bad:\n call void @free(ptr %tmp)\n ret i32 0\n")
|
||||
emith("bad:\n call void @lp_free(ptr %tmp)\n ret i32 0\n")
|
||||
emith("ok:\n %n = call i64 @strlen(ptr %s)\n %w = call i64 @fwrite(ptr %s, i64 1, i64 %n, ptr %f)\n call i32 @fclose(ptr %f)\n")
|
||||
emith(" %rr = call i32 @rename(ptr %tmp, ptr %p)\n call void @free(ptr %tmp)\n %ok2 = icmp eq i32 %rr, 0\n %z = zext i1 %ok2 to i32\n ret i32 %z\n}\n")
|
||||
emith(" %rr = call i32 @rename(ptr %tmp, ptr %p)\n call void @lp_free(ptr %tmp)\n %ok2 = icmp eq i32 %rr, 0\n %z = zext i1 %ok2 to i32\n ret i32 %z\n}\n")
|
||||
|
||||
# concat two strings (used to build "<p>.tmp"); local so write_text needs no
|
||||
# dependency on the Os prelude
|
||||
emith("define ptr @lp_path_join_ext(ptr %a, ptr %b) {\n")
|
||||
emith("entry:\n %la = call i64 @strlen(ptr %a)\n %lb = call i64 @strlen(ptr %b)\n %sum = add i64 %la, %lb\n %tot = add i64 %sum, 1\n %m = call ptr @malloc(i64 %tot)\n")
|
||||
emith("entry:\n %la = call i64 @strlen(ptr %a)\n %lb = call i64 @strlen(ptr %b)\n %sum = add i64 %la, %lb\n %tot = add i64 %sum, 1\n %m = call ptr @lp_malloc(i64 %tot)\n")
|
||||
emith(" call ptr @memcpy(ptr %m, ptr %a, i64 %la)\n %m2 = getelementptr i8, ptr %m, i64 %la\n call ptr @memcpy(ptr %m2, ptr %b, i64 %lb)\n %ep = getelementptr i8, ptr %m, i64 %sum\n store i8 0, ptr %ep\n ret ptr %m\n}\n")
|
||||
|
||||
emith("define i32 @lp_fs_append_text(ptr %p, ptr %s) {\n")
|
||||
|
|
@ -309,8 +309,8 @@ function emit_fs_io() -> void {
|
|||
emith("bad:\n ret i32 0\n")
|
||||
# the whole file read and the "<dst>.tmp" name are this call's own: both go on every way out
|
||||
emith("ok:\n %tmp = call ptr @lp_path_join_ext(ptr %dst, ptr @fn_str_dottmp)\n %f = call ptr @fopen(ptr %tmp, ptr @fn_str_wb)\n %fnz = icmp ne ptr %f, null\n br i1 %fnz, label %w, label %badw\n")
|
||||
emith("badw:\n call void @free(ptr %buf)\n call void @free(ptr %tmp)\n ret i32 0\n")
|
||||
emith("w:\n %n = load i32, ptr %lp\n %nz64 = zext i32 %n to i64\n call i64 @fwrite(ptr %buf, i64 1, i64 %nz64, ptr %f)\n call i32 @fclose(ptr %f)\n %rr = call i32 @rename(ptr %tmp, ptr %dst)\n call void @free(ptr %buf)\n call void @free(ptr %tmp)\n %ok2 = icmp eq i32 %rr, 0\n %z = zext i1 %ok2 to i32\n ret i32 %z\n}\n")
|
||||
emith("badw:\n call void @lp_free(ptr %buf)\n call void @lp_free(ptr %tmp)\n ret i32 0\n")
|
||||
emith("w:\n %n = load i32, ptr %lp\n %nz64 = zext i32 %n to i64\n call i64 @fwrite(ptr %buf, i64 1, i64 %nz64, ptr %f)\n call i32 @fclose(ptr %f)\n %rr = call i32 @rename(ptr %tmp, ptr %dst)\n call void @lp_free(ptr %buf)\n call void @lp_free(ptr %tmp)\n %ok2 = icmp eq i32 %rr, 0\n %z = zext i1 %ok2 to i32\n ret i32 %z\n}\n")
|
||||
|
||||
# mkdir: create %p and any missing parents (mkdir -p). Returns 1 if the
|
||||
# directory exists afterwards. Intermediate EEXIST errors are ignored.
|
||||
|
|
@ -320,7 +320,7 @@ function emit_fs_io() -> void {
|
|||
emith("go:\n %sl = icmp eq i32 %ci, 47\n br i1 %sl, label %cut, label %nx\n")
|
||||
emith("cut:\n store i8 0, ptr %pp\n call i32 @mkdir(ptr %dup, i32 493)\n store i8 47, ptr %pp\n br label %nx\n")
|
||||
emith("nx:\n %i1 = add i32 %i, 1\n store i32 %i1, ptr %ip\n br label %lp\n")
|
||||
emith("fin:\n call i32 @mkdir(ptr %dup, i32 493)\n call void @free(ptr %dup)\n %r = call i32 @lp_fs_is_dir(ptr %p)\n ret i32 %r\n}\n")
|
||||
emith("fin:\n call i32 @mkdir(ptr %dup, i32 493)\n call void @lp_free(ptr %dup)\n %r = call i32 @lp_fs_is_dir(ptr %p)\n ret i32 %r\n}\n")
|
||||
}
|
||||
|
||||
# ---- Fs.list (+ sort) ------------------------------------------------------
|
||||
|
|
@ -330,8 +330,8 @@ function emit_fs_dir() -> void {
|
|||
# the readdir buffer before the next call. macOS/BSD dirent: d_name at offset
|
||||
# 21 (documented native layout).
|
||||
emith("define ptr @lp_fs_list(ptr %path) {\n")
|
||||
emith("entry:\n %h = call ptr @malloc(i64 16)\n %d0 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 0\n %d1 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 1\n %d2 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 2\n")
|
||||
emith(" %datap = alloca ptr\n %cntp = alloca i32\n %capp = alloca i32\n %init = call ptr @malloc(i64 128)\n store ptr %init, ptr %datap\n store i32 0, ptr %cntp\n store i32 16, ptr %capp\n")
|
||||
emith("entry:\n %h = call ptr @lp_malloc(i64 16)\n %d0 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 0\n %d1 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 1\n %d2 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 2\n")
|
||||
emith(" %datap = alloca ptr\n %cntp = alloca i32\n %capp = alloca i32\n %init = call ptr @lp_malloc(i64 128)\n store ptr %init, ptr %datap\n store i32 0, ptr %cntp\n store i32 16, ptr %capp\n")
|
||||
emith(" %dir = call ptr @opendir(ptr %path)\n %dnz = icmp ne ptr %dir, null\n br i1 %dnz, label %rd, label %empty\n")
|
||||
emith("rd:\n %de = call ptr @readdir(ptr %dir)\n %denz = icmp ne ptr %de, null\n br i1 %denz, label %ent, label %closed\n")
|
||||
emith("ent:\n %namep = getelementptr i8, ptr %de, i32 21\n")
|
||||
|
|
@ -343,7 +343,7 @@ function emit_fs_dir() -> void {
|
|||
emith("keep:\n %nm = call ptr @lp_fs_strdup(ptr %namep)\n")
|
||||
# grow if full
|
||||
emith(" %cnt = load i32, ptr %cntp\n %cap = load i32, ptr %capp\n %full = icmp sge i32 %cnt, %cap\n br i1 %full, label %grow, label %put\n")
|
||||
emith("grow:\n %nc = mul i32 %cap, 2\n store i32 %nc, ptr %capp\n %ncz = zext i32 %nc to i64\n %nb = mul i64 %ncz, 8\n %old = load ptr, ptr %datap\n %new = call ptr @realloc(ptr %old, i64 %nb)\n store ptr %new, ptr %datap\n br label %put\n")
|
||||
emith("grow:\n %nc = mul i32 %cap, 2\n store i32 %nc, ptr %capp\n %ncz = zext i32 %nc to i64\n %nb = mul i64 %ncz, 8\n %old = load ptr, ptr %datap\n %new = call ptr @lp_realloc(ptr %old, i64 %nb)\n store ptr %new, ptr %datap\n br label %put\n")
|
||||
emith("put:\n %data = load ptr, ptr %datap\n %cnt2 = load i32, ptr %cntp\n %slot = getelementptr ptr, ptr %data, i32 %cnt2\n store ptr %nm, ptr %slot\n %cnt3 = add i32 %cnt2, 1\n store i32 %cnt3, ptr %cntp\n br label %rd\n")
|
||||
emith("closed:\n call i32 @closedir(ptr %dir)\n br label %sortit\n")
|
||||
emith("empty:\n br label %sortit\n")
|
||||
|
|
@ -410,13 +410,13 @@ function emit_fs_mime() -> void {
|
|||
emith("entry:\n %ext = call ptr @lp_path_ext(ptr %path)\n %lc = call ptr @lp_mime_lc(ptr %ext)\n %ip = alloca i32\n store i32 0, ptr %ip\n br label %lp\n")
|
||||
emith(`lp:\n %i = load i32, ptr %ip\n %lt = icmp slt i32 %i, {itoa(n)}\n br i1 %lt, label %body, label %def\n`)
|
||||
emith(`body:\n %kp = getelementptr [{itoa(n)} x {{ ptr, ptr }}], ptr @mime_tbl, i32 0, i32 %i, i32 0\n %k = load ptr, ptr %kp\n %c = call i32 @strcmp(ptr %lc, ptr %k)\n %eq = icmp eq i32 %c, 0\n br i1 %eq, label %hit, label %nx\n`)
|
||||
emith(`hit:\n %vp = getelementptr [{itoa(n)} x {{ ptr, ptr }}], ptr @mime_tbl, i32 0, i32 %i, i32 1\n %v = load ptr, ptr %vp\n %r = call ptr @lp_fs_strdup(ptr %v)\n call void @free(ptr %ext)\n call void @free(ptr %lc)\n ret ptr %r\n`)
|
||||
emith(`hit:\n %vp = getelementptr [{itoa(n)} x {{ ptr, ptr }}], ptr @mime_tbl, i32 0, i32 %i, i32 1\n %v = load ptr, ptr %vp\n %r = call ptr @lp_fs_strdup(ptr %v)\n call void @lp_free(ptr %ext)\n call void @lp_free(ptr %lc)\n ret ptr %r\n`)
|
||||
emith("nx:\n %i1 = add i32 %i, 1\n store i32 %i1, ptr %ip\n br label %lp\n")
|
||||
emith(`def:\n %d = call ptr @lp_fs_strdup(ptr {k_octet})\n call void @free(ptr %ext)\n call void @free(ptr %lc)\n ret ptr %d\n}}\n`)
|
||||
emith(`def:\n %d = call ptr @lp_fs_strdup(ptr {k_octet})\n call void @lp_free(ptr %ext)\n call void @lp_free(ptr %lc)\n ret ptr %d\n}}\n`)
|
||||
|
||||
# lowercase an extension, dropping a leading '.' (ASCII only, for table lookup)
|
||||
emith("define ptr @lp_mime_lc(ptr %s) {\n")
|
||||
emith("entry:\n %l64 = call i64 @strlen(ptr %s)\n %l = trunc i64 %l64 to i32\n %cap = add i64 %l64, 1\n %out = call ptr @malloc(i64 %cap)\n")
|
||||
emith("entry:\n %l64 = call i64 @strlen(ptr %s)\n %l = trunc i64 %l64 to i32\n %cap = add i64 %l64, 1\n %out = call ptr @lp_malloc(i64 %cap)\n")
|
||||
emith(" %b0 = load i8, ptr %s\n %b0i = zext i8 %b0 to i32\n %isdot = icmp eq i32 %b0i, 46\n %start = select i1 %isdot, i32 1, i32 0\n")
|
||||
emith(" %ip = alloca i32\n %op = alloca i32\n store i32 %start, ptr %ip\n store i32 0, ptr %op\n br label %lp\n")
|
||||
emith("lp:\n %i = load i32, ptr %ip\n %pp = getelementptr i8, ptr %s, i32 %i\n %c = load i8, ptr %pp\n %ci = zext i8 %c to i32\n %z = icmp eq i32 %ci, 0\n br i1 %z, label %done, label %go\n")
|
||||
|
|
@ -431,22 +431,22 @@ function emit_fs_mime() -> void {
|
|||
function emit_fs_sniff() -> void {
|
||||
emith("define ptr @lp_mime_sniff(ptr %path) {\n")
|
||||
emith("entry:\n %f = call ptr @fopen(ptr %path, ptr @fn_str_rb)\n %nz = icmp ne ptr %f, null\n br i1 %nz, label %ok, label %fallback\n")
|
||||
emith("ok:\n %buf = call ptr @malloc(i64 16)\n %rd = call i64 @fread(ptr %buf, i64 1, i64 8, ptr %f)\n call i32 @fclose(ptr %f)\n %rdi = trunc i64 %rd to i32\n %has4 = icmp sge i32 %rdi, 4\n br i1 %has4, label %chk, label %nomatch\n")
|
||||
emith("ok:\n %buf = call ptr @lp_malloc(i64 16)\n %rd = call i64 @fread(ptr %buf, i64 1, i64 8, ptr %f)\n call i32 @fclose(ptr %f)\n %rdi = trunc i64 %rd to i32\n %has4 = icmp sge i32 %rdi, 4\n br i1 %has4, label %chk, label %nomatch\n")
|
||||
emith("chk:\n %b0p = getelementptr i8, ptr %buf, i32 0\n %b0 = load i8, ptr %b0p\n %b0i = zext i8 %b0 to i32\n %b1p = getelementptr i8, ptr %buf, i32 1\n %b1 = load i8, ptr %b1p\n %b1i = zext i8 %b1 to i32\n %b2p = getelementptr i8, ptr %buf, i32 2\n %b2 = load i8, ptr %b2p\n %b2i = zext i8 %b2 to i32\n %b3p = getelementptr i8, ptr %buf, i32 3\n %b3 = load i8, ptr %b3p\n %b3i = zext i8 %b3 to i32\n")
|
||||
# PNG: 89 50 4E 47
|
||||
emith(" %p0 = icmp eq i32 %b0i, 137\n %p1 = icmp eq i32 %b1i, 80\n %p2 = icmp eq i32 %b2i, 78\n %p3 = icmp eq i32 %b3i, 71\n %pa = and i1 %p0, %p1\n %pb = and i1 %pa, %p2\n %pc = and i1 %pb, %p3\n br i1 %pc, label %png, label %cj\n")
|
||||
emith("png:\n %rpng = call ptr @lp_fs_strdup(ptr @fn_sig_png)\n call void @free(ptr %buf)\n ret ptr %rpng\n")
|
||||
emith("png:\n %rpng = call ptr @lp_fs_strdup(ptr @fn_sig_png)\n call void @lp_free(ptr %buf)\n ret ptr %rpng\n")
|
||||
# JPEG: FF D8 FF
|
||||
emith("cj:\n %j0 = icmp eq i32 %b0i, 255\n %j1 = icmp eq i32 %b1i, 216\n %j2 = icmp eq i32 %b2i, 255\n %ja = and i1 %j0, %j1\n %jb = and i1 %ja, %j2\n br i1 %jb, label %jpg, label %cg\n")
|
||||
emith("jpg:\n %rjpg = call ptr @lp_fs_strdup(ptr @fn_sig_jpg)\n call void @free(ptr %buf)\n ret ptr %rjpg\n")
|
||||
emith("jpg:\n %rjpg = call ptr @lp_fs_strdup(ptr @fn_sig_jpg)\n call void @lp_free(ptr %buf)\n ret ptr %rjpg\n")
|
||||
# GIF: 47 49 46
|
||||
emith("cg:\n %g0 = icmp eq i32 %b0i, 71\n %g1 = icmp eq i32 %b1i, 73\n %g2 = icmp eq i32 %b2i, 70\n %ga = and i1 %g0, %g1\n %gb = and i1 %ga, %g2\n br i1 %gb, label %gif, label %cp\n")
|
||||
emith("gif:\n %rgif = call ptr @lp_fs_strdup(ptr @fn_sig_gif)\n call void @free(ptr %buf)\n ret ptr %rgif\n")
|
||||
emith("gif:\n %rgif = call ptr @lp_fs_strdup(ptr @fn_sig_gif)\n call void @lp_free(ptr %buf)\n ret ptr %rgif\n")
|
||||
# PDF: 25 50 44 46
|
||||
emith("cp:\n %q0 = icmp eq i32 %b0i, 37\n %q1 = icmp eq i32 %b1i, 80\n %q2 = icmp eq i32 %b2i, 68\n %q3 = icmp eq i32 %b3i, 70\n %qa = and i1 %q0, %q1\n %qb = and i1 %qa, %q2\n %qc = and i1 %qb, %q3\n br i1 %qc, label %pdf, label %nomatch\n")
|
||||
emith("pdf:\n %rpdf = call ptr @lp_fs_strdup(ptr @fn_sig_pdf)\n call void @free(ptr %buf)\n ret ptr %rpdf\n")
|
||||
emith("pdf:\n %rpdf = call ptr @lp_fs_strdup(ptr @fn_sig_pdf)\n call void @lp_free(ptr %buf)\n ret ptr %rpdf\n")
|
||||
# the 16 bytes read are this call's own and go on every way out
|
||||
emith("nomatch:\n call void @free(ptr %buf)\n br label %fallback\n")
|
||||
emith("nomatch:\n call void @lp_free(ptr %buf)\n br label %fallback\n")
|
||||
emith("fallback:\n %r = call ptr @lp_mime_of(ptr %path)\n ret ptr %r\n}\n")
|
||||
|
||||
# the small string constants the Fs/Path/Mime runtime references
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -164,13 +164,13 @@ function emit_os_prelude() -> void {
|
|||
# concatenate two NUL-terminated strings into a fresh malloc'd buffer
|
||||
emith("define ptr @lp_os_join2(ptr %a, ptr %b) {\n")
|
||||
emith("entry:\n %la = call i64 @strlen(ptr %a)\n %lb = call i64 @strlen(ptr %b)\n")
|
||||
emith(" %sum = add i64 %la, %lb\n %tot = add i64 %sum, 1\n %m = call ptr @malloc(i64 %tot)\n")
|
||||
emith(" %sum = add i64 %la, %lb\n %tot = add i64 %sum, 1\n %m = call ptr @lp_malloc(i64 %tot)\n")
|
||||
emith(" call ptr @memcpy(ptr %m, ptr %a, i64 %la)\n")
|
||||
emith(" %m2 = getelementptr i8, ptr %m, i64 %la\n call ptr @memcpy(ptr %m2, ptr %b, i64 %lb)\n")
|
||||
emith(" %end = getelementptr i8, ptr %m, i64 %sum\n store i8 0, ptr %end\n ret ptr %m\n}\n")
|
||||
|
||||
emith("define ptr @lp_os_join3(ptr %a, ptr %b, ptr %c) {\n")
|
||||
emith("entry:\n %ab = call ptr @lp_os_join2(ptr %a, ptr %b)\n %r = call ptr @lp_os_join2(ptr %ab, ptr %c)\n call void @free(ptr %ab)\n ret ptr %r\n}\n")
|
||||
emith("entry:\n %ab = call ptr @lp_os_join2(ptr %a, ptr %b)\n %r = call ptr @lp_os_join2(ptr %ab, ptr %c)\n call void @lp_free(ptr %ab)\n ret ptr %r\n}\n")
|
||||
|
||||
# Windows has its own known folders and no uname; emit_win.ludic supplies them
|
||||
if g_target_win { emit_os_win_known(k_dot, k_slash); return }
|
||||
|
|
@ -204,9 +204,9 @@ function emit_os_prelude() -> void {
|
|||
emith("define i32 @lp_os_is_darwin() {\n")
|
||||
emith("entry:\n %c = load i32, ptr @L_os_darwin\n %known = icmp sge i32 %c, 0\n br i1 %known, label %done, label %probe\n")
|
||||
emith("done:\n ret i32 %c\n")
|
||||
emith("probe:\n %buf = call ptr @malloc(i64 8192)\n call i32 @uname(ptr %buf)\n")
|
||||
emith("probe:\n %buf = call ptr @lp_malloc(i64 8192)\n call i32 @uname(ptr %buf)\n")
|
||||
emith(` %cd = call i32 @strncmp(ptr %buf, ptr {k_darw}, i64 6)\n %isd = icmp eq i32 %cd, 0\n`)
|
||||
emith(" %r = select i1 %isd, i32 1, i32 0\n call void @free(ptr %buf)\n store i32 %r, ptr @L_os_darwin\n ret i32 %r\n}\n")
|
||||
emith(" %r = select i1 %isd, i32 1, i32 0\n call void @lp_free(ptr %buf)\n store i32 %r, ptr @L_os_darwin\n ret i32 %r\n}\n")
|
||||
|
||||
# $<var> when it is set and non-empty, else $HOME<rel>; the result always ends in '/'
|
||||
emith("define ptr @lp_os_xdg(ptr %var, ptr %rel) {\n")
|
||||
|
|
@ -219,7 +219,7 @@ function emit_os_prelude() -> void {
|
|||
emith("define ptr @lp_os_known(ptr %app, ptr %mac, ptr %var, ptr %rel) {\n")
|
||||
emith("entry:\n %d = call i32 @lp_os_is_darwin()\n %isd = icmp ne i32 %d, 0\n br i1 %isd, label %m, label %x\n")
|
||||
emith("m:\n %h = call ptr @lp_os_home()\n %r1 = call ptr @lp_os_join3(ptr %h, ptr %mac, ptr %app)\n ret ptr %r1\n")
|
||||
emith("x:\n %b = call ptr @lp_os_xdg(ptr %var, ptr %rel)\n %r2 = call ptr @lp_os_join2(ptr %b, ptr %app)\n call void @free(ptr %b)\n ret ptr %r2\n}\n")
|
||||
emith("x:\n %b = call ptr @lp_os_xdg(ptr %var, ptr %rel)\n %r2 = call ptr @lp_os_join2(ptr %b, ptr %app)\n call void @lp_free(ptr %b)\n ret ptr %r2\n}\n")
|
||||
|
||||
emith(`define ptr @lp_os_save_dir(ptr %app) {{\n %r = call ptr @lp_os_known(ptr %app, ptr {k_appsp}, ptr {k_xdg_data}, ptr {k_dot_share})\n ret ptr %r\n}}\n`)
|
||||
emith(`define ptr @lp_os_config_dir(ptr %app) {{\n %r = call ptr @lp_os_known(ptr %app, ptr {k_appsp}, ptr {k_xdg_cfg}, ptr {k_dot_config})\n ret ptr %r\n}}\n`)
|
||||
|
|
@ -230,7 +230,7 @@ function emit_os_prelude() -> void {
|
|||
# snapshot the caller may iterate or index like any other []string.
|
||||
emith("define ptr @lp_os_args() {\n")
|
||||
emith("entry:\n %c = load i32, ptr @L_argc\n %v = load ptr, ptr @L_argv\n")
|
||||
emith(" %h = call ptr @malloc(i64 16)\n")
|
||||
emith(" %h = call ptr @lp_malloc(i64 16)\n")
|
||||
emith(" %d0 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 0\n store ptr %v, ptr %d0\n")
|
||||
emith(" %d1 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 1\n store i32 %c, ptr %d1\n")
|
||||
emith(" %d2 = getelementptr inbounds %LSlice, ptr %h, i32 0, i32 2\n store i32 %c, ptr %d2\n")
|
||||
|
|
@ -242,7 +242,7 @@ function emit_os_prelude() -> void {
|
|||
emith("define ptr @lp_os_uts() {\n")
|
||||
emith("entry:\n %c = load ptr, ptr @L_os_uts\n %have = icmp ne ptr %c, null\n br i1 %have, label %done, label %make\n")
|
||||
emith("done:\n ret ptr %c\n")
|
||||
emith("make:\n %buf = call ptr @malloc(i64 8192)\n call i32 @uname(ptr %buf)\n store ptr %buf, ptr @L_os_uts\n ret ptr %buf\n}\n")
|
||||
emith("make:\n %buf = call ptr @lp_malloc(i64 8192)\n call i32 @uname(ptr %buf)\n store ptr %buf, ptr @L_os_uts\n ret ptr %buf\n}\n")
|
||||
|
||||
# platform(): uname sysname (field 0, portable) mapped to a short id
|
||||
emith("define ptr @lp_os_platform() {\n")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
94458
selfhost/ludicc.seed.ll
94458
selfhost/ludicc.seed.ll
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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 +72 B (2 made, 0 freed)") and named and (counted == "kept 72 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 +4 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,
|
||||
|
|
@ -1081,6 +1111,9 @@ function cmd_dev_test() -> int {
|
|||
reject_case("rejected/long_into_int", "4294967295 does not fit one; it is a long", "a long literal given to an int is refused, not wrapped")
|
||||
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/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")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue