1 // Copyright © 2022 Collabora, Ltd. 2 // SPDX-License-Identifier: MIT 3 4 use crate::ir::*; 5 6 use compiler::bitset::BitSet; 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 impl Extend<SSAValue> for LiveSet { extend<T: IntoIterator<Item = SSAValue>>(&mut self, iter: T)119 fn extend<T: IntoIterator<Item = SSAValue>>(&mut self, iter: T) { 120 for ssa in iter { 121 self.insert(ssa); 122 } 123 } 124 } 125 126 pub trait BlockLiveness { 127 /// Returns true if @val is still live after @ip is_live_after_ip(&self, val: &SSAValue, ip: usize) -> bool128 fn is_live_after_ip(&self, val: &SSAValue, ip: usize) -> bool; 129 130 /// Returns true if @val is live-in to this block is_live_in(&self, val: &SSAValue) -> bool131 fn is_live_in(&self, val: &SSAValue) -> bool; 132 133 /// Returns true if @val is live-out of this block is_live_out(&self, val: &SSAValue) -> bool134 fn is_live_out(&self, val: &SSAValue) -> bool; 135 get_instr_pressure(&self, ip: usize, instr: &Instr) -> PerRegFile<u8>136 fn get_instr_pressure(&self, ip: usize, instr: &Instr) -> PerRegFile<u8> { 137 let mut live = PerRegFile::new_with(|_| 0_i8); 138 139 // Vector destinations go live before sources are killed. 140 for dst in instr.dsts() { 141 if let Dst::SSA(vec) = dst { 142 if vec.comps() > 1 { 143 for ssa in vec.iter() { 144 live[ssa.file()] += 1; 145 } 146 } 147 } 148 } 149 150 // This is the first high point 151 let vec_dst_live = live; 152 153 // Use a hash set because sources may occur more than once 154 let mut killed = HashSet::new(); 155 instr.for_each_ssa_use(|ssa| { 156 if !self.is_live_after_ip(ssa, ip) { 157 killed.insert(*ssa); 158 } 159 }); 160 for ssa in killed.drain() { 161 live[ssa.file()] -= 1; 162 } 163 164 // Scalar destinations are allocated last 165 for dst in instr.dsts() { 166 if let Dst::SSA(vec) = dst { 167 if vec.comps() == 1 { 168 live[vec[0].file()] += 1; 169 } 170 } 171 } 172 173 PerRegFile::new_with(|file| { 174 max(0, max(vec_dst_live[file], live[file])) 175 .try_into() 176 .unwrap() 177 }) 178 } 179 } 180 181 pub trait Liveness { 182 type PerBlock: BlockLiveness; 183 block_live(&self, idx: usize) -> &Self::PerBlock184 fn block_live(&self, idx: usize) -> &Self::PerBlock; 185 calc_max_live(&self, f: &Function) -> PerRegFile<u32>186 fn calc_max_live(&self, f: &Function) -> PerRegFile<u32> { 187 let mut max_live: PerRegFile<u32> = Default::default(); 188 let mut block_live_out: Vec<LiveSet> = Vec::new(); 189 190 for (bb_idx, bb) in f.blocks.iter().enumerate() { 191 let bl = self.block_live(bb_idx); 192 193 let mut live = LiveSet::new(); 194 195 // Predecessors are added block order so we can just grab the first 196 // one (if any) and it will be a block we've processed. 197 if let Some(pred_idx) = f.blocks.pred_indices(bb_idx).first() { 198 let pred_out = &block_live_out[*pred_idx]; 199 for ssa in pred_out.iter() { 200 if bl.is_live_in(ssa) { 201 live.insert(*ssa); 202 } 203 } 204 } 205 206 for (ip, instr) in bb.instrs.iter().enumerate() { 207 let live_at_instr = live.insert_instr_top_down(ip, instr, bl); 208 max_live = PerRegFile::new_with(|file| { 209 max(max_live[file], live_at_instr[file]) 210 }); 211 212 if let Op::RegOut(reg_out) = &instr.op { 213 // This should be the last instruction. Everything should 214 // be dead once we've processed it. 215 debug_assert!(live.count(RegFile::GPR) == 0); 216 let num_gprs_out = reg_out.srcs.len().try_into().unwrap(); 217 max_live[RegFile::GPR] = 218 max(max_live[RegFile::GPR], num_gprs_out); 219 } 220 } 221 222 assert!(block_live_out.len() == bb_idx); 223 block_live_out.push(live); 224 } 225 226 max_live 227 } 228 } 229 230 pub struct SimpleBlockLiveness { 231 defs: BitSet, 232 uses: BitSet, 233 last_use: HashMap<u32, usize>, 234 live_in: BitSet, 235 live_out: BitSet, 236 } 237 238 impl SimpleBlockLiveness { new() -> Self239 fn new() -> Self { 240 Self { 241 defs: BitSet::new(), 242 uses: BitSet::new(), 243 last_use: HashMap::new(), 244 live_in: BitSet::new(), 245 live_out: BitSet::new(), 246 } 247 } 248 add_def(&mut self, ssa: SSAValue)249 fn add_def(&mut self, ssa: SSAValue) { 250 self.defs.insert(ssa.idx().try_into().unwrap()); 251 } 252 add_use(&mut self, ssa: SSAValue, ip: usize)253 fn add_use(&mut self, ssa: SSAValue, ip: usize) { 254 self.uses.insert(ssa.idx().try_into().unwrap()); 255 self.last_use.insert(ssa.idx(), ip); 256 } 257 } 258 259 impl BlockLiveness for SimpleBlockLiveness { is_live_after_ip(&self, val: &SSAValue, ip: usize) -> bool260 fn is_live_after_ip(&self, val: &SSAValue, ip: usize) -> bool { 261 if self.live_out.get(val.idx().try_into().unwrap()) { 262 true 263 } else { 264 if let Some(last_use_ip) = self.last_use.get(&val.idx()) { 265 *last_use_ip > ip 266 } else { 267 false 268 } 269 } 270 } 271 is_live_in(&self, val: &SSAValue) -> bool272 fn is_live_in(&self, val: &SSAValue) -> bool { 273 self.live_in.get(val.idx().try_into().unwrap()) 274 } 275 is_live_out(&self, val: &SSAValue) -> bool276 fn is_live_out(&self, val: &SSAValue) -> bool { 277 self.live_out.get(val.idx().try_into().unwrap()) 278 } 279 } 280 281 pub struct SimpleLiveness { 282 ssa_block_ip: HashMap<SSAValue, (usize, usize)>, 283 blocks: Vec<SimpleBlockLiveness>, 284 } 285 286 impl SimpleLiveness { for_function(func: &Function) -> SimpleLiveness287 pub fn for_function(func: &Function) -> SimpleLiveness { 288 let mut l = SimpleLiveness { 289 ssa_block_ip: HashMap::new(), 290 blocks: Vec::new(), 291 }; 292 let mut live_in = Vec::new(); 293 294 for (bi, b) in func.blocks.iter().enumerate() { 295 let mut bl = SimpleBlockLiveness::new(); 296 297 for (ip, instr) in b.instrs.iter().enumerate() { 298 instr.for_each_ssa_use(|ssa| { 299 bl.add_use(*ssa, ip); 300 }); 301 instr.for_each_ssa_def(|ssa| { 302 l.ssa_block_ip.insert(*ssa, (bi, ip)); 303 bl.add_def(*ssa); 304 }); 305 } 306 307 l.blocks.push(bl); 308 live_in.push(BitSet::new()); 309 } 310 assert!(l.blocks.len() == func.blocks.len()); 311 assert!(live_in.len() == func.blocks.len()); 312 313 let num_ssa = usize::try_from(func.ssa_alloc.max_idx() + 1).unwrap(); 314 let mut tmp = BitSet::new(); 315 tmp.reserve(num_ssa); 316 317 let mut to_do = true; 318 while to_do { 319 to_do = false; 320 for (b_idx, bl) in l.blocks.iter_mut().enumerate().rev() { 321 // Compute live-out 322 for sb_idx in func.blocks.succ_indices(b_idx) { 323 to_do |= bl.live_out.union_with(&live_in[*sb_idx]); 324 } 325 326 tmp.clear(); 327 tmp.set_words(0..num_ssa, |w| { 328 (bl.live_out.get_word(w) | bl.uses.get_word(w)) 329 & !bl.defs.get_word(w) 330 }); 331 332 to_do |= live_in[b_idx].union_with(&tmp); 333 } 334 } 335 336 for (bl, b_live_in) in l.blocks.iter_mut().zip(live_in.into_iter()) { 337 bl.live_in = b_live_in; 338 } 339 340 l 341 } 342 } 343 344 impl SimpleLiveness { def_block_ip(&self, ssa: &SSAValue) -> (usize, usize)345 pub fn def_block_ip(&self, ssa: &SSAValue) -> (usize, usize) { 346 *self.ssa_block_ip.get(ssa).unwrap() 347 } 348 interferes(&self, a: &SSAValue, b: &SSAValue) -> bool349 pub fn interferes(&self, a: &SSAValue, b: &SSAValue) -> bool { 350 let (ab, ai) = self.def_block_ip(a); 351 let (bb, bi) = self.def_block_ip(b); 352 353 match ab.cmp(&bb).then(ai.cmp(&bi)) { 354 Ordering::Equal => true, 355 Ordering::Less => self.block_live(bb).is_live_after_ip(a, bi), 356 Ordering::Greater => self.block_live(ab).is_live_after_ip(b, ai), 357 } 358 } 359 } 360 361 impl Liveness for SimpleLiveness { 362 type PerBlock = SimpleBlockLiveness; 363 block_live(&self, idx: usize) -> &SimpleBlockLiveness364 fn block_live(&self, idx: usize) -> &SimpleBlockLiveness { 365 &self.blocks[idx] 366 } 367 } 368 369 struct SSAUseDef { 370 defined: bool, 371 uses: Vec<usize>, 372 } 373 374 impl SSAUseDef { add_def(&mut self)375 fn add_def(&mut self) { 376 self.defined = true; 377 } 378 add_in_block_use(&mut self, use_ip: usize)379 fn add_in_block_use(&mut self, use_ip: usize) { 380 self.uses.push(use_ip); 381 } 382 add_successor_use( &mut self, num_block_instrs: usize, use_ip: usize, ) -> bool383 fn add_successor_use( 384 &mut self, 385 num_block_instrs: usize, 386 use_ip: usize, 387 ) -> bool { 388 // IPs are relative to the start of their block 389 let use_ip = num_block_instrs + use_ip; 390 391 if let Some(last_use_ip) = self.uses.last_mut() { 392 if *last_use_ip < num_block_instrs { 393 // We've never seen a successor use before 394 self.uses.push(use_ip); 395 true 396 } else if *last_use_ip > use_ip { 397 // Otherwise, we want the minimum next use 398 *last_use_ip = use_ip; 399 true 400 } else { 401 false 402 } 403 } else { 404 self.uses.push(use_ip); 405 true 406 } 407 } 408 } 409 410 pub struct NextUseBlockLiveness { 411 num_instrs: usize, 412 ssa_map: HashMap<SSAValue, SSAUseDef>, 413 } 414 415 impl NextUseBlockLiveness { new(num_instrs: usize) -> Self416 fn new(num_instrs: usize) -> Self { 417 Self { 418 num_instrs: num_instrs, 419 ssa_map: HashMap::new(), 420 } 421 } 422 entry_mut(&mut self, ssa: SSAValue) -> &mut SSAUseDef423 fn entry_mut(&mut self, ssa: SSAValue) -> &mut SSAUseDef { 424 self.ssa_map.entry(ssa).or_insert_with(|| SSAUseDef { 425 defined: false, 426 uses: Vec::new(), 427 }) 428 } 429 add_def(&mut self, ssa: SSAValue)430 fn add_def(&mut self, ssa: SSAValue) { 431 self.entry_mut(ssa).add_def(); 432 } 433 add_use(&mut self, ssa: SSAValue, ip: usize)434 fn add_use(&mut self, ssa: SSAValue, ip: usize) { 435 self.entry_mut(ssa).add_in_block_use(ip); 436 } 437 438 /// Returns an iterator over all the values which are live-in to this block iter_live_in(&self) -> impl Iterator<Item = &SSAValue>439 pub fn iter_live_in(&self) -> impl Iterator<Item = &SSAValue> { 440 self.ssa_map.iter().filter_map(|(ssa, entry)| { 441 if entry.defined || entry.uses.is_empty() { 442 None 443 } else { 444 Some(ssa) 445 } 446 }) 447 } 448 449 /// Returns the IP of the first use of @val 450 /// 451 /// The returned IP is relative to the start of this block. If the next use 452 /// is in some successor block, the returned IP is relative to the start of 453 /// this block. If @val is not used in this block and is not live-out, None 454 /// is returned. first_use(&self, val: &SSAValue) -> Option<usize>455 pub fn first_use(&self, val: &SSAValue) -> Option<usize> { 456 if let Some(entry) = self.ssa_map.get(val) { 457 entry.uses.first().cloned() 458 } else { 459 None 460 } 461 } 462 463 /// Returns the IP of the first use of @val which is greater than or equal 464 /// to @ip 465 /// 466 /// All IPs are relative to the start of the block. If the next use is some 467 /// 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>468 pub fn next_use_after_or_at_ip( 469 &self, 470 val: &SSAValue, 471 ip: usize, 472 ) -> Option<usize> { 473 if let Some(entry) = self.ssa_map.get(val) { 474 let i = entry.uses.partition_point(|u| *u < ip); 475 if i < entry.uses.len() { 476 Some(entry.uses[i]) 477 } else { 478 None 479 } 480 } else { 481 None 482 } 483 } 484 } 485 486 impl BlockLiveness for NextUseBlockLiveness { is_live_after_ip(&self, val: &SSAValue, ip: usize) -> bool487 fn is_live_after_ip(&self, val: &SSAValue, ip: usize) -> bool { 488 if let Some(entry) = self.ssa_map.get(val) { 489 if let Some(last_use_ip) = entry.uses.last() { 490 *last_use_ip > ip 491 } else { 492 false 493 } 494 } else { 495 false 496 } 497 } 498 is_live_in(&self, val: &SSAValue) -> bool499 fn is_live_in(&self, val: &SSAValue) -> bool { 500 if let Some(entry) = self.ssa_map.get(val) { 501 !entry.defined && !entry.uses.is_empty() 502 } else { 503 false 504 } 505 } 506 is_live_out(&self, val: &SSAValue) -> bool507 fn is_live_out(&self, val: &SSAValue) -> bool { 508 if let Some(entry) = self.ssa_map.get(val) { 509 if let Some(last_use_ip) = entry.uses.last() { 510 *last_use_ip >= self.num_instrs 511 } else { 512 false 513 } 514 } else { 515 false 516 } 517 } 518 } 519 520 /// An implementation of Liveness that tracks next-use IPs for each SSAValue 521 /// 522 /// Along with the usual liveness information, this tracks next-use IPs for each 523 /// SSAValue. Cross-block next-use IPs computed are as per the global next-use 524 /// distance algorithm described in "Register Spilling and Live-Range Splitting 525 /// for SSA-Form Programs" by Braun and Hack. 526 pub struct NextUseLiveness { 527 blocks: Vec<NextUseBlockLiveness>, 528 } 529 530 impl NextUseLiveness { for_function( func: &Function, files: &RegFileSet, ) -> NextUseLiveness531 pub fn for_function( 532 func: &Function, 533 files: &RegFileSet, 534 ) -> NextUseLiveness { 535 let mut blocks = Vec::new(); 536 for (bi, b) in func.blocks.iter().enumerate() { 537 let mut bl = NextUseBlockLiveness::new(b.instrs.len()); 538 539 for (ip, instr) in b.instrs.iter().enumerate() { 540 instr.for_each_ssa_use(|ssa| { 541 if files.contains(ssa.file()) { 542 bl.add_use(*ssa, ip); 543 } 544 }); 545 546 instr.for_each_ssa_def(|ssa| { 547 if files.contains(ssa.file()) { 548 bl.add_def(*ssa); 549 } 550 }); 551 } 552 553 debug_assert!(bi == blocks.len()); 554 blocks.push(RefCell::new(bl)); 555 } 556 557 let mut to_do = true; 558 while to_do { 559 to_do = false; 560 for (b_idx, b) in func.blocks.iter().enumerate().rev() { 561 let num_instrs = b.instrs.len(); 562 let mut bl = blocks[b_idx].borrow_mut(); 563 564 // Compute live-out 565 for sb_idx in func.blocks.succ_indices(b_idx) { 566 if *sb_idx == b_idx { 567 for entry in bl.ssa_map.values_mut() { 568 if entry.defined { 569 continue; 570 } 571 572 let Some(first_use_ip) = entry.uses.first() else { 573 continue; 574 }; 575 576 to_do |= entry 577 .add_successor_use(num_instrs, *first_use_ip); 578 } 579 } else { 580 let sbl = blocks[*sb_idx].borrow(); 581 for (ssa, entry) in sbl.ssa_map.iter() { 582 if entry.defined { 583 continue; 584 } 585 586 let Some(first_use_ip) = entry.uses.first() else { 587 continue; 588 }; 589 590 to_do |= bl 591 .entry_mut(*ssa) 592 .add_successor_use(num_instrs, *first_use_ip); 593 } 594 } 595 } 596 } 597 } 598 599 NextUseLiveness { 600 blocks: blocks.into_iter().map(|bl| bl.into_inner()).collect(), 601 } 602 } 603 } 604 605 impl Liveness for NextUseLiveness { 606 type PerBlock = NextUseBlockLiveness; 607 block_live(&self, idx: usize) -> &NextUseBlockLiveness608 fn block_live(&self, idx: usize) -> &NextUseBlockLiveness { 609 &self.blocks[idx] 610 } 611 } 612