1use super::{Control, Error, Instruction, Opcode, Program, Register, SourceKind, error};
6use std::collections::BTreeSet;
7
8#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
10pub struct Dependencies {
11 pub textures: u16,
13 pub inputs: u16,
15}
16impl Dependencies {
17 fn join(self, other: Self) -> Self {
18 Self {
19 textures: self.textures | other.textures,
20 inputs: self.inputs | other.inputs,
21 }
22 }
23}
24
25#[derive(Clone, Debug, PartialEq, Eq)]
27pub struct Sample {
28 pub offset: usize,
30 pub unit: u8,
32 pub coordinates: Dependencies,
34 pub arguments: Dependencies,
36}
37
38#[derive(Clone, Debug, PartialEq, Eq)]
40pub struct Output {
41 pub register: Register,
43 pub components: [Dependencies; 4],
45}
46
47#[derive(Clone, Debug, PartialEq, Eq)]
49pub struct Analysis {
50 pub samples: Vec<Sample>,
52 pub outputs: Vec<Output>,
54 pub kill: Dependencies,
56 pub consumers: [Vec<Opcode>; 16],
58}
59
60#[derive(Clone)]
61struct State {
62 lanes: [Dependencies; 512],
65 cc: [[Dependencies; 4]; 2],
66 control: Dependencies,
67}
68impl Default for State {
69 fn default() -> Self {
70 Self {
71 lanes: [Dependencies::default(); 512],
72 cc: [[Dependencies::default(); 4]; 2],
73 control: Dependencies::default(),
74 }
75 }
76}
77fn unit_bit(n: usize) -> u16 {
79 u32::try_from(n)
80 .ok()
81 .and_then(|n| 1u16.checked_shl(n))
82 .unwrap_or(0)
83}
84fn positions(r: Register, c: usize) -> std::ops::Range<usize> {
86 if r.half {
87 let at = usize::from(r.index).saturating_mul(4).saturating_add(c);
88 at..at.saturating_add(1)
89 } else {
90 let at = usize::from(r.index)
91 .saturating_mul(8)
92 .saturating_add(c.saturating_mul(2));
93 at..at.saturating_add(2)
94 }
95}
96impl State {
97 fn read(&self, r: Register, c: usize) -> Dependencies {
98 positions(r, c)
99 .map(|n| self.lanes.get(n).copied().unwrap_or_default())
100 .fold(Dependencies::default(), Dependencies::join)
101 }
102 fn merge(&mut self, other: &Self) {
103 for (a, b) in self.lanes.iter_mut().zip(other.lanes) {
104 *a = a.join(b);
105 }
106 for (a, b) in self.cc.iter_mut().flatten().zip(other.cc.into_iter().flatten()) {
107 *a = a.join(b);
108 }
109 self.control = self.control.join(other.control);
110 }
111 fn predicate(&self, i: &Instruction) -> Dependencies {
112 if matches!(i.predicate.test, 0 | 7) {
113 Dependencies::default()
114 } else {
115 i.predicate
116 .swizzle
117 .iter()
118 .map(|&c| {
119 self.cc
120 .get(usize::from(i.predicate.register))
121 .and_then(|row| row.get(usize::from(c)))
122 .copied()
123 .unwrap_or_default()
124 })
125 .fold(Dependencies::default(), Dependencies::join)
126 }
127 }
128}
129
130pub fn analyze(program: &Program, outputs: &[Register]) -> Result<Analysis, Error> {
136 if outputs.iter().any(|r| r.index > 63) {
137 return Err(error(0, "output register index exceeds encoding"));
138 }
139 let code = &program.instructions;
140 let mut incoming: Vec<Option<State>> = vec![None; code.len().saturating_add(1)];
141 if let Some(first) = incoming.first_mut() {
142 *first = Some(State::default());
143 }
144 let mut exits: Option<State> = None;
145 let mut samples = Vec::new();
146 let mut consumers: [BTreeSet<u8>; 16] = std::array::from_fn(|_| BTreeSet::new());
147 let mut kill = Dependencies::default();
148 let mut branches = Vec::new();
149 let index = |offset| -> usize { code.iter().position(|i| i.offset == offset).unwrap_or(code.len()) };
150 for (n, i) in code.iter().enumerate() {
151 if i.indexed_input && i.sources.iter().any(|s| matches!(s.kind, SourceKind::Input(_))) {
152 return Err(error(
153 i.offset,
154 "texture flow analysis does not support indexed interpolators",
155 ));
156 }
157 match i.control {
158 Some(Control::If {
159 else_offset,
160 end_offset,
161 }) => branches.push((n, index(else_offset), index(end_offset))),
162 Some(Control::Loop { .. } | Control::Call { .. } | Control::Break) => {
163 return Err(error(
164 i.offset,
165 "texture flow analysis does not support calls, loops or BRK",
166 ));
167 }
168 _ => {}
169 }
170 }
171 for &(start, _, end) in &branches {
172 if branches.iter().any(|&(s, _, e)| start < s && s < end && e > end) {
173 return Err(error(
174 code.get(start).map_or(0, |i| i.offset),
175 "crossing IF regions in flow analysis",
176 ));
177 }
178 }
179 let merge_exit = |exits: &mut Option<State>, state: &State| {
180 if let Some(old) = exits {
181 old.merge(state);
182 } else {
183 *exits = Some(state.clone());
184 }
185 };
186 for (n, i) in code.iter().enumerate() {
187 let Some(mut state) = incoming.get_mut(n).and_then(Option::take) else {
188 continue;
189 };
190 let predicate = state.predicate(i);
191 let mut source = Dependencies::default();
192 let mut coordinates = Dependencies::default();
193 for (operand, s) in i.sources.iter().enumerate() {
194 let dependency = match s.kind {
195 SourceKind::Register(r) => s
196 .swizzle
197 .iter()
198 .map(|&c| state.read(r, usize::from(c)))
199 .fold(Dependencies::default(), Dependencies::join),
200 SourceKind::Input(k) => Dependencies {
201 textures: 0,
202 inputs: unit_bit(usize::from(k)),
203 },
204 SourceKind::Constant => Dependencies::default(),
205 };
206 if operand == 0 {
207 coordinates = dependency;
208 }
209 source = source.join(dependency);
210 }
211 let influenced = source.join(predicate).join(state.control);
212 if i.predicate.test != 0 {
213 for (unit, set) in consumers.iter_mut().enumerate() {
214 if influenced.textures & unit_bit(unit) != 0 {
215 set.insert(i.opcode as u8);
216 }
217 }
218 let mut result = influenced;
219 if let Some(unit) = i.texture_unit {
220 samples.push(Sample {
221 offset: i.offset,
222 unit,
223 coordinates,
224 arguments: source,
225 });
226 result.textures |= unit_bit(usize::from(unit));
227 }
228 if i.opcode.writes_destination() {
229 for c in 0..4 {
230 if i.write_mask & unit_bit(c).to_le_bytes()[0] == 0 {
231 continue;
232 }
233 if !i.no_destination && i.destination.index != 63 {
234 for at in positions(i.destination, c) {
235 if let Some(lane) = state.lanes.get_mut(at) {
236 *lane = if i.predicate.test == 7 {
237 result
238 } else {
239 lane.join(result)
240 };
241 }
242 }
243 }
244 if i.update_condition
245 && let Some(cc) = state
246 .cc
247 .get_mut(usize::from(i.condition_destination))
248 .and_then(|row| row.get_mut(c))
249 {
250 *cc = if i.predicate.test == 7 {
251 result
252 } else {
253 cc.join(result)
254 };
255 }
256 }
257 }
258 if i.opcode == Opcode::KIL {
259 kill = kill.join(predicate).join(state.control);
260 }
261 }
262 let mut next = Vec::new();
263 let returns = matches!(i.control, Some(Control::Return));
264 let kills = i.opcode == Opcode::KIL;
265 if returns && i.predicate.test != 0 {
266 state.control = state.control.join(predicate);
267 }
268 if (i.end || returns && i.predicate.test != 0) && !(kills && i.predicate.test == 7) {
269 merge_exit(&mut exits, &state);
270 }
271 if !i.end && (!(returns || kills) || i.predicate.test != 7) {
272 if let Some(Control::If {
273 else_offset,
274 end_offset,
275 }) = i.control
276 {
277 state.control = state.control.join(predicate);
278 if i.predicate.test != 0 {
279 next.push(if n.saturating_add(1) == index(else_offset) {
280 index(end_offset)
281 } else {
282 n.saturating_add(1)
283 });
284 }
285 if i.predicate.test != 7 {
286 next.push(index(else_offset));
287 }
288 } else {
289 next.push(n.saturating_add(1));
290 }
291 }
292 for mut successor in next {
293 for &(start, else_at, end_at) in branches.iter().rev() {
296 if start < n && n < else_at && successor == else_at && else_at != end_at {
297 successor = end_at;
298 }
299 }
300 if let Some(slot) = incoming.get_mut(successor) {
301 match slot {
302 Some(old) => old.merge(&state),
303 None => *slot = Some(state.clone()),
304 }
305 }
306 }
307 }
308 if let Some(state) = incoming.last().and_then(Option::as_ref) {
309 merge_exit(&mut exits, state);
310 }
311 let state = exits.unwrap_or_default();
312 Ok(Analysis {
313 samples,
314 outputs: outputs
315 .iter()
316 .map(|&r| Output {
317 register: r,
318 components: std::array::from_fn(|c| state.read(r, c)),
319 })
320 .collect(),
321 kill,
322 consumers: consumers.map(|set| set.into_iter().filter_map(Opcode::read).collect()),
323 })
324}