nak: Rework phis

Have a single phi src/dest instruction per block which handles all the
phis at one go.  This makes phis very similar to parallel copies.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/24998>
This commit is contained in:
Faith Ekstrand 2023-04-10 17:23:26 -05:00 committed by Marge Bot
parent 15a4b620b9
commit 8004416e38
4 changed files with 196 additions and 91 deletions

View file

@ -58,14 +58,22 @@ impl TrivialRegAlloc {
RegRef::new(file, idx, comps)
}
fn get_ssa_reg(&mut self, ssa: SSAValue) -> RegRef {
if let Some(reg) = self.reg_map.get(&ssa) {
*reg
} else {
let reg = self.alloc_reg(ssa.file(), ssa.comps());
self.reg_map.insert(ssa, reg);
reg
fn alloc_ssa(&mut self, ssa: SSAValue) -> RegRef {
let reg = self.alloc_reg(ssa.file(), ssa.comps());
let old = self.reg_map.insert(ssa, reg);
assert!(old.is_none());
reg
}
fn get_ssa_reg(&self, ssa: SSAValue) -> RegRef {
*self.reg_map.get(&ssa).unwrap()
}
fn map_src(&self, mut src: Src) -> Src {
if let SrcRef::SSA(ssa) = src.src_ref {
src.src_ref = self.get_ssa_reg(ssa).into();
}
src
}
pub fn do_alloc(&mut self, s: &mut Shader) {
@ -73,18 +81,21 @@ impl TrivialRegAlloc {
for b in &mut f.blocks {
for instr in &mut b.instrs {
match &instr.op {
Op::PhiDst(op) => {
let dst_ssa = op.dst.as_ssa().unwrap();
let reg =
self.alloc_reg(dst_ssa.file(), dst_ssa.comps());
self.phi_map.insert(op.phi_id, reg);
Op::PhiDsts(phi) => {
let mut pcopy = OpParCopy::new();
let dst = self.get_ssa_reg(*dst_ssa);
instr.op = Op::Mov(OpMov {
dst: dst.into(),
src: reg.into(),
quad_lanes: 0xf,
});
assert!(phi.ids.len() == phi.dsts.len());
for (id, dst) in phi.iter() {
let dst_ssa = dst.as_ssa().unwrap();
let dst_reg = self.alloc_ssa(*dst_ssa);
let src_reg = self
.alloc_reg(dst_ssa.file(), dst_ssa.comps());
self.phi_map.insert(*id, src_reg);
pcopy.srcs.push(src_reg.into());
pcopy.dsts.push(dst_reg.into());
}
instr.op = Op::ParCopy(pcopy);
}
_ => (),
}
@ -96,18 +107,21 @@ impl TrivialRegAlloc {
for b in &mut f.blocks {
for instr in &mut b.instrs {
match &instr.op {
Op::PhiSrc(op) => {
assert!(op.src.src_mod.is_none());
let reg = *self.phi_map.get(&op.phi_id).unwrap();
let src = if let SrcRef::SSA(ssa) = op.src.src_ref {
Src::from(self.get_ssa_reg(ssa))
} else {
op.src
};
instr.op = Op::Mov(OpMov {
dst: reg.into(),
src: src.into(),
quad_lanes: 0xf,
Op::PhiSrcs(phi) => {
assert!(phi.ids.len() == phi.srcs.len());
instr.op = Op::ParCopy(OpParCopy {
srcs: phi
.srcs
.iter()
.map(|src| self.map_src(*src))
.collect(),
dsts: phi
.ids
.iter()
.map(|id| {
(*self.phi_map.get(id).unwrap()).into()
})
.collect(),
});
}
_ => {
@ -116,13 +130,11 @@ impl TrivialRegAlloc {
}
for dst in instr.dsts_mut() {
if let Dst::SSA(ssa) = dst {
*dst = self.get_ssa_reg(*ssa).into();
*dst = self.alloc_ssa(*ssa).into();
}
}
for src in instr.srcs_mut() {
if let SrcRef::SSA(ssa) = src.src_ref {
src.src_ref = self.get_ssa_reg(ssa).into();
}
*src = self.map_src(*src);
}
}
}

View file

@ -529,6 +529,25 @@ impl<'a> ShaderFromNir<'a> {
}
fn parse_block(&mut self, nb: &nir_block) {
let mut phi = OpPhiDsts {
ids: Vec::new(),
dsts: Vec::new(),
};
for ni in nb.iter_instr_list() {
if ni.type_ == nir_instr_type_phi {
let np = ni.as_phi().unwrap();
phi.ids.push(self.get_phi_id(np));
phi.dsts.push(self.get_dst(&np.def));
} else {
break;
}
}
if !phi.ids.is_empty() {
self.instrs.push(Instr::new(Op::PhiDsts(phi)));
}
for ni in nb.iter_instr_list() {
match ni.type_ {
nir_instr_type_alu => self.parse_alu(ni.as_alu().unwrap()),
@ -543,12 +562,7 @@ impl<'a> ShaderFromNir<'a> {
nir_instr_type_undef => {
self.parse_undef(ni.as_undef().unwrap())
}
nir_instr_type_phi => {
let phi = ni.as_phi().unwrap();
let dst = self.get_dst(&phi.def);
let phi_id = self.get_phi_id(phi);
self.instrs.push(Instr::new_phi_dst(phi_id, dst));
}
nir_instr_type_phi => (),
_ => panic!("Unsupported instruction type"),
}
}
@ -560,20 +574,29 @@ impl<'a> ShaderFromNir<'a> {
None => continue,
};
let mut phi = OpPhiSrcs {
srcs: Vec::new(),
ids: Vec::new(),
};
for i in sb.iter_instr_list() {
let phi = match i.as_phi() {
let np = match i.as_phi() {
Some(phi) => phi,
None => break,
};
let phi_id = self.get_phi_id(phi);
for ps in phi.iter_srcs() {
for ps in np.iter_srcs() {
if ps.pred().index == nb.index {
let src = self.get_src(&ps.src);
self.instrs.push(Instr::new_phi_src(phi_id, src));
phi.srcs.push(self.get_src(&ps.src));
phi.ids.push(self.get_phi_id(np));
break;
}
}
}
if !phi.ids.is_empty() {
self.instrs.push(Instr::new(Op::PhiSrcs(phi)));
}
}
let s0 = succ[0].unwrap();

View file

@ -1419,28 +1419,88 @@ impl fmt::Display for OpSplit {
}
#[repr(C)]
#[derive(SrcsAsSlice, DstsAsSlice)]
pub struct OpPhiSrc {
pub src: Src,
pub phi_id: u32,
#[derive(DstsAsSlice)]
pub struct OpPhiSrcs {
pub srcs: Vec<Src>,
pub ids: Vec<u32>,
}
impl fmt::Display for OpPhiSrc {
impl OpPhiSrcs {
pub fn is_empty(&self) -> bool {
assert!(self.ids.len() == self.srcs.len());
self.ids.is_empty()
}
pub fn iter(&self) -> Zip<slice::Iter<'_, u32>, slice::Iter<'_, Src>> {
assert!(self.ids.len() == self.srcs.len());
self.ids.iter().zip(self.srcs.iter())
}
}
impl SrcsAsSlice for OpPhiSrcs {
fn srcs_as_slice(&self) -> &[Src] {
&self.srcs
}
fn srcs_as_mut_slice(&mut self) -> &mut [Src] {
&mut self.srcs
}
}
impl fmt::Display for OpPhiSrcs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PHI_SRC({}) {}", self.phi_id, self.src)
write!(f, "PHI_SRC {{")?;
assert!(self.ids.len() == self.srcs.len());
for i in 0..self.ids.len() {
if i > 0 {
write!(f, ",")?;
}
write!(f, " {} <- {}", self.ids[i], self.srcs[i])?;
}
write!(f, " }}")
}
}
#[repr(C)]
#[derive(SrcsAsSlice, DstsAsSlice)]
pub struct OpPhiDst {
pub dst: Dst,
pub phi_id: u32,
#[derive(SrcsAsSlice)]
pub struct OpPhiDsts {
pub ids: Vec<u32>,
pub dsts: Vec<Dst>,
}
impl fmt::Display for OpPhiDst {
impl OpPhiDsts {
pub fn is_empty(&self) -> bool {
assert!(self.ids.len() == self.dsts.len());
self.ids.is_empty()
}
pub fn iter(&self) -> Zip<slice::Iter<'_, u32>, slice::Iter<'_, Dst>> {
assert!(self.ids.len() == self.dsts.len());
self.ids.iter().zip(self.dsts.iter())
}
}
impl DstsAsSlice for OpPhiDsts {
fn dsts_as_slice(&self) -> &[Dst] {
&self.dsts
}
fn dsts_as_mut_slice(&mut self) -> &mut [Dst] {
&mut self.dsts
}
}
impl fmt::Display for OpPhiDsts {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PHI_DST({}) {}", self.phi_id, self.dst)
write!(f, "PHI_DST {{")?;
assert!(self.ids.len() == self.dsts.len());
for i in 0..self.ids.len() {
if i > 0 {
write!(f, ",")?;
}
write!(f, " {} <- {}", self.dsts[i], self.ids[i])?;
}
write!(f, " }}")
}
}
@ -1568,8 +1628,8 @@ pub enum Op {
S2R(OpS2R),
FMov(OpFMov),
IMov(OpIMov),
PhiSrc(OpPhiSrc),
PhiDst(OpPhiDst),
PhiSrcs(OpPhiSrcs),
PhiDsts(OpPhiDsts),
Vec(OpVec),
Split(OpSplit),
Swap(OpSwap),
@ -1909,20 +1969,6 @@ impl Instr {
Instr::new(Op::S2R(OpS2R { dst: dst, idx: idx }))
}
pub fn new_phi_src(phi_id: u32, src: Src) -> Instr {
Instr::new(Op::PhiSrc(OpPhiSrc {
phi_id: phi_id,
src: src,
}))
}
pub fn new_phi_dst(phi_id: u32, dst: Dst) -> Instr {
Instr::new(Op::PhiDst(OpPhiDst {
phi_id: phi_id,
dst: dst,
}))
}
pub fn new_vec(dst: Dst, srcs: &[Src]) -> Instr {
Instr::new(Op::Vec(OpVec {
dst: dst,
@ -2005,8 +2051,8 @@ impl Instr {
Op::Bra(_) | Op::Exit(_) => Some(15),
Op::FMov(_)
| Op::IMov(_)
| Op::PhiSrc(_)
| Op::PhiDst(_)
| Op::PhiSrcs(_)
| Op::PhiDsts(_)
| Op::Vec(_)
| Op::Split(_)
| Op::Swap(_)

View file

@ -55,12 +55,6 @@ impl DeadCodePass {
return true;
}
if let Op::PhiSrc(op) = &instr.op {
if self.live_phi.get(&op.phi_id).is_some() {
return true;
}
}
for dst in instr.dsts() {
if self.is_dst_live(dst) {
return true;
@ -72,20 +66,24 @@ impl DeadCodePass {
fn mark_instr(&mut self, instr: &Instr) {
match &instr.op {
Op::PhiSrc(phi) => {
Op::PhiSrcs(phi) => {
assert!(instr.pred.is_none());
if self.is_phi_live(phi.phi_id) {
self.mark_src_live(&phi.src);
} else {
self.any_dead = true;
for (id, src) in phi.iter() {
if self.is_phi_live(*id) {
self.mark_src_live(src);
} else {
self.any_dead = true;
}
}
}
Op::PhiDst(phi) => {
Op::PhiDsts(phi) => {
assert!(instr.pred.is_none());
if self.is_dst_live(&phi.dst) {
self.mark_phi_live(phi.phi_id);
} else {
self.any_dead = true;
for (id, dst) in phi.iter() {
if self.is_dst_live(dst) {
self.mark_phi_live(*id);
} else {
self.any_dead = true;
}
}
}
Op::ParCopy(pcopy) => {
@ -116,6 +114,32 @@ impl DeadCodePass {
fn map_instr(&self, mut instr: Instr) -> Vec<Instr> {
let is_live = match &mut instr.op {
Op::PhiSrcs(phi) => {
assert!(phi.ids.len() == phi.srcs.len());
let mut i = 0;
while i < phi.ids.len() {
if self.is_phi_live(phi.ids[i]) {
i += 1;
} else {
phi.srcs.remove(i);
phi.ids.remove(i);
}
}
i > 0
}
Op::PhiDsts(phi) => {
assert!(phi.ids.len() == phi.dsts.len());
let mut i = 0;
while i < phi.ids.len() {
if self.is_dst_live(&phi.dsts[i]) {
i += 1;
} else {
phi.ids.remove(i);
phi.dsts.remove(i);
}
}
i > 0
}
Op::ParCopy(pcopy) => {
assert!(pcopy.srcs.len() == pcopy.dsts.len());
let mut i = 0;