kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

commit a68bf13802fcf376e1f5331422e27e7457d37504
parent 2bca75d1fd472787b94d07a51196293622891597
Author: Ryan Sepassi <rsepassi@gmail.com>
Date:   Thu, 21 May 2026 19:34:56 -0700

Optimize pressure relief pass

Diffstat:
Msrc/opt/ir.h | 1+
Msrc/opt/pass_analysis.c | 10++++++++--
Msrc/opt/pass_o2.c | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Mtest/opt/opt_test.c | 37+++++++++++++++++++++++++++++++++++++
4 files changed, 131 insertions(+), 17 deletions(-)

diff --git a/src/opt/ir.h b/src/opt/ir.h @@ -468,6 +468,7 @@ typedef struct Func { OptUse* opt_uses; u32 opt_nuses, opt_uses_cap; u32* opt_first_use_by_val; /* indexed by Val, OPT_USE_NONE if no uses */ + u32 opt_first_use_by_val_cap; u32 opt_valid_analyses; Reg opt_hard_regs[OPT_REG_CLASSES][OPT_MAX_HARD_REGS]; diff --git a/src/opt/pass_analysis.c b/src/opt/pass_analysis.c @@ -316,10 +316,16 @@ static void opt_collect_inst_uses(Func* f, u32 b, u32 i, Inst* in) { } void opt_rebuild_def_use(Func* f) { + u32 nheads; if (!f) return; f->opt_nuses = 0; - f->opt_first_use_by_val = - arena_array(f->arena, u32, f->nvals ? f->nvals : 1u); + nheads = f->nvals ? f->nvals : 1u; + if (nheads > f->opt_first_use_by_val_cap) { + u32 ncap = f->opt_first_use_by_val_cap ? f->opt_first_use_by_val_cap : 16u; + while (ncap < nheads) ncap *= 2u; + f->opt_first_use_by_val = arena_array(f->arena, u32, ncap); + f->opt_first_use_by_val_cap = ncap; + } for (u32 v = 0; v < f->nvals; ++v) f->opt_first_use_by_val[v] = OPT_USE_NONE; for (u32 b = 0; b < f->nblocks; ++b) { Block* bl = &f->blocks[b]; diff --git a/src/opt/pass_o2.c b/src/opt/pass_o2.c @@ -161,6 +161,18 @@ typedef struct LicmLoop { u8* body; } LicmLoop; +typedef struct PressureBlockPlan { + u8* move_src; + u32* first_before; + u32* last_before; + u32* next_move; +} PressureBlockPlan; + +typedef struct PressurePlan { + PressureBlockPlan* blocks; + u32 nmoves; +} PressurePlan; + static u32 opt_inst_count(Func* f) { u32 n = 0; for (u32 b = 0; b < f->nblocks; ++b) n += f->blocks[b].ninsts; @@ -2481,16 +2493,45 @@ static int pressure_can_move_within_block(Func* f, u32 b, u32 from, u32 to) { return 1; } -static void pressure_move_before_use(Func* f, u32 b, u32 from, u32 use_inst) { - Block* bl = &f->blocks[b]; - Inst moved = bl->insts[from]; - for (u32 i = from + 1u; i < use_inst; ++i) bl->insts[i - 1u] = bl->insts[i]; - bl->insts[use_inst - 1u] = moved; - dse_refresh_def_locations(f); +static PressureBlockPlan* pressure_block_plan(Func* f, PressurePlan* plan, + u32 b) { + if (!plan->blocks) + plan->blocks = arena_zarray(f->arena, PressureBlockPlan, + f->nblocks ? f->nblocks : 1u); + PressureBlockPlan* bp = &plan->blocks[b]; + if (!bp->move_src) { + Block* bl = &f->blocks[b]; + u32 n = bl->ninsts ? bl->ninsts : 1u; + bp->move_src = arena_zarray(f->arena, u8, n); + bp->first_before = arena_array(f->arena, u32, n); + bp->last_before = arena_array(f->arena, u32, n); + bp->next_move = arena_array(f->arena, u32, n); + for (u32 i = 0; i < n; ++i) { + bp->first_before[i] = OPT_BLOCK_NONE; + bp->last_before[i] = OPT_BLOCK_NONE; + bp->next_move[i] = OPT_BLOCK_NONE; + } + } + return bp; } -static int pressure_relief_once(Func* f) { - opt_rebuild_def_use(f); +static void pressure_plan_move(Func* f, PressurePlan* plan, u32 b, u32 from, + u32 to) { + PressureBlockPlan* bp = pressure_block_plan(f, plan, b); + if (bp->move_src[from]) return; + bp->move_src[from] = 1; + bp->next_move[from] = OPT_BLOCK_NONE; + if (bp->first_before[to] == OPT_BLOCK_NONE) { + bp->first_before[to] = from; + } else { + bp->next_move[bp->last_before[to]] = from; + } + bp->last_before[to] = from; + plan->nmoves++; +} + +static int pressure_plan(Func* f, PressurePlan* plan) { + memset(plan, 0, sizeof *plan); for (Val v = 1; v < f->nvals; ++v) { u32 db = f->val_def_block[v]; u32 di = f->val_def_inst[v]; @@ -2506,11 +2547,40 @@ static int pressure_relief_once(Func* f) { if (f->blocks[use.block].loop_depth > f->blocks[db].loop_depth) continue; if (!pressure_can_move_within_block(f, db, di, use.inst)) continue; - pressure_move_before_use(f, db, di, use.inst); - opt_analysis_invalidate(f, OPT_ANALYSIS_DEF_USE); - return 1; + pressure_plan_move(f, plan, db, di, use.inst); } - return 0; + return plan->nmoves != 0; +} + +static void pressure_apply_block(Func* f, u32 b, PressureBlockPlan* bp) { + Block* bl = &f->blocks[b]; + Inst* old = bl->insts; + Inst* insts = arena_array(f->arena, Inst, bl->ninsts ? bl->ninsts : 1u); + u32 w = 0; + for (u32 i = 0; i < bl->ninsts; ++i) { + for (u32 m = bp->first_before[i]; m != OPT_BLOCK_NONE; + m = bp->next_move[m]) { + insts[w++] = old[m]; + } + if (!bp->move_src[i]) insts[w++] = old[i]; + } + bl->insts = insts; + bl->cap = bl->ninsts; + if (w != bl->ninsts) { + SrcLoc loc = {0, 0, 0}; + compiler_panic(f->c, loc, "opt pressure-relief: bad rewrite (%u, %u)", + (unsigned)w, (unsigned)bl->ninsts); + } +} + +static void pressure_apply(Func* f, PressurePlan* plan) { + if (!plan->blocks || !plan->nmoves) return; + for (u32 b = 0; b < f->nblocks; ++b) { + PressureBlockPlan* bp = &plan->blocks[b]; + if (bp->move_src) pressure_apply_block(f, b, bp); + } + dse_refresh_def_locations(f); + opt_analysis_invalidate(f, OPT_ANALYSIS_DEF_USE); } void opt_pressure_relief(Func* f) { @@ -2520,9 +2590,9 @@ void opt_pressure_relief(Func* f) { return; } - int changed = 0; - while (pressure_relief_once(f)) changed = 1; - if (changed) opt_analysis_invalidate(f, OPT_ANALYSIS_DEF_USE); + PressurePlan plan; + opt_rebuild_def_use(f); + if (pressure_plan(f, &plan)) pressure_apply(f, &plan); opt_rebuild_def_use(f); } diff --git a/test/opt/opt_test.c b/test/opt/opt_test.c @@ -3803,6 +3803,42 @@ static void opt_pressure_relief_does_not_cross_memory_ops(void) { tc_fini(&tc); } +static void opt_pressure_relief_sinks_many_immediates_in_one_pass(void) { + TestCtx tc; + tc_init(&tc); + Func* f = new_func(&tc); + Val x = add_val(f, tc.i32); + Val y = add_val(f, tc.i32); + Val imm[32]; + Val out[32]; + Val final; + + emit_scalar_input(f, f->entry, x, tc.i32); + emit_scalar_input(f, f->entry, y, tc.i32); + for (u32 i = 0; i < 32; ++i) { + imm[i] = add_val(f, tc.i32); + emit_load_imm(f, f->entry, imm[i], tc.i32, (i64)i); + } + for (u32 i = 0; i < 32; ++i) { + out[i] = add_val(f, tc.i32); + emit_binop(f, f->entry, out[i], imm[i], x, tc.i32); + } + final = add_val(f, tc.i32); + emit_binop(f, f->entry, final, x, out[31], tc.i32); + emit_ret_val(f, f->entry, final, tc.i32); + + opt_build_cfg(f); + opt_build_loop_tree(f); + opt_pressure_relief(f); + opt_verify(f, "test-pressure-many-sink"); + + for (u32 i = 0; i < 32; ++i) { + EXPECT(f->val_def_inst[imm[i]] + 1u == f->val_def_inst[out[i]], + "pressure relief should sink each immediate before its use"); + } + tc_fini(&tc); +} + static void opt_regalloc_priority(void) { TestCtx tc; tc_init(&tc); @@ -5924,6 +5960,7 @@ int main(void) { opt_pressure_relief_does_not_sink_into_loop(); opt_pressure_relief_preserves_multi_use_constant(); opt_pressure_relief_does_not_cross_memory_ops(); + opt_pressure_relief_sinks_many_immediates_in_one_pass(); opt_liveness_branch(); opt_block_liveness_phase1(); opt_live_ranges_phase2();