nx_shader_ir_args_candidate_t206.nx
buildroot/runtime/nx_shader_ir_args_candidate_t206.nx
about
nx_shader_ir.nx -- THE SHADER IR: ONE source of truth, N dialect backends.
WHY THIS EXISTS (measured 2026-08-23). The estate's shaders are hand-written GLSL string
literals inside nx_game_page_emit. Drawing characters on the WebGPU tier therefore demanded a
SECOND HAND COPY in WGSL -- ~600 lines (dual-quaternion skinning off a joint texture, instance
uniform arrays, the voxel occlusion march, the procedural anatomy/eye/hair/skin paint) of the
most actively-edited shader in the estate: three separate feature edits landed in it on the day
this was written. Two artifacts that must agree, kept in agreement by discipline, is the exact
shape the banked law forbids. This module is the construction that makes disagreement
IMPOSSIBLE: neither dialect is authored, both are EMITTED from this representation.
HOUSE SHAPE. This mirrors the NishiLang compiler itself -- lex_source -> parse_module ->
opt_run -> {nx_compile_x86, nx_compile_wat}: one front-end representation, many backends.
nx_glsl.nx and nx_wgsl.nx are backends over this IR, not transpilers bolted to a call site.
THE SOURCE OF TRUTH IS NISHILANG (operator, 2026-08-23: "we dont want python or glsl or
javascript or all that it should be nishi lang and nishi ecosystem from the first byte up each
rung gaining all these capabilities"). GLSL IS A TARGET, NOT AN INPUT. This is NOT a GLSL->WGSL
transpiler: translating would enshrine GLSL as the canonical language and make a foreign dialect
the artifact we maintain. A shader is authored ONCE in NishiLang; GLSL and WGSL are both OUTPUTS,
exactly as x86_64 and WAT are outputs of the same NishiLang front-end. The ~600 lines of GLSL in
MVS/MFS are therefore a MIGRATION TARGET, not the source: each rung expresses more of them in
NishiLang and PROVES the emitted GLSL equivalent to the hand-written GLSL that ships today.
LADDER (each rung widens the covered subset; rung 1 is what this lane ships):
rung 1 the fullscreen-triangle vertex stage -- authored in NishiLang, emitting BOTH the GLSL
VSH and the WGSL vs that are hand-written and SHIPPING today, proven against them.
rung 2 lower the real NishiLang AST (nx_tokenizer/nx_parse) into this IR, so a shader is an
ordinary NishiLang function rather than builder calls -- the full 'target, not
translator' shape, sharing the front-end with nx_compile_x86 and the WAT lane.
rung 3 widen the subset to the MFS paint set; rung 4 the MVS skinning set; then the character
pass becomes a compile target and the WebGPU abstention flips from owed to shipped.
THE SOURCE OF TRUTH IS NISHILANG (operator, 2026-08-23: "we dont want python or glsl or
javascript or all that it should be nishi lang and nishi ecosystem from the first byte up each
rung gaining all these capabilities"). GLSL IS A TARGET, NOT AN INPUT. This is NOT a GLSL->WGSL
transpiler: translating would enshrine GLSL as the canonical language and make a foreign dialect
the artifact we maintain. A shader is authored ONCE in NishiLang; GLSL and WGSL are both OUTPUTS,
exactly as x86_64 and WAT are outputs of the same NishiLang front-end. The ~600 lines of GLSL in
MVS/MFS are therefore a MIGRATION TARGET, not the source: each rung expresses more of them in
dependencies 1 imports · 1 importers
imports: nx_syscalls.nx
imported by: nx_shader_arg_ownership_gate_t206.nx
structs
| none |
consts
| 66 | const SIR_SLOTS: i64 = 8 |
| 71 | const SIR_MAXNODE: i64 = 16384 |
| 72 | const SIR_POOLCAP: i64 = 65536 |
| 74 | const SIR_KIND: i64 = 0 |
| 75 | const SIR_TY: i64 = 1 |
| 76 | const SIR_A: i64 = 2 |
| 77 | const SIR_B: i64 = 3 |
| 78 | const SIR_C: i64 = 4 |
| 79 | const SIR_NAME: i64 = 5 |
| 80 | const SIR_D: i64 = 6 |
| 81 | const SIR_NEXT: i64 = 7 |
| 84 | const M_NODES: i64 = 0 |
| 85 | const M_NCOUNT: i64 = 1 |
| 86 | const M_POOL: i64 = 2 |
| 87 | const M_POOLUSED: i64 = 3 |
| 88 | const M_DECLH: i64 = 4 |
| 89 | const M_DECLT: i64 = 5 |
| 90 | const M_FUNCH: i64 = 6 |
| 91 | const M_FUNCT: i64 = 7 |
| 92 | const M_REFUSE: i64 = 8 |
| 93 | const M_OVER: i64 = 9 |
| 98 | const M_CURFN: i64 = 10 |
| 105 | const M_FRAGOUT: i64 = 11 |
| 110 | const M_VIOMODE: i64 = 12 |
| 115 | const M_USESII: i64 = 13 |
| 122 | const M_WGFRAGOUT: i64 = 14 |
| 128 | const M_BINDGROUP: i64 = 15 |
| 131 | const M_CLIP_DEPTH: i64 = 16 |
| 132 | const SIR_MODULE_WORDS: i64 = 17 |
| 133 | const SIR_CLIP_NATIVE: i64 = 0 |
| 134 | const SIR_CLIP_NEGATIVE_ONE_TO_ONE: i64 = 1 |
| 136 | const SIR_LIT_USUFFIX: i64 = 1 |
| 139 | const K_UNIFORM: i64 = 1 |
| 140 | const K_ATTRIB: i64 = 2 |
| 141 | const K_VARY: i64 = 3 |
| 142 | const K_FUNC: i64 = 4 |
| 143 | const K_PARAM: i64 = 5 |
| 151 | const K_STORAGE: i64 = 6 |
| 158 | const K_TEXTURE: i64 = 7 |
| 169 | const K_FRAGOUT: i64 = 8 |
| 177 | const K_STRUCT: i64 = 9 |
| 178 | const S_RETURN: i64 = 10 |
| 179 | const S_ASSIGN: i64 = 11 |
| 180 | const S_VAR: i64 = 12 |
| 181 | const S_IF: i64 = 13 |
| 182 | const S_DISCARD: i64 = 14 |
| 190 | const S_LOOP: i64 = 15 |
| 191 | const S_BREAK: i64 = 16 |
| 195 | const S_EXPR: i64 = 17 |
| 196 | const E_LIT: i64 = 20 |
| 197 | const E_IDENT: i64 = 21 |
| 198 | const E_BIN: i64 = 22 |
| 199 | const E_CALL: i64 = 23 |
| 200 | const E_SWZ: i64 = 24 |
| 201 | const E_INDEX: i64 = 25 |
| 202 | const E_TEXLOAD: i64 = 26 |
| 203 | const E_TEXSAMPLE: i64 = 27 |
| 204 | const E_CTOR: i64 = 28 |
| 208 | const E_CAST: i64 = 29 |
| 209 | const E_BUILTIN: i64 = 30 |
| 214 | const E_ATOMIC: i64 = 31 |
| 220 | const E_SELECT: i64 = 32 |
| 224 | const E_NEG: i64 = 33 |
| 230 | const K_PRIVATE: i64 = 34 |
| 236 | const E_NOT: i64 = 35 |
| 239 | const T_VOID: i64 = 0 |
| 240 | const T_F32: i64 = 1 |
| 241 | const T_I32: i64 = 2 |
| 242 | const T_U32: i64 = 3 |
| 243 | const T_BOOL: i64 = 4 |
| 244 | const T_V2F: i64 = 5 |
| 245 | const T_V3F: i64 = 6 |
| 246 | const T_V4F: i64 = 7 |
| 247 | const T_V3I: i64 = 8 |
| 248 | const T_TEX3U: i64 = 9 |
| 249 | const T_TEX2F: i64 = 10 |
| 251 | const T_V3U: i64 = 11 |
| 254 | const T_V2I: i64 = 12 |
| 258 | const T_M3F: i64 = 13 |
| 259 | const T_MAX: i64 = 13 |
| 410 | const P_PLAIN: i64 = 0 |
| 411 | const P_BUILTIN_POSITION: i64 = 1 |
| 493 | const SIR_VAR_CONST: i64 = 2 |
| 532 | const SIR_VAR_LET: i64 = 1 |
functions
| 261 | func sir_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 263 | func sir_new() -> *i64 |
| 280 | func sir_get(m: *i64, id: i64, k: i64) -> i64 |
| 285 | func sir_set(m: *i64, id: i64, k: i64, v: i64) -> i64 |
| 293 | func sir_str(m: *i64, s: *u8) -> i64 |
| 307 | func sir_cstr(m: *i64, off: i64) -> *u8 |
| 311 | func sir_node(m: *i64, kind: i64, ty: i64, a: i64, b: i64, c: i64, nameoff: i64) -> i64 |
| 327 | func sir_chain(m: *i64, hw: i64, tw: i64, id: i64) -> i64 |
| 337 | func sir_uniform(m: *i64, name: *u8, ty: i64, arraylen: i64) -> i64 |
| 343 | func sir_private(m: *i64, name: *u8, ty: i64, arraylen: i64) -> i64 |
| 348 | func sir_attrib(m: *i64, name: *u8, ty: i64, location: i64) -> i64 |
| 353 | func sir_vary(m: *i64, name: *u8, ty: i64, location: i64, flat: i64) -> i64 |
| 360 | func sir_storage(m: *i64, name: *u8, elemty: i64, binding: i64, readwrite: i64, atomic: i64) -> i64 |
| 366 | func sir_texture(m: *i64, name: *u8, ty: i64) -> i64 |
| 373 | func sir_fragout(m: *i64, name: *u8, ty: i64, location: i64) -> i64 |
| 383 | func sir_struct(m: *i64, name: *u8) -> i64 |
| 387 | func sir_member(m: *i64, st: i64, name: *u8, ty: i64) -> i64 { return sir_param(m, st, name, ty) } calls 1: sir_param |
| 388 | func sir_struct_ty(st: i64) -> i64 { return SIR_MAXNODE + st } |
| 390 | func sir_struct_of(m: *i64, ty: i64) -> i64 calls 1: sir_get |
| 397 | func sir_member_of(m: *i64, base: i64, name: *u8, ty: i64) -> i64 { return sir_swz(m, base, name, ty) } calls 1: sir_swz |
| 400 | func sir_func(m: *i64, name: *u8, rettype: i64) -> i64 |
| 413 | func sir_param(m: *i64, fn: i64, name: *u8, ty: i64) -> i64 |
| 422 | func sir_param_position(m: *i64, fn: i64, name: *u8) -> i64 |
| 429 | func sir_stmt(m: *i64, fn: i64, st: i64) -> i64 |
| 438 | func sir_return(m: *i64, e: i64) -> i64 { return sir_node(m, S_RETURN, T_VOID, e, 0, 0, 0) } calls 1: sir_node |
| 441 | func sir_return_depth(m: *i64, color: i64, depth: i64) -> i64 calls 1: sir_node |
| 445 | func sir_assign(m: *i64, lhs: i64, rhs: i64) -> i64 { return sir_node(m, S_ASSIGN, T_VOID, lhs, rhs, 0, 0) } calls 1: sir_node |
| 446 | func sir_var(m: *i64, name: *u8, ty: i64, init: i64) -> i64 { return sir_node(m, S_VAR, ty, init, 0, 0, sir_str(m, name)) } |
| 447 | func sir_if(m: *i64, cond: i64, thenh: i64, elseh: i64) -> i64 { return sir_node(m, S_IF, T_VOID, cond, thenh, elseh, 0) } |
| 448 | func sir_discard(m: *i64) -> i64 { return sir_node(m, S_DISCARD, T_VOID, 0, 0, 0, 0) } calls 1: sir_node |
| 452 | func sir_loop(m: *i64, body: i64) -> i64 { return sir_node(m, S_LOOP, T_VOID, body, 0, 0, 0) } |
| 453 | func sir_break(m: *i64) -> i64 { return sir_node(m, S_BREAK, T_VOID, 0, 0, 0, 0) } |
| 455 | func sir_expr(m: *i64, e: i64) -> i64 { return sir_node(m, S_EXPR, T_VOID, e, 0, 0, 0) } calls 1: sir_node |
| 464 | func sir_for_until(m: *i64, fn: i64, init: i64, until: i64, step: i64, body: i64) -> i64 |
| 473 | func sir_seq(m: *i64, a: i64, b: i64) -> i64 |
| 482 | func sir_lit(m: *i64, text: *u8, ty: i64) -> i64 { return sir_node(m, E_LIT, ty, 0, 0, 0, sir_str(m, text)) } |
| 486 | func sir_lit_u(m: *i64, text: *u8) -> i64 |
| 494 | func sir_const(m: *i64, name: *u8, ty: i64, init: i64) -> i64 |
| 499 | func sir_ident(m: *i64, name: *u8, ty: i64) -> i64 { return sir_node(m, E_IDENT, ty, 0, 0, 0, sir_str(m, name)) } |
| 500 | func sir_bin(m: *i64, op: *u8, l: i64, r: i64, ty: i64) -> i64 { return sir_node(m, E_BIN, ty, l, r, 0, sir_str(m, op)) } |
| 501 | func sir_swz(m: *i64, base: i64, sel: *u8, ty: i64) -> i64 { return sir_node(m, E_SWZ, ty, base, 0, 0, sir_str(m, sel)) } |
| 502 | func sir_index(m: *i64, base: i64, idx: i64, ty: i64) -> i64 { return sir_node(m, E_INDEX, ty, base, idx, 0, 0) } calls 1: sir_node |
| 506 | func sir_texload(m: *i64, tex: i64, coord: i64, lod: i64) -> i64 |
| 511 | func sir_texsample(m: *i64, tex: i64, coord: i64) -> i64 { return sir_node(m, E_TEXSAMPLE, T_V4F, tex, coord, 0, 0) } calls 1: sir_node |
| 514 | func sir_call(m: *i64, name: *u8, ty: i64) -> i64 { return sir_node(m, E_CALL, ty, 0, 0, 0, sir_str(m, name)) } |
| 515 | func sir_ctor(m: *i64, ty: i64) -> i64 { return sir_node(m, E_CTOR, ty, 0, 0, 0, 0) } |
| 516 | func sir_cast(m: *i64, e: i64, ty: i64) -> i64 { return sir_node(m, E_CAST, ty, e, 0, 0, 0) } calls 1: sir_node |
| 517 | func sir_builtin(m: *i64, which: *u8, ty: i64) -> i64 |
| 523 | func sir_atomic(m: *i64, op: *u8, target: i64, val: i64, ty: i64) -> i64 { return sir_node(m, E_ATOMIC, ty, target, val, 0, sir_str(m, op)) } |
| 525 | func sir_select(m: *i64, cond: i64, tval: i64, fval: i64, ty: i64) -> i64 { return sir_node(m, E_SELECT, ty, cond, tval, fval, 0) } calls 1: sir_node |
| 527 | func sir_neg(m: *i64, e: i64, ty: i64) -> i64 { return sir_node(m, E_NEG, ty, e, 0, 0, 0) } calls 1: sir_node |
| 529 | func sir_not(m: *i64, e: i64) -> i64 { return sir_node(m, E_NOT, T_BOOL, e, 0, 0, 0) } calls 1: sir_node |
| 533 | func sir_let(m: *i64, name: *u8, ty: i64, init: i64) -> i64 |
| 539 | func sir_arg(m: *i64, callid: i64, e: i64) -> i64 |
| 556 | func sir_refuse(m: *i64, what: *u8) -> i64 calls 1: sir_str |
| 560 | func sir_refused(m: *i64) -> i64 { return m[M_REFUSE] } |
| 563 | func eb_put(out: *u8, pos: i64, cap: i64, s: *u8) -> i64 |
| 571 | func eb_num(out: *u8, pos: i64, cap: i64, v: i64) -> i64 |
| 588 | func tr_add(trace: *i64, tcap: i64, tn: *i64, id: i64) -> i64 |