1use std::cmp::Reverse;
79use std::collections::BinaryHeap;
80
81use rucc_base::hash::{Map, Set};
82use rucc_mir::{Func, Inst, Reg};
83use rucc_target::{PhysReg, RegClass};
84
85use crate::assign::{self, Assignment, Blocks, Env, FEW, Pieces, Place, Reuse, Want};
86use crate::live::{Area, Live, Range};
87use crate::order::Order;
88use crate::pressure::Pressure;
89use crate::spill;
90
91pub const BUDGET: u64 = 50_000_000;
94
95pub const ROUNDS: u32 = 4;
97
98#[derive(Debug, Clone, Copy)]
100struct Value<'a> {
101 reg: Reg,
102 class: RegClass,
103 area: Area<'a>,
104 range: Range,
105 weight: u128,
106 size: u32,
107}
108
109#[must_use]
115pub fn assign(func: &Func, order: &Order, live: &Live, env: &Env) -> Assignment {
116 let linear = assign::assign(func, order, live, env);
117 let mut best = cost(func, order, &linear);
118 let mut kept = linear;
119 let pressure = Pressure::of(func, order, live, env);
120 let spilled = spill::choose(func, live, &pressure);
121 let tries: &[&[Reg]] = if spilled.is_empty() { &[&[]] } else { &[&[], &spilled] };
123 for &early in tries {
124 let Some(ours) = placed(func, order, live, env, BUDGET, early) else { break };
125 let spent = cost(func, order, &ours);
126 if spent <= best {
127 best = spent;
128 kept = ours;
129 }
130 }
131 kept
132}
133
134#[must_use]
148pub fn cost(func: &Func, order: &Order, assignment: &Assignment) -> u128 {
149 let costs = costs(func);
150 let mut total = 0;
151 for (reg, place) in assignment.placed() {
152 if !matches!(place, Place::Reg(_)) {
153 total += costs[index(reg)];
154 }
155 }
156 let mut weights = Map::default();
157 for block in func.blocks() {
158 let weight = u128::from(func[block].weight.raw().max(1));
159 for inst in func.insts(block) {
160 weights.insert(inst, weight);
161 }
162 for call in &func[block].succs {
163 for (&arg, param) in call.args.iter().zip(&func[call.block].params) {
164 if assignment.place(arg) != assignment.place(param.reg) {
165 total += weight;
166 }
167 }
168 }
169 }
170 let commuted: Set<Inst> = assignment.commuted().iter().copied().collect();
171 for (number, reuse) in assign::reuses(func, order).iter().enumerate() {
172 let Some(reuse) = reuse else { continue };
173 let answer = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
174 let tied = if commuted.contains(&reuse.inst) { reuse.second } else { Some(reuse.source) };
175 let Some(at) = assignment.place(answer) else { continue };
176 if tied.and_then(|tied| assignment.place(tied)) != Some(at) {
177 total += weights.get(&reuse.inst).copied().unwrap_or(1);
178 }
179 }
180 total
181}
182
183#[must_use]
190pub fn within(
191 func: &Func,
192 order: &Order,
193 live: &Live,
194 env: &Env,
195 budget: u64,
196) -> Option<Assignment> {
197 placed(func, order, live, env, budget, &[])
198}
199
200fn placed(
201 func: &Func,
202 order: &Order,
203 live: &Live,
204 env: &Env,
205 budget: u64,
206 early: &[Reg],
207) -> Option<Assignment> {
208 let blocked = assign::blocked(func, order);
209 let forced = assign::forced(func);
210 let reuses = assign::reuses(func, order);
211 let hints = assign::hints(func);
212 let passed = assign::passed(func);
213 let received = received(func);
214 let reused = reused(&reuses);
215 let seconds = seconds(&reuses);
216 let costs = costs(func);
217
218 let count = func.vregs();
219 let mut values: Vec<Option<Value<'_>>> = vec![None; count];
220 let mut queue = BinaryHeap::new();
221 for (number, reuse) in reuses.iter().enumerate() {
222 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
223 let (Some(mut area), Some(class)) = (live.area(reg), func.class_of(reg)) else {
224 continue;
225 };
226 if let Some(reuse) = reuse {
227 area = area.with(reuse.at);
228 }
229 let size = size(area);
230 let weight = costs[number] * 1024 / u128::from(size + 8);
231 values[number] = Some(Value { reg, class, area, range: area.hull(), weight, size });
232 queue.push((size, Reverse(number)));
233 }
234
235 let mut state = State {
236 live,
237 blocked: &blocked,
238 reuses: &reuses,
239 values: &values,
240 held: Vec::new(),
241 pieces: Vec::new(),
242 at: vec![None; count],
243 commuted: vec![None; count],
244 work: 0,
245 budget,
246 };
247 let mut assignment = Assignment::empty(count);
248 let mut lost = vec![0u32; count];
249 while let Some((_, Reverse(number))) = queue.pop() {
250 let Some(value) = values[number] else { continue };
251 if forced.contains(&value.reg) || early.contains(&value.reg) {
252 assignment.spill(value.reg, value.class);
253 continue;
254 }
255 assert!(
256 !env.order(value.class).is_empty(),
257 "a value in class {}, which the target hands out no registers from",
258 value.class.number()
259 );
260 let chosen = state
266 .coalesced(value, &reused[number])
267 .or_else(|| state.hinted(value, &hints[number], Want::Allowed))
268 .or_else(|| {
269 let partners = passed[number].iter().chain(&received[number]);
270 let partners: Vec<PhysReg> =
271 partners.filter_map(|&other| state.reg_of(other)).collect();
272 state.hinted(value, &partners, Want::Allowed)
273 })
274 .or_else(|| state.swapped(value, &seconds[number]))
275 .or_else(|| {
276 let mut ties = reused[number].clone();
277 let source = reuses[number].and_then(|reuse| reuse.source.number());
278 ties.extend(source.and_then(|source| usize::try_from(source).ok()));
279 ties.retain(|&tie| state.at[tie].is_none() && values[tie].is_some());
280 let tied = ties.iter().flat_map(|&tie| hints[tie].iter());
281 let wanted: Vec<PhysReg> = tied.chain(env.order(value.class)).copied().collect();
282 state.together(value, &ties, &wanted)
283 })
284 .or_else(|| state.hinted(value, env.order(value.class), Want::Allowed));
285 if state.work > state.budget {
286 return None;
287 }
288 if let Some(at) = chosen {
289 state.take(value, at);
290 continue;
291 }
292 match state.cheapest(value, env.order(value.class)) {
293 Some(at) => {
294 for other in state.evict(value, at) {
295 lost[other] += 1;
296 let Some(evicted) = values[other] else { continue };
297 if lost[other] > ROUNDS {
298 assignment.spill(evicted.reg, evicted.class);
299 } else {
300 queue.push((evicted.size, Reverse(other)));
301 }
302 }
303 state.take(value, at);
304 }
305 None => assignment.spill(value.reg, value.class),
306 }
307 if state.work > state.budget {
308 return None;
309 }
310 }
311 state.settle(&reused, &passed, &received);
312 if state.work > state.budget {
313 return None;
314 }
315 for (number, at) in state.at.iter().enumerate() {
316 let Some(at) = *at else { continue };
317 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
318 assignment.put(reg, Place::Reg(at));
319 }
320 for inst in state.commuted.iter().flatten() {
321 assignment.commute(*inst);
322 }
323 Some(assignment)
324}
325
326type Held = (usize, Range);
328
329struct State<'a, 'v> {
331 live: &'a Live,
332 blocked: &'a Blocks,
333 reuses: &'a [Option<Reuse>],
334 values: &'v [Option<Value<'a>>],
335 held: Vec<((RegClass, PhysReg), Vec<Held>)>,
339 pieces: Vec<Pieces>,
342 at: Vec<Option<PhysReg>>,
344 commuted: Vec<Option<Inst>>,
347 work: u64,
348 budget: u64,
349}
350
351impl<'a> State<'a, '_> {
352 fn reg_of(&self, reg: Reg) -> Option<PhysReg> {
353 self.at.get(usize::try_from(reg.number()?).ok()?).copied().flatten()
354 }
355
356 fn slot(&mut self, class: RegClass, at: PhysReg) -> usize {
357 let found = self.held.iter().position(|(key, _)| *key == (class, at));
358 found.unwrap_or_else(|| {
359 self.held.push(((class, at), Vec::new()));
360 self.pieces.push(Pieces::default());
361 self.held.len() - 1
362 })
363 }
364
365 fn remove(&mut self, index: usize, gone: &[usize]) {
367 self.held[index].1.retain(|(other, _)| !gone.contains(other));
368 let pieces = &mut self.pieces[index];
369 if pieces.kept {
370 for value in gone.iter().filter_map(|&other| self.values[other]) {
371 for piece in value.area.pieces() {
372 pieces.remove(piece, value.reg);
373 }
374 }
375 }
376 }
377
378 fn clashes(&mut self, value: Value<'_>, at: PhysReg) -> Vec<usize> {
381 let Some(found) = self.held.iter().position(|(key, _)| *key == (value.class, at)) else {
382 return Vec::new();
383 };
384 let held = &self.held[found].1;
385 self.work += held.len() as u64;
388 if held.len() > FEW {
389 let pieces = &mut self.pieces[found];
390 if !pieces.kept {
391 pieces.kept = true;
392 for other in held.iter().filter_map(|&(other, _)| self.values[other]) {
393 for piece in other.area.pieces() {
394 pieces.insert(piece, other.reg);
395 }
396 }
397 }
398 if let Some(owners) = pieces.owners(value.area) {
399 let clash = owners.into_iter().filter(|&other| !self.shares(value.reg, other));
400 return clash.map(index).collect();
401 }
402 }
403 let mut clashes = Vec::new();
404 for &(other, range) in held {
405 if !range.overlaps(value.range) {
406 continue;
407 }
408 let Some(held) = self.values[other] else { continue };
409 if !held.area.overlaps(value.area) {
410 continue;
411 }
412 if !self.shares(value.reg, held.reg) {
413 clashes.push(other);
414 }
415 }
416 clashes
417 }
418
419 fn shares(&self, one: Reg, two: Reg) -> bool {
422 let reads = |answer: Reg, source: Reg| {
423 let Some(number) = answer.number().and_then(|n| usize::try_from(n).ok()) else {
424 return false;
425 };
426 let Some(reuse) = self.reuses[number] else { return false };
427 match self.commuted[number] {
428 Some(_) => reuse.second == Some(source),
429 None => reuse.source == source,
430 }
431 };
432 (reads(one, two) || reads(two, one)) && assign::apart(self.live, one, two)
433 }
434
435 fn free(&mut self, value: Value<'_>, at: PhysReg, want: Want) -> bool {
436 !self.blocked.insists(value.reg, value.class, value.area, value.range, at, want)
437 && self.clashes(value, at).is_empty()
438 }
439
440 fn hinted(&mut self, value: Value<'_>, wanted: &[PhysReg], want: Want) -> Option<PhysReg> {
442 wanted.iter().copied().find(|&at| self.work <= self.budget && self.free(value, at, want))
443 }
444
445 fn together(&mut self, value: Value<'_>, ties: &[usize], order: &[PhysReg]) -> Option<PhysReg> {
451 if ties.is_empty() {
452 return None;
453 }
454 for &at in order {
455 if self.work > self.budget {
456 return None;
457 }
458 if !self.free(value, at, Want::Clear) {
459 continue;
460 }
461 let values = self.values;
462 let mut tied = ties.iter().filter_map(|&tie| values[tie]);
463 if tied.all(|other| self.free(other, at, Want::Allowed)) {
464 return Some(at);
465 }
466 }
467 None
468 }
469
470 fn coalesced(&mut self, value: Value<'_>, answers: &[usize]) -> Option<PhysReg> {
476 let number = index(value.reg);
477 if let Some(reuse) = self.reuses[number] {
478 if let Some(at) = self.reg_of(reuse.source) {
479 if self.free(value, at, Want::Allowed) {
480 return Some(at);
481 }
482 }
483 if let Some(second) = reuse.second {
484 if let Some(at) = self.reg_of(second) {
485 self.commuted[number] = Some(reuse.inst);
486 if self.free(value, at, Want::Allowed) {
487 return Some(at);
488 }
489 self.commuted[number] = None;
490 }
491 }
492 }
493 for &answer in answers {
494 let Some(at) = self.at[answer] else { continue };
495 if self.commuted[answer].is_none() && self.free(value, at, Want::Allowed) {
496 return Some(at);
497 }
498 }
499 None
500 }
501
502 fn swapped(&mut self, value: Value<'_>, seconds: &[usize]) -> Option<PhysReg> {
508 if self.reuses[index(value.reg)].is_some() {
512 return None;
513 }
514 for &answer in seconds {
515 let (Some(at), Some(reuse)) = (self.at[answer], self.reuses[answer]) else { continue };
516 let Some(written) = self.values[answer] else { continue };
517 if self.commuted[answer].is_some()
521 || self.reg_of(reuse.source) == Some(at)
522 || assign::apart(self.live, written.reg, reuse.source)
523 {
524 continue;
525 }
526 self.commuted[answer] = Some(reuse.inst);
527 if self.free(value, at, Want::Allowed) {
528 return Some(at);
529 }
530 self.commuted[answer] = None;
531 }
532 None
533 }
534
535 fn cheapest(&mut self, value: Value<'_>, order: &[PhysReg]) -> Option<PhysReg> {
538 let mut best: Option<(u128, u128, PhysReg)> = None;
539 for &at in order {
540 if self.blocked.insists(
541 value.reg,
542 value.class,
543 value.area,
544 value.range,
545 at,
546 Want::Allowed,
547 ) {
548 continue;
549 }
550 let clashes = self.clashes(value, at);
551 let weights = clashes.iter().filter_map(|&other| self.values[other]).map(|v| v.weight);
552 let (heaviest, total) = weights.fold((0, 0), |(most, sum), w| (most.max(w), sum + w));
553 if heaviest >= value.weight {
554 continue;
555 }
556 if best.is_none_or(|(most, sum, _)| (heaviest, total) < (most, sum)) {
557 best = Some((heaviest, total, at));
558 }
559 }
560 best.map(|(_, _, at)| at)
561 }
562
563 fn evict(&mut self, value: Value<'_>, at: PhysReg) -> Vec<usize> {
565 let clashes = self.clashes(value, at);
566 let index = self.slot(value.class, at);
567 self.remove(index, &clashes);
568 for &other in &clashes {
569 self.at[other] = None;
570 self.commuted[other] = None;
571 }
572 clashes
573 }
574
575 fn settle(&mut self, reused: &[Vec<usize>], passed: &[Vec<Reg>], received: &[Vec<Reg>]) {
584 let mut moved = true;
585 while moved && self.work <= self.budget {
586 moved = false;
587 for number in 0..self.at.len() {
588 let (Some(value), Some(now)) = (self.values[number], self.at[number]) else {
589 continue;
590 };
591 let reuse = self.reuses[number];
592 let source = reuse.and_then(|reuse| self.reg_of(reuse.source));
593 let second =
594 reuse.and_then(|reuse| reuse.second).and_then(|second| self.reg_of(second));
595 let mut wanted: Vec<PhysReg> = source.into_iter().chain(second).collect();
596 for &answer in &reused[number] {
597 if self.commuted[answer].is_none() {
598 wanted.extend(self.at[answer]);
599 }
600 }
601 let partners = passed[number].iter().chain(&received[number]);
602 wanted.extend(partners.filter_map(|&other| self.reg_of(other)));
603 let met = |at: PhysReg| wanted.iter().filter(|&®| reg == at).count();
604 let here = met(now);
605 let mut better: Vec<(usize, PhysReg)> = Vec::new();
606 for &at in &wanted {
607 let count = met(at);
608 if count > here && !better.contains(&(count, at)) {
609 better.push((count, at));
610 }
611 }
612 if better.is_empty() {
613 continue;
614 }
615 better.sort_by_key(|&(count, _)| Reverse(count));
616 let index = self.slot(value.class, now);
617 self.remove(index, &[number]);
618 self.at[number] = None;
619 let was = self.commuted[number];
620 let mut to = now;
621 for (_, at) in better {
622 self.commuted[number] = match reuse {
623 Some(reuse) if second == Some(at) && source != Some(at) => Some(reuse.inst),
624 _ => None,
625 };
626 if self.free(value, at, Want::Allowed) {
627 to = at;
628 moved = true;
629 break;
630 }
631 }
632 if to == now {
633 self.commuted[number] = was;
634 }
635 self.take(value, to);
636 }
637 }
638 }
639
640 fn take(&mut self, value: Value<'_>, at: PhysReg) {
641 let number = index(value.reg);
642 self.at[number] = Some(at);
643 let index = self.slot(value.class, at);
644 self.held[index].1.push((number, value.range));
645 let pieces = &mut self.pieces[index];
646 if pieces.kept {
647 for piece in value.area.pieces() {
648 pieces.insert(piece, value.reg);
649 }
650 }
651 }
652}
653
654pub(crate) fn size(area: Area<'_>) -> u32 {
656 area.pieces().map(|piece| piece.end - piece.start + 1).sum()
657}
658
659pub(crate) fn costs(func: &Func) -> Vec<u128> {
664 let mut costs = vec![0u128; func.vregs()];
665 let mut add = |reg: Reg, weight: u128| {
666 let number = reg.number().and_then(|number| usize::try_from(number).ok());
667 if let Some(cost) = number.and_then(|number| costs.get_mut(number)) {
668 *cost += weight;
669 }
670 };
671 for block in func.blocks() {
672 let weight = u128::from(func[block].weight.raw().max(1));
673 for param in &func[block].params {
674 add(param.reg, weight);
675 }
676 for inst in func.insts(block) {
677 for operand in &func[func[inst].operands] {
678 add(operand.reg, weight);
679 }
680 }
681 for call in &func[block].succs {
682 for &arg in &call.args {
683 add(arg, weight);
684 }
685 }
686 }
687 costs
688}
689
690fn received(func: &Func) -> Vec<Vec<Reg>> {
692 let mut received = vec![Vec::new(); func.vregs()];
693 for block in func.blocks() {
694 for call in &func[block].succs {
695 for (&arg, param) in call.args.iter().zip(&func[call.block].params) {
696 let number = param.reg.number().and_then(|number| usize::try_from(number).ok());
697 let Some(from) = number.and_then(|number| received.get_mut(number)) else {
698 continue;
699 };
700 if !from.contains(&arg) {
701 from.push(arg);
702 }
703 }
704 }
705 }
706 received
707}
708
709fn reused(reuses: &[Option<Reuse>]) -> Vec<Vec<usize>> {
711 let mut reused = vec![Vec::new(); reuses.len()];
712 for (answer, reuse) in reuses.iter().enumerate() {
713 let Some(reuse) = reuse else { continue };
714 let number = reuse.source.number().and_then(|number| usize::try_from(number).ok());
715 if let Some(answers) = number.and_then(|number| reused.get_mut(number)) {
716 answers.push(answer);
717 }
718 }
719 reused
720}
721
722fn seconds(reuses: &[Option<Reuse>]) -> Vec<Vec<usize>> {
725 let mut seconds = vec![Vec::new(); reuses.len()];
726 for (answer, reuse) in reuses.iter().enumerate() {
727 let Some(second) = reuse.and_then(|reuse| reuse.second) else { continue };
728 let number = second.number().and_then(|number| usize::try_from(number).ok());
729 if let Some(answers) = number.and_then(|number| seconds.get_mut(number)) {
730 answers.push(answer);
731 }
732 }
733 seconds
734}
735
736fn index(reg: Reg) -> usize {
737 usize::try_from(reg.number().expect("a virtual register")).expect("a register number")
738}
739
740#[cfg(test)]
741mod tests {
742 use rucc_base::Interner;
743 use rucc_mir::{BlockCall, Constraint, Flags, Opcode, Operand, Param};
744 use rucc_target::x86_64::{GPR, REGS, SYSV};
745
746 use super::*;
747 use crate::check;
748
749 fn env() -> Env {
750 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
751 Env::new().with(GPR, order, scratch)
752 }
753
754 fn narrow(count: usize) -> Env {
755 Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 1])
756 }
757
758 fn named(place: Option<Place>) -> String {
759 match place {
760 Some(Place::Reg(reg)) => REGS.name(GPR, reg).expect("a register").to_string(),
761 Some(Place::Slot(_)) => "slot".to_string(),
762 None => "nowhere".to_string(),
763 }
764 }
765
766 fn places(func: &mut Func, env: &Env) -> Vec<String> {
768 let order = Order::of(func);
769 let live = Live::of(func, &order);
770 let assignment = within(func, &order, &live, env, BUDGET).expect("inside the budget");
771 for &inst in assignment.commuted() {
774 let list = func[inst].operands;
775 func[list].swap(1, 2);
776 }
777 let problems = check::check(func, &order, &live, &assignment);
778 assert!(problems.is_empty(), "{}", check::report(&problems));
779 (0..func.vregs())
780 .map(|number| {
781 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
782 named(assignment.place(reg))
783 })
784 .collect()
785 }
786
787 fn linear(func: &Func, env: &Env) -> Vec<String> {
788 let order = Order::of(func);
789 let live = Live::of(func, &order);
790 let assignment = assign::assign(func, &order, &live, env);
791 (0..func.vregs())
792 .map(|number| {
793 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
794 named(assignment.place(reg))
795 })
796 .collect()
797 }
798
799 #[test]
800 fn a_value_the_spill_phase_picked_goes_to_the_stack_and_the_rest_fit() {
801 let mut names = Interner::new();
802 let mut func = Func::new(names.intern("f"));
803 let opcode = Opcode::new(names.intern("x64.nop"));
804 let block = func.create_block();
805 let busy = func.new_vreg(GPR);
806 let once = func.new_vreg(GPR);
807 let other = func.new_vreg(GPR);
808 func.build(block, opcode).def(busy, GPR).finish();
809 func.build(block, opcode).def(once, GPR).finish();
810 func.build(block, opcode).def(other, GPR).finish();
811 for _ in 0..3 {
812 func.build(block, opcode).uses(busy, GPR).uses(other, GPR).finish();
813 }
814 func.build(block, opcode).uses(once, GPR).uses(busy, GPR).uses(other, GPR).finish();
815
816 let env = narrow(2);
817 let order = Order::of(&func);
818 let live = Live::of(&func, &order);
819 let pressure = Pressure::of(&func, &order, &live, &env);
820 let early = spill::choose(&func, &live, &pressure);
821 assert_eq!(early, [once]);
822 let assignment = placed(&func, &order, &live, &env, BUDGET, &early).expect("in budget");
823 let problems = check::check(&func, &order, &live, &assignment);
824 assert!(problems.is_empty(), "{}", check::report(&problems));
825 assert_eq!(named(assignment.place(once)), "slot");
826 assert_eq!(assignment.spilled(), 1);
827 }
828
829 #[test]
830 fn two_values_that_are_never_both_wanted_share_a_register() {
831 let mut names = Interner::new();
832 let mut func = Func::new(names.intern("f"));
833 let opcode = Opcode::new(names.intern("x64.nop"));
834 let block = func.create_block();
835 let first = func.new_vreg(GPR);
836 let second = func.new_vreg(GPR);
837 func.build(block, opcode).def(first, GPR).finish();
838 func.build(block, opcode).uses(first, GPR).finish();
839 func.build(block, opcode).def(second, GPR).finish();
840 func.build(block, opcode).uses(second, GPR).finish();
841
842 assert_eq!(places(&mut func, &env()), ["rax", "rax"]);
843 }
844
845 #[test]
846 fn the_value_read_most_often_keeps_its_register_though_it_is_wanted_longest() {
847 let mut names = Interner::new();
848 let mut func = Func::new(names.intern("f"));
849 let opcode = Opcode::new(names.intern("x64.nop"));
850 let block = func.create_block();
851 let busy = func.new_vreg(GPR);
852 let once = func.new_vreg(GPR);
853 func.build(block, opcode).def(busy, GPR).finish();
854 func.build(block, opcode).def(once, GPR).finish();
855 for _ in 0..6 {
856 func.build(block, opcode).uses(busy, GPR).finish();
857 }
858 func.build(block, opcode).uses(once, GPR).finish();
859 func.build(block, opcode).uses(busy, GPR).finish();
860
861 assert_eq!(linear(&func, &narrow(1)), ["slot", "rax"]);
864 assert_eq!(places(&mut func, &narrow(1)), ["rax", "slot"]);
865 }
866
867 #[test]
868 fn a_value_read_in_a_loop_keeps_its_register_over_one_read_outside_it() {
869 let mut names = Interner::new();
870 let mut func = Func::new(names.intern("f"));
871 let opcode = Opcode::new(names.intern("x64.nop"));
872 let entry = func.create_block();
873 let body = func.create_block();
874 let out = func.create_block();
875 let step = func.new_vreg(GPR);
876 let cold = func.new_vreg(GPR);
877 func.build(entry, opcode).def(cold, GPR).finish();
878 func.build(entry, opcode).def(step, GPR).finish();
879 *func.succs_mut(entry) = vec![BlockCall::to(body)];
880 func.build(body, opcode).uses(step, GPR).finish();
881 *func.succs_mut(body) = vec![BlockCall::to(body), BlockCall::to(out)];
882 func.set_weight(body, rucc_mir::Weight::parts(100 * rucc_mir::Weight::SCALE));
883 func.build(out, opcode).uses(cold, GPR).finish();
884 func.build(out, opcode).uses(step, GPR).finish();
885
886 assert_eq!(places(&mut func, &narrow(1)), ["rax", "slot"]);
889 }
890
891 #[test]
892 fn a_long_value_gives_its_register_back_to_a_short_busy_one() {
893 let mut names = Interner::new();
894 let mut func = Func::new(names.intern("f"));
895 let opcode = Opcode::new(names.intern("x64.nop"));
896 let block = func.create_block();
897 let long = func.new_vreg(GPR);
898 let short = func.new_vreg(GPR);
899 func.build(block, opcode).def(long, GPR).finish();
900 for _ in 0..4 {
901 func.build(block, opcode).finish();
902 }
903 func.build(block, opcode).def(short, GPR).finish();
904 for _ in 0..4 {
905 func.build(block, opcode).uses(short, GPR).finish();
906 }
907 func.build(block, opcode).uses(long, GPR).finish();
908
909 assert_eq!(places(&mut func, &narrow(1)), ["slot", "rax"]);
912 }
913
914 #[test]
915 fn the_answer_of_a_two_address_instruction_goes_where_its_source_ends() {
916 let mut names = Interner::new();
917 let mut func = Func::new(names.intern("f"));
918 let opcode = Opcode::new(names.intern("x64.nop"));
919 let block = func.create_block();
920 let left = func.new_vreg(GPR);
921 let right = func.new_vreg(GPR);
922 let sum = func.new_vreg(GPR);
923 func.build(block, opcode).def(left, GPR).finish();
924 func.build(block, opcode).def(right, GPR).finish();
925 func.build(block, opcode)
926 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
927 .uses(left, GPR)
928 .uses(right, GPR)
929 .finish();
930 func.build(block, opcode).uses(right, GPR).finish();
931 func.build(block, opcode).uses(sum, GPR).finish();
932
933 let places = places(&mut func, &env());
934 assert_eq!(places[2], places[0]);
935 assert_ne!(places[1], places[0]);
936 }
937
938 #[test]
939 fn a_source_placed_after_its_answer_goes_where_the_answer_is() {
940 let mut names = Interner::new();
941 let mut func = Func::new(names.intern("f"));
942 let opcode = Opcode::new(names.intern("x64.nop"));
943 let block = func.create_block();
944 let left = func.new_vreg(GPR);
945 let sum = func.new_vreg(GPR);
946 func.build(block, opcode).def(left, GPR).finish();
947 func.build(block, opcode)
948 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
949 .uses(left, GPR)
950 .finish();
951 for _ in 0..6 {
952 func.build(block, opcode).uses(sum, GPR).finish();
953 }
954
955 let places = places(&mut func, &env());
957 assert_eq!(places[0], places[1]);
958 }
959
960 #[test]
961 fn an_answer_that_commutes_stays_where_the_loop_passes_it() {
962 let mut names = Interner::new();
963 let mut func = Func::new(names.intern("f"));
964 let opcode = Opcode::new(names.intern("x64.nop"));
965 let entry = func.create_block();
966 let head = func.create_block();
967 let out = func.create_block();
968 let seed = func.new_vreg(GPR);
969 let total = func.new_vreg(GPR);
970 let term = func.new_vreg(GPR);
971 let next = func.new_vreg(GPR);
972 func.build(entry, opcode).def(seed, GPR).finish();
973 *func.succs_mut(entry) = vec![BlockCall::with(head, vec![seed])];
974 func.params_mut(head).push(Param { reg: total, class: GPR });
975 func.build(head, opcode).def(term, GPR).finish();
976 func.build(head, opcode)
977 .flags(Flags::COMMUTES)
978 .operand(Operand::write(next, GPR).with(Constraint::Reuse(1)))
979 .uses(total, GPR)
980 .uses(term, GPR)
981 .finish();
982 *func.succs_mut(head) = vec![BlockCall::with(head, vec![next]), BlockCall::to(out)];
983
984 let places = places(&mut func, &env());
985 assert_eq!(places[3], places[1]);
986 }
987
988 #[test]
989 fn an_answer_whose_first_source_lives_on_goes_where_the_second_one_ends() {
990 let mut names = Interner::new();
991 let mut func = Func::new(names.intern("f"));
992 let opcode = Opcode::new(names.intern("x64.nop"));
993 let block = func.create_block();
994 let base = func.new_vreg(GPR);
995 let entry = func.new_vreg(GPR);
996 let target = func.new_vreg(GPR);
997 func.build(block, opcode).def(base, GPR).finish();
998 func.build(block, opcode).def(entry, GPR).finish();
999 func.build(block, opcode)
1000 .flags(Flags::COMMUTES)
1001 .operand(Operand::write(target, GPR).with(Constraint::Reuse(1)))
1002 .uses(base, GPR)
1003 .uses(entry, GPR)
1004 .finish();
1005 func.build(block, opcode).uses(target, GPR).finish();
1006 func.build(block, opcode).uses(base, GPR).finish();
1007
1008 let places = places(&mut func, &env());
1009 assert_eq!(places[2], places[1]);
1010 assert_ne!(places[2], places[0]);
1011 }
1012
1013 #[test]
1014 fn an_offset_added_to_a_base_the_loop_reads_again_goes_where_the_answer_went() {
1015 let mut names = Interner::new();
1016 let mut func = Func::new(names.intern("f"));
1017 let opcode = Opcode::new(names.intern("x64.nop"));
1018 let entry = func.create_block();
1019 let head = func.create_block();
1020 let out = func.create_block();
1021 let base = func.new_vreg(GPR);
1022 let at = func.new_vreg(GPR);
1023 let offset = func.new_vreg(GPR);
1024 let target = func.new_vreg(GPR);
1025 let later = func.new_vreg(GPR);
1026 func.build(entry, opcode).def(base, GPR).finish();
1027 *func.succs_mut(entry) = vec![BlockCall::to(head)];
1028 func.build(head, opcode).def(at, GPR).finish();
1029 func.build(head, opcode).def(offset, GPR).uses(base, GPR).uses(at, GPR).finish();
1030 func.build(head, opcode)
1031 .flags(Flags::COMMUTES)
1032 .operand(Operand::write(target, GPR).with(Constraint::Reuse(1)))
1033 .uses(base, GPR)
1034 .uses(offset, GPR)
1035 .finish();
1036 func.build(head, opcode).def(later, GPR).finish();
1037 func.build(head, opcode).uses(target, GPR).finish();
1038 for _ in 0..4 {
1039 func.build(head, opcode).finish();
1040 }
1041 func.build(head, opcode).uses(later, GPR).finish();
1042 *func.succs_mut(head) = vec![BlockCall::to(head), BlockCall::to(out)];
1043
1044 let places = places(&mut func, &env());
1050 assert_eq!(places[3], places[2]);
1051 assert_ne!(places[3], places[0]);
1052 }
1053
1054 #[test]
1055 fn a_register_an_instruction_insists_on_is_left_to_the_value_it_names() {
1056 let mut names = Interner::new();
1057 let mut func = Func::new(names.intern("f"));
1058 let opcode = Opcode::new(names.intern("x64.nop"));
1059 let block = func.create_block();
1060 let kept = func.new_vreg(GPR);
1061 let passed = func.new_vreg(GPR);
1062 func.build(block, opcode).def(kept, GPR).finish();
1063 func.build(block, opcode).def(passed, GPR).finish();
1064 func.build(block, opcode)
1065 .operand(Operand::read(passed, GPR).with(Constraint::Fixed(rucc_target::x86_64::RAX)))
1066 .finish();
1067 func.build(block, opcode).uses(kept, GPR).finish();
1068
1069 let places = places(&mut func, &env());
1070 assert_eq!(places[1], "rax");
1071 assert_ne!(places[0], "rax");
1072 }
1073
1074 #[test]
1075 fn a_value_only_memory_can_hold_goes_to_the_stack() {
1076 let mut names = Interner::new();
1077 let mut func = Func::new(names.intern("f"));
1078 let opcode = Opcode::new(names.intern("x64.nop"));
1079 let block = func.create_block();
1080 let value = func.new_vreg(GPR);
1081 func.build(block, opcode).def(value, GPR).finish();
1082 func.build(block, opcode)
1083 .operand(Operand::read(value, GPR).with(Constraint::Stack))
1084 .finish();
1085
1086 assert_eq!(places(&mut func, &env()), ["slot"]);
1087 }
1088
1089 #[test]
1090 fn a_function_over_the_budget_gets_the_linear_scan_answer() {
1091 let mut names = Interner::new();
1092 let mut func = Func::new(names.intern("f"));
1093 let opcode = Opcode::new(names.intern("x64.nop"));
1094 let block = func.create_block();
1095 let busy = func.new_vreg(GPR);
1096 let once = func.new_vreg(GPR);
1097 func.build(block, opcode).def(busy, GPR).finish();
1098 func.build(block, opcode).def(once, GPR).finish();
1099 for _ in 0..6 {
1100 func.build(block, opcode).uses(busy, GPR).finish();
1101 }
1102 func.build(block, opcode).uses(once, GPR).finish();
1103 func.build(block, opcode).uses(busy, GPR).finish();
1104
1105 let order = Order::of(&func);
1106 let live = Live::of(&func, &order);
1107 assert!(within(&func, &order, &live, &narrow(1), 0).is_none());
1108 let fallen = linear(&func, &narrow(1));
1109 assert_eq!(fallen, ["slot", "rax"]);
1110 }
1111
1112 #[test]
1113 fn the_cheaper_of_the_two_answers_is_the_one_kept() {
1114 let mut names = Interner::new();
1115 let mut func = Func::new(names.intern("f"));
1116 let opcode = Opcode::new(names.intern("x64.nop"));
1117 let block = func.create_block();
1118 let busy = func.new_vreg(GPR);
1119 let once = func.new_vreg(GPR);
1120 func.build(block, opcode).def(busy, GPR).finish();
1121 func.build(block, opcode).def(once, GPR).finish();
1122 for _ in 0..6 {
1123 func.build(block, opcode).uses(busy, GPR).finish();
1124 }
1125 func.build(block, opcode).uses(once, GPR).finish();
1126 func.build(block, opcode).uses(busy, GPR).finish();
1127
1128 let order = Order::of(&func);
1129 let live = Live::of(&func, &order);
1130 let env = narrow(1);
1131 let linear = assign::assign(&func, &order, &live, &env);
1132 let ours = within(&func, &order, &live, &env, BUDGET).expect("inside the budget");
1133 let once_through = u128::from(func[block].weight.raw());
1136 assert_eq!(cost(&func, &order, &linear), 8 * once_through);
1137 assert_eq!(cost(&func, &order, &ours), 2 * once_through);
1138 let kept = assign(&func, &order, &live, &env);
1139 assert_eq!(kept.place(busy), ours.place(busy));
1140 assert_eq!(kept.place(once), ours.place(once));
1141 }
1142
1143 #[test]
1144 fn a_copy_left_between_a_two_address_answer_and_its_source_is_counted() {
1145 let mut names = Interner::new();
1146 let mut func = Func::new(names.intern("f"));
1147 let opcode = Opcode::new(names.intern("x64.nop"));
1148 let block = func.create_block();
1149 let left = func.new_vreg(GPR);
1150 let right = func.new_vreg(GPR);
1151 let sum = func.new_vreg(GPR);
1152 func.build(block, opcode).def(left, GPR).finish();
1153 func.build(block, opcode).def(right, GPR).finish();
1154 func.build(block, opcode)
1155 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1156 .uses(left, GPR)
1157 .uses(right, GPR)
1158 .finish();
1159 func.build(block, opcode).uses(sum, GPR).finish();
1160
1161 let order = Order::of(&func);
1162 let live = Live::of(&func, &order);
1163 let chosen = assign(&func, &order, &live, &env());
1164 assert_eq!(cost(&func, &order, &chosen), 0);
1165 let mut apart = chosen.clone();
1166 apart.put(sum, chosen.place(right).expect("a place for right"));
1167 assert_eq!(cost(&func, &order, &apart), u128::from(func[block].weight.raw()));
1168 }
1169
1170 #[test]
1171 fn many_values_in_few_registers_come_out_as_something_the_machine_can_run() {
1172 let mut names = Interner::new();
1173 let mut func = Func::new(names.intern("f"));
1174 let opcode = Opcode::new(names.intern("x64.nop"));
1175 let block = func.create_block();
1176 let regs: Vec<Reg> = (0..24).map(|_| func.new_vreg(GPR)).collect();
1177 for ® in ®s {
1178 func.build(block, opcode).def(reg, GPR).finish();
1179 }
1180 for (at, ®) in regs.iter().enumerate().rev() {
1181 for _ in 0..(at % 5) {
1182 func.build(block, opcode).uses(reg, GPR).finish();
1183 }
1184 func.build(block, opcode).uses(reg, GPR).finish();
1185 }
1186
1187 let places = places(&mut func, &narrow(4));
1188 assert_eq!(places.iter().filter(|place| *place != "slot").count(), 4);
1189 }
1190}