Skip to main content

rucc_codegen/
quad.rs

1//! The float that is wider than any instruction, as the calls that do the work.
2//!
3//! `_Float128` is the one arithmetic type a C program writes that no machine computes with.
4//! Sixteen bytes fit in a vector register, so moving one, passing one and returning one are
5//! instructions this back end has, and `spec/10-backend.md` section 10.8's rule set is written at
6//! this width for exactly those three. Everything else is a call: no processor anyone compiles for
7//! has an instruction that adds two binary128 values, which is why `spec/12-abi-and-runtime.md`
8//! section 12.8 puts the whole format in the runtime rather than in the part of the runtime that
9//! exists for targets with no floating point unit. This pass is what turns the operation the
10//! program wrote into the call the routine is.
11//!
12//! After it there is no arithmetic, no comparison and no conversion at this format left in the
13//! function, which is what lets the selector go on being a table over widths the machine has. What
14//! is left at the width is the three things a rule is written for, and a call is one of them in the
15//! sense that matters: the value travels in the register the convention names.
16//!
17//! # Why a pass and not a rule
18//!
19//! The same reason [`crate::wide`] is a pass. A rule rewrites a term into instructions of the
20//! machine, and there is no instruction here to rewrite into, so there is nothing for a rule to
21//! produce. A call is not a rewrite of a term either, because what it needs first is the
22//! convention's answer about where each operand goes, which is `crate::abi`'s and not a rule's.
23//!
24//! # The names are libgcc's, and the spelling is a target fact
25//!
26//! `__addtf3` and the rest, which is what `runtime/builtins/quad.c` defines and what libgcc defines
27//! beside it, so a call this pass writes links against either. The `tf` in the name means binary128
28//! on every target this compiler has a back end for, and on ppc64 it does not: there `long double`
29//! is a pair of doubles, `__addtf3` is the arithmetic on that pair, and binary128 is spelled `kf`.
30//! So the table below is right for the back ends that exist and is the first thing to look at when
31//! a ppc64 one does, which is `tamnd/rucc#618`'s row rather than this pass's business today.
32//!
33//! # What is left alone
34//!
35//! An operation at this format whose routine is not in the archive is left exactly as it was and
36//! refused below by name, the same way [`crate::wide`] leaves a conversion at eighty bits alone.
37//! That is a conversion against a `_Float16` or an eighty bit float. A refusal naming the
38//! instruction is the outcome both of those had before this pass existed and it is still the right
39//! one: the alternative is a call to a routine no archive defines, which is a link that fails
40//! further from the cause.
41//!
42//! A `select` of two quads used to be listed here as a third one, and it is not, because nothing in
43//! this compiler can build one. `select` is an integer instruction: [`rucc_opt::phiopt`] is the only
44//! pass that turns a choice into one and it asks for a scalar integer of eight to sixty four bits
45//! before it will, every other writer of one in the tree is choosing between integers, and the rule
46//! set answers it with a conditional move, which this machine has for a general purpose register and
47//! for nothing else. A conditional expression over two quads is a branch and a phi and stays one. So
48//! the refusal that named it was a guard against a shape no front end path and no pass produces, and
49//! saying it was left alone was describing a gap that is not there. If a float `select` is ever
50//! wanted, what decides it is the machine rather than this pass, since a quad lives in a vector
51//! register and there is no conditional move for one, so it would be a mask and two ands and an or
52//! rather than a call.
53//!
54//! A conversion against a `__int128` is not in that list and is not this pass's work either.
55//! [`crate::wide`] runs above here and turns one into a call to `__floattitf`, `__floatuntitf`,
56//! `__fixtfti` or `__fixunstfti`, with the integer as the pair of words the convention passes it in,
57//! so by the time this pass looks there is nothing at that width left to refuse.
58
59use rucc_base::Interner;
60use rucc_ir::{
61    Abi, CallInfo, Extra, Flags, Float, FloatPred, Func, Imm, Inst, InstData, IntPred, MemInfo,
62    MemOrder, Opcode, Param, Restrict, Signature, Type, Value,
63};
64use rucc_target::AbiDescription;
65
66use crate::capability;
67
68/// The routine the capability table names for this operation at this mode.
69///
70/// Every mode this pass asks about is one no instruction on this machine covers, which is the whole
71/// reason the pass exists, so the table always has an answer. A missing one is the table and this
72/// pass having gone out of step rather than anything a program can reach.
73fn routine(opcode: Opcode, mode: &str) -> &'static str {
74    capability::libcall(opcode, mode)
75        .unwrap_or_else(|| panic!("no routine for `{}` at `{mode}`", opcode.name()))
76}
77
78/// The format this pass is about.
79const QUAD: Float = Float::F128;
80
81/// What the capability table calls that format, which is how the rule language spells one.
82const MODE: &str = "f128";
83
84/// How wide it is, in bits and then in bytes.
85const BITS: u32 = 128;
86const BYTES: u64 = (BITS / 8) as u64;
87
88/// The two widths the runtime has an integer conversion at, which are the two a C program on a
89/// machine with sixty four bit registers has integers of.
90const NARROW: u32 = 32;
91const WORD: u32 = 64;
92
93/// Rewrites every operation at this format into the call that performs it.
94///
95/// The instructions are collected before any of them is touched, because a rewrite puts
96/// instructions in front of the one it replaces and the walk would otherwise see its own work.
97pub fn calls(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription) {
98    let found: Vec<Inst> =
99        func.blocks().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
100    for inst in found {
101        match func[inst].opcode {
102            Opcode::FAdd | Opcode::FSub | Opcode::FMul | Opcode::FDiv => {
103                arithmetic(func, names, abi, inst);
104            }
105            Opcode::FNeg => negate(func, names, abi, inst),
106            Opcode::FCmp => compare(func, names, abi, inst),
107            Opcode::FConst => constant(func, inst),
108            Opcode::FPExt => widen(func, names, abi, inst),
109            Opcode::FPTrunc => narrow(func, names, abi, inst),
110            Opcode::SIToFP | Opcode::UIToFP => from_integer(func, names, abi, inst),
111            Opcode::FPToSI | Opcode::FPToUI => to_integer(func, names, abi, inst),
112            _ => {}
113        }
114    }
115}
116
117/// Whether this type is the format.
118fn quad(ty: Type) -> bool {
119    ty.is_scalar() && ty.format() == Some(QUAD)
120}
121
122/// The type of an instruction's first result, or nothing where it has none.
123fn produced(func: &Func, inst: Inst) -> Option<Type> {
124    func[inst].first_result.map(|value| func[value].ty)
125}
126
127/// The four operations, each of them the routine of its name over the two operands.
128///
129/// The call goes in place of the instruction rather than in front of it, so the value the rest of
130/// the function reads is the value it already read and nothing has to be substituted anywhere.
131/// That works here and not in [`crate::wide`] because the answer is one value of the same type:
132/// nothing about this format is split, it simply is not computed.
133fn arithmetic(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
134    let Some(ty) = produced(func, inst) else { return };
135    if !quad(ty) {
136        return;
137    }
138    let args = func[func[inst].args].to_vec();
139    let [a, b] = args[..] else { return };
140    // Which opcodes are a binary operation is a fact about their shape and is decided here. Which
141    // routine each one is, is a fact about what this target cannot do and is in the table.
142    let opcode = func[inst].opcode;
143    let (Opcode::FAdd | Opcode::FSub | Opcode::FMul | Opcode::FDiv) = opcode else { return };
144    let Some(routine) = capability::libcall(opcode, MODE) else { return };
145    into_call(func, names, abi, inst, routine, &[a, b]);
146}
147
148/// The negation, which is a call here and an exclusive or at the two narrower formats.
149///
150/// [`crate::expand::floats`] flips the sign bit of a narrower float in a general purpose register,
151/// and the exchange that makes that worth doing runs out at this width twice over: the integer that
152/// would hold the bits has no register either, and the mask would want a constant pool that nothing
153/// else in this back end needs. libgcc has the routine, so the routine is what this is.
154fn negate(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
155    let Some(ty) = produced(func, inst) else { return };
156    let Some(&arg) = func[func[inst].args].first() else { return };
157    if !quad(ty) {
158        return;
159    }
160    into_call(func, names, abi, inst, routine(Opcode::FNeg, MODE), &[arg]);
161}
162
163/// A comparison, as the call that answers it and the test of that answer against zero.
164///
165/// Only the sign of what these routines hand back is specified, never its magnitude, so the caller
166/// compares against zero and the predicate it compares with is the one the name promised. Six of
167/// the sixteen predicates are a routine each, six more are one of those six read the other way
168/// round, and two need both calls.
169///
170/// The six that are one call are the ordered comparisons, because the number a routine answers for
171/// a not a number is the one that makes its own test come out false. So `__lttf2` is below zero for
172/// `a < b` and above zero for a not a number, and a test for below zero is then ordered and less
173/// than and nothing else. Reading that same answer as at or above zero is unordered or greater than
174/// or equal, which is the negation, and that is where the other six come from: `ult` is not `oge`,
175/// `ule` is not `ogt`, and so on down.
176///
177/// `one` and `ueq` are the two that are not a reading of one answer. Ordered and not equal is
178/// neither operand a not a number and the two of them different, and there is no single routine for
179/// it, so it is `__unordtf2` saying ordered and `__netf2` saying different. Unordered or equal is
180/// the negation of that and is the same two calls with the other connective. gcc emits the pair for
181/// them too. Neither is a shape C's operators produce, since `!(a == b)` is unordered or not equal
182/// and not this, but the optimizer may fold its way to one and the back end has to have an answer.
183fn compare(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
184    let args = func[func[inst].args].to_vec();
185    let [a, b] = args[..] else { return };
186    if !quad(func[a].ty) || !quad(func[b].ty) {
187        return;
188    }
189    let Extra::FloatPred(pred) = func[inst].extra else { return };
190    if let Some((routine, test)) = single(pred) {
191        let answer = call(func, names, abi, inst, routine, &[a, b], Type::int(NARROW));
192        let zero = ahead_const(func, inst, Imm::int(0, Type::int(NARROW)), Type::int(NARROW));
193        let extra = Extra::IntPred(test);
194        becomes(func, inst, Opcode::ICmp, extra, &[answer, zero]);
195        return;
196    }
197    // Always false and always true are constants rather than comparisons, and they are here rather
198    // than left alone because a rule at this format would be a rule about an operand it never
199    // reads.
200    if let FloatPred::False | FloatPred::True = pred {
201        let bits = u128::from(pred == FloatPred::True);
202        let extra = Extra::Imm(func.add_imm(Imm::int(bits as i128, Type::I1)));
203        becomes(func, inst, Opcode::IConst, extra, &[]);
204        return;
205    }
206    let (FloatPred::One | FloatPred::Ueq) = pred else { return };
207    let uno = routine(Opcode::FCmp, "uno.f128");
208    let une = routine(Opcode::FCmp, "une.f128");
209    let ordered = pair(func, names, abi, inst, uno, &[a, b], IntPred::Eq);
210    let different = pair(func, names, abi, inst, une, &[a, b], IntPred::Ne);
211    // Ordered and different, or the negation of it, which by De Morgan is unordered or the same.
212    let (opcode, args) = if pred == FloatPred::One {
213        (Opcode::And, [ordered, different])
214    } else {
215        let unordered = flipped(func, inst, ordered);
216        let same = flipped(func, inst, different);
217        (Opcode::Or, [unordered, same])
218    };
219    becomes(func, inst, opcode, Extra::None, &args);
220}
221
222/// The routine for a predicate that is one call, and the test its answer is read with.
223fn single(pred: FloatPred) -> Option<(&'static str, IntPred)> {
224    // The left of each pair is the routine, which the table names by the predicate the routine
225    // itself answers, and the right is how this predicate reads that answer. The four unordered
226    // ones are an ordered routine read as its negation, which is why the two halves differ there.
227    let (named, test) = match pred {
228        FloatPred::Oeq => ("oeq.f128", IntPred::Eq),
229        FloatPred::Une => ("une.f128", IntPred::Ne),
230        FloatPred::Olt => ("olt.f128", IntPred::Slt),
231        FloatPred::Ole => ("ole.f128", IntPred::Sle),
232        FloatPred::Ogt => ("ogt.f128", IntPred::Sgt),
233        FloatPred::Oge => ("oge.f128", IntPred::Sge),
234        FloatPred::Uno => ("uno.f128", IntPred::Ne),
235        FloatPred::Ord => ("uno.f128", IntPred::Eq),
236        // The four that are one of the ordered answers read as its negation.
237        FloatPred::Ult => ("oge.f128", IntPred::Slt),
238        FloatPred::Ule => ("ogt.f128", IntPred::Sle),
239        FloatPred::Ugt => ("ole.f128", IntPred::Sgt),
240        FloatPred::Uge => ("olt.f128", IntPred::Sge),
241        _ => return None,
242    };
243    Some((routine(Opcode::FCmp, named), test))
244}
245
246/// One of the two calls a `one` or a `ueq` is made of, and its answer tested against zero.
247fn pair(
248    func: &mut Func,
249    names: &mut Interner,
250    abi: &'static AbiDescription,
251    inst: Inst,
252    routine: &str,
253    args: &[Value],
254    test: IntPred,
255) -> Value {
256    let answer = call(func, names, abi, inst, routine, args, Type::int(NARROW));
257    let zero = ahead_const(func, inst, Imm::int(0, Type::int(NARROW)), Type::int(NARROW));
258    let args = func.push_values(&[answer, zero]);
259    let extra = Extra::IntPred(test);
260    written(func, inst, InstData { args, extra, ..InstData::new(Opcode::ICmp) }, Type::I1)
261}
262
263/// A truth value with the other answer, which is an exclusive or with one.
264pub(crate) fn flipped(func: &mut Func, inst: Inst, value: Value) -> Value {
265    let one = ahead_const(func, inst, Imm::int(1, Type::I1), Type::I1);
266    let args = func.push_values(&[value, one]);
267    written(func, inst, InstData { args, ..InstData::new(Opcode::Xor) }, Type::I1)
268}
269
270/// A constant, as the bits written into a frame slot and read back at this format.
271///
272/// Every other constant in this back end is an immediate in an instruction, and this one cannot be:
273/// no instruction carries sixteen bytes of immediate, the integer that would spell the bits has no
274/// register either, and the constant pool a literal would otherwise go in is a section this back
275/// end does not have yet. So the bits go where the value lives, which for a value this back end
276/// has no other home for is the frame, and the read back is the whole register move the rule set
277/// already has at this width.
278///
279/// The low word goes at the lower address, which is this machine's order and is the same
280/// assumption [`crate::wide`] makes about the halves of an integer this wide. A back end for a big
281/// endian target is where that becomes a question, and it is the same question in both passes.
282///
283/// The slot is a fixed size `alloca`, so it is one slot in the frame however many times control
284/// reaches it, and a constant inside a loop costs two stores a time round rather than anything that
285/// grows.
286fn constant(func: &mut Func, inst: Inst) {
287    let Some(ty) = produced(func, inst) else { return };
288    let Extra::Imm(imm) = func[inst].extra else { return };
289    // A `_Decimal128` constant is sixteen bytes with no instruction to hold it either, and its bits
290    // are already the encoding, so it goes through the frame the same way.
291    if !quad(ty) && !(ty.is_scalar() && ty.format() == Some(Float::D128)) {
292        return;
293    }
294    let bits = func[imm].bits();
295    let whole = whole();
296    let slot = slot(func, inst);
297    let half = u64::from(WORD / 8);
298    let word = Type::int(WORD);
299    let low = ahead_const(func, inst, Imm::int(bits as i128, word), word);
300    write(func, inst, low, slot, MemInfo { size: half, ..whole });
301    let step = ahead_const(func, inst, Imm::int(half as i128, word), word);
302    let args = func.push_values(&[slot, step]);
303    let above = written(func, inst, InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
304    let high = ahead_const(func, inst, Imm::int((bits >> WORD) as i128, word), word);
305    write(func, inst, high, above, MemInfo { size: half, align: WORD / 8, ..whole });
306    let extra = Extra::Mem(func.add_mem(whole));
307    becomes(func, inst, Opcode::Load, extra, &[slot]);
308}
309
310/// A narrower float becoming a quad, which is one of two routines and never rounds.
311///
312/// Only from the two formats the runtime has a routine from. A `_Float16` widening straight to this
313/// format is not one of them and neither is the eighty bit format, which this machine has no
314/// register for anyway, and both are left alone and refused below.
315fn widen(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
316    let Some(ty) = produced(func, inst) else { return };
317    let Some(&arg) = func[func[inst].args].first() else { return };
318    if !quad(ty) {
319        return;
320    }
321    let mode = match func[arg].ty.format() {
322        Some(Float::F32) => "f32.f128",
323        Some(Float::F64) => "f64.f128",
324        _ => return,
325    };
326    let routine = routine(Opcode::FPExt, mode);
327    into_call(func, names, abi, inst, routine, &[arg]);
328}
329
330/// A quad becoming a narrower float, which is the other direction of the same pair and rounds.
331fn narrow(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
332    let Some(ty) = produced(func, inst) else { return };
333    let Some(&arg) = func[func[inst].args].first() else { return };
334    if !quad(func[arg].ty) {
335        return;
336    }
337    let mode = match ty.format() {
338        Some(Float::F32) => "f128.f32",
339        Some(Float::F64) => "f128.f64",
340        _ => return,
341    };
342    let routine = routine(Opcode::FPTrunc, mode);
343    into_call(func, names, abi, inst, routine, &[arg]);
344}
345
346/// An integer becoming a quad, which is a widening to a width the runtime has a routine at and then
347/// that routine.
348///
349/// The runtime has four, a signed and an unsigned integer at thirty two bits and at sixty four, so
350/// an integer narrower than that is widened first, with the sign for a signed one and with zeroes
351/// for an unsigned one. That is the same move [`crate::expand::floats`] makes in front of the
352/// machine's own conversion and for the same reason: after the widening the value is the same
353/// number at a width there is a conversion from.
354///
355/// Nothing rounds in any of the four, which is the property `spec/12-abi-and-runtime.md` section
356/// 12.8 measures rather than assumes, so a program that widens an integer through this format and
357/// back has the integer it started with.
358///
359/// A `__int128` never gets this far. [`crate::wide`] has already turned a conversion at that width
360/// into a call of its own, to the one routine in this family whose answer is not exact: a hundred
361/// and thirteen significant bits hold every integer the four below deal in and do not hold every
362/// value of a `__int128`, so that one rounds and these four do not.
363fn from_integer(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
364    let Some(ty) = produced(func, inst) else { return };
365    let Some(&arg) = func[func[inst].args].first() else { return };
366    let from = func[arg].ty;
367    if !quad(ty) || !from.is_int() || !from.is_scalar() {
368        return;
369    }
370    let signed = func[inst].opcode == Opcode::SIToFP;
371    let Some(width) = holder(from.bits()) else { return };
372    let opcode = if signed { Opcode::SIToFP } else { Opcode::UIToFP };
373    let routine = routine(opcode, if width == NARROW { "i32.f128" } else { "i64.f128" });
374    let value = if from.bits() == width {
375        arg
376    } else {
377        let opcode = if signed { Opcode::SExt } else { Opcode::ZExt };
378        let args = func.push_values(&[arg]);
379        written(func, inst, InstData { args, ..InstData::new(opcode) }, Type::int(width))
380    };
381    into_call(func, names, abi, inst, routine, &[value]);
382}
383
384/// A quad becoming an integer, which is the routine at a width the runtime has one at and then a
385/// truncation to the width the program asked for.
386///
387/// The four routines answer an `int`, an `unsigned int`, a `long long` and an `unsigned long long`,
388/// so a narrower answer is the thirty two bit routine and a truncation. Nothing is lost by that:
389/// the value has to fit the type the program named or the conversion is undefined, and a value that
390/// fits is a value the truncation leaves alone.
391///
392/// The four cases C leaves undefined, which are a value too large, a value too small, an infinity
393/// and a not a number, answer zero in the routine. Section 12.8 records that as a convention the
394/// differential can hold both implementations to rather than as a promise a program may read, and
395/// nothing here makes it one: no test goes in front of the call, the same way nothing tests a
396/// divisor for zero in front of `__divti3`.
397fn to_integer(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
398    let Some(ty) = produced(func, inst) else { return };
399    let Some(&arg) = func[func[inst].args].first() else { return };
400    if !quad(func[arg].ty) || !ty.is_int() || !ty.is_scalar() {
401        return;
402    }
403    let signed = func[inst].opcode == Opcode::FPToSI;
404    let Some(width) = holder(ty.bits()) else { return };
405    let opcode = if signed { Opcode::FPToSI } else { Opcode::FPToUI };
406    let routine = routine(opcode, if width == NARROW { "f128.i32" } else { "f128.i64" });
407    if ty.bits() == width {
408        into_call(func, names, abi, inst, routine, &[arg]);
409        return;
410    }
411    let answer = call(func, names, abi, inst, routine, &[arg], Type::int(width));
412    becomes(func, inst, Opcode::Trunc, Extra::None, &[answer]);
413}
414
415/// The width of the routine that serves an integer of this width, where one does.
416///
417/// A width at or below thirty two is served by the thirty two bit routine and one above it by the
418/// sixty four bit routine, and a hundred and twenty eight is served by nothing here because nothing
419/// at that width arrives: [`crate::wide`] has written its call already by then. Every width
420/// reaching this pass is one of the machine's own, because [`crate::widths`] has already rounded an
421/// integer of forty bits up into one of sixty four, so the only widths this sees are one, eight,
422/// sixteen, thirty two, sixty four and a hundred and twenty eight.
423fn holder(bits: u32) -> Option<u32> {
424    match bits {
425        0..=NARROW => Some(NARROW),
426        33..=WORD => Some(WORD),
427        _ => None,
428    }
429}
430
431/// Turns an instruction into the call that performs it, in place.
432///
433/// In place rather than in front of, because the call produces one value of the type the
434/// instruction already produced, so every reader of it goes on reading the same value. What the
435/// program said about rounding and about not a numbers is dropped, since a call carries none of it
436/// and the routine has its own answers, which are libgcc's.
437///
438/// Where the convention brings the answer back through an address the instruction becomes the load
439/// of it instead, which is in place in the same sense: it is still one instruction producing the
440/// one value every reader already reads.
441pub(crate) fn into_call(
442    func: &mut Func,
443    names: &mut Interner,
444    abi: &'static AbiDescription,
445    inst: Inst,
446    routine: &str,
447    args: &[Value],
448) {
449    let Some(ty) = produced(func, inst) else { return };
450    let shape = shaped(func, abi, inst, args, ty);
451    let extra = signature(func, names, routine, &shape, ty);
452    let Some(out) = shape.out else {
453        becomes(func, inst, Opcode::Call, extra, &shape.values);
454        return;
455    };
456    made(func, inst, extra, &shape.values);
457    let read = Extra::Mem(func.add_mem(whole()));
458    becomes(func, inst, Opcode::Load, read, &[out]);
459}
460
461/// A call to a runtime routine written in front of an instruction, and the value it answers.
462pub(crate) fn call(
463    func: &mut Func,
464    names: &mut Interner,
465    abi: &'static AbiDescription,
466    inst: Inst,
467    routine: &str,
468    args: &[Value],
469    ty: Type,
470) -> Value {
471    let shape = shaped(func, abi, inst, args, ty);
472    let extra = signature(func, names, routine, &shape, ty);
473    let Some(out) = shape.out else {
474        let args = func.push_values(&shape.values);
475        return written(func, inst, InstData { args, extra, ..InstData::new(Opcode::Call) }, ty);
476    };
477    made(func, inst, extra, &shape.values);
478    let extra = Extra::Mem(func.add_mem(whole()));
479    let args = func.push_values(&[out]);
480    written(func, inst, InstData { args, extra, ..InstData::new(Opcode::Load) }, ty)
481}
482
483/// A call that produces nothing, put in front of an instruction.
484///
485/// Nothing rather than one value because the answer is not coming back in a register: the routine
486/// writes it through the address it was handed, so what the call has is an effect and the value is
487/// the load after it.
488fn made(func: &mut Func, inst: Inst, extra: Extra, values: &[Value]) {
489    let span = func.span(inst);
490    let args = func.push_values(values);
491    let data = InstData { args, extra, ..InstData::new(Opcode::Call) };
492    let call = func.create_inst(data, &[], span);
493    func.insert_before(call, inst);
494}
495
496/// A call's operands once the convention has been asked about each of them.
497///
498/// What it is for is the one rule `tamnd/rucc#1331` put in the ABI description: a scalar of a size
499/// no register holds travels as the address of a copy the caller made. That is a rule about a call,
500/// so it applies to a call this pass writes exactly as it applies to one the program wrote, and on
501/// Windows x64 every `_Float128` in and out of these routines is sixteen bytes and therefore an
502/// address. Writing the SysV shape there is not a wrong answer that a test catches, it is a routine
503/// reading three registers nothing was put in.
504struct Shape {
505    /// What each operand is in the signature, which is `ptr` for the ones that became an address.
506    params: Vec<Param>,
507    /// The values the call instruction actually reads, in the same order.
508    values: Vec<Value>,
509    /// Where the answer is written, on a convention that brings it back through an address.
510    out: Option<Value>,
511}
512
513/// The operands of one call, with everything the convention passes by address spilled to the frame.
514///
515/// A slot per value rather than one slot reused, because the two operands of `__addtf3` are live at
516/// the same instruction and the routine is entitled to write through the address it was handed. The
517/// slots are fixed size `alloca`s, so a call inside a loop costs the stores and nothing that grows,
518/// which is the same bargain [`constant`] already makes.
519fn shaped(
520    func: &mut Func,
521    abi: &'static AbiDescription,
522    inst: Inst,
523    args: &[Value],
524    ty: Type,
525) -> Shape {
526    let mut shape = Shape { params: Vec::new(), values: Vec::new(), out: None };
527    if quad(ty) && abi.scalar_is_by_reference(BYTES) {
528        let out = slot(func, inst);
529        shape.params.push(Param::with_abi(Type::PTR, Abi::Sret { size: BYTES, align: BITS / 8 }));
530        shape.values.push(out);
531        shape.out = Some(out);
532    }
533    for &value in args {
534        let ty = func[value].ty;
535        let size = u64::from(ty.bits().div_ceil(8));
536        if quad(ty) && abi.scalar_is_by_reference(size) {
537            let copy = slot(func, inst);
538            write(func, inst, value, copy, whole());
539            shape.params.push(Param::new(Type::PTR));
540            shape.values.push(copy);
541        } else {
542            shape.params.push(Param::new(ty));
543            shape.values.push(value);
544        }
545    }
546    shape
547}
548
549/// The call this shape is, as the `Extra` an instruction carries it in.
550fn signature(
551    func: &mut Func,
552    names: &mut Interner,
553    routine: &str,
554    shape: &Shape,
555    ty: Type,
556) -> Extra {
557    let mut built = Signature::new();
558    built.params = shape.params.clone();
559    if shape.out.is_none() {
560        built.returns = vec![Param::new(ty)];
561    }
562    let signature = func.add_signature(built);
563    let callee = Some(names.intern(routine));
564    let varargs = func.push_abis(&[]);
565    Extra::Call(func.add_call(CallInfo { callee, signature, varargs }))
566}
567
568/// A frame slot the size of the format, put in front of an instruction.
569fn slot(func: &mut Func, inst: Inst) -> Value {
570    let extra = Extra::Mem(func.add_mem(whole()));
571    written(func, inst, InstData { extra, ..InstData::new(Opcode::Alloca) }, Type::PTR)
572}
573
574/// An access to the whole of one value of the format.
575fn whole() -> MemInfo {
576    MemInfo {
577        size: BYTES,
578        align: BITS / 8,
579        order: MemOrder::NotAtomic,
580        tbaa: None,
581        owns: 0,
582        restrict: Restrict::NONE,
583    }
584}
585
586/// A constant put in front of an instruction.
587pub(crate) fn ahead_const(func: &mut Func, inst: Inst, imm: Imm, ty: Type) -> Value {
588    let extra = Extra::Imm(func.add_imm(imm));
589    written(func, inst, InstData { extra, ..InstData::new(Opcode::IConst) }, ty)
590}
591
592/// A store put in front of an instruction, which produces nothing and is only its effect.
593fn write(func: &mut Func, inst: Inst, value: Value, into: Value, info: MemInfo) {
594    let span = func.span(inst);
595    let extra = Extra::Mem(func.add_mem(info));
596    let args = func.push_values(&[value, into]);
597    let data = InstData { args, extra, ..InstData::new(Opcode::Store) };
598    let made = func.create_inst(data, &[], span);
599    func.insert_before(made, inst);
600}
601
602/// Creates an instruction, puts it in front of another one, and reads its value back out.
603pub(crate) fn written(func: &mut Func, inst: Inst, data: InstData, ty: Type) -> Value {
604    let span = func.span(inst);
605    let made = func.create_inst(data, &[ty], span);
606    func.insert_before(made, inst);
607    func[made].first_result.expect("an instruction created with one result has one")
608}
609
610/// Turns an instruction into a different one over different operands, in place.
611pub(crate) fn becomes(func: &mut Func, inst: Inst, opcode: Opcode, extra: Extra, args: &[Value]) {
612    let args = func.push_values(args);
613    let data = &mut func[inst];
614    data.opcode = opcode;
615    data.args = args;
616    data.extra = extra;
617    data.flags = data.flags.intersection(Flags::legal_on(opcode));
618}
619
620#[cfg(test)]
621mod tests {
622    use rucc_base::Interner;
623    use rucc_ir::{Block, Builder, Module, Signature};
624    use rucc_target::{AbiDescription, Arch, Env, Os, TargetInfo, Triple, x86_64};
625
626    use super::{BITS, Flags, Float, FloatPred, Func, Opcode, Type, Value, calls};
627
628    /// The convention nearly every test here runs under, which is the one that passes and returns
629    /// a value of this format in a vector register.
630    fn sysv() -> &'static AbiDescription {
631        x86_64::SYSV.abi
632    }
633
634    /// The one that does not, where sixteen bytes of anything is the address of a copy.
635    fn win64() -> &'static AbiDescription {
636        x86_64::WIN64.abi
637    }
638
639    /// The format the pass is about, as a type, which is what every test builds with.
640    fn quad() -> Type {
641        Type::float(Float::F128)
642    }
643
644    fn target() -> TargetInfo {
645        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
646    }
647
648    fn printed(func: &Func, names: &mut Interner) -> String {
649        let module = Module::new(names.intern("q.c"), &target());
650        rucc_ir::print_func(&module, func, names)
651    }
652
653    /// A function of those parameters returning that, with its entry block and its parameters.
654    fn shell(names: &mut Interner, params: &[Type], returns: &[Type]) -> (Func, Block, Vec<Value>) {
655        let signature = Signature::new().with_params(params).with_returns(returns);
656        let mut func = Func::new(names.intern("f"), signature);
657        let entry = func.create_block();
658        let values = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
659        (func, entry, values)
660    }
661
662    /// The pass run over a function of two quads whose one instruction is that binary operation.
663    fn binary(opcode: Opcode) -> String {
664        binary_on(opcode, sysv())
665    }
666
667    /// The same, under the convention given rather than under the usual one.
668    fn binary_on(opcode: Opcode, abi: &'static AbiDescription) -> String {
669        let mut names = Interner::new();
670        let (mut func, entry, params) = shell(&mut names, &[quad(), quad()], &[quad()]);
671        let mut build = Builder::new(&mut func, entry);
672        let answer = build.binary(opcode, params[0], params[1], Flags::NONE);
673        build.ret(&[answer]);
674        calls(&mut func, &mut names, abi);
675        printed(&func, &mut names)
676    }
677
678    /// The pass run over a function of two quads whose one instruction is that comparison.
679    fn compared(pred: FloatPred) -> String {
680        compared_on(pred, sysv())
681    }
682
683    /// The same, under the convention given rather than under the usual one.
684    fn compared_on(pred: FloatPred, abi: &'static AbiDescription) -> String {
685        let mut names = Interner::new();
686        let (mut func, entry, params) = shell(&mut names, &[quad(), quad()], &[Type::I1]);
687        let mut build = Builder::new(&mut func, entry);
688        let answer = build.fcmp(pred, params[0], params[1], Flags::NONE);
689        build.ret(&[answer]);
690        calls(&mut func, &mut names, abi);
691        printed(&func, &mut names)
692    }
693
694    #[test]
695    fn the_four_operations_are_the_four_routines() {
696        for (opcode, routine) in [
697            (Opcode::FAdd, "__addtf3"),
698            (Opcode::FSub, "__subtf3"),
699            (Opcode::FMul, "__multf3"),
700            (Opcode::FDiv, "__divtf3"),
701        ] {
702            let text = binary(opcode);
703            assert!(text.contains(&format!("@{routine}")), "{routine}: {text}");
704            // The arithmetic is gone rather than sitting beside the call, which is the whole point:
705            // the selector has no rule to match it with.
706            assert_eq!(text.matches(" = f").count(), 0, "no float arithmetic left: {text}");
707            assert_eq!(text.matches(" = call").count(), 1, "one call: {text}");
708        }
709    }
710
711    #[test]
712    fn a_negation_is_the_routine_rather_than_a_sign_flip() {
713        let mut names = Interner::new();
714        let (mut func, entry, params) = shell(&mut names, &[quad()], &[quad()]);
715        let mut build = Builder::new(&mut func, entry);
716        let answer = build.unary(Opcode::FNeg, params[0], quad());
717        build.ret(&[answer]);
718        calls(&mut func, &mut names, sysv());
719        let text = printed(&func, &mut names);
720        assert!(text.contains("@__negtf2"), "{text}");
721        assert!(!text.contains("xor"), "no sign flip in a register: {text}");
722    }
723
724    /// The six ordered predicates, each the routine of its name and the test its answer is read
725    /// with.
726    #[test]
727    fn an_ordered_comparison_is_its_own_routine_tested_against_zero() {
728        for (pred, routine, test) in [
729            (FloatPred::Oeq, "__eqtf2", "icmp eq"),
730            (FloatPred::Une, "__netf2", "icmp ne"),
731            (FloatPred::Olt, "__lttf2", "icmp slt"),
732            (FloatPred::Ole, "__letf2", "icmp sle"),
733            (FloatPred::Ogt, "__gttf2", "icmp sgt"),
734            (FloatPred::Oge, "__getf2", "icmp sge"),
735        ] {
736            let text = compared(pred);
737            assert!(text.contains(&format!("@{routine}")), "{routine}: {text}");
738            assert!(text.contains(test), "{test}: {text}");
739            assert!(!text.contains("fcmp"), "the comparison is gone: {text}");
740        }
741    }
742
743    /// The four that are one of those six answers read as its negation.
744    ///
745    /// The routine is the opposite one and the test is the same one, which is the part worth a test
746    /// of its own: a pass that took the obvious route and kept the routine while flipping the test
747    /// would be wrong only for a not a number, which is the operand nothing in a corpus has.
748    #[test]
749    fn an_unordered_comparison_is_the_opposite_routine_read_the_same_way() {
750        for (pred, routine, test) in [
751            (FloatPred::Ult, "__getf2", "icmp slt"),
752            (FloatPred::Ule, "__gttf2", "icmp sle"),
753            (FloatPred::Ugt, "__letf2", "icmp sgt"),
754            (FloatPred::Uge, "__lttf2", "icmp sge"),
755        ] {
756            let text = compared(pred);
757            assert!(text.contains(&format!("@{routine}")), "{routine}: {text}");
758            assert!(text.contains(test), "{test}: {text}");
759        }
760    }
761
762    #[test]
763    fn whether_two_values_can_be_ordered_at_all_is_one_routine_either_way_round() {
764        let unordered = compared(FloatPred::Uno);
765        assert!(unordered.contains("@__unordtf2"), "{unordered}");
766        assert!(unordered.contains("icmp ne"), "{unordered}");
767        let ordered = compared(FloatPred::Ord);
768        assert!(ordered.contains("@__unordtf2"), "{ordered}");
769        assert!(ordered.contains("icmp eq"), "{ordered}");
770    }
771
772    /// Ordered and not equal is the one predicate that needs both calls.
773    #[test]
774    fn ordered_and_different_is_two_calls_joined() {
775        let text = compared(FloatPred::One);
776        assert!(text.contains("@__unordtf2"), "{text}");
777        assert!(text.contains("@__netf2"), "{text}");
778        assert_eq!(text.matches(" = call").count(), 2, "both calls: {text}");
779        assert_eq!(text.matches(" = and").count(), 1, "joined: {text}");
780        assert!(!text.contains("xor"), "nothing is negated: {text}");
781    }
782
783    /// Unordered or equal is the negation of that, which is the same two calls the other way up.
784    #[test]
785    fn unordered_or_equal_is_the_negation_of_it() {
786        let text = compared(FloatPred::Ueq);
787        assert_eq!(text.matches(" = call").count(), 2, "both calls: {text}");
788        assert_eq!(text.matches(" = or").count(), 1, "joined the other way: {text}");
789        assert_eq!(text.matches(" = xor").count(), 2, "both answers negated: {text}");
790    }
791
792    #[test]
793    fn the_two_comparisons_with_no_operands_to_read_are_constants() {
794        let never = compared(FloatPred::False);
795        assert!(never.contains("iconst.i1 0"), "{never}");
796        assert!(!never.contains("call"), "nothing is called: {never}");
797        // Printed as minus one, because the printer writes an integer signed and the one bit of an
798        // `i1` that is set is its sign bit.
799        let always = compared(FloatPred::True);
800        assert!(always.contains("iconst.i1 -1"), "{always}");
801    }
802
803    /// A constant is the bits written into a slot and read back at the format.
804    #[test]
805    fn a_constant_goes_through_the_frame_a_word_at_a_time() {
806        let mut names = Interner::new();
807        let (mut func, entry, _) = shell(&mut names, &[], &[quad()]);
808        let mut build = Builder::new(&mut func, entry);
809        // One in the low word and one in the high word, so a pass that wrote either word twice or
810        // wrote one of them into the wrong half is a different answer rather than the same zero.
811        let value = build.fconst(quad(), (3u128 << 64) | 5);
812        build.ret(&[value]);
813        calls(&mut func, &mut names, sysv());
814        let text = printed(&func, &mut names);
815        assert!(!text.contains("fconst"), "the constant is gone: {text}");
816        assert_eq!(text.matches("alloca").count(), 1, "one slot: {text}");
817        assert_eq!(text.matches("store").count(), 2, "a word at a time: {text}");
818        assert!(text.contains("iconst.i64 5"), "the low word first: {text}");
819        assert!(text.contains("iconst.i64 3"), "the high word above it: {text}");
820        assert_eq!(text.matches("ptr_add").count(), 1, "the high word is eight bytes up: {text}");
821        assert_eq!(text.matches(" = load").count(), 1, "read back as one value: {text}");
822    }
823
824    #[test]
825    fn the_two_narrower_formats_are_a_routine_each_way() {
826        for (from, to, routine) in [
827            (Float::F32, Float::F128, "__extendsftf2"),
828            (Float::F64, Float::F128, "__extenddftf2"),
829            (Float::F128, Float::F32, "__trunctfsf2"),
830            (Float::F128, Float::F64, "__trunctfdf2"),
831        ] {
832            let mut names = Interner::new();
833            let (mut func, entry, params) =
834                shell(&mut names, &[Type::float(from)], &[Type::float(to)]);
835            let mut build = Builder::new(&mut func, entry);
836            let opcode = if to == Float::F128 { Opcode::FPExt } else { Opcode::FPTrunc };
837            let answer = build.unary(opcode, params[0], Type::float(to));
838            build.ret(&[answer]);
839            calls(&mut func, &mut names, sysv());
840            let text = printed(&func, &mut names);
841            assert!(text.contains(&format!("@{routine}")), "{routine}: {text}");
842        }
843    }
844
845    /// An integer the runtime has no routine at is widened to one it does, with the sign the
846    /// conversion has.
847    #[test]
848    fn a_narrow_integer_is_widened_before_the_conversion() {
849        for (opcode, bits, extend, routine) in [
850            (Opcode::SIToFP, 16, " = sext", "__floatsitf"),
851            (Opcode::UIToFP, 16, " = zext", "__floatunsitf"),
852            (Opcode::SIToFP, 32, "", "__floatsitf"),
853            (Opcode::UIToFP, 64, "", "__floatunditf"),
854        ] {
855            let mut names = Interner::new();
856            let (mut func, entry, params) = shell(&mut names, &[Type::int(bits)], &[quad()]);
857            let mut build = Builder::new(&mut func, entry);
858            let answer = build.unary(opcode, params[0], quad());
859            build.ret(&[answer]);
860            calls(&mut func, &mut names, sysv());
861            let text = printed(&func, &mut names);
862            assert!(text.contains(&format!("@{routine}")), "{routine}: {text}");
863            if extend.is_empty() {
864                assert!(!text.contains(" = sext"), "nothing to widen: {text}");
865                assert!(!text.contains(" = zext"), "nothing to widen: {text}");
866            } else {
867                assert!(text.contains(extend), "{extend}: {text}");
868            }
869        }
870    }
871
872    /// Coming down, the answer is truncated to the width the program asked for.
873    #[test]
874    fn a_narrow_answer_is_the_wider_routine_and_a_truncation() {
875        let mut names = Interner::new();
876        let (mut func, entry, params) = shell(&mut names, &[quad()], &[Type::int(16)]);
877        let mut build = Builder::new(&mut func, entry);
878        let answer = build.unary(Opcode::FPToSI, params[0], Type::int(16));
879        build.ret(&[answer]);
880        calls(&mut func, &mut names, sysv());
881        let text = printed(&func, &mut names);
882        assert!(text.contains("@__fixtfsi"), "{text}");
883        assert_eq!(text.matches(" = trunc").count(), 1, "cut down afterwards: {text}");
884    }
885
886    #[test]
887    fn a_conversion_against_a_wide_integer_is_left_exactly_as_it_was() {
888        let mut names = Interner::new();
889        let (mut func, entry, params) = shell(&mut names, &[Type::int(BITS)], &[quad()]);
890        let mut build = Builder::new(&mut func, entry);
891        let answer = build.unary(Opcode::SIToFP, params[0], quad());
892        build.ret(&[answer]);
893        calls(&mut func, &mut names, sysv());
894        let text = printed(&func, &mut names);
895        assert!(!text.contains("call"), "no routine is called: {text}");
896        assert!(text.contains("sitofp"), "the conversion is still there to be refused: {text}");
897    }
898
899    /// An operation at a format the machine has is not this pass's business.
900    #[test]
901    fn the_narrower_formats_go_past_untouched() {
902        let mut names = Interner::new();
903        let double = Type::float(Float::F64);
904        let (mut func, entry, params) = shell(&mut names, &[double, double], &[double]);
905        let mut build = Builder::new(&mut func, entry);
906        let sum = build.binary(Opcode::FAdd, params[0], params[1], Flags::NONE);
907        let answer = build.fcmp(FloatPred::Olt, sum, params[1], Flags::NONE);
908        build.ret(&[sum]);
909        let _ = answer;
910        calls(&mut func, &mut names, sysv());
911        let text = printed(&func, &mut names);
912        assert!(!text.contains("call"), "nothing became a call: {text}");
913        assert!(text.contains("fadd"), "the add is still an add: {text}");
914        assert!(text.contains("fcmp"), "the comparison is still a comparison: {text}");
915    }
916
917    /// The convention decides the shape of the call, and on one of them that shape is addresses.
918    ///
919    /// Windows x64 passes a scalar of a size no register holds as the address of a copy the caller
920    /// made, and returns one the same way, so `__addtf3` there takes three pointers and answers
921    /// nothing. libgcc's routine is compiled to that convention on that target and reads those three
922    /// registers, so writing the other shape is not a difference a test catches later, it is a
923    /// routine reading registers nothing was put in.
924    #[test]
925    fn on_windows_the_operands_and_the_answer_all_travel_as_addresses() {
926        let text = binary_on(Opcode::FAdd, win64());
927        assert!(text.contains("@__addtf3"), "{text}");
928        // Three slots: one per operand, because the routine is entitled to write through an address
929        // it was handed, and one for the answer.
930        assert_eq!(text.matches("alloca").count(), 3, "three slots: {text}");
931        assert_eq!(text.matches("store").count(), 2, "a copy of each operand: {text}");
932        // The call produces nothing, so the value the rest of the function reads is the load after
933        // it rather than the call itself.
934        assert_eq!(text.matches(" = call").count(), 0, "the call answers nothing: {text}");
935        assert_eq!(text.matches("call ").count(), 1, "and there is one of them: {text}");
936        assert_eq!(text.matches(" = load").count(), 1, "read back out of the slot: {text}");
937    }
938
939    /// A comparison answers an `int`, which is a register on every convention, so only the operands
940    /// change shape.
941    #[test]
942    fn on_windows_a_comparison_hands_over_its_operands_and_keeps_its_answer() {
943        let text = compared_on(FloatPred::Oeq, win64());
944        assert!(text.contains("@__eqtf2"), "{text}");
945        assert_eq!(text.matches("alloca").count(), 2, "one slot per operand: {text}");
946        assert_eq!(text.matches("store").count(), 2, "and a copy into each: {text}");
947        assert_eq!(text.matches(" = call").count(), 1, "the answer is still a result: {text}");
948        assert!(text.contains("icmp eq"), "read the same way: {text}");
949    }
950
951    /// The same function on the convention that has registers wide enough is the plain shape.
952    #[test]
953    fn the_convention_that_holds_one_in_a_register_puts_nothing_on_the_frame() {
954        let text = binary_on(Opcode::FAdd, sysv());
955        assert!(text.contains("@__addtf3"), "{text}");
956        assert!(!text.contains("alloca"), "nothing goes through the frame: {text}");
957        assert!(!text.contains("store"), "nothing is copied: {text}");
958        assert_eq!(text.matches(" = call").count(), 1, "the call is the value: {text}");
959    }
960}