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 two independent words travel as two words each aligned to eight, which
35//! is a different place as soon as an odd number of words went before them. So a function whose
36//! wide parameter would run out of registers is left exactly as it was and refused by name, the
37//! same as a function this pass does not understand. `tamnd/rucc#351` carries what passing one in
38//! memory would take, which is a form of parameter the IR has no way to spell today.
39//!
40//! # Dividing and converting are calls into the runtime
41//!
42//! Every other operation at this width is the same operation over the halves with whatever crossed
43//! between them put back. A quotient is not. The halves of a quotient are not a function of the
44//! halves of its operands taken apart, at this width or at any other, which is why every compiler's
45//! runtime has a division routine in it and none of them has an addition one. So a divide and a
46//! remainder become a call to the routines `runtime/builtins/div.c` defines, which are libgcc's four
47//! names and libgcc's signatures, and `spec/12-abi-and-runtime.md` section 12.8 is what they are.
48//!
49//! A conversion to or from a floating point value is the other one, for a plainer reason: the
50//! machine's own conversion reaches sixty four bits and no further, so there is no instruction to
51//! split into. Those are the eight names `runtime/builtins/convert.c` defines, one for each of a
52//! signed and an unsigned integer against a `float` and a `double` in each direction, and the four
53//! `runtime/builtins/quad.c` defines for a `_Float128`, which has no instruction of its own at any
54//! width and so is a call here for both reasons at once. An eighty bit float is not among them,
55//! because this machine has no register that holds one and the back end says so, which is
56//! tamnd/rucc#326, so a function converting at that width is left alone here and refused below the
57//! way every function of this width used to be.
58//!
59//! The call is built with the halves already in it, four parameters of sixty four bits for the two
60//! operands of a divide and two results for the answer, or two parameters and a float, or a float
61//! and two results, which is the shape this pass gives a call it found in the program anyway. Both
62//! ends agree because the convention puts a `__int128` argument in two registers in a row and hands
63//! out argument registers in order, which is the same sentence the section below about crossing the
64//! boundary is.
65
66use std::collections::{HashMap, HashSet};
67
68use rucc_base::Interner;
69use rucc_ir::{
70    Abi, Block, BlockCall, CallInfo, Def, Extra, Flags, Float, Func, Imm, Inst, InstData, IntPred,
71    MemInfo, Opcode, Param, Signature, Type, Value,
72};
73use rucc_target::{CallRegs, Places, Where};
74
75use crate::expand;
76
77/// The width this pass is about, which is the one width a C program writes that no register holds.
78const WIDE: u32 = 128;
79
80/// The width each half is, which is a register on every target this pass runs for.
81const HALF: u32 = 64;
82
83/// How many bytes one half takes in memory, which is how far the high one sits above the low one.
84const STEP: u64 = 8;
85
86/// Whether a type is the width this pass splits.
87fn is_wide(ty: Type) -> bool {
88    ty.is_int() && ty.is_scalar() && ty.bits() == WIDE
89}
90
91/// The type each half has.
92fn half() -> Type {
93    Type::int(HALF)
94}
95
96/// Splits every integer the machine holds in two registers into the two halves it holds it in.
97///
98/// Gives back whether it changed anything, which is what a test asks and what tells a reader of a
99/// dump that the function the selector saw is not the one the middle end produced.
100///
101/// The function is left exactly as it was when there is nothing at that width, when something at
102/// that width is reached by an instruction this does not understand, and when a half would cross
103/// the function's boundary somewhere the convention has no register for it. All three leave the
104/// refusal to the passes below, which name the construct they could not lower, rather than
105/// rewriting into something that guessed.
106pub fn halves(func: &mut Func, names: &mut Interner, conv: &CallRegs) -> bool {
107    if !func.values().any(|value| is_wide(func[value].ty)) {
108        return false;
109    }
110    let insts: Vec<Inst> =
111        walk(func).into_iter().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
112    let order: HashMap<Inst, usize> =
113        insts.iter().enumerate().map(|(at, &inst)| (inst, at)).collect();
114    if !insts.iter().enumerate().all(|(at, &inst)| can_split(func, &order, at, inst)) {
115        return false;
116    }
117    if !func.signatures().all(|signature| fits(signature, conv)) {
118        return false;
119    }
120
121    let mut halves: Halves = HashMap::new();
122    let mut forward: HashMap<Value, Value> = HashMap::new();
123    for block in func.blocks().collect::<Vec<_>>() {
124        params(func, block, &mut halves, &mut forward);
125    }
126    for &inst in &insts {
127        rewrite(func, names, &mut halves, &mut forward, inst);
128    }
129    substitute(func, &forward);
130    let signature = split_signature(func.signature());
131    func.set_signature(signature);
132    true
133}
134
135/// Every block, in an order where a block comes after everything that dominates it.
136///
137/// Reverse postorder from the entry, then whatever the walk did not reach, in the order the
138/// function holds them. The order is what the rule below about a use and its definition is read
139/// against, and the two together are the whole of why this is not simply the order the function
140/// holds the blocks in: a value is defined in a block that dominates every block reading it, a
141/// dominator is on every path from the entry, so a depth first walk finishes it last and reverse
142/// postorder puts it first. The order the function holds blocks in says nothing of the kind. It is
143/// the order they were made in, and every pass in the optimizer that makes a block, which is every
144/// pass that gives a loop a preheader or copies a header in front of one, puts a block that runs
145/// early at the end of that list. So the same program compiled at `-O0` and at `-O1` gave two
146/// different answers to whether this pass understood it, and above `-O0` the answer was often no.
147/// tamnd/rucc#1054.
148///
149/// A block nothing reaches cannot be walked to and is put at the end rather than dropped, because
150/// deciding a block is unreachable is not this pass's business. Two of them in the wrong order
151/// refuse the function the way they always did.
152fn walk(func: &Func) -> Vec<Block> {
153    let Some(entry) = func.entry() else { return func.blocks().collect() };
154    let mut seen: HashSet<Block> = HashSet::new();
155    let mut order: Vec<Block> = Vec::new();
156    // A postorder without recursion: the second time a block comes off the stack every block below
157    // it has been finished, so that is where it belongs in the postorder.
158    let mut stack: Vec<(Block, bool)> = vec![(entry, false)];
159    seen.insert(entry);
160    while let Some((block, done)) = stack.pop() {
161        if done {
162            order.push(block);
163            continue;
164        }
165        stack.push((block, true));
166        let Some(term) = func.terminator(block) else { continue };
167        for call in func.successors(term) {
168            if seen.insert(call.block) {
169                stack.push((call.block, false));
170            }
171        }
172    }
173    order.reverse();
174    order.extend(func.blocks().filter(|block| !seen.contains(block)));
175    order
176}
177
178/// The two halves each wide value became, low first.
179type Halves = HashMap<Value, (Value, Value)>;
180
181/// The opcodes this pass knows how to split.
182///
183/// An instruction that touches a value of this width and is not one of these is why the whole
184/// function is left alone, so this list is the pass's own statement of what it has thought about.
185/// Adding to it is adding an arm to [`rewrite`] as well.
186///
187/// The four divisions and the four conversions to and from a floating point value are here, and they
188/// are the entries that become a call rather than arithmetic over the halves. Which float formats
189/// those conversions are understood at is a separate question, asked in [`can_split`], because it is
190/// about the type rather than about the opcode.
191fn understood(opcode: Opcode) -> bool {
192    matches!(
193        opcode,
194        Opcode::IConst
195            | Opcode::Load
196            | Opcode::Store
197            | Opcode::Add
198            | Opcode::Sub
199            | Opcode::Mul
200            | Opcode::UDiv
201            | Opcode::SDiv
202            | Opcode::URem
203            | Opcode::SRem
204            | Opcode::Shl
205            | Opcode::LShr
206            | Opcode::AShr
207            | Opcode::And
208            | Opcode::Or
209            | Opcode::Xor
210            | Opcode::ICmp
211            | Opcode::Select
212            | Opcode::SIToFP
213            | Opcode::UIToFP
214            | Opcode::FPToSI
215            | Opcode::FPToUI
216            | Opcode::Trunc
217            | Opcode::SExt
218            | Opcode::ZExt
219            | Opcode::Call
220            | Opcode::CallIndirect
221            | Opcode::Return
222            | Opcode::Jump
223            | Opcode::BrIf
224    )
225}
226
227/// Whether one instruction is one this pass can split, given where it is in the walk.
228///
229/// Asked of every instruction, and answered yes at once for the ones that never see a value this
230/// wide, which in a function that has one at all is still most of them.
231fn can_split(func: &Func, order: &HashMap<Inst, usize>, at: usize, inst: Inst) -> bool {
232    let data = func[inst];
233    let reads = operands(func, inst);
234    let wide = |&value: &Value| is_wide(func[value].ty);
235    if !reads.iter().any(wide) && !data.results().any(|value| is_wide(func[value].ty)) {
236        return true;
237    }
238    if !understood(data.opcode) {
239        return false;
240    }
241    // Memory SSA threads a version of memory through each access, and splitting one access into two
242    // makes a version this pass would have to name. Nothing hands this crate a function carrying it
243    // today, and leaving one alone costs less than being wrong about it later.
244    if func.carries_mem(inst) {
245        return false;
246    }
247    // The machine sign extends from a byte and no narrower, so a truth value widened into the high
248    // half would become an instruction with no rule behind it. Zero extending one is fine, which is
249    // why only the signed side is asked about.
250    if data.opcode == Opcode::SExt && reads.iter().any(|&value| func[value].ty.bits() < 8) {
251        return false;
252    }
253    // The runtime has a conversion for a `float`, for a `double` and for a `_Float128`, and for
254    // nothing else, so every other format is refused here rather than turned into a call to a name
255    // nothing defines. An eighty bit float is the one a program reaches without asking for it, since
256    // `long double` is that type on this target, and it is tamnd/rucc#326 rather than an oversight.
257    if matches!(data.opcode, Opcode::SIToFP | Opcode::UIToFP | Opcode::FPToSI | Opcode::FPToUI)
258        && converted(func, inst).is_none()
259    {
260        return false;
261    }
262    // Splitting an argument makes two of them, and which parameter an argument stands for is how a
263    // variadic call knows what the ABI asks of the ones its signature does not name. Two values
264    // where that list has one entry is a call laid out against the wrong list.
265    if matches!(data.opcode, Opcode::Call | Opcode::CallIndirect) {
266        let Extra::Call(info) = data.extra else { return false };
267        if func[func[info].signature].variadic {
268            return false;
269        }
270    }
271    // The halves of a value are written where the value was, so a use this pass reaches before the
272    // definition is a use whose halves do not exist yet. A value arriving as a block parameter is
273    // always ready, since every block's parameters are split before any instruction is.
274    reads.iter().filter(|value| wide(value)).all(|&value| match func[value].def {
275        Def::Result { inst, .. } => order.get(&inst).is_some_and(|&def| def < at),
276        Def::Param { .. } => true,
277    })
278}
279
280/// The format of the floating point side of a conversion, when the runtime has a routine for it.
281///
282/// One float type is in such an instruction, the result of a conversion going up and the operand of
283/// one coming down, so both ends are looked at and the one is found. `None` means the function is
284/// left alone, and it covers a format with no routine, no float at all, and a float on both ends,
285/// which are three shapes that have nothing to be turned into rather than one.
286fn converted(func: &Func, inst: Inst) -> Option<Float> {
287    let data = func[inst];
288    let mut floats = func[data.args]
289        .iter()
290        .copied()
291        .chain(data.results())
292        .map(|value| func[value].ty)
293        .filter(|ty| ty.is_float());
294    let only = floats.next()?;
295    if floats.next().is_some() {
296        return None;
297    }
298    match only.format() {
299        Some(format @ (Float::F32 | Float::F64 | Float::F128)) => Some(format),
300        _ => None,
301    }
302}
303
304/// Everything an instruction reads: its own operands, and the arguments it passes along its edges.
305///
306/// The arguments of a `jump` and of a `br_if` hang on the block call rather than on the
307/// instruction, so an instruction whose own operands are all narrow may still be handing a wide one
308/// to the block it branches to.
309fn operands(func: &Func, inst: Inst) -> Vec<Value> {
310    let mut reads = func[func[inst].args].to_vec();
311    for call in func.successors(inst).collect::<Vec<_>>() {
312        reads.extend_from_slice(&func[call.args]);
313    }
314    reads
315}
316
317/// Whether both halves of every wide parameter of one signature land in registers.
318///
319/// The walk is the one [`crate::abi::entry`] makes, because the answer has to be the one that walk
320/// will give: it hands out places in the order the signature holds the parameters, and a wide
321/// parameter is about to become two halves in a row in that order. Both have to be registers. One
322/// register and one word of the argument area is where two independent words go and is not where
323/// the convention puts a sixteen byte value.
324///
325/// A return value is not asked about. What comes back comes back in the registers a return uses,
326/// which is a sequence of its own with two in it on this convention, and a signature wanting more
327/// than it has is refused by name in [`crate::lower`] already.
328fn fits(signature: &Signature, conv: &CallRegs) -> bool {
329    let mut places = Places::new(conv);
330    for param in &signature.params {
331        // A structure the classification put in the argument area, which is the one parameter whose
332        // place is bytes rather than a register. Everything else is a value, the pointer an `sret`
333        // hands over included, and a value takes the next register of its own kind.
334        if let Abi::ByVal { size, align } = param.abi {
335            places.on_stack(u32::try_from(size).unwrap_or(u32::MAX), align);
336        } else if crate::abi::on_the_stack(param.ty) {
337            let (size, align) = crate::abi::X87_AREA;
338            places.on_stack(size, align);
339        } else if is_wide(param.ty) {
340            let low = places.integer();
341            let high = places.integer();
342            if !matches!((low, high), (Where::Reg(_), Where::Reg(_))) {
343                return false;
344            }
345        } else if param.ty.is_float() {
346            places.float(crate::abi::float_bytes(param.ty));
347        } else {
348            places.integer();
349        }
350    }
351    true
352}
353
354/// One block's parameters, with each wide one replaced by its two halves in the same position.
355///
356/// Every parameter of such a block is made again rather than only the wide ones, because a
357/// parameter's position is its identity to the branches that feed it and appending is the only way
358/// to add one. The narrow ones are made again as themselves and pointed at the copy, which costs
359/// nothing once the substitution below has run.
360fn params(func: &mut Func, block: Block, halves: &mut Halves, forward: &mut HashMap<Value, Value>) {
361    let old: Vec<Value> = func[block].params.clone();
362    if !old.iter().any(|&value| is_wide(func[value].ty)) {
363        return;
364    }
365    for &value in &old {
366        if is_wide(func[value].ty) {
367            let low = func.append_param(block, half());
368            let high = func.append_param(block, half());
369            halves.insert(value, (low, high));
370        } else {
371            let again = func.append_param(block, func[value].ty);
372            forward.insert(value, again);
373        }
374    }
375    func.retain_params(block, |value| !old.contains(&value));
376}
377
378/// One instruction, as instructions over halves.
379fn rewrite(
380    func: &mut Func,
381    names: &mut Interner,
382    halves: &mut Halves,
383    forward: &mut HashMap<Value, Value>,
384    inst: Inst,
385) {
386    let data = func[inst];
387    let produces = data.results().any(|value| is_wide(func[value].ty));
388    let takes = func[data.args].iter().any(|&value| is_wide(func[value].ty));
389    match data.opcode {
390        Opcode::IConst if produces => constant(func, halves, inst),
391        Opcode::Load if produces => load(func, halves, inst),
392        Opcode::Store if takes => store(func, halves, inst),
393        Opcode::Add | Opcode::Sub if produces => carried(func, halves, inst, data.opcode),
394        Opcode::Mul if produces => multiply(func, halves, inst),
395        Opcode::UDiv | Opcode::SDiv | Opcode::URem | Opcode::SRem if produces => {
396            divide(func, names, halves, inst, data.opcode);
397        }
398        Opcode::Shl | Opcode::LShr | Opcode::AShr if produces => {
399            shifted(func, halves, inst, data.opcode);
400        }
401        Opcode::And | Opcode::Or | Opcode::Xor if produces => {
402            bitwise(func, halves, inst, data.opcode);
403        }
404        Opcode::SIToFP | Opcode::UIToFP if takes => {
405            to_float(func, names, halves, forward, inst, data.opcode == Opcode::SIToFP);
406        }
407        Opcode::FPToSI | Opcode::FPToUI if produces => {
408            from_float(func, names, halves, inst, data.opcode == Opcode::FPToSI);
409        }
410        Opcode::ICmp if takes => compare(func, halves, forward, inst),
411        Opcode::Select if produces => choose(func, halves, inst),
412        Opcode::Trunc if takes => truncate(func, halves, forward, inst),
413        Opcode::SExt | Opcode::ZExt if produces => {
414            extend(func, halves, inst, data.opcode == Opcode::SExt);
415        }
416        Opcode::Call | Opcode::CallIndirect if produces || takes => {
417            call(func, halves, forward, inst);
418        }
419        Opcode::Return if takes => flatten(func, halves, inst),
420        Opcode::Jump | Opcode::BrIf => edges(func, halves, inst),
421        _ => {}
422    }
423}
424
425/// A constant, as the two halves of its bits with the low one first.
426fn constant(func: &mut Func, halves: &mut Halves, inst: Inst) {
427    let Extra::Imm(imm) = func[inst].extra else { return };
428    let bits = func[imm].unsigned();
429    #[expect(clippy::cast_possible_truncation, reason = "the halves are what this is taking")]
430    let (low, high) = (bits as u64, (bits >> HALF) as u64);
431    let low = ahead_const(func, inst, i128::from(low));
432    let high = ahead_const(func, inst, i128::from(high));
433    replace(func, halves, inst, low, high);
434}
435
436/// A read, as the two words of it with the low one first.
437///
438/// Little endian is the order, which is what every target this back end has is. The high word knows
439/// less about its alignment than the low one when the low one knew more than a word, since a
440/// sixteen byte object aligned to sixteen has its high word aligned to eight.
441fn load(func: &mut Func, halves: &mut Halves, inst: Inst) {
442    let data = func[inst];
443    let Extra::Mem(mem) = data.extra else { return };
444    let info = func[mem];
445    let Some(&from) = func[data.args].first() else { return };
446    let low = read(func, inst, from, word(info, 0), data.flags);
447    let up = stepped(func, inst, from);
448    let high = read(func, inst, up, word(info, STEP), data.flags);
449    replace(func, halves, inst, low, high);
450}
451
452/// A write, as the two words of it.
453fn store(func: &mut Func, halves: &mut Halves, inst: Inst) {
454    let data = func[inst];
455    let Extra::Mem(mem) = data.extra else { return };
456    let info = func[mem];
457    let args = func[data.args].to_vec();
458    let [value, into] = args[..] else { return };
459    let Some(&(low, high)) = halves.get(&value) else { return };
460    write(func, inst, low, into, word(info, 0), data.flags);
461    let up = stepped(func, inst, into);
462    write(func, inst, high, up, word(info, STEP), data.flags);
463    func.remove_inst(inst);
464}
465
466/// An add or a subtract, as the same over the low halves and the same again over the high ones with
467/// what the low halves carried between them.
468///
469/// The carry is a comparison and not a flag. An unsigned sum comes out below either operand exactly
470/// when it wrapped, and an unsigned difference wrapped exactly when the left operand was below the
471/// right, which are the two tests [`crate::expand`] writes for the overflow builtins and are what
472/// the machine's own carry flag stands for. Whether the pair is put back together into an `adc` and
473/// an `sbb` is a question for what reads flags rather than for this, and the answer here is correct
474/// either way.
475fn carried(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
476    let args = func[func[inst].args].to_vec();
477    let [a, b] = args[..] else { return };
478    let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
479        return;
480    };
481    let low = ahead(func, inst, opcode, &[a_low, b_low]);
482    let carried = if opcode == Opcode::Add {
483        compared(func, inst, IntPred::Ult, low, a_low)
484    } else {
485        compared(func, inst, IntPred::Ult, a_low, b_low)
486    };
487    let carry = ahead(func, inst, Opcode::ZExt, &[carried]);
488    let high = ahead(func, inst, opcode, &[a_high, b_high]);
489    let high = ahead(func, inst, opcode, &[high, carry]);
490    replace(func, halves, inst, low, high);
491}
492
493/// A multiply, which is long multiplication in base two to the sixty fourth with everything that
494/// lands above the width thrown away.
495///
496/// The low half of the answer is the low halves multiplied together. The high half is what that
497/// multiply carried out of its own top, plus the two cross products, each of which starts at bit
498/// sixty four. The fourth partial product is the two high halves against each other and it starts
499/// at bit one hundred and twenty eight, so the whole of it is above the width and it is never
500/// worked out, which is why a wide multiply is three multiplies and not four.
501///
502/// Nothing here asks whether the operands are signed, because the low hundred and twenty eight bits
503/// of a product are the same bits either way. The sign only matters to the bits that are being
504/// thrown away.
505///
506/// The carry out of the low halves is the high half of a sixty four bit product, which this machine
507/// has an instruction for and this compiler has no way to ask for. [`crate::expand`] already writes
508/// that out as long multiplication one level further down, for the overflow builtins, so this calls
509/// it rather than keeping a second copy of the same arithmetic. It is the expensive part of a wide
510/// multiply by a long way, and `tamnd/rucc#309` is the rule that would make it one instruction for
511/// both callers at once.
512fn multiply(func: &mut Func, halves: &mut Halves, inst: Inst) {
513    let args = func[func[inst].args].to_vec();
514    let [a, b] = args[..] else { return };
515    let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
516        return;
517    };
518    let low = ahead(func, inst, Opcode::Mul, &[a_low, b_low]);
519    let carried = expand::high_half(func, inst, a_low, b_low, false, half());
520    let cross = ahead(func, inst, Opcode::Mul, &[a_low, b_high]);
521    let other = ahead(func, inst, Opcode::Mul, &[a_high, b_low]);
522    let high = ahead(func, inst, Opcode::Add, &[carried, cross]);
523    let high = ahead(func, inst, Opcode::Add, &[high, other]);
524    replace(func, halves, inst, low, high);
525}
526
527/// A divide or a remainder, as a call to the routine in the compiler runtime that works it out.
528///
529/// The four names are libgcc's, and the archive `runtime/builtins/div.c` builds into defines them for
530/// a target that has no libgcc, which is every target this compiler links without gcc's driver. What
531/// picks one of the four is the opcode and nothing else: the sign is in the name because it is in the
532/// answer, since a quotient rounds towards zero and a remainder takes the sign of the dividend, and
533/// neither is the unsigned answer with bits reinterpreted the way a sum is.
534///
535/// The call is created with the halves in it rather than with the wide values, which would then be
536/// split by [`call`] on the next instruction of the walk. Four parameters and two results, in the
537/// order the operands were in and low half first, because that is where the convention puts the two
538/// eightbytes of a value this wide and [`split_signature`] is what the routine's own definition went
539/// through on the way in.
540///
541/// Nothing here is conditional on the divisor. Dividing by zero is undefined in C, the machine traps
542/// on it at every width it has, and a test written in front of the call would be this pass deciding
543/// what an undefined program does.
544fn divide(func: &mut Func, names: &mut Interner, halves: &mut Halves, inst: Inst, opcode: Opcode) {
545    let args = func[func[inst].args].to_vec();
546    let [a, b] = args[..] else { return };
547    let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
548        return;
549    };
550    let routine = match opcode {
551        Opcode::UDiv => "__udivti3",
552        Opcode::SDiv => "__divti3",
553        Opcode::URem => "__umodti3",
554        _ => "__modti3",
555    };
556    let made =
557        runtime(func, names, inst, routine, &[a_low, a_high, b_low, b_high], &[half(), half()]);
558    let mut results = func[made].results();
559    let (Some(low), Some(high)) = (results.next(), results.next()) else { return };
560    replace(func, halves, inst, low, high);
561}
562
563/// A conversion from one of these to a float, as a call to the routine that works it out.
564///
565/// Two parameters of sixty four bits and one float result. The answer is not a wide value, so the
566/// instruction's own result is pointed at the call's rather than halved, which is what [`compare`]
567/// and [`truncate`] do with a narrow answer as well.
568///
569/// The sign is in the name because it is in the answer: the same hundred and twenty eight bits are
570/// two different numbers depending on it, and unlike a sum the float they become is two different
571/// floats.
572fn to_float(
573    func: &mut Func,
574    names: &mut Interner,
575    halves: &Halves,
576    forward: &mut HashMap<Value, Value>,
577    inst: Inst,
578    signed: bool,
579) {
580    let Some(&arg) = func[func[inst].args].first() else { return };
581    let Some(&(low, high)) = halves.get(&arg) else { return };
582    let (Some(result), Some(format)) = (func[inst].first_result, converted(func, inst)) else {
583        return;
584    };
585    let routine = going_up(signed, format);
586    let made = runtime(func, names, inst, routine, &[low, high], &[func[result].ty]);
587    if let Some(answer) = func[made].first_result {
588        forward.insert(result, answer);
589    }
590    func.remove_inst(inst);
591}
592
593/// A conversion from a float to one of these, as a call to the routine that works it out.
594///
595/// One float parameter and two results of sixty four bits, which is the divide's shape with the
596/// operands and the answer the other way round. The operand is a float and so was never split, and
597/// it is passed along as it is.
598///
599/// A value the integer cannot hold, an infinity and a not a number are all undefined in C, and
600/// nothing is written in front of the call about any of them, for the reason [`divide`] writes
601/// nothing in front of itself about a zero divisor.
602fn from_float(
603    func: &mut Func,
604    names: &mut Interner,
605    halves: &mut Halves,
606    inst: Inst,
607    signed: bool,
608) {
609    let Some(&arg) = func[func[inst].args].first() else { return };
610    let Some(format) = converted(func, inst) else { return };
611    let routine = coming_down(signed, format);
612    let made = runtime(func, names, inst, routine, &[arg], &[half(), half()]);
613    let mut results = func[made].results();
614    let (Some(low), Some(high)) = (results.next(), results.next()) else { return };
615    replace(func, halves, inst, low, high);
616}
617
618/// The routine that turns an integer this wide into a float of that format.
619///
620/// Three formats, since [`converted`] answers with no others, and the quad is the last arm rather
621/// than a named one so that a format added to that list arrives here as a routine that does not
622/// exist rather than as a name that is wrong.
623fn going_up(signed: bool, format: Float) -> &'static str {
624    match (signed, format) {
625        (true, Float::F32) => "__floattisf",
626        (true, Float::F64) => "__floattidf",
627        (true, _) => "__floattitf",
628        (false, Float::F32) => "__floatuntisf",
629        (false, Float::F64) => "__floatuntidf",
630        (false, _) => "__floatuntitf",
631    }
632}
633
634/// The routine that turns a float of that format into an integer this wide.
635fn coming_down(signed: bool, format: Float) -> &'static str {
636    match (signed, format) {
637        (true, Float::F32) => "__fixsfti",
638        (true, Float::F64) => "__fixdfti",
639        (true, _) => "__fixtfti",
640        (false, Float::F32) => "__fixunssfti",
641        (false, Float::F64) => "__fixunsdfti",
642        (false, _) => "__fixunstfti",
643    }
644}
645
646/// A call to a routine in the compiler runtime, written in front of an instruction.
647///
648/// The signature is made out of the types of the values being handed over, because the values are
649/// already the halves at this point and the routine's own definition went through
650/// [`split_signature`] on the way in, so the two descriptions are the same one arrived at from the
651/// two ends.
652fn runtime(
653    func: &mut Func,
654    names: &mut Interner,
655    inst: Inst,
656    routine: &str,
657    args: &[Value],
658    results: &[Type],
659) -> Inst {
660    let params: Vec<Type> = args.iter().map(|&value| func[value].ty).collect();
661    let signature = func.add_signature(Signature::new().with_params(&params).with_returns(results));
662    let callee = Some(names.intern(routine));
663    let varargs = func.push_abis(&[]);
664    let extra = Extra::Call(func.add_call(CallInfo { callee, signature, varargs }));
665    let args = func.push_values(args);
666    let span = func.span(inst);
667    let data = InstData { args, extra, ..InstData::new(Opcode::Call) };
668    let made = func.create_inst(data, results, span);
669    func.insert_before(made, inst);
670    made
671}
672
673/// A shift, as each half shifted by the count with the bits that crossed between them put back, and
674/// a second answer for a count that reached a whole half.
675///
676/// A count below sixty four moves each half by the count, and the bits that left one half are the
677/// ones that arrive in the other. A count of sixty four or more empties one half completely, and
678/// what lands in the other is the first half moved by the count less sixty four. Taking the sixty
679/// four bit off a count in range is the same as subtracting sixty four from it, so both cases shift
680/// by the same number of places and differ only in which value ends up where, which means one shift
681/// each and a choice rather than two of everything. The choice is a `select` and not a branch, for
682/// the reason [`choose`] gives.
683///
684/// The bits that cross move the other way by sixty four less the count. That is a shift of sixty
685/// four places when the count is zero, which is not a distance this width has. Moving one place and
686/// then sixty three less the count is the same distance for every count from one to sixty three,
687/// and for a count of zero it shifts a value whose top bit is already gone all the way down to
688/// nothing, which is the right answer: a half that did not move carries nothing into the other one.
689///
690/// A count of a hundred and twenty eight or more is undefined in C and nothing here goes out of its
691/// way about it, the same as at every other width.
692fn shifted(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
693    let args = func[func[inst].args].to_vec();
694    let [a, b] = args[..] else { return };
695    let (Some(&(a_low, a_high)), Some(&(count, _))) = (halves.get(&a), halves.get(&b)) else {
696        return;
697    };
698    let top = ahead_const(func, inst, i128::from(HALF - 1));
699    let places = ahead(func, inst, Opcode::And, &[count, top]);
700    let back = ahead(func, inst, Opcode::Sub, &[top, places]);
701    let one = ahead_const(func, inst, 1);
702    let zero = ahead_const(func, inst, 0);
703    let bit = ahead_const(func, inst, i128::from(HALF));
704    let reach = ahead(func, inst, Opcode::And, &[count, bit]);
705    let whole = compared(func, inst, IntPred::Ne, reach, zero);
706
707    let (low, high) = if opcode == Opcode::Shl {
708        let moved = ahead(func, inst, Opcode::Shl, &[a_low, places]);
709        let edge = ahead(func, inst, Opcode::LShr, &[a_low, one]);
710        let across = ahead(func, inst, Opcode::LShr, &[edge, back]);
711        let above = ahead(func, inst, Opcode::Shl, &[a_high, places]);
712        let joined = ahead(func, inst, Opcode::Or, &[above, across]);
713        let low = ahead(func, inst, Opcode::Select, &[whole, zero, moved]);
714        let high = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
715        (low, high)
716    } else {
717        let moved = ahead(func, inst, opcode, &[a_high, places]);
718        let edge = ahead(func, inst, Opcode::Shl, &[a_high, one]);
719        let across = ahead(func, inst, Opcode::Shl, &[edge, back]);
720        let below = ahead(func, inst, Opcode::LShr, &[a_low, places]);
721        let joined = ahead(func, inst, Opcode::Or, &[below, across]);
722        // What is left behind when the whole low half is gone: zeroes for a logical shift, and for
723        // an arithmetic one the sign bit spread over the half it came from.
724        let spent = if opcode == Opcode::AShr {
725            ahead(func, inst, Opcode::AShr, &[a_high, top])
726        } else {
727            zero
728        };
729        let low = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
730        let high = ahead(func, inst, Opcode::Select, &[whole, spent, moved]);
731        (low, high)
732    };
733    replace(func, halves, inst, low, high);
734}
735
736/// An `and`, an `or` or an `xor`, which is the same operation on each half and nothing between
737/// them.
738fn bitwise(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
739    let args = func[func[inst].args].to_vec();
740    let [a, b] = args[..] else { return };
741    let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
742        return;
743    };
744    let low = ahead(func, inst, opcode, &[a_low, b_low]);
745    let high = ahead(func, inst, opcode, &[a_high, b_high]);
746    replace(func, halves, inst, low, high);
747}
748
749/// A comparison, which produces one bit and so is pointed at its answer rather than halved.
750///
751/// An equality is the two halves differing in neither place, which is one `or` over two `xor`s
752/// against zero and is shorter than comparing twice and combining. An ordering is the high halves
753/// settling it outright, or the low halves settling it when the high halves are equal, and the low
754/// halves are compared without a sign because the low half of a signed number is unsigned whatever
755/// the number is.
756///
757/// The high halves are asked a strict question even when the predicate is not strict. A predicate
758/// that lets the two be equal is true of two equal high halves whatever the low halves say, and
759/// what decides it there is the low halves, so `a >= b` is `a.hi > b.hi` or the high halves being
760/// equal and `a.lo >= b.lo` unsigned. Asking `a.hi >= b.hi` instead makes every value with a high
761/// half of its own greater than or equal to every other, which is the shape of this that a
762/// differential run against GCC caught.
763fn compare(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
764    let Extra::IntPred(pred) = func[inst].extra else { return };
765    let args = func[func[inst].args].to_vec();
766    let [a, b] = args[..] else { return };
767    let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
768        return;
769    };
770    let answer = if matches!(pred, IntPred::Eq | IntPred::Ne) {
771        let low = ahead(func, inst, Opcode::Xor, &[a_low, b_low]);
772        let high = ahead(func, inst, Opcode::Xor, &[a_high, b_high]);
773        let both = ahead(func, inst, Opcode::Or, &[low, high]);
774        let zero = ahead_const(func, inst, 0);
775        compared(func, inst, pred, both, zero)
776    } else {
777        let above = compared(func, inst, strict(pred), a_high, b_high);
778        let below = compared(func, inst, unsigned(pred), a_low, b_low);
779        let same = compared(func, inst, IntPred::Eq, a_high, b_high);
780        let tail = bit(func, inst, Opcode::And, same, below);
781        bit(func, inst, Opcode::Or, above, tail)
782    };
783    if let Some(result) = func[inst].first_result {
784        forward.insert(result, answer);
785    }
786    func.remove_inst(inst);
787}
788
789/// The same ordering with the equal case taken out of it, which is what the high halves are asked.
790fn strict(pred: IntPred) -> IntPred {
791    match pred {
792        IntPred::Sle => IntPred::Slt,
793        IntPred::Sge => IntPred::Sgt,
794        IntPred::Ule => IntPred::Ult,
795        IntPred::Uge => IntPred::Ugt,
796        other => other,
797    }
798}
799
800/// The same ordering with no sign in it, which is how the low halves of two signed numbers compare.
801fn unsigned(pred: IntPred) -> IntPred {
802    match pred {
803        IntPred::Slt => IntPred::Ult,
804        IntPred::Sle => IntPred::Ule,
805        IntPred::Sgt => IntPred::Ugt,
806        IntPred::Sge => IntPred::Uge,
807        other => other,
808    }
809}
810
811/// A choice between two wide values, which is the same choice made on each half.
812///
813/// Two of them rather than one, with the condition read twice. What that costs is one more
814/// conditional move, and what the alternative costs is a branch, which is the more expensive of the
815/// two on anything that predicts.
816fn choose(func: &mut Func, halves: &mut Halves, inst: Inst) {
817    let args = func[func[inst].args].to_vec();
818    let [cond, then, other] = args[..] else { return };
819    let (Some(&(then_low, then_high)), Some(&(other_low, other_high))) =
820        (halves.get(&then), halves.get(&other))
821    else {
822        return;
823    };
824    let low = ahead(func, inst, Opcode::Select, &[cond, then_low, other_low]);
825    let high = ahead(func, inst, Opcode::Select, &[cond, then_high, other_high]);
826    replace(func, halves, inst, low, high);
827}
828
829/// Keeping the low bits of a wide value, which is the low half and then whatever is left to do.
830///
831/// Down to sixty four there is nothing left to do and the low half is the answer, so the truncation
832/// goes and its readers read the half. Down to anything narrower the machine's own truncation still
833/// happens, out of the half rather than out of the value that is no longer there.
834fn truncate(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
835    let Some(&arg) = func[func[inst].args].first() else { return };
836    let Some(&(low, _)) = halves.get(&arg) else { return };
837    let Some(result) = func[inst].first_result else { return };
838    if func[result].ty.bits() == HALF {
839        forward.insert(result, low);
840        func.remove_inst(inst);
841        return;
842    }
843    becomes(func, inst, Opcode::Trunc, &[low]);
844}
845
846/// Widening into a wide value, which is the value in the low half and its own sign or zero above.
847fn extend(func: &mut Func, halves: &mut Halves, inst: Inst, signed: bool) {
848    let Some(&arg) = func[func[inst].args].first() else { return };
849    let low = if func[arg].ty.bits() == HALF {
850        arg
851    } else {
852        let opcode = if signed { Opcode::SExt } else { Opcode::ZExt };
853        ahead(func, inst, opcode, &[arg])
854    };
855    let high = if signed {
856        let top = ahead_const(func, inst, i128::from(HALF - 1));
857        ahead(func, inst, Opcode::AShr, &[low, top])
858    } else {
859        ahead_const(func, inst, 0)
860    };
861    replace(func, halves, inst, low, high);
862}
863
864/// A call, as a call passing and receiving halves.
865///
866/// The instruction is made again rather than edited, because how many values a call gives back is
867/// settled when it is created and a wide return value is two where it was one. Its signature is
868/// made again for the same reason, since the signature is what each end of the call lays itself out
869/// against and both ends are split the same way.
870fn call(func: &mut Func, halves: &mut Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
871    let data = func[inst];
872    let Extra::Call(info) = data.extra else { return };
873    let info = func[info];
874    let args = spread(&func[data.args], halves);
875    let results: Vec<Type> = data
876        .results()
877        .map(|value| func[value].ty)
878        .flat_map(|ty| if is_wide(ty) { vec![half(), half()] } else { vec![ty] })
879        .collect();
880    let signature = func.add_signature(split_signature(&func[info.signature]));
881    let extra = Extra::Call(func.add_call(CallInfo { signature, ..info }));
882    let args = func.push_values(&args);
883    let span = func.span(inst);
884    let made = func.create_inst(InstData { args, extra, ..data }, &results, span);
885    func.insert_before(made, inst);
886    let mut fresh = func[made].results();
887    for old in data.results() {
888        if is_wide(func[old].ty) {
889            let (Some(low), Some(high)) = (fresh.next(), fresh.next()) else { return };
890            halves.insert(old, (low, high));
891        } else if let Some(again) = fresh.next() {
892            forward.insert(old, again);
893        }
894    }
895    func.remove_inst(inst);
896}
897
898/// A `return`, whose operands are the values the signature says and so are halves now.
899fn flatten(func: &mut Func, halves: &Halves, inst: Inst) {
900    let args = spread(&func[func[inst].args], halves);
901    func[inst].args = func.push_values(&args);
902}
903
904/// A branch, whose arguments hang on the edge rather than on the instruction.
905fn edges(func: &mut Func, halves: &Halves, inst: Inst) {
906    for at in func.target_list(inst).iter() {
907        let call = func[at];
908        let args = func[call.args].to_vec();
909        if !args.iter().any(|value| halves.contains_key(value)) {
910            continue;
911        }
912        let args = func.push_values(&spread(&args, halves));
913        func.set_block_call(at, BlockCall { args, ..call });
914    }
915}
916
917/// A list of values with each wide one replaced by its two halves in the same position.
918fn spread(args: &[Value], halves: &Halves) -> Vec<Value> {
919    args.iter()
920        .flat_map(|value| match halves.get(value) {
921            Some(&(low, high)) => vec![low, high],
922            None => vec![*value],
923        })
924        .collect()
925}
926
927/// One signature with every wide parameter and return value as two halves in its place.
928///
929/// Each half is plain. What the ABI asks beyond a type is about the bits above a narrow value and
930/// about an object whose address travels, and a half is neither: it is exactly a register wide and
931/// it is the value itself.
932fn split_signature(signature: &Signature) -> Signature {
933    let split = |params: &[Param]| -> Vec<Param> {
934        params
935            .iter()
936            .flat_map(|param| {
937                if is_wide(param.ty) {
938                    vec![Param::new(half()), Param::new(half())]
939                } else {
940                    vec![*param]
941                }
942            })
943            .collect()
944    };
945    Signature {
946        params: split(&signature.params),
947        returns: split(&signature.returns),
948        variadic: signature.variadic,
949    }
950}
951
952/// Records the two halves an instruction became and takes the instruction out.
953fn replace(func: &mut Func, halves: &mut Halves, inst: Inst, low: Value, high: Value) {
954    if let Some(result) = func[inst].first_result {
955        halves.insert(result, (low, high));
956    }
957    func.remove_inst(inst);
958}
959
960/// Points every reader of a value this pass replaced at what replaced it.
961///
962/// The arguments of each instruction and the arguments of the blocks it branches to, which between
963/// them are everywhere a value can be read. Nothing chases, because every value this map answers
964/// with is one made here and so is never itself a key.
965fn substitute(func: &mut Func, forward: &HashMap<Value, Value>) {
966    if forward.is_empty() {
967        return;
968    }
969    let with = |value: Value| forward.get(&value).copied().unwrap_or(value);
970    for block in func.blocks().collect::<Vec<_>>() {
971        for inst in func.insts(block).collect::<Vec<Inst>>() {
972            let args = func[inst].args;
973            func.rewrite(args, with);
974            for call in func.successors(inst).collect::<Vec<_>>() {
975                func.rewrite(call.args, with);
976            }
977        }
978    }
979}
980
981/// The access one word of a wide access is, that many bytes into it.
982fn word(info: MemInfo, at: u64) -> MemInfo {
983    let align = if at == 0 { info.align } else { info.align.min(8) };
984    MemInfo { size: STEP, align, ..info }
985}
986
987/// The address one word past another, written in front of an instruction.
988fn stepped(func: &mut Func, inst: Inst, from: Value) -> Value {
989    let step = ahead_const(func, inst, i128::from(STEP));
990    let args = func.push_values(&[from, step]);
991    written(func, inst, InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR)
992}
993
994/// A load put in front of an instruction, and the half it reads.
995fn read(func: &mut Func, inst: Inst, from: Value, info: MemInfo, flags: Flags) -> Value {
996    let extra = Extra::Mem(func.add_mem(info));
997    let args = func.push_values(&[from]);
998    let data = InstData { args, flags, extra, ..InstData::new(Opcode::Load) };
999    written(func, inst, data, half())
1000}
1001
1002/// A store put in front of an instruction, which produces nothing and is only its effect.
1003fn write(func: &mut Func, inst: Inst, value: Value, into: Value, info: MemInfo, flags: Flags) {
1004    let span = func.span(inst);
1005    let extra = Extra::Mem(func.add_mem(info));
1006    let args = func.push_values(&[value, into]);
1007    let data = InstData { args, flags, extra, ..InstData::new(Opcode::Store) };
1008    let made = func.create_inst(data, &[], span);
1009    func.insert_before(made, inst);
1010}
1011
1012/// A comparison written in front of an instruction, which carries its predicate where everything
1013/// else carries nothing.
1014fn compared(func: &mut Func, inst: Inst, pred: IntPred, lhs: Value, rhs: Value) -> Value {
1015    let args = func.push_values(&[lhs, rhs]);
1016    let extra = Extra::IntPred(pred);
1017    written(func, inst, InstData { args, extra, ..InstData::new(Opcode::ICmp) }, Type::I1)
1018}
1019
1020/// An `and` or an `or` over two truth values, which is the same instruction at the width of one.
1021fn bit(func: &mut Func, inst: Inst, opcode: Opcode, lhs: Value, rhs: Value) -> Value {
1022    let args = func.push_values(&[lhs, rhs]);
1023    written(func, inst, InstData { args, ..InstData::new(opcode) }, Type::I1)
1024}
1025
1026/// An instruction over these operands put in front of another one, producing a half.
1027fn ahead(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) -> Value {
1028    let args = func.push_values(args);
1029    written(func, inst, InstData { args, ..InstData::new(opcode) }, half())
1030}
1031
1032/// A constant half put in front of an instruction.
1033fn ahead_const(func: &mut Func, inst: Inst, value: i128) -> Value {
1034    let extra = Extra::Imm(func.add_imm(Imm::int(value, half())));
1035    written(func, inst, InstData { extra, ..InstData::new(Opcode::IConst) }, half())
1036}
1037
1038/// Creates the instruction, puts it in front of another, and reads its value back out.
1039fn written(func: &mut Func, inst: Inst, data: InstData, ty: Type) -> Value {
1040    let span = func.span(inst);
1041    let made = func.create_inst(data, &[ty], span);
1042    func.insert_before(made, inst);
1043    func[made].first_result.expect("an instruction created with one result has one")
1044}
1045
1046/// Turns an instruction into a different one over different operands, in place.
1047fn becomes(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) {
1048    let args = func.push_values(args);
1049    let data = &mut func[inst];
1050    data.opcode = opcode;
1051    data.args = args;
1052    data.extra = Extra::None;
1053    data.flags = data.flags.intersection(Flags::legal_on(opcode));
1054}
1055
1056#[cfg(test)]
1057mod tests {
1058    use rucc_base::Interner;
1059    use rucc_ir::{
1060        Block, Builder, Flags, Float, Func, MemOrder, Module, Restrict, Signature, Type, Value,
1061    };
1062    use rucc_target::x86_64::SYSV;
1063    use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1064
1065    use super::{HALF, IntPred, MemInfo, Opcode, halves};
1066
1067    /// The width the pass is about, as a type, which is what every test builds with.
1068    fn wide() -> Type {
1069        Type::int(super::WIDE)
1070    }
1071
1072    fn target() -> TargetInfo {
1073        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1074    }
1075
1076    fn printed(func: &Func, names: &mut Interner) -> String {
1077        let module = Module::new(names.intern("w.c"), &target());
1078        rucc_ir::print_func(&module, func, names)
1079    }
1080
1081    /// A function of those parameters returning that, with its entry block and its parameters.
1082    fn shell(names: &mut Interner, params: &[Type], returns: &[Type]) -> (Func, Block, Vec<Value>) {
1083        let signature = Signature::new().with_params(params).with_returns(returns);
1084        let mut func = Func::new(names.intern("f"), signature);
1085        let entry = func.create_block();
1086        let values = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
1087        (func, entry, values)
1088    }
1089
1090    /// An ordinary access of that many bytes, aligned that far.
1091    fn info(size: u64, align: u32) -> MemInfo {
1092        MemInfo {
1093            size,
1094            align,
1095            order: MemOrder::NotAtomic,
1096            tbaa: None,
1097            owns: 0,
1098            restrict: Restrict::NONE,
1099        }
1100    }
1101
1102    #[test]
1103    fn an_add_carries_from_the_low_half_into_the_high_one() {
1104        let mut names = Interner::new();
1105        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1106        let mut build = Builder::new(&mut func, entry);
1107        let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1108        build.ret(&[sum]);
1109
1110        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1111        let text = printed(&func, &mut names);
1112        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1113        // Two adds for the halves, one more for the carry, and the carry itself is the unsigned
1114        // comparison that says the low half wrapped.
1115        assert_eq!(text.matches(" = add ").count(), 3, "three adds: {text}");
1116        assert_eq!(text.matches("icmp ult").count(), 1, "one carry: {text}");
1117        assert_eq!(text.matches(" = zext.i64 ").count(), 1, "the carry as a number: {text}");
1118    }
1119
1120    #[test]
1121    fn a_subtract_borrows_the_other_way_round() {
1122        let mut names = Interner::new();
1123        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1124        let mut build = Builder::new(&mut func, entry);
1125        let difference = build.binary(Opcode::Sub, params[0], params[1], Flags::NONE);
1126        build.ret(&[difference]);
1127
1128        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1129        let text = printed(&func, &mut names);
1130        assert_eq!(text.matches(" = sub ").count(), 3, "three subtracts: {text}");
1131        // The borrow is the operands compared, not the answer, which is what tells a reader the
1132        // two directions were thought about separately.
1133        assert!(text.contains("icmp ult %0, %2"), "the operands are compared: {text}");
1134    }
1135
1136    #[test]
1137    fn the_signature_and_the_entry_block_say_the_same_thing() {
1138        let mut names = Interner::new();
1139        let (mut func, entry, params) = shell(&mut names, &[Type::int(32), wide()], &[wide()]);
1140        let mut build = Builder::new(&mut func, entry);
1141        build.ret(&[params[1]]);
1142
1143        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1144        assert_eq!(
1145            func.signature().param_types().collect::<Vec<_>>(),
1146            [Type::int(32), Type::int(HALF), Type::int(HALF)],
1147            "the wide parameter became two where it stood"
1148        );
1149        assert_eq!(
1150            func.signature().return_types().collect::<Vec<_>>(),
1151            [Type::int(HALF), Type::int(HALF)],
1152            "and so did what comes back"
1153        );
1154        let text = printed(&func, &mut names);
1155        assert!(text.contains("block0(%0: i32, %1: i64, %2: i64)"), "the block agrees: {text}");
1156        assert!(text.contains("return %1, %2"), "both halves go back: {text}");
1157        let _ = entry;
1158    }
1159
1160    #[test]
1161    fn a_read_takes_the_high_word_a_word_above_the_low_one() {
1162        let mut names = Interner::new();
1163        let (mut func, entry, params) = shell(&mut names, &[Type::PTR], &[wide()]);
1164        let mut build = Builder::new(&mut func, entry);
1165        let value = build.load(wide(), params[0], info(16, 16), Flags::NONE);
1166        build.ret(&[value]);
1167
1168        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1169        let text = printed(&func, &mut names);
1170        assert_eq!(text.matches(" = load.i64 ").count(), 2, "two reads: {text}");
1171        assert!(text.contains("ptr_add"), "the high word is a word up: {text}");
1172        // The object is aligned to sixteen and its high word is not, which is the one thing
1173        // splitting an access can get wrong quietly.
1174        assert!(text.contains("align 16"), "the low word keeps what the object had: {text}");
1175        assert!(text.contains("align 8"), "the high word knows less: {text}");
1176    }
1177
1178    #[test]
1179    fn an_equality_asks_once_about_both_halves() {
1180        let mut names = Interner::new();
1181        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1182        let mut build = Builder::new(&mut func, entry);
1183        let same = build.icmp(IntPred::Eq, params[0], params[1]);
1184        let answer = build.unary(Opcode::ZExt, same, Type::int(32));
1185        build.ret(&[answer]);
1186
1187        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1188        let text = printed(&func, &mut names);
1189        assert_eq!(text.matches("icmp").count(), 1, "one comparison: {text}");
1190        assert_eq!(text.matches(" = xor ").count(), 2, "the halves differ or they do not: {text}");
1191    }
1192
1193    #[test]
1194    fn an_ordering_reads_the_low_halves_without_a_sign() {
1195        let mut names = Interner::new();
1196        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1197        let mut build = Builder::new(&mut func, entry);
1198        let below = build.icmp(IntPred::Slt, params[0], params[1]);
1199        let answer = build.unary(Opcode::ZExt, below, Type::int(32));
1200        build.ret(&[answer]);
1201
1202        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1203        let text = printed(&func, &mut names);
1204        assert!(text.contains("icmp slt"), "the high halves keep the sign: {text}");
1205        assert!(text.contains("icmp ult"), "the low halves have none: {text}");
1206        assert!(
1207            text.contains("icmp eq"),
1208            "and the low halves only matter when the high tie: {text}"
1209        );
1210    }
1211
1212    /// An ordering that allows the two to be equal still asks the high halves a strict question.
1213    ///
1214    /// Two values whose high halves are equal are ordered by their low halves alone, and a high
1215    /// half that is greater than or equal to the other says nothing about that. Asking the high
1216    /// halves the predicate as it stands makes every ordering that is not strict answer yes on a
1217    /// tie, which is the mistake a run against GCC caught.
1218    #[test]
1219    fn an_ordering_that_allows_equality_asks_the_high_halves_a_strict_question() {
1220        let mut names = Interner::new();
1221        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1222        let mut build = Builder::new(&mut func, entry);
1223        let at_least = build.icmp(IntPred::Sge, params[0], params[1]);
1224        let answer = build.unary(Opcode::ZExt, at_least, Type::int(32));
1225        build.ret(&[answer]);
1226
1227        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1228        let text = printed(&func, &mut names);
1229        assert!(text.contains("icmp sgt"), "the high halves settle it outright: {text}");
1230        assert!(!text.contains("icmp sge"), "a tie in the high halves settles nothing: {text}");
1231        assert!(text.contains("icmp uge"), "the low halves are the ones allowed to tie: {text}");
1232    }
1233
1234    #[test]
1235    fn a_widening_puts_the_sign_of_the_value_in_the_high_half() {
1236        let mut names = Interner::new();
1237        let (mut func, entry, params) = shell(&mut names, &[Type::int(32)], &[wide()]);
1238        let mut build = Builder::new(&mut func, entry);
1239        let value = build.unary(Opcode::SExt, params[0], wide());
1240        build.ret(&[value]);
1241
1242        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1243        let text = printed(&func, &mut names);
1244        assert!(text.contains("sext.i64"), "the value fills the low half: {text}");
1245        assert!(text.contains("ashr"), "and its sign fills the high one: {text}");
1246    }
1247
1248    #[test]
1249    fn a_block_parameter_becomes_two_and_every_branch_passes_two() {
1250        let mut names = Interner::new();
1251        let (mut func, entry, params) = shell(&mut names, &[wide(), Type::int(32)], &[wide()]);
1252        let tail = func.create_block();
1253        let carried = func.append_param(tail, wide());
1254        let mut build = Builder::new(&mut func, entry);
1255        let zero = build.iconst(Type::int(32), 0);
1256        let taken = build.icmp(IntPred::Ne, params[1], zero);
1257        let other = build.iconst(wide(), 7);
1258        build.br_if(taken, tail, &[params[0]], tail, &[other]);
1259        let mut build = Builder::new(&mut func, tail);
1260        build.ret(&[carried]);
1261
1262        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1263        let text = printed(&func, &mut names);
1264        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1265        assert!(text.contains("block1(%7: i64, %8: i64)"), "the block takes two: {text}");
1266        assert_eq!(text.matches("block1(").count(), 3, "and both edges pass two: {text}");
1267    }
1268
1269    /// A multiply is the low halves, the two cross products, and nothing for the fourth corner.
1270    ///
1271    /// Three at the top, and four more inside the carry out of the low halves, which is a product
1272    /// at half the width again worked out the same way. What the count is really saying is that the
1273    /// two high halves are never multiplied together, because the whole of that partial product
1274    /// lands above the width.
1275    #[test]
1276    fn a_multiply_is_three_multiplies_and_the_carry_out_of_the_low_ones() {
1277        let mut names = Interner::new();
1278        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1279        let mut build = Builder::new(&mut func, entry);
1280        let product = build.binary(Opcode::Mul, params[0], params[1], Flags::NONE);
1281        build.ret(&[product]);
1282
1283        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1284        let text = printed(&func, &mut names);
1285        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1286        assert_eq!(text.matches(" = mul ").count(), 7, "three and the carry's four: {text}");
1287    }
1288
1289    /// Each of the four divisions becomes a call to the routine of that name in the runtime.
1290    ///
1291    /// The sign is in the name because it is in the answer. A quotient rounds towards zero and a
1292    /// remainder takes the sign of the dividend, so the signed routine and the unsigned one work out
1293    /// two different numbers, where a wide add is one computation that two signednesses read the
1294    /// same bits of.
1295    #[test]
1296    fn each_of_the_four_divisions_calls_the_routine_of_that_name() {
1297        for (opcode, routine) in [
1298            (Opcode::UDiv, "__udivti3"),
1299            (Opcode::SDiv, "__divti3"),
1300            (Opcode::URem, "__umodti3"),
1301            (Opcode::SRem, "__modti3"),
1302        ] {
1303            let mut names = Interner::new();
1304            let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1305            let mut build = Builder::new(&mut func, entry);
1306            let answer = build.binary(opcode, params[0], params[1], Flags::NONE);
1307            build.ret(&[answer]);
1308
1309            assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1310            let text = printed(&func, &mut names);
1311            assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1312            assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1313        }
1314    }
1315
1316    /// The call hands over four halves and takes two back, which is the shape of the routine.
1317    ///
1318    /// Low half first and the dividend first, which is what the definition of the routine was split
1319    /// into by the same code on the way in. The operands here are the entry block's parameters, so
1320    /// the four values the call passes are the four the block now takes, in order.
1321    #[test]
1322    fn a_divide_hands_over_four_halves_and_takes_two_back() {
1323        let mut names = Interner::new();
1324        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1325        let mut build = Builder::new(&mut func, entry);
1326        let quotient = build.binary(Opcode::UDiv, params[0], params[1], Flags::NONE);
1327        build.ret(&[quotient]);
1328
1329        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1330        let text = printed(&func, &mut names);
1331        assert!(text.contains("@__udivti3(%0, %1, %2, %3)"), "four halves go over: {text}");
1332        assert!(text.contains("return %4, %5"), "and two come back: {text}");
1333    }
1334
1335    /// A divide whose operands were worked out in the function calls with the halves of those.
1336    ///
1337    /// The other direction of the same rule the walk is for: the call is built where the divide was,
1338    /// so the halves of a sum computed above it exist by then, and what reaches the routine is the
1339    /// two values the sum became rather than anything at the old width.
1340    #[test]
1341    fn a_divide_of_something_computed_calls_with_the_halves_of_it() {
1342        let mut names = Interner::new();
1343        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1344        let mut build = Builder::new(&mut func, entry);
1345        let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1346        let quotient = build.binary(Opcode::SDiv, sum, params[1], Flags::NONE);
1347        build.ret(&[quotient]);
1348
1349        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1350        let text = printed(&func, &mut names);
1351        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1352        assert_eq!(text.matches(" = add ").count(), 3, "the sum is still a sum: {text}");
1353        assert_eq!(text.matches("call @__divti3").count(), 1, "one call: {text}");
1354    }
1355
1356    /// Each conversion between this width and a float becomes a call to the routine of that name.
1357    ///
1358    /// Twelve of them, which is a signed and an unsigned integer against a `float`, a `double` and a
1359    /// `_Float128` in each direction, and the table is here rather than in a comment because the
1360    /// names are the whole of what this has to get right.
1361    #[test]
1362    fn each_conversion_between_this_width_and_a_float_calls_the_routine_of_that_name() {
1363        let double = Type::float(Float::F64);
1364        let single = Type::float(Float::F32);
1365        let quad = Type::float(Float::F128);
1366        for (opcode, float, routine) in [
1367            (Opcode::SIToFP, double, "__floattidf"),
1368            (Opcode::SIToFP, single, "__floattisf"),
1369            (Opcode::UIToFP, double, "__floatuntidf"),
1370            (Opcode::UIToFP, single, "__floatuntisf"),
1371            (Opcode::SIToFP, quad, "__floattitf"),
1372            (Opcode::UIToFP, quad, "__floatuntitf"),
1373        ] {
1374            let mut names = Interner::new();
1375            let (mut func, entry, params) = shell(&mut names, &[wide()], &[float]);
1376            let mut build = Builder::new(&mut func, entry);
1377            let answer = build.unary(opcode, params[0], float);
1378            build.ret(&[answer]);
1379
1380            assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1381            let text = printed(&func, &mut names);
1382            assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1383            assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1384        }
1385        for (opcode, float, routine) in [
1386            (Opcode::FPToSI, double, "__fixdfti"),
1387            (Opcode::FPToSI, single, "__fixsfti"),
1388            (Opcode::FPToUI, double, "__fixunsdfti"),
1389            (Opcode::FPToUI, single, "__fixunssfti"),
1390            (Opcode::FPToSI, quad, "__fixtfti"),
1391            (Opcode::FPToUI, quad, "__fixunstfti"),
1392        ] {
1393            let mut names = Interner::new();
1394            let (mut func, entry, params) = shell(&mut names, &[float], &[wide()]);
1395            let mut build = Builder::new(&mut func, entry);
1396            let answer = build.unary(opcode, params[0], wide());
1397            build.ret(&[answer]);
1398
1399            assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1400            let text = printed(&func, &mut names);
1401            assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1402            assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1403        }
1404    }
1405
1406    /// A conversion up hands over two halves and takes one float back, and one coming down is the
1407    /// same call the other way round.
1408    ///
1409    /// The answer going up is not a wide value, so it is one result and the readers of the
1410    /// conversion read it, the way they read the answer of a comparison.
1411    #[test]
1412    fn a_conversion_hands_over_halves_one_way_and_takes_them_back_the_other() {
1413        let double = Type::float(Float::F64);
1414        let mut names = Interner::new();
1415        let (mut func, entry, params) = shell(&mut names, &[wide()], &[double]);
1416        let mut build = Builder::new(&mut func, entry);
1417        let answer = build.unary(Opcode::SIToFP, params[0], double);
1418        build.ret(&[answer]);
1419
1420        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1421        let text = printed(&func, &mut names);
1422        assert!(text.contains("@__floattidf(%0, %1)"), "two halves go over: {text}");
1423        assert!(text.contains("return %2"), "and one float comes back: {text}");
1424
1425        let mut names = Interner::new();
1426        let (mut func, entry, params) = shell(&mut names, &[double], &[wide()]);
1427        let mut build = Builder::new(&mut func, entry);
1428        let answer = build.unary(Opcode::FPToSI, params[0], wide());
1429        build.ret(&[answer]);
1430
1431        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1432        let text = printed(&func, &mut names);
1433        assert!(text.contains("@__fixdfti(%0)"), "the float goes over as it is: {text}");
1434        assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1435    }
1436
1437    /// A conversion against a quad is that same shape, with the quad crossing whole.
1438    ///
1439    /// Worth its own test because the two sides of it are wide for different reasons. The integer is
1440    /// a pair here because no register holds a hundred and twenty eight bits of integer, and the
1441    /// quad is one value because a vector register holds all of it, so the call this pass writes has
1442    /// two operands and one result going up and one operand and two results coming down.
1443    #[test]
1444    fn a_conversion_against_a_quad_hands_over_the_pair_and_the_quad_whole() {
1445        let quad = Type::float(Float::F128);
1446        let mut names = Interner::new();
1447        let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
1448        let mut build = Builder::new(&mut func, entry);
1449        let answer = build.unary(Opcode::UIToFP, params[0], quad);
1450        build.ret(&[answer]);
1451
1452        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1453        let text = printed(&func, &mut names);
1454        assert!(text.contains("@__floatuntitf(%0, %1)"), "two halves go over: {text}");
1455        assert!(text.contains("return %2"), "and one quad comes back: {text}");
1456
1457        let mut names = Interner::new();
1458        let (mut func, entry, params) = shell(&mut names, &[quad], &[wide()]);
1459        let mut build = Builder::new(&mut func, entry);
1460        let answer = build.unary(Opcode::FPToSI, params[0], wide());
1461        build.ret(&[answer]);
1462
1463        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1464        let text = printed(&func, &mut names);
1465        assert!(text.contains("@__fixtfti(%0)"), "the quad goes over as it is: {text}");
1466        assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1467    }
1468
1469    /// A conversion at a float width the runtime has no routine for leaves the function alone.
1470    ///
1471    /// `long double` is the eighty bit float on this target and the runtime has no conversion for
1472    /// it, because the back end has no register that holds one, which is tamnd/rucc#326. So the
1473    /// function keeps its wide values and is refused below by name, rather than being turned into a
1474    /// call to a routine nothing defines.
1475    #[test]
1476    fn a_conversion_at_a_width_the_runtime_has_no_routine_for_is_left_alone() {
1477        let long = Type::float(Float::F80);
1478        let mut names = Interner::new();
1479        let (mut func, entry, params) = shell(&mut names, &[wide()], &[long]);
1480        let mut build = Builder::new(&mut func, entry);
1481        let answer = build.unary(Opcode::SIToFP, params[0], long);
1482        build.ret(&[answer]);
1483
1484        assert!(!halves(&mut func, &mut names, &SYSV), "the pass does not understand this one");
1485        let text = printed(&func, &mut names);
1486        assert!(text.contains("i128"), "the width is still there: {text}");
1487    }
1488
1489    /// A shift left moves each half and chooses between the count having crossed a half and not.
1490    ///
1491    /// Two shifts left, one per half, and the low one does for both cases: a count that reached a
1492    /// whole half puts exactly that value in the high half and nothing in the low one, so the only
1493    /// thing the far case needs is the shift the near case already did. Two right shifts carry the
1494    /// crossing bits, two selects pick a half each, and there is no branch anywhere.
1495    #[test]
1496    fn a_shift_left_chooses_between_a_count_that_crossed_a_half_and_one_that_did_not() {
1497        let mut names = Interner::new();
1498        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1499        let mut build = Builder::new(&mut func, entry);
1500        let moved = build.binary(Opcode::Shl, params[0], params[1], Flags::NONE);
1501        build.ret(&[moved]);
1502
1503        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1504        let text = printed(&func, &mut names);
1505        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1506        assert_eq!(
1507            text.matches(" = shl ").count(),
1508            2,
1509            "one per half, and the far case reuses one: {text}"
1510        );
1511        assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1512        assert_eq!(text.matches(" = lshr ").count(), 2, "the crossing bits, in two steps: {text}");
1513    }
1514
1515    /// The bits that cross move one place and then the rest, so a count of zero carries nothing.
1516    ///
1517    /// Sixty four less a count of zero is sixty four, which is not a distance a sixty four bit shift
1518    /// has. One place first and sixty three less the count after is the same distance everywhere the
1519    /// question is asked, and for a count of zero it moves a value whose top bit has already gone
1520    /// all the way out, which leaves the zero a half that did not move should carry.
1521    #[test]
1522    fn the_bits_that_cross_move_one_place_and_then_the_rest_of_the_way() {
1523        let mut names = Interner::new();
1524        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1525        let mut build = Builder::new(&mut func, entry);
1526        let moved = build.binary(Opcode::LShr, params[0], params[1], Flags::NONE);
1527        build.ret(&[moved]);
1528
1529        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1530        let text = printed(&func, &mut names);
1531        assert!(text.contains("iconst.i64 63"), "sixty three is the distance left: {text}");
1532        assert!(text.contains("iconst.i64 1"), "after the one place that comes first: {text}");
1533        assert!(text.contains(" = sub "), "the rest of the way is worked out: {text}");
1534        assert!(
1535            !text.contains("iconst.i64 127"),
1536            "and the count is not masked to the width: {text}"
1537        );
1538    }
1539
1540    /// An arithmetic shift right leaves the sign bit behind where a logical one leaves zeroes.
1541    ///
1542    /// What the two differ in is only the half the count moved out of entirely. A logical shift puts
1543    /// zeroes there, which is a constant already in hand, and an arithmetic one puts the sign bit
1544    /// spread across the half it came from, which is one more shift.
1545    #[test]
1546    fn an_arithmetic_shift_right_leaves_the_sign_bit_where_a_logical_one_leaves_zeroes() {
1547        let mut names = Interner::new();
1548        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1549        let mut build = Builder::new(&mut func, entry);
1550        let moved = build.binary(Opcode::AShr, params[0], params[1], Flags::NONE);
1551        build.ret(&[moved]);
1552
1553        assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1554        let text = printed(&func, &mut names);
1555        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1556        // The high half by the count, and the high half by sixty three for the half left empty.
1557        assert_eq!(text.matches(" = ashr ").count(), 2, "the count and the sign: {text}");
1558        assert_eq!(text.matches(" = lshr ").count(), 1, "the low half is not signed: {text}");
1559        assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1560    }
1561
1562    #[test]
1563    fn a_parameter_with_one_register_left_leaves_the_function_alone() {
1564        let mut names = Interner::new();
1565        let word = Type::int(HALF);
1566        // Five words take five of the six argument registers, so the halves of the sixth
1567        // parameter would be one register and one word of the caller's stack, which is not where
1568        // the convention puts a value this wide.
1569        let params = [word, word, word, word, word, wide()];
1570        let (mut func, entry, values) = shell(&mut names, &params, &[word]);
1571        let mut build = Builder::new(&mut func, entry);
1572        let low = build.unary(Opcode::Trunc, values[5], word);
1573        build.ret(&[low]);
1574        let before = printed(&func, &mut names);
1575
1576        assert!(!halves(&mut func, &mut names, &SYSV), "one of the halves has no register");
1577        assert_eq!(printed(&func, &mut names), before, "so nothing moved");
1578    }
1579
1580    /// The order the function holds its blocks in is not the order they run in.
1581    ///
1582    /// This is what the optimizer produced and `-O0` did not. A block that runs early is made late
1583    /// by whichever pass needed it, so the list the function keeps had a use of a wide value in it
1584    /// before the instruction defining that value, and the walk that asks whether every definition
1585    /// comes first said no and left the whole function alone. Then the selector met an instruction
1586    /// at a width it has no register for and refused the program. The blocks here are made in the
1587    /// order that produces, which is the tail before the middle. tamnd/rucc#1054.
1588    #[test]
1589    fn a_block_made_after_the_one_it_runs_before_is_still_split() {
1590        let mut names = Interner::new();
1591        let (mut func, entry, params) = shell(&mut names, &[wide()], &[wide()]);
1592        let tail = func.create_block();
1593        let middle = func.create_block();
1594        let mut build = Builder::new(&mut func, entry);
1595        build.jump(middle, &[]);
1596        let mut build = Builder::new(&mut func, middle);
1597        let doubled = build.binary(Opcode::Add, params[0], params[0], Flags::NONE);
1598        build.jump(tail, &[]);
1599        let mut build = Builder::new(&mut func, tail);
1600        let again = build.binary(Opcode::Add, doubled, doubled, Flags::NONE);
1601        build.ret(&[again]);
1602
1603        assert!(
1604            halves(&mut func, &mut names, &SYSV),
1605            "the definition runs before the use whatever the list says"
1606        );
1607        let text = printed(&func, &mut names);
1608        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1609    }
1610
1611    #[test]
1612    fn a_function_with_nothing_that_wide_is_not_touched() {
1613        let mut names = Interner::new();
1614        let word = Type::int(HALF);
1615        let (mut func, entry, params) = shell(&mut names, &[word, word], &[word]);
1616        let mut build = Builder::new(&mut func, entry);
1617        let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1618        build.ret(&[sum]);
1619
1620        assert!(!halves(&mut func, &mut names, &SYSV), "there is nothing to split");
1621    }
1622}