1 // Copyright © 2023 Collabora, Ltd. 2 // SPDX-License-Identifier: MIT 3 4 use crate::api::{GetDebugFlags, DEBUG}; 5 use crate::ir::*; 6 7 use compiler::bitset::BitSet; 8 use std::collections::HashMap; 9 10 struct PhiMap { 11 phi_ssa: HashMap<u32, Vec<SSAValue>>, 12 ssa_phi: HashMap<SSAValue, u32>, 13 } 14 15 impl PhiMap { new() -> PhiMap16 pub fn new() -> PhiMap { 17 PhiMap { 18 ssa_phi: HashMap::new(), 19 phi_ssa: HashMap::new(), 20 } 21 } 22 add_phi_srcs(&mut self, op: &OpPhiSrcs)23 fn add_phi_srcs(&mut self, op: &OpPhiSrcs) { 24 for (idx, src) in op.srcs.iter() { 25 if let SrcRef::SSA(ssa) = &src.src_ref { 26 assert!(ssa.comps() == 1); 27 let phi_srcs = self.phi_ssa.entry(*idx).or_default(); 28 phi_srcs.push(ssa[0]); 29 } 30 } 31 } 32 add_phi_dsts(&mut self, op: &OpPhiDsts)33 fn add_phi_dsts(&mut self, op: &OpPhiDsts) { 34 for (idx, dst) in op.dsts.iter() { 35 if let Dst::SSA(ssa) = dst { 36 assert!(ssa.comps() == 1); 37 self.ssa_phi.insert(ssa[0], *idx); 38 } 39 } 40 } 41 get_phi(&self, ssa: &SSAValue) -> Option<&u32>42 fn get_phi(&self, ssa: &SSAValue) -> Option<&u32> { 43 self.ssa_phi.get(ssa) 44 } 45 phi_srcs(&self, idx: &u32) -> &[SSAValue]46 fn phi_srcs(&self, idx: &u32) -> &[SSAValue] { 47 self.phi_ssa.get(idx).unwrap() 48 } 49 } 50 51 struct BarPropPass { 52 ssa_map: HashMap<SSAValue, SSAValue>, 53 phi_is_bar: BitSet, 54 phi_is_not_bar: BitSet, 55 } 56 57 impl BarPropPass { new() -> BarPropPass58 pub fn new() -> BarPropPass { 59 BarPropPass { 60 ssa_map: HashMap::new(), 61 phi_is_bar: BitSet::new(), 62 phi_is_not_bar: BitSet::new(), 63 } 64 } 65 add_copy(&mut self, dst: SSAValue, src: SSAValue)66 fn add_copy(&mut self, dst: SSAValue, src: SSAValue) { 67 assert!(dst.file() == RegFile::Bar || src.file() == RegFile::Bar); 68 self.ssa_map.insert(dst, src); 69 } 70 is_bar(&self, ssa: &SSAValue) -> bool71 fn is_bar(&self, ssa: &SSAValue) -> bool { 72 ssa.file() == RegFile::Bar || self.ssa_map.contains_key(ssa) 73 } 74 map_bar<'a>(&'a self, ssa: &'a SSAValue) -> Option<&SSAValue>75 fn map_bar<'a>(&'a self, ssa: &'a SSAValue) -> Option<&SSAValue> { 76 let mut ssa = ssa; 77 let mut last_bar = None; 78 loop { 79 let Some(mapped) = self.ssa_map.get(ssa) else { 80 break; 81 }; 82 83 if mapped.file() == RegFile::Bar { 84 last_bar = Some(mapped); 85 } 86 ssa = mapped; 87 } 88 89 last_bar 90 } 91 phi_can_be_bar_recur( &mut self, phi_map: &PhiMap, seen: &mut BitSet, phi: u32, ) -> bool92 fn phi_can_be_bar_recur( 93 &mut self, 94 phi_map: &PhiMap, 95 seen: &mut BitSet, 96 phi: u32, 97 ) -> bool { 98 if self.phi_is_not_bar.get(phi.try_into().unwrap()) { 99 return false; 100 } 101 102 if seen.get(phi.try_into().unwrap()) { 103 // If we've hit a cycle, that's not a fail 104 return true; 105 } 106 107 seen.insert(phi.try_into().unwrap()); 108 109 for src_ssa in phi_map.phi_srcs(&phi) { 110 if self.is_bar(src_ssa) { 111 continue; 112 } 113 114 if let Some(src_phi) = phi_map.get_phi(src_ssa) { 115 if self.phi_can_be_bar_recur(phi_map, seen, *src_phi) { 116 continue; 117 } 118 } 119 120 self.phi_is_not_bar.insert(phi.try_into().unwrap()); 121 return false; 122 } 123 124 true 125 } 126 add_phi_recur( &mut self, ssa_alloc: &mut SSAValueAllocator, phi_map: &PhiMap, needs_bar: &mut BitSet, phi: u32, ssa: SSAValue, )127 fn add_phi_recur( 128 &mut self, 129 ssa_alloc: &mut SSAValueAllocator, 130 phi_map: &PhiMap, 131 needs_bar: &mut BitSet, 132 phi: u32, 133 ssa: SSAValue, 134 ) { 135 if !needs_bar.get(phi.try_into().unwrap()) { 136 return; 137 } 138 139 let bar = ssa_alloc.alloc(RegFile::Bar); 140 self.ssa_map.insert(ssa, bar); 141 self.phi_is_bar.insert(phi.try_into().unwrap()); 142 needs_bar.remove(phi.try_into().unwrap()); 143 144 for src_ssa in phi_map.phi_srcs(&phi) { 145 if let Some(src_phi) = phi_map.get_phi(src_ssa) { 146 self.add_phi_recur( 147 ssa_alloc, phi_map, needs_bar, *src_phi, *src_ssa, 148 ); 149 } 150 } 151 } 152 try_add_phi( &mut self, ssa_alloc: &mut SSAValueAllocator, phi_map: &PhiMap, phi: u32, ssa: SSAValue, )153 fn try_add_phi( 154 &mut self, 155 ssa_alloc: &mut SSAValueAllocator, 156 phi_map: &PhiMap, 157 phi: u32, 158 ssa: SSAValue, 159 ) { 160 if self.phi_is_bar.get(phi.try_into().unwrap()) { 161 return; 162 } 163 164 let mut seen = BitSet::new(); 165 if self.phi_can_be_bar_recur(phi_map, &mut seen, phi) { 166 self.add_phi_recur(ssa_alloc, phi_map, &mut seen, phi, ssa); 167 assert!(seen.is_empty()); 168 } 169 } 170 run(&mut self, f: &mut Function)171 fn run(&mut self, f: &mut Function) { 172 let mut phi_map = PhiMap::new(); 173 let mut phis_want_bar = Vec::new(); 174 for b in &f.blocks { 175 for instr in &b.instrs { 176 match &instr.op { 177 Op::PhiSrcs(op) => { 178 phi_map.add_phi_srcs(op); 179 } 180 Op::PhiDsts(op) => { 181 phi_map.add_phi_dsts(op); 182 } 183 Op::BMov(op) => { 184 assert!(!op.clear); 185 assert!(op.src.src_mod.is_none()); 186 let dst = op.dst.as_ssa().unwrap(); 187 let src = op.src.as_ssa().unwrap(); 188 assert!(dst.comps() == 1 && src.comps() == 1); 189 190 self.add_copy(dst[0], src[0]); 191 192 if let Some(phi) = phi_map.get_phi(&src[0]) { 193 phis_want_bar.push((*phi, src[0])); 194 } 195 } 196 _ => (), 197 } 198 } 199 } 200 201 for (phi, ssa) in phis_want_bar.drain(..) { 202 self.try_add_phi(&mut f.ssa_alloc, &phi_map, phi, ssa); 203 } 204 205 f.map_instrs(|mut instr, _| { 206 match &mut instr.op { 207 Op::PhiSrcs(op) => { 208 for (idx, src) in op.srcs.iter_mut() { 209 if self.phi_is_bar.get((*idx).try_into().unwrap()) { 210 // Barrier immediates don't exist 211 let ssa = src.as_ssa().unwrap(); 212 let bar = *self.map_bar(&ssa[0]).unwrap(); 213 *src = bar.into(); 214 } 215 } 216 MappedInstrs::One(instr) 217 } 218 Op::PhiDsts(op) => { 219 let mut bmovs = Vec::new(); 220 for (idx, dst) in op.dsts.iter_mut() { 221 if self.phi_is_bar.get((*idx).try_into().unwrap()) { 222 let ssa = *dst.as_ssa().unwrap(); 223 let bar = *self.ssa_map.get(&ssa[0]).unwrap(); 224 *dst = bar.into(); 225 226 // On the off chance that someone still wants the 227 // GPR version of this barrier, insert an OpBMov to 228 // copy into the GPR. DCE will clean it up if it's 229 // never used. 230 bmovs.push(Instr::new_boxed(OpBMov { 231 dst: ssa.into(), 232 src: bar.into(), 233 clear: false, 234 })); 235 } 236 } 237 if bmovs.is_empty() { 238 MappedInstrs::One(instr) 239 } else { 240 if DEBUG.annotate() { 241 bmovs.insert( 242 0, 243 Instr::new_boxed(OpAnnotate { 244 annotation: "generated by opt_bar_prop" 245 .into(), 246 }), 247 ); 248 } 249 bmovs.insert(1, instr); 250 MappedInstrs::Many(bmovs) 251 } 252 } 253 _ => { 254 let src_types = instr.src_types(); 255 for (i, src) in instr.srcs_mut().iter_mut().enumerate() { 256 if src_types[i] != SrcType::Bar { 257 continue; 258 } 259 if let SrcRef::SSA(ssa) = &src.src_ref { 260 if let Some(bar) = self.map_bar(&ssa[0]) { 261 *src = (*bar).into(); 262 } 263 }; 264 } 265 MappedInstrs::One(instr) 266 } 267 } 268 }); 269 } 270 } 271 272 impl Shader<'_> { opt_bar_prop(&mut self)273 pub fn opt_bar_prop(&mut self) { 274 for f in &mut self.functions { 275 BarPropPass::new().run(f); 276 } 277 } 278 } 279