1use std::collections::{HashMap, HashSet};
85
86use rucc_base::Interner;
87use rucc_mir::{Func, Inst, Opcode, Reg};
88use rucc_regalloc::Allocation;
89use rucc_regalloc::assign::Place;
90use rucc_regalloc::live::{Area, Live, Range};
91use rucc_regalloc::order::Order;
92use rucc_regalloc::rewrite::At;
93use rucc_target::FrameInsts;
94
95use crate::frame::Local;
96
97pub const CROWDED: usize = 2048;
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
106pub struct Cell {
107 pub size: u32,
109 pub align: u32,
111}
112
113#[derive(Debug, Clone, Default, PartialEq, Eq)]
115pub struct Slots {
116 cells: Vec<Cell>,
117 locals: Vec<usize>,
118 slots: Vec<usize>,
119}
120
121impl Slots {
122 #[must_use]
129 pub fn apart(locals: &[Local], widths: &[u32]) -> Self {
130 let mut cells = Vec::with_capacity(locals.len() + widths.len());
131 for &Local { size, align } in locals {
132 cells.push(Cell { size, align });
133 }
134 for &width in widths {
135 cells.push(Cell { size: width, align: width });
136 }
137 Self {
138 locals: (0..locals.len()).collect(),
139 slots: (locals.len()..cells.len()).collect(),
140 cells,
141 }
142 }
143
144 #[must_use]
152 pub fn share(
153 func: &Func,
154 reach: &Reach,
155 allocation: &Allocation,
156 locals: &[Local],
157 widths: &[u32],
158 ) -> Self {
159 if locals.len() + widths.len() > CROWDED {
160 return Self::apart(locals, widths);
161 }
162 let mut wants = Vec::with_capacity(locals.len() + widths.len());
163 let mut reached = areas(func, reach, &allocation.live, &allocation.order);
164 for (local, &Local { size, align }) in locals.iter().enumerate() {
165 let area = reached.get_mut(local).and_then(Option::take);
166 wants.push(Want { what: What::Local(local), size, align, area });
167 }
168 let held = spilled(allocation, widths.len());
169 let moved = moved(allocation, widths.len());
170 for (slot, &width) in widths.iter().enumerate() {
171 let area = held[slot]
172 .and_then(|reg| allocation.live.area(reg))
173 .map(|live| merged(live.pieces().chain(moved[slot].iter().copied())));
174 wants.push(Want { what: What::Slot(slot), size: width, align: width, area });
175 }
176 fit(wants, locals.len(), widths.len())
177 }
178
179 #[must_use]
181 pub fn cells(&self) -> &[Cell] {
182 &self.cells
183 }
184
185 #[must_use]
187 pub fn local(&self, local: usize) -> Option<usize> {
188 self.locals.get(local).copied()
189 }
190
191 #[must_use]
193 pub fn slot(&self, slot: u32) -> Option<usize> {
194 self.slots.get(usize::try_from(slot).ok()?).copied()
195 }
196
197 #[must_use]
203 pub fn saved(&self) -> usize {
204 self.locals.len() + self.slots.len() - self.cells.len()
205 }
206}
207
208#[derive(Debug)]
210struct Want {
211 what: What,
212 size: u32,
213 align: u32,
214 area: Option<Vec<Range>>,
216}
217
218#[derive(Debug, Clone, Copy)]
220enum What {
221 Local(usize),
222 Slot(usize),
223}
224
225fn fit(mut wants: Vec<Want>, locals: usize, slots: usize) -> Slots {
233 let mut order: Vec<usize> = (0..wants.len()).collect();
234 order.sort_by_key(|&want| {
235 let Want { size, align, .. } = wants[want];
236 (std::cmp::Reverse(align), std::cmp::Reverse(size), want)
237 });
238
239 let mut cells: Vec<Cell> = Vec::new();
240 let mut busy: Vec<Option<Vec<Range>>> = Vec::new();
243 let mut of_local = vec![0; locals];
244 let mut of_slot = vec![0; slots];
245 for want in order {
246 let Want { what, size, align, area } = std::mem::replace(
247 &mut wants[want],
248 Want { what: What::Local(0), size: 0, align: 0, area: None },
249 );
250 let into = area.as_ref().and_then(|area| {
251 (0..cells.len())
252 .find(|&cell| busy[cell].as_ref().is_some_and(|busy| !clashes(busy, area)))
253 });
254 let cell = match into {
255 Some(cell) => {
256 cells[cell].size = cells[cell].size.max(size);
257 cells[cell].align = cells[cell].align.max(align);
258 let held = busy[cell].take().unwrap_or_default();
259 busy[cell] = Some(merged(held.into_iter().chain(area.into_iter().flatten())));
260 cell
261 }
262 None => {
263 cells.push(Cell { size, align });
264 busy.push(area);
265 cells.len() - 1
266 }
267 };
268 match what {
269 What::Local(local) => of_local[local] = cell,
270 What::Slot(slot) => of_slot[slot] = cell,
271 }
272 }
273 Slots { cells, locals: of_local, slots: of_slot }
274}
275
276fn spilled(allocation: &Allocation, slots: usize) -> Vec<Option<Reg>> {
281 let mut held = vec![None; slots];
282 for (reg, place) in allocation.assignment.placed() {
283 if let Place::Slot(slot) = place {
284 if let Some(at) = usize::try_from(slot).ok().and_then(|slot| held.get_mut(slot)) {
285 *at = Some(reg);
286 }
287 }
288 }
289 held
290}
291
292#[derive(Debug, Clone, Default)]
299pub struct Reach {
300 through: Vec<Option<Carried>>,
301}
302
303impl Reach {
304 fn touches(&self, local: usize, live: &Live, order: &Order) -> Option<Vec<Range>> {
307 let held = self.through.get(local)?.as_ref()?;
308 let mut spots: Vec<Range> = Vec::new();
309 for ® in &held.regs {
310 spots.extend(live.area(reg).into_iter().flat_map(Area::pieces));
311 }
312 for &inst in &held.at {
313 spots.push(Range { start: order.early(inst), end: order.late(inst) });
314 }
315 Some(spots)
316 }
317
318 #[must_use]
320 pub fn shares(&self, local: usize) -> bool {
321 self.through.get(local).is_some_and(Option::is_some)
322 }
323}
324
325fn areas(func: &Func, reach: &Reach, live: &Live, order: &Order) -> Vec<Option<Vec<Range>>> {
336 let blocks = order.blocks();
337 let count = reach.through.len();
338 let words = count.div_ceil(64);
339
340 let starts: Vec<u32> = blocks.iter().map(|&block| order.start(block)).collect();
342 let holding = |point: u32| starts.partition_point(|&start| start <= point).saturating_sub(1);
343
344 let mut touched = vec![vec![0u64; words]; blocks.len()];
346 let mut inside: Vec<Vec<(usize, Range)>> = vec![Vec::new(); blocks.len()];
347 for local in 0..count {
348 let Some(spots) = reach.touches(local, live, order) else { continue };
349 for spot in spots {
350 for at in holding(spot.start)..=holding(spot.end) {
351 let block = blocks[at];
352 let start = spot.start.max(order.start(block));
353 let end = spot.end.min(order.end(block));
354 touched[at][local / 64] |= 1 << (local % 64);
355 inside[at].push((local, Range { start, end }));
356 }
357 }
358 }
359
360 for spots in inside.iter_mut() {
364 spots.sort_unstable_by_key(|&(local, Range { start, .. })| (local, start));
365 let mut kept = 0;
366 for at in 1..spots.len() {
367 if spots[at].0 == spots[kept].0 {
368 spots[kept].1.end = spots[kept].1.end.max(spots[at].1.end);
369 } else {
370 kept += 1;
371 spots[kept] = spots[at];
372 }
373 }
374 spots.truncate(spots.len().min(kept + 1));
375 }
376
377 let mut place = vec![0usize; func.block_count()];
379 for (at, &block) in blocks.iter().enumerate() {
380 place[block.index()] = at;
381 }
382 let mut ahead: Vec<Vec<usize>> = vec![Vec::new(); blocks.len()];
383 let mut behind: Vec<Vec<usize>> = vec![Vec::new(); blocks.len()];
384 for (at, &block) in blocks.iter().enumerate() {
385 for call in &func[block].succs {
386 let to = place[call.block.index()];
387 ahead[at].push(to);
388 behind[to].push(at);
389 }
390 }
391
392 let written = spread(&behind, &touched, words, true);
393 let read = spread(&ahead, &touched, words, false);
394
395 let mut out = vec![None; count];
396 for (local, pieces) in out.iter_mut().enumerate() {
397 if reach.shares(local) {
398 *pieces = Some(Vec::new());
399 }
400 }
401 for (at, &block) in blocks.iter().enumerate() {
402 let whole = Range { start: order.start(block), end: order.end(block) };
403 for word in 0..words {
404 let mut bits = written[at][word] & read[at][word];
405 while bits != 0 {
406 let local = word * 64 + bits.trailing_zeros() as usize;
407 bits &= bits - 1;
408 if let Some(pieces) = out[local].as_mut() {
409 pieces.push(whole);
410 }
411 }
412 }
413 for &(local, spot) in &inside[at] {
417 let held = |bits: &[Vec<u64>]| bits[at][local / 64] & (1 << (local % 64)) != 0;
418 let start = if held(&written) { whole.start } else { spot.start };
419 let end = if held(&read) { whole.end } else { spot.end };
420 if let Some(pieces) = out[local].as_mut() {
421 pieces.push(Range { start, end });
422 }
423 }
424 }
425 for pieces in out.iter_mut().flatten() {
426 *pieces = merged(std::mem::take(pieces));
427 }
428 out
429}
430
431fn spread(
438 edges: &[Vec<usize>],
439 touched: &[Vec<u64>],
440 words: usize,
441 forward: bool,
442) -> Vec<Vec<u64>> {
443 let mut out = vec![vec![0u64; words]; edges.len()];
444 let mut going = true;
445 while going {
446 going = false;
447 for at in 0..edges.len() {
448 let at = if forward { at } else { edges.len() - 1 - at };
449 let mut row = std::mem::take(&mut out[at]);
450 for &from in &edges[at] {
451 for word in 0..words {
452 let had = row[word];
453 row[word] |= out[from][word] | touched[from][word];
454 going |= row[word] != had;
455 }
456 }
457 out[at] = row;
458 }
459 }
460 out
461}
462
463#[derive(Debug, Clone, Default)]
465struct Carried {
466 regs: Vec<Reg>,
468 at: Vec<Inst>,
471}
472
473#[must_use]
484pub fn reach(
485 func: &Func,
486 addresses: &[(Inst, usize)],
487 count: usize,
488 insts: &FrameInsts,
489 names: &mut Interner,
490) -> Reach {
491 let lea = Opcode::new(names.intern(&format!("{}{}", insts.prefix, insts.lea)));
492 let mut through: Vec<Option<Carried>> = vec![None; count];
493 for &(inst, local) in addresses {
494 let Some(held) = through.get_mut(local) else { continue };
495 let held = held.get_or_insert_with(Carried::default);
496 if func[inst].opcode == lea {
501 match def(func, inst) {
502 Some(reg) => held.regs.push(reg),
503 None => {
504 through[local] = None;
505 continue;
506 }
507 }
508 }
509 held.at.push(inst);
512 }
513
514 let readers = readers(func);
515 let crossing = crossing(func);
516 for held in &mut through {
517 if let Some(carried) = held.take() {
518 *held = follow(func, lea, &readers, &crossing, carried);
519 }
520 }
521 Reach { through }
522}
523
524fn follow(
531 func: &Func,
532 lea: Opcode,
533 readers: &HashMap<Reg, Vec<Inst>>,
534 crossing: &HashSet<Reg>,
535 mut held: Carried,
536) -> Option<Carried> {
537 let mut seen: HashSet<Reg> = held.regs.iter().copied().collect();
538 let mut queue = held.regs.clone();
539 while let Some(reg) = queue.pop() {
540 if crossing.contains(®) {
541 return None;
542 }
543 for &inst in readers.get(®).map(Vec::as_slice).unwrap_or_default() {
544 if !addressed(func, inst, reg) {
545 return None;
546 }
547 if func[inst].opcode == lea {
548 let next = def(func, inst)?;
549 if seen.insert(next) {
550 held.regs.push(next);
551 queue.push(next);
552 }
553 }
554 }
555 }
556 Some(held)
557}
558
559fn addressed(func: &Func, inst: Inst, reg: Reg) -> bool {
565 let data = &func[inst];
566 let Some(mem) = data.mem else { return false };
567 let amode = func[mem];
568 func[data.operands].iter().enumerate().all(|(at, operand)| {
569 if operand.reg != reg || operand.role.is_def() {
570 return true;
571 }
572 let at = u8::try_from(at).ok();
573 at.is_some() && (amode.base == at || amode.index == at)
574 })
575}
576
577fn def(func: &Func, inst: Inst) -> Option<Reg> {
579 let mut found = None;
580 for operand in &func[func[inst].operands] {
581 if !operand.role.is_def() {
582 continue;
583 }
584 if operand.reg.number().is_none() || found.is_some() {
585 return None;
586 }
587 found = Some(operand.reg);
588 }
589 found
590}
591
592fn readers(func: &Func) -> HashMap<Reg, Vec<Inst>> {
594 let mut readers: HashMap<Reg, Vec<Inst>> = HashMap::new();
595 for block in func.blocks() {
596 for inst in func.insts(block) {
597 for operand in &func[func[inst].operands] {
598 if operand.role.is_def() || operand.reg.number().is_none() {
599 continue;
600 }
601 let at = readers.entry(operand.reg).or_default();
602 if at.last() != Some(&inst) {
603 at.push(inst);
604 }
605 }
606 }
607 }
608 readers
609}
610
611fn crossing(func: &Func) -> HashSet<Reg> {
618 let mut crossing = HashSet::new();
619 for block in func.blocks() {
620 crossing.extend(func[block].params.iter().map(|param| param.reg));
621 for call in &func[block].succs {
622 crossing.extend(call.args.iter().copied());
623 }
624 }
625 crossing
626}
627
628fn moved(allocation: &Allocation, slots: usize) -> Vec<Vec<Range>> {
633 let order = &allocation.order;
634 let mut moved = vec![Vec::new(); slots];
635 for edit in &allocation.edits {
636 let at = match edit.at {
637 At::Before(inst) => order.early(inst),
638 At::After(inst) => order.late(inst),
639 At::StartOf(block) => order.start(block),
640 At::EndOf(block) => order.end(block),
641 };
642 let around =
643 Range { start: at.saturating_sub(1), end: at.saturating_add(1).min(order.points()) };
644 for place in [edit.mov.to, edit.mov.from] {
645 if let Place::Slot(slot) = place {
646 if let Some(at) = usize::try_from(slot).ok().and_then(|slot| moved.get_mut(slot)) {
647 at.push(around);
648 }
649 }
650 }
651 }
652 moved
653}
654
655fn merged(pieces: impl IntoIterator<Item = Range>) -> Vec<Range> {
657 let mut pieces: Vec<Range> = pieces.into_iter().collect();
658 pieces.sort_by_key(|piece| (piece.start, piece.end));
659 let mut merged: Vec<Range> = Vec::with_capacity(pieces.len());
660 for piece in pieces {
661 match merged.last_mut() {
662 Some(last) if piece.start <= last.end => last.end = last.end.max(piece.end),
663 _ => merged.push(piece),
664 }
665 }
666 merged
667}
668
669fn clashes(one: &[Range], two: &[Range]) -> bool {
675 let (mut mine, mut theirs) = (0, 0);
676 while mine < one.len() && theirs < two.len() {
677 if one[mine].overlaps(two[theirs]) {
678 return true;
679 }
680 if one[mine].end < two[theirs].end {
681 mine += 1;
682 } else {
683 theirs += 1;
684 }
685 }
686 false
687}
688
689#[cfg(test)]
690mod tests {
691 use rucc_base::Interner;
692 use rucc_mir::{Block, BlockCall, Mem, Operand};
693 use rucc_regalloc::assign::Env;
694 use rucc_target::x86_64::{FRAME, GPR, REGS, SYSV};
695
696 use super::*;
697 use crate::frame::{Frame, Layout};
698
699 struct Building {
701 names: Interner,
702 func: Func,
703 lea: Opcode,
704 nop: Opcode,
705 addresses: Vec<(Inst, usize)>,
706 }
707
708 impl Building {
709 fn new() -> (Self, Block) {
711 let mut names = Interner::new();
712 let func = Func::new(names.intern("f"));
713 let lea = Opcode::new(names.intern(&format!("{}{}", FRAME.prefix, FRAME.lea)));
714 let nop = Opcode::new(names.intern("x64.nop"));
715 let mut building = Self { names, func, lea, nop, addresses: Vec::new() };
716 let block = building.func.create_block();
717 (building, block)
718 }
719
720 fn local(&mut self, block: Block, which: usize) -> Reg {
723 let sp = Operand::read(Reg::physical(SYSV.stack_pointer), GPR);
724 let reg = self.func.new_vreg(GPR);
725 let inst = self.func.build(block, self.lea).def(reg, GPR).mem(Mem::at(sp)).finish();
726 self.addresses.push((inst, which));
727 reg
728 }
729
730 fn through(&mut self, block: Block, addr: Reg) {
733 let at = Operand::read(addr, GPR);
734 self.func.build(block, self.nop).mem(Mem::at(at)).finish();
735 }
736
737 fn held(&mut self, block: Block, reg: Reg) {
740 self.func.build(block, self.nop).uses(reg, GPR).finish();
741 }
742
743 fn value(&mut self, block: Block) -> Reg {
745 let reg = self.func.new_vreg(GPR);
746 self.func.build(block, self.nop).def(reg, GPR).finish();
747 reg
748 }
749
750 fn allocate(&mut self, locals: usize, registers: usize) -> (Reach, Allocation) {
753 let reach = reach(&self.func, &self.addresses, locals, &FRAME, &mut self.names);
754 let env =
755 Env::new().with(GPR, &SYSV.int_order[..registers], &SYSV.int_order[registers..]);
756 let allocation = rucc_regalloc::run(&mut self.func, &env, "test");
757 (reach, allocation)
758 }
759 }
760
761 const WORD: Local = Local { size: 8, align: 8 };
763
764 #[test]
765 fn two_locals_that_are_never_both_wanted_are_the_same_bytes() {
766 let (mut building, block) = Building::new();
767 let first = building.local(block, 0);
768 building.through(block, first);
769 let second = building.local(block, 1);
770 building.through(block, second);
771 let (reach, allocation) = building.allocate(2, 4);
772
773 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD, WORD], &[]);
774 assert_eq!(plan.cells().len(), 1, "one run of bytes for the two of them");
775 assert_eq!(plan.local(0), plan.local(1));
776 assert_eq!(plan.saved(), 1);
777 }
778
779 #[test]
780 fn two_locals_that_are_both_wanted_at_once_are_not() {
781 let (mut building, block) = Building::new();
782 let first = building.local(block, 0);
783 let second = building.local(block, 1);
784 building.through(block, first);
787 building.through(block, second);
788 let (reach, allocation) = building.allocate(2, 4);
789
790 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD, WORD], &[]);
791 assert_eq!(plan.cells().len(), 2);
792 assert_ne!(plan.local(0), plan.local(1));
793 assert_eq!(plan.saved(), 0);
794 }
795
796 #[test]
797 fn a_local_and_a_spilled_value_that_do_not_meet_share_one_run_of_bytes() {
798 let (mut building, block) = Building::new();
799 let addr = building.local(block, 0);
800 building.through(block, addr);
801 let values: Vec<Reg> = (0..3).map(|_| building.value(block)).collect();
804 for ® in &values {
805 building.held(block, reg);
806 }
807 let (reach, allocation) = building.allocate(1, 2);
808
809 assert_eq!(allocation.assignment.spilled(), 1, "one value went to the stack");
810 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD], &[8]);
811 assert_eq!(plan.cells().len(), 1);
812 assert_eq!(plan.local(0), plan.slot(0));
813 }
814
815 #[test]
816 fn a_local_whose_address_is_handed_to_something_shares_with_nothing() {
817 let (mut building, block) = Building::new();
818 let first = building.local(block, 0);
819 building.held(block, first);
822 let second = building.local(block, 1);
823 building.through(block, second);
824 let (reach, allocation) = building.allocate(2, 4);
825
826 assert!(!reach.shares(0), "an address that got away");
827 assert!(reach.shares(1));
828 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD, WORD], &[]);
829 assert_eq!(plan.cells().len(), 2);
830 assert_ne!(plan.local(0), plan.local(1));
831 }
832
833 #[test]
834 fn a_local_whose_address_is_carried_into_a_block_shares_with_nothing() {
835 let (mut building, block) = Building::new();
836 let addr = building.local(block, 0);
837 let next = building.func.create_block();
838 let param = building.func.append_param(next, GPR);
839 building.func.build(block, building.nop).finish();
840 building.func.succs_mut(block).push(BlockCall::with(next, vec![addr]));
841 building.through(next, param);
842 let (reach, _) = building.allocate(1, 4);
843
844 assert!(!reach.shares(0), "an address that goes between blocks");
845 }
846
847 #[test]
848 fn a_local_touched_again_later_keeps_its_bytes_over_everything_in_between() {
849 let (mut building, block) = Building::new();
850 let first = building.local(block, 0);
851 building.through(block, first);
852 let second = building.local(block, 1);
856 building.through(block, second);
857 let again = building.local(block, 0);
859 building.through(block, again);
860 let (reach, allocation) = building.allocate(2, 4);
861
862 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD, WORD], &[]);
863 assert_ne!(plan.local(0), plan.local(1));
864 assert_eq!(plan.saved(), 0);
865 }
866
867 #[test]
868 fn a_local_touched_in_a_loop_keeps_its_bytes_over_the_rest_of_the_loop() {
869 let (mut building, block) = Building::new();
870 let header = building.func.create_block();
871 let body = building.func.create_block();
872 building.func.build(block, building.nop).finish();
873 building.func.succs_mut(block).push(BlockCall::to(header));
874
875 let held = building.local(header, 1);
877 building.through(header, held);
878 building.func.build(header, building.nop).finish();
879 building.func.succs_mut(header).push(BlockCall::to(body));
880
881 let addr = building.local(body, 0);
886 building.through(body, addr);
887 building.func.build(body, building.nop).finish();
888 building.func.succs_mut(body).push(BlockCall::to(header));
889 let (reach, allocation) = building.allocate(2, 4);
890
891 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD, WORD], &[]);
892 assert_ne!(plan.local(0), plan.local(1));
893 }
894
895 #[test]
896 fn an_address_a_second_address_computation_reads_is_the_same_local_followed_on() {
897 let (mut building, block) = Building::new();
898 let first = building.local(block, 0);
899 let derived = building.func.new_vreg(GPR);
902 let at = Operand::read(first, GPR);
903 building.func.build(block, building.lea).def(derived, GPR).mem(Mem::at(at)).finish();
904 let second = building.local(block, 1);
905 building.through(block, second);
906 building.through(block, derived);
907 let (reach, allocation) = building.allocate(2, 4);
908
909 assert!(reach.shares(0), "a derived address is still an address into this frame");
910 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD, WORD], &[]);
911 assert_eq!(plan.cells().len(), 2, "the two locals are wanted at once after all");
912 }
913
914 #[test]
915 fn a_cell_two_things_share_is_as_wide_and_as_strict_as_both_of_them() {
916 let (mut building, block) = Building::new();
917 let first = building.local(block, 0);
918 building.through(block, first);
919 let second = building.local(block, 1);
920 building.through(block, second);
921 let (reach, allocation) = building.allocate(2, 4);
922
923 let narrow = Local { size: 4, align: 4 };
924 let wide = Local { size: 16, align: 16 };
925 let plan = Slots::share(&building.func, &reach, &allocation, &[narrow, wide], &[]);
926 assert_eq!(plan.cells(), [Cell { size: 16, align: 16 }]);
927 assert_eq!(plan.local(0), plan.local(1));
928 }
929
930 #[test]
931 fn a_local_nothing_on_the_address_list_names_shares_with_nothing() {
932 let (mut building, block) = Building::new();
933 let addr = building.local(block, 0);
934 building.through(block, addr);
935 let (reach, allocation) = building.allocate(2, 4);
936
937 assert!(!reach.shares(1));
940 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD, WORD], &[]);
941 assert_eq!(plan.cells().len(), 2);
942 }
943
944 #[test]
945 fn the_frame_with_nothing_sharing_gives_every_local_and_every_slot_a_run_of_its_own() {
946 let plan = Slots::apart(&[WORD, Local { size: 4, align: 4 }], &[8, 16]);
947
948 assert_eq!(plan.cells().len(), 4);
949 assert_eq!(plan.saved(), 0);
950 assert_eq!((plan.local(0), plan.local(1)), (Some(0), Some(1)));
951 assert_eq!((plan.slot(0), plan.slot(1)), (Some(2), Some(3)));
952 assert_eq!(plan.cells()[3], Cell { size: 16, align: 16 });
953 }
954
955 #[test]
956 fn a_frame_whose_locals_share_is_smaller_and_puts_them_at_the_same_offset() {
957 let (mut building, block) = Building::new();
958 let first = building.local(block, 0);
959 building.through(block, first);
960 let second = building.local(block, 1);
961 building.through(block, second);
962 let (reach, allocation) = building.allocate(2, 4);
963
964 let locals = [Local { size: 64, align: 8 }; 2];
968 let base = Layout { leaf: false, locals: &locals, ..Layout::new(&SYSV, REGS) };
969 let apart = Frame::of(&building.func, &allocation, &base);
970 let plan = Slots::share(&building.func, &reach, &allocation, &locals, &[]);
971 let layout = Layout { share: Some(&plan), ..base };
972 let together = Frame::of(&building.func, &allocation, &layout);
973
974 assert_ne!(apart.local(0), apart.local(1));
975 assert_eq!(together.local(0), together.local(1));
976 assert_eq!((apart.size(), together.size()), (136, 72));
979 }
980
981 #[test]
982 fn a_run_of_bytes_that_ends_part_way_through_its_alignment_costs_the_frame_nothing() {
983 let (mut building, block) = Building::new();
984 let addr = building.local(block, 0);
985 building.through(block, addr);
986 let (_, allocation) = building.allocate(1, 4);
987
988 let ragged = Local { size: 24, align: 16 };
993 let whole = Local { size: 32, align: 16 };
994 let size = |locals: &[Local]| {
995 let layout = Layout { leaf: false, locals, ..Layout::new(&SYSV, REGS) };
996 Frame::of(&building.func, &allocation, &layout).size()
997 };
998
999 assert_eq!(size(&[ragged, whole]), size(&[whole, ragged]));
1000 assert_eq!(size(&[ragged, whole]), 56);
1003 }
1004
1005 #[test]
1006 fn a_function_with_more_slots_than_anything_real_is_laid_out_the_old_way() {
1007 let (mut building, block) = Building::new();
1008 let addr = building.local(block, 0);
1009 building.through(block, addr);
1010 let (reach, allocation) = building.allocate(1, 4);
1011
1012 let locals = vec![WORD; CROWDED + 1];
1013 let plan = Slots::share(&building.func, &reach, &allocation, &locals, &[]);
1014 assert_eq!(plan.cells().len(), locals.len());
1015 assert_eq!(plan.saved(), 0);
1016 }
1017}