map packs: Fs.mount / Fs.unmount at run time, and a map's bakes under assets/baked/maps/<map>/
Maps ship outside the game, each one a content-addressed pack (.lmap: the .lpak format) downloaded to the save root. The runtime already served reads from packs mounted at boot (packs.index); now one can come and go while the game runs: - Fs.mount(path) -> bool maps a pack over the ones mounted before it (searched first, as a later packs.index line is); Fs.unmount(path) -> bool gives the mapping back (munmap, UnmapViewOfFile on Windows) and closes the gap in the search order. Each slot keeps its length and path for it. A FILE* still open over one of its entries (a baked_open_range) is closed first. Still 8 packs at most. - baked_path(map, file) is assets/baked/maps/<map>/<file> for a map's bake - what its .lmap carries and the game's own pack never does - and assets/baked/<file> for the rest. The IR assembles for macOS and Windows (llvm-as). Compile-only: nothing mounted or run here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
c883687ace
commit
e1585de9b6
6 changed files with 39541 additions and 39098 deletions
|
|
@ -10,10 +10,11 @@ export state BakeState {
|
||||||
said: []string = new []string # the keys a dev build has said it made at run time
|
said: []string = new []string # the keys a dev build has said it made at run time
|
||||||
}
|
}
|
||||||
|
|
||||||
# assets/baked/<map>/<file>, or assets/baked/<file> for no map
|
# a map's bake: assets/baked/maps/<map>/<file>, which ships in that map's own pack (.lmap), never the
|
||||||
|
# game's; anything else: assets/baked/<file>, in the game's pack
|
||||||
export function baked_path(map: string, file: string) -> string {
|
export function baked_path(map: string, file: string) -> string {
|
||||||
if len(map) == 0 { return "assets/baked/" + file }
|
if len(map) == 0 { return "assets/baked/" + file }
|
||||||
return "assets/baked/" + map + "/" + file
|
return "assets/baked/maps/" + map + "/" + file
|
||||||
}
|
}
|
||||||
|
|
||||||
# the payload of a baked file that is this bake at this version, or null (missing, not a baked file,
|
# the payload of a baked file that is this bake at this version, or null (missing, not a baked file,
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ function is_fs_ns(meth: pointer) -> bool {
|
||||||
if (meth == "exists") or (meth == "is_dir") or (meth == "read_text") { return true }
|
if (meth == "exists") or (meth == "is_dir") or (meth == "read_text") { return true }
|
||||||
if (meth == "write_text") or (meth == "append_text") or (meth == "remove") { return true }
|
if (meth == "write_text") or (meth == "append_text") or (meth == "remove") { return true }
|
||||||
if (meth == "size") or (meth == "mkdir") or (meth == "copy") or (meth == "list") { return true }
|
if (meth == "size") or (meth == "mkdir") or (meth == "copy") or (meth == "list") { return true }
|
||||||
|
if (meth == "mount") or (meth == "unmount") { return true }
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
function is_path_ns(meth: pointer) -> bool {
|
function is_path_ns(meth: pointer) -> bool {
|
||||||
|
|
@ -55,6 +56,9 @@ function emit_fs_ns(meth: pointer, e: Node) -> Val {
|
||||||
use_pak()
|
use_pak()
|
||||||
let a = emit_expr(e.kids[0])
|
let a = emit_expr(e.kids[0])
|
||||||
if (meth == "exists") { return val(emit_bind(`call i32 @lp_fs_exists(ptr {a.code})`), "bool") }
|
if (meth == "exists") { return val(emit_bind(`call i32 @lp_fs_exists(ptr {a.code})`), "bool") }
|
||||||
|
# a pack (.lpak / .lmap) mounted at run time over the packs.index ones, searched first; and let go
|
||||||
|
if (meth == "mount") { return val(emit_bind(`call i32 @lp_pak_mount(ptr {a.code})`), "bool") }
|
||||||
|
if (meth == "unmount") { return val(emit_bind(`call i32 @lp_pak_unmount(ptr {a.code})`), "bool") }
|
||||||
if (meth == "is_dir") { return val(emit_bind(`call i32 @lp_fs_is_dir(ptr {a.code})`), "bool") }
|
if (meth == "is_dir") { return val(emit_bind(`call i32 @lp_fs_is_dir(ptr {a.code})`), "bool") }
|
||||||
if (meth == "read_text") { return val(emit_bind(`call ptr @lp_fs_read_text(ptr {a.code})`), "string") }
|
if (meth == "read_text") { return val(emit_bind(`call ptr @lp_fs_read_text(ptr {a.code})`), "string") }
|
||||||
if (meth == "remove") { return val(emit_bind(`call i32 @lp_fs_remove(ptr {a.code})`), "bool") }
|
if (meth == "remove") { return val(emit_bind(`call i32 @lp_fs_remove(ptr {a.code})`), "bool") }
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,7 @@ function emit_pak_prelude() -> void {
|
||||||
emith("declare i64 @lseek(i32, i64, i32)\n")
|
emith("declare i64 @lseek(i32, i64, i32)\n")
|
||||||
if not g_mmap_declared { emith("declare ptr @mmap(ptr, i64, i32, i32, i32, i64)\n"); g_mmap_declared = true }
|
if not g_mmap_declared { emith("declare ptr @mmap(ptr, i64, i32, i32, i32, i64)\n"); g_mmap_declared = true }
|
||||||
emith("declare ptr @fmemopen(ptr, i64, ptr)\n")
|
emith("declare ptr @fmemopen(ptr, i64, ptr)\n")
|
||||||
|
emith("declare i32 @munmap(ptr, i64)\n")
|
||||||
emith("declare i32 @chdir(ptr)\n")
|
emith("declare i32 @chdir(ptr)\n")
|
||||||
emith("declare i32 @_NSGetExecutablePath(ptr, ptr)\n")
|
emith("declare i32 @_NSGetExecutablePath(ptr, ptr)\n")
|
||||||
}
|
}
|
||||||
|
|
@ -109,6 +110,8 @@ function emit_pak_prelude() -> void {
|
||||||
# up to 8 packs may be mounted; the base pointer of each mapping, in mount order
|
# up to 8 packs may be mounted; the base pointer of each mapping, in mount order
|
||||||
emith("@L_pak_base = internal global [8 x ptr] zeroinitializer\n")
|
emith("@L_pak_base = internal global [8 x ptr] zeroinitializer\n")
|
||||||
emith("@L_pak_n = internal global i32 0\n")
|
emith("@L_pak_n = internal global i32 0\n")
|
||||||
|
emith("@L_pak_len = internal global [8 x i64] zeroinitializer\n") # each mapping's length, for unmount
|
||||||
|
emith("@L_pak_path = internal global [8 x ptr] zeroinitializer\n") # and the path it was mounted from
|
||||||
# the splash, named by packs.index and served out of the pack like any asset
|
# the splash, named by packs.index and served out of the pack like any asset
|
||||||
emith("@L_pak_splash = internal global ptr null\n")
|
emith("@L_pak_splash = internal global ptr null\n")
|
||||||
emith("@L_pak_splashbg = internal global i32 0\n")
|
emith("@L_pak_splashbg = internal global i32 0\n")
|
||||||
|
|
@ -128,6 +131,7 @@ function emit_pak_prelude() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
emit_pak_mount()
|
emit_pak_mount()
|
||||||
|
emit_pak_unmount()
|
||||||
emit_pak_lookup()
|
emit_pak_lookup()
|
||||||
emit_pak_open()
|
emit_pak_open()
|
||||||
emit_pak_boot()
|
emit_pak_boot()
|
||||||
|
|
@ -157,11 +161,36 @@ function emit_pak_mount() -> void {
|
||||||
# "LPAK" read as a little-endian i32
|
# "LPAK" read as a little-endian i32
|
||||||
emith("chk:\n %m = load i32, ptr %base\n %okm = icmp eq i32 %m, 1262571596\n br i1 %okm, label %keep, label %clo\n")
|
emith("chk:\n %m = load i32, ptr %base\n %okm = icmp eq i32 %m, 1262571596\n br i1 %okm, label %keep, label %clo\n")
|
||||||
emith("keep:\n %slot = getelementptr [8 x ptr], ptr @L_pak_base, i32 0, i32 %n\n store ptr %base, ptr %slot\n")
|
emith("keep:\n %slot = getelementptr [8 x ptr], ptr @L_pak_base, i32 0, i32 %n\n store ptr %base, ptr %slot\n")
|
||||||
|
# its length and path too, so Fs.unmount can find it and give the mapping back
|
||||||
|
emith(" %lslot = getelementptr [8 x i64], ptr @L_pak_len, i32 0, i32 %n\n store i64 %end, ptr %lslot\n")
|
||||||
|
emith(" %pcopy = call ptr @lp_fs_strdup(ptr %path)\n %pslot = getelementptr [8 x ptr], ptr @L_pak_path, i32 0, i32 %n\n store ptr %pcopy, ptr %pslot\n")
|
||||||
emith(" %n1 = add i32 %n, 1\n store i32 %n1, ptr @L_pak_n\n %c1 = call i32 @close(i32 %fd)\n ret i32 1\n")
|
emith(" %n1 = add i32 %n, 1\n store i32 %n1, ptr @L_pak_n\n %c1 = call i32 @close(i32 %fd)\n ret i32 1\n")
|
||||||
emith("clo:\n %c2 = call i32 @close(i32 %fd)\n ret i32 0\n")
|
emith("clo:\n %c2 = call i32 @close(i32 %fd)\n ret i32 0\n")
|
||||||
emith("no:\n ret i32 0\n}\n")
|
emith("no:\n ret i32 0\n}\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# unmount(path): the pack mounted from exactly that path given back and taken out of the search, the
|
||||||
|
# ones after it moving up a slot; 1 when it was mounted. A map pack downloaded at run time comes and
|
||||||
|
# goes (Fs.mount / Fs.unmount); a FILE* still open over one of its entries must be closed first
|
||||||
|
function emit_pak_unmount() -> void {
|
||||||
|
emith("define i32 @lp_pak_unmount(ptr %path) {\n")
|
||||||
|
emith("entry:\n %n = load i32, ptr @L_pak_n\n %ip = alloca i32\n store i32 0, ptr %ip\n br label %find\n")
|
||||||
|
emith("find:\n %i = load i32, ptr %ip\n %end = icmp sge i32 %i, %n\n br i1 %end, label %no, label %cmp\n")
|
||||||
|
emith("cmp:\n %pp = getelementptr [8 x ptr], ptr @L_pak_path, i32 0, i32 %i\n %p = load ptr, ptr %pp\n %isnull = icmp eq ptr %p, null\n br i1 %isnull, label %next, label %str\n")
|
||||||
|
emith("str:\n %c = call i32 @strcmp(ptr %p, ptr %path)\n %same = icmp eq i32 %c, 0\n br i1 %same, label %drop, label %next\n")
|
||||||
|
emith("next:\n %i1 = add i32 %i, 1\n store i32 %i1, ptr %ip\n br label %find\n")
|
||||||
|
emith("drop:\n %bp = getelementptr [8 x ptr], ptr @L_pak_base, i32 0, i32 %i\n %base = load ptr, ptr %bp\n %lp = getelementptr [8 x i64], ptr @L_pak_len, i32 0, i32 %i\n %len = load i64, ptr %lp\n")
|
||||||
|
if g_target_win { emith(" %u = call i32 @UnmapViewOfFile(ptr %base)\n") } else { emith(" %u = call i32 @munmap(ptr %base, i64 %len)\n") }
|
||||||
|
emith(" call void @lp_free(ptr %p)\n %jp = alloca i32\n store i32 %i, ptr %jp\n br label %shift\n")
|
||||||
|
emith("shift:\n %j = load i32, ptr %jp\n %j1 = add i32 %j, 1\n %more = icmp slt i32 %j1, %n\n br i1 %more, label %mv, label %done\n")
|
||||||
|
emith("mv:\n %sb = getelementptr [8 x ptr], ptr @L_pak_base, i32 0, i32 %j1\n %vb = load ptr, ptr %sb\n %db = getelementptr [8 x ptr], ptr @L_pak_base, i32 0, i32 %j\n store ptr %vb, ptr %db\n")
|
||||||
|
emith(" %sl = getelementptr [8 x i64], ptr @L_pak_len, i32 0, i32 %j1\n %vl = load i64, ptr %sl\n %dl = getelementptr [8 x i64], ptr @L_pak_len, i32 0, i32 %j\n store i64 %vl, ptr %dl\n")
|
||||||
|
emith(" %sp = getelementptr [8 x ptr], ptr @L_pak_path, i32 0, i32 %j1\n %vp = load ptr, ptr %sp\n %dp = getelementptr [8 x ptr], ptr @L_pak_path, i32 0, i32 %j\n store ptr %vp, ptr %dp\n")
|
||||||
|
emith(" store i32 %j1, ptr %jp\n br label %shift\n")
|
||||||
|
emith("done:\n %n1 = sub i32 %n, 1\n store i32 %n1, ptr @L_pak_n\n %last = getelementptr [8 x ptr], ptr @L_pak_path, i32 0, i32 %n1\n store ptr null, ptr %last\n ret i32 1\n")
|
||||||
|
emith("no:\n ret i32 0\n}\n")
|
||||||
|
}
|
||||||
|
|
||||||
# ---- lookup -----------------------------------------------------------------
|
# ---- lookup -----------------------------------------------------------------
|
||||||
|
|
||||||
function emit_pak_lookup() -> void {
|
function emit_pak_lookup() -> void {
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,7 @@ function emit_win_prelude() -> void {
|
||||||
emith("declare i32 @FindClose(ptr)\n")
|
emith("declare i32 @FindClose(ptr)\n")
|
||||||
emith("declare ptr @CreateFileMappingA(ptr, ptr, i32, i32, i32, ptr)\n")
|
emith("declare ptr @CreateFileMappingA(ptr, ptr, i32, i32, i32, ptr)\n")
|
||||||
emith("declare ptr @MapViewOfFile(ptr, i32, i32, i32, i64)\n")
|
emith("declare ptr @MapViewOfFile(ptr, i32, i32, i32, i64)\n")
|
||||||
|
emith("declare i32 @UnmapViewOfFile(ptr)\n")
|
||||||
emith("declare i32 @CloseHandle(ptr)\n")
|
emith("declare i32 @CloseHandle(ptr)\n")
|
||||||
emith("declare i32 @GetModuleFileNameA(ptr, ptr, i32)\n")
|
emith("declare i32 @GetModuleFileNameA(ptr, ptr, i32)\n")
|
||||||
emith("declare i32 @GetTempPathA(i32, ptr)\n")
|
emith("declare i32 @GetTempPathA(i32, ptr)\n")
|
||||||
|
|
|
||||||
38402
selfhost/ludicc.seed.ll
38402
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
Loading…
Add table
Add a link
Reference in a new issue