use super::{Arg, Call, Pass, Slot};
fn fits(size: u64) -> bool {
matches!(size, 1 | 2 | 4 | 8)
}
pub(super) fn returns(call: &mut Call, arg: &Arg<'_>) -> Pass {
let shape = match arg {
Arg::Void => return Pass::Ignore,
Arg::Scalar(_) => return Pass::Direct,
Arg::Aggregate(shape) => shape,
};
if shape.size == 0 {
return Pass::Ignore;
}
if fits(shape.size) {
let size = u32::try_from(shape.size).unwrap_or(8);
return Pass::Pieces(vec![Slot::Integer { offset: 0, size }]);
}
call.gp = call.gp.saturating_sub(1);
Pass::Reference
}
pub(super) fn argument(call: &mut Call, arg: &Arg<'_>) -> Pass {
let shape = match arg {
Arg::Void => return Pass::Ignore,
Arg::Scalar(_) => {
call.gp = call.gp.saturating_sub(1);
return Pass::Direct;
}
Arg::Aggregate(shape) => shape,
};
if shape.size == 0 {
return Pass::Ignore;
}
call.gp = call.gp.saturating_sub(1);
if fits(shape.size) {
let size = u32::try_from(shape.size).unwrap_or(8);
return Pass::Pieces(vec![Slot::Integer { offset: 0, size }]);
}
Pass::Reference
}
#[cfg(test)]
mod tests {
use rucc_base::float::Format;
use super::super::tests::{float, int, packed, record, target};
use super::super::{Arg, Pass, Slot};
use super::*;
fn call() -> Call {
target("x86_64-pc-windows-msvc").call()
}
#[test]
fn a_size_a_register_holds_travels_as_an_integer_whatever_is_in_it() {
for scalars in [vec![int(1)], vec![int(2)], vec![int(4)], vec![int(4), int(4)]] {
let pieces = packed(&scalars);
let shape = record(&pieces);
let size = u32::try_from(shape.size).expect("a small record");
assert_eq!(
call().argument(&Arg::Aggregate(shape)),
Pass::Pieces(vec![Slot::Integer { offset: 0, size }])
);
}
let pieces = packed(&[float(Format::Single, 4), float(Format::Single, 4)]);
assert_eq!(
call().argument(&Arg::Aggregate(record(&pieces))),
Pass::Pieces(vec![Slot::Integer { offset: 0, size: 8 }])
);
}
#[test]
fn any_other_size_travels_as_an_address() {
let pieces = packed(&[int(1), int(1), int(1)]);
assert_eq!(call().argument(&Arg::Aggregate(record(&pieces))), Pass::Reference);
let pieces = packed(&[int(8), int(8), int(8)]);
assert_eq!(call().argument(&Arg::Aggregate(record(&pieces))), Pass::Reference);
assert_eq!(call().returns(&Arg::Aggregate(record(&pieces))), Pass::Reference);
}
#[test]
fn an_argument_past_the_fourth_travels_the_way_the_first_one_does() {
let pieces = packed(&[int(4), int(4)]);
let mut call = call();
for _ in 0..6 {
assert_eq!(call.argument(&Arg::Scalar(int(8))), Pass::Direct);
}
assert_eq!(
call.argument(&Arg::Aggregate(record(&pieces))),
Pass::Pieces(vec![Slot::Integer { offset: 0, size: 8 }])
);
}
}