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.
264fn 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    if !quad(ty) {
290        return;
291    }
292    let bits = func[imm].bits();
293    let whole = whole();
294    let slot = slot(func, inst);
295    let half = u64::from(WORD / 8);
296    let word = Type::int(WORD);
297    let low = ahead_const(func, inst, Imm::int(bits as i128, word), word);
298    write(func, inst, low, slot, MemInfo { size: half, ..whole });
299    let step = ahead_const(func, inst, Imm::int(half as i128, word), word);
300    let args = func.push_values(&[slot, step]);
301    let above = written(func, inst, InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR);
302    let high = ahead_const(func, inst, Imm::int((bits >> WORD) as i128, word), word);
303    write(func, inst, high, above, MemInfo { size: half, align: WORD / 8, ..whole });
304    let extra = Extra::Mem(func.add_mem(whole));
305    becomes(func, inst, Opcode::Load, extra, &[slot]);
306}
307
308/// A narrower float becoming a quad, which is one of two routines and never rounds.
309///
310/// Only from the two formats the runtime has a routine from. A `_Float16` widening straight to this
311/// format is not one of them and neither is the eighty bit format, which this machine has no
312/// register for anyway, and both are left alone and refused below.
313fn widen(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
314    let Some(ty) = produced(func, inst) else { return };
315    let Some(&arg) = func[func[inst].args].first() else { return };
316    if !quad(ty) {
317        return;
318    }
319    let mode = match func[arg].ty.format() {
320        Some(Float::F32) => "f32.f128",
321        Some(Float::F64) => "f64.f128",
322        _ => return,
323    };
324    let routine = routine(Opcode::FPExt, mode);
325    into_call(func, names, abi, inst, routine, &[arg]);
326}
327
328/// A quad becoming a narrower float, which is the other direction of the same pair and rounds.
329fn narrow(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
330    let Some(ty) = produced(func, inst) else { return };
331    let Some(&arg) = func[func[inst].args].first() else { return };
332    if !quad(func[arg].ty) {
333        return;
334    }
335    let mode = match ty.format() {
336        Some(Float::F32) => "f128.f32",
337        Some(Float::F64) => "f128.f64",
338        _ => return,
339    };
340    let routine = routine(Opcode::FPTrunc, mode);
341    into_call(func, names, abi, inst, routine, &[arg]);
342}
343
344/// An integer becoming a quad, which is a widening to a width the runtime has a routine at and then
345/// that routine.
346///
347/// The runtime has four, a signed and an unsigned integer at thirty two bits and at sixty four, so
348/// an integer narrower than that is widened first, with the sign for a signed one and with zeroes
349/// for an unsigned one. That is the same move [`crate::expand::floats`] makes in front of the
350/// machine's own conversion and for the same reason: after the widening the value is the same
351/// number at a width there is a conversion from.
352///
353/// Nothing rounds in any of the four, which is the property `spec/12-abi-and-runtime.md` section
354/// 12.8 measures rather than assumes, so a program that widens an integer through this format and
355/// back has the integer it started with.
356///
357/// A `__int128` never gets this far. [`crate::wide`] has already turned a conversion at that width
358/// into a call of its own, to the one routine in this family whose answer is not exact: a hundred
359/// and thirteen significant bits hold every integer the four below deal in and do not hold every
360/// value of a `__int128`, so that one rounds and these four do not.
361fn from_integer(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
362    let Some(ty) = produced(func, inst) else { return };
363    let Some(&arg) = func[func[inst].args].first() else { return };
364    let from = func[arg].ty;
365    if !quad(ty) || !from.is_int() || !from.is_scalar() {
366        return;
367    }
368    let signed = func[inst].opcode == Opcode::SIToFP;
369    let Some(width) = holder(from.bits()) else { return };
370    let opcode = if signed { Opcode::SIToFP } else { Opcode::UIToFP };
371    let routine = routine(opcode, if width == NARROW { "i32.f128" } else { "i64.f128" });
372    let value = if from.bits() == width {
373        arg
374    } else {
375        let opcode = if signed { Opcode::SExt } else { Opcode::ZExt };
376        let args = func.push_values(&[arg]);
377        written(func, inst, InstData { args, ..InstData::new(opcode) }, Type::int(width))
378    };
379    into_call(func, names, abi, inst, routine, &[value]);
380}
381
382/// A quad becoming an integer, which is the routine at a width the runtime has one at and then a
383/// truncation to the width the program asked for.
384///
385/// The four routines answer an `int`, an `unsigned int`, a `long long` and an `unsigned long long`,
386/// so a narrower answer is the thirty two bit routine and a truncation. Nothing is lost by that:
387/// the value has to fit the type the program named or the conversion is undefined, and a value that
388/// fits is a value the truncation leaves alone.
389///
390/// The four cases C leaves undefined, which are a value too large, a value too small, an infinity
391/// and a not a number, answer zero in the routine. Section 12.8 records that as a convention the
392/// differential can hold both implementations to rather than as a promise a program may read, and
393/// nothing here makes it one: no test goes in front of the call, the same way nothing tests a
394/// divisor for zero in front of `__divti3`.
395fn to_integer(func: &mut Func, names: &mut Interner, abi: &'static AbiDescription, inst: Inst) {
396    let Some(ty) = produced(func, inst) else { return };
397    let Some(&arg) = func[func[inst].args].first() else { return };
398    if !quad(func[arg].ty) || !ty.is_int() || !ty.is_scalar() {
399        return;
400    }
401    let signed = func[inst].opcode == Opcode::FPToSI;
402    let Some(width) = holder(ty.bits()) else { return };
403    let opcode = if signed { Opcode::FPToSI } else { Opcode::FPToUI };
404    let routine = routine(opcode, if width == NARROW { "f128.i32" } else { "f128.i64" });
405    if ty.bits() == width {
406        into_call(func, names, abi, inst, routine, &[arg]);
407        return;
408    }
409    let answer = call(func, names, abi, inst, routine, &[arg], Type::int(width));
410    becomes(func, inst, Opcode::Trunc, Extra::None, &[answer]);
411}
412
413/// The width of the routine that serves an integer of this width, where one does.
414///
415/// A width at or below thirty two is served by the thirty two bit routine and one above it by the
416/// sixty four bit routine, and a hundred and twenty eight is served by nothing here because nothing
417/// at that width arrives: [`crate::wide`] has written its call already by then. Every width
418/// reaching this pass is one of the machine's own, because [`crate::widths`] has already rounded an
419/// integer of forty bits up into one of sixty four, so the only widths this sees are one, eight,
420/// sixteen, thirty two, sixty four and a hundred and twenty eight.
421fn holder(bits: u32) -> Option<u32> {
422    match bits {
423        0..=NARROW => Some(NARROW),
424        33..=WORD => Some(WORD),
425        _ => None,
426    }
427}
428
429/// Turns an instruction into the call that performs it, in place.
430///
431/// In place rather than in front of, because the call produces one value of the type the
432/// instruction already produced, so every reader of it goes on reading the same value. What the
433/// program said about rounding and about not a numbers is dropped, since a call carries none of it
434/// and the routine has its own answers, which are libgcc's.
435///
436/// Where the convention brings the answer back through an address the instruction becomes the load
437/// of it instead, which is in place in the same sense: it is still one instruction producing the
438/// one value every reader already reads.
439fn into_call(
440    func: &mut Func,
441    names: &mut Interner,
442    abi: &'static AbiDescription,
443    inst: Inst,
444    routine: &str,
445    args: &[Value],
446) {
447    let Some(ty) = produced(func, inst) else { return };
448    let shape = shaped(func, abi, inst, args, ty);
449    let extra = signature(func, names, routine, &shape, ty);
450    let Some(out) = shape.out else {
451        becomes(func, inst, Opcode::Call, extra, &shape.values);
452        return;
453    };
454    made(func, inst, extra, &shape.values);
455    let read = Extra::Mem(func.add_mem(whole()));
456    becomes(func, inst, Opcode::Load, read, &[out]);
457}
458
459/// A call to a runtime routine written in front of an instruction, and the value it answers.
460fn call(
461    func: &mut Func,
462    names: &mut Interner,
463    abi: &'static AbiDescription,
464    inst: Inst,
465    routine: &str,
466    args: &[Value],
467    ty: Type,
468) -> Value {
469    let shape = shaped(func, abi, inst, args, ty);
470    let extra = signature(func, names, routine, &shape, ty);
471    let Some(out) = shape.out else {
472        let args = func.push_values(&shape.values);
473        return written(func, inst, InstData { args, extra, ..InstData::new(Opcode::Call) }, ty);
474    };
475    made(func, inst, extra, &shape.values);
476    let extra = Extra::Mem(func.add_mem(whole()));
477    let args = func.push_values(&[out]);
478    written(func, inst, InstData { args, extra, ..InstData::new(Opcode::Load) }, ty)
479}
480
481/// A call that produces nothing, put in front of an instruction.
482///
483/// Nothing rather than one value because the answer is not coming back in a register: the routine
484/// writes it through the address it was handed, so what the call has is an effect and the value is
485/// the load after it.
486fn made(func: &mut Func, inst: Inst, extra: Extra, values: &[Value]) {
487    let span = func.span(inst);
488    let args = func.push_values(values);
489    let data = InstData { args, extra, ..InstData::new(Opcode::Call) };
490    let call = func.create_inst(data, &[], span);
491    func.insert_before(call, inst);
492}
493
494/// A call's operands once the convention has been asked about each of them.
495///
496/// What it is for is the one rule `tamnd/rucc#1331` put in the ABI description: a scalar of a size
497/// no register holds travels as the address of a copy the caller made. That is a rule about a call,
498/// so it applies to a call this pass writes exactly as it applies to one the program wrote, and on
499/// Windows x64 every `_Float128` in and out of these routines is sixteen bytes and therefore an
500/// address. Writing the SysV shape there is not a wrong answer that a test catches, it is a routine
501/// reading three registers nothing was put in.
502struct Shape {
503    /// What each operand is in the signature, which is `ptr` for the ones that became an address.
504    params: Vec<Param>,
505    /// The values the call instruction actually reads, in the same order.
506    values: Vec<Value>,
507    /// Where the answer is written, on a convention that brings it back through an address.
508    out: Option<Value>,
509}
510
511/// The operands of one call, with everything the convention passes by address spilled to the frame.
512///
513/// A slot per value rather than one slot reused, because the two operands of `__addtf3` are live at
514/// the same instruction and the routine is entitled to write through the address it was handed. The
515/// slots are fixed size `alloca`s, so a call inside a loop costs the stores and nothing that grows,
516/// which is the same bargain [`constant`] already makes.
517fn shaped(
518    func: &mut Func,
519    abi: &'static AbiDescription,
520    inst: Inst,
521    args: &[Value],
522    ty: Type,
523) -> Shape {
524    let mut shape = Shape { params: Vec::new(), values: Vec::new(), out: None };
525    if quad(ty) && abi.scalar_is_by_reference(BYTES) {
526        let out = slot(func, inst);
527        shape.params.push(Param::with_abi(Type::PTR, Abi::Sret { size: BYTES, align: BITS / 8 }));
528        shape.values.push(out);
529        shape.out = Some(out);
530    }
531    for &value in args {
532        let ty = func[value].ty;
533        let size = u64::from(ty.bits().div_ceil(8));
534        if quad(ty) && abi.scalar_is_by_reference(size) {
535            let copy = slot(func, inst);
536            write(func, inst, value, copy, whole());
537            shape.params.push(Param::new(Type::PTR));
538            shape.values.push(copy);
539        } else {
540            shape.params.push(Param::new(ty));
541            shape.values.push(value);
542        }
543    }
544    shape
545}
546
547/// The call this shape is, as the `Extra` an instruction carries it in.
548fn signature(
549    func: &mut Func,
550    names: &mut Interner,
551    routine: &str,
552    shape: &Shape,
553    ty: Type,
554) -> Extra {
555    let mut built = Signature::new();
556    built.params = shape.params.clone();
557    if shape.out.is_none() {
558        built.returns = vec![Param::new(ty)];
559    }
560    let signature = func.add_signature(built);
561    let callee = Some(names.intern(routine));
562    let varargs = func.push_abis(&[]);
563    Extra::Call(func.add_call(CallInfo { callee, signature, varargs }))
564}
565
566/// A frame slot the size of the format, put in front of an instruction.
567fn slot(func: &mut Func, inst: Inst) -> Value {
568    let extra = Extra::Mem(func.add_mem(whole()));
569    written(func, inst, InstData { extra, ..InstData::new(Opcode::Alloca) }, Type::PTR)
570}
571
572/// An access to the whole of one value of the format.
573fn whole() -> MemInfo {
574    MemInfo {
575        size: BYTES,
576        align: BITS / 8,
577        order: MemOrder::NotAtomic,
578        tbaa: None,
579        owns: 0,
580        restrict: Restrict::NONE,
581    }
582}
583
584/// A constant put in front of an instruction.
585fn ahead_const(func: &mut Func, inst: Inst, imm: Imm, ty: Type) -> Value {
586    let extra = Extra::Imm(func.add_imm(imm));
587    written(func, inst, InstData { extra, ..InstData::new(Opcode::IConst) }, ty)
588}
589
590/// A store put in front of an instruction, which produces nothing and is only its effect.
591fn write(func: &mut Func, inst: Inst, value: Value, into: Value, info: MemInfo) {
592    let span = func.span(inst);
593    let extra = Extra::Mem(func.add_mem(info));
594    let args = func.push_values(&[value, into]);
595    let data = InstData { args, extra, ..InstData::new(Opcode::Store) };
596    let made = func.create_inst(data, &[], span);
597    func.insert_before(made, inst);
598}
599
600/// Creates an instruction, puts it in front of another one, and reads its value back out.
601fn written(func: &mut Func, inst: Inst, data: InstData, ty: Type) -> Value {
602    let span = func.span(inst);
603    let made = func.create_inst(data, &[ty], span);
604    func.insert_before(made, inst);
605    func[made].first_result.expect("an instruction created with one result has one")
606}
607
608/// Turns an instruction into a different one over different operands, in place.
609fn becomes(func: &mut Func, inst: Inst, opcode: Opcode, extra: Extra, args: &[Value]) {
610    let args = func.push_values(args);
611    let data = &mut func[inst];
612    data.opcode = opcode;
613    data.args = args;
614    data.extra = extra;
615    data.flags = data.flags.intersection(Flags::legal_on(opcode));
616}
617
618#[cfg(test)]
619mod tests {
620    use rucc_base::Interner;
621    use rucc_ir::{Block, Builder, Module, Signature};
622    use rucc_target::{AbiDescription, Arch, Env, Os, TargetInfo, Triple, x86_64};
623
624    use super::{BITS, Flags, Float, FloatPred, Func, Opcode, Type, Value, calls};
625
626    /// The convention nearly every test here runs under, which is the one that passes and returns
627    /// a value of this format in a vector register.
628    fn sysv() -> &'static AbiDescription {
629        x86_64::SYSV.abi
630    }
631
632    /// The one that does not, where sixteen bytes of anything is the address of a copy.
633    fn win64() -> &'static AbiDescription {
634        x86_64::WIN64.abi
635    }
636
637    /// The format the pass is about, as a type, which is what every test builds with.
638    fn quad() -> Type {
639        Type::float(Float::F128)
640    }
641
642    fn target() -> TargetInfo {
643        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
644    }
645
646    fn printed(func: &Func, names: &mut Interner) -> String {
647        let module = Module::new(names.intern("q.c"), &target());
648        rucc_ir::print_func(&module, func, names)
649    }
650
651    /// A function of those parameters returning that, with its entry block and its parameters.
652    fn shell(names: &mut Interner, params: &[Type], returns: &[Type]) -> (Func, Block, Vec<Value>) {
653        let signature = Signature::new().with_params(params).with_returns(returns);
654        let mut func = Func::new(names.intern("f"), signature);
655        let entry = func.create_block();
656        let values = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
657        (func, entry, values)
658    }
659
660    /// The pass run over a function of two quads whose one instruction is that binary operation.
661    fn binary(opcode: Opcode) -> String {
662        binary_on(opcode, sysv())
663    }
664
665    /// The same, under the convention given rather than under the usual one.
666    fn binary_on(opcode: Opcode, abi: &'static AbiDescription) -> String {
667        let mut names = Interner::new();
668        let (mut func, entry, params) = shell(&mut names, &[quad(), quad()], &[quad()]);
669        let mut build = Builder::new(&mut func, entry);
670        let answer = build.binary(opcode, params[0], params[1], Flags::NONE);
671        build.ret(&[answer]);
672        calls(&mut func, &mut names, abi);
673        printed(&func, &mut names)
674    }
675
676    /// The pass run over a function of two quads whose one instruction is that comparison.
677    fn compared(pred: FloatPred) -> String {
678        compared_on(pred, sysv())
679    }
680
681    /// The same, under the convention given rather than under the usual one.
682    fn compared_on(pred: FloatPred, abi: &'static AbiDescription) -> String {
683        let mut names = Interner::new();
684        let (mut func, entry, params) = shell(&mut names, &[quad(), quad()], &[Type::I1]);
685        let mut build = Builder::new(&mut func, entry);
686        let answer = build.fcmp(pred, params[0], params[1], Flags::NONE);
687        build.ret(&[answer]);
688        calls(&mut func, &mut names, abi);
689        printed(&func, &mut names)
690    }
691
692    #[test]
693    fn the_four_operations_are_the_four_routines() {
694        for (opcode, routine) in [
695            (Opcode::FAdd, "__addtf3"),
696            (Opcode::FSub, "__subtf3"),
697            (Opcode::FMul, "__multf3"),
698            (Opcode::FDiv, "__divtf3"),
699        ] {
700            let text = binary(opcode);
701            assert!(text.contains(&format!("@{routine}")), "{routine}: {text}");
702            // The arithmetic is gone rather than sitting beside the call, which is the whole point:
703            // the selector has no rule to match it with.
704            assert_eq!(text.matches(" = f").count(), 0, "no float arithmetic left: {text}");
705            assert_eq!(text.matches(" = call").count(), 1, "one call: {text}");
706        }
707    }
708
709    #[test]
710    fn a_negation_is_the_routine_rather_than_a_sign_flip() {
711        let mut names = Interner::new();
712        let (mut func, entry, params) = shell(&mut names, &[quad()], &[quad()]);
713        let mut build = Builder::new(&mut func, entry);
714        let answer = build.unary(Opcode::FNeg, params[0], quad());
715        build.ret(&[answer]);
716        calls(&mut func, &mut names, sysv());
717        let text = printed(&func, &mut names);
718        assert!(text.contains("@__negtf2"), "{text}");
719        assert!(!text.contains("xor"), "no sign flip in a register: {text}");
720    }
721
722    /// The six ordered predicates, each the routine of its name and the test its answer is read
723    /// with.
724    #[test]
725    fn an_ordered_comparison_is_its_own_routine_tested_against_zero() {
726        for (pred, routine, test) in [
727            (FloatPred::Oeq, "__eqtf2", "icmp eq"),
728            (FloatPred::Une, "__netf2", "icmp ne"),
729            (FloatPred::Olt, "__lttf2", "icmp slt"),
730            (FloatPred::Ole, "__letf2", "icmp sle"),
731            (FloatPred::Ogt, "__gttf2", "icmp sgt"),
732            (FloatPred::Oge, "__getf2", "icmp sge"),
733        ] {
734            let text = compared(pred);
735            assert!(text.contains(&format!("@{routine}")), "{routine}: {text}");
736            assert!(text.contains(test), "{test}: {text}");
737            assert!(!text.contains("fcmp"), "the comparison is gone: {text}");
738        }
739    }
740
741    /// The four that are one of those six answers read as its negation.
742    ///
743    /// The routine is the opposite one and the test is the same one, which is the part worth a test
744    /// of its own: a pass that took the obvious route and kept the routine while flipping the test
745    /// would be wrong only for a not a number, which is the operand nothing in a corpus has.
746    #[test]
747    fn an_unordered_comparison_is_the_opposite_routine_read_the_same_way() {
748        for (pred, routine, test) in [
749            (FloatPred::Ult, "__getf2", "icmp slt"),
750            (FloatPred::Ule, "__gttf2", "icmp sle"),
751            (FloatPred::Ugt, "__letf2", "icmp sgt"),
752            (FloatPred::Uge, "__lttf2", "icmp sge"),
753        ] {
754            let text = compared(pred);
755            assert!(text.contains(&format!("@{routine}")), "{routine}: {text}");
756            assert!(text.contains(test), "{test}: {text}");
757        }
758    }
759
760    #[test]
761    fn whether_two_values_can_be_ordered_at_all_is_one_routine_either_way_round() {
762        let unordered = compared(FloatPred::Uno);
763        assert!(unordered.contains("@__unordtf2"), "{unordered}");
764        assert!(unordered.contains("icmp ne"), "{unordered}");
765        let ordered = compared(FloatPred::Ord);
766        assert!(ordered.contains("@__unordtf2"), "{ordered}");
767        assert!(ordered.contains("icmp eq"), "{ordered}");
768    }
769
770    /// Ordered and not equal is the one predicate that needs both calls.
771    #[test]
772    fn ordered_and_different_is_two_calls_joined() {
773        let text = compared(FloatPred::One);
774        assert!(text.contains("@__unordtf2"), "{text}");
775        assert!(text.contains("@__netf2"), "{text}");
776        assert_eq!(text.matches(" = call").count(), 2, "both calls: {text}");
777        assert_eq!(text.matches(" = and").count(), 1, "joined: {text}");
778        assert!(!text.contains("xor"), "nothing is negated: {text}");
779    }
780
781    /// Unordered or equal is the negation of that, which is the same two calls the other way up.
782    #[test]
783    fn unordered_or_equal_is_the_negation_of_it() {
784        let text = compared(FloatPred::Ueq);
785        assert_eq!(text.matches(" = call").count(), 2, "both calls: {text}");
786        assert_eq!(text.matches(" = or").count(), 1, "joined the other way: {text}");
787        assert_eq!(text.matches(" = xor").count(), 2, "both answers negated: {text}");
788    }
789
790    #[test]
791    fn the_two_comparisons_with_no_operands_to_read_are_constants() {
792        let never = compared(FloatPred::False);
793        assert!(never.contains("iconst.i1 0"), "{never}");
794        assert!(!never.contains("call"), "nothing is called: {never}");
795        // Printed as minus one, because the printer writes an integer signed and the one bit of an
796        // `i1` that is set is its sign bit.
797        let always = compared(FloatPred::True);
798        assert!(always.contains("iconst.i1 -1"), "{always}");
799    }
800
801    /// A constant is the bits written into a slot and read back at the format.
802    #[test]
803    fn a_constant_goes_through_the_frame_a_word_at_a_time() {
804        let mut names = Interner::new();
805        let (mut func, entry, _) = shell(&mut names, &[], &[quad()]);
806        let mut build = Builder::new(&mut func, entry);
807        // One in the low word and one in the high word, so a pass that wrote either word twice or
808        // wrote one of them into the wrong half is a different answer rather than the same zero.
809        let value = build.fconst(quad(), (3u128 << 64) | 5);
810        build.ret(&[value]);
811        calls(&mut func, &mut names, sysv());
812        let text = printed(&func, &mut names);
813        assert!(!text.contains("fconst"), "the constant is gone: {text}");
814        assert_eq!(text.matches("alloca").count(), 1, "one slot: {text}");
815        assert_eq!(text.matches("store").count(), 2, "a word at a time: {text}");
816        assert!(text.contains("iconst.i64 5"), "the low word first: {text}");
817        assert!(text.contains("iconst.i64 3"), "the high word above it: {text}");
818        assert_eq!(text.matches("ptr_add").count(), 1, "the high word is eight bytes up: {text}");
819        assert_eq!(text.matches(" = load").count(), 1, "read back as one value: {text}");
820    }
821
822    #[test]
823    fn the_two_narrower_formats_are_a_routine_each_way() {
824        for (from, to, routine) in [
825            (Float::F32, Float::F128, "__extendsftf2"),
826            (Float::F64, Float::F128, "__extenddftf2"),
827            (Float::F128, Float::F32, "__trunctfsf2"),
828            (Float::F128, Float::F64, "__trunctfdf2"),
829        ] {
830            let mut names = Interner::new();
831            let (mut func, entry, params) =
832                shell(&mut names, &[Type::float(from)], &[Type::float(to)]);
833            let mut build = Builder::new(&mut func, entry);
834            let opcode = if to == Float::F128 { Opcode::FPExt } else { Opcode::FPTrunc };
835            let answer = build.unary(opcode, params[0], Type::float(to));
836            build.ret(&[answer]);
837            calls(&mut func, &mut names, sysv());
838            let text = printed(&func, &mut names);
839            assert!(text.contains(&format!("@{routine}")), "{routine}: {text}");
840        }
841    }
842
843    /// An integer the runtime has no routine at is widened to one it does, with the sign the
844    /// conversion has.
845    #[test]
846    fn a_narrow_integer_is_widened_before_the_conversion() {
847        for (opcode, bits, extend, routine) in [
848            (Opcode::SIToFP, 16, " = sext", "__floatsitf"),
849            (Opcode::UIToFP, 16, " = zext", "__floatunsitf"),
850            (Opcode::SIToFP, 32, "", "__floatsitf"),
851            (Opcode::UIToFP, 64, "", "__floatunditf"),
852        ] {
853            let mut names = Interner::new();
854            let (mut func, entry, params) = shell(&mut names, &[Type::int(bits)], &[quad()]);
855            let mut build = Builder::new(&mut func, entry);
856            let answer = build.unary(opcode, params[0], quad());
857            build.ret(&[answer]);
858            calls(&mut func, &mut names, sysv());
859            let text = printed(&func, &mut names);
860            assert!(text.contains(&format!("@{routine}")), "{routine}: {text}");
861            if extend.is_empty() {
862                assert!(!text.contains(" = sext"), "nothing to widen: {text}");
863                assert!(!text.contains(" = zext"), "nothing to widen: {text}");
864            } else {
865                assert!(text.contains(extend), "{extend}: {text}");
866            }
867        }
868    }
869
870    /// Coming down, the answer is truncated to the width the program asked for.
871    #[test]
872    fn a_narrow_answer_is_the_wider_routine_and_a_truncation() {
873        let mut names = Interner::new();
874        let (mut func, entry, params) = shell(&mut names, &[quad()], &[Type::int(16)]);
875        let mut build = Builder::new(&mut func, entry);
876        let answer = build.unary(Opcode::FPToSI, params[0], Type::int(16));
877        build.ret(&[answer]);
878        calls(&mut func, &mut names, sysv());
879        let text = printed(&func, &mut names);
880        assert!(text.contains("@__fixtfsi"), "{text}");
881        assert_eq!(text.matches(" = trunc").count(), 1, "cut down afterwards: {text}");
882    }
883
884    #[test]
885    fn a_conversion_against_a_wide_integer_is_left_exactly_as_it_was() {
886        let mut names = Interner::new();
887        let (mut func, entry, params) = shell(&mut names, &[Type::int(BITS)], &[quad()]);
888        let mut build = Builder::new(&mut func, entry);
889        let answer = build.unary(Opcode::SIToFP, params[0], quad());
890        build.ret(&[answer]);
891        calls(&mut func, &mut names, sysv());
892        let text = printed(&func, &mut names);
893        assert!(!text.contains("call"), "no routine is called: {text}");
894        assert!(text.contains("sitofp"), "the conversion is still there to be refused: {text}");
895    }
896
897    /// An operation at a format the machine has is not this pass's business.
898    #[test]
899    fn the_narrower_formats_go_past_untouched() {
900        let mut names = Interner::new();
901        let double = Type::float(Float::F64);
902        let (mut func, entry, params) = shell(&mut names, &[double, double], &[double]);
903        let mut build = Builder::new(&mut func, entry);
904        let sum = build.binary(Opcode::FAdd, params[0], params[1], Flags::NONE);
905        let answer = build.fcmp(FloatPred::Olt, sum, params[1], Flags::NONE);
906        build.ret(&[sum]);
907        let _ = answer;
908        calls(&mut func, &mut names, sysv());
909        let text = printed(&func, &mut names);
910        assert!(!text.contains("call"), "nothing became a call: {text}");
911        assert!(text.contains("fadd"), "the add is still an add: {text}");
912        assert!(text.contains("fcmp"), "the comparison is still a comparison: {text}");
913    }
914
915    /// The convention decides the shape of the call, and on one of them that shape is addresses.
916    ///
917    /// Windows x64 passes a scalar of a size no register holds as the address of a copy the caller
918    /// made, and returns one the same way, so `__addtf3` there takes three pointers and answers
919    /// nothing. libgcc's routine is compiled to that convention on that target and reads those three
920    /// registers, so writing the other shape is not a difference a test catches later, it is a
921    /// routine reading registers nothing was put in.
922    #[test]
923    fn on_windows_the_operands_and_the_answer_all_travel_as_addresses() {
924        let text = binary_on(Opcode::FAdd, win64());
925        assert!(text.contains("@__addtf3"), "{text}");
926        // Three slots: one per operand, because the routine is entitled to write through an address
927        // it was handed, and one for the answer.
928        assert_eq!(text.matches("alloca").count(), 3, "three slots: {text}");
929        assert_eq!(text.matches("store").count(), 2, "a copy of each operand: {text}");
930        // The call produces nothing, so the value the rest of the function reads is the load after
931        // it rather than the call itself.
932        assert_eq!(text.matches(" = call").count(), 0, "the call answers nothing: {text}");
933        assert_eq!(text.matches("call ").count(), 1, "and there is one of them: {text}");
934        assert_eq!(text.matches(" = load").count(), 1, "read back out of the slot: {text}");
935    }
936
937    /// A comparison answers an `int`, which is a register on every convention, so only the operands
938    /// change shape.
939    #[test]
940    fn on_windows_a_comparison_hands_over_its_operands_and_keeps_its_answer() {
941        let text = compared_on(FloatPred::Oeq, win64());
942        assert!(text.contains("@__eqtf2"), "{text}");
943        assert_eq!(text.matches("alloca").count(), 2, "one slot per operand: {text}");
944        assert_eq!(text.matches("store").count(), 2, "and a copy into each: {text}");
945        assert_eq!(text.matches(" = call").count(), 1, "the answer is still a result: {text}");
946        assert!(text.contains("icmp eq"), "read the same way: {text}");
947    }
948
949    /// The same function on the convention that has registers wide enough is the plain shape.
950    #[test]
951    fn the_convention_that_holds_one_in_a_register_puts_nothing_on_the_frame() {
952        let text = binary_on(Opcode::FAdd, sysv());
953        assert!(text.contains("@__addtf3"), "{text}");
954        assert!(!text.contains("alloca"), "nothing goes through the frame: {text}");
955        assert!(!text.contains("store"), "nothing is copied: {text}");
956        assert_eq!(text.matches(" = call").count(), 1, "the call is the value: {text}");
957    }
958}