rt_init opened every windowed program's window before main, and render3d's gvk_open then only retitled it. win_open makes the window when there is none (cocoa.ll and win32.ll alike), so for a program that has gvk_open the runtime now leaves it (window_later(), an intrinsic: windowed and render3d present): a process that never reaches the renderer - Maroon Lake's launcher watcher, which only spawns the game and waits - never makes a window, an NSApplication or AppKit's heap. Audited every window native reachable before the renderer opens (settings, telemetry, rescue, the watcher reach App.* and Input.*): on macOS each that loads W_win / W_app / W_view / W_mtl / W_glctx checks it for null; win_close, win_running, win_text, win_held, win_cursor_mode, win_gl_scale and the pad and touch reads load none. On Windows each that loads W_hwnd / W_hdc checks it; the rest load none. Measured, windowed, R3D_DEV=1 R3D_PLAYTEST=2, both killed after: - the game straight to play: the window, the Vulkan swapchain (1920x1080) and the valley's models come up, alive at 30 s; - the launcher (R3D_GAME=launcher): the watcher 11 MB -> 3.7 MB, its launcher window alive at 10 s. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
122 lines
9.6 KiB
Text
122 lines
9.6 KiB
Text
# emit_intrin2.ludic — the rest of the low-level intrinsics: raw memory, fixed
|
|
# memory access, pointer arithmetic, stdio, time, and the windowing hooks.
|
|
# Matches compiler/back/ir_intrin.c. Reached from emit_intrinsic's fall-through.
|
|
|
|
var g_windowed: bool = false # headless by default (games read stdin / dump PPM)
|
|
var g_target_win: bool = false # a Windows target: the header emits emit_win_prelude and the libSystem declares are skipped
|
|
|
|
# the IR that loads stdout (fd 1) or stderr (fd 2) as a FILE*. macOS names them as
|
|
# globals; the UCRT hands them out through a function (see emit_win.ludic).
|
|
function stdstream_rhs(fd: int) -> pointer {
|
|
if g_target_win { return `call ptr @lp_win_stdio(i32 {itoa(fd)})` }
|
|
if fd == 2 { return "load ptr, ptr @__stderrp" }
|
|
return "load ptr, ptr @__stdoutp"
|
|
}
|
|
|
|
function is_intrinsic2(name: pointer) -> bool {
|
|
if (name == "free") or (name == "fill") { return true }
|
|
if (name == "offset") or (name == "read_char") { return true }
|
|
if (name == "as_fixed") or (name == "as_int") { return true }
|
|
if (name == "is_windowed") or (name == "game_title") or (name == "window_later") { return true }
|
|
if (name == "win_open") or (name == "win_poll") or (name == "win_present") { return true }
|
|
if (name == "win_running") or (name == "win_close") { return true }
|
|
if (name == "win_held") or (name == "win_mouse") { return true } # #50 device layer
|
|
if (name == "win_text") { return true } # typed characters
|
|
if (name == "win_key_char") { return true } # Input.key_label
|
|
if (name == "win_pad") or (name == "win_touch") { return true } # #51 gamepad / touch
|
|
if (name == "win_cursor_mode") { return true } # #89 cursor capture
|
|
if (name == "snd_load") or (name == "snd_play") or (name == "snd_stop") { return true } # #22 audio
|
|
if (name == "snd_playing") or (name == "snd_set_volume") or (name == "snd_set_rate") or (name == "snd_set_pan") { return true }
|
|
if (name == "hs_req_new") or (name == "hs_req_header") or (name == "hs_req_body") { return true } # #6 HTTP
|
|
if (name == "hs_send") or (name == "hs_done") or (name == "hs_status") or (name == "hs_body") { return true }
|
|
if (name == "hs_blen") or (name == "hs_header") or (name == "hs_free") { return true }
|
|
return false
|
|
}
|
|
|
|
function emit_intrinsic2(name: pointer, e: Node) -> Val {
|
|
if (name == "free") {
|
|
let fv = emit_expr(e.kids[0])
|
|
# 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 @lp_free(ptr {fd})\n`)
|
|
}
|
|
emit(` call void @lp_free(ptr {fv.code})\n`)
|
|
return val("0", "void")
|
|
}
|
|
if (name == "fill") {
|
|
let p = raw_expr(e.kids[0]).code; let v = arg_code(e, 1); let n = arg_code(e, 2)
|
|
let w = emit_bind(`zext i32 {n} to i64`)
|
|
emit(" call ptr @memset(ptr "); emit(p); emit(", i32 "); emit(v); emit(", i64 "); emit(w); emit(")\n")
|
|
return val("0", "void")
|
|
}
|
|
if (name == "offset") {
|
|
let p = raw_expr(e.kids[0]).code; let n = arg_code(e, 1)
|
|
let g = nreg()
|
|
emit(" "); emit(g); emit(" = getelementptr inbounds i8, ptr "); emit(p); emit(", i32 "); emit(n); emit("\n")
|
|
return val(g, "pointer")
|
|
}
|
|
if (name == "read_char") { return val(emit_bind("call i32 @getchar()"), "int") }
|
|
if (name == "as_fixed") { let a = emit_expr(e.kids[0]); return val(a.code, "fixed") }
|
|
if (name == "as_int") { let a = emit_expr(e.kids[0]); return val(a.code, "int") }
|
|
if (name == "is_windowed") { if g_windowed { return val("1", "bool") }; return val("0", "bool") }
|
|
# a render3d program opens its window when its renderer does (gvk_open): the runtime leaves it till
|
|
# then, so a process that never gets that far (the launcher's watcher) never makes one
|
|
if (name == "window_later") { if g_windowed and find_fn("gvk_open") != null { return val("1", "bool") }; return val("0", "bool") }
|
|
if (name == "game_title") { return val("@.gametitle", "string") }
|
|
# windowing hooks — declared external; only reached on the is_windowed() branch
|
|
if (name == "win_poll") { return val(emit_bind("call i32 @win_poll()"), "int") }
|
|
if (name == "win_running") { return val(emit_bind("call i32 @win_running()"), "bool") }
|
|
if (name == "win_open") {
|
|
let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2); let d = arg_code(e, 3)
|
|
emit(" call void @win_open(i32 "); emit(a); emit(", i32 "); emit(b); emit(", i32 "); emit(c); emit(", ptr "); emit(d); emit(")\n")
|
|
return val("0", "void")
|
|
}
|
|
if (name == "win_present") {
|
|
let a = arg_buf(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2)
|
|
emit(" call void @win_present(ptr "); emit(a); emit(", i32 "); emit(b); emit(", i32 "); emit(c); emit(")\n")
|
|
return val("0", "void")
|
|
}
|
|
if (name == "win_close") { emit(" call void @win_close()\n"); return val("0", "void") }
|
|
# #50 — fill a caller buffer with the platform device state (windowed only,
|
|
# DCE'd headless). win_held: an 8-word held-key bitset; win_mouse: [x, y,
|
|
# button-mask, wheel-delta].
|
|
if (name == "win_held") { let a = arg_buf(e, 0); emit(" call void @win_held(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
|
if (name == "win_mouse") { let a = arg_buf(e, 0); emit(" call void @win_mouse(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
|
# what the active keyboard layout types on a held-set key position (windowed only,
|
|
# DCE'd headless): a code point, 0 for none.
|
|
if (name == "win_key_char") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @win_key_char(i32 {a})`), "int") }
|
|
# what the player TYPED since the last call - UTF-16 code units into the caller's buffer, one per
|
|
# word, returning how many. The layout, the modifiers and any dead key are already applied, which
|
|
# is why a name field must read this rather than build text out of key codes.
|
|
if (name == "win_text") { let a = arg_buf(e, 0); let b = arg_code(e, 1); return val(emit_bind(`call i32 @win_text(ptr {a}, i32 {b})`), "int") }
|
|
# #51 — win_pad: 6 words/pad [connected, mask, lx, ly, rx, ry]; win_touch: 3
|
|
# words/point [active, x, y]. Both windowed-only, DCE'd headless.
|
|
if (name == "win_pad") { let a = arg_buf(e, 0); emit(" call void @win_pad(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
|
if (name == "win_touch") { let a = arg_buf(e, 0); emit(" call void @win_touch(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
|
# #89 — set the OS cursor capture mode (windowed only; DCE'd headless).
|
|
if (name == "win_cursor_mode") { let a = arg_code(e, 0); emit(" call void @win_cursor_mode(i32 "); emit(a); emit(")\n"); return val("0", "void") }
|
|
# #22 audio backend — AVAudioPlayer via audio.ll; windowed-only, DCE'd headless.
|
|
if (name == "snd_load") { let a = arg_code(e, 0); return val(emit_bind(`call ptr @snd_load(ptr {a})`), "pointer") }
|
|
if (name == "snd_play") {
|
|
let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2); let d = arg_code(e, 3)
|
|
emit(" call void @snd_play(ptr "); emit(a); emit(", i32 "); emit(b); emit(", i32 "); emit(c); emit(", i32 "); emit(d); emit(")\n"); return val("0", "void")
|
|
}
|
|
if (name == "snd_stop") { let a = arg_code(e, 0); emit(" call void @snd_stop(ptr "); emit(a); emit(")\n"); return val("0", "void") }
|
|
if (name == "snd_playing") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @snd_playing(ptr {a})`), "int") }
|
|
if (name == "snd_set_volume") { let a = arg_code(e, 0); let b = arg_code(e, 1); emit(" call void @snd_set_volume(ptr "); emit(a); emit(", i32 "); emit(b); emit(")\n"); return val("0", "void") }
|
|
if (name == "snd_set_rate") { let a = arg_code(e, 0); let b = arg_code(e, 1); emit(" call void @snd_set_rate(ptr "); emit(a); emit(", i32 "); emit(b); emit(")\n"); return val("0", "void") }
|
|
if (name == "snd_set_pan") { let a = arg_code(e, 0); let b = arg_code(e, 1); emit(" call void @snd_set_pan(ptr "); emit(a); emit(", i32 "); emit(b); emit(")\n"); return val("0", "void") }
|
|
# #6 HTTP transport — NSURLConnection on a background thread (http.ll).
|
|
if (name == "hs_req_new") { let a = arg_code(e, 0); let b = arg_code(e, 1); return val(emit_bind(`call ptr @hs_req_new(ptr {a}, ptr {b})`), "pointer") }
|
|
if (name == "hs_req_header") { let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2); emit(" call void @hs_req_header(ptr "); emit(a); emit(", ptr "); emit(b); emit(", ptr "); emit(c); emit(")\n"); return val("0", "void") }
|
|
if (name == "hs_req_body") { let a = arg_code(e, 0); let b = arg_code(e, 1); let c = arg_code(e, 2); emit(" call void @hs_req_body(ptr "); emit(a); emit(", ptr "); emit(b); emit(", i32 "); emit(c); emit(")\n"); return val("0", "void") }
|
|
if (name == "hs_send") { let a = arg_code(e, 0); let b = arg_code(e, 1); emit(" call void @hs_send(i32 "); emit(a); emit(", ptr "); emit(b); emit(")\n"); return val("0", "void") }
|
|
if (name == "hs_done") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @hs_done(i32 {a})`), "int") }
|
|
if (name == "hs_status") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @hs_status(i32 {a})`), "int") }
|
|
if (name == "hs_body") { let a = arg_code(e, 0); return val(emit_bind(`call ptr @hs_body(i32 {a})`), "pointer") }
|
|
if (name == "hs_blen") { let a = arg_code(e, 0); return val(emit_bind(`call i32 @hs_blen(i32 {a})`), "int") }
|
|
if (name == "hs_header") { let a = arg_code(e, 0); let b = arg_code(e, 1); return val(emit_bind(`call ptr @hs_header(i32 {a}, ptr {b})`), "pointer") }
|
|
if (name == "hs_free") { let a = arg_code(e, 0); emit(" call void @hs_free(i32 "); emit(a); emit(")\n"); return val("0", "void") }
|
|
return val("0", "void")
|
|
}
|