1use std::cmp::Reverse;
75use std::collections::{BinaryHeap, HashMap, HashSet};
76
77use rucc_mir::{Func, Inst, Reg};
78use rucc_target::{PhysReg, RegClass};
79
80use crate::assign::{self, Assignment, Blocks, Env, Place, Reuse, Want};
81use crate::live::{Area, Live, Range};
82use crate::order::Order;
83use crate::pressure::Pressure;
84use crate::spill;
85
86pub const BUDGET: u64 = 50_000_000;
89
90pub const ROUNDS: u32 = 4;
92
93#[derive(Debug, Clone, Copy)]
95struct Value<'a> {
96 reg: Reg,
97 class: RegClass,
98 area: Area<'a>,
99 range: Range,
100 weight: u128,
101 size: u32,
102}
103
104#[must_use]
110pub fn assign(func: &Func, order: &Order, live: &Live, env: &Env) -> Assignment {
111 let linear = assign::assign(func, order, live, env);
112 let mut best = cost(func, order, &linear);
113 let mut kept = linear;
114 let pressure = Pressure::of(func, order, live, env);
115 let spilled = spill::choose(func, live, &pressure);
116 let tries: &[&[Reg]] = if spilled.is_empty() { &[&[]] } else { &[&[], &spilled] };
118 for &early in tries {
119 let Some(ours) = placed(func, order, live, env, BUDGET, early) else { break };
120 let spent = cost(func, order, &ours);
121 if spent <= best {
122 best = spent;
123 kept = ours;
124 }
125 }
126 kept
127}
128
129#[must_use]
143pub fn cost(func: &Func, order: &Order, assignment: &Assignment) -> u128 {
144 let costs = costs(func);
145 let mut total = 0;
146 for (reg, place) in assignment.placed() {
147 if !matches!(place, Place::Reg(_)) {
148 total += costs[index(reg)];
149 }
150 }
151 let mut weights = HashMap::new();
152 for block in func.blocks() {
153 let weight = u128::from(func[block].weight.raw().max(1));
154 for inst in func.insts(block) {
155 weights.insert(inst, weight);
156 }
157 for call in &func[block].succs {
158 for (&arg, param) in call.args.iter().zip(&func[call.block].params) {
159 if assignment.place(arg) != assignment.place(param.reg) {
160 total += weight;
161 }
162 }
163 }
164 }
165 let commuted: HashSet<Inst> = assignment.commuted().iter().copied().collect();
166 for (number, reuse) in assign::reuses(func, order).iter().enumerate() {
167 let Some(reuse) = reuse else { continue };
168 let answer = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
169 let tied = if commuted.contains(&reuse.inst) { reuse.second } else { Some(reuse.source) };
170 let Some(at) = assignment.place(answer) else { continue };
171 if tied.and_then(|tied| assignment.place(tied)) != Some(at) {
172 total += weights.get(&reuse.inst).copied().unwrap_or(1);
173 }
174 }
175 total
176}
177
178#[must_use]
185pub fn within(
186 func: &Func,
187 order: &Order,
188 live: &Live,
189 env: &Env,
190 budget: u64,
191) -> Option<Assignment> {
192 placed(func, order, live, env, budget, &[])
193}
194
195fn placed(
196 func: &Func,
197 order: &Order,
198 live: &Live,
199 env: &Env,
200 budget: u64,
201 early: &[Reg],
202) -> Option<Assignment> {
203 let blocked = assign::blocked(func, order);
204 let forced = assign::forced(func);
205 let reuses = assign::reuses(func, order);
206 let hints = assign::hints(func);
207 let passed = assign::passed(func);
208 let received = received(func);
209 let reused = reused(&reuses);
210 let costs = costs(func);
211
212 let count = func.vregs();
213 let mut values: Vec<Option<Value<'_>>> = vec![None; count];
214 let mut queue = BinaryHeap::new();
215 for (number, reuse) in reuses.iter().enumerate() {
216 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
217 let (Some(mut area), Some(class)) = (live.area(reg), func.class_of(reg)) else {
218 continue;
219 };
220 if let Some(reuse) = reuse {
221 area = area.with(reuse.at);
222 }
223 let size = size(area);
224 let weight = costs[number] * 1024 / u128::from(size + 8);
225 values[number] = Some(Value { reg, class, area, range: area.hull(), weight, size });
226 queue.push((size, Reverse(number)));
227 }
228
229 let mut state = State {
230 live,
231 blocked: &blocked,
232 reuses: &reuses,
233 values: &values,
234 held: Vec::new(),
235 at: vec![None; count],
236 commuted: vec![None; count],
237 work: 0,
238 budget,
239 };
240 let mut assignment = Assignment::empty(count);
241 let mut lost = vec![0u32; count];
242 while let Some((_, Reverse(number))) = queue.pop() {
243 let Some(value) = values[number] else { continue };
244 if forced.contains(&value.reg) || early.contains(&value.reg) {
245 assignment.spill(value.reg, value.class);
246 continue;
247 }
248 assert!(
249 !env.order(value.class).is_empty(),
250 "a value in class {}, which the target hands out no registers from",
251 value.class.number()
252 );
253 let chosen = state
254 .coalesced(value, &reused[number])
255 .or_else(|| state.hinted(value, &hints[number], Want::Clear))
256 .or_else(|| {
257 let partners = passed[number].iter().chain(&received[number]);
258 let partners: Vec<PhysReg> =
259 partners.filter_map(|&other| state.reg_of(other)).collect();
260 state.hinted(value, &partners, Want::Clear)
261 })
262 .or_else(|| {
263 let mut ties = reused[number].clone();
264 let source = reuses[number].and_then(|reuse| reuse.source.number());
265 ties.extend(source.and_then(|source| usize::try_from(source).ok()));
266 ties.retain(|&tie| state.at[tie].is_none() && values[tie].is_some());
267 let tied = ties.iter().flat_map(|&tie| hints[tie].iter());
268 let wanted: Vec<PhysReg> = tied.chain(env.order(value.class)).copied().collect();
269 state.together(value, &ties, &wanted)
270 })
271 .or_else(|| state.hinted(value, env.order(value.class), Want::Clear))
272 .or_else(|| state.hinted(value, env.order(value.class), Want::Allowed));
273 if state.work > state.budget {
274 return None;
275 }
276 if let Some(at) = chosen {
277 state.take(value, at);
278 continue;
279 }
280 match state.cheapest(value, env.order(value.class)) {
281 Some(at) => {
282 for other in state.evict(value, at) {
283 lost[other] += 1;
284 let Some(evicted) = values[other] else { continue };
285 if lost[other] > ROUNDS {
286 assignment.spill(evicted.reg, evicted.class);
287 } else {
288 queue.push((evicted.size, Reverse(other)));
289 }
290 }
291 state.take(value, at);
292 }
293 None => assignment.spill(value.reg, value.class),
294 }
295 if state.work > state.budget {
296 return None;
297 }
298 }
299 state.settle(&reused, &passed, &received);
300 if state.work > state.budget {
301 return None;
302 }
303 for (number, at) in state.at.iter().enumerate() {
304 let Some(at) = *at else { continue };
305 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
306 assignment.put(reg, Place::Reg(at));
307 }
308 for inst in state.commuted.iter().flatten() {
309 assignment.commute(*inst);
310 }
311 Some(assignment)
312}
313
314struct State<'a, 'v> {
316 live: &'a Live,
317 blocked: &'a Blocks,
318 reuses: &'a [Option<Reuse>],
319 values: &'v [Option<Value<'a>>],
320 held: Vec<((RegClass, PhysReg), Vec<usize>)>,
322 at: Vec<Option<PhysReg>>,
324 commuted: Vec<Option<Inst>>,
327 work: u64,
328 budget: u64,
329}
330
331impl<'a> State<'a, '_> {
332 fn reg_of(&self, reg: Reg) -> Option<PhysReg> {
333 self.at.get(usize::try_from(reg.number()?).ok()?).copied().flatten()
334 }
335
336 fn slot(&mut self, class: RegClass, at: PhysReg) -> &mut Vec<usize> {
337 let found = self.held.iter().position(|(key, _)| *key == (class, at));
338 let index = found.unwrap_or_else(|| {
339 self.held.push(((class, at), Vec::new()));
340 self.held.len() - 1
341 });
342 &mut self.held[index].1
343 }
344
345 fn clashes(&mut self, value: Value<'_>, at: PhysReg) -> Vec<usize> {
348 let Some(index) = self.held.iter().position(|(key, _)| *key == (value.class, at)) else {
349 return Vec::new();
350 };
351 let mut clashes = Vec::new();
352 for &other in &self.held[index].1 {
353 let Some(held) = self.values[other] else { continue };
354 self.work += 1;
355 if !held.range.overlaps(value.range) || !held.area.overlaps(value.area) {
356 continue;
357 }
358 if !self.shares(value.reg, held.reg) {
359 clashes.push(other);
360 }
361 }
362 clashes
363 }
364
365 fn shares(&self, one: Reg, two: Reg) -> bool {
368 let reads = |answer: Reg, source: Reg| {
369 let Some(number) = answer.number().and_then(|n| usize::try_from(n).ok()) else {
370 return false;
371 };
372 let Some(reuse) = self.reuses[number] else { return false };
373 match self.commuted[number] {
374 Some(_) => reuse.second == Some(source),
375 None => reuse.source == source,
376 }
377 };
378 (reads(one, two) || reads(two, one)) && assign::apart(self.live, one, two)
379 }
380
381 fn free(&mut self, value: Value<'_>, at: PhysReg, want: Want) -> bool {
382 !self.blocked.insists(value.reg, value.class, value.area, value.range, at, want)
383 && self.clashes(value, at).is_empty()
384 }
385
386 fn hinted(&mut self, value: Value<'_>, wanted: &[PhysReg], want: Want) -> Option<PhysReg> {
388 wanted.iter().copied().find(|&at| self.work <= self.budget && self.free(value, at, want))
389 }
390
391 fn together(&mut self, value: Value<'_>, ties: &[usize], order: &[PhysReg]) -> Option<PhysReg> {
397 if ties.is_empty() {
398 return None;
399 }
400 for &at in order {
401 if self.work > self.budget {
402 return None;
403 }
404 if !self.free(value, at, Want::Clear) {
405 continue;
406 }
407 let values = self.values;
408 let mut tied = ties.iter().filter_map(|&tie| values[tie]);
409 if tied.all(|other| self.free(other, at, Want::Allowed)) {
410 return Some(at);
411 }
412 }
413 None
414 }
415
416 fn coalesced(&mut self, value: Value<'_>, answers: &[usize]) -> Option<PhysReg> {
422 let number = index(value.reg);
423 if let Some(reuse) = self.reuses[number] {
424 if let Some(at) = self.reg_of(reuse.source) {
425 if self.free(value, at, Want::Allowed) {
426 return Some(at);
427 }
428 }
429 if let Some(second) = reuse.second {
430 if let Some(at) = self.reg_of(second) {
431 self.commuted[number] = Some(reuse.inst);
432 if self.free(value, at, Want::Allowed) {
433 return Some(at);
434 }
435 self.commuted[number] = None;
436 }
437 }
438 }
439 for &answer in answers {
440 let Some(at) = self.at[answer] else { continue };
441 if self.commuted[answer].is_none() && self.free(value, at, Want::Allowed) {
442 return Some(at);
443 }
444 }
445 None
446 }
447
448 fn cheapest(&mut self, value: Value<'_>, order: &[PhysReg]) -> Option<PhysReg> {
451 let mut best: Option<(u128, u128, PhysReg)> = None;
452 for &at in order {
453 if self.blocked.insists(
454 value.reg,
455 value.class,
456 value.area,
457 value.range,
458 at,
459 Want::Allowed,
460 ) {
461 continue;
462 }
463 let clashes = self.clashes(value, at);
464 let weights = clashes.iter().filter_map(|&other| self.values[other]).map(|v| v.weight);
465 let (heaviest, total) = weights.fold((0, 0), |(most, sum), w| (most.max(w), sum + w));
466 if heaviest >= value.weight {
467 continue;
468 }
469 if best.is_none_or(|(most, sum, _)| (heaviest, total) < (most, sum)) {
470 best = Some((heaviest, total, at));
471 }
472 }
473 best.map(|(_, _, at)| at)
474 }
475
476 fn evict(&mut self, value: Value<'_>, at: PhysReg) -> Vec<usize> {
478 let clashes = self.clashes(value, at);
479 self.slot(value.class, at).retain(|other| !clashes.contains(other));
480 for &other in &clashes {
481 self.at[other] = None;
482 self.commuted[other] = None;
483 }
484 clashes
485 }
486
487 fn settle(&mut self, reused: &[Vec<usize>], passed: &[Vec<Reg>], received: &[Vec<Reg>]) {
496 let mut moved = true;
497 while moved && self.work <= self.budget {
498 moved = false;
499 for number in 0..self.at.len() {
500 let (Some(value), Some(now)) = (self.values[number], self.at[number]) else {
501 continue;
502 };
503 let reuse = self.reuses[number];
504 let source = reuse.and_then(|reuse| self.reg_of(reuse.source));
505 let second =
506 reuse.and_then(|reuse| reuse.second).and_then(|second| self.reg_of(second));
507 let mut wanted: Vec<PhysReg> = source.into_iter().chain(second).collect();
508 for &answer in &reused[number] {
509 if self.commuted[answer].is_none() {
510 wanted.extend(self.at[answer]);
511 }
512 }
513 let partners = passed[number].iter().chain(&received[number]);
514 wanted.extend(partners.filter_map(|&other| self.reg_of(other)));
515 let met = |at: PhysReg| wanted.iter().filter(|&®| reg == at).count();
516 let here = met(now);
517 let mut better: Vec<(usize, PhysReg)> = Vec::new();
518 for &at in &wanted {
519 let count = met(at);
520 if count > here && !better.contains(&(count, at)) {
521 better.push((count, at));
522 }
523 }
524 if better.is_empty() {
525 continue;
526 }
527 better.sort_by_key(|&(count, _)| Reverse(count));
528 self.slot(value.class, now).retain(|&other| other != number);
529 self.at[number] = None;
530 let was = self.commuted[number];
531 let mut to = now;
532 for (_, at) in better {
533 self.commuted[number] = match reuse {
534 Some(reuse) if second == Some(at) && source != Some(at) => Some(reuse.inst),
535 _ => None,
536 };
537 if self.free(value, at, Want::Allowed) {
538 to = at;
539 moved = true;
540 break;
541 }
542 }
543 if to == now {
544 self.commuted[number] = was;
545 }
546 self.take(value, to);
547 }
548 }
549 }
550
551 fn take(&mut self, value: Value<'_>, at: PhysReg) {
552 let number = index(value.reg);
553 self.at[number] = Some(at);
554 self.slot(value.class, at).push(number);
555 }
556}
557
558pub(crate) fn size(area: Area<'_>) -> u32 {
560 area.pieces().map(|piece| piece.end - piece.start + 1).sum()
561}
562
563pub(crate) fn costs(func: &Func) -> Vec<u128> {
568 let mut costs = vec![0u128; func.vregs()];
569 let mut add = |reg: Reg, weight: u128| {
570 let number = reg.number().and_then(|number| usize::try_from(number).ok());
571 if let Some(cost) = number.and_then(|number| costs.get_mut(number)) {
572 *cost += weight;
573 }
574 };
575 for block in func.blocks() {
576 let weight = u128::from(func[block].weight.raw().max(1));
577 for param in &func[block].params {
578 add(param.reg, weight);
579 }
580 for inst in func.insts(block) {
581 for operand in &func[func[inst].operands] {
582 add(operand.reg, weight);
583 }
584 }
585 for call in &func[block].succs {
586 for &arg in &call.args {
587 add(arg, weight);
588 }
589 }
590 }
591 costs
592}
593
594fn received(func: &Func) -> Vec<Vec<Reg>> {
596 let mut received = vec![Vec::new(); func.vregs()];
597 for block in func.blocks() {
598 for call in &func[block].succs {
599 for (&arg, param) in call.args.iter().zip(&func[call.block].params) {
600 let number = param.reg.number().and_then(|number| usize::try_from(number).ok());
601 let Some(from) = number.and_then(|number| received.get_mut(number)) else {
602 continue;
603 };
604 if !from.contains(&arg) {
605 from.push(arg);
606 }
607 }
608 }
609 }
610 received
611}
612
613fn reused(reuses: &[Option<Reuse>]) -> Vec<Vec<usize>> {
615 let mut reused = vec![Vec::new(); reuses.len()];
616 for (answer, reuse) in reuses.iter().enumerate() {
617 let Some(reuse) = reuse else { continue };
618 let number = reuse.source.number().and_then(|number| usize::try_from(number).ok());
619 if let Some(answers) = number.and_then(|number| reused.get_mut(number)) {
620 answers.push(answer);
621 }
622 }
623 reused
624}
625
626fn index(reg: Reg) -> usize {
627 usize::try_from(reg.number().expect("a virtual register")).expect("a register number")
628}
629
630#[cfg(test)]
631mod tests {
632 use rucc_base::Interner;
633 use rucc_mir::{BlockCall, Constraint, Flags, Opcode, Operand, Param};
634 use rucc_target::x86_64::{GPR, REGS, SYSV};
635
636 use super::*;
637 use crate::check;
638
639 fn env() -> Env {
640 let (order, scratch) = SYSV.int_order.split_at(SYSV.int_order.len() - 3);
641 Env::new().with(GPR, order, scratch)
642 }
643
644 fn narrow(count: usize) -> Env {
645 Env::new().with(GPR, &SYSV.int_order[..count], &SYSV.int_order[count..count + 1])
646 }
647
648 fn named(place: Option<Place>) -> String {
649 match place {
650 Some(Place::Reg(reg)) => REGS.name(GPR, reg).expect("a register").to_string(),
651 Some(Place::Slot(_)) => "slot".to_string(),
652 None => "nowhere".to_string(),
653 }
654 }
655
656 fn places(func: &mut Func, env: &Env) -> Vec<String> {
658 let order = Order::of(func);
659 let live = Live::of(func, &order);
660 let assignment = within(func, &order, &live, env, BUDGET).expect("inside the budget");
661 for &inst in assignment.commuted() {
664 let list = func[inst].operands;
665 func[list].swap(1, 2);
666 }
667 let problems = check::check(func, &order, &live, &assignment);
668 assert!(problems.is_empty(), "{}", check::report(&problems));
669 (0..func.vregs())
670 .map(|number| {
671 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
672 named(assignment.place(reg))
673 })
674 .collect()
675 }
676
677 fn linear(func: &Func, env: &Env) -> Vec<String> {
678 let order = Order::of(func);
679 let live = Live::of(func, &order);
680 let assignment = assign::assign(func, &order, &live, env);
681 (0..func.vregs())
682 .map(|number| {
683 let reg = Reg::virtual_reg(u32::try_from(number).expect("a register number"));
684 named(assignment.place(reg))
685 })
686 .collect()
687 }
688
689 #[test]
690 fn a_value_the_spill_phase_picked_goes_to_the_stack_and_the_rest_fit() {
691 let mut names = Interner::new();
692 let mut func = Func::new(names.intern("f"));
693 let opcode = Opcode::new(names.intern("x64.nop"));
694 let block = func.create_block();
695 let busy = func.new_vreg(GPR);
696 let once = func.new_vreg(GPR);
697 let other = func.new_vreg(GPR);
698 func.build(block, opcode).def(busy, GPR).finish();
699 func.build(block, opcode).def(once, GPR).finish();
700 func.build(block, opcode).def(other, GPR).finish();
701 for _ in 0..3 {
702 func.build(block, opcode).uses(busy, GPR).uses(other, GPR).finish();
703 }
704 func.build(block, opcode).uses(once, GPR).uses(busy, GPR).uses(other, GPR).finish();
705
706 let env = narrow(2);
707 let order = Order::of(&func);
708 let live = Live::of(&func, &order);
709 let pressure = Pressure::of(&func, &order, &live, &env);
710 let early = spill::choose(&func, &live, &pressure);
711 assert_eq!(early, [once]);
712 let assignment = placed(&func, &order, &live, &env, BUDGET, &early).expect("in budget");
713 let problems = check::check(&func, &order, &live, &assignment);
714 assert!(problems.is_empty(), "{}", check::report(&problems));
715 assert_eq!(named(assignment.place(once)), "slot");
716 assert_eq!(assignment.spilled(), 1);
717 }
718
719 #[test]
720 fn two_values_that_are_never_both_wanted_share_a_register() {
721 let mut names = Interner::new();
722 let mut func = Func::new(names.intern("f"));
723 let opcode = Opcode::new(names.intern("x64.nop"));
724 let block = func.create_block();
725 let first = func.new_vreg(GPR);
726 let second = func.new_vreg(GPR);
727 func.build(block, opcode).def(first, GPR).finish();
728 func.build(block, opcode).uses(first, GPR).finish();
729 func.build(block, opcode).def(second, GPR).finish();
730 func.build(block, opcode).uses(second, GPR).finish();
731
732 assert_eq!(places(&mut func, &env()), ["rax", "rax"]);
733 }
734
735 #[test]
736 fn the_value_read_most_often_keeps_its_register_though_it_is_wanted_longest() {
737 let mut names = Interner::new();
738 let mut func = Func::new(names.intern("f"));
739 let opcode = Opcode::new(names.intern("x64.nop"));
740 let block = func.create_block();
741 let busy = func.new_vreg(GPR);
742 let once = func.new_vreg(GPR);
743 func.build(block, opcode).def(busy, GPR).finish();
744 func.build(block, opcode).def(once, GPR).finish();
745 for _ in 0..6 {
746 func.build(block, opcode).uses(busy, GPR).finish();
747 }
748 func.build(block, opcode).uses(once, GPR).finish();
749 func.build(block, opcode).uses(busy, GPR).finish();
750
751 assert_eq!(linear(&func, &narrow(1)), ["slot", "rax"]);
754 assert_eq!(places(&mut func, &narrow(1)), ["rax", "slot"]);
755 }
756
757 #[test]
758 fn a_value_read_in_a_loop_keeps_its_register_over_one_read_outside_it() {
759 let mut names = Interner::new();
760 let mut func = Func::new(names.intern("f"));
761 let opcode = Opcode::new(names.intern("x64.nop"));
762 let entry = func.create_block();
763 let body = func.create_block();
764 let out = func.create_block();
765 let step = func.new_vreg(GPR);
766 let cold = func.new_vreg(GPR);
767 func.build(entry, opcode).def(cold, GPR).finish();
768 func.build(entry, opcode).def(step, GPR).finish();
769 *func.succs_mut(entry) = vec![BlockCall::to(body)];
770 func.build(body, opcode).uses(step, GPR).finish();
771 *func.succs_mut(body) = vec![BlockCall::to(body), BlockCall::to(out)];
772 func.set_weight(body, rucc_mir::Weight::parts(100 * rucc_mir::Weight::SCALE));
773 func.build(out, opcode).uses(cold, GPR).finish();
774 func.build(out, opcode).uses(step, GPR).finish();
775
776 assert_eq!(places(&mut func, &narrow(1)), ["rax", "slot"]);
779 }
780
781 #[test]
782 fn a_long_value_gives_its_register_back_to_a_short_busy_one() {
783 let mut names = Interner::new();
784 let mut func = Func::new(names.intern("f"));
785 let opcode = Opcode::new(names.intern("x64.nop"));
786 let block = func.create_block();
787 let long = func.new_vreg(GPR);
788 let short = func.new_vreg(GPR);
789 func.build(block, opcode).def(long, GPR).finish();
790 for _ in 0..4 {
791 func.build(block, opcode).finish();
792 }
793 func.build(block, opcode).def(short, GPR).finish();
794 for _ in 0..4 {
795 func.build(block, opcode).uses(short, GPR).finish();
796 }
797 func.build(block, opcode).uses(long, GPR).finish();
798
799 assert_eq!(places(&mut func, &narrow(1)), ["slot", "rax"]);
802 }
803
804 #[test]
805 fn the_answer_of_a_two_address_instruction_goes_where_its_source_ends() {
806 let mut names = Interner::new();
807 let mut func = Func::new(names.intern("f"));
808 let opcode = Opcode::new(names.intern("x64.nop"));
809 let block = func.create_block();
810 let left = func.new_vreg(GPR);
811 let right = func.new_vreg(GPR);
812 let sum = func.new_vreg(GPR);
813 func.build(block, opcode).def(left, GPR).finish();
814 func.build(block, opcode).def(right, GPR).finish();
815 func.build(block, opcode)
816 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
817 .uses(left, GPR)
818 .uses(right, GPR)
819 .finish();
820 func.build(block, opcode).uses(right, GPR).finish();
821 func.build(block, opcode).uses(sum, GPR).finish();
822
823 let places = places(&mut func, &env());
824 assert_eq!(places[2], places[0]);
825 assert_ne!(places[1], places[0]);
826 }
827
828 #[test]
829 fn a_source_placed_after_its_answer_goes_where_the_answer_is() {
830 let mut names = Interner::new();
831 let mut func = Func::new(names.intern("f"));
832 let opcode = Opcode::new(names.intern("x64.nop"));
833 let block = func.create_block();
834 let left = func.new_vreg(GPR);
835 let sum = func.new_vreg(GPR);
836 func.build(block, opcode).def(left, GPR).finish();
837 func.build(block, opcode)
838 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
839 .uses(left, GPR)
840 .finish();
841 for _ in 0..6 {
842 func.build(block, opcode).uses(sum, GPR).finish();
843 }
844
845 let places = places(&mut func, &env());
847 assert_eq!(places[0], places[1]);
848 }
849
850 #[test]
851 fn an_answer_that_commutes_stays_where_the_loop_passes_it() {
852 let mut names = Interner::new();
853 let mut func = Func::new(names.intern("f"));
854 let opcode = Opcode::new(names.intern("x64.nop"));
855 let entry = func.create_block();
856 let head = func.create_block();
857 let out = func.create_block();
858 let seed = func.new_vreg(GPR);
859 let total = func.new_vreg(GPR);
860 let term = func.new_vreg(GPR);
861 let next = func.new_vreg(GPR);
862 func.build(entry, opcode).def(seed, GPR).finish();
863 *func.succs_mut(entry) = vec![BlockCall::with(head, vec![seed])];
864 func.params_mut(head).push(Param { reg: total, class: GPR });
865 func.build(head, opcode).def(term, GPR).finish();
866 func.build(head, opcode)
867 .flags(Flags::COMMUTES)
868 .operand(Operand::write(next, GPR).with(Constraint::Reuse(1)))
869 .uses(total, GPR)
870 .uses(term, GPR)
871 .finish();
872 *func.succs_mut(head) = vec![BlockCall::with(head, vec![next]), BlockCall::to(out)];
873
874 let places = places(&mut func, &env());
875 assert_eq!(places[3], places[1]);
876 }
877
878 #[test]
879 fn an_answer_whose_first_source_lives_on_goes_where_the_second_one_ends() {
880 let mut names = Interner::new();
881 let mut func = Func::new(names.intern("f"));
882 let opcode = Opcode::new(names.intern("x64.nop"));
883 let block = func.create_block();
884 let base = func.new_vreg(GPR);
885 let entry = func.new_vreg(GPR);
886 let target = func.new_vreg(GPR);
887 func.build(block, opcode).def(base, GPR).finish();
888 func.build(block, opcode).def(entry, GPR).finish();
889 func.build(block, opcode)
890 .flags(Flags::COMMUTES)
891 .operand(Operand::write(target, GPR).with(Constraint::Reuse(1)))
892 .uses(base, GPR)
893 .uses(entry, GPR)
894 .finish();
895 func.build(block, opcode).uses(target, GPR).finish();
896 func.build(block, opcode).uses(base, GPR).finish();
897
898 let places = places(&mut func, &env());
899 assert_eq!(places[2], places[1]);
900 assert_ne!(places[2], places[0]);
901 }
902
903 #[test]
904 fn a_register_an_instruction_insists_on_is_left_to_the_value_it_names() {
905 let mut names = Interner::new();
906 let mut func = Func::new(names.intern("f"));
907 let opcode = Opcode::new(names.intern("x64.nop"));
908 let block = func.create_block();
909 let kept = func.new_vreg(GPR);
910 let passed = func.new_vreg(GPR);
911 func.build(block, opcode).def(kept, GPR).finish();
912 func.build(block, opcode).def(passed, GPR).finish();
913 func.build(block, opcode)
914 .operand(Operand::read(passed, GPR).with(Constraint::Fixed(rucc_target::x86_64::RAX)))
915 .finish();
916 func.build(block, opcode).uses(kept, GPR).finish();
917
918 let places = places(&mut func, &env());
919 assert_eq!(places[1], "rax");
920 assert_ne!(places[0], "rax");
921 }
922
923 #[test]
924 fn a_value_only_memory_can_hold_goes_to_the_stack() {
925 let mut names = Interner::new();
926 let mut func = Func::new(names.intern("f"));
927 let opcode = Opcode::new(names.intern("x64.nop"));
928 let block = func.create_block();
929 let value = func.new_vreg(GPR);
930 func.build(block, opcode).def(value, GPR).finish();
931 func.build(block, opcode)
932 .operand(Operand::read(value, GPR).with(Constraint::Stack))
933 .finish();
934
935 assert_eq!(places(&mut func, &env()), ["slot"]);
936 }
937
938 #[test]
939 fn a_function_over_the_budget_gets_the_linear_scan_answer() {
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 busy = func.new_vreg(GPR);
945 let once = func.new_vreg(GPR);
946 func.build(block, opcode).def(busy, GPR).finish();
947 func.build(block, opcode).def(once, GPR).finish();
948 for _ in 0..6 {
949 func.build(block, opcode).uses(busy, GPR).finish();
950 }
951 func.build(block, opcode).uses(once, GPR).finish();
952 func.build(block, opcode).uses(busy, GPR).finish();
953
954 let order = Order::of(&func);
955 let live = Live::of(&func, &order);
956 assert!(within(&func, &order, &live, &narrow(1), 0).is_none());
957 let fallen = linear(&func, &narrow(1));
958 assert_eq!(fallen, ["slot", "rax"]);
959 }
960
961 #[test]
962 fn the_cheaper_of_the_two_answers_is_the_one_kept() {
963 let mut names = Interner::new();
964 let mut func = Func::new(names.intern("f"));
965 let opcode = Opcode::new(names.intern("x64.nop"));
966 let block = func.create_block();
967 let busy = func.new_vreg(GPR);
968 let once = func.new_vreg(GPR);
969 func.build(block, opcode).def(busy, GPR).finish();
970 func.build(block, opcode).def(once, GPR).finish();
971 for _ in 0..6 {
972 func.build(block, opcode).uses(busy, GPR).finish();
973 }
974 func.build(block, opcode).uses(once, GPR).finish();
975 func.build(block, opcode).uses(busy, GPR).finish();
976
977 let order = Order::of(&func);
978 let live = Live::of(&func, &order);
979 let env = narrow(1);
980 let linear = assign::assign(&func, &order, &live, &env);
981 let ours = within(&func, &order, &live, &env, BUDGET).expect("inside the budget");
982 let once_through = u128::from(func[block].weight.raw());
985 assert_eq!(cost(&func, &order, &linear), 8 * once_through);
986 assert_eq!(cost(&func, &order, &ours), 2 * once_through);
987 let kept = assign(&func, &order, &live, &env);
988 assert_eq!(kept.place(busy), ours.place(busy));
989 assert_eq!(kept.place(once), ours.place(once));
990 }
991
992 #[test]
993 fn a_copy_left_between_a_two_address_answer_and_its_source_is_counted() {
994 let mut names = Interner::new();
995 let mut func = Func::new(names.intern("f"));
996 let opcode = Opcode::new(names.intern("x64.nop"));
997 let block = func.create_block();
998 let left = func.new_vreg(GPR);
999 let right = func.new_vreg(GPR);
1000 let sum = func.new_vreg(GPR);
1001 func.build(block, opcode).def(left, GPR).finish();
1002 func.build(block, opcode).def(right, GPR).finish();
1003 func.build(block, opcode)
1004 .operand(Operand::write(sum, GPR).with(Constraint::Reuse(1)))
1005 .uses(left, GPR)
1006 .uses(right, GPR)
1007 .finish();
1008 func.build(block, opcode).uses(sum, GPR).finish();
1009
1010 let order = Order::of(&func);
1011 let live = Live::of(&func, &order);
1012 let chosen = assign(&func, &order, &live, &env());
1013 assert_eq!(cost(&func, &order, &chosen), 0);
1014 let mut apart = chosen.clone();
1015 apart.put(sum, chosen.place(right).expect("a place for right"));
1016 assert_eq!(cost(&func, &order, &apart), u128::from(func[block].weight.raw()));
1017 }
1018
1019 #[test]
1020 fn many_values_in_few_registers_come_out_as_something_the_machine_can_run() {
1021 let mut names = Interner::new();
1022 let mut func = Func::new(names.intern("f"));
1023 let opcode = Opcode::new(names.intern("x64.nop"));
1024 let block = func.create_block();
1025 let regs: Vec<Reg> = (0..24).map(|_| func.new_vreg(GPR)).collect();
1026 for ® in ®s {
1027 func.build(block, opcode).def(reg, GPR).finish();
1028 }
1029 for (at, ®) in regs.iter().enumerate().rev() {
1030 for _ in 0..(at % 5) {
1031 func.build(block, opcode).uses(reg, GPR).finish();
1032 }
1033 func.build(block, opcode).uses(reg, GPR).finish();
1034 }
1035
1036 let places = places(&mut func, &narrow(4));
1037 assert_eq!(places.iter().filter(|place| *place != "slot").count(), 4);
1038 }
1039}