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(
443 edges: &[Vec<usize>],
444 touched: &[Vec<u64>],
445 words: usize,
446 forward: bool,
447) -> Vec<Vec<u64>> {
448 let mut out = vec![vec![0u64; words]; edges.len()];
449 let mut going = true;
450 while going {
451 going = false;
452 for at in 0..edges.len() {
453 let at = if forward { at } else { edges.len() - 1 - at };
454 let mut row = out[at].clone();
455 for &from in &edges[at] {
456 for word in 0..words {
457 let had = row[word];
458 row[word] |= out[from][word] | touched[from][word];
459 going |= row[word] != had;
460 }
461 }
462 out[at] = row;
463 }
464 }
465 out
466}
467
468#[derive(Debug, Clone, Default)]
470struct Carried {
471 regs: Vec<Reg>,
473 at: Vec<Inst>,
476}
477
478#[must_use]
489pub fn reach(
490 func: &Func,
491 addresses: &[(Inst, usize)],
492 count: usize,
493 insts: &FrameInsts,
494 names: &mut Interner,
495) -> Reach {
496 let lea = Opcode::new(names.intern(&format!("{}{}", insts.prefix, insts.lea)));
497 let mut through: Vec<Option<Carried>> = vec![None; count];
498 for &(inst, local) in addresses {
499 let Some(held) = through.get_mut(local) else { continue };
500 let held = held.get_or_insert_with(Carried::default);
501 if func[inst].opcode == lea {
506 match def(func, inst) {
507 Some(reg) => held.regs.push(reg),
508 None => {
509 through[local] = None;
510 continue;
511 }
512 }
513 }
514 held.at.push(inst);
517 }
518
519 let readers = readers(func);
520 let crossing = crossing(func);
521 for held in &mut through {
522 if let Some(carried) = held.take() {
523 *held = follow(func, lea, &readers, &crossing, carried);
524 }
525 }
526 Reach { through }
527}
528
529fn follow(
536 func: &Func,
537 lea: Opcode,
538 readers: &HashMap<Reg, Vec<Inst>>,
539 crossing: &HashSet<Reg>,
540 mut held: Carried,
541) -> Option<Carried> {
542 let mut seen: HashSet<Reg> = held.regs.iter().copied().collect();
543 let mut queue = held.regs.clone();
544 while let Some(reg) = queue.pop() {
545 if crossing.contains(®) {
546 return None;
547 }
548 for &inst in readers.get(®).map(Vec::as_slice).unwrap_or_default() {
549 if !addressed(func, inst, reg) {
550 return None;
551 }
552 if func[inst].opcode == lea {
553 let next = def(func, inst)?;
554 if seen.insert(next) {
555 held.regs.push(next);
556 queue.push(next);
557 }
558 }
559 }
560 }
561 Some(held)
562}
563
564fn addressed(func: &Func, inst: Inst, reg: Reg) -> bool {
570 let data = &func[inst];
571 let Some(mem) = data.mem else { return false };
572 let amode = func[mem];
573 func[data.operands].iter().enumerate().all(|(at, operand)| {
574 if operand.reg != reg || operand.role.is_def() {
575 return true;
576 }
577 let at = u8::try_from(at).ok();
578 at.is_some() && (amode.base == at || amode.index == at)
579 })
580}
581
582fn def(func: &Func, inst: Inst) -> Option<Reg> {
584 let mut found = None;
585 for operand in &func[func[inst].operands] {
586 if !operand.role.is_def() {
587 continue;
588 }
589 if operand.reg.number().is_none() || found.is_some() {
590 return None;
591 }
592 found = Some(operand.reg);
593 }
594 found
595}
596
597fn readers(func: &Func) -> HashMap<Reg, Vec<Inst>> {
599 let mut readers: HashMap<Reg, Vec<Inst>> = HashMap::new();
600 for block in func.blocks() {
601 for inst in func.insts(block) {
602 for operand in &func[func[inst].operands] {
603 if operand.role.is_def() || operand.reg.number().is_none() {
604 continue;
605 }
606 let at = readers.entry(operand.reg).or_default();
607 if at.last() != Some(&inst) {
608 at.push(inst);
609 }
610 }
611 }
612 }
613 readers
614}
615
616fn crossing(func: &Func) -> HashSet<Reg> {
623 let mut crossing = HashSet::new();
624 for block in func.blocks() {
625 crossing.extend(func[block].params.iter().map(|param| param.reg));
626 for call in &func[block].succs {
627 crossing.extend(call.args.iter().copied());
628 }
629 }
630 crossing
631}
632
633fn moved(allocation: &Allocation, slots: usize) -> Vec<Vec<Range>> {
638 let order = &allocation.order;
639 let mut moved = vec![Vec::new(); slots];
640 for edit in &allocation.edits {
641 let at = match edit.at {
642 At::Before(inst) => order.early(inst),
643 At::After(inst) => order.late(inst),
644 At::StartOf(block) => order.start(block),
645 At::EndOf(block) => order.end(block),
646 };
647 let around =
648 Range { start: at.saturating_sub(1), end: at.saturating_add(1).min(order.points()) };
649 for place in [edit.mov.to, edit.mov.from] {
650 if let Place::Slot(slot) = place {
651 if let Some(at) = usize::try_from(slot).ok().and_then(|slot| moved.get_mut(slot)) {
652 at.push(around);
653 }
654 }
655 }
656 }
657 moved
658}
659
660fn merged(pieces: impl IntoIterator<Item = Range>) -> Vec<Range> {
662 let mut pieces: Vec<Range> = pieces.into_iter().collect();
663 pieces.sort_by_key(|piece| (piece.start, piece.end));
664 let mut merged: Vec<Range> = Vec::with_capacity(pieces.len());
665 for piece in pieces {
666 match merged.last_mut() {
667 Some(last) if piece.start <= last.end => last.end = last.end.max(piece.end),
668 _ => merged.push(piece),
669 }
670 }
671 merged
672}
673
674fn clashes(one: &[Range], two: &[Range]) -> bool {
680 let (mut mine, mut theirs) = (0, 0);
681 while mine < one.len() && theirs < two.len() {
682 if one[mine].overlaps(two[theirs]) {
683 return true;
684 }
685 if one[mine].end < two[theirs].end {
686 mine += 1;
687 } else {
688 theirs += 1;
689 }
690 }
691 false
692}
693
694#[cfg(test)]
695mod tests {
696 use rucc_base::Interner;
697 use rucc_mir::{Block, BlockCall, Mem, Operand};
698 use rucc_regalloc::assign::Env;
699 use rucc_target::x86_64::{FRAME, GPR, REGS, SYSV};
700
701 use super::*;
702 use crate::frame::{Frame, Layout};
703
704 struct Building {
706 names: Interner,
707 func: Func,
708 lea: Opcode,
709 nop: Opcode,
710 addresses: Vec<(Inst, usize)>,
711 }
712
713 impl Building {
714 fn new() -> (Self, Block) {
716 let mut names = Interner::new();
717 let func = Func::new(names.intern("f"));
718 let lea = Opcode::new(names.intern(&format!("{}{}", FRAME.prefix, FRAME.lea)));
719 let nop = Opcode::new(names.intern("x64.nop"));
720 let mut building = Self { names, func, lea, nop, addresses: Vec::new() };
721 let block = building.func.create_block();
722 (building, block)
723 }
724
725 fn local(&mut self, block: Block, which: usize) -> Reg {
728 let sp = Operand::read(Reg::physical(SYSV.stack_pointer), GPR);
729 let reg = self.func.new_vreg(GPR);
730 let inst = self.func.build(block, self.lea).def(reg, GPR).mem(Mem::at(sp)).finish();
731 self.addresses.push((inst, which));
732 reg
733 }
734
735 fn through(&mut self, block: Block, addr: Reg) {
738 let at = Operand::read(addr, GPR);
739 self.func.build(block, self.nop).mem(Mem::at(at)).finish();
740 }
741
742 fn held(&mut self, block: Block, reg: Reg) {
745 self.func.build(block, self.nop).uses(reg, GPR).finish();
746 }
747
748 fn value(&mut self, block: Block) -> Reg {
750 let reg = self.func.new_vreg(GPR);
751 self.func.build(block, self.nop).def(reg, GPR).finish();
752 reg
753 }
754
755 fn allocate(&mut self, locals: usize, registers: usize) -> (Reach, Allocation) {
758 let reach = reach(&self.func, &self.addresses, locals, &FRAME, &mut self.names);
759 let env =
760 Env::new().with(GPR, &SYSV.int_order[..registers], &SYSV.int_order[registers..]);
761 let allocation = rucc_regalloc::run(&mut self.func, &env, "test");
762 (reach, allocation)
763 }
764 }
765
766 const WORD: Local = Local { size: 8, align: 8 };
768
769 #[test]
770 fn two_locals_that_are_never_both_wanted_are_the_same_bytes() {
771 let (mut building, block) = Building::new();
772 let first = building.local(block, 0);
773 building.through(block, first);
774 let second = building.local(block, 1);
775 building.through(block, second);
776 let (reach, allocation) = building.allocate(2, 4);
777
778 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD, WORD], &[]);
779 assert_eq!(plan.cells().len(), 1, "one run of bytes for the two of them");
780 assert_eq!(plan.local(0), plan.local(1));
781 assert_eq!(plan.saved(), 1);
782 }
783
784 #[test]
785 fn two_locals_that_are_both_wanted_at_once_are_not() {
786 let (mut building, block) = Building::new();
787 let first = building.local(block, 0);
788 let second = building.local(block, 1);
789 building.through(block, first);
792 building.through(block, second);
793 let (reach, allocation) = building.allocate(2, 4);
794
795 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD, WORD], &[]);
796 assert_eq!(plan.cells().len(), 2);
797 assert_ne!(plan.local(0), plan.local(1));
798 assert_eq!(plan.saved(), 0);
799 }
800
801 #[test]
802 fn a_local_and_a_spilled_value_that_do_not_meet_share_one_run_of_bytes() {
803 let (mut building, block) = Building::new();
804 let addr = building.local(block, 0);
805 building.through(block, addr);
806 let values: Vec<Reg> = (0..3).map(|_| building.value(block)).collect();
809 for ® in &values {
810 building.held(block, reg);
811 }
812 let (reach, allocation) = building.allocate(1, 2);
813
814 assert_eq!(allocation.assignment.spilled(), 1, "one value went to the stack");
815 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD], &[8]);
816 assert_eq!(plan.cells().len(), 1);
817 assert_eq!(plan.local(0), plan.slot(0));
818 }
819
820 #[test]
821 fn a_local_whose_address_is_handed_to_something_shares_with_nothing() {
822 let (mut building, block) = Building::new();
823 let first = building.local(block, 0);
824 building.held(block, first);
827 let second = building.local(block, 1);
828 building.through(block, second);
829 let (reach, allocation) = building.allocate(2, 4);
830
831 assert!(!reach.shares(0), "an address that got away");
832 assert!(reach.shares(1));
833 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD, WORD], &[]);
834 assert_eq!(plan.cells().len(), 2);
835 assert_ne!(plan.local(0), plan.local(1));
836 }
837
838 #[test]
839 fn a_local_whose_address_is_carried_into_a_block_shares_with_nothing() {
840 let (mut building, block) = Building::new();
841 let addr = building.local(block, 0);
842 let next = building.func.create_block();
843 let param = building.func.append_param(next, GPR);
844 building.func.build(block, building.nop).finish();
845 building.func.succs_mut(block).push(BlockCall::with(next, vec![addr]));
846 building.through(next, param);
847 let (reach, _) = building.allocate(1, 4);
848
849 assert!(!reach.shares(0), "an address that goes between blocks");
850 }
851
852 #[test]
853 fn a_local_touched_again_later_keeps_its_bytes_over_everything_in_between() {
854 let (mut building, block) = Building::new();
855 let first = building.local(block, 0);
856 building.through(block, first);
857 let second = building.local(block, 1);
861 building.through(block, second);
862 let again = building.local(block, 0);
864 building.through(block, again);
865 let (reach, allocation) = building.allocate(2, 4);
866
867 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD, WORD], &[]);
868 assert_ne!(plan.local(0), plan.local(1));
869 assert_eq!(plan.saved(), 0);
870 }
871
872 #[test]
873 fn a_local_touched_in_a_loop_keeps_its_bytes_over_the_rest_of_the_loop() {
874 let (mut building, block) = Building::new();
875 let header = building.func.create_block();
876 let body = building.func.create_block();
877 building.func.build(block, building.nop).finish();
878 building.func.succs_mut(block).push(BlockCall::to(header));
879
880 let held = building.local(header, 1);
882 building.through(header, held);
883 building.func.build(header, building.nop).finish();
884 building.func.succs_mut(header).push(BlockCall::to(body));
885
886 let addr = building.local(body, 0);
891 building.through(body, addr);
892 building.func.build(body, building.nop).finish();
893 building.func.succs_mut(body).push(BlockCall::to(header));
894 let (reach, allocation) = building.allocate(2, 4);
895
896 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD, WORD], &[]);
897 assert_ne!(plan.local(0), plan.local(1));
898 }
899
900 #[test]
905 fn a_block_that_is_its_own_neighbour_is_a_loop_like_any_other() {
906 let (mut building, block) = Building::new();
907 let loops = building.func.create_block();
908 building.func.build(block, building.nop).finish();
909 building.func.succs_mut(block).push(BlockCall::to(loops));
910
911 let held = building.local(loops, 1);
914 building.through(loops, held);
915 let addr = building.local(loops, 0);
916 building.through(loops, addr);
917 building.func.build(loops, building.nop).finish();
918 building.func.succs_mut(loops).push(BlockCall::to(loops));
919 let (reach, allocation) = building.allocate(2, 4);
920
921 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD, WORD], &[]);
922 assert_ne!(plan.local(0), plan.local(1));
923 }
924
925 #[test]
926 fn an_address_a_second_address_computation_reads_is_the_same_local_followed_on() {
927 let (mut building, block) = Building::new();
928 let first = building.local(block, 0);
929 let derived = building.func.new_vreg(GPR);
932 let at = Operand::read(first, GPR);
933 building.func.build(block, building.lea).def(derived, GPR).mem(Mem::at(at)).finish();
934 let second = building.local(block, 1);
935 building.through(block, second);
936 building.through(block, derived);
937 let (reach, allocation) = building.allocate(2, 4);
938
939 assert!(reach.shares(0), "a derived address is still an address into this frame");
940 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD, WORD], &[]);
941 assert_eq!(plan.cells().len(), 2, "the two locals are wanted at once after all");
942 }
943
944 #[test]
945 fn a_cell_two_things_share_is_as_wide_and_as_strict_as_both_of_them() {
946 let (mut building, block) = Building::new();
947 let first = building.local(block, 0);
948 building.through(block, first);
949 let second = building.local(block, 1);
950 building.through(block, second);
951 let (reach, allocation) = building.allocate(2, 4);
952
953 let narrow = Local { size: 4, align: 4 };
954 let wide = Local { size: 16, align: 16 };
955 let plan = Slots::share(&building.func, &reach, &allocation, &[narrow, wide], &[]);
956 assert_eq!(plan.cells(), [Cell { size: 16, align: 16 }]);
957 assert_eq!(plan.local(0), plan.local(1));
958 }
959
960 #[test]
961 fn a_local_nothing_on_the_address_list_names_shares_with_nothing() {
962 let (mut building, block) = Building::new();
963 let addr = building.local(block, 0);
964 building.through(block, addr);
965 let (reach, allocation) = building.allocate(2, 4);
966
967 assert!(!reach.shares(1));
970 let plan = Slots::share(&building.func, &reach, &allocation, &[WORD, WORD], &[]);
971 assert_eq!(plan.cells().len(), 2);
972 }
973
974 #[test]
975 fn the_frame_with_nothing_sharing_gives_every_local_and_every_slot_a_run_of_its_own() {
976 let plan = Slots::apart(&[WORD, Local { size: 4, align: 4 }], &[8, 16]);
977
978 assert_eq!(plan.cells().len(), 4);
979 assert_eq!(plan.saved(), 0);
980 assert_eq!((plan.local(0), plan.local(1)), (Some(0), Some(1)));
981 assert_eq!((plan.slot(0), plan.slot(1)), (Some(2), Some(3)));
982 assert_eq!(plan.cells()[3], Cell { size: 16, align: 16 });
983 }
984
985 #[test]
986 fn a_frame_whose_locals_share_is_smaller_and_puts_them_at_the_same_offset() {
987 let (mut building, block) = Building::new();
988 let first = building.local(block, 0);
989 building.through(block, first);
990 let second = building.local(block, 1);
991 building.through(block, second);
992 let (reach, allocation) = building.allocate(2, 4);
993
994 let locals = [Local { size: 64, align: 8 }; 2];
998 let base = Layout { leaf: false, locals: &locals, ..Layout::new(&SYSV, REGS) };
999 let apart = Frame::of(&building.func, &allocation, &base);
1000 let plan = Slots::share(&building.func, &reach, &allocation, &locals, &[]);
1001 let layout = Layout { share: Some(&plan), ..base };
1002 let together = Frame::of(&building.func, &allocation, &layout);
1003
1004 assert_ne!(apart.local(0), apart.local(1));
1005 assert_eq!(together.local(0), together.local(1));
1006 assert_eq!((apart.size(), together.size()), (136, 72));
1009 }
1010
1011 #[test]
1012 fn a_run_of_bytes_that_ends_part_way_through_its_alignment_costs_the_frame_nothing() {
1013 let (mut building, block) = Building::new();
1014 let addr = building.local(block, 0);
1015 building.through(block, addr);
1016 let (_, allocation) = building.allocate(1, 4);
1017
1018 let ragged = Local { size: 24, align: 16 };
1023 let whole = Local { size: 32, align: 16 };
1024 let size = |locals: &[Local]| {
1025 let layout = Layout { leaf: false, locals, ..Layout::new(&SYSV, REGS) };
1026 Frame::of(&building.func, &allocation, &layout).size()
1027 };
1028
1029 assert_eq!(size(&[ragged, whole]), size(&[whole, ragged]));
1030 assert_eq!(size(&[ragged, whole]), 56);
1033 }
1034
1035 #[test]
1036 fn a_function_with_more_slots_than_anything_real_is_laid_out_the_old_way() {
1037 let (mut building, block) = Building::new();
1038 let addr = building.local(block, 0);
1039 building.through(block, addr);
1040 let (reach, allocation) = building.allocate(1, 4);
1041
1042 let locals = vec![WORD; CROWDED + 1];
1043 let plan = Slots::share(&building.func, &reach, &allocation, &locals, &[]);
1044 assert_eq!(plan.cells().len(), locals.len());
1045 assert_eq!(plan.saved(), 0);
1046 }
1047}