From 8e9f5416bbfeac98fe52713d36bc3d52ad4b804b Mon Sep 17 00:00:00 2001 From: Faith Ekstrand Date: Wed, 14 Jun 2023 17:24:39 -0500 Subject: [PATCH] nak: Bring back bitset-based liveness This one is much faster to compute because we can use bitops for the fixed-point data-flow algorithm rather than the clumsy walking of hash sets. The faster version is sufficient for RA and checking for register pressure. We only need to fall back to the slower version for spilling. Thanks to traits, we can get some of the same behavior with both. Part-of: --- src/nouveau/compiler/nak_assign_regs.rs | 4 +- src/nouveau/compiler/nak_liveness.rs | 128 ++++++++++++++++++++++++ 2 files changed, 130 insertions(+), 2 deletions(-) diff --git a/src/nouveau/compiler/nak_assign_regs.rs b/src/nouveau/compiler/nak_assign_regs.rs index 258179c481c..796cdc25a34 100644 --- a/src/nouveau/compiler/nak_assign_regs.rs +++ b/src/nouveau/compiler/nak_assign_regs.rs @@ -8,7 +8,7 @@ use crate::bitset::BitSet; use crate::nak_cfg::CFG; use crate::nak_ir::*; -use crate::nak_liveness::{BlockLiveness, Liveness, NextUseLiveness}; +use crate::nak_liveness::{BlockLiveness, Liveness, SimpleLiveness}; use crate::util::NextMultipleOf; use std::cmp::{max, Ordering}; @@ -1018,7 +1018,7 @@ impl AssignRegs { pub fn run(&mut self, f: &mut Function) { let cfg = CFG::for_function(f); - let live = NextUseLiveness::for_function(f, &cfg); + let live = SimpleLiveness::for_function(f, &cfg); let max_live = live.calc_max_live(f, &cfg); let num_regs = PerRegFile::new_with(|file| { diff --git a/src/nouveau/compiler/nak_liveness.rs b/src/nouveau/compiler/nak_liveness.rs index 215724e4f7f..ac2a8e52dd3 100644 --- a/src/nouveau/compiler/nak_liveness.rs +++ b/src/nouveau/compiler/nak_liveness.rs @@ -3,6 +3,7 @@ * SPDX-License-Identifier: MIT */ +use crate::bitset::BitSet; use crate::nak_cfg::CFG; use crate::nak_ir::*; @@ -163,6 +164,133 @@ pub trait Liveness { } } +pub struct SimpleBlockLiveness { + defs: BitSet, + uses: BitSet, + last_use: HashMap, + live_in: BitSet, + live_out: BitSet, +} + +impl SimpleBlockLiveness { + fn add_def(&mut self, val: &SSAValue) { + self.defs.insert(val.idx().try_into().unwrap()); + } + + fn add_use(&mut self, val: &SSAValue, ip: usize) { + self.uses.insert(val.idx().try_into().unwrap()); + self.last_use.insert(val.idx(), ip); + } + + fn for_block(block: &BasicBlock) -> Self { + let mut bl = Self { + defs: BitSet::new(), + uses: BitSet::new(), + last_use: HashMap::new(), + live_in: BitSet::new(), + live_out: BitSet::new(), + }; + + for (ip, instr) in block.instrs.iter().enumerate() { + if let PredRef::SSA(val) = &instr.pred.pred_ref { + bl.add_use(val, ip); + } + + for src in instr.srcs() { + for sv in src.iter_ssa() { + bl.add_use(sv, ip); + } + } + + for dst in instr.dsts() { + if let Dst::SSA(sr) = dst { + for sv in sr.iter() { + bl.add_def(sv); + } + } + } + } + + bl + } +} + +impl BlockLiveness for SimpleBlockLiveness { + fn is_live_after_ip(&self, val: &SSAValue, ip: usize) -> bool { + if self.live_out.get(val.idx().try_into().unwrap()) { + true + } else { + if let Some(last_use_ip) = self.last_use.get(&val.idx()) { + *last_use_ip > ip + } else { + false + } + } + } + + fn is_live_in(&self, val: &SSAValue) -> bool { + self.live_in.get(val.idx().try_into().unwrap()) + } + + fn is_live_out(&self, val: &SSAValue) -> bool { + self.live_out.get(val.idx().try_into().unwrap()) + } +} + +pub struct SimpleLiveness { + blocks: HashMap, +} + +impl SimpleLiveness { + pub fn for_function(func: &Function, cfg: &CFG) -> SimpleLiveness { + let mut l = SimpleLiveness { + blocks: HashMap::new(), + }; + let mut live_in = HashMap::new(); + + for b in &func.blocks { + let bl = SimpleBlockLiveness::for_block(b); + l.blocks.insert(b.id, bl); + live_in.insert(b.id, BitSet::new()); + } + + let mut to_do = true; + while to_do { + to_do = false; + for b in func.blocks.iter().rev() { + let bl = l.blocks.get_mut(&b.id).unwrap(); + + /* Compute live-out */ + for sb_id in cfg.block_successors(b.id) { + let s_live_in = live_in.get(&sb_id).unwrap(); + to_do |= bl.live_out.union_with(s_live_in); + } + + let b_live_in = live_in.get_mut(&b.id).unwrap(); + + let new_live_in = + (bl.live_out.clone() | bl.uses.clone()) & !bl.defs.clone(); + to_do |= b_live_in.union_with(&new_live_in); + } + } + + for b in &func.blocks { + let bl = l.blocks.get_mut(&b.id).unwrap(); + bl.live_in = live_in.remove(&b.id).unwrap(); + } + + l + } +} + +impl Liveness for SimpleLiveness { + type PerBlock = SimpleBlockLiveness; + + fn block_live(&self, id: u32) -> &SimpleBlockLiveness { + self.blocks.get(&id).unwrap() + } +} + struct SSAUseDef { defined: bool, uses: Vec,