use ir::stackslot::{StackOffset, StackSize, StackSlotKind};
use ir::StackSlots;
use result::{CodegenError, CodegenResult};
use std::cmp::{max, min};
pub fn layout_stack(frame: &mut StackSlots, alignment: StackSize) -> CodegenResult<StackSize> {
let max_size = StackOffset::max_value() as StackSize;
debug_assert!(alignment.is_power_of_two() && alignment <= max_size);
let mut incoming_min = 0;
let mut outgoing_max = 0;
let mut min_align = alignment;
for slot in frame.values() {
if slot.size > max_size {
return Err(CodegenError::ImplLimitExceeded);
}
match slot.kind {
StackSlotKind::IncomingArg => {
incoming_min = min(incoming_min, slot.offset.unwrap());
}
StackSlotKind::OutgoingArg => {
let offset = slot.offset
.unwrap()
.checked_add(slot.size as StackOffset)
.ok_or(CodegenError::ImplLimitExceeded)?;
outgoing_max = max(outgoing_max, offset);
}
StackSlotKind::SpillSlot
| StackSlotKind::ExplicitSlot
| StackSlotKind::EmergencySlot => {
min_align = slot.alignment(min_align);
}
}
}
let mut offset = incoming_min;
debug_assert!(min_align.is_power_of_two());
while min_align <= alignment {
for slot in frame.values_mut() {
match slot.kind {
StackSlotKind::SpillSlot
| StackSlotKind::ExplicitSlot
| StackSlotKind::EmergencySlot => {
if slot.alignment(alignment) != min_align {
continue;
}
}
StackSlotKind::IncomingArg | StackSlotKind::OutgoingArg => continue,
}
offset = offset
.checked_sub(slot.size as StackOffset)
.ok_or(CodegenError::ImplLimitExceeded)?;
offset &= -(min_align as StackOffset);
slot.offset = Some(offset);
}
min_align *= 2;
}
offset = offset
.checked_sub(outgoing_max)
.ok_or(CodegenError::ImplLimitExceeded)?;
offset &= -(alignment as StackOffset);
let frame_size = (offset as StackSize).wrapping_neg();
frame.frame_size = Some(frame_size);
Ok(frame_size)
}
#[cfg(test)]
mod tests {
use super::layout_stack;
use ir::stackslot::StackOffset;
use ir::types;
use ir::{StackSlotData, StackSlotKind, StackSlots};
use result::CodegenError;
#[test]
fn layout() {
let sss = &mut StackSlots::new();
assert_eq!(layout_stack(sss, 1), Ok(0));
assert_eq!(layout_stack(sss, 16), Ok(0));
let in0 = sss.make_incoming_arg(types::I64, 0);
let in1 = sss.make_incoming_arg(types::I64, 8);
assert_eq!(layout_stack(sss, 1), Ok(0));
assert_eq!(layout_stack(sss, 16), Ok(0));
assert_eq!(sss[in0].offset, Some(0));
assert_eq!(sss[in1].offset, Some(8));
let ss0 = sss.make_spill_slot(types::I64);
let ss1 = sss.make_spill_slot(types::I32);
assert_eq!(layout_stack(sss, 1), Ok(12));
assert_eq!(sss[in0].offset, Some(0));
assert_eq!(sss[in1].offset, Some(8));
assert_eq!(sss[ss0].offset, Some(-8));
assert_eq!(sss[ss1].offset, Some(-12));
assert_eq!(layout_stack(sss, 16), Ok(16));
assert_eq!(sss[in0].offset, Some(0));
assert_eq!(sss[in1].offset, Some(8));
assert_eq!(sss[ss0].offset, Some(-16));
assert_eq!(sss[ss1].offset, Some(-4));
let in2 = sss.make_incoming_arg(types::I32, -4);
assert_eq!(layout_stack(sss, 1), Ok(16));
assert_eq!(sss[in0].offset, Some(0));
assert_eq!(sss[in1].offset, Some(8));
assert_eq!(sss[in2].offset, Some(-4));
assert_eq!(sss[ss0].offset, Some(-12));
assert_eq!(sss[ss1].offset, Some(-16));
assert_eq!(layout_stack(sss, 16), Ok(16));
assert_eq!(sss[in0].offset, Some(0));
assert_eq!(sss[in1].offset, Some(8));
assert_eq!(sss[in2].offset, Some(-4));
assert_eq!(sss[ss0].offset, Some(-16));
assert_eq!(sss[ss1].offset, Some(-8));
let out0 = sss.get_outgoing_arg(types::I32, 0);
assert_eq!(layout_stack(sss, 1), Ok(20));
assert_eq!(sss[in0].offset, Some(0));
assert_eq!(sss[in1].offset, Some(8));
assert_eq!(sss[in2].offset, Some(-4));
assert_eq!(sss[ss0].offset, Some(-12));
assert_eq!(sss[ss1].offset, Some(-16));
assert_eq!(sss[out0].offset, Some(0));
assert_eq!(layout_stack(sss, 16), Ok(32));
assert_eq!(sss[in0].offset, Some(0));
assert_eq!(sss[in1].offset, Some(8));
assert_eq!(sss[in2].offset, Some(-4));
assert_eq!(sss[ss0].offset, Some(-16));
assert_eq!(sss[ss1].offset, Some(-8));
assert_eq!(sss[out0].offset, Some(0));
sss.get_outgoing_arg(types::I8, StackOffset::max_value() - 1);
assert_eq!(layout_stack(sss, 1), Err(CodegenError::ImplLimitExceeded));
}
#[test]
fn slot_kinds() {
let sss = &mut StackSlots::new();
let ss0 = sss.make_spill_slot(types::I32);
let ss1 = sss.push(StackSlotData::new(
StackSlotKind::ExplicitSlot,
types::I32.bytes(),
));
let ss2 = sss.get_emergency_slot(types::I32, &[]);
assert_eq!(layout_stack(sss, 1), Ok(12));
assert_eq!(sss[ss0].offset, Some(-4));
assert_eq!(sss[ss1].offset, Some(-8));
assert_eq!(sss[ss2].offset, Some(-12));
}
}