code wiki / (root) / nx_bck_elide.nx

nx_bck_elide.nx

buildroot/runtime/nx_bck_elide.nx

47244 B994 linesdepth 5pulls 7 transitivereach 196 importersview sourcekind library
docsdependenciesstructsconstsfunctions

about

nx_bck_elide.nx -- LN7 SOUND BOUNDS-CHECK ELISION (bck_elide_dominated). WHAT THIS IS. The consumer nx_opt.nx's own VRA header has NAMED and never built since v0.0.1: "opt_bounds_check_elim: drop array bounds checks when index is provably within range". This is that pass, and it is deliberately NOT the range-based version that comment imagines -- it is the DOMINANCE version, which needs no range lattice and whose soundness argument is short enough to be checked by eye. THE THEOREM. emit_bounds_check_v (nx_parse.nx) emits, for one access: b: c1 = idx >=s 0 ; br_cond c1 -> lo, fail lo: c2 = idx <s len ; br_cond c2 -> ok, fail fail: write(2,msg) ; exit_group(71) ; br ok ok: <the access> If a site S1 and a site S2 carry the SAME (idx value id, len constant) and S1's `ok` block DOMINATES S2's entry block, then S2's comparisons can only ever answer true, so making S2's two branches unconditional changes no observable behaviour. Why the facts still hold at S2: values here are SSA, so an id names one immutable quantity for the whole function; dominance says EVERY path from entry to S2 runs through S1's ok; and S1's ok is only entered with the facts established (see the four structural preconditions below). Nothing about the distance between the two sites, or what sits between them, can weaken that -- there is no store that can retarget an SSA id. WRONG IN THE DIRECTION OF KEEPING THE CHECK, BY CONSTRUCTION. Every predicate below is a REASON TO ELIDE; the default is to keep. An unrecognised shape, an unreadable value, a truncated CFG, a length that is not a constant -- all fall through to "keep the check". An unsound elision is a silent memory-safety hole: it deletes the check that would have caught a real overrun, and nothing anywhere reports it. So this file has no "probably" cases. THE FOUR STRUCTURAL PRECONDITIONS a site must meet before it may JUSTIFY an elision -- each one closes a way for `ok` to be entered WITHOUT the facts: (1) `lo` has exactly ONE predecessor, and it is `b`. Otherwise control could enter lo from elsewhere and take the true edge to ok having proven `idx < len` but never `idx >= 0`. (2) every predecessor of `ok` is `lo` or `fail`. Any third edge into ok reaches it with no facts at all. A pred count above the pred slots the struct actually has means the edge list is TRUNCATED and cannot be enumerated -- that is a refusal, not a pass. (3) `fail` provably does not return: it must contain a syscall whose number operand is the exit_group constant. This is the precondition it would be easiest to assume instead of

dependencies 3 imports · 1 importers

nx_types.nx nx_ir.nx nx_dom_fn.nx nx_bck_elide.nx nx_opt.nx

imports: nx_types.nxnx_ir.nxnx_dom_fn.nx

imported by: nx_opt.nx

structs

none

consts

84const BCK_PRED_SLOTS: i64 = 3
88const BCK_O_LO: i64 = 0
89const BCK_O_OK: i64 = 1
90const BCK_O_IDX: i64 = 2
91const BCK_O_LEN: i64 = 3
92const BCK_SCRATCH_BYTES: i64 = 64
101const BCK_R_NOT_BRCOND: i64 = 0
102const BCK_R_GE_NOT_FOUND: i64 = 1
103const BCK_R_NOT_GE: i64 = 2
104const BCK_R_NOT_ZERO: i64 = 3
105const BCK_R_NO_LO: i64 = 4
106const BCK_R_LO_PREDS: i64 = 5
107const BCK_R_LO_NOT_BRCOND: i64 = 6
108const BCK_R_FAIL_MISMATCH: i64 = 7
109const BCK_R_LT_NOT_FOUND: i64 = 8
110const BCK_R_NOT_LT: i64 = 9
111const BCK_R_IDX_MISMATCH: i64 = 10
112const BCK_R_NO_FAIL: i64 = 11
113const BCK_R_FAIL_NOT_TERM: i64 = 12
114const BCK_R_NO_OK: i64 = 13
115const BCK_R_OK_PREDS: i64 = 14
116const BCK_R_MATCHED: i64 = 15
118const BCK_R_N: i64 = 26
612const BCK_IV_MAX_CONST: i64 = 1152921504606846976 // 2^60: guard bound + increment stays far below 2^63
613const BCK_R_IV_LOAD_NOT_LOCAL: i64 = 16
614const BCK_R_IV_NOT_LOAD: i64 = 17
615const BCK_R_IV_NOT_ALLOCA: i64 = 18
616const BCK_R_IV_ESCAPES: i64 = 19
617const BCK_R_IV_NO_INIT: i64 = 20
618const BCK_R_IV_BAD_STORE: i64 = 21
619const BCK_R_IV_INC_UNGUARDED: i64 = 22
620const BCK_R_IV_NO_GUARD: i64 = 23
621const BCK_R_IV_STORE_ON_PATH: i64 = 24
622const BCK_R_IV_MATCHED: i64 = 25
623const BCK_IV_OPERAND_SLOTS: i64 = 24

