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//! # What it does not do yet
41//!
42//! Dividing, which at this width is a call into the compiler runtime rather than arithmetic at all
43//! and waits on the runtime having the four entry points to call. A function that divides one of
44//! these is left alone here and refused by the selector, which is the same answer it got before
45//! this pass existed.
46
47use std::collections::{HashMap, HashSet};
48
49use rucc_ir::{
50    Abi, Block, BlockCall, CallInfo, Def, Extra, Flags, Func, Imm, Inst, InstData, IntPred,
51    MemInfo, Opcode, Param, Signature, Type, Value,
52};
53use rucc_target::{CallRegs, Places, Where};
54
55use crate::expand;
56
57/// The width this pass is about, which is the one width a C program writes that no register holds.
58const WIDE: u32 = 128;
59
60/// The width each half is, which is a register on every target this pass runs for.
61const HALF: u32 = 64;
62
63/// How many bytes one half takes in memory, which is how far the high one sits above the low one.
64const STEP: u64 = 8;
65
66/// Whether a type is the width this pass splits.
67fn is_wide(ty: Type) -> bool {
68    ty.is_int() && ty.is_scalar() && ty.bits() == WIDE
69}
70
71/// The type each half has.
72fn half() -> Type {
73    Type::int(HALF)
74}
75
76/// Splits every integer the machine holds in two registers into the two halves it holds it in.
77///
78/// Gives back whether it changed anything, which is what a test asks and what tells a reader of a
79/// dump that the function the selector saw is not the one the middle end produced.
80///
81/// The function is left exactly as it was when there is nothing at that width, when something at
82/// that width is reached by an instruction this does not understand, and when a half would cross
83/// the function's boundary somewhere the convention has no register for it. All three leave the
84/// refusal to the passes below, which name the construct they could not lower, rather than
85/// rewriting into something that guessed.
86pub fn halves(func: &mut Func, conv: &CallRegs) -> bool {
87    if !func.values().any(|value| is_wide(func[value].ty)) {
88        return false;
89    }
90    let insts: Vec<Inst> =
91        walk(func).into_iter().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
92    let order: HashMap<Inst, usize> =
93        insts.iter().enumerate().map(|(at, &inst)| (inst, at)).collect();
94    if !insts.iter().enumerate().all(|(at, &inst)| can_split(func, &order, at, inst)) {
95        return false;
96    }
97    if !func.signatures().all(|signature| fits(signature, conv)) {
98        return false;
99    }
100
101    let mut halves: Halves = HashMap::new();
102    let mut forward: HashMap<Value, Value> = HashMap::new();
103    for block in func.blocks().collect::<Vec<_>>() {
104        params(func, block, &mut halves, &mut forward);
105    }
106    for &inst in &insts {
107        rewrite(func, &mut halves, &mut forward, inst);
108    }
109    substitute(func, &forward);
110    let signature = split_signature(func.signature());
111    func.set_signature(signature);
112    true
113}
114
115/// Every block, in an order where a block comes after everything that dominates it.
116///
117/// Reverse postorder from the entry, then whatever the walk did not reach, in the order the
118/// function holds them. The order is what the rule below about a use and its definition is read
119/// against, and the two together are the whole of why this is not simply the order the function
120/// holds the blocks in: a value is defined in a block that dominates every block reading it, a
121/// dominator is on every path from the entry, so a depth first walk finishes it last and reverse
122/// postorder puts it first. The order the function holds blocks in says nothing of the kind. It is
123/// the order they were made in, and every pass in the optimizer that makes a block, which is every
124/// pass that gives a loop a preheader or copies a header in front of one, puts a block that runs
125/// early at the end of that list. So the same program compiled at `-O0` and at `-O1` gave two
126/// different answers to whether this pass understood it, and above `-O0` the answer was often no.
127/// tamnd/rucc#1054.
128///
129/// A block nothing reaches cannot be walked to and is put at the end rather than dropped, because
130/// deciding a block is unreachable is not this pass's business. Two of them in the wrong order
131/// refuse the function the way they always did.
132fn walk(func: &Func) -> Vec<Block> {
133    let Some(entry) = func.entry() else { return func.blocks().collect() };
134    let mut seen: HashSet<Block> = HashSet::new();
135    let mut order: Vec<Block> = Vec::new();
136    // A postorder without recursion: the second time a block comes off the stack every block below
137    // it has been finished, so that is where it belongs in the postorder.
138    let mut stack: Vec<(Block, bool)> = vec![(entry, false)];
139    seen.insert(entry);
140    while let Some((block, done)) = stack.pop() {
141        if done {
142            order.push(block);
143            continue;
144        }
145        stack.push((block, true));
146        let Some(term) = func.terminator(block) else { continue };
147        for call in func.successors(term) {
148            if seen.insert(call.block) {
149                stack.push((call.block, false));
150            }
151        }
152    }
153    order.reverse();
154    order.extend(func.blocks().filter(|block| !seen.contains(block)));
155    order
156}
157
158/// The two halves each wide value became, low first.
159type Halves = HashMap<Value, (Value, Value)>;
160
161/// The opcodes this pass knows how to split.
162///
163/// An instruction that touches a value of this width and is not one of these is why the whole
164/// function is left alone, so this list is the pass's own statement of what it has thought about.
165/// Adding to it is adding an arm to [`rewrite`] as well.
166///
167/// The divisions are deliberately not here, and the module documentation says what they wait on. A
168/// conversion between one of these and a floating point value is missing for a different reason:
169/// the conversion the machine has stops at sixty four bits, so what is needed there is arithmetic
170/// rather than a split, and it belongs beside the other conversions in [`crate::expand`].
171fn understood(opcode: Opcode) -> bool {
172    matches!(
173        opcode,
174        Opcode::IConst
175            | Opcode::Load
176            | Opcode::Store
177            | Opcode::Add
178            | Opcode::Sub
179            | Opcode::Mul
180            | Opcode::Shl
181            | Opcode::LShr
182            | Opcode::AShr
183            | Opcode::And
184            | Opcode::Or
185            | Opcode::Xor
186            | Opcode::ICmp
187            | Opcode::Select
188            | Opcode::Trunc
189            | Opcode::SExt
190            | Opcode::ZExt
191            | Opcode::Call
192            | Opcode::CallIndirect
193            | Opcode::Return
194            | Opcode::Jump
195            | Opcode::BrIf
196    )
197}
198
199/// Whether one instruction is one this pass can split, given where it is in the walk.
200///
201/// Asked of every instruction, and answered yes at once for the ones that never see a value this
202/// wide, which in a function that has one at all is still most of them.
203fn can_split(func: &Func, order: &HashMap<Inst, usize>, at: usize, inst: Inst) -> bool {
204    let data = func[inst];
205    let reads = operands(func, inst);
206    let wide = |&value: &Value| is_wide(func[value].ty);
207    if !reads.iter().any(wide) && !data.results().any(|value| is_wide(func[value].ty)) {
208        return true;
209    }
210    if !understood(data.opcode) {
211        return false;
212    }
213    // Memory SSA threads a version of memory through each access, and splitting one access into two
214    // makes a version this pass would have to name. Nothing hands this crate a function carrying it
215    // today, and leaving one alone costs less than being wrong about it later.
216    if func.carries_mem(inst) {
217        return false;
218    }
219    // The machine sign extends from a byte and no narrower, so a truth value widened into the high
220    // half would become an instruction with no rule behind it. Zero extending one is fine, which is
221    // why only the signed side is asked about.
222    if data.opcode == Opcode::SExt && reads.iter().any(|&value| func[value].ty.bits() < 8) {
223        return false;
224    }
225    // Splitting an argument makes two of them, and which parameter an argument stands for is how a
226    // variadic call knows what the ABI asks of the ones its signature does not name. Two values
227    // where that list has one entry is a call laid out against the wrong list.
228    if matches!(data.opcode, Opcode::Call | Opcode::CallIndirect) {
229        let Extra::Call(info) = data.extra else { return false };
230        if func[func[info].signature].variadic {
231            return false;
232        }
233    }
234    // The halves of a value are written where the value was, so a use this pass reaches before the
235    // definition is a use whose halves do not exist yet. A value arriving as a block parameter is
236    // always ready, since every block's parameters are split before any instruction is.
237    reads.iter().filter(|value| wide(value)).all(|&value| match func[value].def {
238        Def::Result { inst, .. } => order.get(&inst).is_some_and(|&def| def < at),
239        Def::Param { .. } => true,
240    })
241}
242
243/// Everything an instruction reads: its own operands, and the arguments it passes along its edges.
244///
245/// The arguments of a `jump` and of a `br_if` hang on the block call rather than on the
246/// instruction, so an instruction whose own operands are all narrow may still be handing a wide one
247/// to the block it branches to.
248fn operands(func: &Func, inst: Inst) -> Vec<Value> {
249    let mut reads = func[func[inst].args].to_vec();
250    for call in func.successors(inst).collect::<Vec<_>>() {
251        reads.extend_from_slice(&func[call.args]);
252    }
253    reads
254}
255
256/// Whether both halves of every wide parameter of one signature land in registers.
257///
258/// The walk is the one [`crate::abi::entry`] makes, because the answer has to be the one that walk
259/// will give: it hands out places in the order the signature holds the parameters, and a wide
260/// parameter is about to become two halves in a row in that order. Both have to be registers. One
261/// register and one word of the argument area is where two independent words go and is not where
262/// the convention puts a sixteen byte value.
263///
264/// A return value is not asked about. What comes back comes back in the registers a return uses,
265/// which is a sequence of its own with two in it on this convention, and a signature wanting more
266/// than it has is refused by name in [`crate::lower`] already.
267fn fits(signature: &Signature, conv: &CallRegs) -> bool {
268    let mut places = Places::new(conv);
269    for param in &signature.params {
270        // A structure the classification put in the argument area, which is the one parameter whose
271        // place is bytes rather than a register. Everything else is a value, the pointer an `sret`
272        // hands over included, and a value takes the next register of its own kind.
273        if let Abi::ByVal { size, align } = param.abi {
274            places.on_stack(u32::try_from(size).unwrap_or(u32::MAX), align);
275        } else if crate::abi::on_the_stack(param.ty) {
276            let (size, align) = crate::abi::X87_AREA;
277            places.on_stack(size, align);
278        } else if is_wide(param.ty) {
279            let low = places.integer();
280            let high = places.integer();
281            if !matches!((low, high), (Where::Reg(_), Where::Reg(_))) {
282                return false;
283            }
284        } else if param.ty.is_float() {
285            places.float();
286        } else {
287            places.integer();
288        }
289    }
290    true
291}
292
293/// One block's parameters, with each wide one replaced by its two halves in the same position.
294///
295/// Every parameter of such a block is made again rather than only the wide ones, because a
296/// parameter's position is its identity to the branches that feed it and appending is the only way
297/// to add one. The narrow ones are made again as themselves and pointed at the copy, which costs
298/// nothing once the substitution below has run.
299fn params(func: &mut Func, block: Block, halves: &mut Halves, forward: &mut HashMap<Value, Value>) {
300    let old: Vec<Value> = func[block].params.clone();
301    if !old.iter().any(|&value| is_wide(func[value].ty)) {
302        return;
303    }
304    for &value in &old {
305        if is_wide(func[value].ty) {
306            let low = func.append_param(block, half());
307            let high = func.append_param(block, half());
308            halves.insert(value, (low, high));
309        } else {
310            let again = func.append_param(block, func[value].ty);
311            forward.insert(value, again);
312        }
313    }
314    func.retain_params(block, |value| !old.contains(&value));
315}
316
317/// One instruction, as instructions over halves.
318fn rewrite(func: &mut Func, halves: &mut Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
319    let data = func[inst];
320    let produces = data.results().any(|value| is_wide(func[value].ty));
321    let takes = func[data.args].iter().any(|&value| is_wide(func[value].ty));
322    match data.opcode {
323        Opcode::IConst if produces => constant(func, halves, inst),
324        Opcode::Load if produces => load(func, halves, inst),
325        Opcode::Store if takes => store(func, halves, inst),
326        Opcode::Add | Opcode::Sub if produces => carried(func, halves, inst, data.opcode),
327        Opcode::Mul if produces => multiply(func, halves, inst),
328        Opcode::Shl | Opcode::LShr | Opcode::AShr if produces => {
329            shifted(func, halves, inst, data.opcode);
330        }
331        Opcode::And | Opcode::Or | Opcode::Xor if produces => {
332            bitwise(func, halves, inst, data.opcode);
333        }
334        Opcode::ICmp if takes => compare(func, halves, forward, inst),
335        Opcode::Select if produces => choose(func, halves, inst),
336        Opcode::Trunc if takes => truncate(func, halves, forward, inst),
337        Opcode::SExt | Opcode::ZExt if produces => {
338            extend(func, halves, inst, data.opcode == Opcode::SExt);
339        }
340        Opcode::Call | Opcode::CallIndirect if produces || takes => {
341            call(func, halves, forward, inst);
342        }
343        Opcode::Return if takes => flatten(func, halves, inst),
344        Opcode::Jump | Opcode::BrIf => edges(func, halves, inst),
345        _ => {}
346    }
347}
348
349/// A constant, as the two halves of its bits with the low one first.
350fn constant(func: &mut Func, halves: &mut Halves, inst: Inst) {
351    let Extra::Imm(imm) = func[inst].extra else { return };
352    let bits = func[imm].unsigned();
353    #[expect(clippy::cast_possible_truncation, reason = "the halves are what this is taking")]
354    let (low, high) = (bits as u64, (bits >> HALF) as u64);
355    let low = ahead_const(func, inst, i128::from(low));
356    let high = ahead_const(func, inst, i128::from(high));
357    replace(func, halves, inst, low, high);
358}
359
360/// A read, as the two words of it with the low one first.
361///
362/// Little endian is the order, which is what every target this back end has is. The high word knows
363/// less about its alignment than the low one when the low one knew more than a word, since a
364/// sixteen byte object aligned to sixteen has its high word aligned to eight.
365fn load(func: &mut Func, halves: &mut Halves, inst: Inst) {
366    let data = func[inst];
367    let Extra::Mem(mem) = data.extra else { return };
368    let info = func[mem];
369    let Some(&from) = func[data.args].first() else { return };
370    let low = read(func, inst, from, word(info, 0), data.flags);
371    let up = stepped(func, inst, from);
372    let high = read(func, inst, up, word(info, STEP), data.flags);
373    replace(func, halves, inst, low, high);
374}
375
376/// A write, as the two words of it.
377fn store(func: &mut Func, halves: &mut Halves, inst: Inst) {
378    let data = func[inst];
379    let Extra::Mem(mem) = data.extra else { return };
380    let info = func[mem];
381    let args = func[data.args].to_vec();
382    let [value, into] = args[..] else { return };
383    let Some(&(low, high)) = halves.get(&value) else { return };
384    write(func, inst, low, into, word(info, 0), data.flags);
385    let up = stepped(func, inst, into);
386    write(func, inst, high, up, word(info, STEP), data.flags);
387    func.remove_inst(inst);
388}
389
390/// An add or a subtract, as the same over the low halves and the same again over the high ones with
391/// what the low halves carried between them.
392///
393/// The carry is a comparison and not a flag. An unsigned sum comes out below either operand exactly
394/// when it wrapped, and an unsigned difference wrapped exactly when the left operand was below the
395/// right, which are the two tests [`crate::expand`] writes for the overflow builtins and are what
396/// the machine's own carry flag stands for. Whether the pair is put back together into an `adc` and
397/// an `sbb` is a question for what reads flags rather than for this, and the answer here is correct
398/// either way.
399fn carried(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
400    let args = func[func[inst].args].to_vec();
401    let [a, b] = args[..] else { return };
402    let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
403        return;
404    };
405    let low = ahead(func, inst, opcode, &[a_low, b_low]);
406    let carried = if opcode == Opcode::Add {
407        compared(func, inst, IntPred::Ult, low, a_low)
408    } else {
409        compared(func, inst, IntPred::Ult, a_low, b_low)
410    };
411    let carry = ahead(func, inst, Opcode::ZExt, &[carried]);
412    let high = ahead(func, inst, opcode, &[a_high, b_high]);
413    let high = ahead(func, inst, opcode, &[high, carry]);
414    replace(func, halves, inst, low, high);
415}
416
417/// A multiply, which is long multiplication in base two to the sixty fourth with everything that
418/// lands above the width thrown away.
419///
420/// The low half of the answer is the low halves multiplied together. The high half is what that
421/// multiply carried out of its own top, plus the two cross products, each of which starts at bit
422/// sixty four. The fourth partial product is the two high halves against each other and it starts
423/// at bit one hundred and twenty eight, so the whole of it is above the width and it is never
424/// worked out, which is why a wide multiply is three multiplies and not four.
425///
426/// Nothing here asks whether the operands are signed, because the low hundred and twenty eight bits
427/// of a product are the same bits either way. The sign only matters to the bits that are being
428/// thrown away.
429///
430/// The carry out of the low halves is the high half of a sixty four bit product, which this machine
431/// has an instruction for and this compiler has no way to ask for. [`crate::expand`] already writes
432/// that out as long multiplication one level further down, for the overflow builtins, so this calls
433/// it rather than keeping a second copy of the same arithmetic. It is the expensive part of a wide
434/// multiply by a long way, and `tamnd/rucc#309` is the rule that would make it one instruction for
435/// both callers at once.
436fn multiply(func: &mut Func, halves: &mut Halves, inst: Inst) {
437    let args = func[func[inst].args].to_vec();
438    let [a, b] = args[..] else { return };
439    let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
440        return;
441    };
442    let low = ahead(func, inst, Opcode::Mul, &[a_low, b_low]);
443    let carried = expand::high_half(func, inst, a_low, b_low, false, half());
444    let cross = ahead(func, inst, Opcode::Mul, &[a_low, b_high]);
445    let other = ahead(func, inst, Opcode::Mul, &[a_high, b_low]);
446    let high = ahead(func, inst, Opcode::Add, &[carried, cross]);
447    let high = ahead(func, inst, Opcode::Add, &[high, other]);
448    replace(func, halves, inst, low, high);
449}
450
451/// A shift, as each half shifted by the count with the bits that crossed between them put back, and
452/// a second answer for a count that reached a whole half.
453///
454/// A count below sixty four moves each half by the count, and the bits that left one half are the
455/// ones that arrive in the other. A count of sixty four or more empties one half completely, and
456/// what lands in the other is the first half moved by the count less sixty four. Taking the sixty
457/// four bit off a count in range is the same as subtracting sixty four from it, so both cases shift
458/// by the same number of places and differ only in which value ends up where, which means one shift
459/// each and a choice rather than two of everything. The choice is a `select` and not a branch, for
460/// the reason [`choose`] gives.
461///
462/// The bits that cross move the other way by sixty four less the count. That is a shift of sixty
463/// four places when the count is zero, which is not a distance this width has. Moving one place and
464/// then sixty three less the count is the same distance for every count from one to sixty three,
465/// and for a count of zero it shifts a value whose top bit is already gone all the way down to
466/// nothing, which is the right answer: a half that did not move carries nothing into the other one.
467///
468/// A count of a hundred and twenty eight or more is undefined in C and nothing here goes out of its
469/// way about it, the same as at every other width.
470fn shifted(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
471    let args = func[func[inst].args].to_vec();
472    let [a, b] = args[..] else { return };
473    let (Some(&(a_low, a_high)), Some(&(count, _))) = (halves.get(&a), halves.get(&b)) else {
474        return;
475    };
476    let top = ahead_const(func, inst, i128::from(HALF - 1));
477    let places = ahead(func, inst, Opcode::And, &[count, top]);
478    let back = ahead(func, inst, Opcode::Sub, &[top, places]);
479    let one = ahead_const(func, inst, 1);
480    let zero = ahead_const(func, inst, 0);
481    let bit = ahead_const(func, inst, i128::from(HALF));
482    let reach = ahead(func, inst, Opcode::And, &[count, bit]);
483    let whole = compared(func, inst, IntPred::Ne, reach, zero);
484
485    let (low, high) = if opcode == Opcode::Shl {
486        let moved = ahead(func, inst, Opcode::Shl, &[a_low, places]);
487        let edge = ahead(func, inst, Opcode::LShr, &[a_low, one]);
488        let across = ahead(func, inst, Opcode::LShr, &[edge, back]);
489        let above = ahead(func, inst, Opcode::Shl, &[a_high, places]);
490        let joined = ahead(func, inst, Opcode::Or, &[above, across]);
491        let low = ahead(func, inst, Opcode::Select, &[whole, zero, moved]);
492        let high = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
493        (low, high)
494    } else {
495        let moved = ahead(func, inst, opcode, &[a_high, places]);
496        let edge = ahead(func, inst, Opcode::Shl, &[a_high, one]);
497        let across = ahead(func, inst, Opcode::Shl, &[edge, back]);
498        let below = ahead(func, inst, Opcode::LShr, &[a_low, places]);
499        let joined = ahead(func, inst, Opcode::Or, &[below, across]);
500        // What is left behind when the whole low half is gone: zeroes for a logical shift, and for
501        // an arithmetic one the sign bit spread over the half it came from.
502        let spent = if opcode == Opcode::AShr {
503            ahead(func, inst, Opcode::AShr, &[a_high, top])
504        } else {
505            zero
506        };
507        let low = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
508        let high = ahead(func, inst, Opcode::Select, &[whole, spent, moved]);
509        (low, high)
510    };
511    replace(func, halves, inst, low, high);
512}
513
514/// An `and`, an `or` or an `xor`, which is the same operation on each half and nothing between
515/// them.
516fn bitwise(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
517    let args = func[func[inst].args].to_vec();
518    let [a, b] = args[..] else { return };
519    let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
520        return;
521    };
522    let low = ahead(func, inst, opcode, &[a_low, b_low]);
523    let high = ahead(func, inst, opcode, &[a_high, b_high]);
524    replace(func, halves, inst, low, high);
525}
526
527/// A comparison, which produces one bit and so is pointed at its answer rather than halved.
528///
529/// An equality is the two halves differing in neither place, which is one `or` over two `xor`s
530/// against zero and is shorter than comparing twice and combining. An ordering is the high halves
531/// settling it outright, or the low halves settling it when the high halves are equal, and the low
532/// halves are compared without a sign because the low half of a signed number is unsigned whatever
533/// the number is.
534///
535/// The high halves are asked a strict question even when the predicate is not strict. A predicate
536/// that lets the two be equal is true of two equal high halves whatever the low halves say, and
537/// what decides it there is the low halves, so `a >= b` is `a.hi > b.hi` or the high halves being
538/// equal and `a.lo >= b.lo` unsigned. Asking `a.hi >= b.hi` instead makes every value with a high
539/// half of its own greater than or equal to every other, which is the shape of this that a
540/// differential run against GCC caught.
541fn compare(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
542    let Extra::IntPred(pred) = func[inst].extra else { return };
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 answer = if matches!(pred, IntPred::Eq | IntPred::Ne) {
549        let low = ahead(func, inst, Opcode::Xor, &[a_low, b_low]);
550        let high = ahead(func, inst, Opcode::Xor, &[a_high, b_high]);
551        let both = ahead(func, inst, Opcode::Or, &[low, high]);
552        let zero = ahead_const(func, inst, 0);
553        compared(func, inst, pred, both, zero)
554    } else {
555        let above = compared(func, inst, strict(pred), a_high, b_high);
556        let below = compared(func, inst, unsigned(pred), a_low, b_low);
557        let same = compared(func, inst, IntPred::Eq, a_high, b_high);
558        let tail = bit(func, inst, Opcode::And, same, below);
559        bit(func, inst, Opcode::Or, above, tail)
560    };
561    if let Some(result) = func[inst].first_result {
562        forward.insert(result, answer);
563    }
564    func.remove_inst(inst);
565}
566
567/// The same ordering with the equal case taken out of it, which is what the high halves are asked.
568fn strict(pred: IntPred) -> IntPred {
569    match pred {
570        IntPred::Sle => IntPred::Slt,
571        IntPred::Sge => IntPred::Sgt,
572        IntPred::Ule => IntPred::Ult,
573        IntPred::Uge => IntPred::Ugt,
574        other => other,
575    }
576}
577
578/// The same ordering with no sign in it, which is how the low halves of two signed numbers compare.
579fn unsigned(pred: IntPred) -> IntPred {
580    match pred {
581        IntPred::Slt => IntPred::Ult,
582        IntPred::Sle => IntPred::Ule,
583        IntPred::Sgt => IntPred::Ugt,
584        IntPred::Sge => IntPred::Uge,
585        other => other,
586    }
587}
588
589/// A choice between two wide values, which is the same choice made on each half.
590///
591/// Two of them rather than one, with the condition read twice. What that costs is one more
592/// conditional move, and what the alternative costs is a branch, which is the more expensive of the
593/// two on anything that predicts.
594fn choose(func: &mut Func, halves: &mut Halves, inst: Inst) {
595    let args = func[func[inst].args].to_vec();
596    let [cond, then, other] = args[..] else { return };
597    let (Some(&(then_low, then_high)), Some(&(other_low, other_high))) =
598        (halves.get(&then), halves.get(&other))
599    else {
600        return;
601    };
602    let low = ahead(func, inst, Opcode::Select, &[cond, then_low, other_low]);
603    let high = ahead(func, inst, Opcode::Select, &[cond, then_high, other_high]);
604    replace(func, halves, inst, low, high);
605}
606
607/// Keeping the low bits of a wide value, which is the low half and then whatever is left to do.
608///
609/// Down to sixty four there is nothing left to do and the low half is the answer, so the truncation
610/// goes and its readers read the half. Down to anything narrower the machine's own truncation still
611/// happens, out of the half rather than out of the value that is no longer there.
612fn truncate(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
613    let Some(&arg) = func[func[inst].args].first() else { return };
614    let Some(&(low, _)) = halves.get(&arg) else { return };
615    let Some(result) = func[inst].first_result else { return };
616    if func[result].ty.bits() == HALF {
617        forward.insert(result, low);
618        func.remove_inst(inst);
619        return;
620    }
621    becomes(func, inst, Opcode::Trunc, &[low]);
622}
623
624/// Widening into a wide value, which is the value in the low half and its own sign or zero above.
625fn extend(func: &mut Func, halves: &mut Halves, inst: Inst, signed: bool) {
626    let Some(&arg) = func[func[inst].args].first() else { return };
627    let low = if func[arg].ty.bits() == HALF {
628        arg
629    } else {
630        let opcode = if signed { Opcode::SExt } else { Opcode::ZExt };
631        ahead(func, inst, opcode, &[arg])
632    };
633    let high = if signed {
634        let top = ahead_const(func, inst, i128::from(HALF - 1));
635        ahead(func, inst, Opcode::AShr, &[low, top])
636    } else {
637        ahead_const(func, inst, 0)
638    };
639    replace(func, halves, inst, low, high);
640}
641
642/// A call, as a call passing and receiving halves.
643///
644/// The instruction is made again rather than edited, because how many values a call gives back is
645/// settled when it is created and a wide return value is two where it was one. Its signature is
646/// made again for the same reason, since the signature is what each end of the call lays itself out
647/// against and both ends are split the same way.
648fn call(func: &mut Func, halves: &mut Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
649    let data = func[inst];
650    let Extra::Call(info) = data.extra else { return };
651    let info = func[info];
652    let args = spread(&func[data.args], halves);
653    let results: Vec<Type> = data
654        .results()
655        .map(|value| func[value].ty)
656        .flat_map(|ty| if is_wide(ty) { vec![half(), half()] } else { vec![ty] })
657        .collect();
658    let signature = func.add_signature(split_signature(&func[info.signature]));
659    let extra = Extra::Call(func.add_call(CallInfo { signature, ..info }));
660    let args = func.push_values(&args);
661    let span = func.span(inst);
662    let made = func.create_inst(InstData { args, extra, ..data }, &results, span);
663    func.insert_before(made, inst);
664    let mut fresh = func[made].results();
665    for old in data.results() {
666        if is_wide(func[old].ty) {
667            let (Some(low), Some(high)) = (fresh.next(), fresh.next()) else { return };
668            halves.insert(old, (low, high));
669        } else if let Some(again) = fresh.next() {
670            forward.insert(old, again);
671        }
672    }
673    func.remove_inst(inst);
674}
675
676/// A `return`, whose operands are the values the signature says and so are halves now.
677fn flatten(func: &mut Func, halves: &Halves, inst: Inst) {
678    let args = spread(&func[func[inst].args], halves);
679    func[inst].args = func.push_values(&args);
680}
681
682/// A branch, whose arguments hang on the edge rather than on the instruction.
683fn edges(func: &mut Func, halves: &Halves, inst: Inst) {
684    for at in func.target_list(inst).iter() {
685        let call = func[at];
686        let args = func[call.args].to_vec();
687        if !args.iter().any(|value| halves.contains_key(value)) {
688            continue;
689        }
690        let args = func.push_values(&spread(&args, halves));
691        func.set_block_call(at, BlockCall { block: call.block, args });
692    }
693}
694
695/// A list of values with each wide one replaced by its two halves in the same position.
696fn spread(args: &[Value], halves: &Halves) -> Vec<Value> {
697    args.iter()
698        .flat_map(|value| match halves.get(value) {
699            Some(&(low, high)) => vec![low, high],
700            None => vec![*value],
701        })
702        .collect()
703}
704
705/// One signature with every wide parameter and return value as two halves in its place.
706///
707/// Each half is plain. What the ABI asks beyond a type is about the bits above a narrow value and
708/// about an object whose address travels, and a half is neither: it is exactly a register wide and
709/// it is the value itself.
710fn split_signature(signature: &Signature) -> Signature {
711    let split = |params: &[Param]| -> Vec<Param> {
712        params
713            .iter()
714            .flat_map(|param| {
715                if is_wide(param.ty) {
716                    vec![Param::new(half()), Param::new(half())]
717                } else {
718                    vec![*param]
719                }
720            })
721            .collect()
722    };
723    Signature {
724        params: split(&signature.params),
725        returns: split(&signature.returns),
726        variadic: signature.variadic,
727    }
728}
729
730/// Records the two halves an instruction became and takes the instruction out.
731fn replace(func: &mut Func, halves: &mut Halves, inst: Inst, low: Value, high: Value) {
732    if let Some(result) = func[inst].first_result {
733        halves.insert(result, (low, high));
734    }
735    func.remove_inst(inst);
736}
737
738/// Points every reader of a value this pass replaced at what replaced it.
739///
740/// The arguments of each instruction and the arguments of the blocks it branches to, which between
741/// them are everywhere a value can be read. Nothing chases, because every value this map answers
742/// with is one made here and so is never itself a key.
743fn substitute(func: &mut Func, forward: &HashMap<Value, Value>) {
744    if forward.is_empty() {
745        return;
746    }
747    let with = |value: Value| forward.get(&value).copied().unwrap_or(value);
748    for block in func.blocks().collect::<Vec<_>>() {
749        for inst in func.insts(block).collect::<Vec<Inst>>() {
750            let args = func[inst].args;
751            func.rewrite(args, with);
752            for call in func.successors(inst).collect::<Vec<_>>() {
753                func.rewrite(call.args, with);
754            }
755        }
756    }
757}
758
759/// The access one word of a wide access is, that many bytes into it.
760fn word(info: MemInfo, at: u64) -> MemInfo {
761    let align = if at == 0 { info.align } else { info.align.min(8) };
762    MemInfo { size: STEP, align, ..info }
763}
764
765/// The address one word past another, written in front of an instruction.
766fn stepped(func: &mut Func, inst: Inst, from: Value) -> Value {
767    let step = ahead_const(func, inst, i128::from(STEP));
768    let args = func.push_values(&[from, step]);
769    written(func, inst, InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR)
770}
771
772/// A load put in front of an instruction, and the half it reads.
773fn read(func: &mut Func, inst: Inst, from: Value, info: MemInfo, flags: Flags) -> Value {
774    let extra = Extra::Mem(func.add_mem(info));
775    let args = func.push_values(&[from]);
776    let data = InstData { args, flags, extra, ..InstData::new(Opcode::Load) };
777    written(func, inst, data, half())
778}
779
780/// A store put in front of an instruction, which produces nothing and is only its effect.
781fn write(func: &mut Func, inst: Inst, value: Value, into: Value, info: MemInfo, flags: Flags) {
782    let span = func.span(inst);
783    let extra = Extra::Mem(func.add_mem(info));
784    let args = func.push_values(&[value, into]);
785    let data = InstData { args, flags, extra, ..InstData::new(Opcode::Store) };
786    let made = func.create_inst(data, &[], span);
787    func.insert_before(made, inst);
788}
789
790/// A comparison written in front of an instruction, which carries its predicate where everything
791/// else carries nothing.
792fn compared(func: &mut Func, inst: Inst, pred: IntPred, lhs: Value, rhs: Value) -> Value {
793    let args = func.push_values(&[lhs, rhs]);
794    let extra = Extra::IntPred(pred);
795    written(func, inst, InstData { args, extra, ..InstData::new(Opcode::ICmp) }, Type::I1)
796}
797
798/// An `and` or an `or` over two truth values, which is the same instruction at the width of one.
799fn bit(func: &mut Func, inst: Inst, opcode: Opcode, lhs: Value, rhs: Value) -> Value {
800    let args = func.push_values(&[lhs, rhs]);
801    written(func, inst, InstData { args, ..InstData::new(opcode) }, Type::I1)
802}
803
804/// An instruction over these operands put in front of another one, producing a half.
805fn ahead(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) -> Value {
806    let args = func.push_values(args);
807    written(func, inst, InstData { args, ..InstData::new(opcode) }, half())
808}
809
810/// A constant half put in front of an instruction.
811fn ahead_const(func: &mut Func, inst: Inst, value: i128) -> Value {
812    let extra = Extra::Imm(func.add_imm(Imm::int(value, half())));
813    written(func, inst, InstData { extra, ..InstData::new(Opcode::IConst) }, half())
814}
815
816/// Creates the instruction, puts it in front of another, and reads its value back out.
817fn written(func: &mut Func, inst: Inst, data: InstData, ty: Type) -> Value {
818    let span = func.span(inst);
819    let made = func.create_inst(data, &[ty], span);
820    func.insert_before(made, inst);
821    func[made].first_result.expect("an instruction created with one result has one")
822}
823
824/// Turns an instruction into a different one over different operands, in place.
825fn becomes(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) {
826    let args = func.push_values(args);
827    let data = &mut func[inst];
828    data.opcode = opcode;
829    data.args = args;
830    data.extra = Extra::None;
831    data.flags = data.flags.intersection(Flags::legal_on(opcode));
832}
833
834#[cfg(test)]
835mod tests {
836    use rucc_base::Interner;
837    use rucc_ir::{
838        Block, Builder, Flags, Func, MemOrder, Module, Restrict, Signature, Type, Value,
839    };
840    use rucc_target::x86_64::SYSV;
841    use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
842
843    use super::{HALF, IntPred, MemInfo, Opcode, halves};
844
845    /// The width the pass is about, as a type, which is what every test builds with.
846    fn wide() -> Type {
847        Type::int(super::WIDE)
848    }
849
850    fn target() -> TargetInfo {
851        TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
852    }
853
854    fn printed(func: &Func, names: &mut Interner) -> String {
855        let module = Module::new(names.intern("w.c"), &target());
856        rucc_ir::print_func(&module, func, names)
857    }
858
859    /// A function of those parameters returning that, with its entry block and its parameters.
860    fn shell(names: &mut Interner, params: &[Type], returns: &[Type]) -> (Func, Block, Vec<Value>) {
861        let signature = Signature::new().with_params(params).with_returns(returns);
862        let mut func = Func::new(names.intern("f"), signature);
863        let entry = func.create_block();
864        let values = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
865        (func, entry, values)
866    }
867
868    /// An ordinary access of that many bytes, aligned that far.
869    fn info(size: u64, align: u32) -> MemInfo {
870        MemInfo {
871            size,
872            align,
873            order: MemOrder::NotAtomic,
874            tbaa: None,
875            owns: 0,
876            restrict: Restrict::NONE,
877        }
878    }
879
880    #[test]
881    fn an_add_carries_from_the_low_half_into_the_high_one() {
882        let mut names = Interner::new();
883        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
884        let mut build = Builder::new(&mut func, entry);
885        let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
886        build.ret(&[sum]);
887
888        assert!(halves(&mut func, &SYSV), "there is a width to split");
889        let text = printed(&func, &mut names);
890        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
891        // Two adds for the halves, one more for the carry, and the carry itself is the unsigned
892        // comparison that says the low half wrapped.
893        assert_eq!(text.matches(" = add ").count(), 3, "three adds: {text}");
894        assert_eq!(text.matches("icmp ult").count(), 1, "one carry: {text}");
895        assert_eq!(text.matches(" = zext.i64 ").count(), 1, "the carry as a number: {text}");
896    }
897
898    #[test]
899    fn a_subtract_borrows_the_other_way_round() {
900        let mut names = Interner::new();
901        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
902        let mut build = Builder::new(&mut func, entry);
903        let difference = build.binary(Opcode::Sub, params[0], params[1], Flags::NONE);
904        build.ret(&[difference]);
905
906        assert!(halves(&mut func, &SYSV), "there is a width to split");
907        let text = printed(&func, &mut names);
908        assert_eq!(text.matches(" = sub ").count(), 3, "three subtracts: {text}");
909        // The borrow is the operands compared, not the answer, which is what tells a reader the
910        // two directions were thought about separately.
911        assert!(text.contains("icmp ult %0, %2"), "the operands are compared: {text}");
912    }
913
914    #[test]
915    fn the_signature_and_the_entry_block_say_the_same_thing() {
916        let mut names = Interner::new();
917        let (mut func, entry, params) = shell(&mut names, &[Type::int(32), wide()], &[wide()]);
918        let mut build = Builder::new(&mut func, entry);
919        build.ret(&[params[1]]);
920
921        assert!(halves(&mut func, &SYSV), "there is a width to split");
922        assert_eq!(
923            func.signature().param_types().collect::<Vec<_>>(),
924            [Type::int(32), Type::int(HALF), Type::int(HALF)],
925            "the wide parameter became two where it stood"
926        );
927        assert_eq!(
928            func.signature().return_types().collect::<Vec<_>>(),
929            [Type::int(HALF), Type::int(HALF)],
930            "and so did what comes back"
931        );
932        let text = printed(&func, &mut names);
933        assert!(text.contains("block0(%0: i32, %1: i64, %2: i64)"), "the block agrees: {text}");
934        assert!(text.contains("return %1, %2"), "both halves go back: {text}");
935        let _ = entry;
936    }
937
938    #[test]
939    fn a_read_takes_the_high_word_a_word_above_the_low_one() {
940        let mut names = Interner::new();
941        let (mut func, entry, params) = shell(&mut names, &[Type::PTR], &[wide()]);
942        let mut build = Builder::new(&mut func, entry);
943        let value = build.load(wide(), params[0], info(16, 16), Flags::NONE);
944        build.ret(&[value]);
945
946        assert!(halves(&mut func, &SYSV), "there is a width to split");
947        let text = printed(&func, &mut names);
948        assert_eq!(text.matches(" = load.i64 ").count(), 2, "two reads: {text}");
949        assert!(text.contains("ptr_add"), "the high word is a word up: {text}");
950        // The object is aligned to sixteen and its high word is not, which is the one thing
951        // splitting an access can get wrong quietly.
952        assert!(text.contains("align 16"), "the low word keeps what the object had: {text}");
953        assert!(text.contains("align 8"), "the high word knows less: {text}");
954    }
955
956    #[test]
957    fn an_equality_asks_once_about_both_halves() {
958        let mut names = Interner::new();
959        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
960        let mut build = Builder::new(&mut func, entry);
961        let same = build.icmp(IntPred::Eq, params[0], params[1]);
962        let answer = build.unary(Opcode::ZExt, same, Type::int(32));
963        build.ret(&[answer]);
964
965        assert!(halves(&mut func, &SYSV), "there is a width to split");
966        let text = printed(&func, &mut names);
967        assert_eq!(text.matches("icmp").count(), 1, "one comparison: {text}");
968        assert_eq!(text.matches(" = xor ").count(), 2, "the halves differ or they do not: {text}");
969    }
970
971    #[test]
972    fn an_ordering_reads_the_low_halves_without_a_sign() {
973        let mut names = Interner::new();
974        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
975        let mut build = Builder::new(&mut func, entry);
976        let below = build.icmp(IntPred::Slt, params[0], params[1]);
977        let answer = build.unary(Opcode::ZExt, below, Type::int(32));
978        build.ret(&[answer]);
979
980        assert!(halves(&mut func, &SYSV), "there is a width to split");
981        let text = printed(&func, &mut names);
982        assert!(text.contains("icmp slt"), "the high halves keep the sign: {text}");
983        assert!(text.contains("icmp ult"), "the low halves have none: {text}");
984        assert!(
985            text.contains("icmp eq"),
986            "and the low halves only matter when the high tie: {text}"
987        );
988    }
989
990    /// An ordering that allows the two to be equal still asks the high halves a strict question.
991    ///
992    /// Two values whose high halves are equal are ordered by their low halves alone, and a high
993    /// half that is greater than or equal to the other says nothing about that. Asking the high
994    /// halves the predicate as it stands makes every ordering that is not strict answer yes on a
995    /// tie, which is the mistake a run against GCC caught.
996    #[test]
997    fn an_ordering_that_allows_equality_asks_the_high_halves_a_strict_question() {
998        let mut names = Interner::new();
999        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1000        let mut build = Builder::new(&mut func, entry);
1001        let at_least = build.icmp(IntPred::Sge, params[0], params[1]);
1002        let answer = build.unary(Opcode::ZExt, at_least, Type::int(32));
1003        build.ret(&[answer]);
1004
1005        assert!(halves(&mut func, &SYSV), "there is a width to split");
1006        let text = printed(&func, &mut names);
1007        assert!(text.contains("icmp sgt"), "the high halves settle it outright: {text}");
1008        assert!(!text.contains("icmp sge"), "a tie in the high halves settles nothing: {text}");
1009        assert!(text.contains("icmp uge"), "the low halves are the ones allowed to tie: {text}");
1010    }
1011
1012    #[test]
1013    fn a_widening_puts_the_sign_of_the_value_in_the_high_half() {
1014        let mut names = Interner::new();
1015        let (mut func, entry, params) = shell(&mut names, &[Type::int(32)], &[wide()]);
1016        let mut build = Builder::new(&mut func, entry);
1017        let value = build.unary(Opcode::SExt, params[0], wide());
1018        build.ret(&[value]);
1019
1020        assert!(halves(&mut func, &SYSV), "there is a width to split");
1021        let text = printed(&func, &mut names);
1022        assert!(text.contains("sext.i64"), "the value fills the low half: {text}");
1023        assert!(text.contains("ashr"), "and its sign fills the high one: {text}");
1024    }
1025
1026    #[test]
1027    fn a_block_parameter_becomes_two_and_every_branch_passes_two() {
1028        let mut names = Interner::new();
1029        let (mut func, entry, params) = shell(&mut names, &[wide(), Type::int(32)], &[wide()]);
1030        let tail = func.create_block();
1031        let carried = func.append_param(tail, wide());
1032        let mut build = Builder::new(&mut func, entry);
1033        let zero = build.iconst(Type::int(32), 0);
1034        let taken = build.icmp(IntPred::Ne, params[1], zero);
1035        let other = build.iconst(wide(), 7);
1036        build.br_if(taken, tail, &[params[0]], tail, &[other]);
1037        let mut build = Builder::new(&mut func, tail);
1038        build.ret(&[carried]);
1039
1040        assert!(halves(&mut func, &SYSV), "there is a width to split");
1041        let text = printed(&func, &mut names);
1042        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1043        assert!(text.contains("block1(%7: i64, %8: i64)"), "the block takes two: {text}");
1044        assert_eq!(text.matches("block1(").count(), 3, "and both edges pass two: {text}");
1045    }
1046
1047    /// A multiply is the low halves, the two cross products, and nothing for the fourth corner.
1048    ///
1049    /// Three at the top, and four more inside the carry out of the low halves, which is a product
1050    /// at half the width again worked out the same way. What the count is really saying is that the
1051    /// two high halves are never multiplied together, because the whole of that partial product
1052    /// lands above the width.
1053    #[test]
1054    fn a_multiply_is_three_multiplies_and_the_carry_out_of_the_low_ones() {
1055        let mut names = Interner::new();
1056        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1057        let mut build = Builder::new(&mut func, entry);
1058        let product = build.binary(Opcode::Mul, params[0], params[1], Flags::NONE);
1059        build.ret(&[product]);
1060
1061        assert!(halves(&mut func, &SYSV), "there is a width to split");
1062        let text = printed(&func, &mut names);
1063        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1064        assert_eq!(text.matches(" = mul ").count(), 7, "three and the carry's four: {text}");
1065    }
1066
1067    /// A shift left moves each half and chooses between the count having crossed a half and not.
1068    ///
1069    /// Two shifts left, one per half, and the low one does for both cases: a count that reached a
1070    /// whole half puts exactly that value in the high half and nothing in the low one, so the only
1071    /// thing the far case needs is the shift the near case already did. Two right shifts carry the
1072    /// crossing bits, two selects pick a half each, and there is no branch anywhere.
1073    #[test]
1074    fn a_shift_left_chooses_between_a_count_that_crossed_a_half_and_one_that_did_not() {
1075        let mut names = Interner::new();
1076        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1077        let mut build = Builder::new(&mut func, entry);
1078        let moved = build.binary(Opcode::Shl, params[0], params[1], Flags::NONE);
1079        build.ret(&[moved]);
1080
1081        assert!(halves(&mut func, &SYSV), "there is a width to split");
1082        let text = printed(&func, &mut names);
1083        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1084        assert_eq!(
1085            text.matches(" = shl ").count(),
1086            2,
1087            "one per half, and the far case reuses one: {text}"
1088        );
1089        assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1090        assert_eq!(text.matches(" = lshr ").count(), 2, "the crossing bits, in two steps: {text}");
1091    }
1092
1093    /// The bits that cross move one place and then the rest, so a count of zero carries nothing.
1094    ///
1095    /// Sixty four less a count of zero is sixty four, which is not a distance a sixty four bit shift
1096    /// has. One place first and sixty three less the count after is the same distance everywhere the
1097    /// question is asked, and for a count of zero it moves a value whose top bit has already gone
1098    /// all the way out, which leaves the zero a half that did not move should carry.
1099    #[test]
1100    fn the_bits_that_cross_move_one_place_and_then_the_rest_of_the_way() {
1101        let mut names = Interner::new();
1102        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1103        let mut build = Builder::new(&mut func, entry);
1104        let moved = build.binary(Opcode::LShr, params[0], params[1], Flags::NONE);
1105        build.ret(&[moved]);
1106
1107        assert!(halves(&mut func, &SYSV), "there is a width to split");
1108        let text = printed(&func, &mut names);
1109        assert!(text.contains("iconst.i64 63"), "sixty three is the distance left: {text}");
1110        assert!(text.contains("iconst.i64 1"), "after the one place that comes first: {text}");
1111        assert!(text.contains(" = sub "), "the rest of the way is worked out: {text}");
1112        assert!(
1113            !text.contains("iconst.i64 127"),
1114            "and the count is not masked to the width: {text}"
1115        );
1116    }
1117
1118    /// An arithmetic shift right leaves the sign bit behind where a logical one leaves zeroes.
1119    ///
1120    /// What the two differ in is only the half the count moved out of entirely. A logical shift puts
1121    /// zeroes there, which is a constant already in hand, and an arithmetic one puts the sign bit
1122    /// spread across the half it came from, which is one more shift.
1123    #[test]
1124    fn an_arithmetic_shift_right_leaves_the_sign_bit_where_a_logical_one_leaves_zeroes() {
1125        let mut names = Interner::new();
1126        let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1127        let mut build = Builder::new(&mut func, entry);
1128        let moved = build.binary(Opcode::AShr, params[0], params[1], Flags::NONE);
1129        build.ret(&[moved]);
1130
1131        assert!(halves(&mut func, &SYSV), "there is a width to split");
1132        let text = printed(&func, &mut names);
1133        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1134        // The high half by the count, and the high half by sixty three for the half left empty.
1135        assert_eq!(text.matches(" = ashr ").count(), 2, "the count and the sign: {text}");
1136        assert_eq!(text.matches(" = lshr ").count(), 1, "the low half is not signed: {text}");
1137        assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1138    }
1139
1140    #[test]
1141    fn a_parameter_with_one_register_left_leaves_the_function_alone() {
1142        let mut names = Interner::new();
1143        let word = Type::int(HALF);
1144        // Five words take five of the six argument registers, so the halves of the sixth
1145        // parameter would be one register and one word of the caller's stack, which is not where
1146        // the convention puts a value this wide.
1147        let params = [word, word, word, word, word, wide()];
1148        let (mut func, entry, values) = shell(&mut names, &params, &[word]);
1149        let mut build = Builder::new(&mut func, entry);
1150        let low = build.unary(Opcode::Trunc, values[5], word);
1151        build.ret(&[low]);
1152        let before = printed(&func, &mut names);
1153
1154        assert!(!halves(&mut func, &SYSV), "one of the halves has no register");
1155        assert_eq!(printed(&func, &mut names), before, "so nothing moved");
1156    }
1157
1158    /// The order the function holds its blocks in is not the order they run in.
1159    ///
1160    /// This is what the optimizer produced and `-O0` did not. A block that runs early is made late
1161    /// by whichever pass needed it, so the list the function keeps had a use of a wide value in it
1162    /// before the instruction defining that value, and the walk that asks whether every definition
1163    /// comes first said no and left the whole function alone. Then the selector met an instruction
1164    /// at a width it has no register for and refused the program. The blocks here are made in the
1165    /// order that produces, which is the tail before the middle. tamnd/rucc#1054.
1166    #[test]
1167    fn a_block_made_after_the_one_it_runs_before_is_still_split() {
1168        let mut names = Interner::new();
1169        let (mut func, entry, params) = shell(&mut names, &[wide()], &[wide()]);
1170        let tail = func.create_block();
1171        let middle = func.create_block();
1172        let mut build = Builder::new(&mut func, entry);
1173        build.jump(middle, &[]);
1174        let mut build = Builder::new(&mut func, middle);
1175        let doubled = build.binary(Opcode::Add, params[0], params[0], Flags::NONE);
1176        build.jump(tail, &[]);
1177        let mut build = Builder::new(&mut func, tail);
1178        let again = build.binary(Opcode::Add, doubled, doubled, Flags::NONE);
1179        build.ret(&[again]);
1180
1181        assert!(
1182            halves(&mut func, &SYSV),
1183            "the definition runs before the use whatever the list says"
1184        );
1185        let text = printed(&func, &mut names);
1186        assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1187    }
1188
1189    #[test]
1190    fn a_function_with_nothing_that_wide_is_not_touched() {
1191        let mut names = Interner::new();
1192        let word = Type::int(HALF);
1193        let (mut func, entry, params) = shell(&mut names, &[word, word], &[word]);
1194        let mut build = Builder::new(&mut func, entry);
1195        let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1196        build.ret(&[sum]);
1197
1198        assert!(!halves(&mut func, &SYSV), "there is nothing to split");
1199    }
1200}