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 asked: (None, Vec::new()),
247 };
248 let mut assignment = Assignment::empty(count);
249 let mut lost = vec![0u32; count];
250 while let Some((_, Reverse(number))) = queue.pop() {
251 let Some(value) = values[number] else { continue };
252 if forced.contains(&value.reg) || early.contains(&value.reg) {
253 assignment.spill(value.reg, value.class);
254 continue;
255 }
256 assert!(
257 !env.order(value.class).is_empty(),
258 "a value in class {}, which the target hands out no registers from",
259 value.class.number()
260 );
261 let order = env.order(value.class);
271 let handed = |list: &[PhysReg]| -> Vec<PhysReg> {
272 list.iter().copied().filter(|at| order.contains(at)).collect()
273 };
274 let chosen = state
275 .coalesced(value, &reused[number])
276 .or_else(|| state.hinted(value, &handed(&hints[number]), Want::Allowed))
277 .or_else(|| {
278 let partners = passed[number].iter().chain(&received[number]);
279 let partners: Vec<PhysReg> =
280 partners.filter_map(|&other| state.reg_of(other)).collect();
281 state.hinted(value, &partners, Want::Allowed)
282 })
283 .or_else(|| state.swapped(value, &seconds[number]))
284 .or_else(|| {
285 let mut ties = reused[number].to_vec();
286 let source = reuses[number].and_then(|reuse| reuse.source.number());
287 ties.extend(source.and_then(|source| usize::try_from(source).ok()));
288 ties.retain(|&tie| state.at[tie].is_none() && values[tie].is_some());
289 let tied = ties.iter().flat_map(|&tie| handed(&hints[tie]));
290 let wanted: Vec<PhysReg> = tied.chain(order.iter().copied()).collect();
291 state.together(value, &ties, &wanted)
292 })
293 .or_else(|| state.hinted(value, env.order(value.class), Want::Allowed));
294 if state.work > state.budget {
295 return None;
296 }
297 if let Some(at) = chosen {
298 state.take(value, at);
299 continue;
300 }
301 match state.cheapest(value, env.order(value.class)) {
302 Some(at) => {
303 for other in state.evict(value, at) {
304 lost[other] += 1;
305 let Some(evicted) = values[other] else { continue };
306 if lost[other] > ROUNDS {
307 assignment.spill(evicted.reg, evicted.class);
308 } else {
309 queue.push((evicted.size, Reverse(other)));
310 }
311 }
312 state.take(value, at);
313 }
314 None => assignment.spill(value.reg, value.class),
315 }
316 if state.work > state.budget {
317 return None;
318 }
319 }
320 state.settle(&reused, &passed, &received);
321 if state.work > state.budget {
322 return None;
323 }
324 for (number, at) in state.at.iter().enumerate() {
325 let Some(at) = *at else { continue };
326 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
327 assignment.put(reg, Place::Reg(at));
328 }
329 for inst in state.commuted.iter().flatten() {
330 assignment.commute(*inst);
331 }
332 Some(assignment)
333}
334
335type Held = (usize, Range);
337
338struct State<'a, 'v> {
340 live: &'a Live,
341 blocked: &'a Blocks,
342 reuses: &'a [Option<Reuse>],
343 values: &'v [Option<Value<'a>>],
344 held: Vec<((RegClass, PhysReg), Vec<Held>)>,
348 pieces: Vec<Pieces>,
351 at: Vec<Option<PhysReg>>,
353 commuted: Vec<Option<Inst>>,
356 work: u64,
357 budget: u64,
358 asked: (Option<Reg>, Vec<[Option<bool>; 2]>),
363}
364
365impl<'a> State<'a, '_> {
366 fn reg_of(&self, reg: Reg) -> Option<PhysReg> {
367 self.at.get(usize::try_from(reg.number()?).ok()?).copied().flatten()
368 }
369
370 fn slot(&mut self, class: RegClass, at: PhysReg) -> usize {
371 let found = self.held.iter().position(|(key, _)| *key == (class, at));
372 found.unwrap_or_else(|| {
373 self.held.push(((class, at), Vec::new()));
374 self.pieces.push(Pieces::default());
375 self.held.len() - 1
376 })
377 }
378
379 fn remove(&mut self, index: usize, gone: &[usize]) {
381 self.held[index].1.retain(|(other, _)| !gone.contains(other));
382 let pieces = &mut self.pieces[index];
383 if pieces.kept {
384 for value in gone.iter().filter_map(|&other| self.values[other]) {
385 for piece in value.area.pieces() {
386 pieces.remove(piece, value.reg);
387 }
388 }
389 }
390 }
391
392 fn clashes(&mut self, value: Value<'_>, at: PhysReg) -> Vec<usize> {
395 let Some(found) = self.held.iter().position(|(key, _)| *key == (value.class, at)) else {
396 return Vec::new();
397 };
398 let held = &self.held[found].1;
399 self.work += held.len() as u64;
402 if held.len() > FEW {
403 let pieces = &mut self.pieces[found];
404 if !pieces.kept {
405 pieces.kept = true;
406 for other in held.iter().filter_map(|&(other, _)| self.values[other]) {
407 for piece in other.area.pieces() {
408 pieces.insert(piece, other.reg);
409 }
410 }
411 }
412 if let Some(owners) = pieces.owners(value.area) {
413 let clash = owners.into_iter().filter(|&other| !self.shares(value.reg, other));
414 return clash.map(index).collect();
415 }
416 }
417 let mut clashes = Vec::new();
418 for &(other, range) in held {
419 if !range.overlaps(value.range) {
420 continue;
421 }
422 let Some(held) = self.values[other] else { continue };
423 if !held.area.overlaps(value.area) {
424 continue;
425 }
426 if !self.shares(value.reg, held.reg) {
427 clashes.push(other);
428 }
429 }
430 clashes
431 }
432
433 fn shares(&self, one: Reg, two: Reg) -> bool {
436 let reads = |answer: Reg, source: Reg| {
437 let Some(number) = answer.number().and_then(|n| usize::try_from(n).ok()) else {
438 return false;
439 };
440 let Some(reuse) = self.reuses[number] else { return false };
441 match self.commuted[number] {
442 Some(_) => reuse.second == Some(source),
443 None => reuse.source == source,
444 }
445 };
446 (reads(one, two) || reads(two, one)) && assign::apart(self.live, one, two)
447 }
448
449 fn free(&mut self, value: Value<'_>, at: PhysReg, want: Want) -> bool {
450 !self.insists(value, at, want) && self.clashes(value, at).is_empty()
451 }
452
453 fn insists(&mut self, value: Value<'_>, at: PhysReg, want: Want) -> bool {
457 let (asked, answers) = &mut self.asked;
458 if *asked != Some(value.reg) {
459 *asked = Some(value.reg);
460 answers.fill([None; 2]);
461 }
462 let number = usize::from(at.number());
463 if answers.len() <= number {
464 answers.resize(number + 1, [None; 2]);
465 }
466 let slot = usize::from(want == Want::Clear);
467 if let Some(answer) = answers[number][slot] {
468 return answer;
469 }
470 let answer =
471 self.blocked.insists(value.reg, value.class, value.area, value.range, at, want);
472 let known = &mut answers[number];
473 known[slot] = Some(answer);
474 match (want, answer) {
475 (Want::Clear, false) => known[0] = Some(false),
476 (Want::Allowed, true) => known[1] = Some(true),
477 _ => {}
478 }
479 answer
480 }
481
482 fn hinted(&mut self, value: Value<'_>, wanted: &[PhysReg], want: Want) -> Option<PhysReg> {
484 wanted.iter().copied().find(|&at| self.work <= self.budget && self.free(value, at, want))
485 }
486
487 fn together(&mut self, value: Value<'_>, ties: &[usize], order: &[PhysReg]) -> Option<PhysReg> {
493 if ties.is_empty() {
494 return None;
495 }
496 for &at in order {
497 if self.work > self.budget {
498 return None;
499 }
500 if !self.free(value, at, Want::Clear) {
501 continue;
502 }
503 let values = self.values;
504 let mut tied = ties.iter().filter_map(|&tie| values[tie]);
505 if tied.all(|other| self.free(other, at, Want::Allowed)) {
506 return Some(at);
507 }
508 }
509 None
510 }
511
512 fn coalesced(&mut self, value: Value<'_>, answers: &[usize]) -> Option<PhysReg> {
518 let number = index(value.reg);
519 if let Some(reuse) = self.reuses[number] {
520 if let Some(at) = self.reg_of(reuse.source) {
521 if self.free(value, at, Want::Allowed) {
522 return Some(at);
523 }
524 }
525 if let Some(second) = reuse.second {
526 if let Some(at) = self.reg_of(second) {
527 self.commuted[number] = Some(reuse.inst);
528 if self.free(value, at, Want::Allowed) {
529 return Some(at);
530 }
531 self.commuted[number] = None;
532 }
533 }
534 }
535 for &answer in answers {
536 let Some(at) = self.at[answer] else { continue };
537 if self.commuted[answer].is_none() && self.free(value, at, Want::Allowed) {
538 return Some(at);
539 }
540 }
541 None
542 }
543
544 fn swapped(&mut self, value: Value<'_>, seconds: &[usize]) -> Option<PhysReg> {
550 if self.reuses[index(value.reg)].is_some() {
554 return None;
555 }
556 for &answer in seconds {
557 let (Some(at), Some(reuse)) = (self.at[answer], self.reuses[answer]) else { continue };
558 let Some(written) = self.values[answer] else { continue };
559 if self.commuted[answer].is_some()
563 || self.reg_of(reuse.source) == Some(at)
564 || assign::apart(self.live, written.reg, reuse.source)
565 {
566 continue;
567 }
568 self.commuted[answer] = Some(reuse.inst);
569 if self.free(value, at, Want::Allowed) {
570 return Some(at);
571 }
572 self.commuted[answer] = None;
573 }
574 None
575 }
576
577 fn cheapest(&mut self, value: Value<'_>, order: &[PhysReg]) -> Option<PhysReg> {
580 let mut best: Option<(u128, u128, PhysReg)> = None;
581 for &at in order {
582 if self.insists(value, at, Want::Allowed) {
583 continue;
584 }
585 let clashes = self.clashes(value, at);
586 let weights = clashes.iter().filter_map(|&other| self.values[other]).map(|v| v.weight);
587 let (heaviest, total) = weights.fold((0, 0), |(most, sum), w| (most.max(w), sum + w));
588 if heaviest >= value.weight {
589 continue;
590 }
591 if best.is_none_or(|(most, sum, _)| (heaviest, total) < (most, sum)) {
592 best = Some((heaviest, total, at));
593 }
594 }
595 best.map(|(_, _, at)| at)
596 }
597
598 fn evict(&mut self, value: Value<'_>, at: PhysReg) -> Vec<usize> {
600 let clashes = self.clashes(value, at);
601 let index = self.slot(value.class, at);
602 self.remove(index, &clashes);
603 for &other in &clashes {
604 self.at[other] = None;
605 self.commuted[other] = None;
606 }
607 clashes
608 }
609
610 fn settle(&mut self, reused: &Answers, passed: &[Vec<Reg>], received: &[Vec<Reg>]) {
619 let mut moved = true;
620 while moved && self.work <= self.budget {
621 moved = false;
622 for number in 0..self.at.len() {
623 let (Some(value), Some(now)) = (self.values[number], self.at[number]) else {
624 continue;
625 };
626 let reuse = self.reuses[number];
627 let source = reuse.and_then(|reuse| self.reg_of(reuse.source));
628 let second =
629 reuse.and_then(|reuse| reuse.second).and_then(|second| self.reg_of(second));
630 let mut wanted: Vec<PhysReg> = source.into_iter().chain(second).collect();
631 for &answer in &reused[number] {
632 if self.commuted[answer].is_none() {
633 wanted.extend(self.at[answer]);
634 }
635 }
636 let partners = passed[number].iter().chain(&received[number]);
637 wanted.extend(partners.filter_map(|&other| self.reg_of(other)));
638 let met = |at: PhysReg| wanted.iter().filter(|&®| reg == at).count();
639 let here = met(now);
640 let mut better: Vec<(usize, PhysReg)> = Vec::new();
641 for &at in &wanted {
642 let count = met(at);
643 if count > here && !better.contains(&(count, at)) {
644 better.push((count, at));
645 }
646 }
647 if better.is_empty() {
648 continue;
649 }
650 better.sort_by_key(|&(count, _)| Reverse(count));
651 let index = self.slot(value.class, now);
652 self.remove(index, &[number]);
653 self.at[number] = None;
654 let was = self.commuted[number];
655 let mut to = now;
656 for (_, at) in better {
657 self.commuted[number] = match reuse {
658 Some(reuse) if second == Some(at) && source != Some(at) => Some(reuse.inst),
659 _ => None,
660 };
661 if self.free(value, at, Want::Allowed) {
662 to = at;
663 moved = true;
664 break;
665 }
666 }
667 if to == now {
668 self.commuted[number] = was;
669 }
670 self.take(value, to);
671 }
672 }
673 }
674
675 fn take(&mut self, value: Value<'_>, at: PhysReg) {
676 let number = index(value.reg);
677 self.at[number] = Some(at);
678 let index = self.slot(value.class, at);
679 self.held[index].1.push((number, value.range));
680 let pieces = &mut self.pieces[index];
681 if pieces.kept {
682 for piece in value.area.pieces() {
683 pieces.insert(piece, value.reg);
684 }
685 }
686 }
687}
688
689pub(crate) fn size(area: Area<'_>) -> u32 {
691 area.pieces().map(|piece| piece.end - piece.start + 1).sum()
692}
693
694pub(crate) fn costs(func: &Func) -> Vec<u128> {
699 let mut costs = vec![0u128; func.vregs()];
700 let mut add = |reg: Reg, weight: u128| {
701 let number = reg.number().and_then(|number| usize::try_from(number).ok());
702 if let Some(cost) = number.and_then(|number| costs.get_mut(number)) {
703 *cost += weight;
704 }
705 };
706 for block in func.blocks() {
707 let weight = u128::from(func[block].weight.raw().max(1));
708 for param in &func[block].params {
709 add(param.reg, weight);
710 }
711 for inst in func.insts(block) {
712 for operand in &func[func[inst].operands] {
713 add(operand.reg, weight);
714 }
715 }
716 for call in &func[block].succs {
717 for &arg in &call.args {
718 add(arg, weight);
719 }
720 }
721 }
722 costs
723}
724
725fn received(func: &Func) -> Vec<Vec<Reg>> {
727 let mut received = vec![Vec::new(); func.vregs()];
728 for block in func.blocks() {
729 for call in &func[block].succs {
730 for (&arg, param) in call.args.iter().zip(&func[call.block].params) {
731 let number = param.reg.number().and_then(|number| usize::try_from(number).ok());
732 let Some(from) = number.and_then(|number| received.get_mut(number)) else {
733 continue;
734 };
735 if !from.contains(&arg) {
736 from.push(arg);
737 }
738 }
739 }
740 }
741 received
742}
743
744struct Answers {
749 starts: Vec<usize>,
750 all: Vec<usize>,
751}
752
753impl std::ops::Index<usize> for Answers {
754 type Output = [usize];
755
756 fn index(&self, number: usize) -> &[usize] {
757 &self.all[self.starts[number]..self.starts[number + 1]]
758 }
759}
760
761fn answers(reuses: &[Option<Reuse>], of: impl Fn(Reuse) -> Option<Reg>) -> Answers {
763 let number = |reuse: &Option<Reuse>| {
764 let reg = reuse.and_then(&of)?;
765 let number = usize::try_from(reg.number()?).ok()?;
766 (number < reuses.len()).then_some(number)
767 };
768 let mut starts = vec![0; reuses.len() + 1];
771 for reuse in reuses {
772 if let Some(number) = number(reuse) {
773 starts[number] += 1;
774 }
775 }
776 let mut total = 0;
777 for start in &mut starts {
778 total += *start;
779 *start = total;
780 }
781 let mut all = vec![0; total];
782 for (answer, reuse) in reuses.iter().enumerate().rev() {
783 if let Some(number) = number(reuse) {
784 starts[number] -= 1;
785 all[starts[number]] = answer;
786 }
787 }
788 Answers { starts, all }
789}
790
791fn reused(reuses: &[Option<Reuse>]) -> Answers {
793 answers(reuses, |reuse| Some(reuse.source))
794}
795
796fn seconds(reuses: &[Option<Reuse>]) -> Answers {
799 answers(reuses, |reuse| reuse.second)
800}
801
802fn index(reg: Reg) -> usize {
803 usize::try_from(reg.number().expect("a virtual register")).expect("a register number")
804}
805
806#[cfg(test)]
807mod tests {
808 use rucc_base::Interner;
809 use rucc_mir::{BlockCall, Constraint, Flags, Opcode, Operand, Param};
810 use rucc_target::x86_64::{GPR, REGS, SYSV};
811
812 use super::*;
813 use crate::check;
814
815 fn env() -> Env {
816 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
817 Env::new().with(GPR, order, scratch)
818 }
819
820 fn narrow(count: usize) -> Env {
821 Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 1])
822 }
823
824 fn named(place: Option<Place>) -> String {
825 match place {
826 Some(Place::Reg(reg)) => REGS.name(GPR, reg).expect("a register").to_string(),
827 Some(Place::Slot(_)) => "slot".to_string(),
828 None => "nowhere".to_string(),
829 }
830 }
831
832 fn places(func: &mut Func, env: &Env) -> Vec<String> {
834 let order = Order::of(func);
835 let live = Live::of(func, &order);
836 let assignment = within(func, &order, &live, env, BUDGET).expect("inside the budget");
837 for &inst in assignment.commuted() {
840 let list = func[inst].operands;
841 func[list].swap(1, 2);
842 }
843 let problems = check::check(func, &order, &live, &assignment);
844 assert!(problems.is_empty(), "{}", check::report(&problems));
845 (0..func.vregs())
846 .map(|number| {
847 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
848 named(assignment.place(reg))
849 })
850 .collect()
851 }
852
853 fn linear(func: &Func, env: &Env) -> Vec<String> {
854 let order = Order::of(func);
855 let live = Live::of(func, &order);
856 let assignment = assign::assign(func, &order, &live, env);
857 (0..func.vregs())
858 .map(|number| {
859 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
860 named(assignment.place(reg))
861 })
862 .collect()
863 }
864
865 #[test]
866 fn a_value_the_spill_phase_picked_goes_to_the_stack_and_the_rest_fit() {
867 let mut names = Interner::new();
868 let mut func = Func::new(names.intern("f"));
869 let opcode = Opcode::new(names.intern("x64.nop"));
870 let block = func.create_block();
871 let busy = func.new_vreg(GPR);
872 let once = func.new_vreg(GPR);
873 let other = func.new_vreg(GPR);
874 func.build(block, opcode).def(busy, GPR).finish();
875 func.build(block, opcode).def(once, GPR).finish();
876 func.build(block, opcode).def(other, GPR).finish();
877 for _ in 0..3 {
878 func.build(block, opcode).uses(busy, GPR).uses(other, GPR).finish();
879 }
880 func.build(block, opcode).uses(once, GPR).uses(busy, GPR).uses(other, GPR).finish();
881
882 let env = narrow(2);
883 let order = Order::of(&func);
884 let live = Live::of(&func, &order);
885 let pressure = Pressure::of(&func, &order, &live, &env);
886 let early = spill::choose(&func, &live, &pressure);
887 assert_eq!(early, [once]);
888 let assignment = placed(&func, &order, &live, &env, BUDGET, &early).expect("in budget");
889 let problems = check::check(&func, &order, &live, &assignment);
890 assert!(problems.is_empty(), "{}", check::report(&problems));
891 assert_eq!(named(assignment.place(once)), "slot");
892 assert_eq!(assignment.spilled(), 1);
893 }
894
895 #[test]
896 fn two_values_that_are_never_both_wanted_share_a_register() {
897 let mut names = Interner::new();
898 let mut func = Func::new(names.intern("f"));
899 let opcode = Opcode::new(names.intern("x64.nop"));
900 let block = func.create_block();
901 let first = func.new_vreg(GPR);
902 let second = func.new_vreg(GPR);
903 func.build(block, opcode).def(first, GPR).finish();
904 func.build(block, opcode).uses(first, GPR).finish();
905 func.build(block, opcode).def(second, GPR).finish();
906 func.build(block, opcode).uses(second, GPR).finish();
907
908 assert_eq!(places(&mut func, &env()), ["rax", "rax"]);
909 }
910
911 #[test]
912 fn the_value_read_most_often_keeps_its_register_though_it_is_wanted_longest() {
913 let mut names = Interner::new();
914 let mut func = Func::new(names.intern("f"));
915 let opcode = Opcode::new(names.intern("x64.nop"));
916 let block = func.create_block();
917 let busy = func.new_vreg(GPR);
918 let once = func.new_vreg(GPR);
919 func.build(block, opcode).def(busy, GPR).finish();
920 func.build(block, opcode).def(once, GPR).finish();
921 for _ in 0..6 {
922 func.build(block, opcode).uses(busy, GPR).finish();
923 }
924 func.build(block, opcode).uses(once, GPR).finish();
925 func.build(block, opcode).uses(busy, GPR).finish();
926
927 assert_eq!(linear(&func, &narrow(1)), ["slot", "rax"]);
930 assert_eq!(places(&mut func, &narrow(1)), ["rax", "slot"]);
931 }
932
933 #[test]
934 fn a_value_read_in_a_loop_keeps_its_register_over_one_read_outside_it() {
935 let mut names = Interner::new();
936 let mut func = Func::new(names.intern("f"));
937 let opcode = Opcode::new(names.intern("x64.nop"));
938 let entry = func.create_block();
939 let body = func.create_block();
940 let out = func.create_block();
941 let step = func.new_vreg(GPR);
942 let cold = func.new_vreg(GPR);
943 func.build(entry, opcode).def(cold, GPR).finish();
944 func.build(entry, opcode).def(step, GPR).finish();
945 *func.succs_mut(entry) = vec![BlockCall::to(body)];
946 func.build(body, opcode).uses(step, GPR).finish();
947 *func.succs_mut(body) = vec![BlockCall::to(body), BlockCall::to(out)];
948 func.set_weight(body, rucc_mir::Weight::parts(100 * rucc_mir::Weight::SCALE));
949 func.build(out, opcode).uses(cold, GPR).finish();
950 func.build(out, opcode).uses(step, GPR).finish();
951
952 assert_eq!(places(&mut func, &narrow(1)), ["rax", "slot"]);
955 }
956
957 #[test]
958 fn a_long_value_gives_its_register_back_to_a_short_busy_one() {
959 let mut names = Interner::new();
960 let mut func = Func::new(names.intern("f"));
961 let opcode = Opcode::new(names.intern("x64.nop"));
962 let block = func.create_block();
963 let long = func.new_vreg(GPR);
964 let short = func.new_vreg(GPR);
965 func.build(block, opcode).def(long, GPR).finish();
966 for _ in 0..4 {
967 func.build(block, opcode).finish();
968 }
969 func.build(block, opcode).def(short, GPR).finish();
970 for _ in 0..4 {
971 func.build(block, opcode).uses(short, GPR).finish();
972 }
973 func.build(block, opcode).uses(long, GPR).finish();
974
975 assert_eq!(places(&mut func, &narrow(1)), ["slot", "rax"]);
978 }
979
980 #[test]
981 fn the_answer_of_a_two_address_instruction_goes_where_its_source_ends() {
982 let mut names = Interner::new();
983 let mut func = Func::new(names.intern("f"));
984 let opcode = Opcode::new(names.intern("x64.nop"));
985 let block = func.create_block();
986 let left = func.new_vreg(GPR);
987 let right = func.new_vreg(GPR);
988 let sum = func.new_vreg(GPR);
989 func.build(block, opcode).def(left, GPR).finish();
990 func.build(block, opcode).def(right, GPR).finish();
991 func.build(block, opcode)
992 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
993 .uses(left, GPR)
994 .uses(right, GPR)
995 .finish();
996 func.build(block, opcode).uses(right, GPR).finish();
997 func.build(block, opcode).uses(sum, GPR).finish();
998
999 let places = places(&mut func, &env());
1000 assert_eq!(places[2], places[0]);
1001 assert_ne!(places[1], places[0]);
1002 }
1003
1004 #[test]
1005 fn a_source_placed_after_its_answer_goes_where_the_answer_is() {
1006 let mut names = Interner::new();
1007 let mut func = Func::new(names.intern("f"));
1008 let opcode = Opcode::new(names.intern("x64.nop"));
1009 let block = func.create_block();
1010 let left = func.new_vreg(GPR);
1011 let sum = func.new_vreg(GPR);
1012 func.build(block, opcode).def(left, GPR).finish();
1013 func.build(block, opcode)
1014 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1015 .uses(left, GPR)
1016 .finish();
1017 for _ in 0..6 {
1018 func.build(block, opcode).uses(sum, GPR).finish();
1019 }
1020
1021 let places = places(&mut func, &env());
1023 assert_eq!(places[0], places[1]);
1024 }
1025
1026 #[test]
1027 fn an_answer_that_commutes_stays_where_the_loop_passes_it() {
1028 let mut names = Interner::new();
1029 let mut func = Func::new(names.intern("f"));
1030 let opcode = Opcode::new(names.intern("x64.nop"));
1031 let entry = func.create_block();
1032 let head = func.create_block();
1033 let out = func.create_block();
1034 let seed = func.new_vreg(GPR);
1035 let total = func.new_vreg(GPR);
1036 let term = func.new_vreg(GPR);
1037 let next = func.new_vreg(GPR);
1038 func.build(entry, opcode).def(seed, GPR).finish();
1039 *func.succs_mut(entry) = vec![BlockCall::with(head, vec![seed])];
1040 func.params_mut(head).push(Param { reg: total, class: GPR });
1041 func.build(head, opcode).def(term, GPR).finish();
1042 func.build(head, opcode)
1043 .flags(Flags::COMMUTES)
1044 .operand(Operand::write(next, GPR).with(Constraint::Reuse(1)))
1045 .uses(total, GPR)
1046 .uses(term, GPR)
1047 .finish();
1048 *func.succs_mut(head) = vec![BlockCall::with(head, vec![next]), BlockCall::to(out)];
1049
1050 let places = places(&mut func, &env());
1051 assert_eq!(places[3], places[1]);
1052 }
1053
1054 #[test]
1055 fn an_answer_whose_first_source_lives_on_goes_where_the_second_one_ends() {
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 base = func.new_vreg(GPR);
1061 let entry = func.new_vreg(GPR);
1062 let target = func.new_vreg(GPR);
1063 func.build(block, opcode).def(base, GPR).finish();
1064 func.build(block, opcode).def(entry, GPR).finish();
1065 func.build(block, opcode)
1066 .flags(Flags::COMMUTES)
1067 .operand(Operand::write(target, GPR).with(Constraint::Reuse(1)))
1068 .uses(base, GPR)
1069 .uses(entry, GPR)
1070 .finish();
1071 func.build(block, opcode).uses(target, GPR).finish();
1072 func.build(block, opcode).uses(base, GPR).finish();
1073
1074 let places = places(&mut func, &env());
1075 assert_eq!(places[2], places[1]);
1076 assert_ne!(places[2], places[0]);
1077 }
1078
1079 #[test]
1080 fn an_offset_added_to_a_base_the_loop_reads_again_goes_where_the_answer_went() {
1081 let mut names = Interner::new();
1082 let mut func = Func::new(names.intern("f"));
1083 let opcode = Opcode::new(names.intern("x64.nop"));
1084 let entry = func.create_block();
1085 let head = func.create_block();
1086 let out = func.create_block();
1087 let base = func.new_vreg(GPR);
1088 let at = func.new_vreg(GPR);
1089 let offset = func.new_vreg(GPR);
1090 let target = func.new_vreg(GPR);
1091 let later = func.new_vreg(GPR);
1092 func.build(entry, opcode).def(base, GPR).finish();
1093 *func.succs_mut(entry) = vec![BlockCall::to(head)];
1094 func.build(head, opcode).def(at, GPR).finish();
1095 func.build(head, opcode).def(offset, GPR).uses(base, GPR).uses(at, GPR).finish();
1096 func.build(head, opcode)
1097 .flags(Flags::COMMUTES)
1098 .operand(Operand::write(target, GPR).with(Constraint::Reuse(1)))
1099 .uses(base, GPR)
1100 .uses(offset, GPR)
1101 .finish();
1102 func.build(head, opcode).def(later, GPR).finish();
1103 func.build(head, opcode).uses(target, GPR).finish();
1104 for _ in 0..4 {
1105 func.build(head, opcode).finish();
1106 }
1107 func.build(head, opcode).uses(later, GPR).finish();
1108 *func.succs_mut(head) = vec![BlockCall::to(head), BlockCall::to(out)];
1109
1110 let places = places(&mut func, &env());
1116 assert_eq!(places[3], places[2]);
1117 assert_ne!(places[3], places[0]);
1118 }
1119
1120 #[test]
1121 fn a_register_an_instruction_insists_on_is_left_to_the_value_it_names() {
1122 let mut names = Interner::new();
1123 let mut func = Func::new(names.intern("f"));
1124 let opcode = Opcode::new(names.intern("x64.nop"));
1125 let block = func.create_block();
1126 let kept = func.new_vreg(GPR);
1127 let passed = func.new_vreg(GPR);
1128 func.build(block, opcode).def(kept, GPR).finish();
1129 func.build(block, opcode).def(passed, GPR).finish();
1130 func.build(block, opcode)
1131 .operand(Operand::read(passed, GPR).with(Constraint::Fixed(rucc_target::x86_64::RAX)))
1132 .finish();
1133 func.build(block, opcode).uses(kept, GPR).finish();
1134
1135 let places = places(&mut func, &env());
1136 assert_eq!(places[1], "rax");
1137 assert_ne!(places[0], "rax");
1138 }
1139
1140 #[test]
1141 fn a_value_only_memory_can_hold_goes_to_the_stack() {
1142 let mut names = Interner::new();
1143 let mut func = Func::new(names.intern("f"));
1144 let opcode = Opcode::new(names.intern("x64.nop"));
1145 let block = func.create_block();
1146 let value = func.new_vreg(GPR);
1147 func.build(block, opcode).def(value, GPR).finish();
1148 func.build(block, opcode)
1149 .operand(Operand::read(value, GPR).with(Constraint::Stack))
1150 .finish();
1151
1152 assert_eq!(places(&mut func, &env()), ["slot"]);
1153 }
1154
1155 #[test]
1156 fn a_function_over_the_budget_gets_the_linear_scan_answer() {
1157 let mut names = Interner::new();
1158 let mut func = Func::new(names.intern("f"));
1159 let opcode = Opcode::new(names.intern("x64.nop"));
1160 let block = func.create_block();
1161 let busy = func.new_vreg(GPR);
1162 let once = func.new_vreg(GPR);
1163 func.build(block, opcode).def(busy, GPR).finish();
1164 func.build(block, opcode).def(once, GPR).finish();
1165 for _ in 0..6 {
1166 func.build(block, opcode).uses(busy, GPR).finish();
1167 }
1168 func.build(block, opcode).uses(once, GPR).finish();
1169 func.build(block, opcode).uses(busy, GPR).finish();
1170
1171 let order = Order::of(&func);
1172 let live = Live::of(&func, &order);
1173 assert!(within(&func, &order, &live, &narrow(1), 0).is_none());
1174 let fallen = linear(&func, &narrow(1));
1175 assert_eq!(fallen, ["slot", "rax"]);
1176 }
1177
1178 #[test]
1179 fn the_cheaper_of_the_two_answers_is_the_one_kept() {
1180 let mut names = Interner::new();
1181 let mut func = Func::new(names.intern("f"));
1182 let opcode = Opcode::new(names.intern("x64.nop"));
1183 let block = func.create_block();
1184 let busy = func.new_vreg(GPR);
1185 let once = func.new_vreg(GPR);
1186 func.build(block, opcode).def(busy, GPR).finish();
1187 func.build(block, opcode).def(once, GPR).finish();
1188 for _ in 0..6 {
1189 func.build(block, opcode).uses(busy, GPR).finish();
1190 }
1191 func.build(block, opcode).uses(once, GPR).finish();
1192 func.build(block, opcode).uses(busy, GPR).finish();
1193
1194 let order = Order::of(&func);
1195 let live = Live::of(&func, &order);
1196 let env = narrow(1);
1197 let linear = assign::assign(&func, &order, &live, &env);
1198 let ours = within(&func, &order, &live, &env, BUDGET).expect("inside the budget");
1199 let once_through = u128::from(func[block].weight.raw());
1202 assert_eq!(cost(&func, &order, &linear), 8 * once_through);
1203 assert_eq!(cost(&func, &order, &ours), 2 * once_through);
1204 let kept = assign(&func, &order, &live, &env);
1205 assert_eq!(kept.place(busy), ours.place(busy));
1206 assert_eq!(kept.place(once), ours.place(once));
1207 }
1208
1209 #[test]
1210 fn a_copy_left_between_a_two_address_answer_and_its_source_is_counted() {
1211 let mut names = Interner::new();
1212 let mut func = Func::new(names.intern("f"));
1213 let opcode = Opcode::new(names.intern("x64.nop"));
1214 let block = func.create_block();
1215 let left = func.new_vreg(GPR);
1216 let right = func.new_vreg(GPR);
1217 let sum = func.new_vreg(GPR);
1218 func.build(block, opcode).def(left, GPR).finish();
1219 func.build(block, opcode).def(right, GPR).finish();
1220 func.build(block, opcode)
1221 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1222 .uses(left, GPR)
1223 .uses(right, GPR)
1224 .finish();
1225 func.build(block, opcode).uses(sum, GPR).finish();
1226
1227 let order = Order::of(&func);
1228 let live = Live::of(&func, &order);
1229 let chosen = assign(&func, &order, &live, &env());
1230 assert_eq!(cost(&func, &order, &chosen), 0);
1231 let mut apart = chosen.clone();
1232 apart.put(sum, chosen.place(right).expect("a place for right"));
1233 assert_eq!(cost(&func, &order, &apart), u128::from(func[block].weight.raw()));
1234 }
1235
1236 #[test]
1237 fn many_values_in_few_registers_come_out_as_something_the_machine_can_run() {
1238 let mut names = Interner::new();
1239 let mut func = Func::new(names.intern("f"));
1240 let opcode = Opcode::new(names.intern("x64.nop"));
1241 let block = func.create_block();
1242 let regs: Vec<Reg> = (0..24).map(|_| func.new_vreg(GPR)).collect();
1243 for ® in ®s {
1244 func.build(block, opcode).def(reg, GPR).finish();
1245 }
1246 for (at, ®) in regs.iter().enumerate().rev() {
1247 for _ in 0..(at % 5) {
1248 func.build(block, opcode).uses(reg, GPR).finish();
1249 }
1250 func.build(block, opcode).uses(reg, GPR).finish();
1251 }
1252
1253 let places = places(&mut func, &narrow(4));
1254 assert_eq!(places.iter().filter(|place| *place != "slot").count(), 4);
1255 }
1256}