1use std::cmp::Reverse;
100use std::collections::BinaryHeap;
101
102use rucc_base::hash::{Map, Set};
103use rucc_mir::{Func, Inst, Reg};
104use rucc_target::{PhysReg, RegClass};
105
106use crate::assign::{self, Assignment, Blocks, Env, FEW, Pieces, Place, Reuse, Want};
107use crate::live::{Area, Live, Range};
108use crate::order::{Order, Point};
109use crate::pressure::Pressure;
110use crate::spill;
111
112pub const BUDGET: u64 = 50_000_000;
115
116pub const ROUNDS: u32 = 4;
118
119#[derive(Debug, Clone, Copy)]
121struct Value<'a> {
122 reg: Reg,
123 class: RegClass,
124 area: Area<'a>,
125 range: Range,
126 weight: u128,
127 size: u32,
128}
129
130#[must_use]
136pub fn assign(func: &Func, order: &Order, live: &Live, env: &Env) -> Assignment {
137 let linear = assign::assign(func, order, live, env);
138 let mut best = cost(func, order, &linear);
139 let mut kept = linear;
140 let pressure = Pressure::of(func, order, live, env);
141 let spilled = spill::choose(func, live, &pressure);
142 let tries: &[&[Reg]] = if spilled.is_empty() { &[&[]] } else { &[&[], &spilled] };
144 for &early in tries {
145 let Some(ours) = placed(func, order, live, env, BUDGET, early) else { break };
146 let spent = cost(func, order, &ours);
147 if spent <= best {
148 best = spent;
149 kept = ours;
150 }
151 }
152 kept
153}
154
155#[must_use]
169pub fn cost(func: &Func, order: &Order, assignment: &Assignment) -> u128 {
170 let costs = costs(func);
171 let mut total = 0;
172 for (reg, place) in assignment.placed() {
173 if !matches!(place, Place::Reg(_)) {
174 total += costs[index(reg)];
175 }
176 }
177 let mut weights = Map::default();
178 for block in func.blocks() {
179 let weight = u128::from(func[block].weight.raw().max(1));
180 for inst in func.insts(block) {
181 weights.insert(inst, weight);
182 }
183 for call in &func[block].succs {
184 for (&arg, param) in call.args.iter().zip(&func[call.block].params) {
185 if assignment.place(arg) != assignment.place(param.reg) {
186 total += weight;
187 }
188 }
189 }
190 }
191 for save in assignment.saves() {
192 total += 2 * weights.get(&save.inst).copied().unwrap_or(1);
193 }
194 let commuted: Set<Inst> = assignment.commuted().iter().copied().collect();
195 for (number, reuse) in assign::reuses(func, order).iter().enumerate() {
196 let Some(reuse) = reuse else { continue };
197 let answer = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
198 let tied = if commuted.contains(&reuse.inst) { reuse.second } else { Some(reuse.source) };
199 let Some(at) = assignment.place(answer) else { continue };
200 if tied.and_then(|tied| assignment.place(tied)) != Some(at) {
201 total += weights.get(&reuse.inst).copied().unwrap_or(1);
202 }
203 }
204 total
205}
206
207#[must_use]
214pub fn within(
215 func: &Func,
216 order: &Order,
217 live: &Live,
218 env: &Env,
219 budget: u64,
220) -> Option<Assignment> {
221 placed(func, order, live, env, budget, &[])
222}
223
224fn placed(
225 func: &Func,
226 order: &Order,
227 live: &Live,
228 env: &Env,
229 budget: u64,
230 early: &[Reg],
231) -> Option<Assignment> {
232 let blocked = assign::blocked(func, order);
233 let forced = assign::forced(func);
234 let reuses = assign::reuses(func, order);
235 let hints = assign::hints(func);
236 let passed = assign::passed(func);
237 let received = received(func);
238 let reused = reused(&reuses);
239 let seconds = seconds(&reuses);
240 let costs = costs(func);
241 let saveable = saveable(func, order);
242
243 let count = func.vregs();
244 let mut values: Vec<Option<Value<'_>>> = vec![None; count];
245 let mut queue = BinaryHeap::new();
246 for (number, reuse) in reuses.iter().enumerate() {
247 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
248 let (Some(mut area), Some(class)) = (live.area(reg), func.class_of(reg)) else {
249 continue;
250 };
251 if let Some(reuse) = reuse {
252 area = area.with(reuse.at);
253 }
254 let size = size(area);
255 let weight = costs[number] * 1024 / u128::from(size + 8);
256 values[number] = Some(Value { reg, class, area, range: area.hull(), weight, size });
257 queue.push((size, Reverse(number)));
258 }
259
260 let mut state = State {
261 live,
262 blocked: &blocked,
263 reuses: &reuses,
264 values: &values,
265 held: Vec::new(),
266 pieces: Vec::new(),
267 at: vec![None; count],
268 commuted: vec![None; count],
269 saves: vec![Vec::new(); count],
270 work: 0,
271 budget,
272 asked: (None, Vec::new()),
273 };
274 let mut assignment = Assignment::empty(count);
275 let mut lost = vec![0u32; count];
276 let mut sent = Vec::new();
277 while let Some((_, Reverse(number))) = queue.pop() {
278 let Some(value) = values[number] else { continue };
279 if forced.contains(&value.reg) {
280 assignment.spill(value.reg, value.class);
281 continue;
282 }
283 if early.contains(&value.reg) {
284 sent.push(value);
285 continue;
286 }
287 assert!(
288 !env.order(value.class).is_empty(),
289 "a value in class {}, which the target hands out no registers from",
290 value.class.number()
291 );
292 let order = env.order(value.class);
302 let handed = |list: &[PhysReg]| -> Vec<PhysReg> {
303 list.iter().copied().filter(|at| order.contains(at)).collect()
304 };
305 let chosen = state
306 .coalesced(value, &reused[number])
307 .or_else(|| state.hinted(value, &handed(&hints[number]), Want::Allowed))
308 .or_else(|| {
309 let partners = passed[number].iter().chain(&received[number]);
310 let partners: Vec<PhysReg> =
311 partners.filter_map(|&other| state.reg_of(other)).collect();
312 state.hinted(value, &partners, Want::Allowed)
313 })
314 .or_else(|| state.swapped(value, &seconds[number]))
315 .or_else(|| {
316 let mut ties = reused[number].to_vec();
317 let source = reuses[number].and_then(|reuse| reuse.source.number());
318 ties.extend(source.and_then(|source| usize::try_from(source).ok()));
319 ties.retain(|&tie| state.at[tie].is_none() && values[tie].is_some());
320 let tied = ties.iter().flat_map(|&tie| handed(&hints[tie]));
321 let wanted: Vec<PhysReg> = tied.chain(order.iter().copied()).collect();
322 state.together(value, &ties, &wanted)
323 })
324 .or_else(|| state.hinted(value, env.order(value.class), Want::Allowed));
325 if state.work > state.budget {
326 return None;
327 }
328 if let Some(at) = chosen {
329 state.take(value, at);
330 continue;
331 }
332 let mut gone = Vec::new();
333 match state.cheapest(value, env.order(value.class)) {
334 Some(at) => {
335 for other in state.evict(value, at) {
336 lost[other] += 1;
337 let Some(evicted) = values[other] else { continue };
338 if lost[other] > ROUNDS {
339 gone.push(evicted);
340 } else {
341 queue.push((evicted.size, Reverse(other)));
342 }
343 }
344 state.take(value, at);
345 }
346 None => gone.push(value),
347 }
348 for last in gone {
351 let spilled = costs[index(last.reg)];
352 match state.saved(func, last, env.order(last.class), spilled, &saveable) {
353 Some((at, insts)) => {
354 state.take(last, at);
355 state.saves[index(last.reg)] = insts;
356 }
357 None => assignment.spill(last.reg, last.class),
358 }
359 }
360 if state.work > state.budget {
361 return None;
362 }
363 }
364 sent.sort_by_key(|value| Reverse(costs[index(value.reg)]));
371 for value in sent {
372 let spilled = costs[index(value.reg)];
373 match state.saved(func, value, env.order(value.class), spilled, &saveable) {
374 Some((at, insts)) => {
375 state.take(value, at);
376 state.saves[index(value.reg)] = insts;
377 }
378 None => assignment.spill(value.reg, value.class),
379 }
380 }
381 if state.work > state.budget {
382 return None;
383 }
384 state.settle(&reused, &passed, &received);
385 if state.work > state.budget {
386 return None;
387 }
388 for (number, at) in state.at.iter().enumerate() {
389 let Some(at) = *at else { continue };
390 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
391 assignment.put(reg, Place::Reg(at));
392 if let Some(value) = values[number] {
393 assignment.save(reg, value.class, &state.saves[number]);
394 }
395 }
396 for inst in state.commuted.iter().flatten() {
397 assignment.commute(*inst);
398 }
399 Some(assignment)
400}
401
402type Held = (usize, Range);
404
405struct State<'a, 'v> {
407 live: &'a Live,
408 blocked: &'a Blocks,
409 reuses: &'a [Option<Reuse>],
410 values: &'v [Option<Value<'a>>],
411 held: Vec<((RegClass, PhysReg), Vec<Held>)>,
415 pieces: Vec<Pieces>,
418 at: Vec<Option<PhysReg>>,
420 commuted: Vec<Option<Inst>>,
423 saves: Vec<Vec<Inst>>,
426 work: u64,
427 budget: u64,
428 asked: (Option<Reg>, Vec<[Option<bool>; 2]>),
433}
434
435impl<'a> State<'a, '_> {
436 fn reg_of(&self, reg: Reg) -> Option<PhysReg> {
437 self.at.get(usize::try_from(reg.number()?).ok()?).copied().flatten()
438 }
439
440 fn slot(&mut self, class: RegClass, at: PhysReg) -> usize {
441 let found = self.held.iter().position(|(key, _)| *key == (class, at));
442 found.unwrap_or_else(|| {
443 self.held.push(((class, at), Vec::new()));
444 self.pieces.push(Pieces::default());
445 self.held.len() - 1
446 })
447 }
448
449 fn remove(&mut self, index: usize, gone: &[usize]) {
451 self.held[index].1.retain(|(other, _)| !gone.contains(other));
452 let pieces = &mut self.pieces[index];
453 if pieces.kept {
454 for value in gone.iter().filter_map(|&other| self.values[other]) {
455 for piece in value.area.pieces() {
456 pieces.remove(piece, value.reg);
457 }
458 }
459 }
460 }
461
462 fn clashes(&mut self, value: Value<'_>, at: PhysReg) -> Vec<usize> {
465 let Some(found) = self.held.iter().position(|(key, _)| *key == (value.class, at)) else {
466 return Vec::new();
467 };
468 let held = &self.held[found].1;
469 self.work += held.len() as u64;
472 if held.len() > FEW {
473 let pieces = &mut self.pieces[found];
474 if !pieces.kept {
475 pieces.kept = true;
476 for other in held.iter().filter_map(|&(other, _)| self.values[other]) {
477 for piece in other.area.pieces() {
478 pieces.insert(piece, other.reg);
479 }
480 }
481 }
482 if let Some(owners) = pieces.owners(value.area) {
483 let clash = owners.into_iter().filter(|&other| !self.shares(value.reg, other));
484 return clash.map(index).collect();
485 }
486 }
487 let mut clashes = Vec::new();
488 for &(other, range) in held {
489 if !range.overlaps(value.range) {
490 continue;
491 }
492 let Some(held) = self.values[other] else { continue };
493 if !held.area.overlaps(value.area) {
494 continue;
495 }
496 if !self.shares(value.reg, held.reg) {
497 clashes.push(other);
498 }
499 }
500 clashes
501 }
502
503 fn shares(&self, one: Reg, two: Reg) -> bool {
506 let reads = |answer: Reg, source: Reg| {
507 let Some(number) = answer.number().and_then(|n| usize::try_from(n).ok()) else {
508 return false;
509 };
510 let Some(reuse) = self.reuses[number] else { return false };
511 match self.commuted[number] {
512 Some(_) => reuse.second == Some(source),
513 None => reuse.source == source,
514 }
515 };
516 (reads(one, two) || reads(two, one)) && assign::apart(self.live, one, two)
517 }
518
519 fn free(&mut self, value: Value<'_>, at: PhysReg, want: Want) -> bool {
520 !self.insists(value, at, want) && self.clashes(value, at).is_empty()
521 }
522
523 fn insists(&mut self, value: Value<'_>, at: PhysReg, want: Want) -> bool {
527 let (asked, answers) = &mut self.asked;
528 if *asked != Some(value.reg) {
529 *asked = Some(value.reg);
530 answers.fill([None; 2]);
531 }
532 let number = usize::from(at.number());
533 if answers.len() <= number {
534 answers.resize(number + 1, [None; 2]);
535 }
536 let slot = usize::from(want == Want::Clear);
537 if let Some(answer) = answers[number][slot] {
538 return answer;
539 }
540 let answer =
541 self.blocked.insists(value.reg, value.class, value.area, value.range, at, want);
542 let known = &mut answers[number];
543 known[slot] = Some(answer);
544 match (want, answer) {
545 (Want::Clear, false) => known[0] = Some(false),
546 (Want::Allowed, true) => known[1] = Some(true),
547 _ => {}
548 }
549 answer
550 }
551
552 fn hinted(&mut self, value: Value<'_>, wanted: &[PhysReg], want: Want) -> Option<PhysReg> {
554 wanted.iter().copied().find(|&at| self.work <= self.budget && self.free(value, at, want))
555 }
556
557 fn together(&mut self, value: Value<'_>, ties: &[usize], order: &[PhysReg]) -> Option<PhysReg> {
563 if ties.is_empty() {
564 return None;
565 }
566 for &at in order {
567 if self.work > self.budget {
568 return None;
569 }
570 if !self.free(value, at, Want::Clear) {
571 continue;
572 }
573 let values = self.values;
574 let mut tied = ties.iter().filter_map(|&tie| values[tie]);
575 if tied.all(|other| self.free(other, at, Want::Allowed)) {
576 return Some(at);
577 }
578 }
579 None
580 }
581
582 fn coalesced(&mut self, value: Value<'_>, answers: &[usize]) -> Option<PhysReg> {
588 let number = index(value.reg);
589 if let Some(reuse) = self.reuses[number] {
590 if let Some(at) = self.reg_of(reuse.source) {
591 if self.free(value, at, Want::Allowed) {
592 return Some(at);
593 }
594 }
595 if let Some(second) = reuse.second {
596 if let Some(at) = self.reg_of(second) {
597 self.commuted[number] = Some(reuse.inst);
598 if self.free(value, at, Want::Allowed) {
599 return Some(at);
600 }
601 self.commuted[number] = None;
602 }
603 }
604 }
605 for &answer in answers {
606 let Some(at) = self.at[answer] else { continue };
607 if self.commuted[answer].is_none() && self.free(value, at, Want::Allowed) {
608 return Some(at);
609 }
610 }
611 None
612 }
613
614 fn swapped(&mut self, value: Value<'_>, seconds: &[usize]) -> Option<PhysReg> {
620 if self.reuses[index(value.reg)].is_some() {
624 return None;
625 }
626 for &answer in seconds {
627 let (Some(at), Some(reuse)) = (self.at[answer], self.reuses[answer]) else { continue };
628 let Some(written) = self.values[answer] else { continue };
629 if self.commuted[answer].is_some()
633 || self.reg_of(reuse.source) == Some(at)
634 || assign::apart(self.live, written.reg, reuse.source)
635 {
636 continue;
637 }
638 self.commuted[answer] = Some(reuse.inst);
639 if self.free(value, at, Want::Allowed) {
640 return Some(at);
641 }
642 self.commuted[answer] = None;
643 }
644 None
645 }
646
647 fn cheapest(&mut self, value: Value<'_>, order: &[PhysReg]) -> Option<PhysReg> {
650 let mut best: Option<(u128, u128, PhysReg)> = None;
651 for &at in order {
652 if self.insists(value, at, Want::Allowed) {
653 continue;
654 }
655 let clashes = self.clashes(value, at);
656 let weights = clashes.iter().filter_map(|&other| self.values[other]).map(|v| v.weight);
657 let (heaviest, total) = weights.fold((0, 0), |(most, sum), w| (most.max(w), sum + w));
658 if heaviest >= value.weight {
659 continue;
660 }
661 if best.is_none_or(|(most, sum, _)| (heaviest, total) < (most, sum)) {
662 best = Some((heaviest, total, at));
663 }
664 }
665 best.map(|(_, _, at)| at)
666 }
667
668 fn evict(&mut self, value: Value<'_>, at: PhysReg) -> Vec<usize> {
670 let clashes = self.clashes(value, at);
671 let index = self.slot(value.class, at);
672 self.remove(index, &clashes);
673 for &other in &clashes {
674 self.at[other] = None;
675 self.commuted[other] = None;
676 self.saves[other].clear();
677 }
678 clashes
679 }
680
681 fn saved(
688 &mut self,
689 func: &Func,
690 value: Value<'_>,
691 order: &[PhysReg],
692 spilled: u128,
693 saveable: &Map<Point, (Inst, u128)>,
694 ) -> Option<(PhysReg, Vec<Inst>)> {
695 let mut best: Option<(u128, PhysReg, Vec<Inst>)> = None;
696 for &at in order {
697 if self.work > self.budget {
698 return None;
699 }
700 let found = self.blocked.destroyed(
701 value.reg,
702 value.class,
703 value.area,
704 value.range,
705 at,
706 |point| saveable.contains_key(&point),
707 );
708 let Some(points) = found else { continue };
709 let mut insts = Vec::new();
710 let mut spent = Some(0u128);
711 for point in &points {
712 let Some(&(inst, weight)) = saveable.get(point) else { continue };
713 let written = func[func[inst].operands]
714 .iter()
715 .any(|operand| operand.reg == value.reg && operand.role.is_def());
716 spent = spent.filter(|_| !written).map(|spent| spent + 2 * weight);
717 insts.push(inst);
718 }
719 let Some(spent) = spent else { continue };
720 if spent >= spilled || best.as_ref().is_some_and(|&(least, _, _)| least <= spent) {
721 continue;
722 }
723 if self.clashes(value, at).is_empty() {
724 best = Some((spent, at, insts));
725 }
726 }
727 best.map(|(_, at, insts)| (at, insts))
728 }
729
730 fn settle(&mut self, reused: &Answers, passed: &[Vec<Reg>], received: &[Vec<Reg>]) {
739 let mut moved = true;
740 while moved && self.work <= self.budget {
741 moved = false;
742 for number in 0..self.at.len() {
743 let (Some(value), Some(now)) = (self.values[number], self.at[number]) else {
744 continue;
745 };
746 let reuse = self.reuses[number];
747 let source = reuse.and_then(|reuse| self.reg_of(reuse.source));
748 let second =
749 reuse.and_then(|reuse| reuse.second).and_then(|second| self.reg_of(second));
750 let mut wanted: Vec<PhysReg> = source.into_iter().chain(second).collect();
751 for &answer in &reused[number] {
752 if self.commuted[answer].is_none() {
753 wanted.extend(self.at[answer]);
754 }
755 }
756 let partners = passed[number].iter().chain(&received[number]);
757 wanted.extend(partners.filter_map(|&other| self.reg_of(other)));
758 let met = |at: PhysReg| wanted.iter().filter(|&®| reg == at).count();
759 let here = met(now);
760 let mut better: Vec<(usize, PhysReg)> = Vec::new();
761 for &at in &wanted {
762 let count = met(at);
763 if count > here && !better.contains(&(count, at)) {
764 better.push((count, at));
765 }
766 }
767 if better.is_empty() {
768 continue;
769 }
770 better.sort_by_key(|&(count, _)| Reverse(count));
771 let index = self.slot(value.class, now);
772 self.remove(index, &[number]);
773 self.at[number] = None;
774 let was = self.commuted[number];
775 let mut to = now;
776 for (_, at) in better {
777 self.commuted[number] = match reuse {
778 Some(reuse) if second == Some(at) && source != Some(at) => Some(reuse.inst),
779 _ => None,
780 };
781 if self.free(value, at, Want::Allowed) {
782 to = at;
783 moved = true;
784 break;
785 }
786 }
787 if to == now {
788 self.commuted[number] = was;
789 } else {
790 self.saves[number].clear();
791 }
792 self.take(value, to);
793 }
794 }
795 }
796
797 fn take(&mut self, value: Value<'_>, at: PhysReg) {
798 let number = index(value.reg);
799 self.at[number] = Some(at);
800 let index = self.slot(value.class, at);
801 self.held[index].1.push((number, value.range));
802 let pieces = &mut self.pieces[index];
803 if pieces.kept {
804 for piece in value.area.pieces() {
805 pieces.insert(piece, value.reg);
806 }
807 }
808 }
809}
810
811fn saveable(func: &Func, order: &Order) -> Map<Point, (Inst, u128)> {
818 let mut saveable = Map::default();
819 for block in func.blocks() {
820 let weight = u128::from(func[block].weight.raw().max(1));
821 let last = if func[block].succs.len() > 1 { func.insts(block).last() } else { None };
822 for inst in func.insts(block) {
823 if Some(inst) != last {
824 saveable.insert(order.late(inst), (inst, weight));
825 }
826 }
827 }
828 saveable
829}
830
831pub(crate) fn size(area: Area<'_>) -> u32 {
833 area.pieces().map(|piece| piece.end - piece.start + 1).sum()
834}
835
836pub(crate) fn costs(func: &Func) -> Vec<u128> {
841 let mut costs = vec![0u128; func.vregs()];
842 let mut add = |reg: Reg, weight: u128| {
843 let number = reg.number().and_then(|number| usize::try_from(number).ok());
844 if let Some(cost) = number.and_then(|number| costs.get_mut(number)) {
845 *cost += weight;
846 }
847 };
848 for block in func.blocks() {
849 let weight = u128::from(func[block].weight.raw().max(1));
850 for param in &func[block].params {
851 add(param.reg, weight);
852 }
853 for inst in func.insts(block) {
854 for operand in &func[func[inst].operands] {
855 add(operand.reg, weight);
856 }
857 }
858 for call in &func[block].succs {
859 for &arg in &call.args {
860 add(arg, weight);
861 }
862 }
863 }
864 costs
865}
866
867fn received(func: &Func) -> Vec<Vec<Reg>> {
869 let mut received = vec![Vec::new(); func.vregs()];
870 for block in func.blocks() {
871 for call in &func[block].succs {
872 for (&arg, param) in call.args.iter().zip(&func[call.block].params) {
873 let number = param.reg.number().and_then(|number| usize::try_from(number).ok());
874 let Some(from) = number.and_then(|number| received.get_mut(number)) else {
875 continue;
876 };
877 if !from.contains(&arg) {
878 from.push(arg);
879 }
880 }
881 }
882 }
883 received
884}
885
886struct Answers {
891 starts: Vec<usize>,
892 all: Vec<usize>,
893}
894
895impl std::ops::Index<usize> for Answers {
896 type Output = [usize];
897
898 fn index(&self, number: usize) -> &[usize] {
899 &self.all[self.starts[number]..self.starts[number + 1]]
900 }
901}
902
903fn answers(reuses: &[Option<Reuse>], of: impl Fn(Reuse) -> Option<Reg>) -> Answers {
905 let number = |reuse: &Option<Reuse>| {
906 let reg = reuse.and_then(&of)?;
907 let number = usize::try_from(reg.number()?).ok()?;
908 (number < reuses.len()).then_some(number)
909 };
910 let mut starts = vec![0; reuses.len() + 1];
913 for reuse in reuses {
914 if let Some(number) = number(reuse) {
915 starts[number] += 1;
916 }
917 }
918 let mut total = 0;
919 for start in &mut starts {
920 total += *start;
921 *start = total;
922 }
923 let mut all = vec![0; total];
924 for (answer, reuse) in reuses.iter().enumerate().rev() {
925 if let Some(number) = number(reuse) {
926 starts[number] -= 1;
927 all[starts[number]] = answer;
928 }
929 }
930 Answers { starts, all }
931}
932
933fn reused(reuses: &[Option<Reuse>]) -> Answers {
935 answers(reuses, |reuse| Some(reuse.source))
936}
937
938fn seconds(reuses: &[Option<Reuse>]) -> Answers {
941 answers(reuses, |reuse| reuse.second)
942}
943
944fn index(reg: Reg) -> usize {
945 usize::try_from(reg.number().expect("a virtual register")).expect("a register number")
946}
947
948#[cfg(test)]
949mod tests {
950 use rucc_base::Interner;
951 use rucc_mir::{BlockCall, Constraint, Flags, Opcode, Operand, Param};
952 use rucc_target::x86_64::{GPR, RAX, RCX, REGS, SYSV};
953
954 use super::*;
955 use crate::check;
956
957 fn env() -> Env {
958 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
959 Env::new().with(GPR, order, scratch)
960 }
961
962 fn narrow(count: usize) -> Env {
963 Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 1])
964 }
965
966 fn named(place: Option<Place>) -> String {
967 match place {
968 Some(Place::Reg(reg)) => REGS.name(GPR, reg).expect("a register").to_string(),
969 Some(Place::Slot(_)) => "slot".to_string(),
970 None => "nowhere".to_string(),
971 }
972 }
973
974 fn places(func: &mut Func, env: &Env) -> Vec<String> {
976 let order = Order::of(func);
977 let live = Live::of(func, &order);
978 let assignment = within(func, &order, &live, env, BUDGET).expect("inside the budget");
979 for &inst in assignment.commuted() {
982 let list = func[inst].operands;
983 func[list].swap(1, 2);
984 }
985 let problems = check::check(func, &order, &live, &assignment);
986 assert!(problems.is_empty(), "{}", check::report(&problems));
987 (0..func.vregs())
988 .map(|number| {
989 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
990 named(assignment.place(reg))
991 })
992 .collect()
993 }
994
995 fn linear(func: &Func, env: &Env) -> Vec<String> {
996 let order = Order::of(func);
997 let live = Live::of(func, &order);
998 let assignment = assign::assign(func, &order, &live, env);
999 (0..func.vregs())
1000 .map(|number| {
1001 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
1002 named(assignment.place(reg))
1003 })
1004 .collect()
1005 }
1006
1007 #[test]
1008 fn a_value_the_spill_phase_picked_goes_to_the_stack_and_the_rest_fit() {
1009 let mut names = Interner::new();
1010 let mut func = Func::new(names.intern("f"));
1011 let opcode = Opcode::new(names.intern("x64.nop"));
1012 let block = func.create_block();
1013 let busy = func.new_vreg(GPR);
1014 let once = func.new_vreg(GPR);
1015 let other = func.new_vreg(GPR);
1016 func.build(block, opcode).def(busy, GPR).finish();
1017 func.build(block, opcode).def(once, GPR).finish();
1018 func.build(block, opcode).def(other, GPR).finish();
1019 for _ in 0..3 {
1020 func.build(block, opcode).uses(busy, GPR).uses(other, GPR).finish();
1021 }
1022 func.build(block, opcode).uses(once, GPR).uses(busy, GPR).uses(other, GPR).finish();
1023
1024 let env = narrow(2);
1025 let order = Order::of(&func);
1026 let live = Live::of(&func, &order);
1027 let pressure = Pressure::of(&func, &order, &live, &env);
1028 let early = spill::choose(&func, &live, &pressure);
1029 assert_eq!(early, [once]);
1030 let assignment = placed(&func, &order, &live, &env, BUDGET, &early).expect("in budget");
1031 let problems = check::check(&func, &order, &live, &assignment);
1032 assert!(problems.is_empty(), "{}", check::report(&problems));
1033 assert_eq!(named(assignment.place(once)), "slot");
1034 assert_eq!(assignment.spilled(), 1);
1035 }
1036
1037 #[test]
1040 fn a_value_sent_ahead_is_put_away_around_the_call_the_loop_seldom_makes() {
1041 let mut names = Interner::new();
1042 let mut func = Func::new(names.intern("f"));
1043 let opcode = Opcode::new(names.intern("x64.nop"));
1044 let [entry, head, cold, skip, latch, back, out] = [(); 7].map(|()| func.create_block());
1045 let step = func.new_vreg(GPR);
1046 func.build(entry, opcode).def(step, GPR).finish();
1047 *func.succs_mut(entry) = vec![BlockCall::to(head)];
1048 func.build(head, opcode).uses(step, GPR).finish();
1049 *func.succs_mut(head) = vec![BlockCall::to(cold), BlockCall::to(skip)];
1050 let call = func
1052 .build(cold, opcode)
1053 .operand(Operand::write(Reg::physical(RAX), GPR))
1054 .operand(Operand::write(Reg::physical(RCX), GPR))
1055 .finish();
1056 *func.succs_mut(cold) = vec![BlockCall::to(latch)];
1057 *func.succs_mut(skip) = vec![BlockCall::to(latch)];
1058 func.build(latch, opcode).uses(step, GPR).finish();
1059 *func.succs_mut(latch) = vec![BlockCall::to(back), BlockCall::to(out)];
1060 *func.succs_mut(back) = vec![BlockCall::to(head)];
1061 func.build(out, opcode).uses(step, GPR).finish();
1062 for (block, often) in [(head, 100), (skip, 99), (latch, 100), (back, 99)] {
1063 func.set_weight(block, rucc_mir::Weight::parts(often * rucc_mir::Weight::SCALE));
1064 }
1065
1066 let env = narrow(2);
1067 let order = Order::of(&func);
1068 let live = Live::of(&func, &order);
1069 let assignment = placed(&func, &order, &live, &env, BUDGET, &[step]).expect("in budget");
1070 let problems = check::check(&func, &order, &live, &assignment);
1071 assert!(problems.is_empty(), "{}", check::report(&problems));
1072 assert_ne!(named(assignment.place(step)), "slot");
1073 let saves = assignment.saves();
1074 assert_eq!(saves.len(), 1);
1075 assert_eq!((saves[0].reg, saves[0].inst), (step, call));
1076 }
1077
1078 #[test]
1079 fn two_values_that_are_never_both_wanted_share_a_register() {
1080 let mut names = Interner::new();
1081 let mut func = Func::new(names.intern("f"));
1082 let opcode = Opcode::new(names.intern("x64.nop"));
1083 let block = func.create_block();
1084 let first = func.new_vreg(GPR);
1085 let second = func.new_vreg(GPR);
1086 func.build(block, opcode).def(first, GPR).finish();
1087 func.build(block, opcode).uses(first, GPR).finish();
1088 func.build(block, opcode).def(second, GPR).finish();
1089 func.build(block, opcode).uses(second, GPR).finish();
1090
1091 assert_eq!(places(&mut func, &env()), ["rax", "rax"]);
1092 }
1093
1094 #[test]
1095 fn the_value_read_most_often_keeps_its_register_though_it_is_wanted_longest() {
1096 let mut names = Interner::new();
1097 let mut func = Func::new(names.intern("f"));
1098 let opcode = Opcode::new(names.intern("x64.nop"));
1099 let block = func.create_block();
1100 let busy = func.new_vreg(GPR);
1101 let once = func.new_vreg(GPR);
1102 func.build(block, opcode).def(busy, GPR).finish();
1103 func.build(block, opcode).def(once, GPR).finish();
1104 for _ in 0..6 {
1105 func.build(block, opcode).uses(busy, GPR).finish();
1106 }
1107 func.build(block, opcode).uses(once, GPR).finish();
1108 func.build(block, opcode).uses(busy, GPR).finish();
1109
1110 assert_eq!(linear(&func, &narrow(1)), ["slot", "rax"]);
1113 assert_eq!(places(&mut func, &narrow(1)), ["rax", "slot"]);
1114 }
1115
1116 #[test]
1117 fn a_value_read_in_a_loop_keeps_its_register_over_one_read_outside_it() {
1118 let mut names = Interner::new();
1119 let mut func = Func::new(names.intern("f"));
1120 let opcode = Opcode::new(names.intern("x64.nop"));
1121 let entry = func.create_block();
1122 let body = func.create_block();
1123 let out = func.create_block();
1124 let step = func.new_vreg(GPR);
1125 let cold = func.new_vreg(GPR);
1126 func.build(entry, opcode).def(cold, GPR).finish();
1127 func.build(entry, opcode).def(step, GPR).finish();
1128 *func.succs_mut(entry) = vec![BlockCall::to(body)];
1129 func.build(body, opcode).uses(step, GPR).finish();
1130 *func.succs_mut(body) = vec![BlockCall::to(body), BlockCall::to(out)];
1131 func.set_weight(body, rucc_mir::Weight::parts(100 * rucc_mir::Weight::SCALE));
1132 func.build(out, opcode).uses(cold, GPR).finish();
1133 func.build(out, opcode).uses(step, GPR).finish();
1134
1135 assert_eq!(places(&mut func, &narrow(1)), ["rax", "slot"]);
1138 }
1139
1140 #[test]
1141 fn a_long_value_gives_its_register_back_to_a_short_busy_one() {
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 long = func.new_vreg(GPR);
1147 let short = func.new_vreg(GPR);
1148 func.build(block, opcode).def(long, GPR).finish();
1149 for _ in 0..4 {
1150 func.build(block, opcode).finish();
1151 }
1152 func.build(block, opcode).def(short, GPR).finish();
1153 for _ in 0..4 {
1154 func.build(block, opcode).uses(short, GPR).finish();
1155 }
1156 func.build(block, opcode).uses(long, GPR).finish();
1157
1158 assert_eq!(places(&mut func, &narrow(1)), ["slot", "rax"]);
1161 }
1162
1163 #[test]
1164 fn the_answer_of_a_two_address_instruction_goes_where_its_source_ends() {
1165 let mut names = Interner::new();
1166 let mut func = Func::new(names.intern("f"));
1167 let opcode = Opcode::new(names.intern("x64.nop"));
1168 let block = func.create_block();
1169 let left = func.new_vreg(GPR);
1170 let right = func.new_vreg(GPR);
1171 let sum = func.new_vreg(GPR);
1172 func.build(block, opcode).def(left, GPR).finish();
1173 func.build(block, opcode).def(right, GPR).finish();
1174 func.build(block, opcode)
1175 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1176 .uses(left, GPR)
1177 .uses(right, GPR)
1178 .finish();
1179 func.build(block, opcode).uses(right, GPR).finish();
1180 func.build(block, opcode).uses(sum, GPR).finish();
1181
1182 let places = places(&mut func, &env());
1183 assert_eq!(places[2], places[0]);
1184 assert_ne!(places[1], places[0]);
1185 }
1186
1187 #[test]
1188 fn a_source_placed_after_its_answer_goes_where_the_answer_is() {
1189 let mut names = Interner::new();
1190 let mut func = Func::new(names.intern("f"));
1191 let opcode = Opcode::new(names.intern("x64.nop"));
1192 let block = func.create_block();
1193 let left = func.new_vreg(GPR);
1194 let sum = func.new_vreg(GPR);
1195 func.build(block, opcode).def(left, GPR).finish();
1196 func.build(block, opcode)
1197 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1198 .uses(left, GPR)
1199 .finish();
1200 for _ in 0..6 {
1201 func.build(block, opcode).uses(sum, GPR).finish();
1202 }
1203
1204 let places = places(&mut func, &env());
1206 assert_eq!(places[0], places[1]);
1207 }
1208
1209 #[test]
1210 fn an_answer_that_commutes_stays_where_the_loop_passes_it() {
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 entry = func.create_block();
1215 let head = func.create_block();
1216 let out = func.create_block();
1217 let seed = func.new_vreg(GPR);
1218 let total = func.new_vreg(GPR);
1219 let term = func.new_vreg(GPR);
1220 let next = func.new_vreg(GPR);
1221 func.build(entry, opcode).def(seed, GPR).finish();
1222 *func.succs_mut(entry) = vec![BlockCall::with(head, vec![seed])];
1223 func.params_mut(head).push(Param { reg: total, class: GPR });
1224 func.build(head, opcode).def(term, GPR).finish();
1225 func.build(head, opcode)
1226 .flags(Flags::COMMUTES)
1227 .operand(Operand::write(next, GPR).with(Constraint::Reuse(1)))
1228 .uses(total, GPR)
1229 .uses(term, GPR)
1230 .finish();
1231 *func.succs_mut(head) = vec![BlockCall::with(head, vec![next]), BlockCall::to(out)];
1232
1233 let places = places(&mut func, &env());
1234 assert_eq!(places[3], places[1]);
1235 }
1236
1237 #[test]
1238 fn an_answer_whose_first_source_lives_on_goes_where_the_second_one_ends() {
1239 let mut names = Interner::new();
1240 let mut func = Func::new(names.intern("f"));
1241 let opcode = Opcode::new(names.intern("x64.nop"));
1242 let block = func.create_block();
1243 let base = func.new_vreg(GPR);
1244 let entry = func.new_vreg(GPR);
1245 let target = func.new_vreg(GPR);
1246 func.build(block, opcode).def(base, GPR).finish();
1247 func.build(block, opcode).def(entry, GPR).finish();
1248 func.build(block, opcode)
1249 .flags(Flags::COMMUTES)
1250 .operand(Operand::write(target, GPR).with(Constraint::Reuse(1)))
1251 .uses(base, GPR)
1252 .uses(entry, GPR)
1253 .finish();
1254 func.build(block, opcode).uses(target, GPR).finish();
1255 func.build(block, opcode).uses(base, GPR).finish();
1256
1257 let places = places(&mut func, &env());
1258 assert_eq!(places[2], places[1]);
1259 assert_ne!(places[2], places[0]);
1260 }
1261
1262 #[test]
1263 fn an_offset_added_to_a_base_the_loop_reads_again_goes_where_the_answer_went() {
1264 let mut names = Interner::new();
1265 let mut func = Func::new(names.intern("f"));
1266 let opcode = Opcode::new(names.intern("x64.nop"));
1267 let entry = func.create_block();
1268 let head = func.create_block();
1269 let out = func.create_block();
1270 let base = func.new_vreg(GPR);
1271 let at = func.new_vreg(GPR);
1272 let offset = func.new_vreg(GPR);
1273 let target = func.new_vreg(GPR);
1274 let later = func.new_vreg(GPR);
1275 func.build(entry, opcode).def(base, GPR).finish();
1276 *func.succs_mut(entry) = vec![BlockCall::to(head)];
1277 func.build(head, opcode).def(at, GPR).finish();
1278 func.build(head, opcode).def(offset, GPR).uses(base, GPR).uses(at, GPR).finish();
1279 func.build(head, opcode)
1280 .flags(Flags::COMMUTES)
1281 .operand(Operand::write(target, GPR).with(Constraint::Reuse(1)))
1282 .uses(base, GPR)
1283 .uses(offset, GPR)
1284 .finish();
1285 func.build(head, opcode).def(later, GPR).finish();
1286 func.build(head, opcode).uses(target, GPR).finish();
1287 for _ in 0..4 {
1288 func.build(head, opcode).finish();
1289 }
1290 func.build(head, opcode).uses(later, GPR).finish();
1291 *func.succs_mut(head) = vec![BlockCall::to(head), BlockCall::to(out)];
1292
1293 let places = places(&mut func, &env());
1299 assert_eq!(places[3], places[2]);
1300 assert_ne!(places[3], places[0]);
1301 }
1302
1303 #[test]
1304 fn a_register_an_instruction_insists_on_is_left_to_the_value_it_names() {
1305 let mut names = Interner::new();
1306 let mut func = Func::new(names.intern("f"));
1307 let opcode = Opcode::new(names.intern("x64.nop"));
1308 let block = func.create_block();
1309 let kept = func.new_vreg(GPR);
1310 let passed = func.new_vreg(GPR);
1311 func.build(block, opcode).def(kept, GPR).finish();
1312 func.build(block, opcode).def(passed, GPR).finish();
1313 func.build(block, opcode)
1314 .operand(Operand::read(passed, GPR).with(Constraint::Fixed(RAX)))
1315 .finish();
1316 func.build(block, opcode).uses(kept, GPR).finish();
1317
1318 let places = places(&mut func, &env());
1319 assert_eq!(places[1], "rax");
1320 assert_ne!(places[0], "rax");
1321 }
1322
1323 #[test]
1324 fn a_value_only_memory_can_hold_goes_to_the_stack() {
1325 let mut names = Interner::new();
1326 let mut func = Func::new(names.intern("f"));
1327 let opcode = Opcode::new(names.intern("x64.nop"));
1328 let block = func.create_block();
1329 let value = func.new_vreg(GPR);
1330 func.build(block, opcode).def(value, GPR).finish();
1331 func.build(block, opcode)
1332 .operand(Operand::read(value, GPR).with(Constraint::Stack))
1333 .finish();
1334
1335 assert_eq!(places(&mut func, &env()), ["slot"]);
1336 }
1337
1338 #[test]
1339 fn a_function_over_the_budget_gets_the_linear_scan_answer() {
1340 let mut names = Interner::new();
1341 let mut func = Func::new(names.intern("f"));
1342 let opcode = Opcode::new(names.intern("x64.nop"));
1343 let block = func.create_block();
1344 let busy = func.new_vreg(GPR);
1345 let once = func.new_vreg(GPR);
1346 func.build(block, opcode).def(busy, GPR).finish();
1347 func.build(block, opcode).def(once, GPR).finish();
1348 for _ in 0..6 {
1349 func.build(block, opcode).uses(busy, GPR).finish();
1350 }
1351 func.build(block, opcode).uses(once, GPR).finish();
1352 func.build(block, opcode).uses(busy, GPR).finish();
1353
1354 let order = Order::of(&func);
1355 let live = Live::of(&func, &order);
1356 assert!(within(&func, &order, &live, &narrow(1), 0).is_none());
1357 let fallen = linear(&func, &narrow(1));
1358 assert_eq!(fallen, ["slot", "rax"]);
1359 }
1360
1361 #[test]
1362 fn the_cheaper_of_the_two_answers_is_the_one_kept() {
1363 let mut names = Interner::new();
1364 let mut func = Func::new(names.intern("f"));
1365 let opcode = Opcode::new(names.intern("x64.nop"));
1366 let block = func.create_block();
1367 let busy = func.new_vreg(GPR);
1368 let once = func.new_vreg(GPR);
1369 func.build(block, opcode).def(busy, GPR).finish();
1370 func.build(block, opcode).def(once, GPR).finish();
1371 for _ in 0..6 {
1372 func.build(block, opcode).uses(busy, GPR).finish();
1373 }
1374 func.build(block, opcode).uses(once, GPR).finish();
1375 func.build(block, opcode).uses(busy, GPR).finish();
1376
1377 let order = Order::of(&func);
1378 let live = Live::of(&func, &order);
1379 let env = narrow(1);
1380 let linear = assign::assign(&func, &order, &live, &env);
1381 let ours = within(&func, &order, &live, &env, BUDGET).expect("inside the budget");
1382 let once_through = u128::from(func[block].weight.raw());
1385 assert_eq!(cost(&func, &order, &linear), 8 * once_through);
1386 assert_eq!(cost(&func, &order, &ours), 2 * once_through);
1387 let kept = assign(&func, &order, &live, &env);
1388 assert_eq!(kept.place(busy), ours.place(busy));
1389 assert_eq!(kept.place(once), ours.place(once));
1390 }
1391
1392 #[test]
1393 fn a_copy_left_between_a_two_address_answer_and_its_source_is_counted() {
1394 let mut names = Interner::new();
1395 let mut func = Func::new(names.intern("f"));
1396 let opcode = Opcode::new(names.intern("x64.nop"));
1397 let block = func.create_block();
1398 let left = func.new_vreg(GPR);
1399 let right = func.new_vreg(GPR);
1400 let sum = func.new_vreg(GPR);
1401 func.build(block, opcode).def(left, GPR).finish();
1402 func.build(block, opcode).def(right, GPR).finish();
1403 func.build(block, opcode)
1404 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1405 .uses(left, GPR)
1406 .uses(right, GPR)
1407 .finish();
1408 func.build(block, opcode).uses(sum, GPR).finish();
1409
1410 let order = Order::of(&func);
1411 let live = Live::of(&func, &order);
1412 let chosen = assign(&func, &order, &live, &env());
1413 assert_eq!(cost(&func, &order, &chosen), 0);
1414 let mut apart = chosen.clone();
1415 apart.put(sum, chosen.place(right).expect("a place for right"));
1416 assert_eq!(cost(&func, &order, &apart), u128::from(func[block].weight.raw()));
1417 }
1418
1419 fn around_calls(calls: usize, reads: usize) -> Vec<String> {
1422 let mut names = Interner::new();
1423 let mut func = Func::new(names.intern("f"));
1424 let opcode = Opcode::new(names.intern("x64.nop"));
1425 let block = func.create_block();
1426 let value = func.new_vreg(GPR);
1427 func.build(block, opcode).def(value, GPR).finish();
1428 for _ in 0..calls {
1429 func.build(block, opcode)
1430 .operand(Operand::write(Reg::physical(RAX), GPR))
1431 .operand(Operand::write(Reg::physical(RCX), GPR))
1432 .finish();
1433 }
1434 for _ in 0..reads {
1435 func.build(block, opcode).uses(value, GPR).finish();
1436 }
1437 places(&mut func, &narrow(2))
1438 }
1439
1440 #[test]
1441 fn a_value_is_put_away_around_a_call_only_when_that_is_cheaper_than_the_stack() {
1442 assert_eq!(around_calls(1, 10), ["rax"]);
1444 assert_eq!(around_calls(3, 1), ["slot"]);
1446 }
1447
1448 #[test]
1449 fn many_values_in_few_registers_come_out_as_something_the_machine_can_run() {
1450 let mut names = Interner::new();
1451 let mut func = Func::new(names.intern("f"));
1452 let opcode = Opcode::new(names.intern("x64.nop"));
1453 let block = func.create_block();
1454 let regs: Vec<Reg> = (0..24).map(|_| func.new_vreg(GPR)).collect();
1455 for ® in ®s {
1456 func.build(block, opcode).def(reg, GPR).finish();
1457 }
1458 for (at, ®) in regs.iter().enumerate().rev() {
1459 for _ in 0..(at % 5) {
1460 func.build(block, opcode).uses(reg, GPR).finish();
1461 }
1462 func.build(block, opcode).uses(reg, GPR).finish();
1463 }
1464
1465 let places = places(&mut func, &narrow(4));
1466 assert_eq!(places.iter().filter(|place| *place != "slot").count(), 4);
1467 }
1468}