mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2025-12-28 16:50:10 +01:00
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: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/24998>
This commit is contained in:
parent
26748d6fbf
commit
8e9f5416bb
2 changed files with 130 additions and 2 deletions
|
|
@ -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| {
|
||||
|
|
|
|||
|
|
@ -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<u32, usize>,
|
||||
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<u32, SimpleBlockLiveness>,
|
||||
}
|
||||
|
||||
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<usize>,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue