Skip to main content

rucc_codegen/
wide.rs

1//! The integer that is wider than a register, as the two registers it is held in.
2//!
3//! `__int128` is the one integer a C program on this machine writes that no register holds.
4//! Everything else the front end produces is a width the machine has, or is a width
5//! [`crate::widths`] rounds up into one, and neither of those is true here: there is nothing to
6//! round up into above sixty four bits. What there is, is two registers, and the convention already
7//! says so. System V classifies a `__int128` as two eightbytes of class INTEGER, so it travels in a
8//! pair of general purpose registers, comes back in the pair a return comes back in, and sits in
9//! memory as two words with the low one first. That is what this pass writes down.
10//!
11//! Every value a hundred and twenty eight bits wide becomes two values of sixty four, a low half
12//! and a high half, and every instruction over such a value becomes instructions over the halves.
13//! After it there is no value of that width left anywhere in the function, which is what lets the
14//! rest of the back end stay written about widths the machine has. Nothing below this knows the
15//! type existed.
16//!
17//! # Why a pass and not a rule
18//!
19//! A rule matches a term and rewrites it into instructions of the machine, and the selector works a
20//! value at a time. There is no register a value this wide can be selected into, so there is
21//! nothing for a rule to produce, and a rule that produced a pair would have to say which register
22//! each half landed in, which is the allocator's answer and not a rule's. So the splitting happens
23//! before selection, in the IR, where a value is still something a pass may make two of. That is
24//! the same reasoning [`crate::widths`] follows from the other end, and the two are the two halves
25//! of one sentence: nothing reaching the selector is at a width the machine has no register for.
26//!
27//! # What crosses the boundary
28//!
29//! A parameter and a return value are agreed with something this compilation is not looking at, so
30//! splitting one is a claim about where the two halves are. The claim is true when both halves land
31//! in registers, because the convention hands out argument registers in order and two halves in a
32//! row take the two registers the whole value would have taken. It is not true when they do not: a
33//! value the convention could not fit in registers travels in the argument area as sixteen bytes
34//! aligned to sixteen and leaves the register it could not use to whatever comes after it, and two
35//! independent words in a row would put the first one in that register and the second a word up
36//! from wherever the area had got to.
37//!
38//! So a signature with such a parameter is laid out again rather than split in place. Where every
39//! parameter is meant to be is worked out first, and then the split signature is put in the order
40//! that makes the ordinary walk over it arrive at the same places: the ones in registers first,
41//! then the ones in the argument area by how far up it they are, with a filler that carries nothing
42//! wherever the convention leaves a register or a word empty. Both ends of a call are built from the
43//! same layout, a function's own parameters and every call it makes, so nothing below this has to
44//! know a parameter was ever in any other order, and the IR needs no form of parameter it did not
45//! have.
46//!
47//! A call to a variadic function is laid out the same way over everything it passes, the arguments
48//! past the `...` included, because System V puts each of those where it would have gone had it
49//! been named. What the ABI asks of each of them moves onto the parameter it becomes, so the call
50//! names all its arguments afterwards and still says the callee is variadic, which is what sets the
51//! count of vector registers the callee is told about. A variadic function's own signature with a
52//! wide named parameter that goes in memory is still refused, because its named parameters are what
53//! `va_start` counts from, and so is any of this on a convention that counts the two register files
54//! as one run, where a float past the `...` travels in both. The `va_arg` of one is read in
55//! `rucc-lower` as a pair of words, which is the walk [`crate::varargs`] already has for a small
56//! structure.
57//!
58//! # Dividing and converting are calls into the runtime
59//!
60//! Every other operation at this width is the same operation over the halves with whatever crossed
61//! between them put back. A quotient is not. The halves of a quotient are not a function of the
62//! halves of its operands taken apart, at this width or at any other, which is why every compiler's
63//! runtime has a division routine in it and none of them has an addition one. So a divide and a
64//! remainder become a call to the routines `runtime/builtins/div.c` defines, which are libgcc's four
65//! names and libgcc's signatures, and `spec/12-abi-and-runtime.md` section 12.8 is what they are.
66//!
67//! A conversion to or from a floating point value is the other one, for a plainer reason: the
68//! machine's own conversion reaches sixty four bits and no further, so there is no instruction to
69//! split into. Those are the eight names `runtime/builtins/convert.c` defines, one for each of a
70//! signed and an unsigned integer against a `float` and a `double` in each direction, and the four
71//! `runtime/builtins/quad.c` defines for a `_Float128`, which has no instruction of its own at any
72//! width and so is a call here for both reasons at once. An eighty bit float is not among them,
73//! because this machine has no register that holds one and the back end says so, which is
74//! tamnd/rucc#326, so a function converting at that width is left alone here and refused below the
75//! way every function of this width used to be.
76//!
77//! The call is asked for with the halves already in it, four parameters of sixty four bits for the
78//! two operands of a divide and two results for the answer, or two parameters and a float, or a
79//! float and two results, which is the shape this pass gives a call it found in the program anyway.
80//! Both ends agree because the convention puts a `__int128` argument in two registers in a row and
81//! hands out argument registers in order, which is the same sentence the section below about
82//! crossing the boundary is.
83//!
84//! That is the shape on every convention that has registers for a value this wide and it is not the
85//! shape on Windows x64, where such a value travels as the address of a copy the caller made and an
86//! integer this wide comes back whole in a vector register. The one place the two are told apart is
87//! where the call is built, by asking the ABI the same question a call in the program is asked, so
88//! everything above it goes on describing the call in halves and nothing above it names a target.
89
90use std::collections::{HashMap, HashSet};
91
92use rucc_base::Interner;
93use rucc_ir::{
94    Abi, Block, BlockCall, CallInfo, Def, Extra, Flags, Float, Func, Imm, Inst, InstData, IntPred,
95    MemInfo, MemOrder, Opcode, Param, Restrict, Signature, Type, Value,
96};
97use rucc_target::{AbiDescription, CallRegs, Convention, Places, Variadic, Where};
98
99use crate::capability;
100use crate::expand;
101
102/// The width this pass is about, which is the one width a C program writes that no register holds.
103const WIDE: u32 = 128;
104
105/// What the capability table calls that width, which is how the rule language spells one.
106const MODE: &str = "i128";
107
108/// The width each half is, which is a register on every target this pass runs for.
109const HALF: u32 = 64;
110
111/// How many bytes one half takes in memory, which is how far the high one sits above the low one.
112const STEP: u64 = 8;
113
114/// Whether a type is the width this pass splits.
115fn is_wide(ty: Type) -> bool {
116    ty.is_int() && ty.is_scalar() && ty.bits() == WIDE
117}
118
119/// The type each half has.
120fn half() -> Type {
121    Type::int(HALF)
122}
123
124/// Splits every integer the machine holds in two registers into the two halves it holds it in.
125///
126/// Gives back whether it changed anything, which is what a test asks and what tells a reader of a
127/// dump that the function the selector saw is not the one the middle end produced.
128///
129/// The function is left exactly as it was when there is nothing at that width, when something at
130/// that width is reached by an instruction this does not understand, and when a half would cross
131/// the function's boundary somewhere the convention has no register for it. All three leave the
132/// refusal to the passes below, which name the construct they could not lower, rather than
133/// rewriting into something that guessed.
134pub fn halves(func: &mut Func, names: &mut Interner, conv: &CallRegs) -> bool {
135    if !func.values().any(|value| is_wide(func[value].ty)) {
136        return false;
137    }
138    let insts: Vec<Inst> =
139        walk(func).into_iter().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
140    let order: HashMap<Inst, usize> =
141        insts.iter().enumerate().map(|(at, &inst)| (inst, at)).collect();
142    if !insts.iter().enumerate().all(|(at, &inst)| can_split(func, conv, &order, at, inst)) {
143        return false;
144    }
145    let Some(arriving) = plan(func.signature(), conv) else { return false };
146    if !func.signatures().all(|signature| plan(signature, conv).is_some()) {
147        return false;
148    }
149
150    let mut halves: Halves = HashMap::new();
151    let mut forward: HashMap<Value, Value> = HashMap::new();
152    let entry = func.entry();
153    for block in func.blocks().collect::<Vec<_>>() {
154        if Some(block) == entry {
155            arrive(func, block, &arriving, &mut halves, &mut forward);
156        } else {
157            params(func, block, &mut halves, &mut forward);
158        }
159    }
160    for &inst in &insts {
161        rewrite(func, names, conv, &mut halves, &mut forward, inst);
162    }
163    substitute(func, &forward);
164    let signature = planned(func.signature(), &arriving);
165    func.set_signature(signature);
166    true
167}
168
169/// Every block, in an order where a block comes after everything that dominates it.
170///
171/// Reverse postorder from the entry, then whatever the walk did not reach, in the order the
172/// function holds them. The order is what the rule below about a use and its definition is read
173/// against, and the two together are the whole of why this is not simply the order the function
174/// holds the blocks in: a value is defined in a block that dominates every block reading it, a
175/// dominator is on every path from the entry, so a depth first walk finishes it last and reverse
176/// postorder puts it first. The order the function holds blocks in says nothing of the kind. It is
177/// the order they were made in, and every pass in the optimizer that makes a block, which is every
178/// pass that gives a loop a preheader or copies a header in front of one, puts a block that runs
179/// early at the end of that list. So the same program compiled at `-O0` and at `-O1` gave two
180/// different answers to whether this pass understood it, and above `-O0` the answer was often no.
181/// tamnd/rucc#1054.
182///
183/// A block nothing reaches cannot be walked to and is put at the end rather than dropped, because
184/// deciding a block is unreachable is not this pass's business. Two of them in the wrong order
185/// refuse the function the way they always did.
186fn walk(func: &Func) -> Vec<Block> {
187    let Some(entry) = func.entry() else { return func.blocks().collect() };
188    let mut seen: HashSet<Block> = HashSet::new();
189    let mut order: Vec<Block> = Vec::new();
190    // A postorder without recursion: the second time a block comes off the stack every block below
191    // it has been finished, so that is where it belongs in the postorder.
192    let mut stack: Vec<(Block, bool)> = vec![(entry, false)];
193    seen.insert(entry);
194    while let Some((block, done)) = stack.pop() {
195        if done {
196            order.push(block);
197            continue;
198        }
199        stack.push((block, true));
200        let Some(term) = func.terminator(block) else { continue };
201        for call in func.successors(term) {
202            if seen.insert(call.block) {
203                stack.push((call.block, false));
204            }
205        }
206    }
207    order.reverse();
208    order.extend(func.blocks().filter(|block| !seen.contains(block)));
209    order
210}
211
212/// The two halves each wide value became, low first.
213type Halves = HashMap<Value, (Value, Value)>;
214
215/// The opcodes this pass knows how to split.
216///
217/// An instruction that touches a value of this width and is not one of these is why the whole
218/// function is left alone, so this list is the pass's own statement of what it has thought about.
219/// Adding to it is adding an arm to [`rewrite`] as well.
220///
221/// The four divisions and the four conversions to and from a floating point value are here, and they
222/// are the entries that become a call rather than arithmetic over the halves. Which float formats
223/// those conversions are understood at is a separate question, asked in [`can_split`], because it is
224/// about the type rather than about the opcode.
225fn understood(opcode: Opcode) -> bool {
226    matches!(
227        opcode,
228        Opcode::IConst
229            | Opcode::Load
230            | Opcode::Store
231            | Opcode::Add
232            | Opcode::Sub
233            | Opcode::Mul
234            | Opcode::UDiv
235            | Opcode::SDiv
236            | Opcode::URem
237            | Opcode::SRem
238            | Opcode::Shl
239            | Opcode::LShr
240            | Opcode::AShr
241            | Opcode::And
242            | Opcode::Or
243            | Opcode::Xor
244            | Opcode::ICmp
245            | Opcode::Select
246            | Opcode::SIToFP
247            | Opcode::UIToFP
248            | Opcode::FPToSI
249            | Opcode::FPToUI
250            | Opcode::Trunc
251            | Opcode::SExt
252            | Opcode::ZExt
253            | Opcode::Call
254            | Opcode::CallIndirect
255            | Opcode::Return
256            | Opcode::Jump
257            | Opcode::BrIf
258    )
259}
260
261/// Whether one instruction is one this pass can split, given where it is in the walk.
262///
263/// Asked of every instruction, and answered yes at once for the ones that never see a value this
264/// wide, which in a function that has one at all is still most of them.
265fn can_split(
266    func: &Func,
267    conv: &CallRegs,
268    order: &HashMap<Inst, usize>,
269    at: usize,
270    inst: Inst,
271) -> bool {
272    let data = func[inst];
273    let reads = operands(func, inst);
274    let wide = |&value: &Value| is_wide(func[value].ty);
275    if !reads.iter().any(wide) && !data.results().any(|value| is_wide(func[value].ty)) {
276        return true;
277    }
278    if !understood(data.opcode) {
279        return false;
280    }
281    // Memory SSA threads a version of memory through each access, and splitting one access into two
282    // makes a version this pass would have to name. Nothing hands this crate a function carrying it
283    // today, and leaving one alone costs less than being wrong about it later.
284    if func.carries_mem(inst) {
285        return false;
286    }
287    // The machine sign extends from a byte and no narrower, so a truth value widened into the high
288    // half would become an instruction with no rule behind it. Zero extending one is fine, which is
289    // why only the signed side is asked about.
290    if data.opcode == Opcode::SExt && reads.iter().any(|&value| func[value].ty.bits() < 8) {
291        return false;
292    }
293    // The runtime has a conversion for a `float`, for a `double` and for a `_Float128`, and for
294    // nothing else, so every other format is refused here rather than turned into a call to a name
295    // nothing defines. An eighty bit float is the one a program reaches without asking for it, since
296    // `long double` is that type on this target, and it is tamnd/rucc#326 rather than an oversight.
297    if matches!(data.opcode, Opcode::SIToFP | Opcode::UIToFP | Opcode::FPToSI | Opcode::FPToUI)
298        && converted(func, inst).is_none()
299    {
300        return false;
301    }
302    // Splitting an argument makes two of them, and which parameter an argument stands for is how a
303    // variadic call knows what the ABI asks of the ones its signature does not name. So a variadic
304    // call is laid out as a whole, with the arguments past the `...` named as parameters, and that
305    // is only right where naming them changes nothing. On a convention that counts the two files as
306    // one run it does, because a float past the `...` goes in both files and one before it does
307    // not, and on Apple's AArch64 it does too, because everything past the `...` goes in memory.
308    if matches!(data.opcode, Opcode::Call | Opcode::CallIndirect) {
309        let Some((site, variadic)) = site(func, inst) else { return false };
310        let Some(conv) = conv.under(site.convention) else { return false };
311        let in_memory = conv.abi.variadic == Variadic::AlwaysMemory;
312        if variadic && (conv.shared_positions || in_memory || plan(&site, conv).is_none()) {
313            return false;
314        }
315    }
316    // The halves of a value are written where the value was, so a use this pass reaches before the
317    // definition is a use whose halves do not exist yet. A value arriving as a block parameter is
318    // always ready, since every block's parameters are split before any instruction is.
319    reads.iter().filter(|value| wide(value)).all(|&value| match func[value].def {
320        Def::Result { inst, .. } => order.get(&inst).is_some_and(|&def| def < at),
321        Def::Param { .. } => true,
322    })
323}
324
325/// The format of the floating point side of a conversion, when the runtime has a routine for it.
326///
327/// One float type is in such an instruction, the result of a conversion going up and the operand of
328/// one coming down, so both ends are looked at and the one is found. `None` means the function is
329/// left alone, and it covers a format with no routine, no float at all, and a float on both ends,
330/// which are three shapes that have nothing to be turned into rather than one.
331fn converted(func: &Func, inst: Inst) -> Option<Float> {
332    let data = func[inst];
333    let mut floats = func[data.args]
334        .iter()
335        .copied()
336        .chain(data.results())
337        .map(|value| func[value].ty)
338        .filter(|ty| ty.is_float());
339    let only = floats.next()?;
340    if floats.next().is_some() {
341        return None;
342    }
343    match only.format() {
344        Some(format @ (Float::F32 | Float::F64 | Float::F128)) => Some(format),
345        _ => None,
346    }
347}
348
349/// Everything an instruction reads: its own operands, and the arguments it passes along its edges.
350///
351/// The arguments of a `jump` and of a `br_if` hang on the block call rather than on the
352/// instruction, so an instruction whose own operands are all narrow may still be handing a wide one
353/// to the block it branches to.
354fn operands(func: &Func, inst: Inst) -> Vec<Value> {
355    let mut reads = func[func[inst].args].to_vec();
356    for call in func.successors(inst).collect::<Vec<_>>() {
357        reads.extend_from_slice(&func[call.args]);
358    }
359    reads
360}
361
362/// What one parameter of a split signature is.
363#[derive(Debug, Clone, Copy, PartialEq, Eq)]
364enum Slot {
365    /// The parameter at that index of the whole signature, which is not wide, as it was.
366    Whole(usize),
367    /// The low half of the wide parameter at that index.
368    Low(usize),
369    /// The high half of it.
370    High(usize),
371    /// A value of that type that carries nothing and is there to take a place nothing else takes.
372    Filler(Type),
373}
374
375/// The parameters of one signature once every wide one is two halves, in the order that puts each
376/// of them where the convention puts the whole signature.
377///
378/// The walk is the one [`crate::abi::entry`] and [`crate::abi::call`] make, because the answer has
379/// to be the one that walk will give: it hands out places in the order the signature holds the
380/// parameters. When both halves of every wide parameter land in registers, two halves in a row in
381/// the place of the whole take the two registers the whole value would have taken, and the order
382/// is the order the signature had.
383///
384/// When one does not, System V puts the whole value in the argument area as sixteen bytes aligned
385/// to sixteen and leaves the registers it did not take to the parameters after it. Two words in a
386/// row go somewhere else: the first takes whatever register is left, and a word only has to be
387/// aligned to eight. So the order is built from where everything is meant to be rather than from
388/// the signature. The ones in general purpose registers come first in register order, then the
389/// ones in vector registers, then the ones in the argument area by how far up it they are. A filler
390/// takes a register a wide value skipped, since a word of the argument area is only handed out once
391/// the registers of its kind have run out, and a filler takes each word the alignment of a wide
392/// value leaves empty. The walk is made again over what was built, and anything that did not land
393/// where it was meant to is `None`, as is a variadic signature, whose named parameters have to stay
394/// in front of the rest, and a convention that counts the two register files as one run, which
395/// passes a value this wide some other way.
396///
397/// A return value is not asked about. What comes back comes back in the registers a return uses,
398/// which is a sequence of its own with two in it on this convention, and a signature wanting more
399/// than it has is refused by name in [`crate::lower`] already.
400///
401/// `conv` is the convention of the function being split, and the signature is laid out under the
402/// convention it names itself, found through it: a function of one convention calls functions of
403/// the other, and each call is laid out the way its callee reads it. A convention the platform
404/// does not have is no layout at all, and the function is left alone.
405fn plan(signature: &Signature, conv: &CallRegs) -> Option<Vec<Slot>> {
406    let conv = conv.under(signature.convention)?;
407    let word = Param::new(half());
408    let mut places = Places::new(conv);
409    let mut meant: Vec<(Slot, Param, Where)> = Vec::new();
410    let mut moved = false;
411    for (index, &param) in signature.params.iter().enumerate() {
412        if !is_wide(param.ty) {
413            meant.push((Slot::Whole(index), param, place(&mut places, param, conv)));
414            continue;
415        }
416        let scalars = conv.abi.scalars;
417        let mut ahead = places.clone();
418        // AAPCS64 starts the pair at an even register, and a filler takes the odd one it skips.
419        let skipped = (scalars.wide_integer_starts_even && ahead.integers() % 2 == 1)
420            .then(|| ahead.integer(HALF / 8));
421        if let (low @ Where::Reg(_), high @ Where::Reg(_)) =
422            (ahead.integer(HALF / 8), ahead.integer(HALF / 8))
423        {
424            places = ahead;
425            if let Some(at) = skipped {
426                meant.push((Slot::Filler(word.ty), word, at));
427            }
428            meant.push((Slot::Low(index), word, low));
429            meant.push((Slot::High(index), word, high));
430            continue;
431        }
432        if scalars.wide_integer_drains {
433            places.drain_integers();
434        }
435        let Where::Stack(at) = places.on_stack(WIDE / 8, WIDE / 8) else { return None };
436        meant.push((Slot::Low(index), word, Where::Stack(at)));
437        meant.push((Slot::High(index), word, Where::Stack(at + HALF / 8)));
438        moved = true;
439    }
440    if !moved {
441        return Some(meant.into_iter().map(|(slot, _, _)| slot).collect());
442    }
443    if signature.variadic || conv.shared_positions {
444        return None;
445    }
446
447    let in_reg = |at: &Where| matches!(at, Where::Reg(_));
448    let mut stacked: Vec<&(Slot, Param, Where)> =
449        meant.iter().filter(|(_, _, at)| !in_reg(at)).collect();
450    stacked.sort_by_key(|(_, _, at)| match at {
451        Where::Stack(up) => *up,
452        Where::Reg(_) => 0,
453    });
454    let mut order: Vec<(Slot, Param, Option<Where>)> = Vec::new();
455    for float in [false, true] {
456        let kind = |param: &Param| scalar(*param) && param.ty.is_float() == float;
457        order.extend(
458            meant
459                .iter()
460                .filter(|(_, param, at)| kind(param) && in_reg(at))
461                .map(|&(slot, param, at)| (slot, param, Some(at))),
462        );
463        let (count, filler) = match float {
464            false => (conv.int_args.len(), word),
465            true => (conv.sse_args.len(), Param::new(Type::float(Float::F64))),
466        };
467        if stacked.iter().any(|(_, param, _)| kind(param)) {
468            let took = order.iter().filter(|(_, param, _)| kind(param)).count();
469            let padding = count.saturating_sub(took);
470            order.extend((0..padding).map(|_| (Slot::Filler(filler.ty), filler, None)));
471        }
472    }
473
474    let mut places = Places::new(conv);
475    let mut slots = Vec::with_capacity(order.len() + stacked.len());
476    for (slot, param, meant) in order {
477        let at = place(&mut places, param, conv);
478        if !in_reg(&at) || meant.is_some_and(|meant| meant != at) {
479            return None;
480        }
481        slots.push(slot);
482    }
483    for &&(slot, param, meant) in &stacked {
484        let Where::Stack(up) = meant else { return None };
485        while places.size() < up {
486            if in_reg(&place(&mut places, word, conv)) {
487                return None;
488            }
489            slots.push(Slot::Filler(word.ty));
490        }
491        if place(&mut places, param, conv) != meant {
492            return None;
493        }
494        slots.push(slot);
495    }
496    Some(slots)
497}
498
499/// Where the next parameter goes, asked the way [`crate::abi::entry`] asks.
500fn place(places: &mut Places<'_>, param: Param, conv: &CallRegs) -> Where {
501    // A structure the classification put in the argument area, which is the one parameter whose
502    // place is bytes rather than a register. Everything else is a value, the pointer an `sret`
503    // hands over included, and a value takes the next register of its own kind.
504    if let Abi::ByVal { size, align, drains } = param.abi {
505        let at = places.object(u32::try_from(size).unwrap_or(u32::MAX), align);
506        crate::abi::drain(places, drains);
507        at
508    } else if crate::abi::on_the_stack(param.ty) {
509        let (size, align) = crate::abi::X87_AREA;
510        places.on_stack(size, align)
511    } else if param.ty.is_float() {
512        places.float(crate::abi::float_bytes(param.ty))
513    } else {
514        places.integer(crate::abi::int_bytes(param.ty, conv))
515    }
516}
517
518/// Whether a parameter is a value that takes a register of its kind while there is one left.
519fn scalar(param: Param) -> bool {
520    !matches!(param.abi, Abi::ByVal { .. }) && !crate::abi::on_the_stack(param.ty)
521}
522
523/// A signature laid out the way [`plan`] said, with every wide return value as two halves.
524fn planned(signature: &Signature, slots: &[Slot]) -> Signature {
525    let params = slots
526        .iter()
527        .map(|&slot| match slot {
528            Slot::Whole(index) => signature.params[index],
529            Slot::Low(_) | Slot::High(_) => Param::new(half()),
530            Slot::Filler(ty) => Param::new(ty),
531        })
532        .collect();
533    Signature { params, ..split_signature(signature) }
534}
535
536/// One block's parameters, with each wide one replaced by its two halves in the same position.
537///
538/// Every parameter of such a block is made again rather than only the wide ones, because a
539/// parameter's position is its identity to the branches that feed it and appending is the only way
540/// to add one. The narrow ones are made again as themselves and pointed at the copy, which costs
541/// nothing once the substitution below has run.
542fn params(func: &mut Func, block: Block, halves: &mut Halves, forward: &mut HashMap<Value, Value>) {
543    let old: Vec<Value> = func[block].params.clone();
544    if !old.iter().any(|&value| is_wide(func[value].ty)) {
545        return;
546    }
547    for &value in &old {
548        if is_wide(func[value].ty) {
549            let low = func.append_param(block, half());
550            let high = func.append_param(block, half());
551            halves.insert(value, (low, high));
552        } else {
553            let again = func.append_param(block, func[value].ty);
554            forward.insert(value, again);
555        }
556    }
557    func.retain_params(block, |value| !old.contains(&value));
558}
559
560/// The entry block's parameters, laid out the way [`plan`] said the function's own are.
561fn arrive(
562    func: &mut Func,
563    block: Block,
564    slots: &[Slot],
565    halves: &mut Halves,
566    forward: &mut HashMap<Value, Value>,
567) {
568    let old: Vec<Value> = func[block].params.clone();
569    if !old.iter().any(|&value| is_wide(func[value].ty)) {
570        return;
571    }
572    let mut lows = HashMap::new();
573    for &slot in slots {
574        match slot {
575            Slot::Whole(index) => {
576                let again = func.append_param(block, func[old[index]].ty);
577                forward.insert(old[index], again);
578            }
579            Slot::Low(index) => {
580                lows.insert(index, func.append_param(block, half()));
581            }
582            Slot::High(index) => {
583                let high = func.append_param(block, half());
584                halves.insert(old[index], (lows[&index], high));
585            }
586            Slot::Filler(ty) => {
587                func.append_param(block, ty);
588            }
589        }
590    }
591    func.retain_params(block, |value| !old.contains(&value));
592}
593
594/// One instruction, as instructions over halves.
595fn rewrite(
596    func: &mut Func,
597    names: &mut Interner,
598    conv: &CallRegs,
599    halves: &mut Halves,
600    forward: &mut HashMap<Value, Value>,
601    inst: Inst,
602) {
603    // The runtime's routines are ordinary functions of the platform, whatever convention the
604    // function calling them was written in, so a call to one is shaped by the platform's own.
605    let abi = conv.under(Convention::Target).unwrap_or(conv).abi;
606    let data = func[inst];
607    let produces = data.results().any(|value| is_wide(func[value].ty));
608    let takes = func[data.args].iter().any(|&value| is_wide(func[value].ty));
609    match data.opcode {
610        Opcode::IConst if produces => constant(func, halves, inst),
611        Opcode::Load if produces => load(func, halves, inst),
612        Opcode::Store if takes => store(func, halves, inst),
613        Opcode::Add | Opcode::Sub if produces => carried(func, halves, inst, data.opcode),
614        Opcode::Mul if produces => multiply(func, halves, inst),
615        Opcode::UDiv | Opcode::SDiv | Opcode::URem | Opcode::SRem if produces => {
616            divide(func, names, abi, halves, inst, data.opcode);
617        }
618        Opcode::Shl | Opcode::LShr | Opcode::AShr if produces => {
619            shifted(func, halves, inst, data.opcode);
620        }
621        Opcode::And | Opcode::Or | Opcode::Xor if produces => {
622            bitwise(func, halves, inst, data.opcode);
623        }
624        Opcode::SIToFP | Opcode::UIToFP if takes => {
625            to_float(func, names, abi, halves, forward, inst, data.opcode == Opcode::SIToFP);
626        }
627        Opcode::FPToSI | Opcode::FPToUI if produces => {
628            from_float(func, names, abi, halves, inst, data.opcode == Opcode::FPToSI);
629        }
630        Opcode::ICmp if takes => compare(func, halves, forward, inst),
631        Opcode::Select if produces => choose(func, halves, inst),
632        Opcode::Trunc if takes => truncate(func, halves, forward, inst),
633        Opcode::SExt | Opcode::ZExt if produces => {
634            extend(func, halves, inst, data.opcode == Opcode::SExt);
635        }
636        Opcode::Call | Opcode::CallIndirect if produces || takes => {
637            call(func, conv, halves, forward, inst);
638        }
639        Opcode::Return if takes => flatten(func, halves, inst),
640        Opcode::Jump | Opcode::BrIf => edges(func, halves, inst),
641        _ => {}
642    }
643}
644
645/// A constant, as the two halves of its bits with the low one first.
646fn constant(func: &mut Func, halves: &mut Halves, inst: Inst) {
647    let Extra::Imm(imm) = func[inst].extra else { return };
648    let bits = func[imm].unsigned();
649    #[expect(clippy::cast_possible_truncation, reason = "the halves are what this is taking")]
650    let (low, high) = (bits as u64, (bits >> HALF) as u64);
651    let low = ahead_const(func, inst, i128::from(low));
652    let high = ahead_const(func, inst, i128::from(high));
653    replace(func, halves, inst, low, high);
654}
655
656/// A read, as the two words of it with the low one first.
657///
658/// Little endian is the order, which is what every target this back end has is. The high word knows
659/// less about its alignment than the low one when the low one knew more than a word, since a
660/// sixteen byte object aligned to sixteen has its high word aligned to eight.
661fn load(func: &mut Func, halves: &mut Halves, inst: Inst) {
662    let data = func[inst];
663    let Extra::Mem(mem) = data.extra else { return };
664    let info = func[mem];
665    let Some(&from) = func[data.args].first() else { return };
666    let low = read(func, inst, from, word(info, 0), data.flags);
667    let up = stepped(func, inst, from);
668    let high = read(func, inst, up, word(info, STEP), data.flags);
669    replace(func, halves, inst, low, high);
670}
671
672/// A write, as the two words of it.
673fn store(func: &mut Func, halves: &mut Halves, inst: Inst) {
674    let data = func[inst];
675    let Extra::Mem(mem) = data.extra else { return };
676    let info = func[mem];
677    let args = func[data.args].to_vec();
678    let [value, into] = args[..] else { return };
679    let Some(&(low, high)) = halves.get(&value) else { return };
680    write(func, inst, low, into, word(info, 0), data.flags);
681    let up = stepped(func, inst, into);
682    write(func, inst, high, up, word(info, STEP), data.flags);
683    func.remove_inst(inst);
684}
685
686/// An add or a subtract, as the same over the low halves and the same again over the high ones with
687/// what the low halves carried between them.
688///
689/// The carry is a comparison and not a flag. An unsigned sum comes out below either operand exactly
690/// when it wrapped, and an unsigned difference wrapped exactly when the left operand was below the
691/// right, which are the two tests [`crate::expand`] writes for the overflow builtins and are what
692/// the machine's own carry flag stands for. Whether the pair is put back together into an `adc` and
693/// an `sbb` is a question for what reads flags rather than for this, and the answer here is correct
694/// either way.
695fn carried(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
696    let args = func[func[inst].args].to_vec();
697    let [a, b] = args[..] else { return };
698    let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
699        return;
700    };
701    let low = ahead(func, inst, opcode, &[a_low, b_low]);
702    let carried = if opcode == Opcode::Add {
703        compared(func, inst, IntPred::Ult, low, a_low)
704    } else {
705        compared(func, inst, IntPred::Ult, a_low, b_low)
706    };
707    let carry = ahead(func, inst, Opcode::ZExt, &[carried]);
708    let high = ahead(func, inst, opcode, &[a_high, b_high]);
709    let high = ahead(func, inst, opcode, &[high, carry]);
710    replace(func, halves, inst, low, high);
711}
712
713/// A multiply, which is long multiplication in base two to the sixty fourth with everything that
714/// lands above the width thrown away.
715///
716/// The low half of the answer is the low halves multiplied together. The high half is what that
717/// multiply carried out of its own top, plus the two cross products, each of which starts at bit
718/// sixty four. The fourth partial product is the two high halves against each other and it starts
719/// at bit one hundred and twenty eight, so the whole of it is above the width and it is never
720/// worked out, which is why a wide multiply is three multiplies and not four.
721///
722/// Nothing here asks whether the operands are signed, because the low hundred and twenty eight bits
723/// of a product are the same bits either way. The sign only matters to the bits that are being
724/// thrown away.
725///
726/// The carry out of the low halves is the high half of a sixty four bit product, which this machine
727/// has an instruction for and this compiler has no way to ask for. [`crate::expand`] already writes
728/// that out as long multiplication one level further down, for the overflow builtins, so this calls
729/// it rather than keeping a second copy of the same arithmetic. It is the expensive part of a wide
730/// multiply by a long way, and `tamnd/rucc#309` is the rule that would make it one instruction for
731/// both callers at once.
732fn multiply(func: &mut Func, halves: &mut Halves, inst: Inst) {
733    let args = func[func[inst].args].to_vec();
734    let [a, b] = args[..] else { return };
735    let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
736        return;
737    };
738    let low = ahead(func, inst, Opcode::Mul, &[a_low, b_low]);
739    let carried = expand::high_half(func, inst, a_low, b_low, false, half());
740    let cross = ahead(func, inst, Opcode::Mul, &[a_low, b_high]);
741    let other = ahead(func, inst, Opcode::Mul, &[a_high, b_low]);
742    let high = ahead(func, inst, Opcode::Add, &[carried, cross]);
743    let high = ahead(func, inst, Opcode::Add, &[high, other]);
744    replace(func, halves, inst, low, high);
745}
746
747/// A divide or a remainder, as a call to the routine in the compiler runtime that works it out.
748///
749/// The four names are libgcc's, and the archive `runtime/builtins/div.c` builds into defines them for
750/// a target that has no libgcc, which is every target this compiler links without gcc's driver. What
751/// picks one of the four is the opcode and nothing else: the sign is in the name because it is in the
752/// answer, since a quotient rounds towards zero and a remainder takes the sign of the dividend, and
753/// neither is the unsigned answer with bits reinterpreted the way a sum is.
754///
755/// The call is created with the halves in it rather than with the wide values, which would then be
756/// split by [`call`] on the next instruction of the walk. Four parameters and two results, in the
757/// order the operands were in and low half first, because that is where the convention puts the two
758/// eightbytes of a value this wide and [`split_signature`] is what the routine's own definition went
759/// through on the way in.
760///
761/// Nothing here is conditional on the divisor. Dividing by zero is undefined in C, the machine traps
762/// on it at every width it has, and a test written in front of the call would be this pass deciding
763/// what an undefined program does.
764fn divide(
765    func: &mut Func,
766    names: &mut Interner,
767    abi: &'static AbiDescription,
768    halves: &mut Halves,
769    inst: Inst,
770    opcode: Opcode,
771) {
772    let args = func[func[inst].args].to_vec();
773    let [a, b] = args[..] else { return };
774    let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
775        return;
776    };
777    // The four that need the whole value at once, which is why they are calls rather than a pair
778    // of half width instructions like everything else in this pass. Which call each one is, is in
779    // the capability table, since a routine name is a fact about what this target cannot do.
780    let Some(routine) = capability::libcall(opcode, MODE) else { return };
781    let args = [Operand::Split(a_low, a_high), Operand::Split(b_low, b_high)];
782    let made = runtime(func, names, abi, inst, routine, &args, &[half(), half()]);
783    let [low, high] = made[..] else { return };
784    replace(func, halves, inst, low, high);
785}
786
787/// A conversion from one of these to a float, as a call to the routine that works it out.
788///
789/// Two parameters of sixty four bits and one float result. The answer is not a wide value, so the
790/// instruction's own result is pointed at the call's rather than halved, which is what [`compare`]
791/// and [`truncate`] do with a narrow answer as well.
792///
793/// The sign is in the name because it is in the answer: the same hundred and twenty eight bits are
794/// two different numbers depending on it, and unlike a sum the float they become is two different
795/// floats.
796fn to_float(
797    func: &mut Func,
798    names: &mut Interner,
799    abi: &'static AbiDescription,
800    halves: &Halves,
801    forward: &mut HashMap<Value, Value>,
802    inst: Inst,
803    signed: bool,
804) {
805    let Some(&arg) = func[func[inst].args].first() else { return };
806    let Some(&(low, high)) = halves.get(&arg) else { return };
807    let (Some(result), Some(format)) = (func[inst].first_result, converted(func, inst)) else {
808        return;
809    };
810    let routine = going_up(signed, format);
811    let args = [Operand::Split(low, high)];
812    let made = runtime(func, names, abi, inst, routine, &args, &[func[result].ty]);
813    if let [answer] = made[..] {
814        forward.insert(result, answer);
815    }
816    func.remove_inst(inst);
817}
818
819/// A conversion from a float to one of these, as a call to the routine that works it out.
820///
821/// One float parameter and two results of sixty four bits, which is the divide's shape with the
822/// operands and the answer the other way round. The operand is a float and so was never split, and
823/// it is passed along as it is.
824///
825/// A value the integer cannot hold, an infinity and a not a number are all undefined in C, and
826/// nothing is written in front of the call about any of them, for the reason [`divide`] writes
827/// nothing in front of itself about a zero divisor.
828fn from_float(
829    func: &mut Func,
830    names: &mut Interner,
831    abi: &'static AbiDescription,
832    halves: &mut Halves,
833    inst: Inst,
834    signed: bool,
835) {
836    let Some(&arg) = func[func[inst].args].first() else { return };
837    let Some(format) = converted(func, inst) else { return };
838    let routine = coming_down(signed, format);
839    let args = [Operand::Whole(arg)];
840    let made = runtime(func, names, abi, inst, routine, &args, &[half(), half()]);
841    let [low, high] = made[..] else { return };
842    replace(func, halves, inst, low, high);
843}
844
845/// The routine that turns an integer this wide into a float of that format.
846///
847/// Three formats, since [`converted`] answers with no others, and the quad is the last arm rather
848/// than a named one so that a format added to that list arrives here as a routine that does not
849/// exist rather than as a name that is wrong.
850fn going_up(signed: bool, format: Float) -> &'static str {
851    let mode = match format {
852        Float::F32 => "i128.f32",
853        Float::F64 => "i128.f64",
854        _ => "i128.f128",
855    };
856    routine(if signed { Opcode::SIToFP } else { Opcode::UIToFP }, mode)
857}
858
859/// The routine that turns a float of that format into an integer this wide.
860fn coming_down(signed: bool, format: Float) -> &'static str {
861    let mode = match format {
862        Float::F32 => "f32.i128",
863        Float::F64 => "f64.i128",
864        _ => "f128.i128",
865    };
866    routine(if signed { Opcode::FPToSI } else { Opcode::FPToUI }, mode)
867}
868
869/// The routine the capability table names for this operation at this width.
870///
871/// Every mode this pass asks about is one this machine has no register wide enough for, so the
872/// table always has an answer and a missing one is the table and this pass having gone out of step.
873fn routine(opcode: Opcode, mode: &str) -> &'static str {
874    capability::libcall(opcode, mode)
875        .unwrap_or_else(|| panic!("no routine for `{}` at `{mode}`", opcode.name()))
876}
877
878/// One operand of a call to a runtime routine, as this pass has it in hand.
879///
880/// A wide integer is two halves here because two halves is what this pass has turned every one of
881/// them into, and whether the routine is handed the two of them or the address of the one value
882/// they are is the convention's answer rather than this pass's.
883#[derive(Clone, Copy)]
884enum Operand {
885    /// A value of a type the machine holds, given as itself.
886    Whole(Value),
887    /// A wide integer, given as the low half and the high half it became.
888    Split(Value, Value),
889}
890
891/// A call to a routine in the compiler runtime, written in front of an instruction, with the values
892/// it answers.
893///
894/// Where the convention hands everything over in registers the signature is made out of the types
895/// of the values being handed over, because the values are already the halves at this point and the
896/// routine's own definition went through [`split_signature`] on the way in, so the two descriptions
897/// are the same one arrived at from the two ends.
898///
899/// Windows x64 does not hand a value of this width over in registers. A scalar of a size no register
900/// holds travels as the address of a copy the caller made, which is the rule `tamnd/rucc#1331` put
901/// in the ABI description, and it applies to a call this pass writes exactly as it applies to a call
902/// the program wrote: libgcc's `__floattitf` on that target reads its `__int128` out of the address
903/// in `rdx` and writes its answer through the address in `rcx`. So an operand the convention passes
904/// by address becomes a frame slot with a copy of the value in it, a slot per operand because a
905/// routine may write through an address it was handed, and a single answer that comes back by
906/// address becomes a slot passed as the leading `sret` argument with the load out of it standing for
907/// the call's result.
908///
909/// An answer at this width is the third shape and the one that reads strangely. mingw brings a
910/// sixteen byte integer back in `xmm0` rather than through an address, which is gcc's answer to a
911/// convention that has no integer that size in it, so the call is written as returning one value at
912/// the quad float format and the two halves are read back out of a slot it is stored into. The
913/// format is a name for sixteen bytes in a vector register and says nothing about what is in them,
914/// which is the same thing the routine's own `movaps` says.
915fn runtime(
916    func: &mut Func,
917    names: &mut Interner,
918    abi: &'static AbiDescription,
919    inst: Inst,
920    routine: &str,
921    args: &[Operand],
922    results: &[Type],
923) -> Vec<Value> {
924    let mut params: Vec<Param> = Vec::new();
925    let mut values: Vec<Value> = Vec::new();
926    let mut answer = Answer::Registers;
927    // The answer first, because an address it comes back through is the first argument.
928    match *results {
929        [ty] if abi.scalar_is_by_reference(bytes(ty)) => {
930            let size = bytes(ty);
931            let align = align(size);
932            let slot = room(func, inst, size, align);
933            params.push(Param::with_abi(Type::PTR, Abi::Sret { size, align }));
934            values.push(slot);
935            answer = Answer::Slot(slot, ty);
936        }
937        [low, high] if low == half() && high == half() => {
938            if let Some(format) = packed(abi) {
939                answer = Answer::Packed(format);
940            }
941        }
942        _ => {}
943    }
944    for &arg in args {
945        handed(func, abi, inst, arg, &mut params, &mut values);
946    }
947    let answers = match answer {
948        Answer::Registers => results.to_vec(),
949        Answer::Slot(..) => Vec::new(),
950        Answer::Packed(format) => vec![Type::float(format)],
951    };
952    let returns = answers.iter().map(|&ty| Param::new(ty)).collect();
953    // The platform's own convention, as the routine is an ordinary function of the platform.
954    let signature = func.add_signature(Signature { params, returns, ..Signature::new() });
955    let callee = Some(names.intern(routine));
956    let varargs = func.push_abis(&[]);
957    let extra = Extra::Call(func.add_call(CallInfo { callee, signature, varargs }));
958    let pushed = func.push_values(&values);
959    let span = func.span(inst);
960    let data = InstData { args: pushed, extra, ..InstData::new(Opcode::Call) };
961    let made = func.create_inst(data, &answers, span);
962    func.insert_before(made, inst);
963    match answer {
964        Answer::Registers => func[made].results().collect(),
965        Answer::Slot(slot, ty) => {
966            let size = bytes(ty);
967            let info = whole(size, align(size));
968            let extra = Extra::Mem(func.add_mem(info));
969            let args = func.push_values(&[slot]);
970            let data = InstData { args, extra, ..InstData::new(Opcode::Load) };
971            vec![written(func, inst, data, ty)]
972        }
973        Answer::Packed(format) => unpacked(func, inst, made, format),
974    }
975}
976
977/// Where the answer of such a call is, once the convention has been asked about it.
978#[derive(Clone, Copy)]
979enum Answer {
980    /// In the registers the signature's own result types name, which is every convention that has
981    /// registers for a value of the width being asked about.
982    Registers,
983    /// In a frame slot whose address went over as the leading `sret` argument, with the type the
984    /// call was meant to answer with.
985    Slot(Value, Type),
986    /// Whole in a vector register, at a format that is this many bytes and is not what is in them.
987    Packed(Float),
988}
989
990/// The format a wide integer answer comes back in on this convention, where it comes back in a
991/// register at all, as the IR spells a format.
992///
993/// [`None`] everywhere but Windows x64. The question is asked of the ABI rather than of the target
994/// name for the reason the rest of this pass asks it there: a second list of which targets do this
995/// is a list that can disagree with the one the classifier reads.
996fn packed(abi: &'static AbiDescription) -> Option<Float> {
997    let format = abi.wide_integer_returns_in(u64::from(WIDE / 8))?;
998    Float::from_bits(format.width())
999}
1000
1001/// The two halves of an answer that came back whole in a register, read out of a slot it is stored
1002/// into.
1003///
1004/// A store and two loads rather than anything cleverer because the value in hand is at a float
1005/// format and the halves wanted are integers, and the frame is the only place this compiler moves
1006/// bits between the two files without saying something about them. It is what gcc writes for the
1007/// same call.
1008fn unpacked(func: &mut Func, inst: Inst, call: Inst, format: Float) -> Vec<Value> {
1009    let Some(value) = func[call].first_result else { return Vec::new() };
1010    let size = bytes(Type::float(format));
1011    let align = align(size);
1012    let slot = room(func, inst, size, align);
1013    let info = whole(size, align);
1014    write(func, inst, value, slot, info, Flags::NONE);
1015    let low = read(func, inst, slot, word(info, 0), Flags::NONE);
1016    let up = stepped(func, inst, slot);
1017    let high = read(func, inst, up, word(info, STEP), Flags::NONE);
1018    vec![low, high]
1019}
1020
1021/// One operand of such a call, in the form the convention hands it over in.
1022fn handed(
1023    func: &mut Func,
1024    abi: &'static AbiDescription,
1025    inst: Inst,
1026    arg: Operand,
1027    params: &mut Vec<Param>,
1028    values: &mut Vec<Value>,
1029) {
1030    match arg {
1031        Operand::Whole(value) => {
1032            let ty = func[value].ty;
1033            let size = bytes(ty);
1034            if !abi.scalar_is_by_reference(size) {
1035                params.push(Param::new(ty));
1036                values.push(value);
1037                return;
1038            }
1039            let align = align(size);
1040            let slot = room(func, inst, size, align);
1041            write(func, inst, value, slot, whole(size, align), Flags::NONE);
1042            params.push(Param::new(Type::PTR));
1043            values.push(slot);
1044        }
1045        Operand::Split(low, high) => {
1046            let size = u64::from(WIDE / 8);
1047            if !abi.scalar_is_by_reference(size) {
1048                params.push(Param::new(half()));
1049                values.push(low);
1050                params.push(Param::new(half()));
1051                values.push(high);
1052                return;
1053            }
1054            let align = align(size);
1055            let slot = room(func, inst, size, align);
1056            let info = whole(size, align);
1057            write(func, inst, low, slot, word(info, 0), Flags::NONE);
1058            let up = stepped(func, inst, slot);
1059            write(func, inst, high, up, word(info, STEP), Flags::NONE);
1060            params.push(Param::new(Type::PTR));
1061            values.push(slot);
1062        }
1063    }
1064}
1065
1066/// How many bytes a value of this type takes.
1067fn bytes(ty: Type) -> u64 {
1068    u64::from(ty.bits().div_ceil(8))
1069}
1070
1071/// How far a value of that size is aligned, which at these sizes is the size itself.
1072fn align(size: u64) -> u32 {
1073    u32::try_from(size).unwrap_or(u32::MAX)
1074}
1075
1076/// An ordinary access of the whole of one value of that size.
1077fn whole(size: u64, align: u32) -> MemInfo {
1078    MemInfo {
1079        size,
1080        align,
1081        order: MemOrder::NotAtomic,
1082        tbaa: None,
1083        owns: 0,
1084        restrict: Restrict::NONE,
1085    }
1086}
1087
1088/// A frame slot of that size, written in front of an instruction.
1089fn room(func: &mut Func, inst: Inst, size: u64, align: u32) -> Value {
1090    let extra = Extra::Mem(func.add_mem(whole(size, align)));
1091    written(func, inst, InstData { extra, ..InstData::new(Opcode::Alloca) }, Type::PTR)
1092}
1093
1094/// A shift, as each half shifted by the count with the bits that crossed between them put back, and
1095/// a second answer for a count that reached a whole half.
1096///
1097/// A count below sixty four moves each half by the count, and the bits that left one half are the
1098/// ones that arrive in the other. A count of sixty four or more empties one half completely, and
1099/// what lands in the other is the first half moved by the count less sixty four. Taking the sixty
1100/// four bit off a count in range is the same as subtracting sixty four from it, so both cases shift
1101/// by the same number of places and differ only in which value ends up where, which means one shift
1102/// each and a choice rather than two of everything. The choice is a `select` and not a branch, for
1103/// the reason [`choose`] gives.
1104///
1105/// The bits that cross move the other way by sixty four less the count. That is a shift of sixty
1106/// four places when the count is zero, which is not a distance this width has. Moving one place and
1107/// then sixty three less the count is the same distance for every count from one to sixty three,
1108/// and for a count of zero it shifts a value whose top bit is already gone all the way down to
1109/// nothing, which is the right answer: a half that did not move carries nothing into the other one.
1110///
1111/// A count of a hundred and twenty eight or more is undefined in C and nothing here goes out of its
1112/// way about it, the same as at every other width.
1113fn shifted(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
1114    let args = func[func[inst].args].to_vec();
1115    let [a, b] = args[..] else { return };
1116    let (Some(&(a_low, a_high)), Some(&(count, _))) = (halves.get(&a), halves.get(&b)) else {
1117        return;
1118    };
1119    let top = ahead_const(func, inst, i128::from(HALF - 1));
1120    let places = ahead(func, inst, Opcode::And, &[count, top]);
1121    let back = ahead(func, inst, Opcode::Sub, &[top, places]);
1122    let one = ahead_const(func, inst, 1);
1123    let zero = ahead_const(func, inst, 0);
1124    let bit = ahead_const(func, inst, i128::from(HALF));
1125    let reach = ahead(func, inst, Opcode::And, &[count, bit]);
1126    let whole = compared(func, inst, IntPred::Ne, reach, zero);
1127
1128    let (low, high) = if opcode == Opcode::Shl {
1129        let moved = ahead(func, inst, Opcode::Shl, &[a_low, places]);
1130        let edge = ahead(func, inst, Opcode::LShr, &[a_low, one]);
1131        let across = ahead(func, inst, Opcode::LShr, &[edge, back]);
1132        let above = ahead(func, inst, Opcode::Shl, &[a_high, places]);
1133        let joined = ahead(func, inst, Opcode::Or, &[above, across]);
1134        let low = ahead(func, inst, Opcode::Select, &[whole, zero, moved]);
1135        let high = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
1136        (low, high)
1137    } else {
1138        let moved = ahead(func, inst, opcode, &[a_high, places]);
1139        let edge = ahead(func, inst, Opcode::Shl, &[a_high, one]);
1140        let across = ahead(func, inst, Opcode::Shl, &[edge, back]);
1141        let below = ahead(func, inst, Opcode::LShr, &[a_low, places]);
1142        let joined = ahead(func, inst, Opcode::Or, &[below, across]);
1143        // What is left behind when the whole low half is gone: zeroes for a logical shift, and for
1144        // an arithmetic one the sign bit spread over the half it came from.
1145        let spent = if opcode == Opcode::AShr {
1146            ahead(func, inst, Opcode::AShr, &[a_high, top])
1147        } else {
1148            zero
1149        };
1150        let low = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
1151        let high = ahead(func, inst, Opcode::Select, &[whole, spent, moved]);
1152        (low, high)
1153    };
1154    replace(func, halves, inst, low, high);
1155}
1156
1157/// An `and`, an `or` or an `xor`, which is the same operation on each half and nothing between
1158/// them.
1159fn bitwise(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
1160    let args = func[func[inst].args].to_vec();
1161    let [a, b] = args[..] else { return };
1162    let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
1163        return;
1164    };
1165    let low = ahead(func, inst, opcode, &[a_low, b_low]);
1166    let high = ahead(func, inst, opcode, &[a_high, b_high]);
1167    replace(func, halves, inst, low, high);
1168}
1169
1170/// A comparison, which produces one bit and so is pointed at its answer rather than halved.
1171///
1172/// An equality is the two halves differing in neither place, which is one `or` over two `xor`s
1173/// against zero and is shorter than comparing twice and combining. An ordering is the high halves
1174/// settling it outright, or the low halves settling it when the high halves are equal, and the low
1175/// halves are compared without a sign because the low half of a signed number is unsigned whatever
1176/// the number is.
1177///
1178/// The high halves are asked a strict question even when the predicate is not strict. A predicate
1179/// that lets the two be equal is true of two equal high halves whatever the low halves say, and
1180/// what decides it there is the low halves, so `a >= b` is `a.hi > b.hi` or the high halves being
1181/// equal and `a.lo >= b.lo` unsigned. Asking `a.hi >= b.hi` instead makes every value with a high
1182/// half of its own greater than or equal to every other, which is the shape of this that a
1183/// differential run against GCC caught.
1184fn compare(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
1185    let Extra::IntPred(pred) = func[inst].extra else { return };
1186    let args = func[func[inst].args].to_vec();
1187    let [a, b] = args[..] else { return };
1188    let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
1189        return;
1190    };
1191    let answer = if matches!(pred, IntPred::Eq | IntPred::Ne) {
1192        let low = ahead(func, inst, Opcode::Xor, &[a_low, b_low]);
1193        let high = ahead(func, inst, Opcode::Xor, &[a_high, b_high]);
1194        let both = ahead(func, inst, Opcode::Or, &[low, high]);
1195        let zero = ahead_const(func, inst, 0);
1196        compared(func, inst, pred, both, zero)
1197    } else {
1198        let above = compared(func, inst, strict(pred), a_high, b_high);
1199        let below = compared(func, inst, unsigned(pred), a_low, b_low);
1200        let same = compared(func, inst, IntPred::Eq, a_high, b_high);
1201        let tail = bit(func, inst, Opcode::And, same, below);
1202        bit(func, inst, Opcode::Or, above, tail)
1203    };
1204    if let Some(result) = func[inst].first_result {
1205        forward.insert(result, answer);
1206    }
1207    func.remove_inst(inst);
1208}
1209
1210/// The same ordering with the equal case taken out of it, which is what the high halves are asked.
1211fn strict(pred: IntPred) -> IntPred {
1212    match pred {
1213        IntPred::Sle => IntPred::Slt,
1214        IntPred::Sge => IntPred::Sgt,
1215        IntPred::Ule => IntPred::Ult,
1216        IntPred::Uge => IntPred::Ugt,
1217        other => other,
1218    }
1219}
1220
1221/// The same ordering with no sign in it, which is how the low halves of two signed numbers compare.
1222fn unsigned(pred: IntPred) -> IntPred {
1223    match pred {
1224        IntPred::Slt => IntPred::Ult,
1225        IntPred::Sle => IntPred::Ule,
1226        IntPred::Sgt => IntPred::Ugt,
1227        IntPred::Sge => IntPred::Uge,
1228        other => other,
1229    }
1230}
1231
1232/// A choice between two wide values, which is the same choice made on each half.
1233///
1234/// Two of them rather than one, with the condition read twice. What that costs is one more
1235/// conditional move, and what the alternative costs is a branch, which is the more expensive of the
1236/// two on anything that predicts.
1237fn choose(func: &mut Func, halves: &mut Halves, inst: Inst) {
1238    let args = func[func[inst].args].to_vec();
1239    let [cond, then, other] = args[..] else { return };
1240    let (Some(&(then_low, then_high)), Some(&(other_low, other_high))) =
1241        (halves.get(&then), halves.get(&other))
1242    else {
1243        return;
1244    };
1245    let low = ahead(func, inst, Opcode::Select, &[cond, then_low, other_low]);
1246    let high = ahead(func, inst, Opcode::Select, &[cond, then_high, other_high]);
1247    replace(func, halves, inst, low, high);
1248}
1249
1250/// Keeping the low bits of a wide value, which is the low half and then whatever is left to do.
1251///
1252/// Down to sixty four there is nothing left to do and the low half is the answer, so the truncation
1253/// goes and its readers read the half. Down to anything narrower the machine's own truncation still
1254/// happens, out of the half rather than out of the value that is no longer there.
1255fn truncate(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
1256    let Some(&arg) = func[func[inst].args].first() else { return };
1257    let Some(&(low, _)) = halves.get(&arg) else { return };
1258    let Some(result) = func[inst].first_result else { return };
1259    if func[result].ty.bits() == HALF {
1260        forward.insert(result, low);
1261        func.remove_inst(inst);
1262        return;
1263    }
1264    becomes(func, inst, Opcode::Trunc, &[low]);
1265}
1266
1267/// Widening into a wide value, which is the value in the low half and its own sign or zero above.
1268fn extend(func: &mut Func, halves: &mut Halves, inst: Inst, signed: bool) {
1269    let Some(&arg) = func[func[inst].args].first() else { return };
1270    let low = if func[arg].ty.bits() == HALF {
1271        arg
1272    } else {
1273        let opcode = if signed { Opcode::SExt } else { Opcode::ZExt };
1274        ahead(func, inst, opcode, &[arg])
1275    };
1276    let high = if signed {
1277        let top = ahead_const(func, inst, i128::from(HALF - 1));
1278        ahead(func, inst, Opcode::AShr, &[low, top])
1279    } else {
1280        ahead_const(func, inst, 0)
1281    };
1282    replace(func, halves, inst, low, high);
1283}
1284
1285/// A call, as a call passing and receiving halves.
1286///
1287/// The instruction is made again rather than edited, because how many values a call gives back is
1288/// settled when it is created and a wide return value is two where it was one. Its signature is
1289/// made again for the same reason, since the signature is what each end of the call lays itself out
1290/// against and both ends are split the same way.
1291fn call(
1292    func: &mut Func,
1293    conv: &CallRegs,
1294    halves: &mut Halves,
1295    forward: &mut HashMap<Value, Value>,
1296    inst: Inst,
1297) {
1298    let data = func[inst];
1299    let Extra::Call(info) = data.extra else { return };
1300    let info = func[info];
1301    let Some((whole, variadic)) = site(func, inst) else { return };
1302    let old = func[data.args].to_vec();
1303    // An indirect call's first operand is the address it calls, and the arguments come after it.
1304    let skip = usize::from(data.opcode == Opcode::CallIndirect);
1305    let Some(slots) = plan(&whole, conv) else { return };
1306    let mut args = spread(&old[..skip], halves);
1307    for slot in slots.iter().copied() {
1308        let value = match slot {
1309            Slot::Whole(index) => old[skip + index],
1310            Slot::Low(index) => halves[&old[skip + index]].0,
1311            Slot::High(index) => halves[&old[skip + index]].1,
1312            Slot::Filler(ty) if ty.is_float() => {
1313                let extra = Extra::Imm(func.add_imm(Imm::from_bits(0)));
1314                written(func, inst, InstData { extra, ..InstData::new(Opcode::FConst) }, ty)
1315            }
1316            Slot::Filler(_) => ahead_const(func, inst, 0),
1317        };
1318        args.push(value);
1319    }
1320    let results: Vec<Type> = data
1321        .results()
1322        .map(|value| func[value].ty)
1323        .flat_map(|ty| if is_wide(ty) { vec![half(), half()] } else { vec![ty] })
1324        .collect();
1325    let signature = func.add_signature(planned(&Signature { variadic, ..whole }, &slots));
1326    // What the ABI asks of each argument past the `...` is on the parameter it became now, so the
1327    // call names none of them any more and the list it kept that in is empty.
1328    let varargs = if variadic { func.push_abis(&[]) } else { info.varargs };
1329    let extra = Extra::Call(func.add_call(CallInfo { signature, varargs, ..info }));
1330    let args = func.push_values(&args);
1331    let span = func.span(inst);
1332    let made = func.create_inst(InstData { args, extra, ..data }, &results, span);
1333    func.insert_before(made, inst);
1334    let mut fresh = func[made].results();
1335    for old in data.results() {
1336        if is_wide(func[old].ty) {
1337            let (Some(low), Some(high)) = (fresh.next(), fresh.next()) else { return };
1338            halves.insert(old, (low, high));
1339        } else if let Some(again) = fresh.next() {
1340            forward.insert(old, again);
1341        }
1342    }
1343    func.remove_inst(inst);
1344}
1345
1346/// Everything one call passes as the one list of parameters it is laid out as, and whether the
1347/// callee is variadic.
1348///
1349/// A variadic callee's signature names the parameters in front of the `...` and nothing else, and
1350/// what the ABI asks of each argument behind it is on the call. Here those become parameters too,
1351/// each with what the call said about it, which is the list the convention lays out anyway: on
1352/// System V an argument past the `...` goes where it would have gone had it been named, and the
1353/// one thing the callee is told is how many vector registers were used, which is a count over all
1354/// of them. The list comes back not variadic so [`plan`] lays it out as a whole, and the flag is
1355/// handed back beside it for the signature the call is made against.
1356fn site(func: &Func, inst: Inst) -> Option<(Signature, bool)> {
1357    let data = func[inst];
1358    let Extra::Call(info) = data.extra else { return None };
1359    let info = func[info];
1360    let mut whole = func[info.signature].clone();
1361    let skip = usize::from(data.opcode == Opcode::CallIndirect);
1362    let args = &func[data.args];
1363    let named = skip + whole.params.len();
1364    if args.len() < named {
1365        return None;
1366    }
1367    let beyond = &func[info.varargs];
1368    for (index, &value) in args[named..].iter().enumerate() {
1369        let abi = beyond.get(index).copied().unwrap_or_default();
1370        whole.params.push(Param { ty: func[value].ty, abi });
1371    }
1372    let variadic = whole.variadic;
1373    whole.variadic = false;
1374    Some((whole, variadic))
1375}
1376
1377/// A `return`, whose operands are the values the signature says and so are halves now.
1378fn flatten(func: &mut Func, halves: &Halves, inst: Inst) {
1379    let args = spread(&func[func[inst].args], halves);
1380    func[inst].args = func.push_values(&args);
1381}
1382
1383/// A branch, whose arguments hang on the edge rather than on the instruction.
1384fn edges(func: &mut Func, halves: &Halves, inst: Inst) {
1385    for at in func.target_list(inst).iter() {
1386        let call = func[at];
1387        let args = func[call.args].to_vec();
1388        if !args.iter().any(|value| halves.contains_key(value)) {
1389            continue;
1390        }
1391        let args = func.push_values(&spread(&args, halves));
1392        func.set_block_call(at, BlockCall { args, ..call });
1393    }
1394}
1395
1396/// A list of values with each wide one replaced by its two halves in the same position.
1397fn spread(args: &[Value], halves: &Halves) -> Vec<Value> {
1398    args.iter()
1399        .flat_map(|value| match halves.get(value) {
1400            Some(&(low, high)) => vec![low, high],
1401            None => vec![*value],
1402        })
1403        .collect()
1404}
1405
1406/// One signature with every wide parameter and return value as two halves in its place.
1407///
1408/// Each half is plain. What the ABI asks beyond a type is about the bits above a narrow value and
1409/// about an object whose address travels, and a half is neither: it is exactly a register wide and
1410/// it is the value itself.
1411fn split_signature(signature: &Signature) -> Signature {
1412    let split = |params: &[Param]| -> Vec<Param> {
1413        params
1414            .iter()
1415            .flat_map(|param| {
1416                if is_wide(param.ty) {
1417                    vec![Param::new(half()), Param::new(half())]
1418                } else {
1419                    vec![*param]
1420                }
1421            })
1422            .collect()
1423    };
1424    Signature {
1425        params: split(&signature.params),
1426        returns: split(&signature.returns),
1427        variadic: signature.variadic,
1428        convention: signature.convention,
1429    }
1430}
1431
1432/// Records the two halves an instruction became and takes the instruction out.
1433fn replace(func: &mut Func, halves: &mut Halves, inst: Inst, low: Value, high: Value) {
1434    if let Some(result) = func[inst].first_result {
1435        halves.insert(result, (low, high));
1436    }
1437    func.remove_inst(inst);
1438}
1439
1440/// Points every reader of a value this pass replaced at what replaced it.
1441///
1442/// The arguments of each instruction and the arguments of the blocks it branches to, which between
1443/// them are everywhere a value can be read. Nothing chases, because every value this map answers
1444/// with is one made here and so is never itself a key.
1445fn substitute(func: &mut Func, forward: &HashMap<Value, Value>) {
1446    if forward.is_empty() {
1447        return;
1448    }
1449    let with = |value: Value| forward.get(&value).copied().unwrap_or(value);
1450    for block in func.blocks().collect::<Vec<_>>() {
1451        for inst in func.insts(block).collect::<Vec<Inst>>() {
1452            let args = func[inst].args;
1453            func.rewrite(args, with);
1454            for call in func.successors(inst).collect::<Vec<_>>() {
1455                func.rewrite(call.args, with);
1456            }
1457        }
1458    }
1459}
1460
1461/// The access one word of a wide access is, that many bytes into it.
1462fn word(info: MemInfo, at: u64) -> MemInfo {
1463    let align = if at == 0 { info.align } else { info.align.min(8) };
1464    MemInfo { size: STEP, align, ..info }
1465}
1466
1467/// The address one word past another, written in front of an instruction.
1468fn stepped(func: &mut Func, inst: Inst, from: Value) -> Value {
1469    let step = ahead_const(func, inst, i128::from(STEP));
1470    let args = func.push_values(&[from, step]);
1471    written(func, inst, InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR)
1472}
1473
1474/// A load put in front of an instruction, and the half it reads.
1475fn read(func: &mut Func, inst: Inst, from: Value, info: MemInfo, flags: Flags) -> Value {
1476    let extra = Extra::Mem(func.add_mem(info));
1477    let args = func.push_values(&[from]);
1478    let data = InstData { args, flags, extra, ..InstData::new(Opcode::Load) };
1479    written(func, inst, data, half())
1480}
1481
1482/// A store put in front of an instruction, which produces nothing and is only its effect.
1483fn write(func: &mut Func, inst: Inst, value: Value, into: Value, info: MemInfo, flags: Flags) {
1484    let span = func.span(inst);
1485    let extra = Extra::Mem(func.add_mem(info));
1486    let args = func.push_values(&[value, into]);
1487    let data = InstData { args, flags, extra, ..InstData::new(Opcode::Store) };
1488    let made = func.create_inst(data, &[], span);
1489    func.insert_before(made, inst);
1490}
1491
1492/// A comparison written in front of an instruction, which carries its predicate where everything
1493/// else carries nothing.
1494fn compared(func: &mut Func, inst: Inst, pred: IntPred, lhs: Value, rhs: Value) -> Value {
1495    let args = func.push_values(&[lhs, rhs]);
1496    let extra = Extra::IntPred(pred);
1497    written(func, inst, InstData { args, extra, ..InstData::new(Opcode::ICmp) }, Type::I1)
1498}
1499
1500/// An `and` or an `or` over two truth values, which is the same instruction at the width of one.
1501fn bit(func: &mut Func, inst: Inst, opcode: Opcode, lhs: Value, rhs: Value) -> Value {
1502    let args = func.push_values(&[lhs, rhs]);
1503    written(func, inst, InstData { args, ..InstData::new(opcode) }, Type::I1)
1504}
1505
1506/// An instruction over these operands put in front of another one, producing a half.
1507fn ahead(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) -> Value {
1508    let args = func.push_values(args);
1509    written(func, inst, InstData { args, ..InstData::new(opcode) }, half())
1510}
1511
1512/// A constant half put in front of an instruction.
1513fn ahead_const(func: &mut Func, inst: Inst, value: i128) -> Value {
1514    let extra = Extra::Imm(func.add_imm(Imm::int(value, half())));
1515    written(func, inst, InstData { extra, ..InstData::new(Opcode::IConst) }, half())
1516}
1517
1518/// Creates the instruction, puts it in front of another, and reads its value back out.
1519fn written(func: &mut Func, inst: Inst, data: InstData, ty: Type) -> Value {
1520    let span = func.span(inst);
1521    let made = func.create_inst(data, &[ty], span);
1522    func.insert_before(made, inst);
1523    func[made].first_result.expect("an instruction created with one result has one")
1524}
1525
1526/// Turns an instruction into a different one over different operands, in place.
1527fn becomes(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) {
1528    let args = func.push_values(args);
1529    let data = &mut func[inst];
1530    data.opcode = opcode;
1531    data.args = args;
1532    data.extra = Extra::None;
1533    data.flags = data.flags.intersection(Flags::legal_on(opcode));
1534}
1535
1536#[cfg(test)]
1537mod tests {
1538    use rucc_base::Interner;
1539    use rucc_ir::{
1540        Abi, Block, Builder, Flags, Float, Func, MemOrder, Module, Restrict, Signature, Type, Value,
1541    };
1542    use rucc_target::x86_64::{MINGW64, SYSV};
1543    use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1544
1545    use super::{Def, Extra, HALF, IntPred, MemInfo, Opcode, halves};
1546
1547    /// The width the pass is about, as a type, which is what every test builds with.
1548    fn wide() -> Type {
1549        Type::int(super::WIDE)
1550    }
1551
1552    fn target() -> TargetInfo {
1553        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1554    }
1555
1556    fn printed(func: &Func, names: &mut Interner) -> String {
1557        let module = Module::new(names.intern("w.c"), &target());
1558        rucc_ir::print_func(&module, func, names)
1559    }
1560
1561    /// A function of those parameters returning that, with its entry block and its parameters.
1562    fn shell(names: &mut Interner, params: &[Type], returns: &[Type]) -> (Func, Block, Vec<Value>) {
1563        let signature = Signature::new().with_params(params).with_returns(returns);
1564        let mut func = Func::new(names.intern("f"), signature);
1565        let entry = func.create_block();
1566        let values = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
1567        (func, entry, values)
1568    }
1569
1570    /// An ordinary access of that many bytes, aligned that far.
1571    fn info(size: u64, align: u32) -> MemInfo {
1572        MemInfo {
1573            size,
1574            align,
1575            order: MemOrder::NotAtomic,
1576            tbaa: None,
1577            owns: 0,
1578            restrict: Restrict::NONE,
1579        }
1580    }
1581
1582    #[test]
1583    fn an_add_carries_from_the_low_half_into_the_high_one() {
1584        let mut names = Interner::new();
1585        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1586        let mut build = Builder::new(&mut func, entry);
1587        let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1588        build.ret(&[sum]);
1589
1590        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1591        let text = printed(&func, &mut names);
1592        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1593        // Two adds for the halves, one more for the carry, and the carry itself is the unsigned
1594        // comparison that says the low half wrapped.
1595        assert_eq!(text.matches(" = add ").count(), 3, "three adds: {text}");
1596        assert_eq!(text.matches("icmp ult").count(), 1, "one carry: {text}");
1597        assert_eq!(text.matches(" = zext.i64 ").count(), 1, "the carry as a number: {text}");
1598    }
1599
1600    #[test]
1601    fn a_subtract_borrows_the_other_way_round() {
1602        let mut names = Interner::new();
1603        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1604        let mut build = Builder::new(&mut func, entry);
1605        let difference = build.binary(Opcode::Sub, params[0], params[1], Flags::NONE);
1606        build.ret(&[difference]);
1607
1608        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1609        let text = printed(&func, &mut names);
1610        assert_eq!(text.matches(" = sub ").count(), 3, "three subtracts: {text}");
1611        // The borrow is the operands compared, not the answer, which is what tells a reader the
1612        // two directions were thought about separately.
1613        assert!(text.contains("icmp ult %0, %2"), "the operands are compared: {text}");
1614    }
1615
1616    #[test]
1617    fn the_signature_and_the_entry_block_say_the_same_thing() {
1618        let mut names = Interner::new();
1619        let (mut func, entry, params) = shell(&mut names, &[Type::int(32), wide()], &[wide()]);
1620        let mut build = Builder::new(&mut func, entry);
1621        build.ret(&[params[1]]);
1622
1623        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1624        assert_eq!(
1625            func.signature().param_types().collect::<Vec<_>>(),
1626            [Type::int(32), Type::int(HALF), Type::int(HALF)],
1627            "the wide parameter became two where it stood"
1628        );
1629        assert_eq!(
1630            func.signature().return_types().collect::<Vec<_>>(),
1631            [Type::int(HALF), Type::int(HALF)],
1632            "and so did what comes back"
1633        );
1634        let text = printed(&func, &mut names);
1635        assert!(text.contains("block0(%0: i32, %1: i64, %2: i64)"), "the block agrees: {text}");
1636        assert!(text.contains("return %1, %2"), "both halves go back: {text}");
1637        let _ = entry;
1638    }
1639
1640    #[test]
1641    fn a_read_takes_the_high_word_a_word_above_the_low_one() {
1642        let mut names = Interner::new();
1643        let (mut func, entry, params) = shell(&mut names, &[Type::PTR], &[wide()]);
1644        let mut build = Builder::new(&mut func, entry);
1645        let value = build.load(wide(), params[0], info(16, 16), Flags::NONE);
1646        build.ret(&[value]);
1647
1648        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1649        let text = printed(&func, &mut names);
1650        assert_eq!(text.matches(" = load.i64 ").count(), 2, "two reads: {text}");
1651        assert!(text.contains("ptr_add"), "the high word is a word up: {text}");
1652        // The object is aligned to sixteen and its high word is not, which is the one thing
1653        // splitting an access can get wrong quietly.
1654        assert!(text.contains("align 16"), "the low word keeps what the object had: {text}");
1655        assert!(text.contains("align 8"), "the high word knows less: {text}");
1656    }
1657
1658    #[test]
1659    fn an_equality_asks_once_about_both_halves() {
1660        let mut names = Interner::new();
1661        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1662        let mut build = Builder::new(&mut func, entry);
1663        let same = build.icmp(IntPred::Eq, params[0], params[1]);
1664        let answer = build.unary(Opcode::ZExt, same, Type::int(32));
1665        build.ret(&[answer]);
1666
1667        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1668        let text = printed(&func, &mut names);
1669        assert_eq!(text.matches("icmp").count(), 1, "one comparison: {text}");
1670        assert_eq!(text.matches(" = xor ").count(), 2, "the halves differ or they do not: {text}");
1671    }
1672
1673    #[test]
1674    fn an_ordering_reads_the_low_halves_without_a_sign() {
1675        let mut names = Interner::new();
1676        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1677        let mut build = Builder::new(&mut func, entry);
1678        let below = build.icmp(IntPred::Slt, params[0], params[1]);
1679        let answer = build.unary(Opcode::ZExt, below, Type::int(32));
1680        build.ret(&[answer]);
1681
1682        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1683        let text = printed(&func, &mut names);
1684        assert!(text.contains("icmp slt"), "the high halves keep the sign: {text}");
1685        assert!(text.contains("icmp ult"), "the low halves have none: {text}");
1686        assert!(
1687            text.contains("icmp eq"),
1688            "and the low halves only matter when the high tie: {text}"
1689        );
1690    }
1691
1692    /// An ordering that allows the two to be equal still asks the high halves a strict question.
1693    ///
1694    /// Two values whose high halves are equal are ordered by their low halves alone, and a high
1695    /// half that is greater than or equal to the other says nothing about that. Asking the high
1696    /// halves the predicate as it stands makes every ordering that is not strict answer yes on a
1697    /// tie, which is the mistake a run against GCC caught.
1698    #[test]
1699    fn an_ordering_that_allows_equality_asks_the_high_halves_a_strict_question() {
1700        let mut names = Interner::new();
1701        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1702        let mut build = Builder::new(&mut func, entry);
1703        let at_least = build.icmp(IntPred::Sge, params[0], params[1]);
1704        let answer = build.unary(Opcode::ZExt, at_least, Type::int(32));
1705        build.ret(&[answer]);
1706
1707        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1708        let text = printed(&func, &mut names);
1709        assert!(text.contains("icmp sgt"), "the high halves settle it outright: {text}");
1710        assert!(!text.contains("icmp sge"), "a tie in the high halves settles nothing: {text}");
1711        assert!(text.contains("icmp uge"), "the low halves are the ones allowed to tie: {text}");
1712    }
1713
1714    #[test]
1715    fn a_widening_puts_the_sign_of_the_value_in_the_high_half() {
1716        let mut names = Interner::new();
1717        let (mut func, entry, params) = shell(&mut names, &[Type::int(32)], &[wide()]);
1718        let mut build = Builder::new(&mut func, entry);
1719        let value = build.unary(Opcode::SExt, params[0], wide());
1720        build.ret(&[value]);
1721
1722        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1723        let text = printed(&func, &mut names);
1724        assert!(text.contains("sext.i64"), "the value fills the low half: {text}");
1725        assert!(text.contains("ashr"), "and its sign fills the high one: {text}");
1726    }
1727
1728    #[test]
1729    fn a_block_parameter_becomes_two_and_every_branch_passes_two() {
1730        let mut names = Interner::new();
1731        let (mut func, entry, params) = shell(&mut names, &[wide(), Type::int(32)], &[wide()]);
1732        let tail = func.create_block();
1733        let carried = func.append_param(tail, wide());
1734        let mut build = Builder::new(&mut func, entry);
1735        let zero = build.iconst(Type::int(32), 0);
1736        let taken = build.icmp(IntPred::Ne, params[1], zero);
1737        let other = build.iconst(wide(), 7);
1738        build.br_if(taken, tail, &[params[0]], tail, &[other]);
1739        let mut build = Builder::new(&mut func, tail);
1740        build.ret(&[carried]);
1741
1742        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1743        let text = printed(&func, &mut names);
1744        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1745        assert!(text.contains("block1(%7: i64, %8: i64)"), "the block takes two: {text}");
1746        assert_eq!(text.matches("block1(").count(), 3, "and both edges pass two: {text}");
1747    }
1748
1749    /// A multiply is the low halves, the two cross products, and nothing for the fourth corner.
1750    ///
1751    /// Three at the top, and four more inside the carry out of the low halves, which is a product
1752    /// at half the width again worked out the same way. What the count is really saying is that the
1753    /// two high halves are never multiplied together, because the whole of that partial product
1754    /// lands above the width.
1755    #[test]
1756    fn a_multiply_is_three_multiplies_and_the_carry_out_of_the_low_ones() {
1757        let mut names = Interner::new();
1758        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1759        let mut build = Builder::new(&mut func, entry);
1760        let product = build.binary(Opcode::Mul, params[0], params[1], Flags::NONE);
1761        build.ret(&[product]);
1762
1763        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1764        let text = printed(&func, &mut names);
1765        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1766        assert_eq!(text.matches(" = mul ").count(), 7, "three and the carry's four: {text}");
1767    }
1768
1769    /// Each of the four divisions becomes a call to the routine of that name in the runtime.
1770    ///
1771    /// The sign is in the name because it is in the answer. A quotient rounds towards zero and a
1772    /// remainder takes the sign of the dividend, so the signed routine and the unsigned one work out
1773    /// two different numbers, where a wide add is one computation that two signednesses read the
1774    /// same bits of.
1775    #[test]
1776    fn each_of_the_four_divisions_calls_the_routine_of_that_name() {
1777        for (opcode, routine) in [
1778            (Opcode::UDiv, "__udivti3"),
1779            (Opcode::SDiv, "__divti3"),
1780            (Opcode::URem, "__umodti3"),
1781            (Opcode::SRem, "__modti3"),
1782        ] {
1783            let mut names = Interner::new();
1784            let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1785            let mut build = Builder::new(&mut func, entry);
1786            let answer = build.binary(opcode, params[0], params[1], Flags::NONE);
1787            build.ret(&[answer]);
1788
1789            assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1790            let text = printed(&func, &mut names);
1791            assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1792            assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1793        }
1794    }
1795
1796    /// The call hands over four halves and takes two back, which is the shape of the routine.
1797    ///
1798    /// Low half first and the dividend first, which is what the definition of the routine was split
1799    /// into by the same code on the way in. The operands here are the entry block's parameters, so
1800    /// the four values the call passes are the four the block now takes, in order.
1801    #[test]
1802    fn a_divide_hands_over_four_halves_and_takes_two_back() {
1803        let mut names = Interner::new();
1804        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1805        let mut build = Builder::new(&mut func, entry);
1806        let quotient = build.binary(Opcode::UDiv, params[0], params[1], Flags::NONE);
1807        build.ret(&[quotient]);
1808
1809        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1810        let text = printed(&func, &mut names);
1811        assert!(text.contains("@__udivti3(%0, %1, %2, %3)"), "four halves go over: {text}");
1812        assert!(text.contains("return %4, %5"), "and two come back: {text}");
1813    }
1814
1815    /// A divide whose operands were worked out in the function calls with the halves of those.
1816    ///
1817    /// The other direction of the same rule the walk is for: the call is built where the divide was,
1818    /// so the halves of a sum computed above it exist by then, and what reaches the routine is the
1819    /// two values the sum became rather than anything at the old width.
1820    #[test]
1821    fn a_divide_of_something_computed_calls_with_the_halves_of_it() {
1822        let mut names = Interner::new();
1823        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1824        let mut build = Builder::new(&mut func, entry);
1825        let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1826        let quotient = build.binary(Opcode::SDiv, sum, params[1], Flags::NONE);
1827        build.ret(&[quotient]);
1828
1829        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1830        let text = printed(&func, &mut names);
1831        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1832        assert_eq!(text.matches(" = add ").count(), 3, "the sum is still a sum: {text}");
1833        assert_eq!(text.matches("call @__divti3").count(), 1, "one call: {text}");
1834    }
1835
1836    /// Each conversion between this width and a float becomes a call to the routine of that name.
1837    ///
1838    /// Twelve of them, which is a signed and an unsigned integer against a `float`, a `double` and a
1839    /// `_Float128` in each direction, and the table is here rather than in a comment because the
1840    /// names are the whole of what this has to get right.
1841    #[test]
1842    fn each_conversion_between_this_width_and_a_float_calls_the_routine_of_that_name() {
1843        let double = Type::float(Float::F64);
1844        let single = Type::float(Float::F32);
1845        let quad = Type::float(Float::F128);
1846        for (opcode, float, routine) in [
1847            (Opcode::SIToFP, double, "__floattidf"),
1848            (Opcode::SIToFP, single, "__floattisf"),
1849            (Opcode::UIToFP, double, "__floatuntidf"),
1850            (Opcode::UIToFP, single, "__floatuntisf"),
1851            (Opcode::SIToFP, quad, "__floattitf"),
1852            (Opcode::UIToFP, quad, "__floatuntitf"),
1853        ] {
1854            let mut names = Interner::new();
1855            let (mut func, entry, params) = shell(&mut names, &[wide()], &[float]);
1856            let mut build = Builder::new(&mut func, entry);
1857            let answer = build.unary(opcode, params[0], float);
1858            build.ret(&[answer]);
1859
1860            assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1861            let text = printed(&func, &mut names);
1862            assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1863            assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1864        }
1865        for (opcode, float, routine) in [
1866            (Opcode::FPToSI, double, "__fixdfti"),
1867            (Opcode::FPToSI, single, "__fixsfti"),
1868            (Opcode::FPToUI, double, "__fixunsdfti"),
1869            (Opcode::FPToUI, single, "__fixunssfti"),
1870            (Opcode::FPToSI, quad, "__fixtfti"),
1871            (Opcode::FPToUI, quad, "__fixunstfti"),
1872        ] {
1873            let mut names = Interner::new();
1874            let (mut func, entry, params) = shell(&mut names, &[float], &[wide()]);
1875            let mut build = Builder::new(&mut func, entry);
1876            let answer = build.unary(opcode, params[0], wide());
1877            build.ret(&[answer]);
1878
1879            assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1880            let text = printed(&func, &mut names);
1881            assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1882            assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1883        }
1884    }
1885
1886    /// A conversion up hands over two halves and takes one float back, and one coming down is the
1887    /// same call the other way round.
1888    ///
1889    /// The answer going up is not a wide value, so it is one result and the readers of the
1890    /// conversion read it, the way they read the answer of a comparison.
1891    #[test]
1892    fn a_conversion_hands_over_halves_one_way_and_takes_them_back_the_other() {
1893        let double = Type::float(Float::F64);
1894        let mut names = Interner::new();
1895        let (mut func, entry, params) = shell(&mut names, &[wide()], &[double]);
1896        let mut build = Builder::new(&mut func, entry);
1897        let answer = build.unary(Opcode::SIToFP, params[0], double);
1898        build.ret(&[answer]);
1899
1900        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1901        let text = printed(&func, &mut names);
1902        assert!(text.contains("@__floattidf(%0, %1)"), "two halves go over: {text}");
1903        assert!(text.contains("return %2"), "and one float comes back: {text}");
1904
1905        let mut names = Interner::new();
1906        let (mut func, entry, params) = shell(&mut names, &[double], &[wide()]);
1907        let mut build = Builder::new(&mut func, entry);
1908        let answer = build.unary(Opcode::FPToSI, params[0], wide());
1909        build.ret(&[answer]);
1910
1911        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1912        let text = printed(&func, &mut names);
1913        assert!(text.contains("@__fixdfti(%0)"), "the float goes over as it is: {text}");
1914        assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1915    }
1916
1917    /// A conversion against a quad is that same shape, with the quad crossing whole.
1918    ///
1919    /// Worth its own test because the two sides of it are wide for different reasons. The integer is
1920    /// a pair here because no register holds a hundred and twenty eight bits of integer, and the
1921    /// quad is one value because a vector register holds all of it, so the call this pass writes has
1922    /// two operands and one result going up and one operand and two results coming down.
1923    #[test]
1924    fn a_conversion_against_a_quad_hands_over_the_pair_and_the_quad_whole() {
1925        let quad = Type::float(Float::F128);
1926        let mut names = Interner::new();
1927        let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
1928        let mut build = Builder::new(&mut func, entry);
1929        let answer = build.unary(Opcode::UIToFP, params[0], quad);
1930        build.ret(&[answer]);
1931
1932        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1933        let text = printed(&func, &mut names);
1934        assert!(text.contains("@__floatuntitf(%0, %1)"), "two halves go over: {text}");
1935        assert!(text.contains("return %2"), "and one quad comes back: {text}");
1936
1937        let mut names = Interner::new();
1938        let (mut func, entry, params) = shell(&mut names, &[quad], &[wide()]);
1939        let mut build = Builder::new(&mut func, entry);
1940        let answer = build.unary(Opcode::FPToSI, params[0], wide());
1941        build.ret(&[answer]);
1942
1943        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1944        let text = printed(&func, &mut names);
1945        assert!(text.contains("@__fixtfti(%0)"), "the quad goes over as it is: {text}");
1946        assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1947    }
1948
1949    /// A conversion at a float width the runtime has no routine for leaves the function alone.
1950    ///
1951    /// `long double` is the eighty bit float on this target and the runtime has no conversion for
1952    /// it, because the back end has no register that holds one, which is tamnd/rucc#326. So the
1953    /// function keeps its wide values and is refused below by name, rather than being turned into a
1954    /// call to a routine nothing defines.
1955    #[test]
1956    fn a_conversion_at_a_width_the_runtime_has_no_routine_for_is_left_alone() {
1957        let long = Type::float(Float::F80);
1958        let mut names = Interner::new();
1959        let (mut func, entry, params) = shell(&mut names, &[wide()], &[long]);
1960        let mut build = Builder::new(&mut func, entry);
1961        let answer = build.unary(Opcode::SIToFP, params[0], long);
1962        build.ret(&[answer]);
1963
1964        assert!(!halves(&mut func, &mut names, &SYSV), "the pass does not understand this one");
1965        let text = printed(&func, &mut names);
1966        assert!(text.contains("i128"), "the width is still there: {text}");
1967    }
1968
1969    /// A shift left moves each half and chooses between the count having crossed a half and not.
1970    ///
1971    /// Two shifts left, one per half, and the low one does for both cases: a count that reached a
1972    /// whole half puts exactly that value in the high half and nothing in the low one, so the only
1973    /// thing the far case needs is the shift the near case already did. Two right shifts carry the
1974    /// crossing bits, two selects pick a half each, and there is no branch anywhere.
1975    #[test]
1976    fn a_shift_left_chooses_between_a_count_that_crossed_a_half_and_one_that_did_not() {
1977        let mut names = Interner::new();
1978        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1979        let mut build = Builder::new(&mut func, entry);
1980        let moved = build.binary(Opcode::Shl, params[0], params[1], Flags::NONE);
1981        build.ret(&[moved]);
1982
1983        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1984        let text = printed(&func, &mut names);
1985        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1986        assert_eq!(
1987            text.matches(" = shl ").count(),
1988            2,
1989            "one per half, and the far case reuses one: {text}"
1990        );
1991        assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1992        assert_eq!(text.matches(" = lshr ").count(), 2, "the crossing bits, in two steps: {text}");
1993    }
1994
1995    /// The bits that cross move one place and then the rest, so a count of zero carries nothing.
1996    ///
1997    /// Sixty four less a count of zero is sixty four, which is not a distance a sixty four bit shift
1998    /// has. One place first and sixty three less the count after is the same distance everywhere the
1999    /// question is asked, and for a count of zero it moves a value whose top bit has already gone
2000    /// all the way out, which leaves the zero a half that did not move should carry.
2001    #[test]
2002    fn the_bits_that_cross_move_one_place_and_then_the_rest_of_the_way() {
2003        let mut names = Interner::new();
2004        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
2005        let mut build = Builder::new(&mut func, entry);
2006        let moved = build.binary(Opcode::LShr, params[0], params[1], Flags::NONE);
2007        build.ret(&[moved]);
2008
2009        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2010        let text = printed(&func, &mut names);
2011        assert!(text.contains("iconst.i64 63"), "sixty three is the distance left: {text}");
2012        assert!(text.contains("iconst.i64 1"), "after the one place that comes first: {text}");
2013        assert!(text.contains(" = sub "), "the rest of the way is worked out: {text}");
2014        assert!(
2015            !text.contains("iconst.i64 127"),
2016            "and the count is not masked to the width: {text}"
2017        );
2018    }
2019
2020    /// An arithmetic shift right leaves the sign bit behind where a logical one leaves zeroes.
2021    ///
2022    /// What the two differ in is only the half the count moved out of entirely. A logical shift puts
2023    /// zeroes there, which is a constant already in hand, and an arithmetic one puts the sign bit
2024    /// spread across the half it came from, which is one more shift.
2025    #[test]
2026    fn an_arithmetic_shift_right_leaves_the_sign_bit_where_a_logical_one_leaves_zeroes() {
2027        let mut names = Interner::new();
2028        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
2029        let mut build = Builder::new(&mut func, entry);
2030        let moved = build.binary(Opcode::AShr, params[0], params[1], Flags::NONE);
2031        build.ret(&[moved]);
2032
2033        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2034        let text = printed(&func, &mut names);
2035        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
2036        // The high half by the count, and the high half by sixty three for the half left empty.
2037        assert_eq!(text.matches(" = ashr ").count(), 2, "the count and the sign: {text}");
2038        assert_eq!(text.matches(" = lshr ").count(), 1, "the low half is not signed: {text}");
2039        assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
2040    }
2041
2042    /// A wide parameter that gets no register pair, as the function's own parameter.
2043    ///
2044    /// The whole value goes in the argument area and the register it could not use stays empty,
2045    /// so the split signature has a filler where that register is and the two halves after it.
2046    /// What the function reads from the low half is the parameter the filler is followed by.
2047    fn arrived(params: &[Type], wide_at: usize) -> (Signature, usize) {
2048        let mut names = Interner::new();
2049        let word = Type::int(HALF);
2050        let (mut func, entry, values) = shell(&mut names, params, &[word]);
2051        let mut build = Builder::new(&mut func, entry);
2052        let low = build.unary(Opcode::Trunc, values[wide_at], word);
2053        build.ret(&[low]);
2054
2055        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2056        let ret = func.insts(entry).last().expect("the block ends in a return");
2057        let read = func[func[ret].args][0];
2058        let at = func[entry].params.iter().position(|&value| value == read);
2059        (func.signature().clone(), at.expect("the low half is a parameter"))
2060    }
2061
2062    fn types(signature: &Signature) -> Vec<Type> {
2063        signature.params.iter().map(|param| param.ty).collect()
2064    }
2065
2066    #[test]
2067    fn a_parameter_with_one_register_left_goes_in_memory_and_the_register_stays_empty() {
2068        let word = Type::int(HALF);
2069        let (signature, low) = arrived(&[word, word, word, word, word, wide()], 5);
2070        assert_eq!(types(&signature), vec![word; 8], "five, a filler and two halves");
2071        assert_eq!(low, 6, "the halves come after the register the value skipped");
2072    }
2073
2074    #[test]
2075    fn the_register_a_wide_parameter_skipped_goes_to_the_parameter_after_it() {
2076        let word = Type::int(HALF);
2077        let (signature, low) = arrived(&[word, word, word, word, word, wide(), word], 5);
2078        assert_eq!(types(&signature), vec![word; 8], "six registers and two words, no filler");
2079        assert_eq!(low, 6, "the word after the wide value took the sixth register");
2080    }
2081
2082    #[test]
2083    fn a_wide_parameter_in_memory_starts_on_a_sixteen_byte_boundary() {
2084        let word = Type::int(HALF);
2085        let params = [word, word, word, word, word, word, word, wide()];
2086        let (signature, low) = arrived(&params, 7);
2087        assert_eq!(types(&signature), vec![word; 10], "the seventh word, a filler, the halves");
2088        assert_eq!(low, 8, "the filler takes the word the alignment leaves empty");
2089    }
2090
2091    /// The same layout on the calling side, with a constant in the place of the filler.
2092    #[test]
2093    fn a_call_passes_a_wide_argument_in_memory_the_way_the_callee_reads_it() {
2094        let mut names = Interner::new();
2095        let word = Type::int(HALF);
2096        let (mut func, entry, _) = shell(&mut names, &[], &[]);
2097        let callee = names.intern("g");
2098        let params = [word, word, word, word, word, wide()];
2099        let signature = func.add_signature(Signature::new().with_params(&params));
2100        let mut build = Builder::new(&mut func, entry);
2101        let one = build.iconst(word, 1);
2102        let big = build.iconst(wide(), 4);
2103        build.call(callee, signature, &[one, one, one, one, one, big]);
2104        build.ret(&[]);
2105
2106        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2107        let call = func.insts(entry).find(|&inst| func[inst].opcode == Opcode::Call);
2108        let call = call.expect("the call is still there");
2109        let Extra::Call(info) = func[call].extra else { unreachable!("a call has call info") };
2110        let passed = func[func[call].args].to_vec();
2111        assert_eq!(types(&func[func[info].signature]), vec![word; 8], "as the callee has it");
2112        assert_eq!(passed.len(), 8, "one argument for each parameter");
2113        assert_eq!(passed[..5], [one; 5], "the words keep their registers");
2114        let constant = |value: Value| {
2115            let Def::Result { inst, .. } = func[value].def else { return None };
2116            let Extra::Imm(imm) = func[inst].extra else { return None };
2117            Some(func[imm].unsigned())
2118        };
2119        assert_eq!(constant(passed[6]), Some(4), "the low half is the first word in memory");
2120        assert_eq!(constant(passed[7]), Some(0), "and the high half the second");
2121    }
2122
2123    /// A variadic callee's named parameters have to stay in front of the rest, so a wide one that
2124    /// would go in memory is still refused there, by leaving the function as it was.
2125    #[test]
2126    fn a_variadic_call_with_a_wide_argument_in_memory_leaves_the_function_alone() {
2127        let mut names = Interner::new();
2128        let word = Type::int(HALF);
2129        let (mut func, entry, _) = shell(&mut names, &[], &[]);
2130        let callee = names.intern("g");
2131        let params = [word, word, word, word, word, wide()];
2132        let signature = Signature { variadic: true, ..Signature::new().with_params(&params) };
2133        let signature = func.add_signature(signature);
2134        let mut build = Builder::new(&mut func, entry);
2135        let one = build.iconst(word, 1);
2136        let big = build.iconst(wide(), 4);
2137        build.call(callee, signature, &[one, one, one, one, one, big]);
2138        build.ret(&[]);
2139        let before = printed(&func, &mut names);
2140
2141        assert!(!halves(&mut func, &mut names, &SYSV), "the named parameters cannot move");
2142        assert_eq!(printed(&func, &mut names), before, "so nothing moved");
2143    }
2144
2145    /// A wide argument past the `...` is laid out as if it had been named, which is what System V
2146    /// does with it, and the call names every argument afterwards so the list of what the ABI asks
2147    /// of the unnamed ones is empty.
2148    #[test]
2149    fn a_wide_argument_past_the_dots_goes_where_a_named_one_would() {
2150        let mut names = Interner::new();
2151        let word = Type::int(HALF);
2152        let (mut func, entry, _) = shell(&mut names, &[], &[]);
2153        let callee = names.intern("g");
2154        let params = [word, word, word, word, word];
2155        let signature = Signature { variadic: true, ..Signature::new().with_params(&params) };
2156        let signature = func.add_signature(signature);
2157        let mut build = Builder::new(&mut func, entry);
2158        let one = build.iconst(word, 1);
2159        let big = build.iconst(wide(), 4);
2160        build.call_varargs(callee, signature, &[one, one, one, one, one, big], &[Abi::Plain]);
2161        build.ret(&[]);
2162
2163        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2164        let call = func.insts(entry).find(|&inst| func[inst].opcode == Opcode::Call);
2165        let call = call.expect("the call is still there");
2166        let Extra::Call(info) = func[call].extra else { unreachable!("a call has call info") };
2167        let made = &func[func[info].signature];
2168        assert!(made.variadic, "the callee is still variadic");
2169        assert_eq!(types(made), vec![word; 8], "five words, a filler and the two halves");
2170        assert!(func[func[info].varargs].is_empty(), "every argument is named now");
2171        assert_eq!(func[func[call].args].len(), 8, "one argument for each parameter");
2172    }
2173
2174    /// The same call on a convention that counts both files as one run is left alone, because
2175    /// naming a float past the `...` there changes which registers it goes in.
2176    #[test]
2177    fn a_wide_argument_past_the_dots_on_windows_leaves_the_function_alone() {
2178        let mut names = Interner::new();
2179        let word = Type::int(HALF);
2180        let (mut func, entry, _) = shell(&mut names, &[], &[]);
2181        let callee = names.intern("g");
2182        let signature = Signature { variadic: true, ..Signature::new().with_params(&[word]) };
2183        let signature = func.add_signature(signature);
2184        let mut build = Builder::new(&mut func, entry);
2185        let one = build.iconst(word, 1);
2186        let big = build.iconst(wide(), 4);
2187        build.call_varargs(callee, signature, &[one, big], &[Abi::Plain]);
2188        build.ret(&[]);
2189
2190        assert!(!halves(&mut func, &mut names, &MINGW64), "the call is left for a refusal");
2191    }
2192
2193    /// The order the function holds its blocks in is not the order they run in.
2194    ///
2195    /// This is what the optimizer produced and `-O0` did not. A block that runs early is made late
2196    /// by whichever pass needed it, so the list the function keeps had a use of a wide value in it
2197    /// before the instruction defining that value, and the walk that asks whether every definition
2198    /// comes first said no and left the whole function alone. Then the selector met an instruction
2199    /// at a width it has no register for and refused the program. The blocks here are made in the
2200    /// order that produces, which is the tail before the middle. tamnd/rucc#1054.
2201    #[test]
2202    fn a_block_made_after_the_one_it_runs_before_is_still_split() {
2203        let mut names = Interner::new();
2204        let (mut func, entry, params) = shell(&mut names, &[wide()], &[wide()]);
2205        let tail = func.create_block();
2206        let middle = func.create_block();
2207        let mut build = Builder::new(&mut func, entry);
2208        build.jump(middle, &[]);
2209        let mut build = Builder::new(&mut func, middle);
2210        let doubled = build.binary(Opcode::Add, params[0], params[0], Flags::NONE);
2211        build.jump(tail, &[]);
2212        let mut build = Builder::new(&mut func, tail);
2213        let again = build.binary(Opcode::Add, doubled, doubled, Flags::NONE);
2214        build.ret(&[again]);
2215
2216        assert!(
2217            halves(&mut func, &mut names, &SYSV),
2218            "the definition runs before the use whatever the list says"
2219        );
2220        let text = printed(&func, &mut names);
2221        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
2222    }
2223
2224    #[test]
2225    fn a_function_with_nothing_that_wide_is_not_touched() {
2226        let mut names = Interner::new();
2227        let word = Type::int(HALF);
2228        let (mut func, entry, params) = shell(&mut names, &[word, word], &[word]);
2229        let mut build = Builder::new(&mut func, entry);
2230        let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
2231        build.ret(&[sum]);
2232
2233        assert!(!halves(&mut func, &mut names, &SYSV), "there is nothing to split");
2234    }
2235
2236    /// The operands of a runtime call go over the way the target's convention passes them.
2237    ///
2238    /// Windows x64 passes a scalar of a size no register holds as the address of a copy the caller
2239    /// made, so libgcc's `__floattitf` there reads its integer out of the address in `rdx` and
2240    /// writes its answer through the address in `rcx`. Handing it two halves in registers instead is
2241    /// not a worse encoding of the same call, it is a routine reading registers nothing was put in.
2242    #[test]
2243    fn on_windows_a_wide_operand_goes_over_as_the_address_of_a_copy() {
2244        let mut names = Interner::new();
2245        let quad = Type::float(Float::F64);
2246        let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
2247        let mut build = Builder::new(&mut func, entry);
2248        let answer = build.unary(Opcode::SIToFP, params[0], quad);
2249        build.ret(&[answer]);
2250
2251        assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
2252        let text = printed(&func, &mut names);
2253        assert!(text.contains("call @__floattidf"), "{text}");
2254        // One slot with the two halves written into it, and the address of it is the argument.
2255        assert_eq!(text.matches("alloca").count(), 1, "one slot: {text}");
2256        assert_eq!(text.matches("store").count(), 2, "a half at a time: {text}");
2257        assert_eq!(text.matches("ptr_add").count(), 1, "the high half eight bytes up: {text}");
2258        assert!(!text.contains("__floattidf(%0, %1)"), "not the two halves: {text}");
2259    }
2260
2261    /// An answer the convention brings back through an address is the leading argument.
2262    #[test]
2263    fn on_windows_a_wide_answer_at_this_format_comes_back_through_a_slot() {
2264        let mut names = Interner::new();
2265        let quad = Type::float(Float::F128);
2266        let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
2267        let mut build = Builder::new(&mut func, entry);
2268        let answer = build.unary(Opcode::SIToFP, params[0], quad);
2269        build.ret(&[answer]);
2270
2271        assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
2272        let text = printed(&func, &mut names);
2273        assert!(text.contains("call @__floattitf"), "{text}");
2274        // The slot for the answer and the slot for the operand.
2275        assert_eq!(text.matches("alloca").count(), 2, "two slots: {text}");
2276        assert_eq!(text.matches(" = call").count(), 0, "the call answers nothing: {text}");
2277        assert_eq!(text.matches(" = load").count(), 1, "the answer is the load after it: {text}");
2278    }
2279
2280    /// The convention that hands the halves over in registers is left exactly as it was.
2281    #[test]
2282    fn the_convention_with_registers_for_both_halves_puts_nothing_on_the_frame() {
2283        let mut names = Interner::new();
2284        let double = Type::float(Float::F64);
2285        let (mut func, entry, params) = shell(&mut names, &[wide()], &[double]);
2286        let mut build = Builder::new(&mut func, entry);
2287        let answer = build.unary(Opcode::SIToFP, params[0], double);
2288        build.ret(&[answer]);
2289
2290        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2291        let text = printed(&func, &mut names);
2292        assert!(text.contains("@__floattidf(%0, %1)"), "both halves in registers: {text}");
2293        assert!(!text.contains("alloca"), "nothing goes through the frame: {text}");
2294    }
2295
2296    /// An answer at this width on Windows, which comes back whole in a vector register and is taken
2297    /// apart through a slot rather than read out of two of them.
2298    #[test]
2299    fn on_windows_a_wide_answer_comes_back_in_one_register_and_is_split_on_the_frame() {
2300        let mut names = Interner::new();
2301        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
2302        let mut build = Builder::new(&mut func, entry);
2303        let answer = build.binary(Opcode::SDiv, params[0], params[1], Flags::NONE);
2304        build.ret(&[answer]);
2305
2306        assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
2307        let text = printed(&func, &mut names);
2308        // The call answers one value and it is at the quad format, which is the name this compiler
2309        // has for sixteen bytes in a vector register.
2310        assert!(text.contains("call @__divti3(%4, %7) : (ptr, ptr) -> f128"), "{text}");
2311        assert_eq!(text.matches(" = call").count(), 1, "one answer: {text}");
2312        // Two slots for the two operands and one more to take the answer apart in.
2313        assert_eq!(text.matches("alloca").count(), 3, "three slots: {text}");
2314        assert_eq!(text.matches(" = load").count(), 2, "the two halves: {text}");
2315    }
2316
2317    /// The same division where the convention has a pair of registers for the answer.
2318    #[test]
2319    fn the_convention_with_registers_for_the_answer_reads_both_halves_out_of_them() {
2320        let mut names = Interner::new();
2321        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
2322        let mut build = Builder::new(&mut func, entry);
2323        let answer = build.binary(Opcode::SDiv, params[0], params[1], Flags::NONE);
2324        build.ret(&[answer]);
2325
2326        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2327        let text = printed(&func, &mut names);
2328        assert!(text.contains("@__divti3(%0, %1, %2, %3)"), "four halves over: {text}");
2329        assert!(!text.contains("alloca"), "nothing goes through the frame: {text}");
2330    }
2331}