diff --git a/runtime/native/value.ludic b/runtime/native/value.ludic index 2140d677..5a89158b 100644 --- a/runtime/native/value.ludic +++ b/runtime/native/value.ludic @@ -258,15 +258,19 @@ function value_num_set(v: Val, n: int) -> void { } # a number's text, kept on the Value until the number changes (a count shown in a HUD every frame wrote # its text every frame; Ludic frees nothing) +# interned, and the text written for it given back: a number that changed dropped its last text function value_num_text(v: Val) -> string { if v.txt != null { return v.txt } - if v.tag == 1 { v.txt = string(v.num) } + var t: string = null + if v.tag == 1 { t = string(v.num) } else if v.tag == 7 { let f = float_from_bits(v.num) - if f == float(int(f)) { v.txt = string(int(f)) } else { v.txt = string(f) } + if f == float(int(f)) { t = string(int(f)) } else { t = string(f) } } - else if v.tag == 2 { v.txt = json_fixed_str(v.num) } - if v.txt == null { return "" } + else if v.tag == 2 { t = json_fixed_str(v.num) } + if t == null { return "" } + v.txt = intern(t) + free(t) return v.txt } # a list or an object emptied in place, its room kept - for one filled again every frame diff --git a/selfhost/backend/emit_fence_ir.ludic b/selfhost/backend/emit_fence_ir.ludic index 47f47370..e4dfc5ee 100644 --- a/selfhost/backend/emit_fence_ir.ludic +++ b/selfhost/backend/emit_fence_ir.ludic @@ -253,6 +253,16 @@ function emit_fence_entry() -> void { emith("}\n") emith("define void @lp_free(ptr %o) {\n") emith("entry:\n") + emith(" %ist = ptrtoint ptr @lp_istore to i64\n") # an interned text is the store's: never freed + emith(" %ien = add i64 %ist, 4194304\n") + emith(" %ix = ptrtoint ptr %o to i64\n") + emith(" %ige = icmp uge i64 %ix, %ist\n") + emith(" %ilt = icmp ult i64 %ix, %ien\n") + emith(" %iin = and i1 %ige, %ilt\n") + emith(" br i1 %iin, label %istore, label %free0\n") + emith("istore:\n") + emith(" ret void\n") + emith("free0:\n") emith(" %x = ptrtoint ptr %o to i64\n") emith(" %lo = load i64, ptr @lp_ar_lo\n") emith(" %hi = load i64, ptr @lp_ar_hi\n") @@ -1837,6 +1847,16 @@ function emit_fence_win() -> void { emith("}\n") emith("define void @lp_free(ptr %o) {\n") emith("entry:\n") + emith(" %ist = ptrtoint ptr @lp_istore to i64\n") # an interned text is the store's: never freed + emith(" %ien = add i64 %ist, 4194304\n") + emith(" %ix = ptrtoint ptr %o to i64\n") + emith(" %ige = icmp uge i64 %ix, %ist\n") + emith(" %ilt = icmp ult i64 %ix, %ien\n") + emith(" %iin = and i1 %ige, %ilt\n") + emith(" br i1 %iin, label %istore, label %free0\n") + emith("istore:\n") + emith(" ret void\n") + emith("free0:\n") emith(" tail call void @free(ptr %o)\n") emith(" ret void\n") emith("}\n") @@ -1872,9 +1892,24 @@ function emit_fence_win() -> void { function emit_fence_intern() -> void { emith("@lp_itab = global [65536 x ptr] zeroinitializer\n") emith("@lp_icount = global i64 0\n") + # the interned texts' bytes: one store in the binary, filled front to back and never given back, so + # a text seen for the first time costs no allocation (a HUD's clock made one a minute); the heap + # only past its 4 MB + emith("@lp_istore = internal global [4194304 x i8] zeroinitializer\n") + emith("@lp_ipos = internal global i64 0\n") emith("define internal ptr @lp_copystr(ptr %s, i64 %n) {\n") emith("entry:\n") emith(" %n1 = add i64 %n, 1\n") + emith(" %pos = load i64, ptr @lp_ipos\n") + emith(" %end = add i64 %pos, %n1\n") + emith(" %fits = icmp ule i64 %end, 4194304\n") + emith(" br i1 %fits, label %bump, label %heap\n") + emith("bump:\n") + emith(" %b = getelementptr inbounds [4194304 x i8], ptr @lp_istore, i64 0, i64 %pos\n") + emith(" store i64 %end, ptr @lp_ipos\n") + emith(" call ptr @memcpy(ptr %b, ptr %s, i64 %n1)\n") + emith(" ret ptr %b\n") + emith("heap:\n") emith(" %p = call ptr @lp_malloc(i64 %n1)\n") emith(" call ptr @memcpy(ptr %p, ptr %s, i64 %n1)\n") emith(" ret ptr %p\n")