1use rucc_base::Interner;
24use rucc_ir::{Extra, Float, FloatPred, Func, Imm, Inst, InstData, IntPred, Opcode, Type};
25use rucc_target::AbiDescription;
26
27use crate::quad::{ahead_const, becomes, call, flipped, into_call, written};
28
29const NARROW: u32 = 32;
32
33pub fn calls(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription) {
38 let found: Vec<Inst> =
39 func.blocks().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
40 for inst in found {
41 match func[inst].opcode {
42 Opcode::FAdd | Opcode::FSub | Opcode::FMul | Opcode::FDiv => {
43 arithmetic(func, names, abi, inst);
44 }
45 Opcode::FNeg => negate(func, names, abi, inst),
46 Opcode::FCmp => compare(func, names, abi, inst),
47 Opcode::FPExt | Opcode::FPTrunc => convert(func, names, abi, inst),
48 Opcode::SIToFP | Opcode::UIToFP => from_integer(func, names, abi, inst),
49 Opcode::FPToSI | Opcode::FPToUI => to_integer(func, names, abi, inst),
50 _ => {}
51 }
52 }
53}
54
55fn letters(ty: Type) -> Option<&'static str> {
57 if !ty.is_scalar() {
58 return None;
59 }
60 Some(match ty.format()? {
61 Float::D32 => "sd",
62 Float::D64 => "dd",
63 Float::D128 => "td",
64 Float::F16 => "hf",
65 Float::F32 => "sf",
66 Float::F64 => "df",
67 Float::F80 => "xf",
68 Float::F128 => "tf",
69 })
70}
71
72fn decimal(ty: Type) -> Option<&'static str> {
74 if ty.format().is_some_and(Float::is_decimal) { letters(ty) } else { None }
75}
76
77fn produced(func: &Func, inst: Inst) -> Option<Type> {
79 func[inst].first_result.map(|value| func[value].ty)
80}
81
82fn arithmetic(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
84 let Some(ty) = produced(func, inst) else { return };
85 let Some(mode) = decimal(ty) else { return };
86 let args = func[func[inst].args].to_vec();
87 let [a, b] = args[..] else { return };
88 let operation = match func[inst].opcode {
89 Opcode::FAdd => "add",
90 Opcode::FSub => "sub",
91 Opcode::FMul => "mul",
92 _ => "div",
93 };
94 into_call(func, names, abi, inst, &format!("__bid_{operation}{mode}3"), &[a, b]);
95}
96
97fn negate(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
105 let Some(ty) = produced(func, inst) else { return };
106 let Some(&arg) = func[func[inst].args].first() else { return };
107 if decimal(ty) != Some("td") {
108 return;
109 }
110 into_call(func, names, abi, inst, "__negtf2", &[arg]);
111}
112
113fn compare(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
119 let args = func[func[inst].args].to_vec();
120 let [a, b] = args[..] else { return };
121 let Some(mode) = decimal(func[a].ty) else { return };
122 let Extra::FloatPred(pred) = func[inst].extra else { return };
123 let named = |operation: &str| format!("__bid_{operation}{mode}2");
124 let answer = Type::int(NARROW);
125 let single = match pred {
126 FloatPred::Oeq => Some(("eq", IntPred::Eq)),
127 FloatPred::Une => Some(("ne", IntPred::Ne)),
128 FloatPred::Olt => Some(("lt", IntPred::Slt)),
129 FloatPred::Ole => Some(("le", IntPred::Sle)),
130 FloatPred::Ogt => Some(("gt", IntPred::Sgt)),
131 FloatPred::Oge => Some(("ge", IntPred::Sge)),
132 FloatPred::Uno => Some(("unord", IntPred::Ne)),
133 FloatPred::Ord => Some(("unord", IntPred::Eq)),
134 FloatPred::Ult => Some(("ge", IntPred::Slt)),
135 FloatPred::Ule => Some(("gt", IntPred::Sle)),
136 FloatPred::Ugt => Some(("le", IntPred::Sgt)),
137 FloatPred::Uge => Some(("lt", IntPred::Sge)),
138 _ => None,
139 };
140 if let Some((operation, test)) = single {
141 let got = call(func, names, abi, inst, &named(operation), &[a, b], answer);
142 let zero = ahead_const(func, inst, Imm::int(0, answer), answer);
143 becomes(func, inst, Opcode::ICmp, Extra::IntPred(test), &[got, zero]);
144 return;
145 }
146 if let FloatPred::False | FloatPred::True = pred {
147 let bits = i128::from(pred == FloatPred::True);
148 let extra = Extra::Imm(func.add_imm(Imm::int(bits, Type::I1)));
149 becomes(func, inst, Opcode::IConst, extra, &[]);
150 return;
151 }
152 let (FloatPred::One | FloatPred::Ueq) = pred else { return };
153 let mut tested = |operation: &str, test: IntPred| {
154 let got = call(func, names, abi, inst, &named(operation), &[a, b], answer);
155 let zero = ahead_const(func, inst, Imm::int(0, answer), answer);
156 let args = func.push_values(&[got, zero]);
157 let extra = Extra::IntPred(test);
158 written(func, inst, InstData { args, extra, ..InstData::new(Opcode::ICmp) }, Type::I1)
159 };
160 let ordered = tested("unord", IntPred::Eq);
161 let different = tested("ne", IntPred::Ne);
162 let (opcode, args) = if pred == FloatPred::One {
163 (Opcode::And, [ordered, different])
164 } else {
165 let unordered = flipped(func, inst, ordered);
166 let same = flipped(func, inst, different);
167 (Opcode::Or, [unordered, same])
168 };
169 becomes(func, inst, opcode, Extra::None, &args);
170}
171
172fn convert(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
177 let Some(ty) = produced(func, inst) else { return };
178 let Some(&arg) = func[func[inst].args].first() else { return };
179 let from = func[arg].ty;
180 if decimal(ty).is_none() && decimal(from).is_none() {
181 return;
182 }
183 let (Some(into), Some(out)) = (letters(ty), letters(from)) else { return };
184 let way = if across(from, ty) { "extend" } else { "trunc" };
185 let tail = if decimal(ty).is_some() && decimal(from).is_some() { "2" } else { "" };
186 into_call(func, names, abi, inst, &format!("__bid_{way}{out}{into}{tail}"), &[arg]);
187}
188
189fn across(from: Type, into: Type) -> bool {
196 let (a, b) = (into.bits(), from.bits());
197 a > b || (a == b && decimal(into).is_some())
198}
199
200fn holder(bits: u32) -> Option<(u32, &'static str)> {
207 match bits {
208 0..=32 => Some((32, "si")),
209 33..=64 => Some((64, "di")),
210 65..=128 => Some((128, "ti")),
211 _ => None,
212 }
213}
214
215fn from_integer(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
217 let Some(ty) = produced(func, inst) else { return };
218 let Some(&arg) = func[func[inst].args].first() else { return };
219 let from = func[arg].ty;
220 let Some(mode) = decimal(ty) else { return };
221 if !from.is_int() || !from.is_scalar() {
222 return;
223 }
224 let Some((width, letters)) = holder(from.bits()) else { return };
225 let signed = func[inst].opcode == Opcode::SIToFP;
226 let value = if from.bits() == width {
227 arg
228 } else {
229 let opcode = if signed { Opcode::SExt } else { Opcode::ZExt };
230 let args = func.push_values(&[arg]);
231 written(func, inst, InstData { args, ..InstData::new(opcode) }, Type::int(width))
232 };
233 let kind = if signed { "float" } else { "floatuns" };
234 into_call(func, names, abi, inst, &format!("__bid_{kind}{letters}{mode}"), &[value]);
235}
236
237fn to_integer(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
240 let Some(ty) = produced(func, inst) else { return };
241 let Some(&arg) = func[func[inst].args].first() else { return };
242 let Some(mode) = decimal(func[arg].ty) else { return };
243 if !ty.is_int() || !ty.is_scalar() {
244 return;
245 }
246 let Some((width, letters)) = holder(ty.bits()) else { return };
247 let kind = if func[inst].opcode == Opcode::FPToSI { "fix" } else { "fixuns" };
248 let routine = format!("__bid_{kind}{mode}{letters}");
249 if ty.bits() == width {
250 into_call(func, names, abi, inst, &routine, &[arg]);
251 return;
252 }
253 let answer = call(func, names, abi, inst, &routine, &[arg], Type::int(width));
254 becomes(func, inst, Opcode::Trunc, Extra::None, &[answer]);
255}
256
257#[cfg(test)]
258mod tests {
259 use rucc_base::Interner;
260 use rucc_ir::{Block, Builder, Flags, Module, Signature, Value};
261 use rucc_target::{Arch, Env, Os, TargetInfo, Triple, x86_64};
262
263 use super::{Float, FloatPred, Func, Opcode, Type, calls};
264
265 fn target() -> TargetInfo {
266 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
267 }
268
269 fn printed(func: &Func, names: &mut Interner) -> String {
270 let module = Module::new(names.intern("d.c"), &target());
271 rucc_ir::print_func(&module, func, names)
272 }
273
274 fn shell(names: &mut Interner, params: &[Type], returns: &[Type]) -> (Func, Block, Vec<Value>) {
275 let signature = Signature::new().with_params(params).with_returns(returns);
276 let mut func = Func::new(names.intern("f"), signature);
277 let entry = func.create_block();
278 let values = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
279 (func, entry, values)
280 }
281
282 fn after(
284 params: &[Type],
285 returns: Type,
286 make: impl FnOnce(&mut Builder<'_>, &[Value]) -> Value,
287 ) -> String {
288 let mut names = Interner::new();
289 let (mut func, entry, args) = shell(&mut names, params, &[returns]);
290 let mut build = Builder::new(&mut func, entry);
291 let made = make(&mut build, &args);
292 build.ret(&[made]);
293 calls(&mut func, &mut names, x86_64::SYSV.abi);
294 printed(&func, &mut names)
295 }
296
297 #[test]
298 fn arithmetic_at_each_width_is_the_routine_with_that_widths_letters() {
299 for (format, name) in [
300 (Float::D32, "__bid_addsd3"),
301 (Float::D64, "__bid_adddd3"),
302 (Float::D128, "__bid_addtd3"),
303 ] {
304 let ty = Type::float(format);
305 let text = after(&[ty, ty], ty, |build, args| {
306 build.binary(Opcode::FAdd, args[0], args[1], Flags::NONE)
307 });
308 assert!(text.contains(name), "{text}");
309 assert!(!text.contains("fadd"), "{text}");
310 }
311 }
312
313 #[test]
314 fn a_binary_float_is_left_for_the_steps_after_this_one() {
315 let ty = Type::float(Float::F64);
316 let text = after(&[ty, ty], ty, |build, args| {
317 build.binary(Opcode::FMul, args[0], args[1], Flags::NONE)
318 });
319 assert!(text.contains("fmul"), "{text}");
320 assert!(!text.contains("__bid"), "{text}");
321 }
322
323 #[test]
324 fn a_comparison_is_the_routine_and_a_test_of_its_answer() {
325 let ty = Type::float(Float::D64);
326 let text = after(&[ty, ty], Type::I1, |build, args| {
327 build.fcmp(FloatPred::Olt, args[0], args[1], Flags::NONE)
328 });
329 assert!(text.contains("__bid_ltdd2"), "{text}");
330 assert!(text.contains("icmp slt"), "{text}");
331 let text = after(&[ty, ty], Type::I1, |build, args| {
332 build.fcmp(FloatPred::One, args[0], args[1], Flags::NONE)
333 });
334 assert!(text.contains("__bid_unorddd2") && text.contains("__bid_nedd2"), "{text}");
335 }
336
337 #[test]
338 fn a_conversion_is_named_the_way_libgcc_names_it() {
339 let rows = [
340 (Float::D32, Float::D64, "__bid_extendsddd2"),
341 (Float::D128, Float::D64, "__bid_trunctddd2"),
342 (Float::F64, Float::D64, "__bid_extenddfdd"),
343 (Float::D64, Float::F64, "__bid_truncdddf"),
344 (Float::F80, Float::D64, "__bid_truncxfdd"),
345 (Float::F80, Float::D128, "__bid_extendxftd"),
346 (Float::D32, Float::F128, "__bid_extendsdtf"),
347 ];
348 for (from, into, name) in rows {
349 let (from, into) = (Type::float(from), Type::float(into));
350 let opcode = if super::across(from, into) { Opcode::FPExt } else { Opcode::FPTrunc };
351 let text = after(&[from], into, |build, args| build.unary(opcode, args[0], into));
352 assert!(text.contains(name), "{name}: {text}");
353 }
354 }
355
356 #[test]
357 fn an_integer_conversion_goes_through_a_width_there_is_a_routine_at() {
358 let ty = Type::float(Float::D64);
359 let text =
360 after(&[Type::int(16)], ty, |build, args| build.unary(Opcode::SIToFP, args[0], ty));
361 assert!(text.contains("sext") && text.contains("__bid_floatsidd"), "{text}");
362 let text = after(&[ty], Type::int(8), |build, args| {
363 build.unary(Opcode::FPToUI, args[0], Type::int(8))
364 });
365 assert!(text.contains("__bid_fixunsddsi") && text.contains("trunc"), "{text}");
366 let text =
367 after(&[Type::int(128)], ty, |build, args| build.unary(Opcode::UIToFP, args[0], ty));
368 assert!(text.contains("__bid_floatunstidd"), "{text}");
369 }
370}