1use rucc_ir::{Abi, Type};
11
12use super::{Insts, place};
13
14pub static INSTS: Insts = Insts {
16 arg: head_of,
17 load: load_of,
18 store: store_of,
19 ret: ret_of,
20 call: "a64.bl",
21 call_reg: "a64.blr",
22 lea: "a64.lea_64",
23 small: "a64.mov_ri_32",
24 extend: extend_of,
25};
26
27fn extend_of(ty: Type, abi: Abi) -> Option<&'static str> {
34 match (abi, ty.bits()) {
35 (Abi::Zext, 1) => Some("a64.bit_to_32"),
36 (Abi::Sext, 8) => Some("a64.sxtb_32"),
37 (Abi::Sext, 16) => Some("a64.sxth_32"),
38 (Abi::Zext, 8) => Some("a64.uxtb_32"),
39 (Abi::Zext, 16) => Some("a64.uxth_32"),
40 _ => None,
41 }
42}
43
44fn head_of(ty: Type) -> Option<&'static str> {
46 if crate::term::is_quad(ty) {
47 return Some("a64.arg_val_f128");
48 }
49 if crate::term::is_half(ty) {
50 return None;
51 }
52 if let Some(at) = crate::term::float_slot(ty) {
53 return Some(["a64.arg_val_f32", "a64.arg_val_f64"][at]);
54 }
55 let names = ["a64.arg_val_32", "a64.arg_val_32", "a64.arg_val_32", "a64.arg_val_64"];
56 Some(names[place(ty)?])
57}
58
59fn load_of(ty: Type) -> Option<&'static str> {
64 if crate::term::is_quad(ty) {
65 return Some("a64.ldr_f128");
66 }
67 if crate::term::is_half(ty) {
68 return None;
69 }
70 if let Some(at) = crate::term::float_slot(ty) {
71 return Some(["a64.ldr_f32", "a64.ldr_f64"][at]);
72 }
73 let names = ["a64.ldr_8", "a64.ldr_16", "a64.ldr_32", "a64.ldr_64"];
74 Some(names[place(ty)?])
75}
76
77fn store_of(ty: Type) -> Option<&'static str> {
79 if crate::term::is_quad(ty) {
80 return Some("a64.str_f128");
81 }
82 if crate::term::is_half(ty) {
83 return None;
84 }
85 if let Some(at) = crate::term::float_slot(ty) {
86 return Some(["a64.str_f32", "a64.str_f64"][at]);
87 }
88 let names = ["a64.str_8", "a64.str_16", "a64.str_32", "a64.str_64"];
89 Some(names[place(ty)?])
90}
91
92fn ret_of(ty: Type, at: usize) -> Option<&'static str> {
99 if crate::term::is_quad(ty) {
100 let names =
101 ["a64.ret_val_f128", "a64.ret_val2_f128", "a64.ret_val3_f128", "a64.ret_val4_f128"];
102 return Some(*names.get(at)?);
103 }
104 if crate::term::is_half(ty) {
105 return None;
106 }
107 if let Some(width) = crate::term::float_slot(ty) {
108 let names = [
111 ["a64.ret_val_f32", "a64.ret_val_f64"],
112 ["a64.ret_val2_f32", "a64.ret_val2_f64"],
113 ["a64.ret_val3_f32", "a64.ret_val3_f64"],
114 ["a64.ret_val4_f32", "a64.ret_val4_f64"],
115 ];
116 return Some(names.get(at)?[width]);
117 }
118 let names = [
119 ["a64.ret_val_32", "a64.ret_val_32", "a64.ret_val_32", "a64.ret_val_64"],
120 ["a64.ret_val2_32", "a64.ret_val2_32", "a64.ret_val2_32", "a64.ret_val2_64"],
121 ];
122 Some(names.get(at)?[place(ty)?])
123}
124
125#[cfg(test)]
126mod tests {
127 use rucc_ir::{Float, Type};
128 use rucc_target::aarch64;
129
130 use super::*;
131
132 fn types() -> Vec<Type> {
134 let mut types: Vec<Type> = [1, 8, 16, 32, 64, 128].into_iter().map(Type::int).collect();
135 types
136 .extend([Float::F16, Float::F32, Float::F64, Float::F80, Float::F128].map(Type::float));
137 types.push(Type::PTR);
138 types
139 }
140
141 #[test]
142 fn every_name_is_an_instruction_the_machine_describes() {
143 let described = |name: &str| {
144 let bare = name.strip_prefix("a64.").expect("an AArch64 name");
145 assert!(aarch64::form(bare).is_some(), "{name}");
146 };
147 for ty in types() {
148 for name in [head_of(ty), load_of(ty), store_of(ty)]
149 .into_iter()
150 .chain((0..4).map(|at| ret_of(ty, at)))
151 {
152 name.map(described);
153 }
154 }
155 for name in [INSTS.call, INSTS.call_reg, INSTS.lea, INSTS.small] {
156 described(name);
157 }
158 }
159
160 #[test]
161 fn the_four_answer_for_the_same_types() {
162 for ty in types() {
163 let arrives = head_of(ty).is_some();
164 assert_eq!(arrives, load_of(ty).is_some(), "{ty:?}");
165 assert_eq!(arrives, store_of(ty).is_some(), "{ty:?}");
166 assert_eq!(arrives, ret_of(ty, 0).is_some(), "{ty:?}");
167 }
168 }
169
170 #[test]
171 fn a_narrow_integer_travels_in_a_32_bit_register_and_is_read_at_its_own_width() {
172 assert_eq!(head_of(Type::int(8)), Some("a64.arg_val_32"));
173 assert_eq!(head_of(Type::int(16)), Some("a64.arg_val_32"));
174 assert_eq!(head_of(Type::PTR), Some("a64.arg_val_64"));
175 assert_eq!(load_of(Type::int(8)), Some("a64.ldr_8"));
176 assert_eq!(store_of(Type::int(16)), Some("a64.str_16"));
177 assert_eq!(ret_of(Type::int(1), 0), Some("a64.ret_val_32"));
178 assert_eq!(ret_of(Type::int(64), 1), Some("a64.ret_val2_64"));
179 assert_eq!(ret_of(Type::int(64), 2), None);
180 }
181
182 #[test]
185 fn a_floating_point_aggregate_comes_back_in_up_to_four_registers() {
186 assert_eq!(ret_of(Type::float(Float::F32), 2), Some("a64.ret_val3_f32"));
187 assert_eq!(ret_of(Type::float(Float::F64), 3), Some("a64.ret_val4_f64"));
188 assert_eq!(ret_of(Type::float(Float::F128), 3), Some("a64.ret_val4_f128"));
189 assert_eq!(ret_of(Type::float(Float::F64), 4), None);
190 }
191
192 #[test]
193 fn a_long_double_is_a_quad_and_a_half_is_not_passed_yet() {
194 assert_eq!(head_of(Type::float(Float::F128)), Some("a64.arg_val_f128"));
195 assert_eq!(head_of(Type::float(Float::F16)), None);
196 assert_eq!(head_of(Type::int(128)), None);
197 }
198}