1use 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
75const WIDE: u32 = 128;
77
78const HALF: u32 = 64;
80
81const STEP: u64 = 8;
83
84fn is_wide(ty: Type) -> bool {
86 ty.is_int() && ty.is_scalar() && ty.bits() == WIDE
87}
88
89fn half() -> Type {
91 Type::int(HALF)
92}
93
94pub 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
133fn 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 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
176type Halves = HashMap<Value, (Value, Value)>;
178
179fn 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
225fn 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 if func.carries_mem(inst) {
243 return false;
244 }
245 if data.opcode == Opcode::SExt && reads.iter().any(|&value| func[value].ty.bits() < 8) {
249 return false;
250 }
251 if matches!(data.opcode, Opcode::SIToFP | Opcode::UIToFP | Opcode::FPToSI | Opcode::FPToUI)
256 && converted(func, inst).is_none()
257 {
258 return false;
259 }
260 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 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
278fn 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
302fn 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
315fn fits(signature: &Signature, conv: &CallRegs) -> bool {
327 let mut places = Places::new(conv);
328 for param in &signature.params {
329 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
352fn 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
376fn 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
423fn 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
434fn 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
450fn 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
464fn 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
491fn 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
525fn 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
561fn 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
591fn 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
616fn 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
626fn 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
636fn 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(¶ms).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
663fn 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 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
726fn 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
739fn 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
779fn 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
790fn 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
801fn 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
819fn 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
836fn 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
854fn 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
888fn 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
894fn 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
907fn 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
917fn 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
942fn 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
950fn 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
971fn 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
977fn 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
984fn 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
992fn 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
1002fn 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
1010fn 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
1016fn 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
1022fn 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
1028fn 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
1036fn 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 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 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 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 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 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 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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 #[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 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 let params = [word, word, word, word, word, wide()];
1523 let (mut func, entry, values) = shell(&mut names, ¶ms, &[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 #[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}