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::expand;
76
77const WIDE: u32 = 128;
79
80const HALF: u32 = 64;
82
83const STEP: u64 = 8;
85
86fn is_wide(ty: Type) -> bool {
88 ty.is_int() && ty.is_scalar() && ty.bits() == WIDE
89}
90
91fn half() -> Type {
93 Type::int(HALF)
94}
95
96pub fn halves(func: &mut Func, names: &mut Interner, conv: &CallRegs) -> bool {
107 if !func.values().any(|value| is_wide(func[value].ty)) {
108 return false;
109 }
110 let insts: Vec<Inst> =
111 walk(func).into_iter().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
112 let order: HashMap<Inst, usize> =
113 insts.iter().enumerate().map(|(at, &inst)| (inst, at)).collect();
114 if !insts.iter().enumerate().all(|(at, &inst)| can_split(func, &order, at, inst)) {
115 return false;
116 }
117 if !func.signatures().all(|signature| fits(signature, conv)) {
118 return false;
119 }
120
121 let mut halves: Halves = HashMap::new();
122 let mut forward: HashMap<Value, Value> = HashMap::new();
123 for block in func.blocks().collect::<Vec<_>>() {
124 params(func, block, &mut halves, &mut forward);
125 }
126 for &inst in &insts {
127 rewrite(func, names, &mut halves, &mut forward, inst);
128 }
129 substitute(func, &forward);
130 let signature = split_signature(func.signature());
131 func.set_signature(signature);
132 true
133}
134
135fn walk(func: &Func) -> Vec<Block> {
153 let Some(entry) = func.entry() else { return func.blocks().collect() };
154 let mut seen: HashSet<Block> = HashSet::new();
155 let mut order: Vec<Block> = Vec::new();
156 let mut stack: Vec<(Block, bool)> = vec![(entry, false)];
159 seen.insert(entry);
160 while let Some((block, done)) = stack.pop() {
161 if done {
162 order.push(block);
163 continue;
164 }
165 stack.push((block, true));
166 let Some(term) = func.terminator(block) else { continue };
167 for call in func.successors(term) {
168 if seen.insert(call.block) {
169 stack.push((call.block, false));
170 }
171 }
172 }
173 order.reverse();
174 order.extend(func.blocks().filter(|block| !seen.contains(block)));
175 order
176}
177
178type Halves = HashMap<Value, (Value, Value)>;
180
181fn understood(opcode: Opcode) -> bool {
192 matches!(
193 opcode,
194 Opcode::IConst
195 | Opcode::Load
196 | Opcode::Store
197 | Opcode::Add
198 | Opcode::Sub
199 | Opcode::Mul
200 | Opcode::UDiv
201 | Opcode::SDiv
202 | Opcode::URem
203 | Opcode::SRem
204 | Opcode::Shl
205 | Opcode::LShr
206 | Opcode::AShr
207 | Opcode::And
208 | Opcode::Or
209 | Opcode::Xor
210 | Opcode::ICmp
211 | Opcode::Select
212 | Opcode::SIToFP
213 | Opcode::UIToFP
214 | Opcode::FPToSI
215 | Opcode::FPToUI
216 | Opcode::Trunc
217 | Opcode::SExt
218 | Opcode::ZExt
219 | Opcode::Call
220 | Opcode::CallIndirect
221 | Opcode::Return
222 | Opcode::Jump
223 | Opcode::BrIf
224 )
225}
226
227fn can_split(func: &Func, order: &HashMap<Inst, usize>, at: usize, inst: Inst) -> bool {
232 let data = func[inst];
233 let reads = operands(func, inst);
234 let wide = |&value: &Value| is_wide(func[value].ty);
235 if !reads.iter().any(wide) && !data.results().any(|value| is_wide(func[value].ty)) {
236 return true;
237 }
238 if !understood(data.opcode) {
239 return false;
240 }
241 if func.carries_mem(inst) {
245 return false;
246 }
247 if data.opcode == Opcode::SExt && reads.iter().any(|&value| func[value].ty.bits() < 8) {
251 return false;
252 }
253 if matches!(data.opcode, Opcode::SIToFP | Opcode::UIToFP | Opcode::FPToSI | Opcode::FPToUI)
258 && converted(func, inst).is_none()
259 {
260 return false;
261 }
262 if matches!(data.opcode, Opcode::Call | Opcode::CallIndirect) {
266 let Extra::Call(info) = data.extra else { return false };
267 if func[func[info].signature].variadic {
268 return false;
269 }
270 }
271 reads.iter().filter(|value| wide(value)).all(|&value| match func[value].def {
275 Def::Result { inst, .. } => order.get(&inst).is_some_and(|&def| def < at),
276 Def::Param { .. } => true,
277 })
278}
279
280fn converted(func: &Func, inst: Inst) -> Option<Float> {
287 let data = func[inst];
288 let mut floats = func[data.args]
289 .iter()
290 .copied()
291 .chain(data.results())
292 .map(|value| func[value].ty)
293 .filter(|ty| ty.is_float());
294 let only = floats.next()?;
295 if floats.next().is_some() {
296 return None;
297 }
298 match only.format() {
299 Some(format @ (Float::F32 | Float::F64 | Float::F128)) => Some(format),
300 _ => None,
301 }
302}
303
304fn operands(func: &Func, inst: Inst) -> Vec<Value> {
310 let mut reads = func[func[inst].args].to_vec();
311 for call in func.successors(inst).collect::<Vec<_>>() {
312 reads.extend_from_slice(&func[call.args]);
313 }
314 reads
315}
316
317fn fits(signature: &Signature, conv: &CallRegs) -> bool {
329 let mut places = Places::new(conv);
330 for param in &signature.params {
331 if let Abi::ByVal { size, align } = param.abi {
335 places.on_stack(u32::try_from(size).unwrap_or(u32::MAX), align);
336 } else if crate::abi::on_the_stack(param.ty) {
337 let (size, align) = crate::abi::X87_AREA;
338 places.on_stack(size, align);
339 } else if is_wide(param.ty) {
340 let low = places.integer();
341 let high = places.integer();
342 if !matches!((low, high), (Where::Reg(_), Where::Reg(_))) {
343 return false;
344 }
345 } else if param.ty.is_float() {
346 places.float(crate::abi::float_bytes(param.ty));
347 } else {
348 places.integer();
349 }
350 }
351 true
352}
353
354fn params(func: &mut Func, block: Block, halves: &mut Halves, forward: &mut HashMap<Value, Value>) {
361 let old: Vec<Value> = func[block].params.clone();
362 if !old.iter().any(|&value| is_wide(func[value].ty)) {
363 return;
364 }
365 for &value in &old {
366 if is_wide(func[value].ty) {
367 let low = func.append_param(block, half());
368 let high = func.append_param(block, half());
369 halves.insert(value, (low, high));
370 } else {
371 let again = func.append_param(block, func[value].ty);
372 forward.insert(value, again);
373 }
374 }
375 func.retain_params(block, |value| !old.contains(&value));
376}
377
378fn rewrite(
380 func: &mut Func,
381 names: &mut Interner,
382 halves: &mut Halves,
383 forward: &mut HashMap<Value, Value>,
384 inst: Inst,
385) {
386 let data = func[inst];
387 let produces = data.results().any(|value| is_wide(func[value].ty));
388 let takes = func[data.args].iter().any(|&value| is_wide(func[value].ty));
389 match data.opcode {
390 Opcode::IConst if produces => constant(func, halves, inst),
391 Opcode::Load if produces => load(func, halves, inst),
392 Opcode::Store if takes => store(func, halves, inst),
393 Opcode::Add | Opcode::Sub if produces => carried(func, halves, inst, data.opcode),
394 Opcode::Mul if produces => multiply(func, halves, inst),
395 Opcode::UDiv | Opcode::SDiv | Opcode::URem | Opcode::SRem if produces => {
396 divide(func, names, halves, inst, data.opcode);
397 }
398 Opcode::Shl | Opcode::LShr | Opcode::AShr if produces => {
399 shifted(func, halves, inst, data.opcode);
400 }
401 Opcode::And | Opcode::Or | Opcode::Xor if produces => {
402 bitwise(func, halves, inst, data.opcode);
403 }
404 Opcode::SIToFP | Opcode::UIToFP if takes => {
405 to_float(func, names, halves, forward, inst, data.opcode == Opcode::SIToFP);
406 }
407 Opcode::FPToSI | Opcode::FPToUI if produces => {
408 from_float(func, names, halves, inst, data.opcode == Opcode::FPToSI);
409 }
410 Opcode::ICmp if takes => compare(func, halves, forward, inst),
411 Opcode::Select if produces => choose(func, halves, inst),
412 Opcode::Trunc if takes => truncate(func, halves, forward, inst),
413 Opcode::SExt | Opcode::ZExt if produces => {
414 extend(func, halves, inst, data.opcode == Opcode::SExt);
415 }
416 Opcode::Call | Opcode::CallIndirect if produces || takes => {
417 call(func, halves, forward, inst);
418 }
419 Opcode::Return if takes => flatten(func, halves, inst),
420 Opcode::Jump | Opcode::BrIf => edges(func, halves, inst),
421 _ => {}
422 }
423}
424
425fn constant(func: &mut Func, halves: &mut Halves, inst: Inst) {
427 let Extra::Imm(imm) = func[inst].extra else { return };
428 let bits = func[imm].unsigned();
429 #[expect(clippy::cast_possible_truncation, reason = "the halves are what this is taking")]
430 let (low, high) = (bits as u64, (bits >> HALF) as u64);
431 let low = ahead_const(func, inst, i128::from(low));
432 let high = ahead_const(func, inst, i128::from(high));
433 replace(func, halves, inst, low, high);
434}
435
436fn load(func: &mut Func, halves: &mut Halves, inst: Inst) {
442 let data = func[inst];
443 let Extra::Mem(mem) = data.extra else { return };
444 let info = func[mem];
445 let Some(&from) = func[data.args].first() else { return };
446 let low = read(func, inst, from, word(info, 0), data.flags);
447 let up = stepped(func, inst, from);
448 let high = read(func, inst, up, word(info, STEP), data.flags);
449 replace(func, halves, inst, low, high);
450}
451
452fn store(func: &mut Func, halves: &mut Halves, inst: Inst) {
454 let data = func[inst];
455 let Extra::Mem(mem) = data.extra else { return };
456 let info = func[mem];
457 let args = func[data.args].to_vec();
458 let [value, into] = args[..] else { return };
459 let Some(&(low, high)) = halves.get(&value) else { return };
460 write(func, inst, low, into, word(info, 0), data.flags);
461 let up = stepped(func, inst, into);
462 write(func, inst, high, up, word(info, STEP), data.flags);
463 func.remove_inst(inst);
464}
465
466fn carried(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
476 let args = func[func[inst].args].to_vec();
477 let [a, b] = args[..] else { return };
478 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
479 return;
480 };
481 let low = ahead(func, inst, opcode, &[a_low, b_low]);
482 let carried = if opcode == Opcode::Add {
483 compared(func, inst, IntPred::Ult, low, a_low)
484 } else {
485 compared(func, inst, IntPred::Ult, a_low, b_low)
486 };
487 let carry = ahead(func, inst, Opcode::ZExt, &[carried]);
488 let high = ahead(func, inst, opcode, &[a_high, b_high]);
489 let high = ahead(func, inst, opcode, &[high, carry]);
490 replace(func, halves, inst, low, high);
491}
492
493fn multiply(func: &mut Func, halves: &mut Halves, inst: Inst) {
513 let args = func[func[inst].args].to_vec();
514 let [a, b] = args[..] else { return };
515 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
516 return;
517 };
518 let low = ahead(func, inst, Opcode::Mul, &[a_low, b_low]);
519 let carried = expand::high_half(func, inst, a_low, b_low, false, half());
520 let cross = ahead(func, inst, Opcode::Mul, &[a_low, b_high]);
521 let other = ahead(func, inst, Opcode::Mul, &[a_high, b_low]);
522 let high = ahead(func, inst, Opcode::Add, &[carried, cross]);
523 let high = ahead(func, inst, Opcode::Add, &[high, other]);
524 replace(func, halves, inst, low, high);
525}
526
527fn divide(func: &mut Func, names: &mut Interner, halves: &mut Halves, inst: Inst, opcode: Opcode) {
545 let args = func[func[inst].args].to_vec();
546 let [a, b] = args[..] else { return };
547 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
548 return;
549 };
550 let routine = match opcode {
551 Opcode::UDiv => "__udivti3",
552 Opcode::SDiv => "__divti3",
553 Opcode::URem => "__umodti3",
554 _ => "__modti3",
555 };
556 let made =
557 runtime(func, names, inst, routine, &[a_low, a_high, b_low, b_high], &[half(), half()]);
558 let mut results = func[made].results();
559 let (Some(low), Some(high)) = (results.next(), results.next()) else { return };
560 replace(func, halves, inst, low, high);
561}
562
563fn to_float(
573 func: &mut Func,
574 names: &mut Interner,
575 halves: &Halves,
576 forward: &mut HashMap<Value, Value>,
577 inst: Inst,
578 signed: bool,
579) {
580 let Some(&arg) = func[func[inst].args].first() else { return };
581 let Some(&(low, high)) = halves.get(&arg) else { return };
582 let (Some(result), Some(format)) = (func[inst].first_result, converted(func, inst)) else {
583 return;
584 };
585 let routine = going_up(signed, format);
586 let made = runtime(func, names, inst, routine, &[low, high], &[func[result].ty]);
587 if let Some(answer) = func[made].first_result {
588 forward.insert(result, answer);
589 }
590 func.remove_inst(inst);
591}
592
593fn from_float(
603 func: &mut Func,
604 names: &mut Interner,
605 halves: &mut Halves,
606 inst: Inst,
607 signed: bool,
608) {
609 let Some(&arg) = func[func[inst].args].first() else { return };
610 let Some(format) = converted(func, inst) else { return };
611 let routine = coming_down(signed, format);
612 let made = runtime(func, names, inst, routine, &[arg], &[half(), half()]);
613 let mut results = func[made].results();
614 let (Some(low), Some(high)) = (results.next(), results.next()) else { return };
615 replace(func, halves, inst, low, high);
616}
617
618fn going_up(signed: bool, format: Float) -> &'static str {
624 match (signed, format) {
625 (true, Float::F32) => "__floattisf",
626 (true, Float::F64) => "__floattidf",
627 (true, _) => "__floattitf",
628 (false, Float::F32) => "__floatuntisf",
629 (false, Float::F64) => "__floatuntidf",
630 (false, _) => "__floatuntitf",
631 }
632}
633
634fn coming_down(signed: bool, format: Float) -> &'static str {
636 match (signed, format) {
637 (true, Float::F32) => "__fixsfti",
638 (true, Float::F64) => "__fixdfti",
639 (true, _) => "__fixtfti",
640 (false, Float::F32) => "__fixunssfti",
641 (false, Float::F64) => "__fixunsdfti",
642 (false, _) => "__fixunstfti",
643 }
644}
645
646fn runtime(
653 func: &mut Func,
654 names: &mut Interner,
655 inst: Inst,
656 routine: &str,
657 args: &[Value],
658 results: &[Type],
659) -> Inst {
660 let params: Vec<Type> = args.iter().map(|&value| func[value].ty).collect();
661 let signature = func.add_signature(Signature::new().with_params(¶ms).with_returns(results));
662 let callee = Some(names.intern(routine));
663 let varargs = func.push_abis(&[]);
664 let extra = Extra::Call(func.add_call(CallInfo { callee, signature, varargs }));
665 let args = func.push_values(args);
666 let span = func.span(inst);
667 let data = InstData { args, extra, ..InstData::new(Opcode::Call) };
668 let made = func.create_inst(data, results, span);
669 func.insert_before(made, inst);
670 made
671}
672
673fn shifted(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
693 let args = func[func[inst].args].to_vec();
694 let [a, b] = args[..] else { return };
695 let (Some(&(a_low, a_high)), Some(&(count, _))) = (halves.get(&a), halves.get(&b)) else {
696 return;
697 };
698 let top = ahead_const(func, inst, i128::from(HALF - 1));
699 let places = ahead(func, inst, Opcode::And, &[count, top]);
700 let back = ahead(func, inst, Opcode::Sub, &[top, places]);
701 let one = ahead_const(func, inst, 1);
702 let zero = ahead_const(func, inst, 0);
703 let bit = ahead_const(func, inst, i128::from(HALF));
704 let reach = ahead(func, inst, Opcode::And, &[count, bit]);
705 let whole = compared(func, inst, IntPred::Ne, reach, zero);
706
707 let (low, high) = if opcode == Opcode::Shl {
708 let moved = ahead(func, inst, Opcode::Shl, &[a_low, places]);
709 let edge = ahead(func, inst, Opcode::LShr, &[a_low, one]);
710 let across = ahead(func, inst, Opcode::LShr, &[edge, back]);
711 let above = ahead(func, inst, Opcode::Shl, &[a_high, places]);
712 let joined = ahead(func, inst, Opcode::Or, &[above, across]);
713 let low = ahead(func, inst, Opcode::Select, &[whole, zero, moved]);
714 let high = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
715 (low, high)
716 } else {
717 let moved = ahead(func, inst, opcode, &[a_high, places]);
718 let edge = ahead(func, inst, Opcode::Shl, &[a_high, one]);
719 let across = ahead(func, inst, Opcode::Shl, &[edge, back]);
720 let below = ahead(func, inst, Opcode::LShr, &[a_low, places]);
721 let joined = ahead(func, inst, Opcode::Or, &[below, across]);
722 let spent = if opcode == Opcode::AShr {
725 ahead(func, inst, Opcode::AShr, &[a_high, top])
726 } else {
727 zero
728 };
729 let low = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
730 let high = ahead(func, inst, Opcode::Select, &[whole, spent, moved]);
731 (low, high)
732 };
733 replace(func, halves, inst, low, high);
734}
735
736fn bitwise(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
739 let args = func[func[inst].args].to_vec();
740 let [a, b] = args[..] else { return };
741 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
742 return;
743 };
744 let low = ahead(func, inst, opcode, &[a_low, b_low]);
745 let high = ahead(func, inst, opcode, &[a_high, b_high]);
746 replace(func, halves, inst, low, high);
747}
748
749fn compare(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
764 let Extra::IntPred(pred) = func[inst].extra else { return };
765 let args = func[func[inst].args].to_vec();
766 let [a, b] = args[..] else { return };
767 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
768 return;
769 };
770 let answer = if matches!(pred, IntPred::Eq | IntPred::Ne) {
771 let low = ahead(func, inst, Opcode::Xor, &[a_low, b_low]);
772 let high = ahead(func, inst, Opcode::Xor, &[a_high, b_high]);
773 let both = ahead(func, inst, Opcode::Or, &[low, high]);
774 let zero = ahead_const(func, inst, 0);
775 compared(func, inst, pred, both, zero)
776 } else {
777 let above = compared(func, inst, strict(pred), a_high, b_high);
778 let below = compared(func, inst, unsigned(pred), a_low, b_low);
779 let same = compared(func, inst, IntPred::Eq, a_high, b_high);
780 let tail = bit(func, inst, Opcode::And, same, below);
781 bit(func, inst, Opcode::Or, above, tail)
782 };
783 if let Some(result) = func[inst].first_result {
784 forward.insert(result, answer);
785 }
786 func.remove_inst(inst);
787}
788
789fn strict(pred: IntPred) -> IntPred {
791 match pred {
792 IntPred::Sle => IntPred::Slt,
793 IntPred::Sge => IntPred::Sgt,
794 IntPred::Ule => IntPred::Ult,
795 IntPred::Uge => IntPred::Ugt,
796 other => other,
797 }
798}
799
800fn unsigned(pred: IntPred) -> IntPred {
802 match pred {
803 IntPred::Slt => IntPred::Ult,
804 IntPred::Sle => IntPred::Ule,
805 IntPred::Sgt => IntPred::Ugt,
806 IntPred::Sge => IntPred::Uge,
807 other => other,
808 }
809}
810
811fn choose(func: &mut Func, halves: &mut Halves, inst: Inst) {
817 let args = func[func[inst].args].to_vec();
818 let [cond, then, other] = args[..] else { return };
819 let (Some(&(then_low, then_high)), Some(&(other_low, other_high))) =
820 (halves.get(&then), halves.get(&other))
821 else {
822 return;
823 };
824 let low = ahead(func, inst, Opcode::Select, &[cond, then_low, other_low]);
825 let high = ahead(func, inst, Opcode::Select, &[cond, then_high, other_high]);
826 replace(func, halves, inst, low, high);
827}
828
829fn truncate(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
835 let Some(&arg) = func[func[inst].args].first() else { return };
836 let Some(&(low, _)) = halves.get(&arg) else { return };
837 let Some(result) = func[inst].first_result else { return };
838 if func[result].ty.bits() == HALF {
839 forward.insert(result, low);
840 func.remove_inst(inst);
841 return;
842 }
843 becomes(func, inst, Opcode::Trunc, &[low]);
844}
845
846fn extend(func: &mut Func, halves: &mut Halves, inst: Inst, signed: bool) {
848 let Some(&arg) = func[func[inst].args].first() else { return };
849 let low = if func[arg].ty.bits() == HALF {
850 arg
851 } else {
852 let opcode = if signed { Opcode::SExt } else { Opcode::ZExt };
853 ahead(func, inst, opcode, &[arg])
854 };
855 let high = if signed {
856 let top = ahead_const(func, inst, i128::from(HALF - 1));
857 ahead(func, inst, Opcode::AShr, &[low, top])
858 } else {
859 ahead_const(func, inst, 0)
860 };
861 replace(func, halves, inst, low, high);
862}
863
864fn call(func: &mut Func, halves: &mut Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
871 let data = func[inst];
872 let Extra::Call(info) = data.extra else { return };
873 let info = func[info];
874 let args = spread(&func[data.args], halves);
875 let results: Vec<Type> = data
876 .results()
877 .map(|value| func[value].ty)
878 .flat_map(|ty| if is_wide(ty) { vec![half(), half()] } else { vec![ty] })
879 .collect();
880 let signature = func.add_signature(split_signature(&func[info.signature]));
881 let extra = Extra::Call(func.add_call(CallInfo { signature, ..info }));
882 let args = func.push_values(&args);
883 let span = func.span(inst);
884 let made = func.create_inst(InstData { args, extra, ..data }, &results, span);
885 func.insert_before(made, inst);
886 let mut fresh = func[made].results();
887 for old in data.results() {
888 if is_wide(func[old].ty) {
889 let (Some(low), Some(high)) = (fresh.next(), fresh.next()) else { return };
890 halves.insert(old, (low, high));
891 } else if let Some(again) = fresh.next() {
892 forward.insert(old, again);
893 }
894 }
895 func.remove_inst(inst);
896}
897
898fn flatten(func: &mut Func, halves: &Halves, inst: Inst) {
900 let args = spread(&func[func[inst].args], halves);
901 func[inst].args = func.push_values(&args);
902}
903
904fn edges(func: &mut Func, halves: &Halves, inst: Inst) {
906 for at in func.target_list(inst).iter() {
907 let call = func[at];
908 let args = func[call.args].to_vec();
909 if !args.iter().any(|value| halves.contains_key(value)) {
910 continue;
911 }
912 let args = func.push_values(&spread(&args, halves));
913 func.set_block_call(at, BlockCall { args, ..call });
914 }
915}
916
917fn spread(args: &[Value], halves: &Halves) -> Vec<Value> {
919 args.iter()
920 .flat_map(|value| match halves.get(value) {
921 Some(&(low, high)) => vec![low, high],
922 None => vec![*value],
923 })
924 .collect()
925}
926
927fn split_signature(signature: &Signature) -> Signature {
933 let split = |params: &[Param]| -> Vec<Param> {
934 params
935 .iter()
936 .flat_map(|param| {
937 if is_wide(param.ty) {
938 vec![Param::new(half()), Param::new(half())]
939 } else {
940 vec![*param]
941 }
942 })
943 .collect()
944 };
945 Signature {
946 params: split(&signature.params),
947 returns: split(&signature.returns),
948 variadic: signature.variadic,
949 }
950}
951
952fn replace(func: &mut Func, halves: &mut Halves, inst: Inst, low: Value, high: Value) {
954 if let Some(result) = func[inst].first_result {
955 halves.insert(result, (low, high));
956 }
957 func.remove_inst(inst);
958}
959
960fn substitute(func: &mut Func, forward: &HashMap<Value, Value>) {
966 if forward.is_empty() {
967 return;
968 }
969 let with = |value: Value| forward.get(&value).copied().unwrap_or(value);
970 for block in func.blocks().collect::<Vec<_>>() {
971 for inst in func.insts(block).collect::<Vec<Inst>>() {
972 let args = func[inst].args;
973 func.rewrite(args, with);
974 for call in func.successors(inst).collect::<Vec<_>>() {
975 func.rewrite(call.args, with);
976 }
977 }
978 }
979}
980
981fn word(info: MemInfo, at: u64) -> MemInfo {
983 let align = if at == 0 { info.align } else { info.align.min(8) };
984 MemInfo { size: STEP, align, ..info }
985}
986
987fn stepped(func: &mut Func, inst: Inst, from: Value) -> Value {
989 let step = ahead_const(func, inst, i128::from(STEP));
990 let args = func.push_values(&[from, step]);
991 written(func, inst, InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR)
992}
993
994fn read(func: &mut Func, inst: Inst, from: Value, info: MemInfo, flags: Flags) -> Value {
996 let extra = Extra::Mem(func.add_mem(info));
997 let args = func.push_values(&[from]);
998 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Load) };
999 written(func, inst, data, half())
1000}
1001
1002fn write(func: &mut Func, inst: Inst, value: Value, into: Value, info: MemInfo, flags: Flags) {
1004 let span = func.span(inst);
1005 let extra = Extra::Mem(func.add_mem(info));
1006 let args = func.push_values(&[value, into]);
1007 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Store) };
1008 let made = func.create_inst(data, &[], span);
1009 func.insert_before(made, inst);
1010}
1011
1012fn compared(func: &mut Func, inst: Inst, pred: IntPred, lhs: Value, rhs: Value) -> Value {
1015 let args = func.push_values(&[lhs, rhs]);
1016 let extra = Extra::IntPred(pred);
1017 written(func, inst, InstData { args, extra, ..InstData::new(Opcode::ICmp) }, Type::I1)
1018}
1019
1020fn bit(func: &mut Func, inst: Inst, opcode: Opcode, lhs: Value, rhs: Value) -> Value {
1022 let args = func.push_values(&[lhs, rhs]);
1023 written(func, inst, InstData { args, ..InstData::new(opcode) }, Type::I1)
1024}
1025
1026fn ahead(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) -> Value {
1028 let args = func.push_values(args);
1029 written(func, inst, InstData { args, ..InstData::new(opcode) }, half())
1030}
1031
1032fn ahead_const(func: &mut Func, inst: Inst, value: i128) -> Value {
1034 let extra = Extra::Imm(func.add_imm(Imm::int(value, half())));
1035 written(func, inst, InstData { extra, ..InstData::new(Opcode::IConst) }, half())
1036}
1037
1038fn written(func: &mut Func, inst: Inst, data: InstData, ty: Type) -> Value {
1040 let span = func.span(inst);
1041 let made = func.create_inst(data, &[ty], span);
1042 func.insert_before(made, inst);
1043 func[made].first_result.expect("an instruction created with one result has one")
1044}
1045
1046fn becomes(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) {
1048 let args = func.push_values(args);
1049 let data = &mut func[inst];
1050 data.opcode = opcode;
1051 data.args = args;
1052 data.extra = Extra::None;
1053 data.flags = data.flags.intersection(Flags::legal_on(opcode));
1054}
1055
1056#[cfg(test)]
1057mod tests {
1058 use rucc_base::Interner;
1059 use rucc_ir::{
1060 Block, Builder, Flags, Float, Func, MemOrder, Module, Restrict, Signature, Type, Value,
1061 };
1062 use rucc_target::x86_64::SYSV;
1063 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1064
1065 use super::{HALF, IntPred, MemInfo, Opcode, halves};
1066
1067 fn wide() -> Type {
1069 Type::int(super::WIDE)
1070 }
1071
1072 fn target() -> TargetInfo {
1073 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1074 }
1075
1076 fn printed(func: &Func, names: &mut Interner) -> String {
1077 let module = Module::new(names.intern("w.c"), &target());
1078 rucc_ir::print_func(&module, func, names)
1079 }
1080
1081 fn shell(names: &mut Interner, params: &[Type], returns: &[Type]) -> (Func, Block, Vec<Value>) {
1083 let signature = Signature::new().with_params(params).with_returns(returns);
1084 let mut func = Func::new(names.intern("f"), signature);
1085 let entry = func.create_block();
1086 let values = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
1087 (func, entry, values)
1088 }
1089
1090 fn info(size: u64, align: u32) -> MemInfo {
1092 MemInfo {
1093 size,
1094 align,
1095 order: MemOrder::NotAtomic,
1096 tbaa: None,
1097 owns: 0,
1098 restrict: Restrict::NONE,
1099 }
1100 }
1101
1102 #[test]
1103 fn an_add_carries_from_the_low_half_into_the_high_one() {
1104 let mut names = Interner::new();
1105 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1106 let mut build = Builder::new(&mut func, entry);
1107 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1108 build.ret(&[sum]);
1109
1110 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1111 let text = printed(&func, &mut names);
1112 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1113 assert_eq!(text.matches(" = add ").count(), 3, "three adds: {text}");
1116 assert_eq!(text.matches("icmp ult").count(), 1, "one carry: {text}");
1117 assert_eq!(text.matches(" = zext.i64 ").count(), 1, "the carry as a number: {text}");
1118 }
1119
1120 #[test]
1121 fn a_subtract_borrows_the_other_way_round() {
1122 let mut names = Interner::new();
1123 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1124 let mut build = Builder::new(&mut func, entry);
1125 let difference = build.binary(Opcode::Sub, params[0], params[1], Flags::NONE);
1126 build.ret(&[difference]);
1127
1128 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1129 let text = printed(&func, &mut names);
1130 assert_eq!(text.matches(" = sub ").count(), 3, "three subtracts: {text}");
1131 assert!(text.contains("icmp ult %0, %2"), "the operands are compared: {text}");
1134 }
1135
1136 #[test]
1137 fn the_signature_and_the_entry_block_say_the_same_thing() {
1138 let mut names = Interner::new();
1139 let (mut func, entry, params) = shell(&mut names, &[Type::int(32), wide()], &[wide()]);
1140 let mut build = Builder::new(&mut func, entry);
1141 build.ret(&[params[1]]);
1142
1143 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1144 assert_eq!(
1145 func.signature().param_types().collect::<Vec<_>>(),
1146 [Type::int(32), Type::int(HALF), Type::int(HALF)],
1147 "the wide parameter became two where it stood"
1148 );
1149 assert_eq!(
1150 func.signature().return_types().collect::<Vec<_>>(),
1151 [Type::int(HALF), Type::int(HALF)],
1152 "and so did what comes back"
1153 );
1154 let text = printed(&func, &mut names);
1155 assert!(text.contains("block0(%0: i32, %1: i64, %2: i64)"), "the block agrees: {text}");
1156 assert!(text.contains("return %1, %2"), "both halves go back: {text}");
1157 let _ = entry;
1158 }
1159
1160 #[test]
1161 fn a_read_takes_the_high_word_a_word_above_the_low_one() {
1162 let mut names = Interner::new();
1163 let (mut func, entry, params) = shell(&mut names, &[Type::PTR], &[wide()]);
1164 let mut build = Builder::new(&mut func, entry);
1165 let value = build.load(wide(), params[0], info(16, 16), Flags::NONE);
1166 build.ret(&[value]);
1167
1168 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1169 let text = printed(&func, &mut names);
1170 assert_eq!(text.matches(" = load.i64 ").count(), 2, "two reads: {text}");
1171 assert!(text.contains("ptr_add"), "the high word is a word up: {text}");
1172 assert!(text.contains("align 16"), "the low word keeps what the object had: {text}");
1175 assert!(text.contains("align 8"), "the high word knows less: {text}");
1176 }
1177
1178 #[test]
1179 fn an_equality_asks_once_about_both_halves() {
1180 let mut names = Interner::new();
1181 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1182 let mut build = Builder::new(&mut func, entry);
1183 let same = build.icmp(IntPred::Eq, params[0], params[1]);
1184 let answer = build.unary(Opcode::ZExt, same, Type::int(32));
1185 build.ret(&[answer]);
1186
1187 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1188 let text = printed(&func, &mut names);
1189 assert_eq!(text.matches("icmp").count(), 1, "one comparison: {text}");
1190 assert_eq!(text.matches(" = xor ").count(), 2, "the halves differ or they do not: {text}");
1191 }
1192
1193 #[test]
1194 fn an_ordering_reads_the_low_halves_without_a_sign() {
1195 let mut names = Interner::new();
1196 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1197 let mut build = Builder::new(&mut func, entry);
1198 let below = build.icmp(IntPred::Slt, params[0], params[1]);
1199 let answer = build.unary(Opcode::ZExt, below, Type::int(32));
1200 build.ret(&[answer]);
1201
1202 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1203 let text = printed(&func, &mut names);
1204 assert!(text.contains("icmp slt"), "the high halves keep the sign: {text}");
1205 assert!(text.contains("icmp ult"), "the low halves have none: {text}");
1206 assert!(
1207 text.contains("icmp eq"),
1208 "and the low halves only matter when the high tie: {text}"
1209 );
1210 }
1211
1212 #[test]
1219 fn an_ordering_that_allows_equality_asks_the_high_halves_a_strict_question() {
1220 let mut names = Interner::new();
1221 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1222 let mut build = Builder::new(&mut func, entry);
1223 let at_least = build.icmp(IntPred::Sge, params[0], params[1]);
1224 let answer = build.unary(Opcode::ZExt, at_least, Type::int(32));
1225 build.ret(&[answer]);
1226
1227 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1228 let text = printed(&func, &mut names);
1229 assert!(text.contains("icmp sgt"), "the high halves settle it outright: {text}");
1230 assert!(!text.contains("icmp sge"), "a tie in the high halves settles nothing: {text}");
1231 assert!(text.contains("icmp uge"), "the low halves are the ones allowed to tie: {text}");
1232 }
1233
1234 #[test]
1235 fn a_widening_puts_the_sign_of_the_value_in_the_high_half() {
1236 let mut names = Interner::new();
1237 let (mut func, entry, params) = shell(&mut names, &[Type::int(32)], &[wide()]);
1238 let mut build = Builder::new(&mut func, entry);
1239 let value = build.unary(Opcode::SExt, params[0], wide());
1240 build.ret(&[value]);
1241
1242 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1243 let text = printed(&func, &mut names);
1244 assert!(text.contains("sext.i64"), "the value fills the low half: {text}");
1245 assert!(text.contains("ashr"), "and its sign fills the high one: {text}");
1246 }
1247
1248 #[test]
1249 fn a_block_parameter_becomes_two_and_every_branch_passes_two() {
1250 let mut names = Interner::new();
1251 let (mut func, entry, params) = shell(&mut names, &[wide(), Type::int(32)], &[wide()]);
1252 let tail = func.create_block();
1253 let carried = func.append_param(tail, wide());
1254 let mut build = Builder::new(&mut func, entry);
1255 let zero = build.iconst(Type::int(32), 0);
1256 let taken = build.icmp(IntPred::Ne, params[1], zero);
1257 let other = build.iconst(wide(), 7);
1258 build.br_if(taken, tail, &[params[0]], tail, &[other]);
1259 let mut build = Builder::new(&mut func, tail);
1260 build.ret(&[carried]);
1261
1262 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1263 let text = printed(&func, &mut names);
1264 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1265 assert!(text.contains("block1(%7: i64, %8: i64)"), "the block takes two: {text}");
1266 assert_eq!(text.matches("block1(").count(), 3, "and both edges pass two: {text}");
1267 }
1268
1269 #[test]
1276 fn a_multiply_is_three_multiplies_and_the_carry_out_of_the_low_ones() {
1277 let mut names = Interner::new();
1278 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1279 let mut build = Builder::new(&mut func, entry);
1280 let product = build.binary(Opcode::Mul, params[0], params[1], Flags::NONE);
1281 build.ret(&[product]);
1282
1283 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1284 let text = printed(&func, &mut names);
1285 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1286 assert_eq!(text.matches(" = mul ").count(), 7, "three and the carry's four: {text}");
1287 }
1288
1289 #[test]
1296 fn each_of_the_four_divisions_calls_the_routine_of_that_name() {
1297 for (opcode, routine) in [
1298 (Opcode::UDiv, "__udivti3"),
1299 (Opcode::SDiv, "__divti3"),
1300 (Opcode::URem, "__umodti3"),
1301 (Opcode::SRem, "__modti3"),
1302 ] {
1303 let mut names = Interner::new();
1304 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1305 let mut build = Builder::new(&mut func, entry);
1306 let answer = build.binary(opcode, params[0], params[1], Flags::NONE);
1307 build.ret(&[answer]);
1308
1309 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1310 let text = printed(&func, &mut names);
1311 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1312 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1313 }
1314 }
1315
1316 #[test]
1322 fn a_divide_hands_over_four_halves_and_takes_two_back() {
1323 let mut names = Interner::new();
1324 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1325 let mut build = Builder::new(&mut func, entry);
1326 let quotient = build.binary(Opcode::UDiv, params[0], params[1], Flags::NONE);
1327 build.ret(&[quotient]);
1328
1329 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1330 let text = printed(&func, &mut names);
1331 assert!(text.contains("@__udivti3(%0, %1, %2, %3)"), "four halves go over: {text}");
1332 assert!(text.contains("return %4, %5"), "and two come back: {text}");
1333 }
1334
1335 #[test]
1341 fn a_divide_of_something_computed_calls_with_the_halves_of_it() {
1342 let mut names = Interner::new();
1343 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1344 let mut build = Builder::new(&mut func, entry);
1345 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1346 let quotient = build.binary(Opcode::SDiv, sum, params[1], Flags::NONE);
1347 build.ret(&[quotient]);
1348
1349 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1350 let text = printed(&func, &mut names);
1351 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1352 assert_eq!(text.matches(" = add ").count(), 3, "the sum is still a sum: {text}");
1353 assert_eq!(text.matches("call @__divti3").count(), 1, "one call: {text}");
1354 }
1355
1356 #[test]
1362 fn each_conversion_between_this_width_and_a_float_calls_the_routine_of_that_name() {
1363 let double = Type::float(Float::F64);
1364 let single = Type::float(Float::F32);
1365 let quad = Type::float(Float::F128);
1366 for (opcode, float, routine) in [
1367 (Opcode::SIToFP, double, "__floattidf"),
1368 (Opcode::SIToFP, single, "__floattisf"),
1369 (Opcode::UIToFP, double, "__floatuntidf"),
1370 (Opcode::UIToFP, single, "__floatuntisf"),
1371 (Opcode::SIToFP, quad, "__floattitf"),
1372 (Opcode::UIToFP, quad, "__floatuntitf"),
1373 ] {
1374 let mut names = Interner::new();
1375 let (mut func, entry, params) = shell(&mut names, &[wide()], &[float]);
1376 let mut build = Builder::new(&mut func, entry);
1377 let answer = build.unary(opcode, params[0], float);
1378 build.ret(&[answer]);
1379
1380 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1381 let text = printed(&func, &mut names);
1382 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1383 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1384 }
1385 for (opcode, float, routine) in [
1386 (Opcode::FPToSI, double, "__fixdfti"),
1387 (Opcode::FPToSI, single, "__fixsfti"),
1388 (Opcode::FPToUI, double, "__fixunsdfti"),
1389 (Opcode::FPToUI, single, "__fixunssfti"),
1390 (Opcode::FPToSI, quad, "__fixtfti"),
1391 (Opcode::FPToUI, quad, "__fixunstfti"),
1392 ] {
1393 let mut names = Interner::new();
1394 let (mut func, entry, params) = shell(&mut names, &[float], &[wide()]);
1395 let mut build = Builder::new(&mut func, entry);
1396 let answer = build.unary(opcode, params[0], wide());
1397 build.ret(&[answer]);
1398
1399 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1400 let text = printed(&func, &mut names);
1401 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1402 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1403 }
1404 }
1405
1406 #[test]
1412 fn a_conversion_hands_over_halves_one_way_and_takes_them_back_the_other() {
1413 let double = Type::float(Float::F64);
1414 let mut names = Interner::new();
1415 let (mut func, entry, params) = shell(&mut names, &[wide()], &[double]);
1416 let mut build = Builder::new(&mut func, entry);
1417 let answer = build.unary(Opcode::SIToFP, params[0], double);
1418 build.ret(&[answer]);
1419
1420 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1421 let text = printed(&func, &mut names);
1422 assert!(text.contains("@__floattidf(%0, %1)"), "two halves go over: {text}");
1423 assert!(text.contains("return %2"), "and one float comes back: {text}");
1424
1425 let mut names = Interner::new();
1426 let (mut func, entry, params) = shell(&mut names, &[double], &[wide()]);
1427 let mut build = Builder::new(&mut func, entry);
1428 let answer = build.unary(Opcode::FPToSI, params[0], wide());
1429 build.ret(&[answer]);
1430
1431 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1432 let text = printed(&func, &mut names);
1433 assert!(text.contains("@__fixdfti(%0)"), "the float goes over as it is: {text}");
1434 assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1435 }
1436
1437 #[test]
1444 fn a_conversion_against_a_quad_hands_over_the_pair_and_the_quad_whole() {
1445 let quad = Type::float(Float::F128);
1446 let mut names = Interner::new();
1447 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
1448 let mut build = Builder::new(&mut func, entry);
1449 let answer = build.unary(Opcode::UIToFP, params[0], quad);
1450 build.ret(&[answer]);
1451
1452 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1453 let text = printed(&func, &mut names);
1454 assert!(text.contains("@__floatuntitf(%0, %1)"), "two halves go over: {text}");
1455 assert!(text.contains("return %2"), "and one quad comes back: {text}");
1456
1457 let mut names = Interner::new();
1458 let (mut func, entry, params) = shell(&mut names, &[quad], &[wide()]);
1459 let mut build = Builder::new(&mut func, entry);
1460 let answer = build.unary(Opcode::FPToSI, params[0], wide());
1461 build.ret(&[answer]);
1462
1463 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1464 let text = printed(&func, &mut names);
1465 assert!(text.contains("@__fixtfti(%0)"), "the quad goes over as it is: {text}");
1466 assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1467 }
1468
1469 #[test]
1476 fn a_conversion_at_a_width_the_runtime_has_no_routine_for_is_left_alone() {
1477 let long = Type::float(Float::F80);
1478 let mut names = Interner::new();
1479 let (mut func, entry, params) = shell(&mut names, &[wide()], &[long]);
1480 let mut build = Builder::new(&mut func, entry);
1481 let answer = build.unary(Opcode::SIToFP, params[0], long);
1482 build.ret(&[answer]);
1483
1484 assert!(!halves(&mut func, &mut names, &SYSV), "the pass does not understand this one");
1485 let text = printed(&func, &mut names);
1486 assert!(text.contains("i128"), "the width is still there: {text}");
1487 }
1488
1489 #[test]
1496 fn a_shift_left_chooses_between_a_count_that_crossed_a_half_and_one_that_did_not() {
1497 let mut names = Interner::new();
1498 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1499 let mut build = Builder::new(&mut func, entry);
1500 let moved = build.binary(Opcode::Shl, params[0], params[1], Flags::NONE);
1501 build.ret(&[moved]);
1502
1503 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1504 let text = printed(&func, &mut names);
1505 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1506 assert_eq!(
1507 text.matches(" = shl ").count(),
1508 2,
1509 "one per half, and the far case reuses one: {text}"
1510 );
1511 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1512 assert_eq!(text.matches(" = lshr ").count(), 2, "the crossing bits, in two steps: {text}");
1513 }
1514
1515 #[test]
1522 fn the_bits_that_cross_move_one_place_and_then_the_rest_of_the_way() {
1523 let mut names = Interner::new();
1524 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1525 let mut build = Builder::new(&mut func, entry);
1526 let moved = build.binary(Opcode::LShr, params[0], params[1], Flags::NONE);
1527 build.ret(&[moved]);
1528
1529 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1530 let text = printed(&func, &mut names);
1531 assert!(text.contains("iconst.i64 63"), "sixty three is the distance left: {text}");
1532 assert!(text.contains("iconst.i64 1"), "after the one place that comes first: {text}");
1533 assert!(text.contains(" = sub "), "the rest of the way is worked out: {text}");
1534 assert!(
1535 !text.contains("iconst.i64 127"),
1536 "and the count is not masked to the width: {text}"
1537 );
1538 }
1539
1540 #[test]
1546 fn an_arithmetic_shift_right_leaves_the_sign_bit_where_a_logical_one_leaves_zeroes() {
1547 let mut names = Interner::new();
1548 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1549 let mut build = Builder::new(&mut func, entry);
1550 let moved = build.binary(Opcode::AShr, params[0], params[1], Flags::NONE);
1551 build.ret(&[moved]);
1552
1553 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1554 let text = printed(&func, &mut names);
1555 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1556 assert_eq!(text.matches(" = ashr ").count(), 2, "the count and the sign: {text}");
1558 assert_eq!(text.matches(" = lshr ").count(), 1, "the low half is not signed: {text}");
1559 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1560 }
1561
1562 #[test]
1563 fn a_parameter_with_one_register_left_leaves_the_function_alone() {
1564 let mut names = Interner::new();
1565 let word = Type::int(HALF);
1566 let params = [word, word, word, word, word, wide()];
1570 let (mut func, entry, values) = shell(&mut names, ¶ms, &[word]);
1571 let mut build = Builder::new(&mut func, entry);
1572 let low = build.unary(Opcode::Trunc, values[5], word);
1573 build.ret(&[low]);
1574 let before = printed(&func, &mut names);
1575
1576 assert!(!halves(&mut func, &mut names, &SYSV), "one of the halves has no register");
1577 assert_eq!(printed(&func, &mut names), before, "so nothing moved");
1578 }
1579
1580 #[test]
1589 fn a_block_made_after_the_one_it_runs_before_is_still_split() {
1590 let mut names = Interner::new();
1591 let (mut func, entry, params) = shell(&mut names, &[wide()], &[wide()]);
1592 let tail = func.create_block();
1593 let middle = func.create_block();
1594 let mut build = Builder::new(&mut func, entry);
1595 build.jump(middle, &[]);
1596 let mut build = Builder::new(&mut func, middle);
1597 let doubled = build.binary(Opcode::Add, params[0], params[0], Flags::NONE);
1598 build.jump(tail, &[]);
1599 let mut build = Builder::new(&mut func, tail);
1600 let again = build.binary(Opcode::Add, doubled, doubled, Flags::NONE);
1601 build.ret(&[again]);
1602
1603 assert!(
1604 halves(&mut func, &mut names, &SYSV),
1605 "the definition runs before the use whatever the list says"
1606 );
1607 let text = printed(&func, &mut names);
1608 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1609 }
1610
1611 #[test]
1612 fn a_function_with_nothing_that_wide_is_not_touched() {
1613 let mut names = Interner::new();
1614 let word = Type::int(HALF);
1615 let (mut func, entry, params) = shell(&mut names, &[word, word], &[word]);
1616 let mut build = Builder::new(&mut func, entry);
1617 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1618 build.ret(&[sum]);
1619
1620 assert!(!halves(&mut func, &mut names, &SYSV), "there is nothing to split");
1621 }
1622}