• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright © 2022 Collabora, Ltd.
2 // SPDX-License-Identifier: MIT
3 
4 use crate::bitset::BitSet;
5 use crate::ir::*;
6 
7 use std::cell::RefCell;
8 use std::cmp::{max, Ord, Ordering};
9 use std::collections::{hash_set, HashMap, HashSet};
10 
11 #[derive(Clone)]
12 pub struct LiveSet {
13     live: PerRegFile<u32>,
14     set: HashSet<SSAValue>,
15 }
16 
17 impl LiveSet {
new() -> LiveSet18     pub fn new() -> LiveSet {
19         LiveSet {
20             live: Default::default(),
21             set: HashSet::new(),
22         }
23     }
24 
contains(&self, ssa: &SSAValue) -> bool25     pub fn contains(&self, ssa: &SSAValue) -> bool {
26         self.set.contains(ssa)
27     }
28 
count(&self, file: RegFile) -> u3229     pub fn count(&self, file: RegFile) -> u32 {
30         self.live[file]
31     }
32 
insert(&mut self, ssa: SSAValue) -> bool33     pub fn insert(&mut self, ssa: SSAValue) -> bool {
34         if self.set.insert(ssa) {
35             self.live[ssa.file()] += 1;
36             true
37         } else {
38             false
39         }
40     }
41 
iter(&self) -> hash_set::Iter<SSAValue>42     pub fn iter(&self) -> hash_set::Iter<SSAValue> {
43         self.set.iter()
44     }
45 
remove(&mut self, ssa: &SSAValue) -> bool46     pub fn remove(&mut self, ssa: &SSAValue) -> bool {
47         if self.set.remove(ssa) {
48             self.live[ssa.file()] -= 1;
49             true
50         } else {
51             false
52         }
53     }
54 
insert_instr_top_down<L: BlockLiveness>( &mut self, ip: usize, instr: &Instr, bl: &L, ) -> PerRegFile<u32>55     pub fn insert_instr_top_down<L: BlockLiveness>(
56         &mut self,
57         ip: usize,
58         instr: &Instr,
59         bl: &L,
60     ) -> PerRegFile<u32> {
61         // Vector destinations go live before sources are killed.  Even
62         // in the case where the destination is immediately killed, it
63         // still may contribute to pressure temporarily.
64         for dst in instr.dsts() {
65             if let Dst::SSA(vec) = dst {
66                 if vec.comps() > 1 {
67                     for ssa in vec.iter() {
68                         self.insert(*ssa);
69                     }
70                 }
71             }
72         }
73 
74         let after_dsts_live = self.live;
75 
76         instr.for_each_ssa_use(|ssa| {
77             if !bl.is_live_after_ip(ssa, ip) {
78                 self.remove(ssa);
79             }
80         });
81 
82         // Scalar destinations are allocated last
83         for dst in instr.dsts() {
84             if let Dst::SSA(vec) = dst {
85                 if vec.comps() == 1 {
86                     self.insert(vec[0]);
87                 }
88             }
89         }
90 
91         let max_live = PerRegFile::new_with(|file| {
92             max(self.live[file], after_dsts_live[file])
93         });
94 
95         // It's possible (but unlikely) that a destination is immediately
96         // killed. Remove any which are killed by this instruction.
97         instr.for_each_ssa_def(|ssa| {
98             debug_assert!(self.contains(ssa));
99             if !bl.is_live_after_ip(ssa, ip) {
100                 self.remove(ssa);
101             }
102         });
103 
104         max_live
105     }
106 }
107 
108 impl FromIterator<SSAValue> for LiveSet {
from_iter<T: IntoIterator<Item = SSAValue>>(iter: T) -> Self109     fn from_iter<T: IntoIterator<Item = SSAValue>>(iter: T) -> Self {
110         let mut set = LiveSet::new();
111         for ssa in iter {
112             set.insert(ssa);
113         }
114         set
115     }
116 }
117 
118 pub trait BlockLiveness {
119     /// Returns true if @val is still live after @ip
is_live_after_ip(&self, val: &SSAValue, ip: usize) -> bool120     fn is_live_after_ip(&self, val: &SSAValue, ip: usize) -> bool;
121 
122     /// Returns true if @val is live-in to this block
is_live_in(&self, val: &SSAValue) -> bool123     fn is_live_in(&self, val: &SSAValue) -> bool;
124 
125     /// Returns true if @val is live-out of this block
is_live_out(&self, val: &SSAValue) -> bool126     fn is_live_out(&self, val: &SSAValue) -> bool;
127 
get_instr_pressure(&self, ip: usize, instr: &Instr) -> PerRegFile<u8>128     fn get_instr_pressure(&self, ip: usize, instr: &Instr) -> PerRegFile<u8> {
129         let mut live = PerRegFile::new_with(|_| 0_i8);
130 
131         // Vector destinations go live before sources are killed.
132         for dst in instr.dsts() {
133             if let Dst::SSA(vec) = dst {
134                 if vec.comps() > 1 {
135                     for ssa in vec.iter() {
136                         live[ssa.file()] += 1;
137                     }
138                 }
139             }
140         }
141 
142         // This is the first high point
143         let vec_dst_live = live;
144 
145         // Use a hash set because sources may occur more than once
146         let mut killed = HashSet::new();
147         instr.for_each_ssa_use(|ssa| {
148             if !self.is_live_after_ip(ssa, ip) {
149                 killed.insert(*ssa);
150             }
151         });
152         for ssa in killed.drain() {
153             live[ssa.file()] -= 1;
154         }
155 
156         // Scalar destinations are allocated last
157         for dst in instr.dsts() {
158             if let Dst::SSA(vec) = dst {
159                 if vec.comps() == 1 {
160                     live[vec[0].file()] += 1;
161                 }
162             }
163         }
164 
165         PerRegFile::new_with(|file| {
166             max(0, max(vec_dst_live[file], live[file]))
167                 .try_into()
168                 .unwrap()
169         })
170     }
171 }
172 
173 pub trait Liveness {
174     type PerBlock: BlockLiveness;
175 
block_live(&self, idx: usize) -> &Self::PerBlock176     fn block_live(&self, idx: usize) -> &Self::PerBlock;
177 
calc_max_live(&self, f: &Function) -> PerRegFile<u32>178     fn calc_max_live(&self, f: &Function) -> PerRegFile<u32> {
179         let mut max_live: PerRegFile<u32> = Default::default();
180         let mut block_live_out: Vec<LiveSet> = Vec::new();
181 
182         for (bb_idx, bb) in f.blocks.iter().enumerate() {
183             let bl = self.block_live(bb_idx);
184 
185             let mut live = LiveSet::new();
186 
187             // Predecessors are added block order so we can just grab the first
188             // one (if any) and it will be a block we've processed.
189             if let Some(pred_idx) = f.blocks.pred_indices(bb_idx).first() {
190                 let pred_out = &block_live_out[*pred_idx];
191                 for ssa in pred_out.iter() {
192                     if bl.is_live_in(ssa) {
193                         live.insert(*ssa);
194                     }
195                 }
196             }
197 
198             for (ip, instr) in bb.instrs.iter().enumerate() {
199                 let live_at_instr = live.insert_instr_top_down(ip, instr, bl);
200                 max_live = PerRegFile::new_with(|file| {
201                     max(max_live[file], live_at_instr[file])
202                 });
203 
204                 if let Op::FSOut(fs_out) = &instr.op {
205                     // This should be the last instruction.  Everything should
206                     // be dead once we've processed it.
207                     debug_assert!(live.count(RegFile::GPR) == 0);
208                     let num_gprs_out = fs_out.srcs.len().try_into().unwrap();
209                     max_live[RegFile::GPR] =
210                         max(max_live[RegFile::GPR], num_gprs_out);
211                 }
212             }
213 
214             assert!(block_live_out.len() == bb_idx);
215             block_live_out.push(live);
216         }
217 
218         max_live
219     }
220 }
221 
222 pub struct SimpleBlockLiveness {
223     defs: BitSet,
224     uses: BitSet,
225     last_use: HashMap<u32, usize>,
226     live_in: BitSet,
227     live_out: BitSet,
228 }
229 
230 impl SimpleBlockLiveness {
new() -> Self231     fn new() -> Self {
232         Self {
233             defs: BitSet::new(),
234             uses: BitSet::new(),
235             last_use: HashMap::new(),
236             live_in: BitSet::new(),
237             live_out: BitSet::new(),
238         }
239     }
240 
add_def(&mut self, ssa: SSAValue)241     fn add_def(&mut self, ssa: SSAValue) {
242         self.defs.insert(ssa.idx().try_into().unwrap());
243     }
244 
add_use(&mut self, ssa: SSAValue, ip: usize)245     fn add_use(&mut self, ssa: SSAValue, ip: usize) {
246         self.uses.insert(ssa.idx().try_into().unwrap());
247         self.last_use.insert(ssa.idx(), ip);
248     }
249 }
250 
251 impl BlockLiveness for SimpleBlockLiveness {
is_live_after_ip(&self, val: &SSAValue, ip: usize) -> bool252     fn is_live_after_ip(&self, val: &SSAValue, ip: usize) -> bool {
253         if self.live_out.get(val.idx().try_into().unwrap()) {
254             true
255         } else {
256             if let Some(last_use_ip) = self.last_use.get(&val.idx()) {
257                 *last_use_ip > ip
258             } else {
259                 false
260             }
261         }
262     }
263 
is_live_in(&self, val: &SSAValue) -> bool264     fn is_live_in(&self, val: &SSAValue) -> bool {
265         self.live_in.get(val.idx().try_into().unwrap())
266     }
267 
is_live_out(&self, val: &SSAValue) -> bool268     fn is_live_out(&self, val: &SSAValue) -> bool {
269         self.live_out.get(val.idx().try_into().unwrap())
270     }
271 }
272 
273 pub struct SimpleLiveness {
274     ssa_block_ip: HashMap<SSAValue, (usize, usize)>,
275     blocks: Vec<SimpleBlockLiveness>,
276 }
277 
278 impl SimpleLiveness {
for_function(func: &Function) -> SimpleLiveness279     pub fn for_function(func: &Function) -> SimpleLiveness {
280         let mut l = SimpleLiveness {
281             ssa_block_ip: HashMap::new(),
282             blocks: Vec::new(),
283         };
284         let mut live_in = Vec::new();
285 
286         for (bi, b) in func.blocks.iter().enumerate() {
287             let mut bl = SimpleBlockLiveness::new();
288 
289             for (ip, instr) in b.instrs.iter().enumerate() {
290                 instr.for_each_ssa_use(|ssa| {
291                     bl.add_use(*ssa, ip);
292                 });
293                 instr.for_each_ssa_def(|ssa| {
294                     l.ssa_block_ip.insert(*ssa, (bi, ip));
295                     bl.add_def(*ssa);
296                 });
297             }
298 
299             l.blocks.push(bl);
300             live_in.push(BitSet::new());
301         }
302         assert!(l.blocks.len() == func.blocks.len());
303         assert!(live_in.len() == func.blocks.len());
304 
305         let num_ssa = usize::try_from(func.ssa_alloc.max_idx() + 1).unwrap();
306         let mut tmp = BitSet::new();
307         tmp.reserve(num_ssa);
308 
309         let mut to_do = true;
310         while to_do {
311             to_do = false;
312             for (b_idx, bl) in l.blocks.iter_mut().enumerate().rev() {
313                 // Compute live-out
314                 for sb_idx in func.blocks.succ_indices(b_idx) {
315                     to_do |= bl.live_out.union_with(&live_in[*sb_idx]);
316                 }
317 
318                 tmp.clear();
319                 tmp.set_words(0..num_ssa, |w| {
320                     (bl.live_out.get_word(w) | bl.uses.get_word(w))
321                         & !bl.defs.get_word(w)
322                 });
323 
324                 to_do |= live_in[b_idx].union_with(&tmp);
325             }
326         }
327 
328         for (bl, b_live_in) in l.blocks.iter_mut().zip(live_in.into_iter()) {
329             bl.live_in = b_live_in;
330         }
331 
332         l
333     }
334 }
335 
336 impl SimpleLiveness {
def_block_ip(&self, ssa: &SSAValue) -> (usize, usize)337     pub fn def_block_ip(&self, ssa: &SSAValue) -> (usize, usize) {
338         *self.ssa_block_ip.get(ssa).unwrap()
339     }
340 
interferes(&self, a: &SSAValue, b: &SSAValue) -> bool341     pub fn interferes(&self, a: &SSAValue, b: &SSAValue) -> bool {
342         let (ab, ai) = self.def_block_ip(a);
343         let (bb, bi) = self.def_block_ip(b);
344 
345         match ab.cmp(&bb).then(ai.cmp(&bi)) {
346             Ordering::Equal => true,
347             Ordering::Less => self.block_live(bb).is_live_after_ip(a, bi),
348             Ordering::Greater => self.block_live(ab).is_live_after_ip(b, ai),
349         }
350     }
351 }
352 
353 impl Liveness for SimpleLiveness {
354     type PerBlock = SimpleBlockLiveness;
355 
block_live(&self, idx: usize) -> &SimpleBlockLiveness356     fn block_live(&self, idx: usize) -> &SimpleBlockLiveness {
357         &self.blocks[idx]
358     }
359 }
360 
361 struct SSAUseDef {
362     defined: bool,
363     uses: Vec<usize>,
364 }
365 
366 impl SSAUseDef {
add_def(&mut self)367     fn add_def(&mut self) {
368         self.defined = true;
369     }
370 
add_in_block_use(&mut self, use_ip: usize)371     fn add_in_block_use(&mut self, use_ip: usize) {
372         self.uses.push(use_ip);
373     }
374 
add_successor_use( &mut self, num_block_instrs: usize, use_ip: usize, ) -> bool375     fn add_successor_use(
376         &mut self,
377         num_block_instrs: usize,
378         use_ip: usize,
379     ) -> bool {
380         // IPs are relative to the start of their block
381         let use_ip = num_block_instrs + use_ip;
382 
383         if let Some(last_use_ip) = self.uses.last_mut() {
384             if *last_use_ip < num_block_instrs {
385                 // We've never seen a successor use before
386                 self.uses.push(use_ip);
387                 true
388             } else if *last_use_ip > use_ip {
389                 // Otherwise, we want the minimum next use
390                 *last_use_ip = use_ip;
391                 true
392             } else {
393                 false
394             }
395         } else {
396             self.uses.push(use_ip);
397             true
398         }
399     }
400 }
401 
402 pub struct NextUseBlockLiveness {
403     num_instrs: usize,
404     ssa_map: HashMap<SSAValue, SSAUseDef>,
405 }
406 
407 impl NextUseBlockLiveness {
new(num_instrs: usize) -> Self408     fn new(num_instrs: usize) -> Self {
409         Self {
410             num_instrs: num_instrs,
411             ssa_map: HashMap::new(),
412         }
413     }
414 
entry_mut(&mut self, ssa: SSAValue) -> &mut SSAUseDef415     fn entry_mut(&mut self, ssa: SSAValue) -> &mut SSAUseDef {
416         self.ssa_map.entry(ssa).or_insert_with(|| SSAUseDef {
417             defined: false,
418             uses: Vec::new(),
419         })
420     }
421 
add_def(&mut self, ssa: SSAValue)422     fn add_def(&mut self, ssa: SSAValue) {
423         self.entry_mut(ssa).add_def();
424     }
425 
add_use(&mut self, ssa: SSAValue, ip: usize)426     fn add_use(&mut self, ssa: SSAValue, ip: usize) {
427         self.entry_mut(ssa).add_in_block_use(ip);
428     }
429 
430     /// Returns an iterator over all the values which are live-in to this block
iter_live_in(&self) -> impl Iterator<Item = &SSAValue>431     pub fn iter_live_in(&self) -> impl Iterator<Item = &SSAValue> {
432         self.ssa_map.iter().filter_map(|(ssa, entry)| {
433             if entry.defined || entry.uses.is_empty() {
434                 None
435             } else {
436                 Some(ssa)
437             }
438         })
439     }
440 
441     /// Returns the IP of the first use of @val
442     ///
443     /// The returned IP is relative to the start of this block.  If the next use
444     /// is in some successor block, the returned IP is relative to the start of
445     /// this block.  If @val is not used in this block and is not live-out, None
446     /// is returned.
first_use(&self, val: &SSAValue) -> Option<usize>447     pub fn first_use(&self, val: &SSAValue) -> Option<usize> {
448         if let Some(entry) = self.ssa_map.get(val) {
449             entry.uses.first().cloned()
450         } else {
451             None
452         }
453     }
454 
455     /// Returns the IP of the first use of @val which is greater than or equal
456     /// to @ip
457     ///
458     /// All IPs are relative to the start of the block.  If the next use is some
459     /// successor block, the returned IP is relative to the start of this block.
next_use_after_or_at_ip( &self, val: &SSAValue, ip: usize, ) -> Option<usize>460     pub fn next_use_after_or_at_ip(
461         &self,
462         val: &SSAValue,
463         ip: usize,
464     ) -> Option<usize> {
465         if let Some(entry) = self.ssa_map.get(val) {
466             let i = entry.uses.partition_point(|u| *u < ip);
467             if i < entry.uses.len() {
468                 Some(entry.uses[i])
469             } else {
470                 None
471             }
472         } else {
473             None
474         }
475     }
476 }
477 
478 impl BlockLiveness for NextUseBlockLiveness {
is_live_after_ip(&self, val: &SSAValue, ip: usize) -> bool479     fn is_live_after_ip(&self, val: &SSAValue, ip: usize) -> bool {
480         if let Some(entry) = self.ssa_map.get(val) {
481             if let Some(last_use_ip) = entry.uses.last() {
482                 *last_use_ip > ip
483             } else {
484                 false
485             }
486         } else {
487             false
488         }
489     }
490 
is_live_in(&self, val: &SSAValue) -> bool491     fn is_live_in(&self, val: &SSAValue) -> bool {
492         if let Some(entry) = self.ssa_map.get(val) {
493             !entry.defined && !entry.uses.is_empty()
494         } else {
495             false
496         }
497     }
498 
is_live_out(&self, val: &SSAValue) -> bool499     fn is_live_out(&self, val: &SSAValue) -> bool {
500         if let Some(entry) = self.ssa_map.get(val) {
501             if let Some(last_use_ip) = entry.uses.last() {
502                 *last_use_ip >= self.num_instrs
503             } else {
504                 false
505             }
506         } else {
507             false
508         }
509     }
510 }
511 
512 /// An implementation of Liveness that tracks next-use IPs for each SSAValue
513 ///
514 /// Along with the usual liveness information, this tracks next-use IPs for each
515 /// SSAValue.  Cross-block next-use IPs computed are as per the global next-use
516 /// distance algorithm described in "Register Spilling and Live-Range Splitting
517 /// for SSA-Form Programs" by Braun and Hack.
518 pub struct NextUseLiveness {
519     blocks: Vec<NextUseBlockLiveness>,
520 }
521 
522 impl NextUseLiveness {
for_function( func: &Function, files: &RegFileSet, ) -> NextUseLiveness523     pub fn for_function(
524         func: &Function,
525         files: &RegFileSet,
526     ) -> NextUseLiveness {
527         let mut blocks = Vec::new();
528         for (bi, b) in func.blocks.iter().enumerate() {
529             let mut bl = NextUseBlockLiveness::new(b.instrs.len());
530 
531             for (ip, instr) in b.instrs.iter().enumerate() {
532                 instr.for_each_ssa_use(|ssa| {
533                     if files.contains(ssa.file()) {
534                         bl.add_use(*ssa, ip);
535                     }
536                 });
537 
538                 instr.for_each_ssa_def(|ssa| {
539                     if files.contains(ssa.file()) {
540                         bl.add_def(*ssa);
541                     }
542                 });
543             }
544 
545             debug_assert!(bi == blocks.len());
546             blocks.push(RefCell::new(bl));
547         }
548 
549         let mut to_do = true;
550         while to_do {
551             to_do = false;
552             for (b_idx, b) in func.blocks.iter().enumerate().rev() {
553                 let num_instrs = b.instrs.len();
554                 let mut bl = blocks[b_idx].borrow_mut();
555 
556                 // Compute live-out
557                 for sb_idx in func.blocks.succ_indices(b_idx) {
558                     if *sb_idx == b_idx {
559                         for entry in bl.ssa_map.values_mut() {
560                             if entry.defined {
561                                 continue;
562                             }
563 
564                             let Some(first_use_ip) = entry.uses.first() else {
565                                 continue;
566                             };
567 
568                             to_do |= entry
569                                 .add_successor_use(num_instrs, *first_use_ip);
570                         }
571                     } else {
572                         let sbl = blocks[*sb_idx].borrow();
573                         for (ssa, entry) in sbl.ssa_map.iter() {
574                             if entry.defined {
575                                 continue;
576                             }
577 
578                             let Some(first_use_ip) = entry.uses.first() else {
579                                 continue;
580                             };
581 
582                             to_do |= bl
583                                 .entry_mut(*ssa)
584                                 .add_successor_use(num_instrs, *first_use_ip);
585                         }
586                     }
587                 }
588             }
589         }
590 
591         NextUseLiveness {
592             blocks: blocks.into_iter().map(|bl| bl.into_inner()).collect(),
593         }
594     }
595 }
596 
597 impl Liveness for NextUseLiveness {
598     type PerBlock = NextUseBlockLiveness;
599 
block_live(&self, idx: usize) -> &NextUseBlockLiveness600     fn block_live(&self, idx: usize) -> &NextUseBlockLiveness {
601         &self.blocks[idx]
602     }
603 }
604