1use std::collections::{HashMap, HashSet};
73
74use rucc_base::Interner;
75use rucc_ir::{
76 Abi, Block, BlockCall, CallInfo, Def, Extra, Flags, Float, Func, Imm, Inst, InstData, IntPred,
77 MemInfo, MemOrder, Opcode, Param, Restrict, Signature, Type, Value,
78};
79use rucc_target::{AbiDescription, CallRegs, Places, Where};
80
81use crate::capability;
82use crate::expand;
83
84const WIDE: u32 = 128;
86
87const MODE: &str = "i128";
89
90const HALF: u32 = 64;
92
93const STEP: u64 = 8;
95
96fn is_wide(ty: Type) -> bool {
98 ty.is_int() && ty.is_scalar() && ty.bits() == WIDE
99}
100
101fn half() -> Type {
103 Type::int(HALF)
104}
105
106pub fn halves(func: &mut Func, names: &mut Interner, conv: &CallRegs) -> bool {
117 if !func.values().any(|value| is_wide(func[value].ty)) {
118 return false;
119 }
120 let insts: Vec<Inst> =
121 walk(func).into_iter().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
122 let order: HashMap<Inst, usize> =
123 insts.iter().enumerate().map(|(at, &inst)| (inst, at)).collect();
124 if !insts.iter().enumerate().all(|(at, &inst)| can_split(func, &order, at, inst)) {
125 return false;
126 }
127 if !func.signatures().all(|signature| fits(signature, conv)) {
128 return false;
129 }
130
131 let mut halves: Halves = HashMap::new();
132 let mut forward: HashMap<Value, Value> = HashMap::new();
133 for block in func.blocks().collect::<Vec<_>>() {
134 params(func, block, &mut halves, &mut forward);
135 }
136 for &inst in &insts {
137 rewrite(func, names, conv.abi, &mut halves, &mut forward, inst);
138 }
139 substitute(func, &forward);
140 let signature = split_signature(func.signature());
141 func.set_signature(signature);
142 true
143}
144
145fn walk(func: &Func) -> Vec<Block> {
163 let Some(entry) = func.entry() else { return func.blocks().collect() };
164 let mut seen: HashSet<Block> = HashSet::new();
165 let mut order: Vec<Block> = Vec::new();
166 let mut stack: Vec<(Block, bool)> = vec![(entry, false)];
169 seen.insert(entry);
170 while let Some((block, done)) = stack.pop() {
171 if done {
172 order.push(block);
173 continue;
174 }
175 stack.push((block, true));
176 let Some(term) = func.terminator(block) else { continue };
177 for call in func.successors(term) {
178 if seen.insert(call.block) {
179 stack.push((call.block, false));
180 }
181 }
182 }
183 order.reverse();
184 order.extend(func.blocks().filter(|block| !seen.contains(block)));
185 order
186}
187
188type Halves = HashMap<Value, (Value, Value)>;
190
191fn understood(opcode: Opcode) -> bool {
202 matches!(
203 opcode,
204 Opcode::IConst
205 | Opcode::Load
206 | Opcode::Store
207 | Opcode::Add
208 | Opcode::Sub
209 | Opcode::Mul
210 | Opcode::UDiv
211 | Opcode::SDiv
212 | Opcode::URem
213 | Opcode::SRem
214 | Opcode::Shl
215 | Opcode::LShr
216 | Opcode::AShr
217 | Opcode::And
218 | Opcode::Or
219 | Opcode::Xor
220 | Opcode::ICmp
221 | Opcode::Select
222 | Opcode::SIToFP
223 | Opcode::UIToFP
224 | Opcode::FPToSI
225 | Opcode::FPToUI
226 | Opcode::Trunc
227 | Opcode::SExt
228 | Opcode::ZExt
229 | Opcode::Call
230 | Opcode::CallIndirect
231 | Opcode::Return
232 | Opcode::Jump
233 | Opcode::BrIf
234 )
235}
236
237fn can_split(func: &Func, order: &HashMap<Inst, usize>, at: usize, inst: Inst) -> bool {
242 let data = func[inst];
243 let reads = operands(func, inst);
244 let wide = |&value: &Value| is_wide(func[value].ty);
245 if !reads.iter().any(wide) && !data.results().any(|value| is_wide(func[value].ty)) {
246 return true;
247 }
248 if !understood(data.opcode) {
249 return false;
250 }
251 if func.carries_mem(inst) {
255 return false;
256 }
257 if data.opcode == Opcode::SExt && reads.iter().any(|&value| func[value].ty.bits() < 8) {
261 return false;
262 }
263 if matches!(data.opcode, Opcode::SIToFP | Opcode::UIToFP | Opcode::FPToSI | Opcode::FPToUI)
268 && converted(func, inst).is_none()
269 {
270 return false;
271 }
272 if matches!(data.opcode, Opcode::Call | Opcode::CallIndirect) {
276 let Extra::Call(info) = data.extra else { return false };
277 if func[func[info].signature].variadic {
278 return false;
279 }
280 }
281 reads.iter().filter(|value| wide(value)).all(|&value| match func[value].def {
285 Def::Result { inst, .. } => order.get(&inst).is_some_and(|&def| def < at),
286 Def::Param { .. } => true,
287 })
288}
289
290fn converted(func: &Func, inst: Inst) -> Option<Float> {
297 let data = func[inst];
298 let mut floats = func[data.args]
299 .iter()
300 .copied()
301 .chain(data.results())
302 .map(|value| func[value].ty)
303 .filter(|ty| ty.is_float());
304 let only = floats.next()?;
305 if floats.next().is_some() {
306 return None;
307 }
308 match only.format() {
309 Some(format @ (Float::F32 | Float::F64 | Float::F128)) => Some(format),
310 _ => None,
311 }
312}
313
314fn operands(func: &Func, inst: Inst) -> Vec<Value> {
320 let mut reads = func[func[inst].args].to_vec();
321 for call in func.successors(inst).collect::<Vec<_>>() {
322 reads.extend_from_slice(&func[call.args]);
323 }
324 reads
325}
326
327fn fits(signature: &Signature, conv: &CallRegs) -> bool {
339 let mut places = Places::new(conv);
340 for param in &signature.params {
341 if let Abi::ByVal { size, align } = param.abi {
345 places.on_stack(u32::try_from(size).unwrap_or(u32::MAX), align);
346 } else if crate::abi::on_the_stack(param.ty) {
347 let (size, align) = crate::abi::X87_AREA;
348 places.on_stack(size, align);
349 } else if is_wide(param.ty) {
350 let low = places.integer();
351 let high = places.integer();
352 if !matches!((low, high), (Where::Reg(_), Where::Reg(_))) {
353 return false;
354 }
355 } else if param.ty.is_float() {
356 places.float(crate::abi::float_bytes(param.ty));
357 } else {
358 places.integer();
359 }
360 }
361 true
362}
363
364fn params(func: &mut Func, block: Block, halves: &mut Halves, forward: &mut HashMap<Value, Value>) {
371 let old: Vec<Value> = func[block].params.clone();
372 if !old.iter().any(|&value| is_wide(func[value].ty)) {
373 return;
374 }
375 for &value in &old {
376 if is_wide(func[value].ty) {
377 let low = func.append_param(block, half());
378 let high = func.append_param(block, half());
379 halves.insert(value, (low, high));
380 } else {
381 let again = func.append_param(block, func[value].ty);
382 forward.insert(value, again);
383 }
384 }
385 func.retain_params(block, |value| !old.contains(&value));
386}
387
388fn rewrite(
390 func: &mut Func,
391 names: &mut Interner,
392 abi: &'static AbiDescription,
393 halves: &mut Halves,
394 forward: &mut HashMap<Value, Value>,
395 inst: Inst,
396) {
397 let data = func[inst];
398 let produces = data.results().any(|value| is_wide(func[value].ty));
399 let takes = func[data.args].iter().any(|&value| is_wide(func[value].ty));
400 match data.opcode {
401 Opcode::IConst if produces => constant(func, halves, inst),
402 Opcode::Load if produces => load(func, halves, inst),
403 Opcode::Store if takes => store(func, halves, inst),
404 Opcode::Add | Opcode::Sub if produces => carried(func, halves, inst, data.opcode),
405 Opcode::Mul if produces => multiply(func, halves, inst),
406 Opcode::UDiv | Opcode::SDiv | Opcode::URem | Opcode::SRem if produces => {
407 divide(func, names, abi, halves, inst, data.opcode);
408 }
409 Opcode::Shl | Opcode::LShr | Opcode::AShr if produces => {
410 shifted(func, halves, inst, data.opcode);
411 }
412 Opcode::And | Opcode::Or | Opcode::Xor if produces => {
413 bitwise(func, halves, inst, data.opcode);
414 }
415 Opcode::SIToFP | Opcode::UIToFP if takes => {
416 to_float(func, names, abi, halves, forward, inst, data.opcode == Opcode::SIToFP);
417 }
418 Opcode::FPToSI | Opcode::FPToUI if produces => {
419 from_float(func, names, abi, halves, inst, data.opcode == Opcode::FPToSI);
420 }
421 Opcode::ICmp if takes => compare(func, halves, forward, inst),
422 Opcode::Select if produces => choose(func, halves, inst),
423 Opcode::Trunc if takes => truncate(func, halves, forward, inst),
424 Opcode::SExt | Opcode::ZExt if produces => {
425 extend(func, halves, inst, data.opcode == Opcode::SExt);
426 }
427 Opcode::Call | Opcode::CallIndirect if produces || takes => {
428 call(func, halves, forward, inst);
429 }
430 Opcode::Return if takes => flatten(func, halves, inst),
431 Opcode::Jump | Opcode::BrIf => edges(func, halves, inst),
432 _ => {}
433 }
434}
435
436fn constant(func: &mut Func, halves: &mut Halves, inst: Inst) {
438 let Extra::Imm(imm) = func[inst].extra else { return };
439 let bits = func[imm].unsigned();
440 #[expect(clippy::cast_possible_truncation, reason = "the halves are what this is taking")]
441 let (low, high) = (bits as u64, (bits >> HALF) as u64);
442 let low = ahead_const(func, inst, i128::from(low));
443 let high = ahead_const(func, inst, i128::from(high));
444 replace(func, halves, inst, low, high);
445}
446
447fn load(func: &mut Func, halves: &mut Halves, inst: Inst) {
453 let data = func[inst];
454 let Extra::Mem(mem) = data.extra else { return };
455 let info = func[mem];
456 let Some(&from) = func[data.args].first() else { return };
457 let low = read(func, inst, from, word(info, 0), data.flags);
458 let up = stepped(func, inst, from);
459 let high = read(func, inst, up, word(info, STEP), data.flags);
460 replace(func, halves, inst, low, high);
461}
462
463fn store(func: &mut Func, halves: &mut Halves, inst: Inst) {
465 let data = func[inst];
466 let Extra::Mem(mem) = data.extra else { return };
467 let info = func[mem];
468 let args = func[data.args].to_vec();
469 let [value, into] = args[..] else { return };
470 let Some(&(low, high)) = halves.get(&value) else { return };
471 write(func, inst, low, into, word(info, 0), data.flags);
472 let up = stepped(func, inst, into);
473 write(func, inst, high, up, word(info, STEP), data.flags);
474 func.remove_inst(inst);
475}
476
477fn carried(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
487 let args = func[func[inst].args].to_vec();
488 let [a, b] = args[..] else { return };
489 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
490 return;
491 };
492 let low = ahead(func, inst, opcode, &[a_low, b_low]);
493 let carried = if opcode == Opcode::Add {
494 compared(func, inst, IntPred::Ult, low, a_low)
495 } else {
496 compared(func, inst, IntPred::Ult, a_low, b_low)
497 };
498 let carry = ahead(func, inst, Opcode::ZExt, &[carried]);
499 let high = ahead(func, inst, opcode, &[a_high, b_high]);
500 let high = ahead(func, inst, opcode, &[high, carry]);
501 replace(func, halves, inst, low, high);
502}
503
504fn multiply(func: &mut Func, halves: &mut Halves, inst: Inst) {
524 let args = func[func[inst].args].to_vec();
525 let [a, b] = args[..] else { return };
526 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
527 return;
528 };
529 let low = ahead(func, inst, Opcode::Mul, &[a_low, b_low]);
530 let carried = expand::high_half(func, inst, a_low, b_low, false, half());
531 let cross = ahead(func, inst, Opcode::Mul, &[a_low, b_high]);
532 let other = ahead(func, inst, Opcode::Mul, &[a_high, b_low]);
533 let high = ahead(func, inst, Opcode::Add, &[carried, cross]);
534 let high = ahead(func, inst, Opcode::Add, &[high, other]);
535 replace(func, halves, inst, low, high);
536}
537
538fn divide(
556 func: &mut Func,
557 names: &mut Interner,
558 abi: &'static AbiDescription,
559 halves: &mut Halves,
560 inst: Inst,
561 opcode: Opcode,
562) {
563 let args = func[func[inst].args].to_vec();
564 let [a, b] = args[..] else { return };
565 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
566 return;
567 };
568 let Some(routine) = capability::libcall(opcode, MODE) else { return };
572 let args = [Operand::Split(a_low, a_high), Operand::Split(b_low, b_high)];
573 let made = runtime(func, names, abi, inst, routine, &args, &[half(), half()]);
574 let [low, high] = made[..] else { return };
575 replace(func, halves, inst, low, high);
576}
577
578fn to_float(
588 func: &mut Func,
589 names: &mut Interner,
590 abi: &'static AbiDescription,
591 halves: &Halves,
592 forward: &mut HashMap<Value, Value>,
593 inst: Inst,
594 signed: bool,
595) {
596 let Some(&arg) = func[func[inst].args].first() else { return };
597 let Some(&(low, high)) = halves.get(&arg) else { return };
598 let (Some(result), Some(format)) = (func[inst].first_result, converted(func, inst)) else {
599 return;
600 };
601 let routine = going_up(signed, format);
602 let args = [Operand::Split(low, high)];
603 let made = runtime(func, names, abi, inst, routine, &args, &[func[result].ty]);
604 if let [answer] = made[..] {
605 forward.insert(result, answer);
606 }
607 func.remove_inst(inst);
608}
609
610fn from_float(
620 func: &mut Func,
621 names: &mut Interner,
622 abi: &'static AbiDescription,
623 halves: &mut Halves,
624 inst: Inst,
625 signed: bool,
626) {
627 let Some(&arg) = func[func[inst].args].first() else { return };
628 let Some(format) = converted(func, inst) else { return };
629 let routine = coming_down(signed, format);
630 let args = [Operand::Whole(arg)];
631 let made = runtime(func, names, abi, inst, routine, &args, &[half(), half()]);
632 let [low, high] = made[..] else { return };
633 replace(func, halves, inst, low, high);
634}
635
636fn going_up(signed: bool, format: Float) -> &'static str {
642 let mode = match format {
643 Float::F32 => "i128.f32",
644 Float::F64 => "i128.f64",
645 _ => "i128.f128",
646 };
647 routine(if signed { Opcode::SIToFP } else { Opcode::UIToFP }, mode)
648}
649
650fn coming_down(signed: bool, format: Float) -> &'static str {
652 let mode = match format {
653 Float::F32 => "f32.i128",
654 Float::F64 => "f64.i128",
655 _ => "f128.i128",
656 };
657 routine(if signed { Opcode::FPToSI } else { Opcode::FPToUI }, mode)
658}
659
660fn routine(opcode: Opcode, mode: &str) -> &'static str {
665 capability::libcall(opcode, mode)
666 .unwrap_or_else(|| panic!("no routine for `{}` at `{mode}`", opcode.name()))
667}
668
669#[derive(Clone, Copy)]
675enum Operand {
676 Whole(Value),
678 Split(Value, Value),
680}
681
682fn runtime(
707 func: &mut Func,
708 names: &mut Interner,
709 abi: &'static AbiDescription,
710 inst: Inst,
711 routine: &str,
712 args: &[Operand],
713 results: &[Type],
714) -> Vec<Value> {
715 let mut params: Vec<Param> = Vec::new();
716 let mut values: Vec<Value> = Vec::new();
717 let mut answer = Answer::Registers;
718 match *results {
720 [ty] if abi.scalar_is_by_reference(bytes(ty)) => {
721 let size = bytes(ty);
722 let align = align(size);
723 let slot = room(func, inst, size, align);
724 params.push(Param::with_abi(Type::PTR, Abi::Sret { size, align }));
725 values.push(slot);
726 answer = Answer::Slot(slot, ty);
727 }
728 [low, high] if low == half() && high == half() => {
729 if let Some(format) = packed(abi) {
730 answer = Answer::Packed(format);
731 }
732 }
733 _ => {}
734 }
735 for &arg in args {
736 handed(func, abi, inst, arg, &mut params, &mut values);
737 }
738 let answers = match answer {
739 Answer::Registers => results.to_vec(),
740 Answer::Slot(..) => Vec::new(),
741 Answer::Packed(format) => vec![Type::float(format)],
742 };
743 let returns = answers.iter().map(|&ty| Param::new(ty)).collect();
744 let signature = func.add_signature(Signature { params, returns, variadic: false });
745 let callee = Some(names.intern(routine));
746 let varargs = func.push_abis(&[]);
747 let extra = Extra::Call(func.add_call(CallInfo { callee, signature, varargs }));
748 let pushed = func.push_values(&values);
749 let span = func.span(inst);
750 let data = InstData { args: pushed, extra, ..InstData::new(Opcode::Call) };
751 let made = func.create_inst(data, &answers, span);
752 func.insert_before(made, inst);
753 match answer {
754 Answer::Registers => func[made].results().collect(),
755 Answer::Slot(slot, ty) => {
756 let size = bytes(ty);
757 let info = whole(size, align(size));
758 let extra = Extra::Mem(func.add_mem(info));
759 let args = func.push_values(&[slot]);
760 let data = InstData { args, extra, ..InstData::new(Opcode::Load) };
761 vec![written(func, inst, data, ty)]
762 }
763 Answer::Packed(format) => unpacked(func, inst, made, format),
764 }
765}
766
767#[derive(Clone, Copy)]
769enum Answer {
770 Registers,
773 Slot(Value, Type),
776 Packed(Float),
778}
779
780fn packed(abi: &'static AbiDescription) -> Option<Float> {
787 let format = abi.wide_integer_returns_in(u64::from(WIDE / 8))?;
788 Float::from_bits(format.width())
789}
790
791fn unpacked(func: &mut Func, inst: Inst, call: Inst, format: Float) -> Vec<Value> {
799 let Some(value) = func[call].first_result else { return Vec::new() };
800 let size = bytes(Type::float(format));
801 let align = align(size);
802 let slot = room(func, inst, size, align);
803 let info = whole(size, align);
804 write(func, inst, value, slot, info, Flags::NONE);
805 let low = read(func, inst, slot, word(info, 0), Flags::NONE);
806 let up = stepped(func, inst, slot);
807 let high = read(func, inst, up, word(info, STEP), Flags::NONE);
808 vec![low, high]
809}
810
811fn handed(
813 func: &mut Func,
814 abi: &'static AbiDescription,
815 inst: Inst,
816 arg: Operand,
817 params: &mut Vec<Param>,
818 values: &mut Vec<Value>,
819) {
820 match arg {
821 Operand::Whole(value) => {
822 let ty = func[value].ty;
823 let size = bytes(ty);
824 if !abi.scalar_is_by_reference(size) {
825 params.push(Param::new(ty));
826 values.push(value);
827 return;
828 }
829 let align = align(size);
830 let slot = room(func, inst, size, align);
831 write(func, inst, value, slot, whole(size, align), Flags::NONE);
832 params.push(Param::new(Type::PTR));
833 values.push(slot);
834 }
835 Operand::Split(low, high) => {
836 let size = u64::from(WIDE / 8);
837 if !abi.scalar_is_by_reference(size) {
838 params.push(Param::new(half()));
839 values.push(low);
840 params.push(Param::new(half()));
841 values.push(high);
842 return;
843 }
844 let align = align(size);
845 let slot = room(func, inst, size, align);
846 let info = whole(size, align);
847 write(func, inst, low, slot, word(info, 0), Flags::NONE);
848 let up = stepped(func, inst, slot);
849 write(func, inst, high, up, word(info, STEP), Flags::NONE);
850 params.push(Param::new(Type::PTR));
851 values.push(slot);
852 }
853 }
854}
855
856fn bytes(ty: Type) -> u64 {
858 u64::from(ty.bits().div_ceil(8))
859}
860
861fn align(size: u64) -> u32 {
863 u32::try_from(size).unwrap_or(u32::MAX)
864}
865
866fn whole(size: u64, align: u32) -> MemInfo {
868 MemInfo {
869 size,
870 align,
871 order: MemOrder::NotAtomic,
872 tbaa: None,
873 owns: 0,
874 restrict: Restrict::NONE,
875 }
876}
877
878fn room(func: &mut Func, inst: Inst, size: u64, align: u32) -> Value {
880 let extra = Extra::Mem(func.add_mem(whole(size, align)));
881 written(func, inst, InstData { extra, ..InstData::new(Opcode::Alloca) }, Type::PTR)
882}
883
884fn shifted(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
904 let args = func[func[inst].args].to_vec();
905 let [a, b] = args[..] else { return };
906 let (Some(&(a_low, a_high)), Some(&(count, _))) = (halves.get(&a), halves.get(&b)) else {
907 return;
908 };
909 let top = ahead_const(func, inst, i128::from(HALF - 1));
910 let places = ahead(func, inst, Opcode::And, &[count, top]);
911 let back = ahead(func, inst, Opcode::Sub, &[top, places]);
912 let one = ahead_const(func, inst, 1);
913 let zero = ahead_const(func, inst, 0);
914 let bit = ahead_const(func, inst, i128::from(HALF));
915 let reach = ahead(func, inst, Opcode::And, &[count, bit]);
916 let whole = compared(func, inst, IntPred::Ne, reach, zero);
917
918 let (low, high) = if opcode == Opcode::Shl {
919 let moved = ahead(func, inst, Opcode::Shl, &[a_low, places]);
920 let edge = ahead(func, inst, Opcode::LShr, &[a_low, one]);
921 let across = ahead(func, inst, Opcode::LShr, &[edge, back]);
922 let above = ahead(func, inst, Opcode::Shl, &[a_high, places]);
923 let joined = ahead(func, inst, Opcode::Or, &[above, across]);
924 let low = ahead(func, inst, Opcode::Select, &[whole, zero, moved]);
925 let high = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
926 (low, high)
927 } else {
928 let moved = ahead(func, inst, opcode, &[a_high, places]);
929 let edge = ahead(func, inst, Opcode::Shl, &[a_high, one]);
930 let across = ahead(func, inst, Opcode::Shl, &[edge, back]);
931 let below = ahead(func, inst, Opcode::LShr, &[a_low, places]);
932 let joined = ahead(func, inst, Opcode::Or, &[below, across]);
933 let spent = if opcode == Opcode::AShr {
936 ahead(func, inst, Opcode::AShr, &[a_high, top])
937 } else {
938 zero
939 };
940 let low = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
941 let high = ahead(func, inst, Opcode::Select, &[whole, spent, moved]);
942 (low, high)
943 };
944 replace(func, halves, inst, low, high);
945}
946
947fn bitwise(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
950 let args = func[func[inst].args].to_vec();
951 let [a, b] = args[..] else { return };
952 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
953 return;
954 };
955 let low = ahead(func, inst, opcode, &[a_low, b_low]);
956 let high = ahead(func, inst, opcode, &[a_high, b_high]);
957 replace(func, halves, inst, low, high);
958}
959
960fn compare(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
975 let Extra::IntPred(pred) = func[inst].extra else { return };
976 let args = func[func[inst].args].to_vec();
977 let [a, b] = args[..] else { return };
978 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
979 return;
980 };
981 let answer = if matches!(pred, IntPred::Eq | IntPred::Ne) {
982 let low = ahead(func, inst, Opcode::Xor, &[a_low, b_low]);
983 let high = ahead(func, inst, Opcode::Xor, &[a_high, b_high]);
984 let both = ahead(func, inst, Opcode::Or, &[low, high]);
985 let zero = ahead_const(func, inst, 0);
986 compared(func, inst, pred, both, zero)
987 } else {
988 let above = compared(func, inst, strict(pred), a_high, b_high);
989 let below = compared(func, inst, unsigned(pred), a_low, b_low);
990 let same = compared(func, inst, IntPred::Eq, a_high, b_high);
991 let tail = bit(func, inst, Opcode::And, same, below);
992 bit(func, inst, Opcode::Or, above, tail)
993 };
994 if let Some(result) = func[inst].first_result {
995 forward.insert(result, answer);
996 }
997 func.remove_inst(inst);
998}
999
1000fn strict(pred: IntPred) -> IntPred {
1002 match pred {
1003 IntPred::Sle => IntPred::Slt,
1004 IntPred::Sge => IntPred::Sgt,
1005 IntPred::Ule => IntPred::Ult,
1006 IntPred::Uge => IntPred::Ugt,
1007 other => other,
1008 }
1009}
1010
1011fn unsigned(pred: IntPred) -> IntPred {
1013 match pred {
1014 IntPred::Slt => IntPred::Ult,
1015 IntPred::Sle => IntPred::Ule,
1016 IntPred::Sgt => IntPred::Ugt,
1017 IntPred::Sge => IntPred::Uge,
1018 other => other,
1019 }
1020}
1021
1022fn choose(func: &mut Func, halves: &mut Halves, inst: Inst) {
1028 let args = func[func[inst].args].to_vec();
1029 let [cond, then, other] = args[..] else { return };
1030 let (Some(&(then_low, then_high)), Some(&(other_low, other_high))) =
1031 (halves.get(&then), halves.get(&other))
1032 else {
1033 return;
1034 };
1035 let low = ahead(func, inst, Opcode::Select, &[cond, then_low, other_low]);
1036 let high = ahead(func, inst, Opcode::Select, &[cond, then_high, other_high]);
1037 replace(func, halves, inst, low, high);
1038}
1039
1040fn truncate(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
1046 let Some(&arg) = func[func[inst].args].first() else { return };
1047 let Some(&(low, _)) = halves.get(&arg) else { return };
1048 let Some(result) = func[inst].first_result else { return };
1049 if func[result].ty.bits() == HALF {
1050 forward.insert(result, low);
1051 func.remove_inst(inst);
1052 return;
1053 }
1054 becomes(func, inst, Opcode::Trunc, &[low]);
1055}
1056
1057fn extend(func: &mut Func, halves: &mut Halves, inst: Inst, signed: bool) {
1059 let Some(&arg) = func[func[inst].args].first() else { return };
1060 let low = if func[arg].ty.bits() == HALF {
1061 arg
1062 } else {
1063 let opcode = if signed { Opcode::SExt } else { Opcode::ZExt };
1064 ahead(func, inst, opcode, &[arg])
1065 };
1066 let high = if signed {
1067 let top = ahead_const(func, inst, i128::from(HALF - 1));
1068 ahead(func, inst, Opcode::AShr, &[low, top])
1069 } else {
1070 ahead_const(func, inst, 0)
1071 };
1072 replace(func, halves, inst, low, high);
1073}
1074
1075fn call(func: &mut Func, halves: &mut Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
1082 let data = func[inst];
1083 let Extra::Call(info) = data.extra else { return };
1084 let info = func[info];
1085 let args = spread(&func[data.args], halves);
1086 let results: Vec<Type> = data
1087 .results()
1088 .map(|value| func[value].ty)
1089 .flat_map(|ty| if is_wide(ty) { vec![half(), half()] } else { vec![ty] })
1090 .collect();
1091 let signature = func.add_signature(split_signature(&func[info.signature]));
1092 let extra = Extra::Call(func.add_call(CallInfo { signature, ..info }));
1093 let args = func.push_values(&args);
1094 let span = func.span(inst);
1095 let made = func.create_inst(InstData { args, extra, ..data }, &results, span);
1096 func.insert_before(made, inst);
1097 let mut fresh = func[made].results();
1098 for old in data.results() {
1099 if is_wide(func[old].ty) {
1100 let (Some(low), Some(high)) = (fresh.next(), fresh.next()) else { return };
1101 halves.insert(old, (low, high));
1102 } else if let Some(again) = fresh.next() {
1103 forward.insert(old, again);
1104 }
1105 }
1106 func.remove_inst(inst);
1107}
1108
1109fn flatten(func: &mut Func, halves: &Halves, inst: Inst) {
1111 let args = spread(&func[func[inst].args], halves);
1112 func[inst].args = func.push_values(&args);
1113}
1114
1115fn edges(func: &mut Func, halves: &Halves, inst: Inst) {
1117 for at in func.target_list(inst).iter() {
1118 let call = func[at];
1119 let args = func[call.args].to_vec();
1120 if !args.iter().any(|value| halves.contains_key(value)) {
1121 continue;
1122 }
1123 let args = func.push_values(&spread(&args, halves));
1124 func.set_block_call(at, BlockCall { args, ..call });
1125 }
1126}
1127
1128fn spread(args: &[Value], halves: &Halves) -> Vec<Value> {
1130 args.iter()
1131 .flat_map(|value| match halves.get(value) {
1132 Some(&(low, high)) => vec![low, high],
1133 None => vec![*value],
1134 })
1135 .collect()
1136}
1137
1138fn split_signature(signature: &Signature) -> Signature {
1144 let split = |params: &[Param]| -> Vec<Param> {
1145 params
1146 .iter()
1147 .flat_map(|param| {
1148 if is_wide(param.ty) {
1149 vec![Param::new(half()), Param::new(half())]
1150 } else {
1151 vec![*param]
1152 }
1153 })
1154 .collect()
1155 };
1156 Signature {
1157 params: split(&signature.params),
1158 returns: split(&signature.returns),
1159 variadic: signature.variadic,
1160 }
1161}
1162
1163fn replace(func: &mut Func, halves: &mut Halves, inst: Inst, low: Value, high: Value) {
1165 if let Some(result) = func[inst].first_result {
1166 halves.insert(result, (low, high));
1167 }
1168 func.remove_inst(inst);
1169}
1170
1171fn substitute(func: &mut Func, forward: &HashMap<Value, Value>) {
1177 if forward.is_empty() {
1178 return;
1179 }
1180 let with = |value: Value| forward.get(&value).copied().unwrap_or(value);
1181 for block in func.blocks().collect::<Vec<_>>() {
1182 for inst in func.insts(block).collect::<Vec<Inst>>() {
1183 let args = func[inst].args;
1184 func.rewrite(args, with);
1185 for call in func.successors(inst).collect::<Vec<_>>() {
1186 func.rewrite(call.args, with);
1187 }
1188 }
1189 }
1190}
1191
1192fn word(info: MemInfo, at: u64) -> MemInfo {
1194 let align = if at == 0 { info.align } else { info.align.min(8) };
1195 MemInfo { size: STEP, align, ..info }
1196}
1197
1198fn stepped(func: &mut Func, inst: Inst, from: Value) -> Value {
1200 let step = ahead_const(func, inst, i128::from(STEP));
1201 let args = func.push_values(&[from, step]);
1202 written(func, inst, InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR)
1203}
1204
1205fn read(func: &mut Func, inst: Inst, from: Value, info: MemInfo, flags: Flags) -> Value {
1207 let extra = Extra::Mem(func.add_mem(info));
1208 let args = func.push_values(&[from]);
1209 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Load) };
1210 written(func, inst, data, half())
1211}
1212
1213fn write(func: &mut Func, inst: Inst, value: Value, into: Value, info: MemInfo, flags: Flags) {
1215 let span = func.span(inst);
1216 let extra = Extra::Mem(func.add_mem(info));
1217 let args = func.push_values(&[value, into]);
1218 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Store) };
1219 let made = func.create_inst(data, &[], span);
1220 func.insert_before(made, inst);
1221}
1222
1223fn compared(func: &mut Func, inst: Inst, pred: IntPred, lhs: Value, rhs: Value) -> Value {
1226 let args = func.push_values(&[lhs, rhs]);
1227 let extra = Extra::IntPred(pred);
1228 written(func, inst, InstData { args, extra, ..InstData::new(Opcode::ICmp) }, Type::I1)
1229}
1230
1231fn bit(func: &mut Func, inst: Inst, opcode: Opcode, lhs: Value, rhs: Value) -> Value {
1233 let args = func.push_values(&[lhs, rhs]);
1234 written(func, inst, InstData { args, ..InstData::new(opcode) }, Type::I1)
1235}
1236
1237fn ahead(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) -> Value {
1239 let args = func.push_values(args);
1240 written(func, inst, InstData { args, ..InstData::new(opcode) }, half())
1241}
1242
1243fn ahead_const(func: &mut Func, inst: Inst, value: i128) -> Value {
1245 let extra = Extra::Imm(func.add_imm(Imm::int(value, half())));
1246 written(func, inst, InstData { extra, ..InstData::new(Opcode::IConst) }, half())
1247}
1248
1249fn written(func: &mut Func, inst: Inst, data: InstData, ty: Type) -> Value {
1251 let span = func.span(inst);
1252 let made = func.create_inst(data, &[ty], span);
1253 func.insert_before(made, inst);
1254 func[made].first_result.expect("an instruction created with one result has one")
1255}
1256
1257fn becomes(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) {
1259 let args = func.push_values(args);
1260 let data = &mut func[inst];
1261 data.opcode = opcode;
1262 data.args = args;
1263 data.extra = Extra::None;
1264 data.flags = data.flags.intersection(Flags::legal_on(opcode));
1265}
1266
1267#[cfg(test)]
1268mod tests {
1269 use rucc_base::Interner;
1270 use rucc_ir::{
1271 Block, Builder, Flags, Float, Func, MemOrder, Module, Restrict, Signature, Type, Value,
1272 };
1273 use rucc_target::x86_64::{MINGW64, SYSV};
1274 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1275
1276 use super::{HALF, IntPred, MemInfo, Opcode, halves};
1277
1278 fn wide() -> Type {
1280 Type::int(super::WIDE)
1281 }
1282
1283 fn target() -> TargetInfo {
1284 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1285 }
1286
1287 fn printed(func: &Func, names: &mut Interner) -> String {
1288 let module = Module::new(names.intern("w.c"), &target());
1289 rucc_ir::print_func(&module, func, names)
1290 }
1291
1292 fn shell(names: &mut Interner, params: &[Type], returns: &[Type]) -> (Func, Block, Vec<Value>) {
1294 let signature = Signature::new().with_params(params).with_returns(returns);
1295 let mut func = Func::new(names.intern("f"), signature);
1296 let entry = func.create_block();
1297 let values = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
1298 (func, entry, values)
1299 }
1300
1301 fn info(size: u64, align: u32) -> MemInfo {
1303 MemInfo {
1304 size,
1305 align,
1306 order: MemOrder::NotAtomic,
1307 tbaa: None,
1308 owns: 0,
1309 restrict: Restrict::NONE,
1310 }
1311 }
1312
1313 #[test]
1314 fn an_add_carries_from_the_low_half_into_the_high_one() {
1315 let mut names = Interner::new();
1316 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1317 let mut build = Builder::new(&mut func, entry);
1318 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1319 build.ret(&[sum]);
1320
1321 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1322 let text = printed(&func, &mut names);
1323 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1324 assert_eq!(text.matches(" = add ").count(), 3, "three adds: {text}");
1327 assert_eq!(text.matches("icmp ult").count(), 1, "one carry: {text}");
1328 assert_eq!(text.matches(" = zext.i64 ").count(), 1, "the carry as a number: {text}");
1329 }
1330
1331 #[test]
1332 fn a_subtract_borrows_the_other_way_round() {
1333 let mut names = Interner::new();
1334 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1335 let mut build = Builder::new(&mut func, entry);
1336 let difference = build.binary(Opcode::Sub, params[0], params[1], Flags::NONE);
1337 build.ret(&[difference]);
1338
1339 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1340 let text = printed(&func, &mut names);
1341 assert_eq!(text.matches(" = sub ").count(), 3, "three subtracts: {text}");
1342 assert!(text.contains("icmp ult %0, %2"), "the operands are compared: {text}");
1345 }
1346
1347 #[test]
1348 fn the_signature_and_the_entry_block_say_the_same_thing() {
1349 let mut names = Interner::new();
1350 let (mut func, entry, params) = shell(&mut names, &[Type::int(32), wide()], &[wide()]);
1351 let mut build = Builder::new(&mut func, entry);
1352 build.ret(&[params[1]]);
1353
1354 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1355 assert_eq!(
1356 func.signature().param_types().collect::<Vec<_>>(),
1357 [Type::int(32), Type::int(HALF), Type::int(HALF)],
1358 "the wide parameter became two where it stood"
1359 );
1360 assert_eq!(
1361 func.signature().return_types().collect::<Vec<_>>(),
1362 [Type::int(HALF), Type::int(HALF)],
1363 "and so did what comes back"
1364 );
1365 let text = printed(&func, &mut names);
1366 assert!(text.contains("block0(%0: i32, %1: i64, %2: i64)"), "the block agrees: {text}");
1367 assert!(text.contains("return %1, %2"), "both halves go back: {text}");
1368 let _ = entry;
1369 }
1370
1371 #[test]
1372 fn a_read_takes_the_high_word_a_word_above_the_low_one() {
1373 let mut names = Interner::new();
1374 let (mut func, entry, params) = shell(&mut names, &[Type::PTR], &[wide()]);
1375 let mut build = Builder::new(&mut func, entry);
1376 let value = build.load(wide(), params[0], info(16, 16), Flags::NONE);
1377 build.ret(&[value]);
1378
1379 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1380 let text = printed(&func, &mut names);
1381 assert_eq!(text.matches(" = load.i64 ").count(), 2, "two reads: {text}");
1382 assert!(text.contains("ptr_add"), "the high word is a word up: {text}");
1383 assert!(text.contains("align 16"), "the low word keeps what the object had: {text}");
1386 assert!(text.contains("align 8"), "the high word knows less: {text}");
1387 }
1388
1389 #[test]
1390 fn an_equality_asks_once_about_both_halves() {
1391 let mut names = Interner::new();
1392 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1393 let mut build = Builder::new(&mut func, entry);
1394 let same = build.icmp(IntPred::Eq, params[0], params[1]);
1395 let answer = build.unary(Opcode::ZExt, same, Type::int(32));
1396 build.ret(&[answer]);
1397
1398 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1399 let text = printed(&func, &mut names);
1400 assert_eq!(text.matches("icmp").count(), 1, "one comparison: {text}");
1401 assert_eq!(text.matches(" = xor ").count(), 2, "the halves differ or they do not: {text}");
1402 }
1403
1404 #[test]
1405 fn an_ordering_reads_the_low_halves_without_a_sign() {
1406 let mut names = Interner::new();
1407 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1408 let mut build = Builder::new(&mut func, entry);
1409 let below = build.icmp(IntPred::Slt, params[0], params[1]);
1410 let answer = build.unary(Opcode::ZExt, below, Type::int(32));
1411 build.ret(&[answer]);
1412
1413 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1414 let text = printed(&func, &mut names);
1415 assert!(text.contains("icmp slt"), "the high halves keep the sign: {text}");
1416 assert!(text.contains("icmp ult"), "the low halves have none: {text}");
1417 assert!(
1418 text.contains("icmp eq"),
1419 "and the low halves only matter when the high tie: {text}"
1420 );
1421 }
1422
1423 #[test]
1430 fn an_ordering_that_allows_equality_asks_the_high_halves_a_strict_question() {
1431 let mut names = Interner::new();
1432 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1433 let mut build = Builder::new(&mut func, entry);
1434 let at_least = build.icmp(IntPred::Sge, params[0], params[1]);
1435 let answer = build.unary(Opcode::ZExt, at_least, Type::int(32));
1436 build.ret(&[answer]);
1437
1438 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1439 let text = printed(&func, &mut names);
1440 assert!(text.contains("icmp sgt"), "the high halves settle it outright: {text}");
1441 assert!(!text.contains("icmp sge"), "a tie in the high halves settles nothing: {text}");
1442 assert!(text.contains("icmp uge"), "the low halves are the ones allowed to tie: {text}");
1443 }
1444
1445 #[test]
1446 fn a_widening_puts_the_sign_of_the_value_in_the_high_half() {
1447 let mut names = Interner::new();
1448 let (mut func, entry, params) = shell(&mut names, &[Type::int(32)], &[wide()]);
1449 let mut build = Builder::new(&mut func, entry);
1450 let value = build.unary(Opcode::SExt, params[0], wide());
1451 build.ret(&[value]);
1452
1453 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1454 let text = printed(&func, &mut names);
1455 assert!(text.contains("sext.i64"), "the value fills the low half: {text}");
1456 assert!(text.contains("ashr"), "and its sign fills the high one: {text}");
1457 }
1458
1459 #[test]
1460 fn a_block_parameter_becomes_two_and_every_branch_passes_two() {
1461 let mut names = Interner::new();
1462 let (mut func, entry, params) = shell(&mut names, &[wide(), Type::int(32)], &[wide()]);
1463 let tail = func.create_block();
1464 let carried = func.append_param(tail, wide());
1465 let mut build = Builder::new(&mut func, entry);
1466 let zero = build.iconst(Type::int(32), 0);
1467 let taken = build.icmp(IntPred::Ne, params[1], zero);
1468 let other = build.iconst(wide(), 7);
1469 build.br_if(taken, tail, &[params[0]], tail, &[other]);
1470 let mut build = Builder::new(&mut func, tail);
1471 build.ret(&[carried]);
1472
1473 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1474 let text = printed(&func, &mut names);
1475 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1476 assert!(text.contains("block1(%7: i64, %8: i64)"), "the block takes two: {text}");
1477 assert_eq!(text.matches("block1(").count(), 3, "and both edges pass two: {text}");
1478 }
1479
1480 #[test]
1487 fn a_multiply_is_three_multiplies_and_the_carry_out_of_the_low_ones() {
1488 let mut names = Interner::new();
1489 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1490 let mut build = Builder::new(&mut func, entry);
1491 let product = build.binary(Opcode::Mul, params[0], params[1], Flags::NONE);
1492 build.ret(&[product]);
1493
1494 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1495 let text = printed(&func, &mut names);
1496 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1497 assert_eq!(text.matches(" = mul ").count(), 7, "three and the carry's four: {text}");
1498 }
1499
1500 #[test]
1507 fn each_of_the_four_divisions_calls_the_routine_of_that_name() {
1508 for (opcode, routine) in [
1509 (Opcode::UDiv, "__udivti3"),
1510 (Opcode::SDiv, "__divti3"),
1511 (Opcode::URem, "__umodti3"),
1512 (Opcode::SRem, "__modti3"),
1513 ] {
1514 let mut names = Interner::new();
1515 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1516 let mut build = Builder::new(&mut func, entry);
1517 let answer = build.binary(opcode, params[0], params[1], Flags::NONE);
1518 build.ret(&[answer]);
1519
1520 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1521 let text = printed(&func, &mut names);
1522 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1523 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1524 }
1525 }
1526
1527 #[test]
1533 fn a_divide_hands_over_four_halves_and_takes_two_back() {
1534 let mut names = Interner::new();
1535 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1536 let mut build = Builder::new(&mut func, entry);
1537 let quotient = build.binary(Opcode::UDiv, params[0], params[1], Flags::NONE);
1538 build.ret(&[quotient]);
1539
1540 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1541 let text = printed(&func, &mut names);
1542 assert!(text.contains("@__udivti3(%0, %1, %2, %3)"), "four halves go over: {text}");
1543 assert!(text.contains("return %4, %5"), "and two come back: {text}");
1544 }
1545
1546 #[test]
1552 fn a_divide_of_something_computed_calls_with_the_halves_of_it() {
1553 let mut names = Interner::new();
1554 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1555 let mut build = Builder::new(&mut func, entry);
1556 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1557 let quotient = build.binary(Opcode::SDiv, sum, params[1], Flags::NONE);
1558 build.ret(&[quotient]);
1559
1560 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1561 let text = printed(&func, &mut names);
1562 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1563 assert_eq!(text.matches(" = add ").count(), 3, "the sum is still a sum: {text}");
1564 assert_eq!(text.matches("call @__divti3").count(), 1, "one call: {text}");
1565 }
1566
1567 #[test]
1573 fn each_conversion_between_this_width_and_a_float_calls_the_routine_of_that_name() {
1574 let double = Type::float(Float::F64);
1575 let single = Type::float(Float::F32);
1576 let quad = Type::float(Float::F128);
1577 for (opcode, float, routine) in [
1578 (Opcode::SIToFP, double, "__floattidf"),
1579 (Opcode::SIToFP, single, "__floattisf"),
1580 (Opcode::UIToFP, double, "__floatuntidf"),
1581 (Opcode::UIToFP, single, "__floatuntisf"),
1582 (Opcode::SIToFP, quad, "__floattitf"),
1583 (Opcode::UIToFP, quad, "__floatuntitf"),
1584 ] {
1585 let mut names = Interner::new();
1586 let (mut func, entry, params) = shell(&mut names, &[wide()], &[float]);
1587 let mut build = Builder::new(&mut func, entry);
1588 let answer = build.unary(opcode, params[0], float);
1589 build.ret(&[answer]);
1590
1591 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1592 let text = printed(&func, &mut names);
1593 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1594 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1595 }
1596 for (opcode, float, routine) in [
1597 (Opcode::FPToSI, double, "__fixdfti"),
1598 (Opcode::FPToSI, single, "__fixsfti"),
1599 (Opcode::FPToUI, double, "__fixunsdfti"),
1600 (Opcode::FPToUI, single, "__fixunssfti"),
1601 (Opcode::FPToSI, quad, "__fixtfti"),
1602 (Opcode::FPToUI, quad, "__fixunstfti"),
1603 ] {
1604 let mut names = Interner::new();
1605 let (mut func, entry, params) = shell(&mut names, &[float], &[wide()]);
1606 let mut build = Builder::new(&mut func, entry);
1607 let answer = build.unary(opcode, params[0], wide());
1608 build.ret(&[answer]);
1609
1610 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1611 let text = printed(&func, &mut names);
1612 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1613 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1614 }
1615 }
1616
1617 #[test]
1623 fn a_conversion_hands_over_halves_one_way_and_takes_them_back_the_other() {
1624 let double = Type::float(Float::F64);
1625 let mut names = Interner::new();
1626 let (mut func, entry, params) = shell(&mut names, &[wide()], &[double]);
1627 let mut build = Builder::new(&mut func, entry);
1628 let answer = build.unary(Opcode::SIToFP, params[0], double);
1629 build.ret(&[answer]);
1630
1631 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1632 let text = printed(&func, &mut names);
1633 assert!(text.contains("@__floattidf(%0, %1)"), "two halves go over: {text}");
1634 assert!(text.contains("return %2"), "and one float comes back: {text}");
1635
1636 let mut names = Interner::new();
1637 let (mut func, entry, params) = shell(&mut names, &[double], &[wide()]);
1638 let mut build = Builder::new(&mut func, entry);
1639 let answer = build.unary(Opcode::FPToSI, params[0], wide());
1640 build.ret(&[answer]);
1641
1642 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1643 let text = printed(&func, &mut names);
1644 assert!(text.contains("@__fixdfti(%0)"), "the float goes over as it is: {text}");
1645 assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1646 }
1647
1648 #[test]
1655 fn a_conversion_against_a_quad_hands_over_the_pair_and_the_quad_whole() {
1656 let quad = Type::float(Float::F128);
1657 let mut names = Interner::new();
1658 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
1659 let mut build = Builder::new(&mut func, entry);
1660 let answer = build.unary(Opcode::UIToFP, params[0], quad);
1661 build.ret(&[answer]);
1662
1663 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1664 let text = printed(&func, &mut names);
1665 assert!(text.contains("@__floatuntitf(%0, %1)"), "two halves go over: {text}");
1666 assert!(text.contains("return %2"), "and one quad comes back: {text}");
1667
1668 let mut names = Interner::new();
1669 let (mut func, entry, params) = shell(&mut names, &[quad], &[wide()]);
1670 let mut build = Builder::new(&mut func, entry);
1671 let answer = build.unary(Opcode::FPToSI, params[0], wide());
1672 build.ret(&[answer]);
1673
1674 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1675 let text = printed(&func, &mut names);
1676 assert!(text.contains("@__fixtfti(%0)"), "the quad goes over as it is: {text}");
1677 assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1678 }
1679
1680 #[test]
1687 fn a_conversion_at_a_width_the_runtime_has_no_routine_for_is_left_alone() {
1688 let long = Type::float(Float::F80);
1689 let mut names = Interner::new();
1690 let (mut func, entry, params) = shell(&mut names, &[wide()], &[long]);
1691 let mut build = Builder::new(&mut func, entry);
1692 let answer = build.unary(Opcode::SIToFP, params[0], long);
1693 build.ret(&[answer]);
1694
1695 assert!(!halves(&mut func, &mut names, &SYSV), "the pass does not understand this one");
1696 let text = printed(&func, &mut names);
1697 assert!(text.contains("i128"), "the width is still there: {text}");
1698 }
1699
1700 #[test]
1707 fn a_shift_left_chooses_between_a_count_that_crossed_a_half_and_one_that_did_not() {
1708 let mut names = Interner::new();
1709 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1710 let mut build = Builder::new(&mut func, entry);
1711 let moved = build.binary(Opcode::Shl, params[0], params[1], Flags::NONE);
1712 build.ret(&[moved]);
1713
1714 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1715 let text = printed(&func, &mut names);
1716 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1717 assert_eq!(
1718 text.matches(" = shl ").count(),
1719 2,
1720 "one per half, and the far case reuses one: {text}"
1721 );
1722 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1723 assert_eq!(text.matches(" = lshr ").count(), 2, "the crossing bits, in two steps: {text}");
1724 }
1725
1726 #[test]
1733 fn the_bits_that_cross_move_one_place_and_then_the_rest_of_the_way() {
1734 let mut names = Interner::new();
1735 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1736 let mut build = Builder::new(&mut func, entry);
1737 let moved = build.binary(Opcode::LShr, params[0], params[1], Flags::NONE);
1738 build.ret(&[moved]);
1739
1740 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1741 let text = printed(&func, &mut names);
1742 assert!(text.contains("iconst.i64 63"), "sixty three is the distance left: {text}");
1743 assert!(text.contains("iconst.i64 1"), "after the one place that comes first: {text}");
1744 assert!(text.contains(" = sub "), "the rest of the way is worked out: {text}");
1745 assert!(
1746 !text.contains("iconst.i64 127"),
1747 "and the count is not masked to the width: {text}"
1748 );
1749 }
1750
1751 #[test]
1757 fn an_arithmetic_shift_right_leaves_the_sign_bit_where_a_logical_one_leaves_zeroes() {
1758 let mut names = Interner::new();
1759 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1760 let mut build = Builder::new(&mut func, entry);
1761 let moved = build.binary(Opcode::AShr, params[0], params[1], Flags::NONE);
1762 build.ret(&[moved]);
1763
1764 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1765 let text = printed(&func, &mut names);
1766 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1767 assert_eq!(text.matches(" = ashr ").count(), 2, "the count and the sign: {text}");
1769 assert_eq!(text.matches(" = lshr ").count(), 1, "the low half is not signed: {text}");
1770 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1771 }
1772
1773 #[test]
1774 fn a_parameter_with_one_register_left_leaves_the_function_alone() {
1775 let mut names = Interner::new();
1776 let word = Type::int(HALF);
1777 let params = [word, word, word, word, word, wide()];
1781 let (mut func, entry, values) = shell(&mut names, ¶ms, &[word]);
1782 let mut build = Builder::new(&mut func, entry);
1783 let low = build.unary(Opcode::Trunc, values[5], word);
1784 build.ret(&[low]);
1785 let before = printed(&func, &mut names);
1786
1787 assert!(!halves(&mut func, &mut names, &SYSV), "one of the halves has no register");
1788 assert_eq!(printed(&func, &mut names), before, "so nothing moved");
1789 }
1790
1791 #[test]
1800 fn a_block_made_after_the_one_it_runs_before_is_still_split() {
1801 let mut names = Interner::new();
1802 let (mut func, entry, params) = shell(&mut names, &[wide()], &[wide()]);
1803 let tail = func.create_block();
1804 let middle = func.create_block();
1805 let mut build = Builder::new(&mut func, entry);
1806 build.jump(middle, &[]);
1807 let mut build = Builder::new(&mut func, middle);
1808 let doubled = build.binary(Opcode::Add, params[0], params[0], Flags::NONE);
1809 build.jump(tail, &[]);
1810 let mut build = Builder::new(&mut func, tail);
1811 let again = build.binary(Opcode::Add, doubled, doubled, Flags::NONE);
1812 build.ret(&[again]);
1813
1814 assert!(
1815 halves(&mut func, &mut names, &SYSV),
1816 "the definition runs before the use whatever the list says"
1817 );
1818 let text = printed(&func, &mut names);
1819 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1820 }
1821
1822 #[test]
1823 fn a_function_with_nothing_that_wide_is_not_touched() {
1824 let mut names = Interner::new();
1825 let word = Type::int(HALF);
1826 let (mut func, entry, params) = shell(&mut names, &[word, word], &[word]);
1827 let mut build = Builder::new(&mut func, entry);
1828 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1829 build.ret(&[sum]);
1830
1831 assert!(!halves(&mut func, &mut names, &SYSV), "there is nothing to split");
1832 }
1833
1834 #[test]
1841 fn on_windows_a_wide_operand_goes_over_as_the_address_of_a_copy() {
1842 let mut names = Interner::new();
1843 let quad = Type::float(Float::F64);
1844 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
1845 let mut build = Builder::new(&mut func, entry);
1846 let answer = build.unary(Opcode::SIToFP, params[0], quad);
1847 build.ret(&[answer]);
1848
1849 assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
1850 let text = printed(&func, &mut names);
1851 assert!(text.contains("call @__floattidf"), "{text}");
1852 assert_eq!(text.matches("alloca").count(), 1, "one slot: {text}");
1854 assert_eq!(text.matches("store").count(), 2, "a half at a time: {text}");
1855 assert_eq!(text.matches("ptr_add").count(), 1, "the high half eight bytes up: {text}");
1856 assert!(!text.contains("__floattidf(%0, %1)"), "not the two halves: {text}");
1857 }
1858
1859 #[test]
1861 fn on_windows_a_wide_answer_at_this_format_comes_back_through_a_slot() {
1862 let mut names = Interner::new();
1863 let quad = Type::float(Float::F128);
1864 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
1865 let mut build = Builder::new(&mut func, entry);
1866 let answer = build.unary(Opcode::SIToFP, params[0], quad);
1867 build.ret(&[answer]);
1868
1869 assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
1870 let text = printed(&func, &mut names);
1871 assert!(text.contains("call @__floattitf"), "{text}");
1872 assert_eq!(text.matches("alloca").count(), 2, "two slots: {text}");
1874 assert_eq!(text.matches(" = call").count(), 0, "the call answers nothing: {text}");
1875 assert_eq!(text.matches(" = load").count(), 1, "the answer is the load after it: {text}");
1876 }
1877
1878 #[test]
1880 fn the_convention_with_registers_for_both_halves_puts_nothing_on_the_frame() {
1881 let mut names = Interner::new();
1882 let double = Type::float(Float::F64);
1883 let (mut func, entry, params) = shell(&mut names, &[wide()], &[double]);
1884 let mut build = Builder::new(&mut func, entry);
1885 let answer = build.unary(Opcode::SIToFP, params[0], double);
1886 build.ret(&[answer]);
1887
1888 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1889 let text = printed(&func, &mut names);
1890 assert!(text.contains("@__floattidf(%0, %1)"), "both halves in registers: {text}");
1891 assert!(!text.contains("alloca"), "nothing goes through the frame: {text}");
1892 }
1893
1894 #[test]
1897 fn on_windows_a_wide_answer_comes_back_in_one_register_and_is_split_on_the_frame() {
1898 let mut names = Interner::new();
1899 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1900 let mut build = Builder::new(&mut func, entry);
1901 let answer = build.binary(Opcode::SDiv, params[0], params[1], Flags::NONE);
1902 build.ret(&[answer]);
1903
1904 assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
1905 let text = printed(&func, &mut names);
1906 assert!(text.contains("call @__divti3(%4, %7) : (ptr, ptr) -> f128"), "{text}");
1909 assert_eq!(text.matches(" = call").count(), 1, "one answer: {text}");
1910 assert_eq!(text.matches("alloca").count(), 3, "three slots: {text}");
1912 assert_eq!(text.matches(" = load").count(), 2, "the two halves: {text}");
1913 }
1914
1915 #[test]
1917 fn the_convention_with_registers_for_the_answer_reads_both_halves_out_of_them() {
1918 let mut names = Interner::new();
1919 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1920 let mut build = Builder::new(&mut func, entry);
1921 let answer = build.binary(Opcode::SDiv, params[0], params[1], Flags::NONE);
1922 build.ret(&[answer]);
1923
1924 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1925 let text = printed(&func, &mut names);
1926 assert!(text.contains("@__divti3(%0, %1, %2, %3)"), "four halves over: {text}");
1927 assert!(!text.contains("alloca"), "nothing goes through the frame: {text}");
1928 }
1929}