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, MemOrder, Opcode, Param, Restrict, Signature, Type, Value,
72};
73use rucc_target::{AbiDescription, CallRegs, Places, Where};
74
75use crate::capability;
76use crate::expand;
77
78const WIDE: u32 = 128;
80
81const MODE: &str = "i128";
83
84const HALF: u32 = 64;
86
87const STEP: u64 = 8;
89
90fn is_wide(ty: Type) -> bool {
92 ty.is_int() && ty.is_scalar() && ty.bits() == WIDE
93}
94
95fn half() -> Type {
97 Type::int(HALF)
98}
99
100pub fn halves(func: &mut Func, names: &mut Interner, conv: &CallRegs) -> bool {
111 if !func.values().any(|value| is_wide(func[value].ty)) {
112 return false;
113 }
114 let insts: Vec<Inst> =
115 walk(func).into_iter().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
116 let order: HashMap<Inst, usize> =
117 insts.iter().enumerate().map(|(at, &inst)| (inst, at)).collect();
118 if !insts.iter().enumerate().all(|(at, &inst)| can_split(func, &order, at, inst)) {
119 return false;
120 }
121 if !func.signatures().all(|signature| fits(signature, conv)) {
122 return false;
123 }
124
125 let mut halves: Halves = HashMap::new();
126 let mut forward: HashMap<Value, Value> = HashMap::new();
127 for block in func.blocks().collect::<Vec<_>>() {
128 params(func, block, &mut halves, &mut forward);
129 }
130 for &inst in &insts {
131 rewrite(func, names, conv.abi, &mut halves, &mut forward, inst);
132 }
133 substitute(func, &forward);
134 let signature = split_signature(func.signature());
135 func.set_signature(signature);
136 true
137}
138
139fn walk(func: &Func) -> Vec<Block> {
157 let Some(entry) = func.entry() else { return func.blocks().collect() };
158 let mut seen: HashSet<Block> = HashSet::new();
159 let mut order: Vec<Block> = Vec::new();
160 let mut stack: Vec<(Block, bool)> = vec![(entry, false)];
163 seen.insert(entry);
164 while let Some((block, done)) = stack.pop() {
165 if done {
166 order.push(block);
167 continue;
168 }
169 stack.push((block, true));
170 let Some(term) = func.terminator(block) else { continue };
171 for call in func.successors(term) {
172 if seen.insert(call.block) {
173 stack.push((call.block, false));
174 }
175 }
176 }
177 order.reverse();
178 order.extend(func.blocks().filter(|block| !seen.contains(block)));
179 order
180}
181
182type Halves = HashMap<Value, (Value, Value)>;
184
185fn understood(opcode: Opcode) -> bool {
196 matches!(
197 opcode,
198 Opcode::IConst
199 | Opcode::Load
200 | Opcode::Store
201 | Opcode::Add
202 | Opcode::Sub
203 | Opcode::Mul
204 | Opcode::UDiv
205 | Opcode::SDiv
206 | Opcode::URem
207 | Opcode::SRem
208 | Opcode::Shl
209 | Opcode::LShr
210 | Opcode::AShr
211 | Opcode::And
212 | Opcode::Or
213 | Opcode::Xor
214 | Opcode::ICmp
215 | Opcode::Select
216 | Opcode::SIToFP
217 | Opcode::UIToFP
218 | Opcode::FPToSI
219 | Opcode::FPToUI
220 | Opcode::Trunc
221 | Opcode::SExt
222 | Opcode::ZExt
223 | Opcode::Call
224 | Opcode::CallIndirect
225 | Opcode::Return
226 | Opcode::Jump
227 | Opcode::BrIf
228 )
229}
230
231fn can_split(func: &Func, order: &HashMap<Inst, usize>, at: usize, inst: Inst) -> bool {
236 let data = func[inst];
237 let reads = operands(func, inst);
238 let wide = |&value: &Value| is_wide(func[value].ty);
239 if !reads.iter().any(wide) && !data.results().any(|value| is_wide(func[value].ty)) {
240 return true;
241 }
242 if !understood(data.opcode) {
243 return false;
244 }
245 if func.carries_mem(inst) {
249 return false;
250 }
251 if data.opcode == Opcode::SExt && reads.iter().any(|&value| func[value].ty.bits() < 8) {
255 return false;
256 }
257 if matches!(data.opcode, Opcode::SIToFP | Opcode::UIToFP | Opcode::FPToSI | Opcode::FPToUI)
262 && converted(func, inst).is_none()
263 {
264 return false;
265 }
266 if matches!(data.opcode, Opcode::Call | Opcode::CallIndirect) {
270 let Extra::Call(info) = data.extra else { return false };
271 if func[func[info].signature].variadic {
272 return false;
273 }
274 }
275 reads.iter().filter(|value| wide(value)).all(|&value| match func[value].def {
279 Def::Result { inst, .. } => order.get(&inst).is_some_and(|&def| def < at),
280 Def::Param { .. } => true,
281 })
282}
283
284fn converted(func: &Func, inst: Inst) -> Option<Float> {
291 let data = func[inst];
292 let mut floats = func[data.args]
293 .iter()
294 .copied()
295 .chain(data.results())
296 .map(|value| func[value].ty)
297 .filter(|ty| ty.is_float());
298 let only = floats.next()?;
299 if floats.next().is_some() {
300 return None;
301 }
302 match only.format() {
303 Some(format @ (Float::F32 | Float::F64 | Float::F128)) => Some(format),
304 _ => None,
305 }
306}
307
308fn operands(func: &Func, inst: Inst) -> Vec<Value> {
314 let mut reads = func[func[inst].args].to_vec();
315 for call in func.successors(inst).collect::<Vec<_>>() {
316 reads.extend_from_slice(&func[call.args]);
317 }
318 reads
319}
320
321fn fits(signature: &Signature, conv: &CallRegs) -> bool {
333 let mut places = Places::new(conv);
334 for param in &signature.params {
335 if let Abi::ByVal { size, align } = param.abi {
339 places.on_stack(u32::try_from(size).unwrap_or(u32::MAX), align);
340 } else if crate::abi::on_the_stack(param.ty) {
341 let (size, align) = crate::abi::X87_AREA;
342 places.on_stack(size, align);
343 } else if is_wide(param.ty) {
344 let low = places.integer();
345 let high = places.integer();
346 if !matches!((low, high), (Where::Reg(_), Where::Reg(_))) {
347 return false;
348 }
349 } else if param.ty.is_float() {
350 places.float(crate::abi::float_bytes(param.ty));
351 } else {
352 places.integer();
353 }
354 }
355 true
356}
357
358fn params(func: &mut Func, block: Block, halves: &mut Halves, forward: &mut HashMap<Value, Value>) {
365 let old: Vec<Value> = func[block].params.clone();
366 if !old.iter().any(|&value| is_wide(func[value].ty)) {
367 return;
368 }
369 for &value in &old {
370 if is_wide(func[value].ty) {
371 let low = func.append_param(block, half());
372 let high = func.append_param(block, half());
373 halves.insert(value, (low, high));
374 } else {
375 let again = func.append_param(block, func[value].ty);
376 forward.insert(value, again);
377 }
378 }
379 func.retain_params(block, |value| !old.contains(&value));
380}
381
382fn rewrite(
384 func: &mut Func,
385 names: &mut Interner,
386 abi: &'static AbiDescription,
387 halves: &mut Halves,
388 forward: &mut HashMap<Value, Value>,
389 inst: Inst,
390) {
391 let data = func[inst];
392 let produces = data.results().any(|value| is_wide(func[value].ty));
393 let takes = func[data.args].iter().any(|&value| is_wide(func[value].ty));
394 match data.opcode {
395 Opcode::IConst if produces => constant(func, halves, inst),
396 Opcode::Load if produces => load(func, halves, inst),
397 Opcode::Store if takes => store(func, halves, inst),
398 Opcode::Add | Opcode::Sub if produces => carried(func, halves, inst, data.opcode),
399 Opcode::Mul if produces => multiply(func, halves, inst),
400 Opcode::UDiv | Opcode::SDiv | Opcode::URem | Opcode::SRem if produces => {
401 divide(func, names, abi, halves, inst, data.opcode);
402 }
403 Opcode::Shl | Opcode::LShr | Opcode::AShr if produces => {
404 shifted(func, halves, inst, data.opcode);
405 }
406 Opcode::And | Opcode::Or | Opcode::Xor if produces => {
407 bitwise(func, halves, inst, data.opcode);
408 }
409 Opcode::SIToFP | Opcode::UIToFP if takes => {
410 to_float(func, names, abi, halves, forward, inst, data.opcode == Opcode::SIToFP);
411 }
412 Opcode::FPToSI | Opcode::FPToUI if produces => {
413 from_float(func, names, abi, halves, inst, data.opcode == Opcode::FPToSI);
414 }
415 Opcode::ICmp if takes => compare(func, halves, forward, inst),
416 Opcode::Select if produces => choose(func, halves, inst),
417 Opcode::Trunc if takes => truncate(func, halves, forward, inst),
418 Opcode::SExt | Opcode::ZExt if produces => {
419 extend(func, halves, inst, data.opcode == Opcode::SExt);
420 }
421 Opcode::Call | Opcode::CallIndirect if produces || takes => {
422 call(func, halves, forward, inst);
423 }
424 Opcode::Return if takes => flatten(func, halves, inst),
425 Opcode::Jump | Opcode::BrIf => edges(func, halves, inst),
426 _ => {}
427 }
428}
429
430fn constant(func: &mut Func, halves: &mut Halves, inst: Inst) {
432 let Extra::Imm(imm) = func[inst].extra else { return };
433 let bits = func[imm].unsigned();
434 #[expect(clippy::cast_possible_truncation, reason = "the halves are what this is taking")]
435 let (low, high) = (bits as u64, (bits >> HALF) as u64);
436 let low = ahead_const(func, inst, i128::from(low));
437 let high = ahead_const(func, inst, i128::from(high));
438 replace(func, halves, inst, low, high);
439}
440
441fn load(func: &mut Func, halves: &mut Halves, inst: Inst) {
447 let data = func[inst];
448 let Extra::Mem(mem) = data.extra else { return };
449 let info = func[mem];
450 let Some(&from) = func[data.args].first() else { return };
451 let low = read(func, inst, from, word(info, 0), data.flags);
452 let up = stepped(func, inst, from);
453 let high = read(func, inst, up, word(info, STEP), data.flags);
454 replace(func, halves, inst, low, high);
455}
456
457fn store(func: &mut Func, halves: &mut Halves, inst: Inst) {
459 let data = func[inst];
460 let Extra::Mem(mem) = data.extra else { return };
461 let info = func[mem];
462 let args = func[data.args].to_vec();
463 let [value, into] = args[..] else { return };
464 let Some(&(low, high)) = halves.get(&value) else { return };
465 write(func, inst, low, into, word(info, 0), data.flags);
466 let up = stepped(func, inst, into);
467 write(func, inst, high, up, word(info, STEP), data.flags);
468 func.remove_inst(inst);
469}
470
471fn carried(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
481 let args = func[func[inst].args].to_vec();
482 let [a, b] = args[..] else { return };
483 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
484 return;
485 };
486 let low = ahead(func, inst, opcode, &[a_low, b_low]);
487 let carried = if opcode == Opcode::Add {
488 compared(func, inst, IntPred::Ult, low, a_low)
489 } else {
490 compared(func, inst, IntPred::Ult, a_low, b_low)
491 };
492 let carry = ahead(func, inst, Opcode::ZExt, &[carried]);
493 let high = ahead(func, inst, opcode, &[a_high, b_high]);
494 let high = ahead(func, inst, opcode, &[high, carry]);
495 replace(func, halves, inst, low, high);
496}
497
498fn multiply(func: &mut Func, halves: &mut Halves, inst: Inst) {
518 let args = func[func[inst].args].to_vec();
519 let [a, b] = args[..] else { return };
520 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
521 return;
522 };
523 let low = ahead(func, inst, Opcode::Mul, &[a_low, b_low]);
524 let carried = expand::high_half(func, inst, a_low, b_low, false, half());
525 let cross = ahead(func, inst, Opcode::Mul, &[a_low, b_high]);
526 let other = ahead(func, inst, Opcode::Mul, &[a_high, b_low]);
527 let high = ahead(func, inst, Opcode::Add, &[carried, cross]);
528 let high = ahead(func, inst, Opcode::Add, &[high, other]);
529 replace(func, halves, inst, low, high);
530}
531
532fn divide(
550 func: &mut Func,
551 names: &mut Interner,
552 abi: &'static AbiDescription,
553 halves: &mut Halves,
554 inst: Inst,
555 opcode: Opcode,
556) {
557 let args = func[func[inst].args].to_vec();
558 let [a, b] = args[..] else { return };
559 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
560 return;
561 };
562 let Some(routine) = capability::libcall(opcode, MODE) else { return };
566 let args = [Operand::Split(a_low, a_high), Operand::Split(b_low, b_high)];
567 let made = runtime(func, names, abi, inst, routine, &args, &[half(), half()]);
568 let [low, high] = made[..] else { return };
569 replace(func, halves, inst, low, high);
570}
571
572fn to_float(
582 func: &mut Func,
583 names: &mut Interner,
584 abi: &'static AbiDescription,
585 halves: &Halves,
586 forward: &mut HashMap<Value, Value>,
587 inst: Inst,
588 signed: bool,
589) {
590 let Some(&arg) = func[func[inst].args].first() else { return };
591 let Some(&(low, high)) = halves.get(&arg) else { return };
592 let (Some(result), Some(format)) = (func[inst].first_result, converted(func, inst)) else {
593 return;
594 };
595 let routine = going_up(signed, format);
596 let args = [Operand::Split(low, high)];
597 let made = runtime(func, names, abi, inst, routine, &args, &[func[result].ty]);
598 if let [answer] = made[..] {
599 forward.insert(result, answer);
600 }
601 func.remove_inst(inst);
602}
603
604fn from_float(
614 func: &mut Func,
615 names: &mut Interner,
616 abi: &'static AbiDescription,
617 halves: &mut Halves,
618 inst: Inst,
619 signed: bool,
620) {
621 let Some(&arg) = func[func[inst].args].first() else { return };
622 let Some(format) = converted(func, inst) else { return };
623 let routine = coming_down(signed, format);
624 let args = [Operand::Whole(arg)];
625 let made = runtime(func, names, abi, inst, routine, &args, &[half(), half()]);
626 let [low, high] = made[..] else { return };
627 replace(func, halves, inst, low, high);
628}
629
630fn going_up(signed: bool, format: Float) -> &'static str {
636 let mode = match format {
637 Float::F32 => "i128.f32",
638 Float::F64 => "i128.f64",
639 _ => "i128.f128",
640 };
641 routine(if signed { Opcode::SIToFP } else { Opcode::UIToFP }, mode)
642}
643
644fn coming_down(signed: bool, format: Float) -> &'static str {
646 let mode = match format {
647 Float::F32 => "f32.i128",
648 Float::F64 => "f64.i128",
649 _ => "f128.i128",
650 };
651 routine(if signed { Opcode::FPToSI } else { Opcode::FPToUI }, mode)
652}
653
654fn routine(opcode: Opcode, mode: &str) -> &'static str {
659 capability::libcall(opcode, mode)
660 .unwrap_or_else(|| panic!("no routine for `{}` at `{mode}`", opcode.name()))
661}
662
663#[derive(Clone, Copy)]
669enum Operand {
670 Whole(Value),
672 Split(Value, Value),
674}
675
676fn runtime(
700 func: &mut Func,
701 names: &mut Interner,
702 abi: &'static AbiDescription,
703 inst: Inst,
704 routine: &str,
705 args: &[Operand],
706 results: &[Type],
707) -> Vec<Value> {
708 let mut params: Vec<Param> = Vec::new();
709 let mut values: Vec<Value> = Vec::new();
710 let mut out = None;
711 if let [ty] = *results {
713 let size = bytes(ty);
714 if abi.scalar_is_by_reference(size) {
715 let align = align(size);
716 let slot = room(func, inst, size, align);
717 params.push(Param::with_abi(Type::PTR, Abi::Sret { size, align }));
718 values.push(slot);
719 out = Some((slot, ty));
720 }
721 }
722 for &arg in args {
723 handed(func, abi, inst, arg, &mut params, &mut values);
724 }
725 let returns =
726 if out.is_some() { Vec::new() } else { results.iter().map(|&ty| Param::new(ty)).collect() };
727 let signature = func.add_signature(Signature { params, returns, variadic: false });
728 let callee = Some(names.intern(routine));
729 let varargs = func.push_abis(&[]);
730 let extra = Extra::Call(func.add_call(CallInfo { callee, signature, varargs }));
731 let pushed = func.push_values(&values);
732 let span = func.span(inst);
733 let data = InstData { args: pushed, extra, ..InstData::new(Opcode::Call) };
734 let answers: Vec<Type> = if out.is_some() { Vec::new() } else { results.to_vec() };
735 let made = func.create_inst(data, &answers, span);
736 func.insert_before(made, inst);
737 match out {
738 Some((slot, ty)) => {
739 let size = bytes(ty);
740 let info = whole(size, align(size));
741 let extra = Extra::Mem(func.add_mem(info));
742 let args = func.push_values(&[slot]);
743 let data = InstData { args, extra, ..InstData::new(Opcode::Load) };
744 vec![written(func, inst, data, ty)]
745 }
746 None => func[made].results().collect(),
747 }
748}
749
750fn handed(
752 func: &mut Func,
753 abi: &'static AbiDescription,
754 inst: Inst,
755 arg: Operand,
756 params: &mut Vec<Param>,
757 values: &mut Vec<Value>,
758) {
759 match arg {
760 Operand::Whole(value) => {
761 let ty = func[value].ty;
762 let size = bytes(ty);
763 if !abi.scalar_is_by_reference(size) {
764 params.push(Param::new(ty));
765 values.push(value);
766 return;
767 }
768 let align = align(size);
769 let slot = room(func, inst, size, align);
770 write(func, inst, value, slot, whole(size, align), Flags::NONE);
771 params.push(Param::new(Type::PTR));
772 values.push(slot);
773 }
774 Operand::Split(low, high) => {
775 let size = u64::from(WIDE / 8);
776 if !abi.scalar_is_by_reference(size) {
777 params.push(Param::new(half()));
778 values.push(low);
779 params.push(Param::new(half()));
780 values.push(high);
781 return;
782 }
783 let align = align(size);
784 let slot = room(func, inst, size, align);
785 let info = whole(size, align);
786 write(func, inst, low, slot, word(info, 0), Flags::NONE);
787 let up = stepped(func, inst, slot);
788 write(func, inst, high, up, word(info, STEP), Flags::NONE);
789 params.push(Param::new(Type::PTR));
790 values.push(slot);
791 }
792 }
793}
794
795fn bytes(ty: Type) -> u64 {
797 u64::from(ty.bits().div_ceil(8))
798}
799
800fn align(size: u64) -> u32 {
802 u32::try_from(size).unwrap_or(u32::MAX)
803}
804
805fn whole(size: u64, align: u32) -> MemInfo {
807 MemInfo {
808 size,
809 align,
810 order: MemOrder::NotAtomic,
811 tbaa: None,
812 owns: 0,
813 restrict: Restrict::NONE,
814 }
815}
816
817fn room(func: &mut Func, inst: Inst, size: u64, align: u32) -> Value {
819 let extra = Extra::Mem(func.add_mem(whole(size, align)));
820 written(func, inst, InstData { extra, ..InstData::new(Opcode::Alloca) }, Type::PTR)
821}
822
823fn shifted(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
843 let args = func[func[inst].args].to_vec();
844 let [a, b] = args[..] else { return };
845 let (Some(&(a_low, a_high)), Some(&(count, _))) = (halves.get(&a), halves.get(&b)) else {
846 return;
847 };
848 let top = ahead_const(func, inst, i128::from(HALF - 1));
849 let places = ahead(func, inst, Opcode::And, &[count, top]);
850 let back = ahead(func, inst, Opcode::Sub, &[top, places]);
851 let one = ahead_const(func, inst, 1);
852 let zero = ahead_const(func, inst, 0);
853 let bit = ahead_const(func, inst, i128::from(HALF));
854 let reach = ahead(func, inst, Opcode::And, &[count, bit]);
855 let whole = compared(func, inst, IntPred::Ne, reach, zero);
856
857 let (low, high) = if opcode == Opcode::Shl {
858 let moved = ahead(func, inst, Opcode::Shl, &[a_low, places]);
859 let edge = ahead(func, inst, Opcode::LShr, &[a_low, one]);
860 let across = ahead(func, inst, Opcode::LShr, &[edge, back]);
861 let above = ahead(func, inst, Opcode::Shl, &[a_high, places]);
862 let joined = ahead(func, inst, Opcode::Or, &[above, across]);
863 let low = ahead(func, inst, Opcode::Select, &[whole, zero, moved]);
864 let high = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
865 (low, high)
866 } else {
867 let moved = ahead(func, inst, opcode, &[a_high, places]);
868 let edge = ahead(func, inst, Opcode::Shl, &[a_high, one]);
869 let across = ahead(func, inst, Opcode::Shl, &[edge, back]);
870 let below = ahead(func, inst, Opcode::LShr, &[a_low, places]);
871 let joined = ahead(func, inst, Opcode::Or, &[below, across]);
872 let spent = if opcode == Opcode::AShr {
875 ahead(func, inst, Opcode::AShr, &[a_high, top])
876 } else {
877 zero
878 };
879 let low = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
880 let high = ahead(func, inst, Opcode::Select, &[whole, spent, moved]);
881 (low, high)
882 };
883 replace(func, halves, inst, low, high);
884}
885
886fn bitwise(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
889 let args = func[func[inst].args].to_vec();
890 let [a, b] = args[..] else { return };
891 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
892 return;
893 };
894 let low = ahead(func, inst, opcode, &[a_low, b_low]);
895 let high = ahead(func, inst, opcode, &[a_high, b_high]);
896 replace(func, halves, inst, low, high);
897}
898
899fn compare(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
914 let Extra::IntPred(pred) = func[inst].extra else { return };
915 let args = func[func[inst].args].to_vec();
916 let [a, b] = args[..] else { return };
917 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
918 return;
919 };
920 let answer = if matches!(pred, IntPred::Eq | IntPred::Ne) {
921 let low = ahead(func, inst, Opcode::Xor, &[a_low, b_low]);
922 let high = ahead(func, inst, Opcode::Xor, &[a_high, b_high]);
923 let both = ahead(func, inst, Opcode::Or, &[low, high]);
924 let zero = ahead_const(func, inst, 0);
925 compared(func, inst, pred, both, zero)
926 } else {
927 let above = compared(func, inst, strict(pred), a_high, b_high);
928 let below = compared(func, inst, unsigned(pred), a_low, b_low);
929 let same = compared(func, inst, IntPred::Eq, a_high, b_high);
930 let tail = bit(func, inst, Opcode::And, same, below);
931 bit(func, inst, Opcode::Or, above, tail)
932 };
933 if let Some(result) = func[inst].first_result {
934 forward.insert(result, answer);
935 }
936 func.remove_inst(inst);
937}
938
939fn strict(pred: IntPred) -> IntPred {
941 match pred {
942 IntPred::Sle => IntPred::Slt,
943 IntPred::Sge => IntPred::Sgt,
944 IntPred::Ule => IntPred::Ult,
945 IntPred::Uge => IntPred::Ugt,
946 other => other,
947 }
948}
949
950fn unsigned(pred: IntPred) -> IntPred {
952 match pred {
953 IntPred::Slt => IntPred::Ult,
954 IntPred::Sle => IntPred::Ule,
955 IntPred::Sgt => IntPred::Ugt,
956 IntPred::Sge => IntPred::Uge,
957 other => other,
958 }
959}
960
961fn choose(func: &mut Func, halves: &mut Halves, inst: Inst) {
967 let args = func[func[inst].args].to_vec();
968 let [cond, then, other] = args[..] else { return };
969 let (Some(&(then_low, then_high)), Some(&(other_low, other_high))) =
970 (halves.get(&then), halves.get(&other))
971 else {
972 return;
973 };
974 let low = ahead(func, inst, Opcode::Select, &[cond, then_low, other_low]);
975 let high = ahead(func, inst, Opcode::Select, &[cond, then_high, other_high]);
976 replace(func, halves, inst, low, high);
977}
978
979fn truncate(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
985 let Some(&arg) = func[func[inst].args].first() else { return };
986 let Some(&(low, _)) = halves.get(&arg) else { return };
987 let Some(result) = func[inst].first_result else { return };
988 if func[result].ty.bits() == HALF {
989 forward.insert(result, low);
990 func.remove_inst(inst);
991 return;
992 }
993 becomes(func, inst, Opcode::Trunc, &[low]);
994}
995
996fn extend(func: &mut Func, halves: &mut Halves, inst: Inst, signed: bool) {
998 let Some(&arg) = func[func[inst].args].first() else { return };
999 let low = if func[arg].ty.bits() == HALF {
1000 arg
1001 } else {
1002 let opcode = if signed { Opcode::SExt } else { Opcode::ZExt };
1003 ahead(func, inst, opcode, &[arg])
1004 };
1005 let high = if signed {
1006 let top = ahead_const(func, inst, i128::from(HALF - 1));
1007 ahead(func, inst, Opcode::AShr, &[low, top])
1008 } else {
1009 ahead_const(func, inst, 0)
1010 };
1011 replace(func, halves, inst, low, high);
1012}
1013
1014fn call(func: &mut Func, halves: &mut Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
1021 let data = func[inst];
1022 let Extra::Call(info) = data.extra else { return };
1023 let info = func[info];
1024 let args = spread(&func[data.args], halves);
1025 let results: Vec<Type> = data
1026 .results()
1027 .map(|value| func[value].ty)
1028 .flat_map(|ty| if is_wide(ty) { vec![half(), half()] } else { vec![ty] })
1029 .collect();
1030 let signature = func.add_signature(split_signature(&func[info.signature]));
1031 let extra = Extra::Call(func.add_call(CallInfo { signature, ..info }));
1032 let args = func.push_values(&args);
1033 let span = func.span(inst);
1034 let made = func.create_inst(InstData { args, extra, ..data }, &results, span);
1035 func.insert_before(made, inst);
1036 let mut fresh = func[made].results();
1037 for old in data.results() {
1038 if is_wide(func[old].ty) {
1039 let (Some(low), Some(high)) = (fresh.next(), fresh.next()) else { return };
1040 halves.insert(old, (low, high));
1041 } else if let Some(again) = fresh.next() {
1042 forward.insert(old, again);
1043 }
1044 }
1045 func.remove_inst(inst);
1046}
1047
1048fn flatten(func: &mut Func, halves: &Halves, inst: Inst) {
1050 let args = spread(&func[func[inst].args], halves);
1051 func[inst].args = func.push_values(&args);
1052}
1053
1054fn edges(func: &mut Func, halves: &Halves, inst: Inst) {
1056 for at in func.target_list(inst).iter() {
1057 let call = func[at];
1058 let args = func[call.args].to_vec();
1059 if !args.iter().any(|value| halves.contains_key(value)) {
1060 continue;
1061 }
1062 let args = func.push_values(&spread(&args, halves));
1063 func.set_block_call(at, BlockCall { args, ..call });
1064 }
1065}
1066
1067fn spread(args: &[Value], halves: &Halves) -> Vec<Value> {
1069 args.iter()
1070 .flat_map(|value| match halves.get(value) {
1071 Some(&(low, high)) => vec![low, high],
1072 None => vec![*value],
1073 })
1074 .collect()
1075}
1076
1077fn split_signature(signature: &Signature) -> Signature {
1083 let split = |params: &[Param]| -> Vec<Param> {
1084 params
1085 .iter()
1086 .flat_map(|param| {
1087 if is_wide(param.ty) {
1088 vec![Param::new(half()), Param::new(half())]
1089 } else {
1090 vec![*param]
1091 }
1092 })
1093 .collect()
1094 };
1095 Signature {
1096 params: split(&signature.params),
1097 returns: split(&signature.returns),
1098 variadic: signature.variadic,
1099 }
1100}
1101
1102fn replace(func: &mut Func, halves: &mut Halves, inst: Inst, low: Value, high: Value) {
1104 if let Some(result) = func[inst].first_result {
1105 halves.insert(result, (low, high));
1106 }
1107 func.remove_inst(inst);
1108}
1109
1110fn substitute(func: &mut Func, forward: &HashMap<Value, Value>) {
1116 if forward.is_empty() {
1117 return;
1118 }
1119 let with = |value: Value| forward.get(&value).copied().unwrap_or(value);
1120 for block in func.blocks().collect::<Vec<_>>() {
1121 for inst in func.insts(block).collect::<Vec<Inst>>() {
1122 let args = func[inst].args;
1123 func.rewrite(args, with);
1124 for call in func.successors(inst).collect::<Vec<_>>() {
1125 func.rewrite(call.args, with);
1126 }
1127 }
1128 }
1129}
1130
1131fn word(info: MemInfo, at: u64) -> MemInfo {
1133 let align = if at == 0 { info.align } else { info.align.min(8) };
1134 MemInfo { size: STEP, align, ..info }
1135}
1136
1137fn stepped(func: &mut Func, inst: Inst, from: Value) -> Value {
1139 let step = ahead_const(func, inst, i128::from(STEP));
1140 let args = func.push_values(&[from, step]);
1141 written(func, inst, InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR)
1142}
1143
1144fn read(func: &mut Func, inst: Inst, from: Value, info: MemInfo, flags: Flags) -> Value {
1146 let extra = Extra::Mem(func.add_mem(info));
1147 let args = func.push_values(&[from]);
1148 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Load) };
1149 written(func, inst, data, half())
1150}
1151
1152fn write(func: &mut Func, inst: Inst, value: Value, into: Value, info: MemInfo, flags: Flags) {
1154 let span = func.span(inst);
1155 let extra = Extra::Mem(func.add_mem(info));
1156 let args = func.push_values(&[value, into]);
1157 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Store) };
1158 let made = func.create_inst(data, &[], span);
1159 func.insert_before(made, inst);
1160}
1161
1162fn compared(func: &mut Func, inst: Inst, pred: IntPred, lhs: Value, rhs: Value) -> Value {
1165 let args = func.push_values(&[lhs, rhs]);
1166 let extra = Extra::IntPred(pred);
1167 written(func, inst, InstData { args, extra, ..InstData::new(Opcode::ICmp) }, Type::I1)
1168}
1169
1170fn bit(func: &mut Func, inst: Inst, opcode: Opcode, lhs: Value, rhs: Value) -> Value {
1172 let args = func.push_values(&[lhs, rhs]);
1173 written(func, inst, InstData { args, ..InstData::new(opcode) }, Type::I1)
1174}
1175
1176fn ahead(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) -> Value {
1178 let args = func.push_values(args);
1179 written(func, inst, InstData { args, ..InstData::new(opcode) }, half())
1180}
1181
1182fn ahead_const(func: &mut Func, inst: Inst, value: i128) -> Value {
1184 let extra = Extra::Imm(func.add_imm(Imm::int(value, half())));
1185 written(func, inst, InstData { extra, ..InstData::new(Opcode::IConst) }, half())
1186}
1187
1188fn written(func: &mut Func, inst: Inst, data: InstData, ty: Type) -> Value {
1190 let span = func.span(inst);
1191 let made = func.create_inst(data, &[ty], span);
1192 func.insert_before(made, inst);
1193 func[made].first_result.expect("an instruction created with one result has one")
1194}
1195
1196fn becomes(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) {
1198 let args = func.push_values(args);
1199 let data = &mut func[inst];
1200 data.opcode = opcode;
1201 data.args = args;
1202 data.extra = Extra::None;
1203 data.flags = data.flags.intersection(Flags::legal_on(opcode));
1204}
1205
1206#[cfg(test)]
1207mod tests {
1208 use rucc_base::Interner;
1209 use rucc_ir::{
1210 Block, Builder, Flags, Float, Func, MemOrder, Module, Restrict, Signature, Type, Value,
1211 };
1212 use rucc_target::x86_64::{MINGW64, SYSV};
1213 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1214
1215 use super::{HALF, IntPred, MemInfo, Opcode, halves};
1216
1217 fn wide() -> Type {
1219 Type::int(super::WIDE)
1220 }
1221
1222 fn target() -> TargetInfo {
1223 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1224 }
1225
1226 fn printed(func: &Func, names: &mut Interner) -> String {
1227 let module = Module::new(names.intern("w.c"), &target());
1228 rucc_ir::print_func(&module, func, names)
1229 }
1230
1231 fn shell(names: &mut Interner, params: &[Type], returns: &[Type]) -> (Func, Block, Vec<Value>) {
1233 let signature = Signature::new().with_params(params).with_returns(returns);
1234 let mut func = Func::new(names.intern("f"), signature);
1235 let entry = func.create_block();
1236 let values = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
1237 (func, entry, values)
1238 }
1239
1240 fn info(size: u64, align: u32) -> MemInfo {
1242 MemInfo {
1243 size,
1244 align,
1245 order: MemOrder::NotAtomic,
1246 tbaa: None,
1247 owns: 0,
1248 restrict: Restrict::NONE,
1249 }
1250 }
1251
1252 #[test]
1253 fn an_add_carries_from_the_low_half_into_the_high_one() {
1254 let mut names = Interner::new();
1255 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1256 let mut build = Builder::new(&mut func, entry);
1257 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1258 build.ret(&[sum]);
1259
1260 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1261 let text = printed(&func, &mut names);
1262 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1263 assert_eq!(text.matches(" = add ").count(), 3, "three adds: {text}");
1266 assert_eq!(text.matches("icmp ult").count(), 1, "one carry: {text}");
1267 assert_eq!(text.matches(" = zext.i64 ").count(), 1, "the carry as a number: {text}");
1268 }
1269
1270 #[test]
1271 fn a_subtract_borrows_the_other_way_round() {
1272 let mut names = Interner::new();
1273 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1274 let mut build = Builder::new(&mut func, entry);
1275 let difference = build.binary(Opcode::Sub, params[0], params[1], Flags::NONE);
1276 build.ret(&[difference]);
1277
1278 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1279 let text = printed(&func, &mut names);
1280 assert_eq!(text.matches(" = sub ").count(), 3, "three subtracts: {text}");
1281 assert!(text.contains("icmp ult %0, %2"), "the operands are compared: {text}");
1284 }
1285
1286 #[test]
1287 fn the_signature_and_the_entry_block_say_the_same_thing() {
1288 let mut names = Interner::new();
1289 let (mut func, entry, params) = shell(&mut names, &[Type::int(32), wide()], &[wide()]);
1290 let mut build = Builder::new(&mut func, entry);
1291 build.ret(&[params[1]]);
1292
1293 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1294 assert_eq!(
1295 func.signature().param_types().collect::<Vec<_>>(),
1296 [Type::int(32), Type::int(HALF), Type::int(HALF)],
1297 "the wide parameter became two where it stood"
1298 );
1299 assert_eq!(
1300 func.signature().return_types().collect::<Vec<_>>(),
1301 [Type::int(HALF), Type::int(HALF)],
1302 "and so did what comes back"
1303 );
1304 let text = printed(&func, &mut names);
1305 assert!(text.contains("block0(%0: i32, %1: i64, %2: i64)"), "the block agrees: {text}");
1306 assert!(text.contains("return %1, %2"), "both halves go back: {text}");
1307 let _ = entry;
1308 }
1309
1310 #[test]
1311 fn a_read_takes_the_high_word_a_word_above_the_low_one() {
1312 let mut names = Interner::new();
1313 let (mut func, entry, params) = shell(&mut names, &[Type::PTR], &[wide()]);
1314 let mut build = Builder::new(&mut func, entry);
1315 let value = build.load(wide(), params[0], info(16, 16), Flags::NONE);
1316 build.ret(&[value]);
1317
1318 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1319 let text = printed(&func, &mut names);
1320 assert_eq!(text.matches(" = load.i64 ").count(), 2, "two reads: {text}");
1321 assert!(text.contains("ptr_add"), "the high word is a word up: {text}");
1322 assert!(text.contains("align 16"), "the low word keeps what the object had: {text}");
1325 assert!(text.contains("align 8"), "the high word knows less: {text}");
1326 }
1327
1328 #[test]
1329 fn an_equality_asks_once_about_both_halves() {
1330 let mut names = Interner::new();
1331 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1332 let mut build = Builder::new(&mut func, entry);
1333 let same = build.icmp(IntPred::Eq, params[0], params[1]);
1334 let answer = build.unary(Opcode::ZExt, same, Type::int(32));
1335 build.ret(&[answer]);
1336
1337 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1338 let text = printed(&func, &mut names);
1339 assert_eq!(text.matches("icmp").count(), 1, "one comparison: {text}");
1340 assert_eq!(text.matches(" = xor ").count(), 2, "the halves differ or they do not: {text}");
1341 }
1342
1343 #[test]
1344 fn an_ordering_reads_the_low_halves_without_a_sign() {
1345 let mut names = Interner::new();
1346 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1347 let mut build = Builder::new(&mut func, entry);
1348 let below = build.icmp(IntPred::Slt, params[0], params[1]);
1349 let answer = build.unary(Opcode::ZExt, below, Type::int(32));
1350 build.ret(&[answer]);
1351
1352 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1353 let text = printed(&func, &mut names);
1354 assert!(text.contains("icmp slt"), "the high halves keep the sign: {text}");
1355 assert!(text.contains("icmp ult"), "the low halves have none: {text}");
1356 assert!(
1357 text.contains("icmp eq"),
1358 "and the low halves only matter when the high tie: {text}"
1359 );
1360 }
1361
1362 #[test]
1369 fn an_ordering_that_allows_equality_asks_the_high_halves_a_strict_question() {
1370 let mut names = Interner::new();
1371 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1372 let mut build = Builder::new(&mut func, entry);
1373 let at_least = build.icmp(IntPred::Sge, params[0], params[1]);
1374 let answer = build.unary(Opcode::ZExt, at_least, Type::int(32));
1375 build.ret(&[answer]);
1376
1377 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1378 let text = printed(&func, &mut names);
1379 assert!(text.contains("icmp sgt"), "the high halves settle it outright: {text}");
1380 assert!(!text.contains("icmp sge"), "a tie in the high halves settles nothing: {text}");
1381 assert!(text.contains("icmp uge"), "the low halves are the ones allowed to tie: {text}");
1382 }
1383
1384 #[test]
1385 fn a_widening_puts_the_sign_of_the_value_in_the_high_half() {
1386 let mut names = Interner::new();
1387 let (mut func, entry, params) = shell(&mut names, &[Type::int(32)], &[wide()]);
1388 let mut build = Builder::new(&mut func, entry);
1389 let value = build.unary(Opcode::SExt, params[0], wide());
1390 build.ret(&[value]);
1391
1392 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1393 let text = printed(&func, &mut names);
1394 assert!(text.contains("sext.i64"), "the value fills the low half: {text}");
1395 assert!(text.contains("ashr"), "and its sign fills the high one: {text}");
1396 }
1397
1398 #[test]
1399 fn a_block_parameter_becomes_two_and_every_branch_passes_two() {
1400 let mut names = Interner::new();
1401 let (mut func, entry, params) = shell(&mut names, &[wide(), Type::int(32)], &[wide()]);
1402 let tail = func.create_block();
1403 let carried = func.append_param(tail, wide());
1404 let mut build = Builder::new(&mut func, entry);
1405 let zero = build.iconst(Type::int(32), 0);
1406 let taken = build.icmp(IntPred::Ne, params[1], zero);
1407 let other = build.iconst(wide(), 7);
1408 build.br_if(taken, tail, &[params[0]], tail, &[other]);
1409 let mut build = Builder::new(&mut func, tail);
1410 build.ret(&[carried]);
1411
1412 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1413 let text = printed(&func, &mut names);
1414 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1415 assert!(text.contains("block1(%7: i64, %8: i64)"), "the block takes two: {text}");
1416 assert_eq!(text.matches("block1(").count(), 3, "and both edges pass two: {text}");
1417 }
1418
1419 #[test]
1426 fn a_multiply_is_three_multiplies_and_the_carry_out_of_the_low_ones() {
1427 let mut names = Interner::new();
1428 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1429 let mut build = Builder::new(&mut func, entry);
1430 let product = build.binary(Opcode::Mul, params[0], params[1], Flags::NONE);
1431 build.ret(&[product]);
1432
1433 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1434 let text = printed(&func, &mut names);
1435 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1436 assert_eq!(text.matches(" = mul ").count(), 7, "three and the carry's four: {text}");
1437 }
1438
1439 #[test]
1446 fn each_of_the_four_divisions_calls_the_routine_of_that_name() {
1447 for (opcode, routine) in [
1448 (Opcode::UDiv, "__udivti3"),
1449 (Opcode::SDiv, "__divti3"),
1450 (Opcode::URem, "__umodti3"),
1451 (Opcode::SRem, "__modti3"),
1452 ] {
1453 let mut names = Interner::new();
1454 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1455 let mut build = Builder::new(&mut func, entry);
1456 let answer = build.binary(opcode, params[0], params[1], Flags::NONE);
1457 build.ret(&[answer]);
1458
1459 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1460 let text = printed(&func, &mut names);
1461 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1462 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1463 }
1464 }
1465
1466 #[test]
1472 fn a_divide_hands_over_four_halves_and_takes_two_back() {
1473 let mut names = Interner::new();
1474 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1475 let mut build = Builder::new(&mut func, entry);
1476 let quotient = build.binary(Opcode::UDiv, params[0], params[1], Flags::NONE);
1477 build.ret(&[quotient]);
1478
1479 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1480 let text = printed(&func, &mut names);
1481 assert!(text.contains("@__udivti3(%0, %1, %2, %3)"), "four halves go over: {text}");
1482 assert!(text.contains("return %4, %5"), "and two come back: {text}");
1483 }
1484
1485 #[test]
1491 fn a_divide_of_something_computed_calls_with_the_halves_of_it() {
1492 let mut names = Interner::new();
1493 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1494 let mut build = Builder::new(&mut func, entry);
1495 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1496 let quotient = build.binary(Opcode::SDiv, sum, params[1], Flags::NONE);
1497 build.ret(&[quotient]);
1498
1499 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1500 let text = printed(&func, &mut names);
1501 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1502 assert_eq!(text.matches(" = add ").count(), 3, "the sum is still a sum: {text}");
1503 assert_eq!(text.matches("call @__divti3").count(), 1, "one call: {text}");
1504 }
1505
1506 #[test]
1512 fn each_conversion_between_this_width_and_a_float_calls_the_routine_of_that_name() {
1513 let double = Type::float(Float::F64);
1514 let single = Type::float(Float::F32);
1515 let quad = Type::float(Float::F128);
1516 for (opcode, float, routine) in [
1517 (Opcode::SIToFP, double, "__floattidf"),
1518 (Opcode::SIToFP, single, "__floattisf"),
1519 (Opcode::UIToFP, double, "__floatuntidf"),
1520 (Opcode::UIToFP, single, "__floatuntisf"),
1521 (Opcode::SIToFP, quad, "__floattitf"),
1522 (Opcode::UIToFP, quad, "__floatuntitf"),
1523 ] {
1524 let mut names = Interner::new();
1525 let (mut func, entry, params) = shell(&mut names, &[wide()], &[float]);
1526 let mut build = Builder::new(&mut func, entry);
1527 let answer = build.unary(opcode, params[0], float);
1528 build.ret(&[answer]);
1529
1530 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1531 let text = printed(&func, &mut names);
1532 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1533 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1534 }
1535 for (opcode, float, routine) in [
1536 (Opcode::FPToSI, double, "__fixdfti"),
1537 (Opcode::FPToSI, single, "__fixsfti"),
1538 (Opcode::FPToUI, double, "__fixunsdfti"),
1539 (Opcode::FPToUI, single, "__fixunssfti"),
1540 (Opcode::FPToSI, quad, "__fixtfti"),
1541 (Opcode::FPToUI, quad, "__fixunstfti"),
1542 ] {
1543 let mut names = Interner::new();
1544 let (mut func, entry, params) = shell(&mut names, &[float], &[wide()]);
1545 let mut build = Builder::new(&mut func, entry);
1546 let answer = build.unary(opcode, params[0], wide());
1547 build.ret(&[answer]);
1548
1549 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1550 let text = printed(&func, &mut names);
1551 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1552 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1553 }
1554 }
1555
1556 #[test]
1562 fn a_conversion_hands_over_halves_one_way_and_takes_them_back_the_other() {
1563 let double = Type::float(Float::F64);
1564 let mut names = Interner::new();
1565 let (mut func, entry, params) = shell(&mut names, &[wide()], &[double]);
1566 let mut build = Builder::new(&mut func, entry);
1567 let answer = build.unary(Opcode::SIToFP, params[0], double);
1568 build.ret(&[answer]);
1569
1570 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1571 let text = printed(&func, &mut names);
1572 assert!(text.contains("@__floattidf(%0, %1)"), "two halves go over: {text}");
1573 assert!(text.contains("return %2"), "and one float comes back: {text}");
1574
1575 let mut names = Interner::new();
1576 let (mut func, entry, params) = shell(&mut names, &[double], &[wide()]);
1577 let mut build = Builder::new(&mut func, entry);
1578 let answer = build.unary(Opcode::FPToSI, params[0], wide());
1579 build.ret(&[answer]);
1580
1581 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1582 let text = printed(&func, &mut names);
1583 assert!(text.contains("@__fixdfti(%0)"), "the float goes over as it is: {text}");
1584 assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1585 }
1586
1587 #[test]
1594 fn a_conversion_against_a_quad_hands_over_the_pair_and_the_quad_whole() {
1595 let quad = Type::float(Float::F128);
1596 let mut names = Interner::new();
1597 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
1598 let mut build = Builder::new(&mut func, entry);
1599 let answer = build.unary(Opcode::UIToFP, params[0], quad);
1600 build.ret(&[answer]);
1601
1602 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1603 let text = printed(&func, &mut names);
1604 assert!(text.contains("@__floatuntitf(%0, %1)"), "two halves go over: {text}");
1605 assert!(text.contains("return %2"), "and one quad comes back: {text}");
1606
1607 let mut names = Interner::new();
1608 let (mut func, entry, params) = shell(&mut names, &[quad], &[wide()]);
1609 let mut build = Builder::new(&mut func, entry);
1610 let answer = build.unary(Opcode::FPToSI, params[0], wide());
1611 build.ret(&[answer]);
1612
1613 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1614 let text = printed(&func, &mut names);
1615 assert!(text.contains("@__fixtfti(%0)"), "the quad goes over as it is: {text}");
1616 assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1617 }
1618
1619 #[test]
1626 fn a_conversion_at_a_width_the_runtime_has_no_routine_for_is_left_alone() {
1627 let long = Type::float(Float::F80);
1628 let mut names = Interner::new();
1629 let (mut func, entry, params) = shell(&mut names, &[wide()], &[long]);
1630 let mut build = Builder::new(&mut func, entry);
1631 let answer = build.unary(Opcode::SIToFP, params[0], long);
1632 build.ret(&[answer]);
1633
1634 assert!(!halves(&mut func, &mut names, &SYSV), "the pass does not understand this one");
1635 let text = printed(&func, &mut names);
1636 assert!(text.contains("i128"), "the width is still there: {text}");
1637 }
1638
1639 #[test]
1646 fn a_shift_left_chooses_between_a_count_that_crossed_a_half_and_one_that_did_not() {
1647 let mut names = Interner::new();
1648 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1649 let mut build = Builder::new(&mut func, entry);
1650 let moved = build.binary(Opcode::Shl, params[0], params[1], Flags::NONE);
1651 build.ret(&[moved]);
1652
1653 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1654 let text = printed(&func, &mut names);
1655 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1656 assert_eq!(
1657 text.matches(" = shl ").count(),
1658 2,
1659 "one per half, and the far case reuses one: {text}"
1660 );
1661 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1662 assert_eq!(text.matches(" = lshr ").count(), 2, "the crossing bits, in two steps: {text}");
1663 }
1664
1665 #[test]
1672 fn the_bits_that_cross_move_one_place_and_then_the_rest_of_the_way() {
1673 let mut names = Interner::new();
1674 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1675 let mut build = Builder::new(&mut func, entry);
1676 let moved = build.binary(Opcode::LShr, params[0], params[1], Flags::NONE);
1677 build.ret(&[moved]);
1678
1679 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1680 let text = printed(&func, &mut names);
1681 assert!(text.contains("iconst.i64 63"), "sixty three is the distance left: {text}");
1682 assert!(text.contains("iconst.i64 1"), "after the one place that comes first: {text}");
1683 assert!(text.contains(" = sub "), "the rest of the way is worked out: {text}");
1684 assert!(
1685 !text.contains("iconst.i64 127"),
1686 "and the count is not masked to the width: {text}"
1687 );
1688 }
1689
1690 #[test]
1696 fn an_arithmetic_shift_right_leaves_the_sign_bit_where_a_logical_one_leaves_zeroes() {
1697 let mut names = Interner::new();
1698 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1699 let mut build = Builder::new(&mut func, entry);
1700 let moved = build.binary(Opcode::AShr, params[0], params[1], Flags::NONE);
1701 build.ret(&[moved]);
1702
1703 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1704 let text = printed(&func, &mut names);
1705 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1706 assert_eq!(text.matches(" = ashr ").count(), 2, "the count and the sign: {text}");
1708 assert_eq!(text.matches(" = lshr ").count(), 1, "the low half is not signed: {text}");
1709 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1710 }
1711
1712 #[test]
1713 fn a_parameter_with_one_register_left_leaves_the_function_alone() {
1714 let mut names = Interner::new();
1715 let word = Type::int(HALF);
1716 let params = [word, word, word, word, word, wide()];
1720 let (mut func, entry, values) = shell(&mut names, ¶ms, &[word]);
1721 let mut build = Builder::new(&mut func, entry);
1722 let low = build.unary(Opcode::Trunc, values[5], word);
1723 build.ret(&[low]);
1724 let before = printed(&func, &mut names);
1725
1726 assert!(!halves(&mut func, &mut names, &SYSV), "one of the halves has no register");
1727 assert_eq!(printed(&func, &mut names), before, "so nothing moved");
1728 }
1729
1730 #[test]
1739 fn a_block_made_after_the_one_it_runs_before_is_still_split() {
1740 let mut names = Interner::new();
1741 let (mut func, entry, params) = shell(&mut names, &[wide()], &[wide()]);
1742 let tail = func.create_block();
1743 let middle = func.create_block();
1744 let mut build = Builder::new(&mut func, entry);
1745 build.jump(middle, &[]);
1746 let mut build = Builder::new(&mut func, middle);
1747 let doubled = build.binary(Opcode::Add, params[0], params[0], Flags::NONE);
1748 build.jump(tail, &[]);
1749 let mut build = Builder::new(&mut func, tail);
1750 let again = build.binary(Opcode::Add, doubled, doubled, Flags::NONE);
1751 build.ret(&[again]);
1752
1753 assert!(
1754 halves(&mut func, &mut names, &SYSV),
1755 "the definition runs before the use whatever the list says"
1756 );
1757 let text = printed(&func, &mut names);
1758 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1759 }
1760
1761 #[test]
1762 fn a_function_with_nothing_that_wide_is_not_touched() {
1763 let mut names = Interner::new();
1764 let word = Type::int(HALF);
1765 let (mut func, entry, params) = shell(&mut names, &[word, word], &[word]);
1766 let mut build = Builder::new(&mut func, entry);
1767 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1768 build.ret(&[sum]);
1769
1770 assert!(!halves(&mut func, &mut names, &SYSV), "there is nothing to split");
1771 }
1772
1773 #[test]
1780 fn on_windows_a_wide_operand_goes_over_as_the_address_of_a_copy() {
1781 let mut names = Interner::new();
1782 let quad = Type::float(Float::F64);
1783 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
1784 let mut build = Builder::new(&mut func, entry);
1785 let answer = build.unary(Opcode::SIToFP, params[0], quad);
1786 build.ret(&[answer]);
1787
1788 assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
1789 let text = printed(&func, &mut names);
1790 assert!(text.contains("call @__floattidf"), "{text}");
1791 assert_eq!(text.matches("alloca").count(), 1, "one slot: {text}");
1793 assert_eq!(text.matches("store").count(), 2, "a half at a time: {text}");
1794 assert_eq!(text.matches("ptr_add").count(), 1, "the high half eight bytes up: {text}");
1795 assert!(!text.contains("__floattidf(%0, %1)"), "not the two halves: {text}");
1796 }
1797
1798 #[test]
1800 fn on_windows_a_wide_answer_at_this_format_comes_back_through_a_slot() {
1801 let mut names = Interner::new();
1802 let quad = Type::float(Float::F128);
1803 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
1804 let mut build = Builder::new(&mut func, entry);
1805 let answer = build.unary(Opcode::SIToFP, params[0], quad);
1806 build.ret(&[answer]);
1807
1808 assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
1809 let text = printed(&func, &mut names);
1810 assert!(text.contains("call @__floattitf"), "{text}");
1811 assert_eq!(text.matches("alloca").count(), 2, "two slots: {text}");
1813 assert_eq!(text.matches(" = call").count(), 0, "the call answers nothing: {text}");
1814 assert_eq!(text.matches(" = load").count(), 1, "the answer is the load after it: {text}");
1815 }
1816
1817 #[test]
1819 fn the_convention_with_registers_for_both_halves_puts_nothing_on_the_frame() {
1820 let mut names = Interner::new();
1821 let double = Type::float(Float::F64);
1822 let (mut func, entry, params) = shell(&mut names, &[wide()], &[double]);
1823 let mut build = Builder::new(&mut func, entry);
1824 let answer = build.unary(Opcode::SIToFP, params[0], double);
1825 build.ret(&[answer]);
1826
1827 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1828 let text = printed(&func, &mut names);
1829 assert!(text.contains("@__floattidf(%0, %1)"), "both halves in registers: {text}");
1830 assert!(!text.contains("alloca"), "nothing goes through the frame: {text}");
1831 }
1832}