functions

139func nx_bckelide_set(v: i64, exit_group_nr: i64) -> i64
called by 2: mainmain
144func bck_elide_live() -> i64 { return g_bckelide_live }
145func bck_exitgrp() -> i64 { return g_bck_exitgrp }
146func bck_stat_sites() -> i64 { return g_bck_sites }
147func bck_stat_elided() -> i64 { return g_bck_elided }
148func bck_stat_ref_nolen() -> i64 { return g_bck_ref_nolen }
149func bck_stat_ref_nodom() -> i64 { return g_bck_ref_nodom }
150func bck_stat_decl_preds() -> i64 { return g_bck_decl_preds }
155func bck_reasons() -> *i64
164func bck_reason_bump(code: i64) -> i64
174func bck_eputs(s: *u8) -> i64
180func bck_eputn(v: i64) -> i64
called by 1: bck_ekv
195func bck_ekv(label: *u8, v: i64) -> i64 { bck_eputs(label); bck_eputn(v); return 0 }
199func bck_ereason(label: *u8, code: i64) -> i64
208func bck_block_index_by_id(f: *Function, id: i64) -> i64
220func bck_def_in_block(b: *BasicBlock, vid: i64) -> *Instr
236func bck_def_of(f: *Function, b: *BasicBlock, vid: i64) -> *Instr
251func bck_val_is_const(f: *Function, v: i64, want: i64) -> i64
263func bck_const_of(f: *Function, v: i64, flag: *i64) -> i64
275func bck_fail_is_terminal(f: *Function, fb: *BasicBlock, exit_group_nr: i64) -> i64
called by 1: bck_match_site calls 1: bck_val_is_const
289func bck_ok_preds_clean(ok: *BasicBlock, lo: *BasicBlock, fl: *BasicBlock) -> i64
called by 1: bck_match_site
304func bck_lo_preds_clean(lo: *BasicBlock, b: *BasicBlock) -> i64
called by 1: bck_match_site
315func bck_decline(code: i64, tally: i64) -> i64
called by 1: bck_match_site calls 1: bck_reason_bump
320func bck_match_site(f: *Function, bi: i64, exit_group_nr: i64, out: *i64, tally: i64) -> i64
379func bck_rewrite_site(f: *Function, b_i: i64, lo_i: i64, ok_i: i64) -> i64
406func bck_report(fn_blocks: i64, cand_here: i64, sites_here: i64, elided_here: i64) -> i64
445func bck_elide_dominated(f: *Function, info: *DomInfoFn, exit_group_nr: i64) -> i64
627func bck_stat_iv_sites() -> i64 { return g_bck_iv_sites }
628func bck_stat_iv_elided() -> i64 { return g_bck_iv_elided }
632func bck_iv_opk(inst: *Instr, k: i64) -> i64
called by 1: bck_iv_noescape
661func bck_iv_def(f: *Function, v: i64) -> *Instr
668func bck_iv_noescape(f: *Function, aid: i64) -> i64
called by 1: bck_iv_check calls 2: block_atbck_iv_opk
703func bck_iv_small_const(f: *Function, v: i64, out: *i64) -> i64
714func bck_iv_is_load_of(f: *Function, v: i64, aid: i64) -> i64
724func bck_iv_store_kind(f: *Function, st: *Instr, aid: i64) -> i64
736func bck_iv_init_ok(f: *Function, aid: i64) -> i64
753func bck_iv_block_has_store(b: *BasicBlock, aid: i64) -> i64
called by 1: bck_iv_store_free
762func bck_iv_push_succ(f: *Function, s: *BasicBlock, t_i: i64, mark: *u8, stack: *i64, sp: *i64) -> i64
called by 1: bck_iv_mark_fwd calls 1: df_block_index
775func bck_iv_mark_fwd(f: *Function, t_i: i64, mark: *u8, stack: *i64) -> i64
793func bck_iv_push_pred(f: *Function, p: *BasicBlock, t_i: i64, mark: *u8, stack: *i64, sp: *i64) -> i64
called by 1: bck_iv_mark_bwd calls 1: df_block_index
806func bck_iv_mark_bwd(f: *Function, t_i: i64, dst_i: i64, mark: *u8, stack: *i64) -> i64
826func bck_iv_store_free(f: *Function, t_i: i64, dst_i: i64, aid: i64, stop: *Instr, fwd: *u8, bwd: *u8, stack: *i64) -> i64
859func bck_iv_find_guard(f: *Function, info: *DomInfoFn, aid: i64, limit: i64, dst: *BasicBlock) -> i64
892func bck_iv_check(f: *Function, info: *DomInfoFn, b: *BasicBlock, bi: i64, ld: *Instr, lc: i64, fwd: *u8, bwd: *u8, stack: *i64) -> i64
925func bck_iv_report(fn_blocks: i64, cand: i64, elided: i64) -> i64
951func bck_elide_induction(f: *Function, info: *DomInfoFn, exit_group_nr: i64) -> i64