1use std::collections::{HashMap, HashSet};
67
68use rucc_base::Interner;
69use rucc_ir::{
70 Abi, Block, BlockCall, CallInfo, Def, Extra, Flags, Float, Func, Imm, Inst, InstData, IntPred,
71 MemInfo, Opcode, Param, Signature, Type, Value,
72};
73use rucc_target::{CallRegs, Places, Where};
74
75use crate::capability;
76use crate::expand;
77
78const WIDE: u32 = 128;
80
81const MODE: &str = "i128";
83
84const HALF: u32 = 64;
86
87const STEP: u64 = 8;
89
90fn is_wide(ty: Type) -> bool {
92 ty.is_int() && ty.is_scalar() && ty.bits() == WIDE
93}
94
95fn half() -> Type {
97 Type::int(HALF)
98}
99
100pub fn halves(func: &mut Func, names: &mut Interner, conv: &CallRegs) -> bool {
111 if !func.values().any(|value| is_wide(func[value].ty)) {
112 return false;
113 }
114 let insts: Vec<Inst> =
115 walk(func).into_iter().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
116 let order: HashMap<Inst, usize> =
117 insts.iter().enumerate().map(|(at, &inst)| (inst, at)).collect();
118 if !insts.iter().enumerate().all(|(at, &inst)| can_split(func, &order, at, inst)) {
119 return false;
120 }
121 if !func.signatures().all(|signature| fits(signature, conv)) {
122 return false;
123 }
124
125 let mut halves: Halves = HashMap::new();
126 let mut forward: HashMap<Value, Value> = HashMap::new();
127 for block in func.blocks().collect::<Vec<_>>() {
128 params(func, block, &mut halves, &mut forward);
129 }
130 for &inst in &insts {
131 rewrite(func, names, &mut halves, &mut forward, inst);
132 }
133 substitute(func, &forward);
134 let signature = split_signature(func.signature());
135 func.set_signature(signature);
136 true
137}
138
139fn walk(func: &Func) -> Vec<Block> {
157 let Some(entry) = func.entry() else { return func.blocks().collect() };
158 let mut seen: HashSet<Block> = HashSet::new();
159 let mut order: Vec<Block> = Vec::new();
160 let mut stack: Vec<(Block, bool)> = vec![(entry, false)];
163 seen.insert(entry);
164 while let Some((block, done)) = stack.pop() {
165 if done {
166 order.push(block);
167 continue;
168 }
169 stack.push((block, true));
170 let Some(term) = func.terminator(block) else { continue };
171 for call in func.successors(term) {
172 if seen.insert(call.block) {
173 stack.push((call.block, false));
174 }
175 }
176 }
177 order.reverse();
178 order.extend(func.blocks().filter(|block| !seen.contains(block)));
179 order
180}
181
182type Halves = HashMap<Value, (Value, Value)>;
184
185fn understood(opcode: Opcode) -> bool {
196 matches!(
197 opcode,
198 Opcode::IConst
199 | Opcode::Load
200 | Opcode::Store
201 | Opcode::Add
202 | Opcode::Sub
203 | Opcode::Mul
204 | Opcode::UDiv
205 | Opcode::SDiv
206 | Opcode::URem
207 | Opcode::SRem
208 | Opcode::Shl
209 | Opcode::LShr
210 | Opcode::AShr
211 | Opcode::And
212 | Opcode::Or
213 | Opcode::Xor
214 | Opcode::ICmp
215 | Opcode::Select
216 | Opcode::SIToFP
217 | Opcode::UIToFP
218 | Opcode::FPToSI
219 | Opcode::FPToUI
220 | Opcode::Trunc
221 | Opcode::SExt
222 | Opcode::ZExt
223 | Opcode::Call
224 | Opcode::CallIndirect
225 | Opcode::Return
226 | Opcode::Jump
227 | Opcode::BrIf
228 )
229}
230
231fn can_split(func: &Func, order: &HashMap<Inst, usize>, at: usize, inst: Inst) -> bool {
236 let data = func[inst];
237 let reads = operands(func, inst);
238 let wide = |&value: &Value| is_wide(func[value].ty);
239 if !reads.iter().any(wide) && !data.results().any(|value| is_wide(func[value].ty)) {
240 return true;
241 }
242 if !understood(data.opcode) {
243 return false;
244 }
245 if func.carries_mem(inst) {
249 return false;
250 }
251 if data.opcode == Opcode::SExt && reads.iter().any(|&value| func[value].ty.bits() < 8) {
255 return false;
256 }
257 if matches!(data.opcode, Opcode::SIToFP | Opcode::UIToFP | Opcode::FPToSI | Opcode::FPToUI)
262 && converted(func, inst).is_none()
263 {
264 return false;
265 }
266 if matches!(data.opcode, Opcode::Call | Opcode::CallIndirect) {
270 let Extra::Call(info) = data.extra else { return false };
271 if func[func[info].signature].variadic {
272 return false;
273 }
274 }
275 reads.iter().filter(|value| wide(value)).all(|&value| match func[value].def {
279 Def::Result { inst, .. } => order.get(&inst).is_some_and(|&def| def < at),
280 Def::Param { .. } => true,
281 })
282}
283
284fn converted(func: &Func, inst: Inst) -> Option<Float> {
291 let data = func[inst];
292 let mut floats = func[data.args]
293 .iter()
294 .copied()
295 .chain(data.results())
296 .map(|value| func[value].ty)
297 .filter(|ty| ty.is_float());
298 let only = floats.next()?;
299 if floats.next().is_some() {
300 return None;
301 }
302 match only.format() {
303 Some(format @ (Float::F32 | Float::F64 | Float::F128)) => Some(format),
304 _ => None,
305 }
306}
307
308fn operands(func: &Func, inst: Inst) -> Vec<Value> {
314 let mut reads = func[func[inst].args].to_vec();
315 for call in func.successors(inst).collect::<Vec<_>>() {
316 reads.extend_from_slice(&func[call.args]);
317 }
318 reads
319}
320
321fn fits(signature: &Signature, conv: &CallRegs) -> bool {
333 let mut places = Places::new(conv);
334 for param in &signature.params {
335 if let Abi::ByVal { size, align } = param.abi {
339 places.on_stack(u32::try_from(size).unwrap_or(u32::MAX), align);
340 } else if crate::abi::on_the_stack(param.ty) {
341 let (size, align) = crate::abi::X87_AREA;
342 places.on_stack(size, align);
343 } else if is_wide(param.ty) {
344 let low = places.integer();
345 let high = places.integer();
346 if !matches!((low, high), (Where::Reg(_), Where::Reg(_))) {
347 return false;
348 }
349 } else if param.ty.is_float() {
350 places.float(crate::abi::float_bytes(param.ty));
351 } else {
352 places.integer();
353 }
354 }
355 true
356}
357
358fn params(func: &mut Func, block: Block, halves: &mut Halves, forward: &mut HashMap<Value, Value>) {
365 let old: Vec<Value> = func[block].params.clone();
366 if !old.iter().any(|&value| is_wide(func[value].ty)) {
367 return;
368 }
369 for &value in &old {
370 if is_wide(func[value].ty) {
371 let low = func.append_param(block, half());
372 let high = func.append_param(block, half());
373 halves.insert(value, (low, high));
374 } else {
375 let again = func.append_param(block, func[value].ty);
376 forward.insert(value, again);
377 }
378 }
379 func.retain_params(block, |value| !old.contains(&value));
380}
381
382fn rewrite(
384 func: &mut Func,
385 names: &mut Interner,
386 halves: &mut Halves,
387 forward: &mut HashMap<Value, Value>,
388 inst: Inst,
389) {
390 let data = func[inst];
391 let produces = data.results().any(|value| is_wide(func[value].ty));
392 let takes = func[data.args].iter().any(|&value| is_wide(func[value].ty));
393 match data.opcode {
394 Opcode::IConst if produces => constant(func, halves, inst),
395 Opcode::Load if produces => load(func, halves, inst),
396 Opcode::Store if takes => store(func, halves, inst),
397 Opcode::Add | Opcode::Sub if produces => carried(func, halves, inst, data.opcode),
398 Opcode::Mul if produces => multiply(func, halves, inst),
399 Opcode::UDiv | Opcode::SDiv | Opcode::URem | Opcode::SRem if produces => {
400 divide(func, names, halves, inst, data.opcode);
401 }
402 Opcode::Shl | Opcode::LShr | Opcode::AShr if produces => {
403 shifted(func, halves, inst, data.opcode);
404 }
405 Opcode::And | Opcode::Or | Opcode::Xor if produces => {
406 bitwise(func, halves, inst, data.opcode);
407 }
408 Opcode::SIToFP | Opcode::UIToFP if takes => {
409 to_float(func, names, halves, forward, inst, data.opcode == Opcode::SIToFP);
410 }
411 Opcode::FPToSI | Opcode::FPToUI if produces => {
412 from_float(func, names, halves, inst, data.opcode == Opcode::FPToSI);
413 }
414 Opcode::ICmp if takes => compare(func, halves, forward, inst),
415 Opcode::Select if produces => choose(func, halves, inst),
416 Opcode::Trunc if takes => truncate(func, halves, forward, inst),
417 Opcode::SExt | Opcode::ZExt if produces => {
418 extend(func, halves, inst, data.opcode == Opcode::SExt);
419 }
420 Opcode::Call | Opcode::CallIndirect if produces || takes => {
421 call(func, halves, forward, inst);
422 }
423 Opcode::Return if takes => flatten(func, halves, inst),
424 Opcode::Jump | Opcode::BrIf => edges(func, halves, inst),
425 _ => {}
426 }
427}
428
429fn constant(func: &mut Func, halves: &mut Halves, inst: Inst) {
431 let Extra::Imm(imm) = func[inst].extra else { return };
432 let bits = func[imm].unsigned();
433 #[expect(clippy::cast_possible_truncation, reason = "the halves are what this is taking")]
434 let (low, high) = (bits as u64, (bits >> HALF) as u64);
435 let low = ahead_const(func, inst, i128::from(low));
436 let high = ahead_const(func, inst, i128::from(high));
437 replace(func, halves, inst, low, high);
438}
439
440fn load(func: &mut Func, halves: &mut Halves, inst: Inst) {
446 let data = func[inst];
447 let Extra::Mem(mem) = data.extra else { return };
448 let info = func[mem];
449 let Some(&from) = func[data.args].first() else { return };
450 let low = read(func, inst, from, word(info, 0), data.flags);
451 let up = stepped(func, inst, from);
452 let high = read(func, inst, up, word(info, STEP), data.flags);
453 replace(func, halves, inst, low, high);
454}
455
456fn store(func: &mut Func, halves: &mut Halves, inst: Inst) {
458 let data = func[inst];
459 let Extra::Mem(mem) = data.extra else { return };
460 let info = func[mem];
461 let args = func[data.args].to_vec();
462 let [value, into] = args[..] else { return };
463 let Some(&(low, high)) = halves.get(&value) else { return };
464 write(func, inst, low, into, word(info, 0), data.flags);
465 let up = stepped(func, inst, into);
466 write(func, inst, high, up, word(info, STEP), data.flags);
467 func.remove_inst(inst);
468}
469
470fn carried(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
480 let args = func[func[inst].args].to_vec();
481 let [a, b] = args[..] else { return };
482 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
483 return;
484 };
485 let low = ahead(func, inst, opcode, &[a_low, b_low]);
486 let carried = if opcode == Opcode::Add {
487 compared(func, inst, IntPred::Ult, low, a_low)
488 } else {
489 compared(func, inst, IntPred::Ult, a_low, b_low)
490 };
491 let carry = ahead(func, inst, Opcode::ZExt, &[carried]);
492 let high = ahead(func, inst, opcode, &[a_high, b_high]);
493 let high = ahead(func, inst, opcode, &[high, carry]);
494 replace(func, halves, inst, low, high);
495}
496
497fn multiply(func: &mut Func, halves: &mut Halves, inst: Inst) {
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::Mul, &[a_low, b_low]);
523 let carried = expand::high_half(func, inst, a_low, b_low, false, half());
524 let cross = ahead(func, inst, Opcode::Mul, &[a_low, b_high]);
525 let other = ahead(func, inst, Opcode::Mul, &[a_high, b_low]);
526 let high = ahead(func, inst, Opcode::Add, &[carried, cross]);
527 let high = ahead(func, inst, Opcode::Add, &[high, other]);
528 replace(func, halves, inst, low, high);
529}
530
531fn divide(func: &mut Func, names: &mut Interner, halves: &mut Halves, inst: Inst, opcode: Opcode) {
549 let args = func[func[inst].args].to_vec();
550 let [a, b] = args[..] else { return };
551 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
552 return;
553 };
554 let Some(routine) = capability::libcall(opcode, MODE) else { return };
558 let made =
559 runtime(func, names, inst, routine, &[a_low, a_high, b_low, b_high], &[half(), half()]);
560 let mut results = func[made].results();
561 let (Some(low), Some(high)) = (results.next(), results.next()) else { return };
562 replace(func, halves, inst, low, high);
563}
564
565fn to_float(
575 func: &mut Func,
576 names: &mut Interner,
577 halves: &Halves,
578 forward: &mut HashMap<Value, Value>,
579 inst: Inst,
580 signed: bool,
581) {
582 let Some(&arg) = func[func[inst].args].first() else { return };
583 let Some(&(low, high)) = halves.get(&arg) else { return };
584 let (Some(result), Some(format)) = (func[inst].first_result, converted(func, inst)) else {
585 return;
586 };
587 let routine = going_up(signed, format);
588 let made = runtime(func, names, inst, routine, &[low, high], &[func[result].ty]);
589 if let Some(answer) = func[made].first_result {
590 forward.insert(result, answer);
591 }
592 func.remove_inst(inst);
593}
594
595fn from_float(
605 func: &mut Func,
606 names: &mut Interner,
607 halves: &mut Halves,
608 inst: Inst,
609 signed: bool,
610) {
611 let Some(&arg) = func[func[inst].args].first() else { return };
612 let Some(format) = converted(func, inst) else { return };
613 let routine = coming_down(signed, format);
614 let made = runtime(func, names, inst, routine, &[arg], &[half(), half()]);
615 let mut results = func[made].results();
616 let (Some(low), Some(high)) = (results.next(), results.next()) else { return };
617 replace(func, halves, inst, low, high);
618}
619
620fn going_up(signed: bool, format: Float) -> &'static str {
626 let mode = match format {
627 Float::F32 => "i128.f32",
628 Float::F64 => "i128.f64",
629 _ => "i128.f128",
630 };
631 routine(if signed { Opcode::SIToFP } else { Opcode::UIToFP }, mode)
632}
633
634fn coming_down(signed: bool, format: Float) -> &'static str {
636 let mode = match format {
637 Float::F32 => "f32.i128",
638 Float::F64 => "f64.i128",
639 _ => "f128.i128",
640 };
641 routine(if signed { Opcode::FPToSI } else { Opcode::FPToUI }, mode)
642}
643
644fn routine(opcode: Opcode, mode: &str) -> &'static str {
649 capability::libcall(opcode, mode)
650 .unwrap_or_else(|| panic!("no routine for `{}` at `{mode}`", opcode.name()))
651}
652
653fn runtime(
660 func: &mut Func,
661 names: &mut Interner,
662 inst: Inst,
663 routine: &str,
664 args: &[Value],
665 results: &[Type],
666) -> Inst {
667 let params: Vec<Type> = args.iter().map(|&value| func[value].ty).collect();
668 let signature = func.add_signature(Signature::new().with_params(¶ms).with_returns(results));
669 let callee = Some(names.intern(routine));
670 let varargs = func.push_abis(&[]);
671 let extra = Extra::Call(func.add_call(CallInfo { callee, signature, varargs }));
672 let args = func.push_values(args);
673 let span = func.span(inst);
674 let data = InstData { args, extra, ..InstData::new(Opcode::Call) };
675 let made = func.create_inst(data, results, span);
676 func.insert_before(made, inst);
677 made
678}
679
680fn shifted(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
700 let args = func[func[inst].args].to_vec();
701 let [a, b] = args[..] else { return };
702 let (Some(&(a_low, a_high)), Some(&(count, _))) = (halves.get(&a), halves.get(&b)) else {
703 return;
704 };
705 let top = ahead_const(func, inst, i128::from(HALF - 1));
706 let places = ahead(func, inst, Opcode::And, &[count, top]);
707 let back = ahead(func, inst, Opcode::Sub, &[top, places]);
708 let one = ahead_const(func, inst, 1);
709 let zero = ahead_const(func, inst, 0);
710 let bit = ahead_const(func, inst, i128::from(HALF));
711 let reach = ahead(func, inst, Opcode::And, &[count, bit]);
712 let whole = compared(func, inst, IntPred::Ne, reach, zero);
713
714 let (low, high) = if opcode == Opcode::Shl {
715 let moved = ahead(func, inst, Opcode::Shl, &[a_low, places]);
716 let edge = ahead(func, inst, Opcode::LShr, &[a_low, one]);
717 let across = ahead(func, inst, Opcode::LShr, &[edge, back]);
718 let above = ahead(func, inst, Opcode::Shl, &[a_high, places]);
719 let joined = ahead(func, inst, Opcode::Or, &[above, across]);
720 let low = ahead(func, inst, Opcode::Select, &[whole, zero, moved]);
721 let high = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
722 (low, high)
723 } else {
724 let moved = ahead(func, inst, opcode, &[a_high, places]);
725 let edge = ahead(func, inst, Opcode::Shl, &[a_high, one]);
726 let across = ahead(func, inst, Opcode::Shl, &[edge, back]);
727 let below = ahead(func, inst, Opcode::LShr, &[a_low, places]);
728 let joined = ahead(func, inst, Opcode::Or, &[below, across]);
729 let spent = if opcode == Opcode::AShr {
732 ahead(func, inst, Opcode::AShr, &[a_high, top])
733 } else {
734 zero
735 };
736 let low = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
737 let high = ahead(func, inst, Opcode::Select, &[whole, spent, moved]);
738 (low, high)
739 };
740 replace(func, halves, inst, low, high);
741}
742
743fn bitwise(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
746 let args = func[func[inst].args].to_vec();
747 let [a, b] = args[..] else { return };
748 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
749 return;
750 };
751 let low = ahead(func, inst, opcode, &[a_low, b_low]);
752 let high = ahead(func, inst, opcode, &[a_high, b_high]);
753 replace(func, halves, inst, low, high);
754}
755
756fn compare(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
771 let Extra::IntPred(pred) = func[inst].extra else { return };
772 let args = func[func[inst].args].to_vec();
773 let [a, b] = args[..] else { return };
774 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
775 return;
776 };
777 let answer = if matches!(pred, IntPred::Eq | IntPred::Ne) {
778 let low = ahead(func, inst, Opcode::Xor, &[a_low, b_low]);
779 let high = ahead(func, inst, Opcode::Xor, &[a_high, b_high]);
780 let both = ahead(func, inst, Opcode::Or, &[low, high]);
781 let zero = ahead_const(func, inst, 0);
782 compared(func, inst, pred, both, zero)
783 } else {
784 let above = compared(func, inst, strict(pred), a_high, b_high);
785 let below = compared(func, inst, unsigned(pred), a_low, b_low);
786 let same = compared(func, inst, IntPred::Eq, a_high, b_high);
787 let tail = bit(func, inst, Opcode::And, same, below);
788 bit(func, inst, Opcode::Or, above, tail)
789 };
790 if let Some(result) = func[inst].first_result {
791 forward.insert(result, answer);
792 }
793 func.remove_inst(inst);
794}
795
796fn strict(pred: IntPred) -> IntPred {
798 match pred {
799 IntPred::Sle => IntPred::Slt,
800 IntPred::Sge => IntPred::Sgt,
801 IntPred::Ule => IntPred::Ult,
802 IntPred::Uge => IntPred::Ugt,
803 other => other,
804 }
805}
806
807fn unsigned(pred: IntPred) -> IntPred {
809 match pred {
810 IntPred::Slt => IntPred::Ult,
811 IntPred::Sle => IntPred::Ule,
812 IntPred::Sgt => IntPred::Ugt,
813 IntPred::Sge => IntPred::Uge,
814 other => other,
815 }
816}
817
818fn choose(func: &mut Func, halves: &mut Halves, inst: Inst) {
824 let args = func[func[inst].args].to_vec();
825 let [cond, then, other] = args[..] else { return };
826 let (Some(&(then_low, then_high)), Some(&(other_low, other_high))) =
827 (halves.get(&then), halves.get(&other))
828 else {
829 return;
830 };
831 let low = ahead(func, inst, Opcode::Select, &[cond, then_low, other_low]);
832 let high = ahead(func, inst, Opcode::Select, &[cond, then_high, other_high]);
833 replace(func, halves, inst, low, high);
834}
835
836fn truncate(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
842 let Some(&arg) = func[func[inst].args].first() else { return };
843 let Some(&(low, _)) = halves.get(&arg) else { return };
844 let Some(result) = func[inst].first_result else { return };
845 if func[result].ty.bits() == HALF {
846 forward.insert(result, low);
847 func.remove_inst(inst);
848 return;
849 }
850 becomes(func, inst, Opcode::Trunc, &[low]);
851}
852
853fn extend(func: &mut Func, halves: &mut Halves, inst: Inst, signed: bool) {
855 let Some(&arg) = func[func[inst].args].first() else { return };
856 let low = if func[arg].ty.bits() == HALF {
857 arg
858 } else {
859 let opcode = if signed { Opcode::SExt } else { Opcode::ZExt };
860 ahead(func, inst, opcode, &[arg])
861 };
862 let high = if signed {
863 let top = ahead_const(func, inst, i128::from(HALF - 1));
864 ahead(func, inst, Opcode::AShr, &[low, top])
865 } else {
866 ahead_const(func, inst, 0)
867 };
868 replace(func, halves, inst, low, high);
869}
870
871fn call(func: &mut Func, halves: &mut Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
878 let data = func[inst];
879 let Extra::Call(info) = data.extra else { return };
880 let info = func[info];
881 let args = spread(&func[data.args], halves);
882 let results: Vec<Type> = data
883 .results()
884 .map(|value| func[value].ty)
885 .flat_map(|ty| if is_wide(ty) { vec![half(), half()] } else { vec![ty] })
886 .collect();
887 let signature = func.add_signature(split_signature(&func[info.signature]));
888 let extra = Extra::Call(func.add_call(CallInfo { signature, ..info }));
889 let args = func.push_values(&args);
890 let span = func.span(inst);
891 let made = func.create_inst(InstData { args, extra, ..data }, &results, span);
892 func.insert_before(made, inst);
893 let mut fresh = func[made].results();
894 for old in data.results() {
895 if is_wide(func[old].ty) {
896 let (Some(low), Some(high)) = (fresh.next(), fresh.next()) else { return };
897 halves.insert(old, (low, high));
898 } else if let Some(again) = fresh.next() {
899 forward.insert(old, again);
900 }
901 }
902 func.remove_inst(inst);
903}
904
905fn flatten(func: &mut Func, halves: &Halves, inst: Inst) {
907 let args = spread(&func[func[inst].args], halves);
908 func[inst].args = func.push_values(&args);
909}
910
911fn edges(func: &mut Func, halves: &Halves, inst: Inst) {
913 for at in func.target_list(inst).iter() {
914 let call = func[at];
915 let args = func[call.args].to_vec();
916 if !args.iter().any(|value| halves.contains_key(value)) {
917 continue;
918 }
919 let args = func.push_values(&spread(&args, halves));
920 func.set_block_call(at, BlockCall { args, ..call });
921 }
922}
923
924fn spread(args: &[Value], halves: &Halves) -> Vec<Value> {
926 args.iter()
927 .flat_map(|value| match halves.get(value) {
928 Some(&(low, high)) => vec![low, high],
929 None => vec![*value],
930 })
931 .collect()
932}
933
934fn split_signature(signature: &Signature) -> Signature {
940 let split = |params: &[Param]| -> Vec<Param> {
941 params
942 .iter()
943 .flat_map(|param| {
944 if is_wide(param.ty) {
945 vec![Param::new(half()), Param::new(half())]
946 } else {
947 vec![*param]
948 }
949 })
950 .collect()
951 };
952 Signature {
953 params: split(&signature.params),
954 returns: split(&signature.returns),
955 variadic: signature.variadic,
956 }
957}
958
959fn replace(func: &mut Func, halves: &mut Halves, inst: Inst, low: Value, high: Value) {
961 if let Some(result) = func[inst].first_result {
962 halves.insert(result, (low, high));
963 }
964 func.remove_inst(inst);
965}
966
967fn substitute(func: &mut Func, forward: &HashMap<Value, Value>) {
973 if forward.is_empty() {
974 return;
975 }
976 let with = |value: Value| forward.get(&value).copied().unwrap_or(value);
977 for block in func.blocks().collect::<Vec<_>>() {
978 for inst in func.insts(block).collect::<Vec<Inst>>() {
979 let args = func[inst].args;
980 func.rewrite(args, with);
981 for call in func.successors(inst).collect::<Vec<_>>() {
982 func.rewrite(call.args, with);
983 }
984 }
985 }
986}
987
988fn word(info: MemInfo, at: u64) -> MemInfo {
990 let align = if at == 0 { info.align } else { info.align.min(8) };
991 MemInfo { size: STEP, align, ..info }
992}
993
994fn stepped(func: &mut Func, inst: Inst, from: Value) -> Value {
996 let step = ahead_const(func, inst, i128::from(STEP));
997 let args = func.push_values(&[from, step]);
998 written(func, inst, InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR)
999}
1000
1001fn read(func: &mut Func, inst: Inst, from: Value, info: MemInfo, flags: Flags) -> Value {
1003 let extra = Extra::Mem(func.add_mem(info));
1004 let args = func.push_values(&[from]);
1005 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Load) };
1006 written(func, inst, data, half())
1007}
1008
1009fn write(func: &mut Func, inst: Inst, value: Value, into: Value, info: MemInfo, flags: Flags) {
1011 let span = func.span(inst);
1012 let extra = Extra::Mem(func.add_mem(info));
1013 let args = func.push_values(&[value, into]);
1014 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Store) };
1015 let made = func.create_inst(data, &[], span);
1016 func.insert_before(made, inst);
1017}
1018
1019fn compared(func: &mut Func, inst: Inst, pred: IntPred, lhs: Value, rhs: Value) -> Value {
1022 let args = func.push_values(&[lhs, rhs]);
1023 let extra = Extra::IntPred(pred);
1024 written(func, inst, InstData { args, extra, ..InstData::new(Opcode::ICmp) }, Type::I1)
1025}
1026
1027fn bit(func: &mut Func, inst: Inst, opcode: Opcode, lhs: Value, rhs: Value) -> Value {
1029 let args = func.push_values(&[lhs, rhs]);
1030 written(func, inst, InstData { args, ..InstData::new(opcode) }, Type::I1)
1031}
1032
1033fn ahead(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) -> Value {
1035 let args = func.push_values(args);
1036 written(func, inst, InstData { args, ..InstData::new(opcode) }, half())
1037}
1038
1039fn ahead_const(func: &mut Func, inst: Inst, value: i128) -> Value {
1041 let extra = Extra::Imm(func.add_imm(Imm::int(value, half())));
1042 written(func, inst, InstData { extra, ..InstData::new(Opcode::IConst) }, half())
1043}
1044
1045fn written(func: &mut Func, inst: Inst, data: InstData, ty: Type) -> Value {
1047 let span = func.span(inst);
1048 let made = func.create_inst(data, &[ty], span);
1049 func.insert_before(made, inst);
1050 func[made].first_result.expect("an instruction created with one result has one")
1051}
1052
1053fn becomes(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) {
1055 let args = func.push_values(args);
1056 let data = &mut func[inst];
1057 data.opcode = opcode;
1058 data.args = args;
1059 data.extra = Extra::None;
1060 data.flags = data.flags.intersection(Flags::legal_on(opcode));
1061}
1062
1063#[cfg(test)]
1064mod tests {
1065 use rucc_base::Interner;
1066 use rucc_ir::{
1067 Block, Builder, Flags, Float, Func, MemOrder, Module, Restrict, Signature, Type, Value,
1068 };
1069 use rucc_target::x86_64::SYSV;
1070 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1071
1072 use super::{HALF, IntPred, MemInfo, Opcode, halves};
1073
1074 fn wide() -> Type {
1076 Type::int(super::WIDE)
1077 }
1078
1079 fn target() -> TargetInfo {
1080 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1081 }
1082
1083 fn printed(func: &Func, names: &mut Interner) -> String {
1084 let module = Module::new(names.intern("w.c"), &target());
1085 rucc_ir::print_func(&module, func, names)
1086 }
1087
1088 fn shell(names: &mut Interner, params: &[Type], returns: &[Type]) -> (Func, Block, Vec<Value>) {
1090 let signature = Signature::new().with_params(params).with_returns(returns);
1091 let mut func = Func::new(names.intern("f"), signature);
1092 let entry = func.create_block();
1093 let values = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
1094 (func, entry, values)
1095 }
1096
1097 fn info(size: u64, align: u32) -> MemInfo {
1099 MemInfo {
1100 size,
1101 align,
1102 order: MemOrder::NotAtomic,
1103 tbaa: None,
1104 owns: 0,
1105 restrict: Restrict::NONE,
1106 }
1107 }
1108
1109 #[test]
1110 fn an_add_carries_from_the_low_half_into_the_high_one() {
1111 let mut names = Interner::new();
1112 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1113 let mut build = Builder::new(&mut func, entry);
1114 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1115 build.ret(&[sum]);
1116
1117 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1118 let text = printed(&func, &mut names);
1119 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1120 assert_eq!(text.matches(" = add ").count(), 3, "three adds: {text}");
1123 assert_eq!(text.matches("icmp ult").count(), 1, "one carry: {text}");
1124 assert_eq!(text.matches(" = zext.i64 ").count(), 1, "the carry as a number: {text}");
1125 }
1126
1127 #[test]
1128 fn a_subtract_borrows_the_other_way_round() {
1129 let mut names = Interner::new();
1130 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1131 let mut build = Builder::new(&mut func, entry);
1132 let difference = build.binary(Opcode::Sub, params[0], params[1], Flags::NONE);
1133 build.ret(&[difference]);
1134
1135 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1136 let text = printed(&func, &mut names);
1137 assert_eq!(text.matches(" = sub ").count(), 3, "three subtracts: {text}");
1138 assert!(text.contains("icmp ult %0, %2"), "the operands are compared: {text}");
1141 }
1142
1143 #[test]
1144 fn the_signature_and_the_entry_block_say_the_same_thing() {
1145 let mut names = Interner::new();
1146 let (mut func, entry, params) = shell(&mut names, &[Type::int(32), wide()], &[wide()]);
1147 let mut build = Builder::new(&mut func, entry);
1148 build.ret(&[params[1]]);
1149
1150 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1151 assert_eq!(
1152 func.signature().param_types().collect::<Vec<_>>(),
1153 [Type::int(32), Type::int(HALF), Type::int(HALF)],
1154 "the wide parameter became two where it stood"
1155 );
1156 assert_eq!(
1157 func.signature().return_types().collect::<Vec<_>>(),
1158 [Type::int(HALF), Type::int(HALF)],
1159 "and so did what comes back"
1160 );
1161 let text = printed(&func, &mut names);
1162 assert!(text.contains("block0(%0: i32, %1: i64, %2: i64)"), "the block agrees: {text}");
1163 assert!(text.contains("return %1, %2"), "both halves go back: {text}");
1164 let _ = entry;
1165 }
1166
1167 #[test]
1168 fn a_read_takes_the_high_word_a_word_above_the_low_one() {
1169 let mut names = Interner::new();
1170 let (mut func, entry, params) = shell(&mut names, &[Type::PTR], &[wide()]);
1171 let mut build = Builder::new(&mut func, entry);
1172 let value = build.load(wide(), params[0], info(16, 16), Flags::NONE);
1173 build.ret(&[value]);
1174
1175 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1176 let text = printed(&func, &mut names);
1177 assert_eq!(text.matches(" = load.i64 ").count(), 2, "two reads: {text}");
1178 assert!(text.contains("ptr_add"), "the high word is a word up: {text}");
1179 assert!(text.contains("align 16"), "the low word keeps what the object had: {text}");
1182 assert!(text.contains("align 8"), "the high word knows less: {text}");
1183 }
1184
1185 #[test]
1186 fn an_equality_asks_once_about_both_halves() {
1187 let mut names = Interner::new();
1188 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1189 let mut build = Builder::new(&mut func, entry);
1190 let same = build.icmp(IntPred::Eq, params[0], params[1]);
1191 let answer = build.unary(Opcode::ZExt, same, Type::int(32));
1192 build.ret(&[answer]);
1193
1194 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1195 let text = printed(&func, &mut names);
1196 assert_eq!(text.matches("icmp").count(), 1, "one comparison: {text}");
1197 assert_eq!(text.matches(" = xor ").count(), 2, "the halves differ or they do not: {text}");
1198 }
1199
1200 #[test]
1201 fn an_ordering_reads_the_low_halves_without_a_sign() {
1202 let mut names = Interner::new();
1203 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1204 let mut build = Builder::new(&mut func, entry);
1205 let below = build.icmp(IntPred::Slt, params[0], params[1]);
1206 let answer = build.unary(Opcode::ZExt, below, Type::int(32));
1207 build.ret(&[answer]);
1208
1209 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1210 let text = printed(&func, &mut names);
1211 assert!(text.contains("icmp slt"), "the high halves keep the sign: {text}");
1212 assert!(text.contains("icmp ult"), "the low halves have none: {text}");
1213 assert!(
1214 text.contains("icmp eq"),
1215 "and the low halves only matter when the high tie: {text}"
1216 );
1217 }
1218
1219 #[test]
1226 fn an_ordering_that_allows_equality_asks_the_high_halves_a_strict_question() {
1227 let mut names = Interner::new();
1228 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1229 let mut build = Builder::new(&mut func, entry);
1230 let at_least = build.icmp(IntPred::Sge, params[0], params[1]);
1231 let answer = build.unary(Opcode::ZExt, at_least, Type::int(32));
1232 build.ret(&[answer]);
1233
1234 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1235 let text = printed(&func, &mut names);
1236 assert!(text.contains("icmp sgt"), "the high halves settle it outright: {text}");
1237 assert!(!text.contains("icmp sge"), "a tie in the high halves settles nothing: {text}");
1238 assert!(text.contains("icmp uge"), "the low halves are the ones allowed to tie: {text}");
1239 }
1240
1241 #[test]
1242 fn a_widening_puts_the_sign_of_the_value_in_the_high_half() {
1243 let mut names = Interner::new();
1244 let (mut func, entry, params) = shell(&mut names, &[Type::int(32)], &[wide()]);
1245 let mut build = Builder::new(&mut func, entry);
1246 let value = build.unary(Opcode::SExt, params[0], wide());
1247 build.ret(&[value]);
1248
1249 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1250 let text = printed(&func, &mut names);
1251 assert!(text.contains("sext.i64"), "the value fills the low half: {text}");
1252 assert!(text.contains("ashr"), "and its sign fills the high one: {text}");
1253 }
1254
1255 #[test]
1256 fn a_block_parameter_becomes_two_and_every_branch_passes_two() {
1257 let mut names = Interner::new();
1258 let (mut func, entry, params) = shell(&mut names, &[wide(), Type::int(32)], &[wide()]);
1259 let tail = func.create_block();
1260 let carried = func.append_param(tail, wide());
1261 let mut build = Builder::new(&mut func, entry);
1262 let zero = build.iconst(Type::int(32), 0);
1263 let taken = build.icmp(IntPred::Ne, params[1], zero);
1264 let other = build.iconst(wide(), 7);
1265 build.br_if(taken, tail, &[params[0]], tail, &[other]);
1266 let mut build = Builder::new(&mut func, tail);
1267 build.ret(&[carried]);
1268
1269 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1270 let text = printed(&func, &mut names);
1271 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1272 assert!(text.contains("block1(%7: i64, %8: i64)"), "the block takes two: {text}");
1273 assert_eq!(text.matches("block1(").count(), 3, "and both edges pass two: {text}");
1274 }
1275
1276 #[test]
1283 fn a_multiply_is_three_multiplies_and_the_carry_out_of_the_low_ones() {
1284 let mut names = Interner::new();
1285 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1286 let mut build = Builder::new(&mut func, entry);
1287 let product = build.binary(Opcode::Mul, params[0], params[1], Flags::NONE);
1288 build.ret(&[product]);
1289
1290 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1291 let text = printed(&func, &mut names);
1292 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1293 assert_eq!(text.matches(" = mul ").count(), 7, "three and the carry's four: {text}");
1294 }
1295
1296 #[test]
1303 fn each_of_the_four_divisions_calls_the_routine_of_that_name() {
1304 for (opcode, routine) in [
1305 (Opcode::UDiv, "__udivti3"),
1306 (Opcode::SDiv, "__divti3"),
1307 (Opcode::URem, "__umodti3"),
1308 (Opcode::SRem, "__modti3"),
1309 ] {
1310 let mut names = Interner::new();
1311 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1312 let mut build = Builder::new(&mut func, entry);
1313 let answer = build.binary(opcode, params[0], params[1], Flags::NONE);
1314 build.ret(&[answer]);
1315
1316 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1317 let text = printed(&func, &mut names);
1318 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1319 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1320 }
1321 }
1322
1323 #[test]
1329 fn a_divide_hands_over_four_halves_and_takes_two_back() {
1330 let mut names = Interner::new();
1331 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1332 let mut build = Builder::new(&mut func, entry);
1333 let quotient = build.binary(Opcode::UDiv, params[0], params[1], Flags::NONE);
1334 build.ret(&[quotient]);
1335
1336 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1337 let text = printed(&func, &mut names);
1338 assert!(text.contains("@__udivti3(%0, %1, %2, %3)"), "four halves go over: {text}");
1339 assert!(text.contains("return %4, %5"), "and two come back: {text}");
1340 }
1341
1342 #[test]
1348 fn a_divide_of_something_computed_calls_with_the_halves_of_it() {
1349 let mut names = Interner::new();
1350 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1351 let mut build = Builder::new(&mut func, entry);
1352 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1353 let quotient = build.binary(Opcode::SDiv, sum, params[1], Flags::NONE);
1354 build.ret(&[quotient]);
1355
1356 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1357 let text = printed(&func, &mut names);
1358 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1359 assert_eq!(text.matches(" = add ").count(), 3, "the sum is still a sum: {text}");
1360 assert_eq!(text.matches("call @__divti3").count(), 1, "one call: {text}");
1361 }
1362
1363 #[test]
1369 fn each_conversion_between_this_width_and_a_float_calls_the_routine_of_that_name() {
1370 let double = Type::float(Float::F64);
1371 let single = Type::float(Float::F32);
1372 let quad = Type::float(Float::F128);
1373 for (opcode, float, routine) in [
1374 (Opcode::SIToFP, double, "__floattidf"),
1375 (Opcode::SIToFP, single, "__floattisf"),
1376 (Opcode::UIToFP, double, "__floatuntidf"),
1377 (Opcode::UIToFP, single, "__floatuntisf"),
1378 (Opcode::SIToFP, quad, "__floattitf"),
1379 (Opcode::UIToFP, quad, "__floatuntitf"),
1380 ] {
1381 let mut names = Interner::new();
1382 let (mut func, entry, params) = shell(&mut names, &[wide()], &[float]);
1383 let mut build = Builder::new(&mut func, entry);
1384 let answer = build.unary(opcode, params[0], float);
1385 build.ret(&[answer]);
1386
1387 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1388 let text = printed(&func, &mut names);
1389 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1390 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1391 }
1392 for (opcode, float, routine) in [
1393 (Opcode::FPToSI, double, "__fixdfti"),
1394 (Opcode::FPToSI, single, "__fixsfti"),
1395 (Opcode::FPToUI, double, "__fixunsdfti"),
1396 (Opcode::FPToUI, single, "__fixunssfti"),
1397 (Opcode::FPToSI, quad, "__fixtfti"),
1398 (Opcode::FPToUI, quad, "__fixunstfti"),
1399 ] {
1400 let mut names = Interner::new();
1401 let (mut func, entry, params) = shell(&mut names, &[float], &[wide()]);
1402 let mut build = Builder::new(&mut func, entry);
1403 let answer = build.unary(opcode, params[0], wide());
1404 build.ret(&[answer]);
1405
1406 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1407 let text = printed(&func, &mut names);
1408 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1409 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1410 }
1411 }
1412
1413 #[test]
1419 fn a_conversion_hands_over_halves_one_way_and_takes_them_back_the_other() {
1420 let double = Type::float(Float::F64);
1421 let mut names = Interner::new();
1422 let (mut func, entry, params) = shell(&mut names, &[wide()], &[double]);
1423 let mut build = Builder::new(&mut func, entry);
1424 let answer = build.unary(Opcode::SIToFP, params[0], double);
1425 build.ret(&[answer]);
1426
1427 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1428 let text = printed(&func, &mut names);
1429 assert!(text.contains("@__floattidf(%0, %1)"), "two halves go over: {text}");
1430 assert!(text.contains("return %2"), "and one float comes back: {text}");
1431
1432 let mut names = Interner::new();
1433 let (mut func, entry, params) = shell(&mut names, &[double], &[wide()]);
1434 let mut build = Builder::new(&mut func, entry);
1435 let answer = build.unary(Opcode::FPToSI, params[0], wide());
1436 build.ret(&[answer]);
1437
1438 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1439 let text = printed(&func, &mut names);
1440 assert!(text.contains("@__fixdfti(%0)"), "the float goes over as it is: {text}");
1441 assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1442 }
1443
1444 #[test]
1451 fn a_conversion_against_a_quad_hands_over_the_pair_and_the_quad_whole() {
1452 let quad = Type::float(Float::F128);
1453 let mut names = Interner::new();
1454 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
1455 let mut build = Builder::new(&mut func, entry);
1456 let answer = build.unary(Opcode::UIToFP, params[0], quad);
1457 build.ret(&[answer]);
1458
1459 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1460 let text = printed(&func, &mut names);
1461 assert!(text.contains("@__floatuntitf(%0, %1)"), "two halves go over: {text}");
1462 assert!(text.contains("return %2"), "and one quad comes back: {text}");
1463
1464 let mut names = Interner::new();
1465 let (mut func, entry, params) = shell(&mut names, &[quad], &[wide()]);
1466 let mut build = Builder::new(&mut func, entry);
1467 let answer = build.unary(Opcode::FPToSI, params[0], wide());
1468 build.ret(&[answer]);
1469
1470 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1471 let text = printed(&func, &mut names);
1472 assert!(text.contains("@__fixtfti(%0)"), "the quad goes over as it is: {text}");
1473 assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1474 }
1475
1476 #[test]
1483 fn a_conversion_at_a_width_the_runtime_has_no_routine_for_is_left_alone() {
1484 let long = Type::float(Float::F80);
1485 let mut names = Interner::new();
1486 let (mut func, entry, params) = shell(&mut names, &[wide()], &[long]);
1487 let mut build = Builder::new(&mut func, entry);
1488 let answer = build.unary(Opcode::SIToFP, params[0], long);
1489 build.ret(&[answer]);
1490
1491 assert!(!halves(&mut func, &mut names, &SYSV), "the pass does not understand this one");
1492 let text = printed(&func, &mut names);
1493 assert!(text.contains("i128"), "the width is still there: {text}");
1494 }
1495
1496 #[test]
1503 fn a_shift_left_chooses_between_a_count_that_crossed_a_half_and_one_that_did_not() {
1504 let mut names = Interner::new();
1505 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1506 let mut build = Builder::new(&mut func, entry);
1507 let moved = build.binary(Opcode::Shl, params[0], params[1], Flags::NONE);
1508 build.ret(&[moved]);
1509
1510 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1511 let text = printed(&func, &mut names);
1512 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1513 assert_eq!(
1514 text.matches(" = shl ").count(),
1515 2,
1516 "one per half, and the far case reuses one: {text}"
1517 );
1518 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1519 assert_eq!(text.matches(" = lshr ").count(), 2, "the crossing bits, in two steps: {text}");
1520 }
1521
1522 #[test]
1529 fn the_bits_that_cross_move_one_place_and_then_the_rest_of_the_way() {
1530 let mut names = Interner::new();
1531 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1532 let mut build = Builder::new(&mut func, entry);
1533 let moved = build.binary(Opcode::LShr, params[0], params[1], Flags::NONE);
1534 build.ret(&[moved]);
1535
1536 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1537 let text = printed(&func, &mut names);
1538 assert!(text.contains("iconst.i64 63"), "sixty three is the distance left: {text}");
1539 assert!(text.contains("iconst.i64 1"), "after the one place that comes first: {text}");
1540 assert!(text.contains(" = sub "), "the rest of the way is worked out: {text}");
1541 assert!(
1542 !text.contains("iconst.i64 127"),
1543 "and the count is not masked to the width: {text}"
1544 );
1545 }
1546
1547 #[test]
1553 fn an_arithmetic_shift_right_leaves_the_sign_bit_where_a_logical_one_leaves_zeroes() {
1554 let mut names = Interner::new();
1555 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1556 let mut build = Builder::new(&mut func, entry);
1557 let moved = build.binary(Opcode::AShr, params[0], params[1], Flags::NONE);
1558 build.ret(&[moved]);
1559
1560 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1561 let text = printed(&func, &mut names);
1562 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1563 assert_eq!(text.matches(" = ashr ").count(), 2, "the count and the sign: {text}");
1565 assert_eq!(text.matches(" = lshr ").count(), 1, "the low half is not signed: {text}");
1566 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1567 }
1568
1569 #[test]
1570 fn a_parameter_with_one_register_left_leaves_the_function_alone() {
1571 let mut names = Interner::new();
1572 let word = Type::int(HALF);
1573 let params = [word, word, word, word, word, wide()];
1577 let (mut func, entry, values) = shell(&mut names, ¶ms, &[word]);
1578 let mut build = Builder::new(&mut func, entry);
1579 let low = build.unary(Opcode::Trunc, values[5], word);
1580 build.ret(&[low]);
1581 let before = printed(&func, &mut names);
1582
1583 assert!(!halves(&mut func, &mut names, &SYSV), "one of the halves has no register");
1584 assert_eq!(printed(&func, &mut names), before, "so nothing moved");
1585 }
1586
1587 #[test]
1596 fn a_block_made_after_the_one_it_runs_before_is_still_split() {
1597 let mut names = Interner::new();
1598 let (mut func, entry, params) = shell(&mut names, &[wide()], &[wide()]);
1599 let tail = func.create_block();
1600 let middle = func.create_block();
1601 let mut build = Builder::new(&mut func, entry);
1602 build.jump(middle, &[]);
1603 let mut build = Builder::new(&mut func, middle);
1604 let doubled = build.binary(Opcode::Add, params[0], params[0], Flags::NONE);
1605 build.jump(tail, &[]);
1606 let mut build = Builder::new(&mut func, tail);
1607 let again = build.binary(Opcode::Add, doubled, doubled, Flags::NONE);
1608 build.ret(&[again]);
1609
1610 assert!(
1611 halves(&mut func, &mut names, &SYSV),
1612 "the definition runs before the use whatever the list says"
1613 );
1614 let text = printed(&func, &mut names);
1615 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1616 }
1617
1618 #[test]
1619 fn a_function_with_nothing_that_wide_is_not_touched() {
1620 let mut names = Interner::new();
1621 let word = Type::int(HALF);
1622 let (mut func, entry, params) = shell(&mut names, &[word, word], &[word]);
1623 let mut build = Builder::new(&mut func, entry);
1624 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1625 build.ret(&[sum]);
1626
1627 assert!(!halves(&mut func, &mut names, &SYSV), "there is nothing to split");
1628 }
1629}