1use std::collections::{HashMap, HashSet};
48
49use rucc_ir::{
50 Abi, Block, BlockCall, CallInfo, Def, Extra, Flags, Func, Imm, Inst, InstData, IntPred,
51 MemInfo, Opcode, Param, Signature, Type, Value,
52};
53use rucc_target::{CallRegs, Places, Where};
54
55use crate::expand;
56
57const WIDE: u32 = 128;
59
60const HALF: u32 = 64;
62
63const STEP: u64 = 8;
65
66fn is_wide(ty: Type) -> bool {
68 ty.is_int() && ty.is_scalar() && ty.bits() == WIDE
69}
70
71fn half() -> Type {
73 Type::int(HALF)
74}
75
76pub fn halves(func: &mut Func, conv: &CallRegs) -> bool {
87 if !func.values().any(|value| is_wide(func[value].ty)) {
88 return false;
89 }
90 let insts: Vec<Inst> =
91 walk(func).into_iter().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
92 let order: HashMap<Inst, usize> =
93 insts.iter().enumerate().map(|(at, &inst)| (inst, at)).collect();
94 if !insts.iter().enumerate().all(|(at, &inst)| can_split(func, &order, at, inst)) {
95 return false;
96 }
97 if !func.signatures().all(|signature| fits(signature, conv)) {
98 return false;
99 }
100
101 let mut halves: Halves = HashMap::new();
102 let mut forward: HashMap<Value, Value> = HashMap::new();
103 for block in func.blocks().collect::<Vec<_>>() {
104 params(func, block, &mut halves, &mut forward);
105 }
106 for &inst in &insts {
107 rewrite(func, &mut halves, &mut forward, inst);
108 }
109 substitute(func, &forward);
110 let signature = split_signature(func.signature());
111 func.set_signature(signature);
112 true
113}
114
115fn walk(func: &Func) -> Vec<Block> {
133 let Some(entry) = func.entry() else { return func.blocks().collect() };
134 let mut seen: HashSet<Block> = HashSet::new();
135 let mut order: Vec<Block> = Vec::new();
136 let mut stack: Vec<(Block, bool)> = vec![(entry, false)];
139 seen.insert(entry);
140 while let Some((block, done)) = stack.pop() {
141 if done {
142 order.push(block);
143 continue;
144 }
145 stack.push((block, true));
146 let Some(term) = func.terminator(block) else { continue };
147 for call in func.successors(term) {
148 if seen.insert(call.block) {
149 stack.push((call.block, false));
150 }
151 }
152 }
153 order.reverse();
154 order.extend(func.blocks().filter(|block| !seen.contains(block)));
155 order
156}
157
158type Halves = HashMap<Value, (Value, Value)>;
160
161fn understood(opcode: Opcode) -> bool {
172 matches!(
173 opcode,
174 Opcode::IConst
175 | Opcode::Load
176 | Opcode::Store
177 | Opcode::Add
178 | Opcode::Sub
179 | Opcode::Mul
180 | Opcode::Shl
181 | Opcode::LShr
182 | Opcode::AShr
183 | Opcode::And
184 | Opcode::Or
185 | Opcode::Xor
186 | Opcode::ICmp
187 | Opcode::Select
188 | Opcode::Trunc
189 | Opcode::SExt
190 | Opcode::ZExt
191 | Opcode::Call
192 | Opcode::CallIndirect
193 | Opcode::Return
194 | Opcode::Jump
195 | Opcode::BrIf
196 )
197}
198
199fn can_split(func: &Func, order: &HashMap<Inst, usize>, at: usize, inst: Inst) -> bool {
204 let data = func[inst];
205 let reads = operands(func, inst);
206 let wide = |&value: &Value| is_wide(func[value].ty);
207 if !reads.iter().any(wide) && !data.results().any(|value| is_wide(func[value].ty)) {
208 return true;
209 }
210 if !understood(data.opcode) {
211 return false;
212 }
213 if func.carries_mem(inst) {
217 return false;
218 }
219 if data.opcode == Opcode::SExt && reads.iter().any(|&value| func[value].ty.bits() < 8) {
223 return false;
224 }
225 if matches!(data.opcode, Opcode::Call | Opcode::CallIndirect) {
229 let Extra::Call(info) = data.extra else { return false };
230 if func[func[info].signature].variadic {
231 return false;
232 }
233 }
234 reads.iter().filter(|value| wide(value)).all(|&value| match func[value].def {
238 Def::Result { inst, .. } => order.get(&inst).is_some_and(|&def| def < at),
239 Def::Param { .. } => true,
240 })
241}
242
243fn operands(func: &Func, inst: Inst) -> Vec<Value> {
249 let mut reads = func[func[inst].args].to_vec();
250 for call in func.successors(inst).collect::<Vec<_>>() {
251 reads.extend_from_slice(&func[call.args]);
252 }
253 reads
254}
255
256fn fits(signature: &Signature, conv: &CallRegs) -> bool {
268 let mut places = Places::new(conv);
269 for param in &signature.params {
270 if let Abi::ByVal { size, align } = param.abi {
274 places.on_stack(u32::try_from(size).unwrap_or(u32::MAX), align);
275 } else if crate::abi::on_the_stack(param.ty) {
276 let (size, align) = crate::abi::X87_AREA;
277 places.on_stack(size, align);
278 } else if is_wide(param.ty) {
279 let low = places.integer();
280 let high = places.integer();
281 if !matches!((low, high), (Where::Reg(_), Where::Reg(_))) {
282 return false;
283 }
284 } else if param.ty.is_float() {
285 places.float();
286 } else {
287 places.integer();
288 }
289 }
290 true
291}
292
293fn params(func: &mut Func, block: Block, halves: &mut Halves, forward: &mut HashMap<Value, Value>) {
300 let old: Vec<Value> = func[block].params.clone();
301 if !old.iter().any(|&value| is_wide(func[value].ty)) {
302 return;
303 }
304 for &value in &old {
305 if is_wide(func[value].ty) {
306 let low = func.append_param(block, half());
307 let high = func.append_param(block, half());
308 halves.insert(value, (low, high));
309 } else {
310 let again = func.append_param(block, func[value].ty);
311 forward.insert(value, again);
312 }
313 }
314 func.retain_params(block, |value| !old.contains(&value));
315}
316
317fn rewrite(func: &mut Func, halves: &mut Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
319 let data = func[inst];
320 let produces = data.results().any(|value| is_wide(func[value].ty));
321 let takes = func[data.args].iter().any(|&value| is_wide(func[value].ty));
322 match data.opcode {
323 Opcode::IConst if produces => constant(func, halves, inst),
324 Opcode::Load if produces => load(func, halves, inst),
325 Opcode::Store if takes => store(func, halves, inst),
326 Opcode::Add | Opcode::Sub if produces => carried(func, halves, inst, data.opcode),
327 Opcode::Mul if produces => multiply(func, halves, inst),
328 Opcode::Shl | Opcode::LShr | Opcode::AShr if produces => {
329 shifted(func, halves, inst, data.opcode);
330 }
331 Opcode::And | Opcode::Or | Opcode::Xor if produces => {
332 bitwise(func, halves, inst, data.opcode);
333 }
334 Opcode::ICmp if takes => compare(func, halves, forward, inst),
335 Opcode::Select if produces => choose(func, halves, inst),
336 Opcode::Trunc if takes => truncate(func, halves, forward, inst),
337 Opcode::SExt | Opcode::ZExt if produces => {
338 extend(func, halves, inst, data.opcode == Opcode::SExt);
339 }
340 Opcode::Call | Opcode::CallIndirect if produces || takes => {
341 call(func, halves, forward, inst);
342 }
343 Opcode::Return if takes => flatten(func, halves, inst),
344 Opcode::Jump | Opcode::BrIf => edges(func, halves, inst),
345 _ => {}
346 }
347}
348
349fn constant(func: &mut Func, halves: &mut Halves, inst: Inst) {
351 let Extra::Imm(imm) = func[inst].extra else { return };
352 let bits = func[imm].unsigned();
353 #[expect(clippy::cast_possible_truncation, reason = "the halves are what this is taking")]
354 let (low, high) = (bits as u64, (bits >> HALF) as u64);
355 let low = ahead_const(func, inst, i128::from(low));
356 let high = ahead_const(func, inst, i128::from(high));
357 replace(func, halves, inst, low, high);
358}
359
360fn load(func: &mut Func, halves: &mut Halves, inst: Inst) {
366 let data = func[inst];
367 let Extra::Mem(mem) = data.extra else { return };
368 let info = func[mem];
369 let Some(&from) = func[data.args].first() else { return };
370 let low = read(func, inst, from, word(info, 0), data.flags);
371 let up = stepped(func, inst, from);
372 let high = read(func, inst, up, word(info, STEP), data.flags);
373 replace(func, halves, inst, low, high);
374}
375
376fn store(func: &mut Func, halves: &mut Halves, inst: Inst) {
378 let data = func[inst];
379 let Extra::Mem(mem) = data.extra else { return };
380 let info = func[mem];
381 let args = func[data.args].to_vec();
382 let [value, into] = args[..] else { return };
383 let Some(&(low, high)) = halves.get(&value) else { return };
384 write(func, inst, low, into, word(info, 0), data.flags);
385 let up = stepped(func, inst, into);
386 write(func, inst, high, up, word(info, STEP), data.flags);
387 func.remove_inst(inst);
388}
389
390fn carried(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
400 let args = func[func[inst].args].to_vec();
401 let [a, b] = args[..] else { return };
402 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
403 return;
404 };
405 let low = ahead(func, inst, opcode, &[a_low, b_low]);
406 let carried = if opcode == Opcode::Add {
407 compared(func, inst, IntPred::Ult, low, a_low)
408 } else {
409 compared(func, inst, IntPred::Ult, a_low, b_low)
410 };
411 let carry = ahead(func, inst, Opcode::ZExt, &[carried]);
412 let high = ahead(func, inst, opcode, &[a_high, b_high]);
413 let high = ahead(func, inst, opcode, &[high, carry]);
414 replace(func, halves, inst, low, high);
415}
416
417fn multiply(func: &mut Func, halves: &mut Halves, inst: Inst) {
437 let args = func[func[inst].args].to_vec();
438 let [a, b] = args[..] else { return };
439 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
440 return;
441 };
442 let low = ahead(func, inst, Opcode::Mul, &[a_low, b_low]);
443 let carried = expand::high_half(func, inst, a_low, b_low, false, half());
444 let cross = ahead(func, inst, Opcode::Mul, &[a_low, b_high]);
445 let other = ahead(func, inst, Opcode::Mul, &[a_high, b_low]);
446 let high = ahead(func, inst, Opcode::Add, &[carried, cross]);
447 let high = ahead(func, inst, Opcode::Add, &[high, other]);
448 replace(func, halves, inst, low, high);
449}
450
451fn shifted(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
471 let args = func[func[inst].args].to_vec();
472 let [a, b] = args[..] else { return };
473 let (Some(&(a_low, a_high)), Some(&(count, _))) = (halves.get(&a), halves.get(&b)) else {
474 return;
475 };
476 let top = ahead_const(func, inst, i128::from(HALF - 1));
477 let places = ahead(func, inst, Opcode::And, &[count, top]);
478 let back = ahead(func, inst, Opcode::Sub, &[top, places]);
479 let one = ahead_const(func, inst, 1);
480 let zero = ahead_const(func, inst, 0);
481 let bit = ahead_const(func, inst, i128::from(HALF));
482 let reach = ahead(func, inst, Opcode::And, &[count, bit]);
483 let whole = compared(func, inst, IntPred::Ne, reach, zero);
484
485 let (low, high) = if opcode == Opcode::Shl {
486 let moved = ahead(func, inst, Opcode::Shl, &[a_low, places]);
487 let edge = ahead(func, inst, Opcode::LShr, &[a_low, one]);
488 let across = ahead(func, inst, Opcode::LShr, &[edge, back]);
489 let above = ahead(func, inst, Opcode::Shl, &[a_high, places]);
490 let joined = ahead(func, inst, Opcode::Or, &[above, across]);
491 let low = ahead(func, inst, Opcode::Select, &[whole, zero, moved]);
492 let high = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
493 (low, high)
494 } else {
495 let moved = ahead(func, inst, opcode, &[a_high, places]);
496 let edge = ahead(func, inst, Opcode::Shl, &[a_high, one]);
497 let across = ahead(func, inst, Opcode::Shl, &[edge, back]);
498 let below = ahead(func, inst, Opcode::LShr, &[a_low, places]);
499 let joined = ahead(func, inst, Opcode::Or, &[below, across]);
500 let spent = if opcode == Opcode::AShr {
503 ahead(func, inst, Opcode::AShr, &[a_high, top])
504 } else {
505 zero
506 };
507 let low = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
508 let high = ahead(func, inst, Opcode::Select, &[whole, spent, moved]);
509 (low, high)
510 };
511 replace(func, halves, inst, low, high);
512}
513
514fn bitwise(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
517 let args = func[func[inst].args].to_vec();
518 let [a, b] = args[..] else { return };
519 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
520 return;
521 };
522 let low = ahead(func, inst, opcode, &[a_low, b_low]);
523 let high = ahead(func, inst, opcode, &[a_high, b_high]);
524 replace(func, halves, inst, low, high);
525}
526
527fn compare(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
542 let Extra::IntPred(pred) = func[inst].extra else { return };
543 let args = func[func[inst].args].to_vec();
544 let [a, b] = args[..] else { return };
545 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
546 return;
547 };
548 let answer = if matches!(pred, IntPred::Eq | IntPred::Ne) {
549 let low = ahead(func, inst, Opcode::Xor, &[a_low, b_low]);
550 let high = ahead(func, inst, Opcode::Xor, &[a_high, b_high]);
551 let both = ahead(func, inst, Opcode::Or, &[low, high]);
552 let zero = ahead_const(func, inst, 0);
553 compared(func, inst, pred, both, zero)
554 } else {
555 let above = compared(func, inst, strict(pred), a_high, b_high);
556 let below = compared(func, inst, unsigned(pred), a_low, b_low);
557 let same = compared(func, inst, IntPred::Eq, a_high, b_high);
558 let tail = bit(func, inst, Opcode::And, same, below);
559 bit(func, inst, Opcode::Or, above, tail)
560 };
561 if let Some(result) = func[inst].first_result {
562 forward.insert(result, answer);
563 }
564 func.remove_inst(inst);
565}
566
567fn strict(pred: IntPred) -> IntPred {
569 match pred {
570 IntPred::Sle => IntPred::Slt,
571 IntPred::Sge => IntPred::Sgt,
572 IntPred::Ule => IntPred::Ult,
573 IntPred::Uge => IntPred::Ugt,
574 other => other,
575 }
576}
577
578fn unsigned(pred: IntPred) -> IntPred {
580 match pred {
581 IntPred::Slt => IntPred::Ult,
582 IntPred::Sle => IntPred::Ule,
583 IntPred::Sgt => IntPred::Ugt,
584 IntPred::Sge => IntPred::Uge,
585 other => other,
586 }
587}
588
589fn choose(func: &mut Func, halves: &mut Halves, inst: Inst) {
595 let args = func[func[inst].args].to_vec();
596 let [cond, then, other] = args[..] else { return };
597 let (Some(&(then_low, then_high)), Some(&(other_low, other_high))) =
598 (halves.get(&then), halves.get(&other))
599 else {
600 return;
601 };
602 let low = ahead(func, inst, Opcode::Select, &[cond, then_low, other_low]);
603 let high = ahead(func, inst, Opcode::Select, &[cond, then_high, other_high]);
604 replace(func, halves, inst, low, high);
605}
606
607fn truncate(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
613 let Some(&arg) = func[func[inst].args].first() else { return };
614 let Some(&(low, _)) = halves.get(&arg) else { return };
615 let Some(result) = func[inst].first_result else { return };
616 if func[result].ty.bits() == HALF {
617 forward.insert(result, low);
618 func.remove_inst(inst);
619 return;
620 }
621 becomes(func, inst, Opcode::Trunc, &[low]);
622}
623
624fn extend(func: &mut Func, halves: &mut Halves, inst: Inst, signed: bool) {
626 let Some(&arg) = func[func[inst].args].first() else { return };
627 let low = if func[arg].ty.bits() == HALF {
628 arg
629 } else {
630 let opcode = if signed { Opcode::SExt } else { Opcode::ZExt };
631 ahead(func, inst, opcode, &[arg])
632 };
633 let high = if signed {
634 let top = ahead_const(func, inst, i128::from(HALF - 1));
635 ahead(func, inst, Opcode::AShr, &[low, top])
636 } else {
637 ahead_const(func, inst, 0)
638 };
639 replace(func, halves, inst, low, high);
640}
641
642fn call(func: &mut Func, halves: &mut Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
649 let data = func[inst];
650 let Extra::Call(info) = data.extra else { return };
651 let info = func[info];
652 let args = spread(&func[data.args], halves);
653 let results: Vec<Type> = data
654 .results()
655 .map(|value| func[value].ty)
656 .flat_map(|ty| if is_wide(ty) { vec![half(), half()] } else { vec![ty] })
657 .collect();
658 let signature = func.add_signature(split_signature(&func[info.signature]));
659 let extra = Extra::Call(func.add_call(CallInfo { signature, ..info }));
660 let args = func.push_values(&args);
661 let span = func.span(inst);
662 let made = func.create_inst(InstData { args, extra, ..data }, &results, span);
663 func.insert_before(made, inst);
664 let mut fresh = func[made].results();
665 for old in data.results() {
666 if is_wide(func[old].ty) {
667 let (Some(low), Some(high)) = (fresh.next(), fresh.next()) else { return };
668 halves.insert(old, (low, high));
669 } else if let Some(again) = fresh.next() {
670 forward.insert(old, again);
671 }
672 }
673 func.remove_inst(inst);
674}
675
676fn flatten(func: &mut Func, halves: &Halves, inst: Inst) {
678 let args = spread(&func[func[inst].args], halves);
679 func[inst].args = func.push_values(&args);
680}
681
682fn edges(func: &mut Func, halves: &Halves, inst: Inst) {
684 for at in func.target_list(inst).iter() {
685 let call = func[at];
686 let args = func[call.args].to_vec();
687 if !args.iter().any(|value| halves.contains_key(value)) {
688 continue;
689 }
690 let args = func.push_values(&spread(&args, halves));
691 func.set_block_call(at, BlockCall { block: call.block, args });
692 }
693}
694
695fn spread(args: &[Value], halves: &Halves) -> Vec<Value> {
697 args.iter()
698 .flat_map(|value| match halves.get(value) {
699 Some(&(low, high)) => vec![low, high],
700 None => vec![*value],
701 })
702 .collect()
703}
704
705fn split_signature(signature: &Signature) -> Signature {
711 let split = |params: &[Param]| -> Vec<Param> {
712 params
713 .iter()
714 .flat_map(|param| {
715 if is_wide(param.ty) {
716 vec![Param::new(half()), Param::new(half())]
717 } else {
718 vec![*param]
719 }
720 })
721 .collect()
722 };
723 Signature {
724 params: split(&signature.params),
725 returns: split(&signature.returns),
726 variadic: signature.variadic,
727 }
728}
729
730fn replace(func: &mut Func, halves: &mut Halves, inst: Inst, low: Value, high: Value) {
732 if let Some(result) = func[inst].first_result {
733 halves.insert(result, (low, high));
734 }
735 func.remove_inst(inst);
736}
737
738fn substitute(func: &mut Func, forward: &HashMap<Value, Value>) {
744 if forward.is_empty() {
745 return;
746 }
747 let with = |value: Value| forward.get(&value).copied().unwrap_or(value);
748 for block in func.blocks().collect::<Vec<_>>() {
749 for inst in func.insts(block).collect::<Vec<Inst>>() {
750 let args = func[inst].args;
751 func.rewrite(args, with);
752 for call in func.successors(inst).collect::<Vec<_>>() {
753 func.rewrite(call.args, with);
754 }
755 }
756 }
757}
758
759fn word(info: MemInfo, at: u64) -> MemInfo {
761 let align = if at == 0 { info.align } else { info.align.min(8) };
762 MemInfo { size: STEP, align, ..info }
763}
764
765fn stepped(func: &mut Func, inst: Inst, from: Value) -> Value {
767 let step = ahead_const(func, inst, i128::from(STEP));
768 let args = func.push_values(&[from, step]);
769 written(func, inst, InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR)
770}
771
772fn read(func: &mut Func, inst: Inst, from: Value, info: MemInfo, flags: Flags) -> Value {
774 let extra = Extra::Mem(func.add_mem(info));
775 let args = func.push_values(&[from]);
776 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Load) };
777 written(func, inst, data, half())
778}
779
780fn write(func: &mut Func, inst: Inst, value: Value, into: Value, info: MemInfo, flags: Flags) {
782 let span = func.span(inst);
783 let extra = Extra::Mem(func.add_mem(info));
784 let args = func.push_values(&[value, into]);
785 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Store) };
786 let made = func.create_inst(data, &[], span);
787 func.insert_before(made, inst);
788}
789
790fn compared(func: &mut Func, inst: Inst, pred: IntPred, lhs: Value, rhs: Value) -> Value {
793 let args = func.push_values(&[lhs, rhs]);
794 let extra = Extra::IntPred(pred);
795 written(func, inst, InstData { args, extra, ..InstData::new(Opcode::ICmp) }, Type::I1)
796}
797
798fn bit(func: &mut Func, inst: Inst, opcode: Opcode, lhs: Value, rhs: Value) -> Value {
800 let args = func.push_values(&[lhs, rhs]);
801 written(func, inst, InstData { args, ..InstData::new(opcode) }, Type::I1)
802}
803
804fn ahead(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) -> Value {
806 let args = func.push_values(args);
807 written(func, inst, InstData { args, ..InstData::new(opcode) }, half())
808}
809
810fn ahead_const(func: &mut Func, inst: Inst, value: i128) -> Value {
812 let extra = Extra::Imm(func.add_imm(Imm::int(value, half())));
813 written(func, inst, InstData { extra, ..InstData::new(Opcode::IConst) }, half())
814}
815
816fn written(func: &mut Func, inst: Inst, data: InstData, ty: Type) -> Value {
818 let span = func.span(inst);
819 let made = func.create_inst(data, &[ty], span);
820 func.insert_before(made, inst);
821 func[made].first_result.expect("an instruction created with one result has one")
822}
823
824fn becomes(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) {
826 let args = func.push_values(args);
827 let data = &mut func[inst];
828 data.opcode = opcode;
829 data.args = args;
830 data.extra = Extra::None;
831 data.flags = data.flags.intersection(Flags::legal_on(opcode));
832}
833
834#[cfg(test)]
835mod tests {
836 use rucc_base::Interner;
837 use rucc_ir::{
838 Block, Builder, Flags, Func, MemOrder, Module, Restrict, Signature, Type, Value,
839 };
840 use rucc_target::x86_64::SYSV;
841 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
842
843 use super::{HALF, IntPred, MemInfo, Opcode, halves};
844
845 fn wide() -> Type {
847 Type::int(super::WIDE)
848 }
849
850 fn target() -> TargetInfo {
851 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
852 }
853
854 fn printed(func: &Func, names: &mut Interner) -> String {
855 let module = Module::new(names.intern("w.c"), &target());
856 rucc_ir::print_func(&module, func, names)
857 }
858
859 fn shell(names: &mut Interner, params: &[Type], returns: &[Type]) -> (Func, Block, Vec<Value>) {
861 let signature = Signature::new().with_params(params).with_returns(returns);
862 let mut func = Func::new(names.intern("f"), signature);
863 let entry = func.create_block();
864 let values = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
865 (func, entry, values)
866 }
867
868 fn info(size: u64, align: u32) -> MemInfo {
870 MemInfo {
871 size,
872 align,
873 order: MemOrder::NotAtomic,
874 tbaa: None,
875 owns: 0,
876 restrict: Restrict::NONE,
877 }
878 }
879
880 #[test]
881 fn an_add_carries_from_the_low_half_into_the_high_one() {
882 let mut names = Interner::new();
883 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
884 let mut build = Builder::new(&mut func, entry);
885 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
886 build.ret(&[sum]);
887
888 assert!(halves(&mut func, &SYSV), "there is a width to split");
889 let text = printed(&func, &mut names);
890 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
891 assert_eq!(text.matches(" = add ").count(), 3, "three adds: {text}");
894 assert_eq!(text.matches("icmp ult").count(), 1, "one carry: {text}");
895 assert_eq!(text.matches(" = zext.i64 ").count(), 1, "the carry as a number: {text}");
896 }
897
898 #[test]
899 fn a_subtract_borrows_the_other_way_round() {
900 let mut names = Interner::new();
901 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
902 let mut build = Builder::new(&mut func, entry);
903 let difference = build.binary(Opcode::Sub, params[0], params[1], Flags::NONE);
904 build.ret(&[difference]);
905
906 assert!(halves(&mut func, &SYSV), "there is a width to split");
907 let text = printed(&func, &mut names);
908 assert_eq!(text.matches(" = sub ").count(), 3, "three subtracts: {text}");
909 assert!(text.contains("icmp ult %0, %2"), "the operands are compared: {text}");
912 }
913
914 #[test]
915 fn the_signature_and_the_entry_block_say_the_same_thing() {
916 let mut names = Interner::new();
917 let (mut func, entry, params) = shell(&mut names, &[Type::int(32), wide()], &[wide()]);
918 let mut build = Builder::new(&mut func, entry);
919 build.ret(&[params[1]]);
920
921 assert!(halves(&mut func, &SYSV), "there is a width to split");
922 assert_eq!(
923 func.signature().param_types().collect::<Vec<_>>(),
924 [Type::int(32), Type::int(HALF), Type::int(HALF)],
925 "the wide parameter became two where it stood"
926 );
927 assert_eq!(
928 func.signature().return_types().collect::<Vec<_>>(),
929 [Type::int(HALF), Type::int(HALF)],
930 "and so did what comes back"
931 );
932 let text = printed(&func, &mut names);
933 assert!(text.contains("block0(%0: i32, %1: i64, %2: i64)"), "the block agrees: {text}");
934 assert!(text.contains("return %1, %2"), "both halves go back: {text}");
935 let _ = entry;
936 }
937
938 #[test]
939 fn a_read_takes_the_high_word_a_word_above_the_low_one() {
940 let mut names = Interner::new();
941 let (mut func, entry, params) = shell(&mut names, &[Type::PTR], &[wide()]);
942 let mut build = Builder::new(&mut func, entry);
943 let value = build.load(wide(), params[0], info(16, 16), Flags::NONE);
944 build.ret(&[value]);
945
946 assert!(halves(&mut func, &SYSV), "there is a width to split");
947 let text = printed(&func, &mut names);
948 assert_eq!(text.matches(" = load.i64 ").count(), 2, "two reads: {text}");
949 assert!(text.contains("ptr_add"), "the high word is a word up: {text}");
950 assert!(text.contains("align 16"), "the low word keeps what the object had: {text}");
953 assert!(text.contains("align 8"), "the high word knows less: {text}");
954 }
955
956 #[test]
957 fn an_equality_asks_once_about_both_halves() {
958 let mut names = Interner::new();
959 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
960 let mut build = Builder::new(&mut func, entry);
961 let same = build.icmp(IntPred::Eq, params[0], params[1]);
962 let answer = build.unary(Opcode::ZExt, same, Type::int(32));
963 build.ret(&[answer]);
964
965 assert!(halves(&mut func, &SYSV), "there is a width to split");
966 let text = printed(&func, &mut names);
967 assert_eq!(text.matches("icmp").count(), 1, "one comparison: {text}");
968 assert_eq!(text.matches(" = xor ").count(), 2, "the halves differ or they do not: {text}");
969 }
970
971 #[test]
972 fn an_ordering_reads_the_low_halves_without_a_sign() {
973 let mut names = Interner::new();
974 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
975 let mut build = Builder::new(&mut func, entry);
976 let below = build.icmp(IntPred::Slt, params[0], params[1]);
977 let answer = build.unary(Opcode::ZExt, below, Type::int(32));
978 build.ret(&[answer]);
979
980 assert!(halves(&mut func, &SYSV), "there is a width to split");
981 let text = printed(&func, &mut names);
982 assert!(text.contains("icmp slt"), "the high halves keep the sign: {text}");
983 assert!(text.contains("icmp ult"), "the low halves have none: {text}");
984 assert!(
985 text.contains("icmp eq"),
986 "and the low halves only matter when the high tie: {text}"
987 );
988 }
989
990 #[test]
997 fn an_ordering_that_allows_equality_asks_the_high_halves_a_strict_question() {
998 let mut names = Interner::new();
999 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1000 let mut build = Builder::new(&mut func, entry);
1001 let at_least = build.icmp(IntPred::Sge, params[0], params[1]);
1002 let answer = build.unary(Opcode::ZExt, at_least, Type::int(32));
1003 build.ret(&[answer]);
1004
1005 assert!(halves(&mut func, &SYSV), "there is a width to split");
1006 let text = printed(&func, &mut names);
1007 assert!(text.contains("icmp sgt"), "the high halves settle it outright: {text}");
1008 assert!(!text.contains("icmp sge"), "a tie in the high halves settles nothing: {text}");
1009 assert!(text.contains("icmp uge"), "the low halves are the ones allowed to tie: {text}");
1010 }
1011
1012 #[test]
1013 fn a_widening_puts_the_sign_of_the_value_in_the_high_half() {
1014 let mut names = Interner::new();
1015 let (mut func, entry, params) = shell(&mut names, &[Type::int(32)], &[wide()]);
1016 let mut build = Builder::new(&mut func, entry);
1017 let value = build.unary(Opcode::SExt, params[0], wide());
1018 build.ret(&[value]);
1019
1020 assert!(halves(&mut func, &SYSV), "there is a width to split");
1021 let text = printed(&func, &mut names);
1022 assert!(text.contains("sext.i64"), "the value fills the low half: {text}");
1023 assert!(text.contains("ashr"), "and its sign fills the high one: {text}");
1024 }
1025
1026 #[test]
1027 fn a_block_parameter_becomes_two_and_every_branch_passes_two() {
1028 let mut names = Interner::new();
1029 let (mut func, entry, params) = shell(&mut names, &[wide(), Type::int(32)], &[wide()]);
1030 let tail = func.create_block();
1031 let carried = func.append_param(tail, wide());
1032 let mut build = Builder::new(&mut func, entry);
1033 let zero = build.iconst(Type::int(32), 0);
1034 let taken = build.icmp(IntPred::Ne, params[1], zero);
1035 let other = build.iconst(wide(), 7);
1036 build.br_if(taken, tail, &[params[0]], tail, &[other]);
1037 let mut build = Builder::new(&mut func, tail);
1038 build.ret(&[carried]);
1039
1040 assert!(halves(&mut func, &SYSV), "there is a width to split");
1041 let text = printed(&func, &mut names);
1042 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1043 assert!(text.contains("block1(%7: i64, %8: i64)"), "the block takes two: {text}");
1044 assert_eq!(text.matches("block1(").count(), 3, "and both edges pass two: {text}");
1045 }
1046
1047 #[test]
1054 fn a_multiply_is_three_multiplies_and_the_carry_out_of_the_low_ones() {
1055 let mut names = Interner::new();
1056 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1057 let mut build = Builder::new(&mut func, entry);
1058 let product = build.binary(Opcode::Mul, params[0], params[1], Flags::NONE);
1059 build.ret(&[product]);
1060
1061 assert!(halves(&mut func, &SYSV), "there is a width to split");
1062 let text = printed(&func, &mut names);
1063 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1064 assert_eq!(text.matches(" = mul ").count(), 7, "three and the carry's four: {text}");
1065 }
1066
1067 #[test]
1074 fn a_shift_left_chooses_between_a_count_that_crossed_a_half_and_one_that_did_not() {
1075 let mut names = Interner::new();
1076 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1077 let mut build = Builder::new(&mut func, entry);
1078 let moved = build.binary(Opcode::Shl, params[0], params[1], Flags::NONE);
1079 build.ret(&[moved]);
1080
1081 assert!(halves(&mut func, &SYSV), "there is a width to split");
1082 let text = printed(&func, &mut names);
1083 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1084 assert_eq!(
1085 text.matches(" = shl ").count(),
1086 2,
1087 "one per half, and the far case reuses one: {text}"
1088 );
1089 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1090 assert_eq!(text.matches(" = lshr ").count(), 2, "the crossing bits, in two steps: {text}");
1091 }
1092
1093 #[test]
1100 fn the_bits_that_cross_move_one_place_and_then_the_rest_of_the_way() {
1101 let mut names = Interner::new();
1102 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1103 let mut build = Builder::new(&mut func, entry);
1104 let moved = build.binary(Opcode::LShr, params[0], params[1], Flags::NONE);
1105 build.ret(&[moved]);
1106
1107 assert!(halves(&mut func, &SYSV), "there is a width to split");
1108 let text = printed(&func, &mut names);
1109 assert!(text.contains("iconst.i64 63"), "sixty three is the distance left: {text}");
1110 assert!(text.contains("iconst.i64 1"), "after the one place that comes first: {text}");
1111 assert!(text.contains(" = sub "), "the rest of the way is worked out: {text}");
1112 assert!(
1113 !text.contains("iconst.i64 127"),
1114 "and the count is not masked to the width: {text}"
1115 );
1116 }
1117
1118 #[test]
1124 fn an_arithmetic_shift_right_leaves_the_sign_bit_where_a_logical_one_leaves_zeroes() {
1125 let mut names = Interner::new();
1126 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1127 let mut build = Builder::new(&mut func, entry);
1128 let moved = build.binary(Opcode::AShr, params[0], params[1], Flags::NONE);
1129 build.ret(&[moved]);
1130
1131 assert!(halves(&mut func, &SYSV), "there is a width to split");
1132 let text = printed(&func, &mut names);
1133 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1134 assert_eq!(text.matches(" = ashr ").count(), 2, "the count and the sign: {text}");
1136 assert_eq!(text.matches(" = lshr ").count(), 1, "the low half is not signed: {text}");
1137 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1138 }
1139
1140 #[test]
1141 fn a_parameter_with_one_register_left_leaves_the_function_alone() {
1142 let mut names = Interner::new();
1143 let word = Type::int(HALF);
1144 let params = [word, word, word, word, word, wide()];
1148 let (mut func, entry, values) = shell(&mut names, ¶ms, &[word]);
1149 let mut build = Builder::new(&mut func, entry);
1150 let low = build.unary(Opcode::Trunc, values[5], word);
1151 build.ret(&[low]);
1152 let before = printed(&func, &mut names);
1153
1154 assert!(!halves(&mut func, &SYSV), "one of the halves has no register");
1155 assert_eq!(printed(&func, &mut names), before, "so nothing moved");
1156 }
1157
1158 #[test]
1167 fn a_block_made_after_the_one_it_runs_before_is_still_split() {
1168 let mut names = Interner::new();
1169 let (mut func, entry, params) = shell(&mut names, &[wide()], &[wide()]);
1170 let tail = func.create_block();
1171 let middle = func.create_block();
1172 let mut build = Builder::new(&mut func, entry);
1173 build.jump(middle, &[]);
1174 let mut build = Builder::new(&mut func, middle);
1175 let doubled = build.binary(Opcode::Add, params[0], params[0], Flags::NONE);
1176 build.jump(tail, &[]);
1177 let mut build = Builder::new(&mut func, tail);
1178 let again = build.binary(Opcode::Add, doubled, doubled, Flags::NONE);
1179 build.ret(&[again]);
1180
1181 assert!(
1182 halves(&mut func, &SYSV),
1183 "the definition runs before the use whatever the list says"
1184 );
1185 let text = printed(&func, &mut names);
1186 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1187 }
1188
1189 #[test]
1190 fn a_function_with_nothing_that_wide_is_not_touched() {
1191 let mut names = Interner::new();
1192 let word = Type::int(HALF);
1193 let (mut func, entry, params) = shell(&mut names, &[word, word], &[word]);
1194 let mut build = Builder::new(&mut func, entry);
1195 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1196 build.ret(&[sum]);
1197
1198 assert!(!halves(&mut func, &SYSV), "there is nothing to split");
1199 }
1200}