Step 3b code is wired across UIRenderer + GlyphCache + UIPipeline +
chess game (gpu_mode = .metal on iOS, MetalGPU bound via the GPU
protocol). macOS GL chess, iOS-sim GLES chess, and iOS-sim Metal
triangle (63-metal-clear.sx) all still render.
iOS-sim Metal chess crashes inside replaceRegion uploading the 1MB
font atlas. Bisecting that crash exposed several sx-language issues
where mid-bisect tracers (NSLog inside if/else branch bodies) didn't
produce output, blocking further investigation.
Filing each finding as examples/issue-NNNN.sx rather than working
around piecemeal:
Bugs:
- 0024 NSLog/foreign-call inside if/else body not producing output
- 0025 C-ABI param coercion incomplete for composites >16B
(combined direct-call abiCoerceParamType TODO + call_indirect
path that doesn't apply C-ABI coercion at all)
- 0026 replaceRegion 1MB upload crash (likely downstream of 0025)
Features needed for step 4 + cleanup:
- 0027 Obj-C block bridge (^{...}) for animateWithDuration:
- 0028 Optional protocol box (?GPU = null) replaces T = ---; has_T: bool
- 0029 destroy_texture/buffer/shader on GPU protocol
- 0030 extern cross-file globals
Library-side: renderer.sx + glyph_cache.sx + pipeline.sx gain a
`gpu: GPU = ---; has_gpu: bool` field pair + branches that route every
GL touchpoint through the protocol when has_gpu. glyph_cache.init
saves/restores those fields around its memset. pipeline.set_gpu()
propagates to renderer + font. Renderer's MSL shader source added as
UI_MSL_SRC using packed_float2/packed_float4 to keep the 12-float
interleaved vertex layout tight (48 bytes).
metal.sx: dual-phase init (init(null, 0, 0) for eager device+queue,
re-init with the layer once UIKit installs the SxMetalView).
setStorageMode:.shared on every texture descriptor to ensure CPU-
writable atlas pixels on Apple Silicon iOS-sim.
Regression suite: 68 passing, 0 failed. WASM chess build currently
broken under step 3b state (silent compiler crash); documented in
CHECKPOINT.md, likely fallout from one of the filed issues (probably
0028 — the verbose protocol-box pattern). Step 3b resumes after
0024-0030 land.
91 lines
4.3 KiB
Plaintext
91 lines
4.3 KiB
Plaintext
// issue-0024: NSLog/foreign-side-effect calls placed as the FIRST statement
|
|
// of an `if X { ... } else { ... }` branch body do not produce visible
|
|
// output, even when the branch is provably taken (the SECOND statement in
|
|
// the same body — also a foreign call — does produce output).
|
|
//
|
|
// ── Observed iOS-side symptom (session 59 bisect) ─────────────────────────
|
|
//
|
|
// In library/modules/gpu/metal.sx's `metal_create_texture_ios`:
|
|
//
|
|
// slot : TextureSlot = .{ tex = tex, bytes_per_pixel = bytes_per_pixel };
|
|
// self.textures.append(slot);
|
|
// NSLog(ns_string("[metal] T6 appended\n".ptr)); // ← fires
|
|
//
|
|
// pixels_null := pixels == null;
|
|
// if pixels_null {
|
|
// NSLog(ns_string("[metal] T6b null\n".ptr)); // ← never fires
|
|
// } else {
|
|
// NSLog(ns_string("[metal] T6a non-null\n".ptr)); // ← never fires
|
|
// handle : u32 = xx self.textures.len;
|
|
// metal_update_texture_region_ios(self, handle, 0, 0, w, h, pixels);
|
|
// // ← DOES fire
|
|
// // (its first
|
|
// // NSLog at
|
|
// // fn entry
|
|
// // appears in
|
|
// // the unified
|
|
// // log)
|
|
// NSLog(ns_string("[metal] T7 done\n".ptr)); // ← (helper crashed
|
|
// // before this)
|
|
// }
|
|
//
|
|
// T6 appears in the iOS unified log. T6a/T6b never appear. The else
|
|
// branch's helper call DOES fire (its own first-statement NSLog inside
|
|
// the helper appears). So the else-branch IS entered; just its first
|
|
// NSLog statement produces no output.
|
|
//
|
|
// ── Pure-sx repro below does NOT trigger ───────────────────────────────────
|
|
//
|
|
// Running `sx run examples/issue-0024.sx` exits 0 (counter == 4 — all
|
|
// bumps fired). The bug only manifests with foreign calls (NSLog / ns_string),
|
|
// and possibly only when the process subsequently crashes (replaceRegion
|
|
// in the metal.sx case) — which raises the alternative hypothesis that
|
|
// the missing NSLog output is just iOS unified-logging buffer-loss on
|
|
// process death, not a sx compiler bug. The runtime sequence between T6
|
|
// and the crash was ~500μs; logs within ~1ms of an unhandled exception
|
|
// can be lost to OSLog's internal buffering on Apple Silicon iOS-sim.
|
|
//
|
|
// ── Investigation plan ─────────────────────────────────────────────────────
|
|
//
|
|
// Two paths to disambiguate:
|
|
// 1. Replace NSLog markers with `write(STDERR_FILENO, ...)` calls
|
|
// (synchronous, no OSLog involvement). If markers still don't appear:
|
|
// sx compiler bug — likely in src/ir/lower.zig:2166-2196 (the
|
|
// `is_value` branch of `lowerIfExpr` and downstream `lowerBlockValue`
|
|
// around 922-948). Possible: side-effecting leading statements
|
|
// dropped when branches are treated as values.
|
|
// 2. If markers DO appear with synchronous write: the iOS-side symptom
|
|
// is unified-logging buffer-loss, not a compiler bug. Close this issue
|
|
// as "wontfix — diagnostic limitation" and move the iOS debugging to
|
|
// foreign-write tracing.
|
|
//
|
|
// ── Real-world impact ──────────────────────────────────────────────────────
|
|
//
|
|
// Bisecting issue-0026 (replaceRegion crash) is currently blocked: without
|
|
// trustworthy markers inside if/else branches we can't tell which arg
|
|
// arrives wrong. Resolution unblocks step 3b of the Metal port.
|
|
|
|
#import "modules/std.sx";
|
|
|
|
counter : s64 = 0;
|
|
|
|
bump :: () { counter = counter + 1; }
|
|
|
|
probe :: (skip: bool) {
|
|
bump();
|
|
if skip {
|
|
bump();
|
|
bump();
|
|
} else {
|
|
bump();
|
|
bump();
|
|
}
|
|
bump();
|
|
}
|
|
|
|
main :: () -> s32 {
|
|
probe(false);
|
|
// counter == 4 (entry + 2 in false branch + exit) → exit 0
|
|
if counter == 4 then 0 else 1;
|
|
}
|