nak: Add a new VecPair type

This is a pair of vectors but it acts a bit like a vector of a pair.
Everything is inserted, removed, iterated, etc. in tandem to ensure that
we never mismatch between the two vectors.  However, because they're two
separate vectors, we ensure that it can be used to store Src and Dst and
keep everything contiguous.

Of particular interest is the new retain() method which is equivalent to
Vec::retain() which allows filtering a VecPair.  This tricky operation
is performed three different times by DCE and will be needed in spilling
as well.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/24998>
This commit is contained in:
Faith Ekstrand 2023-06-16 14:26:32 -05:00 committed by Marge Bot
parent 67c13aea9e
commit 756d0c0ba3
6 changed files with 121 additions and 124 deletions

View file

@ -483,8 +483,7 @@ impl<'a> PinnedRegAllocator<'a> {
}
fn finish(mut self, pcopy: &mut OpParCopy) {
pcopy.srcs.append(&mut self.pcopy.srcs);
pcopy.dsts.append(&mut self.pcopy.dsts);
pcopy.dsts_srcs.append(&mut self.pcopy.dsts_srcs);
if !self.evicted.is_empty() {
/* Sort so we get determinism, even if the hash map order changes
@ -845,7 +844,7 @@ impl AssignRegsBlock {
None
}
Op::PhiSrcs(phi) => {
for (id, src) in phi.iter() {
for (id, src) in phi.srcs.iter() {
assert!(src.src_mod.is_none());
if let SrcRef::SSA(ssa) = src.src_ref {
assert!(ssa.comps() == 1);
@ -862,7 +861,7 @@ impl AssignRegsBlock {
Op::PhiDsts(phi) => {
assert!(instr.pred.is_true());
for (id, dst) in phi.iter() {
for (id, dst) in phi.dsts.iter() {
if let Dst::SSA(ssa) = dst {
assert!(ssa.comps() == 1);
let ra = &mut self.ra[ssa.file()];

View file

@ -1551,7 +1551,7 @@ impl<'a> ShaderFromNir<'a> {
let dst = alloc_ssa_for_nir(&mut b, np.def.as_def());
for (i, dst) in dst.iter().enumerate() {
let phi_id = self.get_phi_id(np, i.try_into().unwrap());
phi.push(phi_id, (*dst).into());
phi.dsts.push(phi_id, (*dst).into());
}
self.set_ssa(np.def.as_def(), dst);
} else {
@ -1559,7 +1559,7 @@ impl<'a> ShaderFromNir<'a> {
}
}
if !phi.ids.is_empty() {
if !phi.dsts.is_empty() {
b.push_op(phi);
}
@ -1609,14 +1609,14 @@ impl<'a> ShaderFromNir<'a> {
for (i, src) in src.iter().enumerate() {
let phi_id =
self.get_phi_id(np, i.try_into().unwrap());
phi.push(phi_id, (*src).into());
phi.srcs.push(phi_id, (*src).into());
}
break;
}
}
}
if !phi.ids.is_empty() {
if !phi.srcs.is_empty() {
b.push_op(phi);
}
}

View file

@ -3007,46 +3007,104 @@ impl fmt::Display for OpUndef {
}
}
pub struct VecPair<A, B> {
a: Vec<A>,
b: Vec<B>,
}
impl<A, B> VecPair<A, B> {
pub fn append(&mut self, other: &mut VecPair<A, B>) {
self.a.append(&mut other.a);
self.b.append(&mut other.b);
}
pub fn is_empty(&self) -> bool {
debug_assert!(self.a.len() == self.b.len());
self.a.is_empty()
}
pub fn iter(&self) -> Zip<slice::Iter<'_, A>, slice::Iter<'_, B>> {
debug_assert!(self.a.len() == self.b.len());
self.a.iter().zip(self.b.iter())
}
pub fn len(&self) -> usize {
debug_assert!(self.a.len() == self.b.len());
self.a.len()
}
pub fn new() -> Self {
Self {
a: Vec::new(),
b: Vec::new(),
}
}
pub fn push(&mut self, a: A, b: B) {
debug_assert!(self.a.len() == self.b.len());
self.a.push(a);
self.b.push(b);
}
}
impl<A: Clone, B: Clone> VecPair<A, B> {
pub fn retain(&mut self, f: impl Fn(&A, &B) -> bool) {
debug_assert!(self.a.len() == self.b.len());
let len = self.a.len();
let mut i = 0_usize;
while i < len {
if !f(&self.a[i], &self.b[i]) {
break;
}
i += 1;
}
let mut new_len = i;
/* Don't check this one twice. */
i += 1;
while i < len {
/* This could be more efficient but it's good enough for our
* purposes since everything we're storing is small and has a
* trivial Drop.
*/
if f(&self.a[i], &self.b[i]) {
self.a[new_len] = self.a[i].clone();
self.b[new_len] = self.b[i].clone();
new_len += 1;
}
i += 1;
}
if new_len < len {
self.a.truncate(new_len);
self.b.truncate(new_len);
}
}
}
#[repr(C)]
#[derive(DstsAsSlice)]
pub struct OpPhiSrcs {
pub srcs: Vec<Src>,
pub ids: Vec<u32>,
pub srcs: VecPair<u32, Src>,
}
impl OpPhiSrcs {
pub fn new() -> OpPhiSrcs {
OpPhiSrcs {
srcs: Vec::new(),
ids: Vec::new(),
srcs: VecPair::new(),
}
}
#[allow(dead_code)]
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())
}
pub fn push(&mut self, id: u32, src: Src) {
assert!(self.ids.len() == self.srcs.len());
self.ids.push(id);
self.srcs.push(src);
}
}
impl SrcsAsSlice for OpPhiSrcs {
fn srcs_as_slice(&self) -> &[Src] {
&self.srcs
&self.srcs.b
}
fn srcs_as_mut_slice(&mut self) -> &mut [Src] {
&mut self.srcs
&mut self.srcs.b
}
fn src_types(&self) -> SrcTypeList {
@ -3057,12 +3115,11 @@ impl SrcsAsSlice for OpPhiSrcs {
impl fmt::Display for OpPhiSrcs {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PHI_SRC {{")?;
assert!(self.ids.len() == self.srcs.len());
for i in 0..self.ids.len() {
for (i, (id, src)) in self.srcs.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write!(f, " {} <- {}", self.ids[i], self.srcs[i])?;
write!(f, " {} <- {}", id, src)?;
}
write!(f, " }}")
}
@ -3071,55 +3128,35 @@ impl fmt::Display for OpPhiSrcs {
#[repr(C)]
#[derive(SrcsAsSlice)]
pub struct OpPhiDsts {
pub ids: Vec<u32>,
pub dsts: Vec<Dst>,
pub dsts: VecPair<u32, Dst>,
}
impl OpPhiDsts {
pub fn new() -> OpPhiDsts {
OpPhiDsts {
ids: Vec::new(),
dsts: Vec::new(),
dsts: VecPair::new(),
}
}
#[allow(dead_code)]
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())
}
pub fn push(&mut self, id: u32, dst: Dst) {
assert!(self.ids.len() == self.dsts.len());
self.ids.push(id);
self.dsts.push(dst);
}
}
impl DstsAsSlice for OpPhiDsts {
fn dsts_as_slice(&self) -> &[Dst] {
&self.dsts
&self.dsts.b
}
fn dsts_as_mut_slice(&mut self) -> &mut [Dst] {
&mut self.dsts
&mut self.dsts.b
}
}
impl fmt::Display for OpPhiDsts {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PHI_DST {{")?;
assert!(self.ids.len() == self.dsts.len());
for i in 0..self.ids.len() {
for (i, (id, dst)) in self.dsts.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write!(f, " {} <- {}", self.dsts[i], self.ids[i])?;
write!(f, " {} <- {}", dst, id)?;
}
write!(f, " }}")
}
@ -3144,42 +3181,32 @@ impl fmt::Display for OpSwap {
#[repr(C)]
pub struct OpParCopy {
pub dsts: Vec<Dst>,
pub srcs: Vec<Src>,
pub dsts_srcs: VecPair<Dst, Src>,
}
impl OpParCopy {
pub fn new() -> OpParCopy {
OpParCopy {
dsts: Vec::new(),
srcs: Vec::new(),
dsts_srcs: VecPair::new(),
}
}
pub fn is_empty(&self) -> bool {
assert!(self.srcs.len() == self.dsts.len());
self.srcs.is_empty()
}
pub fn iter(&self) -> Zip<slice::Iter<'_, Dst>, slice::Iter<'_, Src>> {
assert!(self.srcs.len() == self.dsts.len());
self.dsts.iter().zip(&self.srcs)
self.dsts_srcs.is_empty()
}
pub fn push(&mut self, dst: Dst, src: Src) {
assert!(self.srcs.len() == self.dsts.len());
self.srcs.push(src);
self.dsts.push(dst);
self.dsts_srcs.push(dst, src);
}
}
impl SrcsAsSlice for OpParCopy {
fn srcs_as_slice(&self) -> &[Src] {
&self.srcs
&self.dsts_srcs.b
}
fn srcs_as_mut_slice(&mut self) -> &mut [Src] {
&mut self.srcs
&mut self.dsts_srcs.b
}
fn src_types(&self) -> SrcTypeList {
@ -3189,23 +3216,22 @@ impl SrcsAsSlice for OpParCopy {
impl DstsAsSlice for OpParCopy {
fn dsts_as_slice(&self) -> &[Dst] {
&self.dsts
&self.dsts_srcs.a
}
fn dsts_as_mut_slice(&mut self) -> &mut [Dst] {
&mut self.dsts
&mut self.dsts_srcs.a
}
}
impl fmt::Display for OpParCopy {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PAR_COPY {{")?;
assert!(self.srcs.len() == self.dsts.len());
for i in 0..self.srcs.len() {
for (i, (dst, src)) in self.dsts_srcs.iter().enumerate() {
if i > 0 {
write!(f, ",")?;
}
write!(f, " {} <- {}", self.dsts[i], self.srcs[i])?;
write!(f, " {} <- {}", dst, src)?;
}
write!(f, " }}")
}
@ -4055,8 +4081,7 @@ impl Shader {
for (i, src) in out.srcs.iter().enumerate() {
let dst =
RegRef::new(RegFile::GPR, i.try_into().unwrap(), 1);
pcopy.srcs.push(*src);
pcopy.dsts.push(dst.into());
pcopy.push(dst.into(), *src);
}
MappedInstrs::One(Instr::new_boxed(pcopy))
}

View file

@ -65,7 +65,7 @@ fn lower_par_copy(pc: OpParCopy) -> MappedInstrs {
let mut vals = Vec::new();
let mut reg_to_idx = HashMap::new();
for (i, dst) in pc.dsts.iter().enumerate() {
for (i, (dst, _)) in pc.dsts_srcs.iter().enumerate() {
/* Destinations must be pairwise unique */
let reg = dst.as_reg().unwrap();
assert!(reg_to_idx.get(reg).is_none());
@ -79,7 +79,7 @@ fn lower_par_copy(pc: OpParCopy) -> MappedInstrs {
reg_to_idx.insert(*reg, i);
}
for (dst_idx, src) in pc.srcs.iter().enumerate() {
for (dst_idx, (_, src)) in pc.dsts_srcs.iter().enumerate() {
assert!(src.src_mod.is_none());
let src = src.src_ref;
@ -113,7 +113,7 @@ fn lower_par_copy(pc: OpParCopy) -> MappedInstrs {
let mut b = InstrBuilder::new();
let mut ready = Vec::new();
for i in 0..pc.dsts.len() {
for i in 0..pc.dsts_srcs.len() {
if graph.num_reads(i) == 0 {
ready.push(i);
}
@ -156,7 +156,7 @@ fn lower_par_copy(pc: OpParCopy) -> MappedInstrs {
*
* QED
*/
for i in 0..pc.dsts.len() {
for i in 0..pc.dsts_srcs.len() {
loop {
if let Some(j) = graph.src(i) {
/* We're part of a cycle so j also has a source */

View file

@ -415,7 +415,7 @@ impl CopyPropPass {
self.add_copy(dst[0], SrcType::I32, neg.src.ineg());
}
Op::ParCopy(pcopy) => {
for (dst, src) in pcopy.iter() {
for (dst, src) in pcopy.dsts_srcs.iter() {
let dst = dst.as_ssa().unwrap();
assert!(dst.comps() == 1);
self.add_copy(dst[0], SrcType::GPR, *src);

View file

@ -81,7 +81,7 @@ impl DeadCodePass {
match &instr.op {
Op::PhiSrcs(phi) => {
assert!(instr.pred.is_true());
for (id, src) in phi.iter() {
for (id, src) in phi.srcs.iter() {
if self.is_phi_live(*id) {
self.mark_src_live(src);
} else {
@ -91,7 +91,7 @@ impl DeadCodePass {
}
Op::PhiDsts(phi) => {
assert!(instr.pred.is_true());
for (id, dst) in phi.iter() {
for (id, dst) in phi.dsts.iter() {
if self.is_dst_live(dst) {
self.mark_phi_live(*id);
} else {
@ -101,7 +101,7 @@ impl DeadCodePass {
}
Op::ParCopy(pcopy) => {
assert!(instr.pred.is_true());
for (dst, src) in pcopy.iter() {
for (dst, src) in pcopy.dsts_srcs.iter() {
if self.is_dst_live(dst) {
self.mark_src_live(src);
} else {
@ -128,43 +128,16 @@ impl DeadCodePass {
fn map_instr(&self, mut instr: Box<Instr>) -> MappedInstrs {
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
phi.srcs.retain(|id, _| self.is_phi_live(*id));
!phi.srcs.is_empty()
}
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
phi.dsts.retain(|_, dst| self.is_dst_live(dst));
!phi.dsts.is_empty()
}
Op::ParCopy(pcopy) => {
assert!(pcopy.srcs.len() == pcopy.dsts.len());
let mut i = 0;
while i < pcopy.dsts.len() {
if self.is_dst_live(&pcopy.dsts[i]) {
i += 1;
} else {
pcopy.srcs.remove(i);
pcopy.dsts.remove(i);
}
}
i > 0
pcopy.dsts_srcs.retain(|dst, _| self.is_dst_live(dst));
!pcopy.dsts_srcs.is_empty()
}
_ => self.is_instr_live(&instr),
};