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