use rucc_ir::Type;
use super::{Insts, place};
pub static INSTS: Insts = Insts {
arg: head_of,
load: load_of,
store: store_of,
ret: ret_of,
call: "a64.bl",
call_reg: "a64.blr",
lea: "a64.lea_64",
small: "a64.mov_ri_32",
};
fn head_of(ty: Type) -> Option<&'static str> {
if crate::term::is_quad(ty) {
return Some("a64.arg_val_f128");
}
if crate::term::is_half(ty) {
return None;
}
if let Some(at) = crate::term::float_slot(ty) {
return Some(["a64.arg_val_f32", "a64.arg_val_f64"][at]);
}
let names = ["a64.arg_val_32", "a64.arg_val_32", "a64.arg_val_32", "a64.arg_val_64"];
Some(names[place(ty)?])
}
fn load_of(ty: Type) -> Option<&'static str> {
if crate::term::is_quad(ty) {
return Some("a64.ldr_f128");
}
if crate::term::is_half(ty) {
return None;
}
if let Some(at) = crate::term::float_slot(ty) {
return Some(["a64.ldr_f32", "a64.ldr_f64"][at]);
}
let names = ["a64.ldr_8", "a64.ldr_16", "a64.ldr_32", "a64.ldr_64"];
Some(names[place(ty)?])
}
fn store_of(ty: Type) -> Option<&'static str> {
if crate::term::is_quad(ty) {
return Some("a64.str_f128");
}
if crate::term::is_half(ty) {
return None;
}
if let Some(at) = crate::term::float_slot(ty) {
return Some(["a64.str_f32", "a64.str_f64"][at]);
}
let names = ["a64.str_8", "a64.str_16", "a64.str_32", "a64.str_64"];
Some(names[place(ty)?])
}
fn ret_of(ty: Type, at: usize) -> Option<&'static str> {
if crate::term::is_quad(ty) {
let names =
["a64.ret_val_f128", "a64.ret_val2_f128", "a64.ret_val3_f128", "a64.ret_val4_f128"];
return Some(*names.get(at)?);
}
if crate::term::is_half(ty) {
return None;
}
if let Some(width) = crate::term::float_slot(ty) {
let names = [
["a64.ret_val_f32", "a64.ret_val_f64"],
["a64.ret_val2_f32", "a64.ret_val2_f64"],
["a64.ret_val3_f32", "a64.ret_val3_f64"],
["a64.ret_val4_f32", "a64.ret_val4_f64"],
];
return Some(names.get(at)?[width]);
}
let names = [
["a64.ret_val_32", "a64.ret_val_32", "a64.ret_val_32", "a64.ret_val_64"],
["a64.ret_val2_32", "a64.ret_val2_32", "a64.ret_val2_32", "a64.ret_val2_64"],
];
Some(names.get(at)?[place(ty)?])
}
#[cfg(test)]
mod tests {
use rucc_ir::{Float, Type};
use rucc_target::aarch64;
use super::*;
fn types() -> Vec<Type> {
let mut types: Vec<Type> = [1, 8, 16, 32, 64, 128].into_iter().map(Type::int).collect();
types
.extend([Float::F16, Float::F32, Float::F64, Float::F80, Float::F128].map(Type::float));
types.push(Type::PTR);
types
}
#[test]
fn every_name_is_an_instruction_the_machine_describes() {
let described = |name: &str| {
let bare = name.strip_prefix("a64.").expect("an AArch64 name");
assert!(aarch64::form(bare).is_some(), "{name}");
};
for ty in types() {
for name in [head_of(ty), load_of(ty), store_of(ty)]
.into_iter()
.chain((0..4).map(|at| ret_of(ty, at)))
{
name.map(described);
}
}
for name in [INSTS.call, INSTS.call_reg, INSTS.lea, INSTS.small] {
described(name);
}
}
#[test]
fn the_four_answer_for_the_same_types() {
for ty in types() {
let arrives = head_of(ty).is_some();
assert_eq!(arrives, load_of(ty).is_some(), "{ty:?}");
assert_eq!(arrives, store_of(ty).is_some(), "{ty:?}");
assert_eq!(arrives, ret_of(ty, 0).is_some(), "{ty:?}");
}
}
#[test]
fn a_narrow_integer_travels_in_a_32_bit_register_and_is_read_at_its_own_width() {
assert_eq!(head_of(Type::int(8)), Some("a64.arg_val_32"));
assert_eq!(head_of(Type::int(16)), Some("a64.arg_val_32"));
assert_eq!(head_of(Type::PTR), Some("a64.arg_val_64"));
assert_eq!(load_of(Type::int(8)), Some("a64.ldr_8"));
assert_eq!(store_of(Type::int(16)), Some("a64.str_16"));
assert_eq!(ret_of(Type::int(1), 0), Some("a64.ret_val_32"));
assert_eq!(ret_of(Type::int(64), 1), Some("a64.ret_val2_64"));
assert_eq!(ret_of(Type::int(64), 2), None);
}
#[test]
fn a_floating_point_aggregate_comes_back_in_up_to_four_registers() {
assert_eq!(ret_of(Type::float(Float::F32), 2), Some("a64.ret_val3_f32"));
assert_eq!(ret_of(Type::float(Float::F64), 3), Some("a64.ret_val4_f64"));
assert_eq!(ret_of(Type::float(Float::F128), 3), Some("a64.ret_val4_f128"));
assert_eq!(ret_of(Type::float(Float::F64), 4), None);
}
#[test]
fn a_long_double_is_a_quad_and_a_half_is_not_passed_yet() {
assert_eq!(head_of(Type::float(Float::F128)), Some("a64.arg_val_f128"));
assert_eq!(head_of(Type::float(Float::F16)), None);
assert_eq!(head_of(Type::int(128)), None);
}
}