use rucc_base::float::Format;
use crate::{Arch, Os, TargetInfo};
mod aapcs;
mod riscv;
mod sysv;
mod win64;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Kind {
Integer,
Float(Format),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Scalar {
pub kind: Kind,
pub size: u64,
pub align: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Piece {
pub offset: u64,
pub scalar: Scalar,
}
impl Piece {
fn end(&self) -> u64 {
self.offset + self.scalar.size.max(1)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Shape<'a> {
pub size: u64,
pub align: u64,
pub pieces: &'a [Piece],
pub complex: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Arg<'a> {
Void,
Scalar(Scalar),
Aggregate(Shape<'a>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Slot {
Integer {
offset: u64,
size: u32,
},
Float {
offset: u64,
format: Format,
},
}
impl Slot {
#[must_use]
pub const fn offset(self) -> u64 {
match self {
Self::Integer { offset, .. } | Self::Float { offset, .. } => offset,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Pass {
Ignore,
Direct,
Pieces(Vec<Slot>),
Reference,
Memory,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Convention {
Sysv,
Aapcs,
Win64,
Riscv,
}
#[derive(Debug)]
pub struct Call {
convention: Convention,
gp: u32,
fp: u32,
}
impl TargetInfo {
#[must_use]
pub fn call(&self) -> Call {
let convention = match (self.triple.arch, self.triple.os) {
(Arch::X86_64, Os::Windows) => Convention::Win64,
(Arch::X86_64, _) => Convention::Sysv,
(Arch::Aarch64, _) => Convention::Aapcs,
(Arch::Riscv64, _) => Convention::Riscv,
};
let (gp, fp) = match convention {
Convention::Sysv => (6, 8),
Convention::Aapcs | Convention::Riscv => (8, 8),
Convention::Win64 => (4, 0),
};
Call { convention, gp, fp }
}
}
impl Call {
#[must_use]
pub fn returns(&mut self, arg: &Arg<'_>) -> Pass {
match self.convention {
Convention::Sysv => sysv::returns(self, arg),
Convention::Aapcs => aapcs::returns(self, arg),
Convention::Win64 => win64::returns(self, arg),
Convention::Riscv => riscv::returns(self, arg),
}
}
#[must_use]
pub fn argument(&mut self, arg: &Arg<'_>) -> Pass {
match self.convention {
Convention::Sysv => sysv::argument(self, arg),
Convention::Aapcs => aapcs::argument(self, arg),
Convention::Win64 => win64::argument(self, arg),
Convention::Riscv => riscv::argument(self, arg),
}
}
}
fn integer_slots(size: u64) -> Vec<Slot> {
(0..size.div_ceil(8))
.map(|index| Slot::Integer {
offset: index * 8,
size: u32::try_from((size - index * 8).min(8)).unwrap_or(8),
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Triple;
pub(super) fn target(triple: &str) -> TargetInfo {
TargetInfo::new(triple.parse::<Triple>().expect("a triple the compiler supports"))
}
pub(super) fn int(size: u64) -> Scalar {
Scalar { kind: Kind::Integer, size, align: size }
}
pub(super) fn float(format: Format, size: u64) -> Scalar {
Scalar { kind: Kind::Float(format), size, align: size }
}
pub(super) const fn gpr(offset: u64, size: u32) -> Slot {
Slot::Integer { offset, size }
}
pub(super) const fn fpr(offset: u64, format: Format) -> Slot {
Slot::Float { offset, format }
}
pub(super) fn packed(scalars: &[Scalar]) -> Vec<Piece> {
let mut pieces = Vec::new();
let mut at: u64 = 0;
for &scalar in scalars {
at = at.next_multiple_of(scalar.align.max(1));
pieces.push(Piece { offset: at, scalar });
at += scalar.size;
}
pieces
}
pub(super) fn record<'a>(pieces: &'a [Piece]) -> Shape<'a> {
let align = pieces.iter().map(|piece| piece.scalar.align).max().unwrap_or(1);
let size = pieces.iter().map(Piece::end).max().unwrap_or(0).next_multiple_of(align);
Shape { size, align, pieces, complex: false }
}
#[test]
fn the_last_register_of_an_aggregate_holds_only_what_is_left_of_it() {
assert_eq!(integer_slots(4), vec![gpr(0, 4)]);
assert_eq!(integer_slots(8), vec![gpr(0, 8)]);
assert_eq!(integer_slots(12), vec![gpr(0, 8), gpr(8, 4)]);
assert_eq!(integer_slots(16), vec![gpr(0, 8), gpr(8, 8)]);
}
#[test]
fn a_triple_picks_the_abi_and_not_the_architecture_alone() {
let mut linux = target("x86_64-unknown-linux-gnu").call();
let mut windows = target("x86_64-pc-windows-msvc").call();
let pieces = packed(&[int(8), int(8)]);
let shape = Arg::Aggregate(record(&pieces));
assert_eq!(linux.argument(&shape), Pass::Pieces(vec![gpr(0, 8), gpr(8, 8)]));
assert_eq!(windows.argument(&shape), Pass::Reference);
}
}