1use std::collections::HashMap;
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 func.blocks().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
115type Halves = HashMap<Value, (Value, Value)>;
117
118fn understood(opcode: Opcode) -> bool {
129 matches!(
130 opcode,
131 Opcode::IConst
132 | Opcode::Load
133 | Opcode::Store
134 | Opcode::Add
135 | Opcode::Sub
136 | Opcode::Mul
137 | Opcode::Shl
138 | Opcode::LShr
139 | Opcode::AShr
140 | Opcode::And
141 | Opcode::Or
142 | Opcode::Xor
143 | Opcode::ICmp
144 | Opcode::Select
145 | Opcode::Trunc
146 | Opcode::SExt
147 | Opcode::ZExt
148 | Opcode::Call
149 | Opcode::CallIndirect
150 | Opcode::Return
151 | Opcode::Jump
152 | Opcode::BrIf
153 )
154}
155
156fn can_split(func: &Func, order: &HashMap<Inst, usize>, at: usize, inst: Inst) -> bool {
161 let data = func[inst];
162 let reads = operands(func, inst);
163 let wide = |&value: &Value| is_wide(func[value].ty);
164 if !reads.iter().any(wide) && !data.results().any(|value| is_wide(func[value].ty)) {
165 return true;
166 }
167 if !understood(data.opcode) {
168 return false;
169 }
170 if func.carries_mem(inst) {
174 return false;
175 }
176 if data.opcode == Opcode::SExt && reads.iter().any(|&value| func[value].ty.bits() < 8) {
180 return false;
181 }
182 if matches!(data.opcode, Opcode::Call | Opcode::CallIndirect) {
186 let Extra::Call(info) = data.extra else { return false };
187 if func[func[info].signature].variadic {
188 return false;
189 }
190 }
191 reads.iter().filter(|value| wide(value)).all(|&value| match func[value].def {
195 Def::Result { inst, .. } => order.get(&inst).is_some_and(|&def| def < at),
196 Def::Param { .. } => true,
197 })
198}
199
200fn operands(func: &Func, inst: Inst) -> Vec<Value> {
206 let mut reads = func[func[inst].args].to_vec();
207 for call in func.successors(inst).collect::<Vec<_>>() {
208 reads.extend_from_slice(&func[call.args]);
209 }
210 reads
211}
212
213fn fits(signature: &Signature, conv: &CallRegs) -> bool {
225 let mut places = Places::new(conv);
226 for param in &signature.params {
227 if let Abi::ByVal { size, align } = param.abi {
231 places.on_stack(u32::try_from(size).unwrap_or(u32::MAX), align);
232 } else if crate::abi::on_the_stack(param.ty) {
233 let (size, align) = crate::abi::X87_AREA;
234 places.on_stack(size, align);
235 } else if is_wide(param.ty) {
236 let low = places.integer();
237 let high = places.integer();
238 if !matches!((low, high), (Where::Reg(_), Where::Reg(_))) {
239 return false;
240 }
241 } else if param.ty.is_float() {
242 places.float();
243 } else {
244 places.integer();
245 }
246 }
247 true
248}
249
250fn params(func: &mut Func, block: Block, halves: &mut Halves, forward: &mut HashMap<Value, Value>) {
257 let old: Vec<Value> = func[block].params.clone();
258 if !old.iter().any(|&value| is_wide(func[value].ty)) {
259 return;
260 }
261 for &value in &old {
262 if is_wide(func[value].ty) {
263 let low = func.append_param(block, half());
264 let high = func.append_param(block, half());
265 halves.insert(value, (low, high));
266 } else {
267 let again = func.append_param(block, func[value].ty);
268 forward.insert(value, again);
269 }
270 }
271 func.retain_params(block, |value| !old.contains(&value));
272}
273
274fn rewrite(func: &mut Func, halves: &mut Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
276 let data = func[inst];
277 let produces = data.results().any(|value| is_wide(func[value].ty));
278 let takes = func[data.args].iter().any(|&value| is_wide(func[value].ty));
279 match data.opcode {
280 Opcode::IConst if produces => constant(func, halves, inst),
281 Opcode::Load if produces => load(func, halves, inst),
282 Opcode::Store if takes => store(func, halves, inst),
283 Opcode::Add | Opcode::Sub if produces => carried(func, halves, inst, data.opcode),
284 Opcode::Mul if produces => multiply(func, halves, inst),
285 Opcode::Shl | Opcode::LShr | Opcode::AShr if produces => {
286 shifted(func, halves, inst, data.opcode);
287 }
288 Opcode::And | Opcode::Or | Opcode::Xor if produces => {
289 bitwise(func, halves, inst, data.opcode);
290 }
291 Opcode::ICmp if takes => compare(func, halves, forward, inst),
292 Opcode::Select if produces => choose(func, halves, inst),
293 Opcode::Trunc if takes => truncate(func, halves, forward, inst),
294 Opcode::SExt | Opcode::ZExt if produces => {
295 extend(func, halves, inst, data.opcode == Opcode::SExt);
296 }
297 Opcode::Call | Opcode::CallIndirect if produces || takes => {
298 call(func, halves, forward, inst);
299 }
300 Opcode::Return if takes => flatten(func, halves, inst),
301 Opcode::Jump | Opcode::BrIf => edges(func, halves, inst),
302 _ => {}
303 }
304}
305
306fn constant(func: &mut Func, halves: &mut Halves, inst: Inst) {
308 let Extra::Imm(imm) = func[inst].extra else { return };
309 let bits = func[imm].unsigned();
310 #[expect(clippy::cast_possible_truncation, reason = "the halves are what this is taking")]
311 let (low, high) = (bits as u64, (bits >> HALF) as u64);
312 let low = ahead_const(func, inst, i128::from(low));
313 let high = ahead_const(func, inst, i128::from(high));
314 replace(func, halves, inst, low, high);
315}
316
317fn load(func: &mut Func, halves: &mut Halves, inst: Inst) {
323 let data = func[inst];
324 let Extra::Mem(mem) = data.extra else { return };
325 let info = func[mem];
326 let Some(&from) = func[data.args].first() else { return };
327 let low = read(func, inst, from, word(info, 0), data.flags);
328 let up = stepped(func, inst, from);
329 let high = read(func, inst, up, word(info, STEP), data.flags);
330 replace(func, halves, inst, low, high);
331}
332
333fn store(func: &mut Func, halves: &mut Halves, inst: Inst) {
335 let data = func[inst];
336 let Extra::Mem(mem) = data.extra else { return };
337 let info = func[mem];
338 let args = func[data.args].to_vec();
339 let [value, into] = args[..] else { return };
340 let Some(&(low, high)) = halves.get(&value) else { return };
341 write(func, inst, low, into, word(info, 0), data.flags);
342 let up = stepped(func, inst, into);
343 write(func, inst, high, up, word(info, STEP), data.flags);
344 func.remove_inst(inst);
345}
346
347fn carried(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
357 let args = func[func[inst].args].to_vec();
358 let [a, b] = args[..] else { return };
359 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
360 return;
361 };
362 let low = ahead(func, inst, opcode, &[a_low, b_low]);
363 let carried = if opcode == Opcode::Add {
364 compared(func, inst, IntPred::Ult, low, a_low)
365 } else {
366 compared(func, inst, IntPred::Ult, a_low, b_low)
367 };
368 let carry = ahead(func, inst, Opcode::ZExt, &[carried]);
369 let high = ahead(func, inst, opcode, &[a_high, b_high]);
370 let high = ahead(func, inst, opcode, &[high, carry]);
371 replace(func, halves, inst, low, high);
372}
373
374fn multiply(func: &mut Func, halves: &mut Halves, inst: Inst) {
394 let args = func[func[inst].args].to_vec();
395 let [a, b] = args[..] else { return };
396 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
397 return;
398 };
399 let low = ahead(func, inst, Opcode::Mul, &[a_low, b_low]);
400 let carried = expand::high_half(func, inst, a_low, b_low, false, half());
401 let cross = ahead(func, inst, Opcode::Mul, &[a_low, b_high]);
402 let other = ahead(func, inst, Opcode::Mul, &[a_high, b_low]);
403 let high = ahead(func, inst, Opcode::Add, &[carried, cross]);
404 let high = ahead(func, inst, Opcode::Add, &[high, other]);
405 replace(func, halves, inst, low, high);
406}
407
408fn shifted(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
428 let args = func[func[inst].args].to_vec();
429 let [a, b] = args[..] else { return };
430 let (Some(&(a_low, a_high)), Some(&(count, _))) = (halves.get(&a), halves.get(&b)) else {
431 return;
432 };
433 let top = ahead_const(func, inst, i128::from(HALF - 1));
434 let places = ahead(func, inst, Opcode::And, &[count, top]);
435 let back = ahead(func, inst, Opcode::Sub, &[top, places]);
436 let one = ahead_const(func, inst, 1);
437 let zero = ahead_const(func, inst, 0);
438 let bit = ahead_const(func, inst, i128::from(HALF));
439 let reach = ahead(func, inst, Opcode::And, &[count, bit]);
440 let whole = compared(func, inst, IntPred::Ne, reach, zero);
441
442 let (low, high) = if opcode == Opcode::Shl {
443 let moved = ahead(func, inst, Opcode::Shl, &[a_low, places]);
444 let edge = ahead(func, inst, Opcode::LShr, &[a_low, one]);
445 let across = ahead(func, inst, Opcode::LShr, &[edge, back]);
446 let above = ahead(func, inst, Opcode::Shl, &[a_high, places]);
447 let joined = ahead(func, inst, Opcode::Or, &[above, across]);
448 let low = ahead(func, inst, Opcode::Select, &[whole, zero, moved]);
449 let high = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
450 (low, high)
451 } else {
452 let moved = ahead(func, inst, opcode, &[a_high, places]);
453 let edge = ahead(func, inst, Opcode::Shl, &[a_high, one]);
454 let across = ahead(func, inst, Opcode::Shl, &[edge, back]);
455 let below = ahead(func, inst, Opcode::LShr, &[a_low, places]);
456 let joined = ahead(func, inst, Opcode::Or, &[below, across]);
457 let spent = if opcode == Opcode::AShr {
460 ahead(func, inst, Opcode::AShr, &[a_high, top])
461 } else {
462 zero
463 };
464 let low = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
465 let high = ahead(func, inst, Opcode::Select, &[whole, spent, moved]);
466 (low, high)
467 };
468 replace(func, halves, inst, low, high);
469}
470
471fn bitwise(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
474 let args = func[func[inst].args].to_vec();
475 let [a, b] = args[..] else { return };
476 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
477 return;
478 };
479 let low = ahead(func, inst, opcode, &[a_low, b_low]);
480 let high = ahead(func, inst, opcode, &[a_high, b_high]);
481 replace(func, halves, inst, low, high);
482}
483
484fn compare(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
499 let Extra::IntPred(pred) = func[inst].extra else { return };
500 let args = func[func[inst].args].to_vec();
501 let [a, b] = args[..] else { return };
502 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
503 return;
504 };
505 let answer = if matches!(pred, IntPred::Eq | IntPred::Ne) {
506 let low = ahead(func, inst, Opcode::Xor, &[a_low, b_low]);
507 let high = ahead(func, inst, Opcode::Xor, &[a_high, b_high]);
508 let both = ahead(func, inst, Opcode::Or, &[low, high]);
509 let zero = ahead_const(func, inst, 0);
510 compared(func, inst, pred, both, zero)
511 } else {
512 let above = compared(func, inst, strict(pred), a_high, b_high);
513 let below = compared(func, inst, unsigned(pred), a_low, b_low);
514 let same = compared(func, inst, IntPred::Eq, a_high, b_high);
515 let tail = bit(func, inst, Opcode::And, same, below);
516 bit(func, inst, Opcode::Or, above, tail)
517 };
518 if let Some(result) = func[inst].first_result {
519 forward.insert(result, answer);
520 }
521 func.remove_inst(inst);
522}
523
524fn strict(pred: IntPred) -> IntPred {
526 match pred {
527 IntPred::Sle => IntPred::Slt,
528 IntPred::Sge => IntPred::Sgt,
529 IntPred::Ule => IntPred::Ult,
530 IntPred::Uge => IntPred::Ugt,
531 other => other,
532 }
533}
534
535fn unsigned(pred: IntPred) -> IntPred {
537 match pred {
538 IntPred::Slt => IntPred::Ult,
539 IntPred::Sle => IntPred::Ule,
540 IntPred::Sgt => IntPred::Ugt,
541 IntPred::Sge => IntPred::Uge,
542 other => other,
543 }
544}
545
546fn choose(func: &mut Func, halves: &mut Halves, inst: Inst) {
552 let args = func[func[inst].args].to_vec();
553 let [cond, then, other] = args[..] else { return };
554 let (Some(&(then_low, then_high)), Some(&(other_low, other_high))) =
555 (halves.get(&then), halves.get(&other))
556 else {
557 return;
558 };
559 let low = ahead(func, inst, Opcode::Select, &[cond, then_low, other_low]);
560 let high = ahead(func, inst, Opcode::Select, &[cond, then_high, other_high]);
561 replace(func, halves, inst, low, high);
562}
563
564fn truncate(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
570 let Some(&arg) = func[func[inst].args].first() else { return };
571 let Some(&(low, _)) = halves.get(&arg) else { return };
572 let Some(result) = func[inst].first_result else { return };
573 if func[result].ty.bits() == HALF {
574 forward.insert(result, low);
575 func.remove_inst(inst);
576 return;
577 }
578 becomes(func, inst, Opcode::Trunc, &[low]);
579}
580
581fn extend(func: &mut Func, halves: &mut Halves, inst: Inst, signed: bool) {
583 let Some(&arg) = func[func[inst].args].first() else { return };
584 let low = if func[arg].ty.bits() == HALF {
585 arg
586 } else {
587 let opcode = if signed { Opcode::SExt } else { Opcode::ZExt };
588 ahead(func, inst, opcode, &[arg])
589 };
590 let high = if signed {
591 let top = ahead_const(func, inst, i128::from(HALF - 1));
592 ahead(func, inst, Opcode::AShr, &[low, top])
593 } else {
594 ahead_const(func, inst, 0)
595 };
596 replace(func, halves, inst, low, high);
597}
598
599fn call(func: &mut Func, halves: &mut Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
606 let data = func[inst];
607 let Extra::Call(info) = data.extra else { return };
608 let info = func[info];
609 let args = spread(&func[data.args], halves);
610 let results: Vec<Type> = data
611 .results()
612 .map(|value| func[value].ty)
613 .flat_map(|ty| if is_wide(ty) { vec![half(), half()] } else { vec![ty] })
614 .collect();
615 let signature = func.add_signature(split_signature(&func[info.signature]));
616 let extra = Extra::Call(func.add_call(CallInfo { signature, ..info }));
617 let args = func.push_values(&args);
618 let span = func.span(inst);
619 let made = func.create_inst(InstData { args, extra, ..data }, &results, span);
620 func.insert_before(made, inst);
621 let mut fresh = func[made].results();
622 for old in data.results() {
623 if is_wide(func[old].ty) {
624 let (Some(low), Some(high)) = (fresh.next(), fresh.next()) else { return };
625 halves.insert(old, (low, high));
626 } else if let Some(again) = fresh.next() {
627 forward.insert(old, again);
628 }
629 }
630 func.remove_inst(inst);
631}
632
633fn flatten(func: &mut Func, halves: &Halves, inst: Inst) {
635 let args = spread(&func[func[inst].args], halves);
636 func[inst].args = func.push_values(&args);
637}
638
639fn edges(func: &mut Func, halves: &Halves, inst: Inst) {
641 for at in func.target_list(inst).iter() {
642 let call = func[at];
643 let args = func[call.args].to_vec();
644 if !args.iter().any(|value| halves.contains_key(value)) {
645 continue;
646 }
647 let args = func.push_values(&spread(&args, halves));
648 func.set_block_call(at, BlockCall { block: call.block, args });
649 }
650}
651
652fn spread(args: &[Value], halves: &Halves) -> Vec<Value> {
654 args.iter()
655 .flat_map(|value| match halves.get(value) {
656 Some(&(low, high)) => vec![low, high],
657 None => vec![*value],
658 })
659 .collect()
660}
661
662fn split_signature(signature: &Signature) -> Signature {
668 let split = |params: &[Param]| -> Vec<Param> {
669 params
670 .iter()
671 .flat_map(|param| {
672 if is_wide(param.ty) {
673 vec![Param::new(half()), Param::new(half())]
674 } else {
675 vec![*param]
676 }
677 })
678 .collect()
679 };
680 Signature {
681 params: split(&signature.params),
682 returns: split(&signature.returns),
683 variadic: signature.variadic,
684 }
685}
686
687fn replace(func: &mut Func, halves: &mut Halves, inst: Inst, low: Value, high: Value) {
689 if let Some(result) = func[inst].first_result {
690 halves.insert(result, (low, high));
691 }
692 func.remove_inst(inst);
693}
694
695fn substitute(func: &mut Func, forward: &HashMap<Value, Value>) {
701 if forward.is_empty() {
702 return;
703 }
704 let with = |value: Value| forward.get(&value).copied().unwrap_or(value);
705 for block in func.blocks().collect::<Vec<_>>() {
706 for inst in func.insts(block).collect::<Vec<Inst>>() {
707 let args = func[inst].args;
708 func.rewrite(args, with);
709 for call in func.successors(inst).collect::<Vec<_>>() {
710 func.rewrite(call.args, with);
711 }
712 }
713 }
714}
715
716fn word(info: MemInfo, at: u64) -> MemInfo {
718 let align = if at == 0 { info.align } else { info.align.min(8) };
719 MemInfo { size: STEP, align, ..info }
720}
721
722fn stepped(func: &mut Func, inst: Inst, from: Value) -> Value {
724 let step = ahead_const(func, inst, i128::from(STEP));
725 let args = func.push_values(&[from, step]);
726 written(func, inst, InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR)
727}
728
729fn read(func: &mut Func, inst: Inst, from: Value, info: MemInfo, flags: Flags) -> Value {
731 let extra = Extra::Mem(func.add_mem(info));
732 let args = func.push_values(&[from]);
733 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Load) };
734 written(func, inst, data, half())
735}
736
737fn write(func: &mut Func, inst: Inst, value: Value, into: Value, info: MemInfo, flags: Flags) {
739 let span = func.span(inst);
740 let extra = Extra::Mem(func.add_mem(info));
741 let args = func.push_values(&[value, into]);
742 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Store) };
743 let made = func.create_inst(data, &[], span);
744 func.insert_before(made, inst);
745}
746
747fn compared(func: &mut Func, inst: Inst, pred: IntPred, lhs: Value, rhs: Value) -> Value {
750 let args = func.push_values(&[lhs, rhs]);
751 let extra = Extra::IntPred(pred);
752 written(func, inst, InstData { args, extra, ..InstData::new(Opcode::ICmp) }, Type::I1)
753}
754
755fn bit(func: &mut Func, inst: Inst, opcode: Opcode, lhs: Value, rhs: Value) -> Value {
757 let args = func.push_values(&[lhs, rhs]);
758 written(func, inst, InstData { args, ..InstData::new(opcode) }, Type::I1)
759}
760
761fn ahead(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) -> Value {
763 let args = func.push_values(args);
764 written(func, inst, InstData { args, ..InstData::new(opcode) }, half())
765}
766
767fn ahead_const(func: &mut Func, inst: Inst, value: i128) -> Value {
769 let extra = Extra::Imm(func.add_imm(Imm::int(value, half())));
770 written(func, inst, InstData { extra, ..InstData::new(Opcode::IConst) }, half())
771}
772
773fn written(func: &mut Func, inst: Inst, data: InstData, ty: Type) -> Value {
775 let span = func.span(inst);
776 let made = func.create_inst(data, &[ty], span);
777 func.insert_before(made, inst);
778 func[made].first_result.expect("an instruction created with one result has one")
779}
780
781fn becomes(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) {
783 let args = func.push_values(args);
784 let data = &mut func[inst];
785 data.opcode = opcode;
786 data.args = args;
787 data.extra = Extra::None;
788 data.flags = data.flags.intersection(Flags::legal_on(opcode));
789}
790
791#[cfg(test)]
792mod tests {
793 use rucc_base::Interner;
794 use rucc_ir::{
795 Block, Builder, Flags, Func, MemOrder, Module, Restrict, Signature, Type, Value,
796 };
797 use rucc_target::x86_64::SYSV;
798 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
799
800 use super::{HALF, IntPred, MemInfo, Opcode, halves};
801
802 fn wide() -> Type {
804 Type::int(super::WIDE)
805 }
806
807 fn target() -> TargetInfo {
808 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
809 }
810
811 fn printed(func: &Func, names: &mut Interner) -> String {
812 let module = Module::new(names.intern("w.c"), &target());
813 rucc_ir::print_func(&module, func, names)
814 }
815
816 fn shell(names: &mut Interner, params: &[Type], returns: &[Type]) -> (Func, Block, Vec<Value>) {
818 let signature = Signature::new().with_params(params).with_returns(returns);
819 let mut func = Func::new(names.intern("f"), signature);
820 let entry = func.create_block();
821 let values = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
822 (func, entry, values)
823 }
824
825 fn info(size: u64, align: u32) -> MemInfo {
827 MemInfo {
828 size,
829 align,
830 order: MemOrder::NotAtomic,
831 tbaa: None,
832 owns: 0,
833 restrict: Restrict::NONE,
834 }
835 }
836
837 #[test]
838 fn an_add_carries_from_the_low_half_into_the_high_one() {
839 let mut names = Interner::new();
840 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
841 let mut build = Builder::new(&mut func, entry);
842 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
843 build.ret(&[sum]);
844
845 assert!(halves(&mut func, &SYSV), "there is a width to split");
846 let text = printed(&func, &mut names);
847 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
848 assert_eq!(text.matches(" = add ").count(), 3, "three adds: {text}");
851 assert_eq!(text.matches("icmp ult").count(), 1, "one carry: {text}");
852 assert_eq!(text.matches(" = zext.i64 ").count(), 1, "the carry as a number: {text}");
853 }
854
855 #[test]
856 fn a_subtract_borrows_the_other_way_round() {
857 let mut names = Interner::new();
858 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
859 let mut build = Builder::new(&mut func, entry);
860 let difference = build.binary(Opcode::Sub, params[0], params[1], Flags::NONE);
861 build.ret(&[difference]);
862
863 assert!(halves(&mut func, &SYSV), "there is a width to split");
864 let text = printed(&func, &mut names);
865 assert_eq!(text.matches(" = sub ").count(), 3, "three subtracts: {text}");
866 assert!(text.contains("icmp ult %0, %2"), "the operands are compared: {text}");
869 }
870
871 #[test]
872 fn the_signature_and_the_entry_block_say_the_same_thing() {
873 let mut names = Interner::new();
874 let (mut func, entry, params) = shell(&mut names, &[Type::int(32), wide()], &[wide()]);
875 let mut build = Builder::new(&mut func, entry);
876 build.ret(&[params[1]]);
877
878 assert!(halves(&mut func, &SYSV), "there is a width to split");
879 assert_eq!(
880 func.signature().param_types().collect::<Vec<_>>(),
881 [Type::int(32), Type::int(HALF), Type::int(HALF)],
882 "the wide parameter became two where it stood"
883 );
884 assert_eq!(
885 func.signature().return_types().collect::<Vec<_>>(),
886 [Type::int(HALF), Type::int(HALF)],
887 "and so did what comes back"
888 );
889 let text = printed(&func, &mut names);
890 assert!(text.contains("block0(%0: i32, %1: i64, %2: i64)"), "the block agrees: {text}");
891 assert!(text.contains("return %1, %2"), "both halves go back: {text}");
892 let _ = entry;
893 }
894
895 #[test]
896 fn a_read_takes_the_high_word_a_word_above_the_low_one() {
897 let mut names = Interner::new();
898 let (mut func, entry, params) = shell(&mut names, &[Type::PTR], &[wide()]);
899 let mut build = Builder::new(&mut func, entry);
900 let value = build.load(wide(), params[0], info(16, 16), Flags::NONE);
901 build.ret(&[value]);
902
903 assert!(halves(&mut func, &SYSV), "there is a width to split");
904 let text = printed(&func, &mut names);
905 assert_eq!(text.matches(" = load.i64 ").count(), 2, "two reads: {text}");
906 assert!(text.contains("ptr_add"), "the high word is a word up: {text}");
907 assert!(text.contains("align 16"), "the low word keeps what the object had: {text}");
910 assert!(text.contains("align 8"), "the high word knows less: {text}");
911 }
912
913 #[test]
914 fn an_equality_asks_once_about_both_halves() {
915 let mut names = Interner::new();
916 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
917 let mut build = Builder::new(&mut func, entry);
918 let same = build.icmp(IntPred::Eq, params[0], params[1]);
919 let answer = build.unary(Opcode::ZExt, same, Type::int(32));
920 build.ret(&[answer]);
921
922 assert!(halves(&mut func, &SYSV), "there is a width to split");
923 let text = printed(&func, &mut names);
924 assert_eq!(text.matches("icmp").count(), 1, "one comparison: {text}");
925 assert_eq!(text.matches(" = xor ").count(), 2, "the halves differ or they do not: {text}");
926 }
927
928 #[test]
929 fn an_ordering_reads_the_low_halves_without_a_sign() {
930 let mut names = Interner::new();
931 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
932 let mut build = Builder::new(&mut func, entry);
933 let below = build.icmp(IntPred::Slt, params[0], params[1]);
934 let answer = build.unary(Opcode::ZExt, below, Type::int(32));
935 build.ret(&[answer]);
936
937 assert!(halves(&mut func, &SYSV), "there is a width to split");
938 let text = printed(&func, &mut names);
939 assert!(text.contains("icmp slt"), "the high halves keep the sign: {text}");
940 assert!(text.contains("icmp ult"), "the low halves have none: {text}");
941 assert!(
942 text.contains("icmp eq"),
943 "and the low halves only matter when the high tie: {text}"
944 );
945 }
946
947 #[test]
954 fn an_ordering_that_allows_equality_asks_the_high_halves_a_strict_question() {
955 let mut names = Interner::new();
956 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
957 let mut build = Builder::new(&mut func, entry);
958 let at_least = build.icmp(IntPred::Sge, params[0], params[1]);
959 let answer = build.unary(Opcode::ZExt, at_least, Type::int(32));
960 build.ret(&[answer]);
961
962 assert!(halves(&mut func, &SYSV), "there is a width to split");
963 let text = printed(&func, &mut names);
964 assert!(text.contains("icmp sgt"), "the high halves settle it outright: {text}");
965 assert!(!text.contains("icmp sge"), "a tie in the high halves settles nothing: {text}");
966 assert!(text.contains("icmp uge"), "the low halves are the ones allowed to tie: {text}");
967 }
968
969 #[test]
970 fn a_widening_puts_the_sign_of_the_value_in_the_high_half() {
971 let mut names = Interner::new();
972 let (mut func, entry, params) = shell(&mut names, &[Type::int(32)], &[wide()]);
973 let mut build = Builder::new(&mut func, entry);
974 let value = build.unary(Opcode::SExt, params[0], wide());
975 build.ret(&[value]);
976
977 assert!(halves(&mut func, &SYSV), "there is a width to split");
978 let text = printed(&func, &mut names);
979 assert!(text.contains("sext.i64"), "the value fills the low half: {text}");
980 assert!(text.contains("ashr"), "and its sign fills the high one: {text}");
981 }
982
983 #[test]
984 fn a_block_parameter_becomes_two_and_every_branch_passes_two() {
985 let mut names = Interner::new();
986 let (mut func, entry, params) = shell(&mut names, &[wide(), Type::int(32)], &[wide()]);
987 let tail = func.create_block();
988 let carried = func.append_param(tail, wide());
989 let mut build = Builder::new(&mut func, entry);
990 let zero = build.iconst(Type::int(32), 0);
991 let taken = build.icmp(IntPred::Ne, params[1], zero);
992 let other = build.iconst(wide(), 7);
993 build.br_if(taken, tail, &[params[0]], tail, &[other]);
994 let mut build = Builder::new(&mut func, tail);
995 build.ret(&[carried]);
996
997 assert!(halves(&mut func, &SYSV), "there is a width to split");
998 let text = printed(&func, &mut names);
999 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1000 assert!(text.contains("block1(%7: i64, %8: i64)"), "the block takes two: {text}");
1001 assert_eq!(text.matches("block1(").count(), 3, "and both edges pass two: {text}");
1002 }
1003
1004 #[test]
1011 fn a_multiply_is_three_multiplies_and_the_carry_out_of_the_low_ones() {
1012 let mut names = Interner::new();
1013 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1014 let mut build = Builder::new(&mut func, entry);
1015 let product = build.binary(Opcode::Mul, params[0], params[1], Flags::NONE);
1016 build.ret(&[product]);
1017
1018 assert!(halves(&mut func, &SYSV), "there is a width to split");
1019 let text = printed(&func, &mut names);
1020 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1021 assert_eq!(text.matches(" = mul ").count(), 7, "three and the carry's four: {text}");
1022 }
1023
1024 #[test]
1031 fn a_shift_left_chooses_between_a_count_that_crossed_a_half_and_one_that_did_not() {
1032 let mut names = Interner::new();
1033 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1034 let mut build = Builder::new(&mut func, entry);
1035 let moved = build.binary(Opcode::Shl, params[0], params[1], Flags::NONE);
1036 build.ret(&[moved]);
1037
1038 assert!(halves(&mut func, &SYSV), "there is a width to split");
1039 let text = printed(&func, &mut names);
1040 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1041 assert_eq!(
1042 text.matches(" = shl ").count(),
1043 2,
1044 "one per half, and the far case reuses one: {text}"
1045 );
1046 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1047 assert_eq!(text.matches(" = lshr ").count(), 2, "the crossing bits, in two steps: {text}");
1048 }
1049
1050 #[test]
1057 fn the_bits_that_cross_move_one_place_and_then_the_rest_of_the_way() {
1058 let mut names = Interner::new();
1059 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1060 let mut build = Builder::new(&mut func, entry);
1061 let moved = build.binary(Opcode::LShr, params[0], params[1], Flags::NONE);
1062 build.ret(&[moved]);
1063
1064 assert!(halves(&mut func, &SYSV), "there is a width to split");
1065 let text = printed(&func, &mut names);
1066 assert!(text.contains("iconst.i64 63"), "sixty three is the distance left: {text}");
1067 assert!(text.contains("iconst.i64 1"), "after the one place that comes first: {text}");
1068 assert!(text.contains(" = sub "), "the rest of the way is worked out: {text}");
1069 assert!(
1070 !text.contains("iconst.i64 127"),
1071 "and the count is not masked to the width: {text}"
1072 );
1073 }
1074
1075 #[test]
1081 fn an_arithmetic_shift_right_leaves_the_sign_bit_where_a_logical_one_leaves_zeroes() {
1082 let mut names = Interner::new();
1083 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1084 let mut build = Builder::new(&mut func, entry);
1085 let moved = build.binary(Opcode::AShr, params[0], params[1], Flags::NONE);
1086 build.ret(&[moved]);
1087
1088 assert!(halves(&mut func, &SYSV), "there is a width to split");
1089 let text = printed(&func, &mut names);
1090 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1091 assert_eq!(text.matches(" = ashr ").count(), 2, "the count and the sign: {text}");
1093 assert_eq!(text.matches(" = lshr ").count(), 1, "the low half is not signed: {text}");
1094 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1095 }
1096
1097 #[test]
1098 fn a_parameter_with_one_register_left_leaves_the_function_alone() {
1099 let mut names = Interner::new();
1100 let word = Type::int(HALF);
1101 let params = [word, word, word, word, word, wide()];
1105 let (mut func, entry, values) = shell(&mut names, ¶ms, &[word]);
1106 let mut build = Builder::new(&mut func, entry);
1107 let low = build.unary(Opcode::Trunc, values[5], word);
1108 build.ret(&[low]);
1109 let before = printed(&func, &mut names);
1110
1111 assert!(!halves(&mut func, &SYSV), "one of the halves has no register");
1112 assert_eq!(printed(&func, &mut names), before, "so nothing moved");
1113 }
1114
1115 #[test]
1116 fn a_function_with_nothing_that_wide_is_not_touched() {
1117 let mut names = Interner::new();
1118 let word = Type::int(HALF);
1119 let (mut func, entry, params) = shell(&mut names, &[word, word], &[word]);
1120 let mut build = Builder::new(&mut func, entry);
1121 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1122 build.ret(&[sum]);
1123
1124 assert!(!halves(&mut func, &SYSV), "there is nothing to split");
1125 }
1126}