1use std::collections::{HashMap, HashSet};
91
92use rucc_base::Interner;
93use rucc_ir::{
94 Abi, Block, BlockCall, CallInfo, Def, Extra, Flags, Float, Func, Imm, Inst, InstData, IntPred,
95 MemInfo, MemOrder, Opcode, Param, Restrict, Signature, Type, Value,
96};
97use rucc_target::{AbiDescription, CallRegs, Places, Where};
98
99use crate::capability;
100use crate::expand;
101
102const WIDE: u32 = 128;
104
105const MODE: &str = "i128";
107
108const HALF: u32 = 64;
110
111const STEP: u64 = 8;
113
114fn is_wide(ty: Type) -> bool {
116 ty.is_int() && ty.is_scalar() && ty.bits() == WIDE
117}
118
119fn half() -> Type {
121 Type::int(HALF)
122}
123
124pub fn halves(func: &mut Func, names: &mut Interner, conv: &CallRegs) -> bool {
135 if !func.values().any(|value| is_wide(func[value].ty)) {
136 return false;
137 }
138 let insts: Vec<Inst> =
139 walk(func).into_iter().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
140 let order: HashMap<Inst, usize> =
141 insts.iter().enumerate().map(|(at, &inst)| (inst, at)).collect();
142 if !insts.iter().enumerate().all(|(at, &inst)| can_split(func, conv, &order, at, inst)) {
143 return false;
144 }
145 let Some(arriving) = plan(func.signature(), conv) else { return false };
146 if !func.signatures().all(|signature| plan(signature, conv).is_some()) {
147 return false;
148 }
149
150 let mut halves: Halves = HashMap::new();
151 let mut forward: HashMap<Value, Value> = HashMap::new();
152 let entry = func.entry();
153 for block in func.blocks().collect::<Vec<_>>() {
154 if Some(block) == entry {
155 arrive(func, block, &arriving, &mut halves, &mut forward);
156 } else {
157 params(func, block, &mut halves, &mut forward);
158 }
159 }
160 for &inst in &insts {
161 rewrite(func, names, conv, &mut halves, &mut forward, inst);
162 }
163 substitute(func, &forward);
164 let signature = planned(func.signature(), &arriving);
165 func.set_signature(signature);
166 true
167}
168
169fn walk(func: &Func) -> Vec<Block> {
187 let Some(entry) = func.entry() else { return func.blocks().collect() };
188 let mut seen: HashSet<Block> = HashSet::new();
189 let mut order: Vec<Block> = Vec::new();
190 let mut stack: Vec<(Block, bool)> = vec![(entry, false)];
193 seen.insert(entry);
194 while let Some((block, done)) = stack.pop() {
195 if done {
196 order.push(block);
197 continue;
198 }
199 stack.push((block, true));
200 let Some(term) = func.terminator(block) else { continue };
201 for call in func.successors(term) {
202 if seen.insert(call.block) {
203 stack.push((call.block, false));
204 }
205 }
206 }
207 order.reverse();
208 order.extend(func.blocks().filter(|block| !seen.contains(block)));
209 order
210}
211
212type Halves = HashMap<Value, (Value, Value)>;
214
215fn understood(opcode: Opcode) -> bool {
226 matches!(
227 opcode,
228 Opcode::IConst
229 | Opcode::Load
230 | Opcode::Store
231 | Opcode::Add
232 | Opcode::Sub
233 | Opcode::Mul
234 | Opcode::UDiv
235 | Opcode::SDiv
236 | Opcode::URem
237 | Opcode::SRem
238 | Opcode::Shl
239 | Opcode::LShr
240 | Opcode::AShr
241 | Opcode::And
242 | Opcode::Or
243 | Opcode::Xor
244 | Opcode::ICmp
245 | Opcode::Select
246 | Opcode::SIToFP
247 | Opcode::UIToFP
248 | Opcode::FPToSI
249 | Opcode::FPToUI
250 | Opcode::Trunc
251 | Opcode::SExt
252 | Opcode::ZExt
253 | Opcode::Call
254 | Opcode::CallIndirect
255 | Opcode::Return
256 | Opcode::Jump
257 | Opcode::BrIf
258 )
259}
260
261fn can_split(
266 func: &Func,
267 conv: &CallRegs,
268 order: &HashMap<Inst, usize>,
269 at: usize,
270 inst: Inst,
271) -> bool {
272 let data = func[inst];
273 let reads = operands(func, inst);
274 let wide = |&value: &Value| is_wide(func[value].ty);
275 if !reads.iter().any(wide) && !data.results().any(|value| is_wide(func[value].ty)) {
276 return true;
277 }
278 if !understood(data.opcode) {
279 return false;
280 }
281 if func.carries_mem(inst) {
285 return false;
286 }
287 if data.opcode == Opcode::SExt && reads.iter().any(|&value| func[value].ty.bits() < 8) {
291 return false;
292 }
293 if matches!(data.opcode, Opcode::SIToFP | Opcode::UIToFP | Opcode::FPToSI | Opcode::FPToUI)
298 && converted(func, inst).is_none()
299 {
300 return false;
301 }
302 if matches!(data.opcode, Opcode::Call | Opcode::CallIndirect) {
309 let Some((site, variadic)) = site(func, inst) else { return false };
310 if variadic && (conv.shared_positions || plan(&site, conv).is_none()) {
311 return false;
312 }
313 }
314 reads.iter().filter(|value| wide(value)).all(|&value| match func[value].def {
318 Def::Result { inst, .. } => order.get(&inst).is_some_and(|&def| def < at),
319 Def::Param { .. } => true,
320 })
321}
322
323fn converted(func: &Func, inst: Inst) -> Option<Float> {
330 let data = func[inst];
331 let mut floats = func[data.args]
332 .iter()
333 .copied()
334 .chain(data.results())
335 .map(|value| func[value].ty)
336 .filter(|ty| ty.is_float());
337 let only = floats.next()?;
338 if floats.next().is_some() {
339 return None;
340 }
341 match only.format() {
342 Some(format @ (Float::F32 | Float::F64 | Float::F128)) => Some(format),
343 _ => None,
344 }
345}
346
347fn operands(func: &Func, inst: Inst) -> Vec<Value> {
353 let mut reads = func[func[inst].args].to_vec();
354 for call in func.successors(inst).collect::<Vec<_>>() {
355 reads.extend_from_slice(&func[call.args]);
356 }
357 reads
358}
359
360#[derive(Debug, Clone, Copy, PartialEq, Eq)]
362enum Slot {
363 Whole(usize),
365 Low(usize),
367 High(usize),
369 Filler(Type),
371}
372
373fn plan(signature: &Signature, conv: &CallRegs) -> Option<Vec<Slot>> {
399 let word = Param::new(half());
400 let mut places = Places::new(conv);
401 let mut meant: Vec<(Slot, Param, Where)> = Vec::new();
402 let mut moved = false;
403 for (index, ¶m) in signature.params.iter().enumerate() {
404 if !is_wide(param.ty) {
405 meant.push((Slot::Whole(index), param, place(&mut places, param)));
406 continue;
407 }
408 let mut ahead = places.clone();
409 if let (low @ Where::Reg(_), high @ Where::Reg(_)) = (ahead.integer(), ahead.integer()) {
410 places = ahead;
411 meant.push((Slot::Low(index), word, low));
412 meant.push((Slot::High(index), word, high));
413 continue;
414 }
415 let Where::Stack(at) = places.on_stack(WIDE / 8, WIDE / 8) else { return None };
416 meant.push((Slot::Low(index), word, Where::Stack(at)));
417 meant.push((Slot::High(index), word, Where::Stack(at + HALF / 8)));
418 moved = true;
419 }
420 if !moved {
421 return Some(meant.into_iter().map(|(slot, _, _)| slot).collect());
422 }
423 if signature.variadic || conv.shared_positions {
424 return None;
425 }
426
427 let in_reg = |at: &Where| matches!(at, Where::Reg(_));
428 let mut stacked: Vec<&(Slot, Param, Where)> =
429 meant.iter().filter(|(_, _, at)| !in_reg(at)).collect();
430 stacked.sort_by_key(|(_, _, at)| match at {
431 Where::Stack(up) => *up,
432 Where::Reg(_) => 0,
433 });
434 let mut order: Vec<(Slot, Param, Option<Where>)> = Vec::new();
435 for float in [false, true] {
436 let kind = |param: &Param| scalar(*param) && param.ty.is_float() == float;
437 order.extend(
438 meant
439 .iter()
440 .filter(|(_, param, at)| kind(param) && in_reg(at))
441 .map(|&(slot, param, at)| (slot, param, Some(at))),
442 );
443 let (count, filler) = match float {
444 false => (conv.int_args.len(), word),
445 true => (conv.sse_args.len(), Param::new(Type::float(Float::F64))),
446 };
447 if stacked.iter().any(|(_, param, _)| kind(param)) {
448 let took = order.iter().filter(|(_, param, _)| kind(param)).count();
449 let padding = count.saturating_sub(took);
450 order.extend((0..padding).map(|_| (Slot::Filler(filler.ty), filler, None)));
451 }
452 }
453
454 let mut places = Places::new(conv);
455 let mut slots = Vec::with_capacity(order.len() + stacked.len());
456 for (slot, param, meant) in order {
457 let at = place(&mut places, param);
458 if !in_reg(&at) || meant.is_some_and(|meant| meant != at) {
459 return None;
460 }
461 slots.push(slot);
462 }
463 for &&(slot, param, meant) in &stacked {
464 let Where::Stack(up) = meant else { return None };
465 while places.size() < up {
466 if in_reg(&place(&mut places, word)) {
467 return None;
468 }
469 slots.push(Slot::Filler(word.ty));
470 }
471 if place(&mut places, param) != meant {
472 return None;
473 }
474 slots.push(slot);
475 }
476 Some(slots)
477}
478
479fn place(places: &mut Places<'_>, param: Param) -> Where {
481 if let Abi::ByVal { size, align } = param.abi {
485 places.on_stack(u32::try_from(size).unwrap_or(u32::MAX), align)
486 } else if crate::abi::on_the_stack(param.ty) {
487 let (size, align) = crate::abi::X87_AREA;
488 places.on_stack(size, align)
489 } else if param.ty.is_float() {
490 places.float(crate::abi::float_bytes(param.ty))
491 } else {
492 places.integer()
493 }
494}
495
496fn scalar(param: Param) -> bool {
498 !matches!(param.abi, Abi::ByVal { .. }) && !crate::abi::on_the_stack(param.ty)
499}
500
501fn planned(signature: &Signature, slots: &[Slot]) -> Signature {
503 let params = slots
504 .iter()
505 .map(|&slot| match slot {
506 Slot::Whole(index) => signature.params[index],
507 Slot::Low(_) | Slot::High(_) => Param::new(half()),
508 Slot::Filler(ty) => Param::new(ty),
509 })
510 .collect();
511 Signature { params, ..split_signature(signature) }
512}
513
514fn params(func: &mut Func, block: Block, halves: &mut Halves, forward: &mut HashMap<Value, Value>) {
521 let old: Vec<Value> = func[block].params.clone();
522 if !old.iter().any(|&value| is_wide(func[value].ty)) {
523 return;
524 }
525 for &value in &old {
526 if is_wide(func[value].ty) {
527 let low = func.append_param(block, half());
528 let high = func.append_param(block, half());
529 halves.insert(value, (low, high));
530 } else {
531 let again = func.append_param(block, func[value].ty);
532 forward.insert(value, again);
533 }
534 }
535 func.retain_params(block, |value| !old.contains(&value));
536}
537
538fn arrive(
540 func: &mut Func,
541 block: Block,
542 slots: &[Slot],
543 halves: &mut Halves,
544 forward: &mut HashMap<Value, Value>,
545) {
546 let old: Vec<Value> = func[block].params.clone();
547 if !old.iter().any(|&value| is_wide(func[value].ty)) {
548 return;
549 }
550 let mut lows = HashMap::new();
551 for &slot in slots {
552 match slot {
553 Slot::Whole(index) => {
554 let again = func.append_param(block, func[old[index]].ty);
555 forward.insert(old[index], again);
556 }
557 Slot::Low(index) => {
558 lows.insert(index, func.append_param(block, half()));
559 }
560 Slot::High(index) => {
561 let high = func.append_param(block, half());
562 halves.insert(old[index], (lows[&index], high));
563 }
564 Slot::Filler(ty) => {
565 func.append_param(block, ty);
566 }
567 }
568 }
569 func.retain_params(block, |value| !old.contains(&value));
570}
571
572fn rewrite(
574 func: &mut Func,
575 names: &mut Interner,
576 conv: &CallRegs,
577 halves: &mut Halves,
578 forward: &mut HashMap<Value, Value>,
579 inst: Inst,
580) {
581 let abi = conv.abi;
582 let data = func[inst];
583 let produces = data.results().any(|value| is_wide(func[value].ty));
584 let takes = func[data.args].iter().any(|&value| is_wide(func[value].ty));
585 match data.opcode {
586 Opcode::IConst if produces => constant(func, halves, inst),
587 Opcode::Load if produces => load(func, halves, inst),
588 Opcode::Store if takes => store(func, halves, inst),
589 Opcode::Add | Opcode::Sub if produces => carried(func, halves, inst, data.opcode),
590 Opcode::Mul if produces => multiply(func, halves, inst),
591 Opcode::UDiv | Opcode::SDiv | Opcode::URem | Opcode::SRem if produces => {
592 divide(func, names, abi, halves, inst, data.opcode);
593 }
594 Opcode::Shl | Opcode::LShr | Opcode::AShr if produces => {
595 shifted(func, halves, inst, data.opcode);
596 }
597 Opcode::And | Opcode::Or | Opcode::Xor if produces => {
598 bitwise(func, halves, inst, data.opcode);
599 }
600 Opcode::SIToFP | Opcode::UIToFP if takes => {
601 to_float(func, names, abi, halves, forward, inst, data.opcode == Opcode::SIToFP);
602 }
603 Opcode::FPToSI | Opcode::FPToUI if produces => {
604 from_float(func, names, abi, halves, inst, data.opcode == Opcode::FPToSI);
605 }
606 Opcode::ICmp if takes => compare(func, halves, forward, inst),
607 Opcode::Select if produces => choose(func, halves, inst),
608 Opcode::Trunc if takes => truncate(func, halves, forward, inst),
609 Opcode::SExt | Opcode::ZExt if produces => {
610 extend(func, halves, inst, data.opcode == Opcode::SExt);
611 }
612 Opcode::Call | Opcode::CallIndirect if produces || takes => {
613 call(func, conv, halves, forward, inst);
614 }
615 Opcode::Return if takes => flatten(func, halves, inst),
616 Opcode::Jump | Opcode::BrIf => edges(func, halves, inst),
617 _ => {}
618 }
619}
620
621fn constant(func: &mut Func, halves: &mut Halves, inst: Inst) {
623 let Extra::Imm(imm) = func[inst].extra else { return };
624 let bits = func[imm].unsigned();
625 #[expect(clippy::cast_possible_truncation, reason = "the halves are what this is taking")]
626 let (low, high) = (bits as u64, (bits >> HALF) as u64);
627 let low = ahead_const(func, inst, i128::from(low));
628 let high = ahead_const(func, inst, i128::from(high));
629 replace(func, halves, inst, low, high);
630}
631
632fn load(func: &mut Func, halves: &mut Halves, inst: Inst) {
638 let data = func[inst];
639 let Extra::Mem(mem) = data.extra else { return };
640 let info = func[mem];
641 let Some(&from) = func[data.args].first() else { return };
642 let low = read(func, inst, from, word(info, 0), data.flags);
643 let up = stepped(func, inst, from);
644 let high = read(func, inst, up, word(info, STEP), data.flags);
645 replace(func, halves, inst, low, high);
646}
647
648fn store(func: &mut Func, halves: &mut Halves, inst: Inst) {
650 let data = func[inst];
651 let Extra::Mem(mem) = data.extra else { return };
652 let info = func[mem];
653 let args = func[data.args].to_vec();
654 let [value, into] = args[..] else { return };
655 let Some(&(low, high)) = halves.get(&value) else { return };
656 write(func, inst, low, into, word(info, 0), data.flags);
657 let up = stepped(func, inst, into);
658 write(func, inst, high, up, word(info, STEP), data.flags);
659 func.remove_inst(inst);
660}
661
662fn carried(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
672 let args = func[func[inst].args].to_vec();
673 let [a, b] = args[..] else { return };
674 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
675 return;
676 };
677 let low = ahead(func, inst, opcode, &[a_low, b_low]);
678 let carried = if opcode == Opcode::Add {
679 compared(func, inst, IntPred::Ult, low, a_low)
680 } else {
681 compared(func, inst, IntPred::Ult, a_low, b_low)
682 };
683 let carry = ahead(func, inst, Opcode::ZExt, &[carried]);
684 let high = ahead(func, inst, opcode, &[a_high, b_high]);
685 let high = ahead(func, inst, opcode, &[high, carry]);
686 replace(func, halves, inst, low, high);
687}
688
689fn multiply(func: &mut Func, halves: &mut Halves, inst: Inst) {
709 let args = func[func[inst].args].to_vec();
710 let [a, b] = args[..] else { return };
711 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
712 return;
713 };
714 let low = ahead(func, inst, Opcode::Mul, &[a_low, b_low]);
715 let carried = expand::high_half(func, inst, a_low, b_low, false, half());
716 let cross = ahead(func, inst, Opcode::Mul, &[a_low, b_high]);
717 let other = ahead(func, inst, Opcode::Mul, &[a_high, b_low]);
718 let high = ahead(func, inst, Opcode::Add, &[carried, cross]);
719 let high = ahead(func, inst, Opcode::Add, &[high, other]);
720 replace(func, halves, inst, low, high);
721}
722
723fn divide(
741 func: &mut Func,
742 names: &mut Interner,
743 abi: &'static AbiDescription,
744 halves: &mut Halves,
745 inst: Inst,
746 opcode: Opcode,
747) {
748 let args = func[func[inst].args].to_vec();
749 let [a, b] = args[..] else { return };
750 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
751 return;
752 };
753 let Some(routine) = capability::libcall(opcode, MODE) else { return };
757 let args = [Operand::Split(a_low, a_high), Operand::Split(b_low, b_high)];
758 let made = runtime(func, names, abi, inst, routine, &args, &[half(), half()]);
759 let [low, high] = made[..] else { return };
760 replace(func, halves, inst, low, high);
761}
762
763fn to_float(
773 func: &mut Func,
774 names: &mut Interner,
775 abi: &'static AbiDescription,
776 halves: &Halves,
777 forward: &mut HashMap<Value, Value>,
778 inst: Inst,
779 signed: bool,
780) {
781 let Some(&arg) = func[func[inst].args].first() else { return };
782 let Some(&(low, high)) = halves.get(&arg) else { return };
783 let (Some(result), Some(format)) = (func[inst].first_result, converted(func, inst)) else {
784 return;
785 };
786 let routine = going_up(signed, format);
787 let args = [Operand::Split(low, high)];
788 let made = runtime(func, names, abi, inst, routine, &args, &[func[result].ty]);
789 if let [answer] = made[..] {
790 forward.insert(result, answer);
791 }
792 func.remove_inst(inst);
793}
794
795fn from_float(
805 func: &mut Func,
806 names: &mut Interner,
807 abi: &'static AbiDescription,
808 halves: &mut Halves,
809 inst: Inst,
810 signed: bool,
811) {
812 let Some(&arg) = func[func[inst].args].first() else { return };
813 let Some(format) = converted(func, inst) else { return };
814 let routine = coming_down(signed, format);
815 let args = [Operand::Whole(arg)];
816 let made = runtime(func, names, abi, inst, routine, &args, &[half(), half()]);
817 let [low, high] = made[..] else { return };
818 replace(func, halves, inst, low, high);
819}
820
821fn going_up(signed: bool, format: Float) -> &'static str {
827 let mode = match format {
828 Float::F32 => "i128.f32",
829 Float::F64 => "i128.f64",
830 _ => "i128.f128",
831 };
832 routine(if signed { Opcode::SIToFP } else { Opcode::UIToFP }, mode)
833}
834
835fn coming_down(signed: bool, format: Float) -> &'static str {
837 let mode = match format {
838 Float::F32 => "f32.i128",
839 Float::F64 => "f64.i128",
840 _ => "f128.i128",
841 };
842 routine(if signed { Opcode::FPToSI } else { Opcode::FPToUI }, mode)
843}
844
845fn routine(opcode: Opcode, mode: &str) -> &'static str {
850 capability::libcall(opcode, mode)
851 .unwrap_or_else(|| panic!("no routine for `{}` at `{mode}`", opcode.name()))
852}
853
854#[derive(Clone, Copy)]
860enum Operand {
861 Whole(Value),
863 Split(Value, Value),
865}
866
867fn runtime(
892 func: &mut Func,
893 names: &mut Interner,
894 abi: &'static AbiDescription,
895 inst: Inst,
896 routine: &str,
897 args: &[Operand],
898 results: &[Type],
899) -> Vec<Value> {
900 let mut params: Vec<Param> = Vec::new();
901 let mut values: Vec<Value> = Vec::new();
902 let mut answer = Answer::Registers;
903 match *results {
905 [ty] if abi.scalar_is_by_reference(bytes(ty)) => {
906 let size = bytes(ty);
907 let align = align(size);
908 let slot = room(func, inst, size, align);
909 params.push(Param::with_abi(Type::PTR, Abi::Sret { size, align }));
910 values.push(slot);
911 answer = Answer::Slot(slot, ty);
912 }
913 [low, high] if low == half() && high == half() => {
914 if let Some(format) = packed(abi) {
915 answer = Answer::Packed(format);
916 }
917 }
918 _ => {}
919 }
920 for &arg in args {
921 handed(func, abi, inst, arg, &mut params, &mut values);
922 }
923 let answers = match answer {
924 Answer::Registers => results.to_vec(),
925 Answer::Slot(..) => Vec::new(),
926 Answer::Packed(format) => vec![Type::float(format)],
927 };
928 let returns = answers.iter().map(|&ty| Param::new(ty)).collect();
929 let signature = func.add_signature(Signature { params, returns, variadic: false });
930 let callee = Some(names.intern(routine));
931 let varargs = func.push_abis(&[]);
932 let extra = Extra::Call(func.add_call(CallInfo { callee, signature, varargs }));
933 let pushed = func.push_values(&values);
934 let span = func.span(inst);
935 let data = InstData { args: pushed, extra, ..InstData::new(Opcode::Call) };
936 let made = func.create_inst(data, &answers, span);
937 func.insert_before(made, inst);
938 match answer {
939 Answer::Registers => func[made].results().collect(),
940 Answer::Slot(slot, ty) => {
941 let size = bytes(ty);
942 let info = whole(size, align(size));
943 let extra = Extra::Mem(func.add_mem(info));
944 let args = func.push_values(&[slot]);
945 let data = InstData { args, extra, ..InstData::new(Opcode::Load) };
946 vec![written(func, inst, data, ty)]
947 }
948 Answer::Packed(format) => unpacked(func, inst, made, format),
949 }
950}
951
952#[derive(Clone, Copy)]
954enum Answer {
955 Registers,
958 Slot(Value, Type),
961 Packed(Float),
963}
964
965fn packed(abi: &'static AbiDescription) -> Option<Float> {
972 let format = abi.wide_integer_returns_in(u64::from(WIDE / 8))?;
973 Float::from_bits(format.width())
974}
975
976fn unpacked(func: &mut Func, inst: Inst, call: Inst, format: Float) -> Vec<Value> {
984 let Some(value) = func[call].first_result else { return Vec::new() };
985 let size = bytes(Type::float(format));
986 let align = align(size);
987 let slot = room(func, inst, size, align);
988 let info = whole(size, align);
989 write(func, inst, value, slot, info, Flags::NONE);
990 let low = read(func, inst, slot, word(info, 0), Flags::NONE);
991 let up = stepped(func, inst, slot);
992 let high = read(func, inst, up, word(info, STEP), Flags::NONE);
993 vec![low, high]
994}
995
996fn handed(
998 func: &mut Func,
999 abi: &'static AbiDescription,
1000 inst: Inst,
1001 arg: Operand,
1002 params: &mut Vec<Param>,
1003 values: &mut Vec<Value>,
1004) {
1005 match arg {
1006 Operand::Whole(value) => {
1007 let ty = func[value].ty;
1008 let size = bytes(ty);
1009 if !abi.scalar_is_by_reference(size) {
1010 params.push(Param::new(ty));
1011 values.push(value);
1012 return;
1013 }
1014 let align = align(size);
1015 let slot = room(func, inst, size, align);
1016 write(func, inst, value, slot, whole(size, align), Flags::NONE);
1017 params.push(Param::new(Type::PTR));
1018 values.push(slot);
1019 }
1020 Operand::Split(low, high) => {
1021 let size = u64::from(WIDE / 8);
1022 if !abi.scalar_is_by_reference(size) {
1023 params.push(Param::new(half()));
1024 values.push(low);
1025 params.push(Param::new(half()));
1026 values.push(high);
1027 return;
1028 }
1029 let align = align(size);
1030 let slot = room(func, inst, size, align);
1031 let info = whole(size, align);
1032 write(func, inst, low, slot, word(info, 0), Flags::NONE);
1033 let up = stepped(func, inst, slot);
1034 write(func, inst, high, up, word(info, STEP), Flags::NONE);
1035 params.push(Param::new(Type::PTR));
1036 values.push(slot);
1037 }
1038 }
1039}
1040
1041fn bytes(ty: Type) -> u64 {
1043 u64::from(ty.bits().div_ceil(8))
1044}
1045
1046fn align(size: u64) -> u32 {
1048 u32::try_from(size).unwrap_or(u32::MAX)
1049}
1050
1051fn whole(size: u64, align: u32) -> MemInfo {
1053 MemInfo {
1054 size,
1055 align,
1056 order: MemOrder::NotAtomic,
1057 tbaa: None,
1058 owns: 0,
1059 restrict: Restrict::NONE,
1060 }
1061}
1062
1063fn room(func: &mut Func, inst: Inst, size: u64, align: u32) -> Value {
1065 let extra = Extra::Mem(func.add_mem(whole(size, align)));
1066 written(func, inst, InstData { extra, ..InstData::new(Opcode::Alloca) }, Type::PTR)
1067}
1068
1069fn shifted(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
1089 let args = func[func[inst].args].to_vec();
1090 let [a, b] = args[..] else { return };
1091 let (Some(&(a_low, a_high)), Some(&(count, _))) = (halves.get(&a), halves.get(&b)) else {
1092 return;
1093 };
1094 let top = ahead_const(func, inst, i128::from(HALF - 1));
1095 let places = ahead(func, inst, Opcode::And, &[count, top]);
1096 let back = ahead(func, inst, Opcode::Sub, &[top, places]);
1097 let one = ahead_const(func, inst, 1);
1098 let zero = ahead_const(func, inst, 0);
1099 let bit = ahead_const(func, inst, i128::from(HALF));
1100 let reach = ahead(func, inst, Opcode::And, &[count, bit]);
1101 let whole = compared(func, inst, IntPred::Ne, reach, zero);
1102
1103 let (low, high) = if opcode == Opcode::Shl {
1104 let moved = ahead(func, inst, Opcode::Shl, &[a_low, places]);
1105 let edge = ahead(func, inst, Opcode::LShr, &[a_low, one]);
1106 let across = ahead(func, inst, Opcode::LShr, &[edge, back]);
1107 let above = ahead(func, inst, Opcode::Shl, &[a_high, places]);
1108 let joined = ahead(func, inst, Opcode::Or, &[above, across]);
1109 let low = ahead(func, inst, Opcode::Select, &[whole, zero, moved]);
1110 let high = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
1111 (low, high)
1112 } else {
1113 let moved = ahead(func, inst, opcode, &[a_high, places]);
1114 let edge = ahead(func, inst, Opcode::Shl, &[a_high, one]);
1115 let across = ahead(func, inst, Opcode::Shl, &[edge, back]);
1116 let below = ahead(func, inst, Opcode::LShr, &[a_low, places]);
1117 let joined = ahead(func, inst, Opcode::Or, &[below, across]);
1118 let spent = if opcode == Opcode::AShr {
1121 ahead(func, inst, Opcode::AShr, &[a_high, top])
1122 } else {
1123 zero
1124 };
1125 let low = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
1126 let high = ahead(func, inst, Opcode::Select, &[whole, spent, moved]);
1127 (low, high)
1128 };
1129 replace(func, halves, inst, low, high);
1130}
1131
1132fn bitwise(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
1135 let args = func[func[inst].args].to_vec();
1136 let [a, b] = args[..] else { return };
1137 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
1138 return;
1139 };
1140 let low = ahead(func, inst, opcode, &[a_low, b_low]);
1141 let high = ahead(func, inst, opcode, &[a_high, b_high]);
1142 replace(func, halves, inst, low, high);
1143}
1144
1145fn compare(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
1160 let Extra::IntPred(pred) = func[inst].extra else { return };
1161 let args = func[func[inst].args].to_vec();
1162 let [a, b] = args[..] else { return };
1163 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
1164 return;
1165 };
1166 let answer = if matches!(pred, IntPred::Eq | IntPred::Ne) {
1167 let low = ahead(func, inst, Opcode::Xor, &[a_low, b_low]);
1168 let high = ahead(func, inst, Opcode::Xor, &[a_high, b_high]);
1169 let both = ahead(func, inst, Opcode::Or, &[low, high]);
1170 let zero = ahead_const(func, inst, 0);
1171 compared(func, inst, pred, both, zero)
1172 } else {
1173 let above = compared(func, inst, strict(pred), a_high, b_high);
1174 let below = compared(func, inst, unsigned(pred), a_low, b_low);
1175 let same = compared(func, inst, IntPred::Eq, a_high, b_high);
1176 let tail = bit(func, inst, Opcode::And, same, below);
1177 bit(func, inst, Opcode::Or, above, tail)
1178 };
1179 if let Some(result) = func[inst].first_result {
1180 forward.insert(result, answer);
1181 }
1182 func.remove_inst(inst);
1183}
1184
1185fn strict(pred: IntPred) -> IntPred {
1187 match pred {
1188 IntPred::Sle => IntPred::Slt,
1189 IntPred::Sge => IntPred::Sgt,
1190 IntPred::Ule => IntPred::Ult,
1191 IntPred::Uge => IntPred::Ugt,
1192 other => other,
1193 }
1194}
1195
1196fn unsigned(pred: IntPred) -> IntPred {
1198 match pred {
1199 IntPred::Slt => IntPred::Ult,
1200 IntPred::Sle => IntPred::Ule,
1201 IntPred::Sgt => IntPred::Ugt,
1202 IntPred::Sge => IntPred::Uge,
1203 other => other,
1204 }
1205}
1206
1207fn choose(func: &mut Func, halves: &mut Halves, inst: Inst) {
1213 let args = func[func[inst].args].to_vec();
1214 let [cond, then, other] = args[..] else { return };
1215 let (Some(&(then_low, then_high)), Some(&(other_low, other_high))) =
1216 (halves.get(&then), halves.get(&other))
1217 else {
1218 return;
1219 };
1220 let low = ahead(func, inst, Opcode::Select, &[cond, then_low, other_low]);
1221 let high = ahead(func, inst, Opcode::Select, &[cond, then_high, other_high]);
1222 replace(func, halves, inst, low, high);
1223}
1224
1225fn truncate(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
1231 let Some(&arg) = func[func[inst].args].first() else { return };
1232 let Some(&(low, _)) = halves.get(&arg) else { return };
1233 let Some(result) = func[inst].first_result else { return };
1234 if func[result].ty.bits() == HALF {
1235 forward.insert(result, low);
1236 func.remove_inst(inst);
1237 return;
1238 }
1239 becomes(func, inst, Opcode::Trunc, &[low]);
1240}
1241
1242fn extend(func: &mut Func, halves: &mut Halves, inst: Inst, signed: bool) {
1244 let Some(&arg) = func[func[inst].args].first() else { return };
1245 let low = if func[arg].ty.bits() == HALF {
1246 arg
1247 } else {
1248 let opcode = if signed { Opcode::SExt } else { Opcode::ZExt };
1249 ahead(func, inst, opcode, &[arg])
1250 };
1251 let high = if signed {
1252 let top = ahead_const(func, inst, i128::from(HALF - 1));
1253 ahead(func, inst, Opcode::AShr, &[low, top])
1254 } else {
1255 ahead_const(func, inst, 0)
1256 };
1257 replace(func, halves, inst, low, high);
1258}
1259
1260fn call(
1267 func: &mut Func,
1268 conv: &CallRegs,
1269 halves: &mut Halves,
1270 forward: &mut HashMap<Value, Value>,
1271 inst: Inst,
1272) {
1273 let data = func[inst];
1274 let Extra::Call(info) = data.extra else { return };
1275 let info = func[info];
1276 let Some((whole, variadic)) = site(func, inst) else { return };
1277 let old = func[data.args].to_vec();
1278 let skip = usize::from(data.opcode == Opcode::CallIndirect);
1280 let Some(slots) = plan(&whole, conv) else { return };
1281 let mut args = spread(&old[..skip], halves);
1282 for slot in slots.iter().copied() {
1283 let value = match slot {
1284 Slot::Whole(index) => old[skip + index],
1285 Slot::Low(index) => halves[&old[skip + index]].0,
1286 Slot::High(index) => halves[&old[skip + index]].1,
1287 Slot::Filler(ty) if ty.is_float() => {
1288 let extra = Extra::Imm(func.add_imm(Imm::from_bits(0)));
1289 written(func, inst, InstData { extra, ..InstData::new(Opcode::FConst) }, ty)
1290 }
1291 Slot::Filler(_) => ahead_const(func, inst, 0),
1292 };
1293 args.push(value);
1294 }
1295 let results: Vec<Type> = data
1296 .results()
1297 .map(|value| func[value].ty)
1298 .flat_map(|ty| if is_wide(ty) { vec![half(), half()] } else { vec![ty] })
1299 .collect();
1300 let signature = func.add_signature(planned(&Signature { variadic, ..whole }, &slots));
1301 let varargs = if variadic { func.push_abis(&[]) } else { info.varargs };
1304 let extra = Extra::Call(func.add_call(CallInfo { signature, varargs, ..info }));
1305 let args = func.push_values(&args);
1306 let span = func.span(inst);
1307 let made = func.create_inst(InstData { args, extra, ..data }, &results, span);
1308 func.insert_before(made, inst);
1309 let mut fresh = func[made].results();
1310 for old in data.results() {
1311 if is_wide(func[old].ty) {
1312 let (Some(low), Some(high)) = (fresh.next(), fresh.next()) else { return };
1313 halves.insert(old, (low, high));
1314 } else if let Some(again) = fresh.next() {
1315 forward.insert(old, again);
1316 }
1317 }
1318 func.remove_inst(inst);
1319}
1320
1321fn site(func: &Func, inst: Inst) -> Option<(Signature, bool)> {
1332 let data = func[inst];
1333 let Extra::Call(info) = data.extra else { return None };
1334 let info = func[info];
1335 let mut whole = func[info.signature].clone();
1336 let skip = usize::from(data.opcode == Opcode::CallIndirect);
1337 let args = &func[data.args];
1338 let named = skip + whole.params.len();
1339 if args.len() < named {
1340 return None;
1341 }
1342 let beyond = &func[info.varargs];
1343 for (index, &value) in args[named..].iter().enumerate() {
1344 let abi = beyond.get(index).copied().unwrap_or_default();
1345 whole.params.push(Param { ty: func[value].ty, abi });
1346 }
1347 let variadic = whole.variadic;
1348 whole.variadic = false;
1349 Some((whole, variadic))
1350}
1351
1352fn flatten(func: &mut Func, halves: &Halves, inst: Inst) {
1354 let args = spread(&func[func[inst].args], halves);
1355 func[inst].args = func.push_values(&args);
1356}
1357
1358fn edges(func: &mut Func, halves: &Halves, inst: Inst) {
1360 for at in func.target_list(inst).iter() {
1361 let call = func[at];
1362 let args = func[call.args].to_vec();
1363 if !args.iter().any(|value| halves.contains_key(value)) {
1364 continue;
1365 }
1366 let args = func.push_values(&spread(&args, halves));
1367 func.set_block_call(at, BlockCall { args, ..call });
1368 }
1369}
1370
1371fn spread(args: &[Value], halves: &Halves) -> Vec<Value> {
1373 args.iter()
1374 .flat_map(|value| match halves.get(value) {
1375 Some(&(low, high)) => vec![low, high],
1376 None => vec![*value],
1377 })
1378 .collect()
1379}
1380
1381fn split_signature(signature: &Signature) -> Signature {
1387 let split = |params: &[Param]| -> Vec<Param> {
1388 params
1389 .iter()
1390 .flat_map(|param| {
1391 if is_wide(param.ty) {
1392 vec![Param::new(half()), Param::new(half())]
1393 } else {
1394 vec![*param]
1395 }
1396 })
1397 .collect()
1398 };
1399 Signature {
1400 params: split(&signature.params),
1401 returns: split(&signature.returns),
1402 variadic: signature.variadic,
1403 }
1404}
1405
1406fn replace(func: &mut Func, halves: &mut Halves, inst: Inst, low: Value, high: Value) {
1408 if let Some(result) = func[inst].first_result {
1409 halves.insert(result, (low, high));
1410 }
1411 func.remove_inst(inst);
1412}
1413
1414fn substitute(func: &mut Func, forward: &HashMap<Value, Value>) {
1420 if forward.is_empty() {
1421 return;
1422 }
1423 let with = |value: Value| forward.get(&value).copied().unwrap_or(value);
1424 for block in func.blocks().collect::<Vec<_>>() {
1425 for inst in func.insts(block).collect::<Vec<Inst>>() {
1426 let args = func[inst].args;
1427 func.rewrite(args, with);
1428 for call in func.successors(inst).collect::<Vec<_>>() {
1429 func.rewrite(call.args, with);
1430 }
1431 }
1432 }
1433}
1434
1435fn word(info: MemInfo, at: u64) -> MemInfo {
1437 let align = if at == 0 { info.align } else { info.align.min(8) };
1438 MemInfo { size: STEP, align, ..info }
1439}
1440
1441fn stepped(func: &mut Func, inst: Inst, from: Value) -> Value {
1443 let step = ahead_const(func, inst, i128::from(STEP));
1444 let args = func.push_values(&[from, step]);
1445 written(func, inst, InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR)
1446}
1447
1448fn read(func: &mut Func, inst: Inst, from: Value, info: MemInfo, flags: Flags) -> Value {
1450 let extra = Extra::Mem(func.add_mem(info));
1451 let args = func.push_values(&[from]);
1452 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Load) };
1453 written(func, inst, data, half())
1454}
1455
1456fn write(func: &mut Func, inst: Inst, value: Value, into: Value, info: MemInfo, flags: Flags) {
1458 let span = func.span(inst);
1459 let extra = Extra::Mem(func.add_mem(info));
1460 let args = func.push_values(&[value, into]);
1461 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Store) };
1462 let made = func.create_inst(data, &[], span);
1463 func.insert_before(made, inst);
1464}
1465
1466fn compared(func: &mut Func, inst: Inst, pred: IntPred, lhs: Value, rhs: Value) -> Value {
1469 let args = func.push_values(&[lhs, rhs]);
1470 let extra = Extra::IntPred(pred);
1471 written(func, inst, InstData { args, extra, ..InstData::new(Opcode::ICmp) }, Type::I1)
1472}
1473
1474fn bit(func: &mut Func, inst: Inst, opcode: Opcode, lhs: Value, rhs: Value) -> Value {
1476 let args = func.push_values(&[lhs, rhs]);
1477 written(func, inst, InstData { args, ..InstData::new(opcode) }, Type::I1)
1478}
1479
1480fn ahead(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) -> Value {
1482 let args = func.push_values(args);
1483 written(func, inst, InstData { args, ..InstData::new(opcode) }, half())
1484}
1485
1486fn ahead_const(func: &mut Func, inst: Inst, value: i128) -> Value {
1488 let extra = Extra::Imm(func.add_imm(Imm::int(value, half())));
1489 written(func, inst, InstData { extra, ..InstData::new(Opcode::IConst) }, half())
1490}
1491
1492fn written(func: &mut Func, inst: Inst, data: InstData, ty: Type) -> Value {
1494 let span = func.span(inst);
1495 let made = func.create_inst(data, &[ty], span);
1496 func.insert_before(made, inst);
1497 func[made].first_result.expect("an instruction created with one result has one")
1498}
1499
1500fn becomes(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) {
1502 let args = func.push_values(args);
1503 let data = &mut func[inst];
1504 data.opcode = opcode;
1505 data.args = args;
1506 data.extra = Extra::None;
1507 data.flags = data.flags.intersection(Flags::legal_on(opcode));
1508}
1509
1510#[cfg(test)]
1511mod tests {
1512 use rucc_base::Interner;
1513 use rucc_ir::{
1514 Abi, Block, Builder, Flags, Float, Func, MemOrder, Module, Restrict, Signature, Type, Value,
1515 };
1516 use rucc_target::x86_64::{MINGW64, SYSV};
1517 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1518
1519 use super::{Def, Extra, HALF, IntPred, MemInfo, Opcode, halves};
1520
1521 fn wide() -> Type {
1523 Type::int(super::WIDE)
1524 }
1525
1526 fn target() -> TargetInfo {
1527 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1528 }
1529
1530 fn printed(func: &Func, names: &mut Interner) -> String {
1531 let module = Module::new(names.intern("w.c"), &target());
1532 rucc_ir::print_func(&module, func, names)
1533 }
1534
1535 fn shell(names: &mut Interner, params: &[Type], returns: &[Type]) -> (Func, Block, Vec<Value>) {
1537 let signature = Signature::new().with_params(params).with_returns(returns);
1538 let mut func = Func::new(names.intern("f"), signature);
1539 let entry = func.create_block();
1540 let values = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
1541 (func, entry, values)
1542 }
1543
1544 fn info(size: u64, align: u32) -> MemInfo {
1546 MemInfo {
1547 size,
1548 align,
1549 order: MemOrder::NotAtomic,
1550 tbaa: None,
1551 owns: 0,
1552 restrict: Restrict::NONE,
1553 }
1554 }
1555
1556 #[test]
1557 fn an_add_carries_from_the_low_half_into_the_high_one() {
1558 let mut names = Interner::new();
1559 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1560 let mut build = Builder::new(&mut func, entry);
1561 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1562 build.ret(&[sum]);
1563
1564 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1565 let text = printed(&func, &mut names);
1566 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1567 assert_eq!(text.matches(" = add ").count(), 3, "three adds: {text}");
1570 assert_eq!(text.matches("icmp ult").count(), 1, "one carry: {text}");
1571 assert_eq!(text.matches(" = zext.i64 ").count(), 1, "the carry as a number: {text}");
1572 }
1573
1574 #[test]
1575 fn a_subtract_borrows_the_other_way_round() {
1576 let mut names = Interner::new();
1577 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1578 let mut build = Builder::new(&mut func, entry);
1579 let difference = build.binary(Opcode::Sub, params[0], params[1], Flags::NONE);
1580 build.ret(&[difference]);
1581
1582 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1583 let text = printed(&func, &mut names);
1584 assert_eq!(text.matches(" = sub ").count(), 3, "three subtracts: {text}");
1585 assert!(text.contains("icmp ult %0, %2"), "the operands are compared: {text}");
1588 }
1589
1590 #[test]
1591 fn the_signature_and_the_entry_block_say_the_same_thing() {
1592 let mut names = Interner::new();
1593 let (mut func, entry, params) = shell(&mut names, &[Type::int(32), wide()], &[wide()]);
1594 let mut build = Builder::new(&mut func, entry);
1595 build.ret(&[params[1]]);
1596
1597 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1598 assert_eq!(
1599 func.signature().param_types().collect::<Vec<_>>(),
1600 [Type::int(32), Type::int(HALF), Type::int(HALF)],
1601 "the wide parameter became two where it stood"
1602 );
1603 assert_eq!(
1604 func.signature().return_types().collect::<Vec<_>>(),
1605 [Type::int(HALF), Type::int(HALF)],
1606 "and so did what comes back"
1607 );
1608 let text = printed(&func, &mut names);
1609 assert!(text.contains("block0(%0: i32, %1: i64, %2: i64)"), "the block agrees: {text}");
1610 assert!(text.contains("return %1, %2"), "both halves go back: {text}");
1611 let _ = entry;
1612 }
1613
1614 #[test]
1615 fn a_read_takes_the_high_word_a_word_above_the_low_one() {
1616 let mut names = Interner::new();
1617 let (mut func, entry, params) = shell(&mut names, &[Type::PTR], &[wide()]);
1618 let mut build = Builder::new(&mut func, entry);
1619 let value = build.load(wide(), params[0], info(16, 16), Flags::NONE);
1620 build.ret(&[value]);
1621
1622 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1623 let text = printed(&func, &mut names);
1624 assert_eq!(text.matches(" = load.i64 ").count(), 2, "two reads: {text}");
1625 assert!(text.contains("ptr_add"), "the high word is a word up: {text}");
1626 assert!(text.contains("align 16"), "the low word keeps what the object had: {text}");
1629 assert!(text.contains("align 8"), "the high word knows less: {text}");
1630 }
1631
1632 #[test]
1633 fn an_equality_asks_once_about_both_halves() {
1634 let mut names = Interner::new();
1635 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1636 let mut build = Builder::new(&mut func, entry);
1637 let same = build.icmp(IntPred::Eq, params[0], params[1]);
1638 let answer = build.unary(Opcode::ZExt, same, Type::int(32));
1639 build.ret(&[answer]);
1640
1641 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1642 let text = printed(&func, &mut names);
1643 assert_eq!(text.matches("icmp").count(), 1, "one comparison: {text}");
1644 assert_eq!(text.matches(" = xor ").count(), 2, "the halves differ or they do not: {text}");
1645 }
1646
1647 #[test]
1648 fn an_ordering_reads_the_low_halves_without_a_sign() {
1649 let mut names = Interner::new();
1650 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1651 let mut build = Builder::new(&mut func, entry);
1652 let below = build.icmp(IntPred::Slt, params[0], params[1]);
1653 let answer = build.unary(Opcode::ZExt, below, Type::int(32));
1654 build.ret(&[answer]);
1655
1656 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1657 let text = printed(&func, &mut names);
1658 assert!(text.contains("icmp slt"), "the high halves keep the sign: {text}");
1659 assert!(text.contains("icmp ult"), "the low halves have none: {text}");
1660 assert!(
1661 text.contains("icmp eq"),
1662 "and the low halves only matter when the high tie: {text}"
1663 );
1664 }
1665
1666 #[test]
1673 fn an_ordering_that_allows_equality_asks_the_high_halves_a_strict_question() {
1674 let mut names = Interner::new();
1675 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1676 let mut build = Builder::new(&mut func, entry);
1677 let at_least = build.icmp(IntPred::Sge, params[0], params[1]);
1678 let answer = build.unary(Opcode::ZExt, at_least, Type::int(32));
1679 build.ret(&[answer]);
1680
1681 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1682 let text = printed(&func, &mut names);
1683 assert!(text.contains("icmp sgt"), "the high halves settle it outright: {text}");
1684 assert!(!text.contains("icmp sge"), "a tie in the high halves settles nothing: {text}");
1685 assert!(text.contains("icmp uge"), "the low halves are the ones allowed to tie: {text}");
1686 }
1687
1688 #[test]
1689 fn a_widening_puts_the_sign_of_the_value_in_the_high_half() {
1690 let mut names = Interner::new();
1691 let (mut func, entry, params) = shell(&mut names, &[Type::int(32)], &[wide()]);
1692 let mut build = Builder::new(&mut func, entry);
1693 let value = build.unary(Opcode::SExt, params[0], wide());
1694 build.ret(&[value]);
1695
1696 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1697 let text = printed(&func, &mut names);
1698 assert!(text.contains("sext.i64"), "the value fills the low half: {text}");
1699 assert!(text.contains("ashr"), "and its sign fills the high one: {text}");
1700 }
1701
1702 #[test]
1703 fn a_block_parameter_becomes_two_and_every_branch_passes_two() {
1704 let mut names = Interner::new();
1705 let (mut func, entry, params) = shell(&mut names, &[wide(), Type::int(32)], &[wide()]);
1706 let tail = func.create_block();
1707 let carried = func.append_param(tail, wide());
1708 let mut build = Builder::new(&mut func, entry);
1709 let zero = build.iconst(Type::int(32), 0);
1710 let taken = build.icmp(IntPred::Ne, params[1], zero);
1711 let other = build.iconst(wide(), 7);
1712 build.br_if(taken, tail, &[params[0]], tail, &[other]);
1713 let mut build = Builder::new(&mut func, tail);
1714 build.ret(&[carried]);
1715
1716 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1717 let text = printed(&func, &mut names);
1718 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1719 assert!(text.contains("block1(%7: i64, %8: i64)"), "the block takes two: {text}");
1720 assert_eq!(text.matches("block1(").count(), 3, "and both edges pass two: {text}");
1721 }
1722
1723 #[test]
1730 fn a_multiply_is_three_multiplies_and_the_carry_out_of_the_low_ones() {
1731 let mut names = Interner::new();
1732 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1733 let mut build = Builder::new(&mut func, entry);
1734 let product = build.binary(Opcode::Mul, params[0], params[1], Flags::NONE);
1735 build.ret(&[product]);
1736
1737 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1738 let text = printed(&func, &mut names);
1739 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1740 assert_eq!(text.matches(" = mul ").count(), 7, "three and the carry's four: {text}");
1741 }
1742
1743 #[test]
1750 fn each_of_the_four_divisions_calls_the_routine_of_that_name() {
1751 for (opcode, routine) in [
1752 (Opcode::UDiv, "__udivti3"),
1753 (Opcode::SDiv, "__divti3"),
1754 (Opcode::URem, "__umodti3"),
1755 (Opcode::SRem, "__modti3"),
1756 ] {
1757 let mut names = Interner::new();
1758 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1759 let mut build = Builder::new(&mut func, entry);
1760 let answer = build.binary(opcode, params[0], params[1], Flags::NONE);
1761 build.ret(&[answer]);
1762
1763 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1764 let text = printed(&func, &mut names);
1765 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1766 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1767 }
1768 }
1769
1770 #[test]
1776 fn a_divide_hands_over_four_halves_and_takes_two_back() {
1777 let mut names = Interner::new();
1778 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1779 let mut build = Builder::new(&mut func, entry);
1780 let quotient = build.binary(Opcode::UDiv, params[0], params[1], Flags::NONE);
1781 build.ret(&[quotient]);
1782
1783 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1784 let text = printed(&func, &mut names);
1785 assert!(text.contains("@__udivti3(%0, %1, %2, %3)"), "four halves go over: {text}");
1786 assert!(text.contains("return %4, %5"), "and two come back: {text}");
1787 }
1788
1789 #[test]
1795 fn a_divide_of_something_computed_calls_with_the_halves_of_it() {
1796 let mut names = Interner::new();
1797 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1798 let mut build = Builder::new(&mut func, entry);
1799 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1800 let quotient = build.binary(Opcode::SDiv, sum, params[1], Flags::NONE);
1801 build.ret(&[quotient]);
1802
1803 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1804 let text = printed(&func, &mut names);
1805 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1806 assert_eq!(text.matches(" = add ").count(), 3, "the sum is still a sum: {text}");
1807 assert_eq!(text.matches("call @__divti3").count(), 1, "one call: {text}");
1808 }
1809
1810 #[test]
1816 fn each_conversion_between_this_width_and_a_float_calls_the_routine_of_that_name() {
1817 let double = Type::float(Float::F64);
1818 let single = Type::float(Float::F32);
1819 let quad = Type::float(Float::F128);
1820 for (opcode, float, routine) in [
1821 (Opcode::SIToFP, double, "__floattidf"),
1822 (Opcode::SIToFP, single, "__floattisf"),
1823 (Opcode::UIToFP, double, "__floatuntidf"),
1824 (Opcode::UIToFP, single, "__floatuntisf"),
1825 (Opcode::SIToFP, quad, "__floattitf"),
1826 (Opcode::UIToFP, quad, "__floatuntitf"),
1827 ] {
1828 let mut names = Interner::new();
1829 let (mut func, entry, params) = shell(&mut names, &[wide()], &[float]);
1830 let mut build = Builder::new(&mut func, entry);
1831 let answer = build.unary(opcode, params[0], float);
1832 build.ret(&[answer]);
1833
1834 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1835 let text = printed(&func, &mut names);
1836 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1837 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1838 }
1839 for (opcode, float, routine) in [
1840 (Opcode::FPToSI, double, "__fixdfti"),
1841 (Opcode::FPToSI, single, "__fixsfti"),
1842 (Opcode::FPToUI, double, "__fixunsdfti"),
1843 (Opcode::FPToUI, single, "__fixunssfti"),
1844 (Opcode::FPToSI, quad, "__fixtfti"),
1845 (Opcode::FPToUI, quad, "__fixunstfti"),
1846 ] {
1847 let mut names = Interner::new();
1848 let (mut func, entry, params) = shell(&mut names, &[float], &[wide()]);
1849 let mut build = Builder::new(&mut func, entry);
1850 let answer = build.unary(opcode, params[0], wide());
1851 build.ret(&[answer]);
1852
1853 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1854 let text = printed(&func, &mut names);
1855 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1856 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1857 }
1858 }
1859
1860 #[test]
1866 fn a_conversion_hands_over_halves_one_way_and_takes_them_back_the_other() {
1867 let double = Type::float(Float::F64);
1868 let mut names = Interner::new();
1869 let (mut func, entry, params) = shell(&mut names, &[wide()], &[double]);
1870 let mut build = Builder::new(&mut func, entry);
1871 let answer = build.unary(Opcode::SIToFP, params[0], double);
1872 build.ret(&[answer]);
1873
1874 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1875 let text = printed(&func, &mut names);
1876 assert!(text.contains("@__floattidf(%0, %1)"), "two halves go over: {text}");
1877 assert!(text.contains("return %2"), "and one float comes back: {text}");
1878
1879 let mut names = Interner::new();
1880 let (mut func, entry, params) = shell(&mut names, &[double], &[wide()]);
1881 let mut build = Builder::new(&mut func, entry);
1882 let answer = build.unary(Opcode::FPToSI, params[0], wide());
1883 build.ret(&[answer]);
1884
1885 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1886 let text = printed(&func, &mut names);
1887 assert!(text.contains("@__fixdfti(%0)"), "the float goes over as it is: {text}");
1888 assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1889 }
1890
1891 #[test]
1898 fn a_conversion_against_a_quad_hands_over_the_pair_and_the_quad_whole() {
1899 let quad = Type::float(Float::F128);
1900 let mut names = Interner::new();
1901 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
1902 let mut build = Builder::new(&mut func, entry);
1903 let answer = build.unary(Opcode::UIToFP, params[0], quad);
1904 build.ret(&[answer]);
1905
1906 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1907 let text = printed(&func, &mut names);
1908 assert!(text.contains("@__floatuntitf(%0, %1)"), "two halves go over: {text}");
1909 assert!(text.contains("return %2"), "and one quad comes back: {text}");
1910
1911 let mut names = Interner::new();
1912 let (mut func, entry, params) = shell(&mut names, &[quad], &[wide()]);
1913 let mut build = Builder::new(&mut func, entry);
1914 let answer = build.unary(Opcode::FPToSI, params[0], wide());
1915 build.ret(&[answer]);
1916
1917 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1918 let text = printed(&func, &mut names);
1919 assert!(text.contains("@__fixtfti(%0)"), "the quad goes over as it is: {text}");
1920 assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1921 }
1922
1923 #[test]
1930 fn a_conversion_at_a_width_the_runtime_has_no_routine_for_is_left_alone() {
1931 let long = Type::float(Float::F80);
1932 let mut names = Interner::new();
1933 let (mut func, entry, params) = shell(&mut names, &[wide()], &[long]);
1934 let mut build = Builder::new(&mut func, entry);
1935 let answer = build.unary(Opcode::SIToFP, params[0], long);
1936 build.ret(&[answer]);
1937
1938 assert!(!halves(&mut func, &mut names, &SYSV), "the pass does not understand this one");
1939 let text = printed(&func, &mut names);
1940 assert!(text.contains("i128"), "the width is still there: {text}");
1941 }
1942
1943 #[test]
1950 fn a_shift_left_chooses_between_a_count_that_crossed_a_half_and_one_that_did_not() {
1951 let mut names = Interner::new();
1952 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1953 let mut build = Builder::new(&mut func, entry);
1954 let moved = build.binary(Opcode::Shl, params[0], params[1], Flags::NONE);
1955 build.ret(&[moved]);
1956
1957 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1958 let text = printed(&func, &mut names);
1959 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1960 assert_eq!(
1961 text.matches(" = shl ").count(),
1962 2,
1963 "one per half, and the far case reuses one: {text}"
1964 );
1965 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1966 assert_eq!(text.matches(" = lshr ").count(), 2, "the crossing bits, in two steps: {text}");
1967 }
1968
1969 #[test]
1976 fn the_bits_that_cross_move_one_place_and_then_the_rest_of_the_way() {
1977 let mut names = Interner::new();
1978 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1979 let mut build = Builder::new(&mut func, entry);
1980 let moved = build.binary(Opcode::LShr, params[0], params[1], Flags::NONE);
1981 build.ret(&[moved]);
1982
1983 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1984 let text = printed(&func, &mut names);
1985 assert!(text.contains("iconst.i64 63"), "sixty three is the distance left: {text}");
1986 assert!(text.contains("iconst.i64 1"), "after the one place that comes first: {text}");
1987 assert!(text.contains(" = sub "), "the rest of the way is worked out: {text}");
1988 assert!(
1989 !text.contains("iconst.i64 127"),
1990 "and the count is not masked to the width: {text}"
1991 );
1992 }
1993
1994 #[test]
2000 fn an_arithmetic_shift_right_leaves_the_sign_bit_where_a_logical_one_leaves_zeroes() {
2001 let mut names = Interner::new();
2002 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
2003 let mut build = Builder::new(&mut func, entry);
2004 let moved = build.binary(Opcode::AShr, params[0], params[1], Flags::NONE);
2005 build.ret(&[moved]);
2006
2007 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2008 let text = printed(&func, &mut names);
2009 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
2010 assert_eq!(text.matches(" = ashr ").count(), 2, "the count and the sign: {text}");
2012 assert_eq!(text.matches(" = lshr ").count(), 1, "the low half is not signed: {text}");
2013 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
2014 }
2015
2016 fn arrived(params: &[Type], wide_at: usize) -> (Signature, usize) {
2022 let mut names = Interner::new();
2023 let word = Type::int(HALF);
2024 let (mut func, entry, values) = shell(&mut names, params, &[word]);
2025 let mut build = Builder::new(&mut func, entry);
2026 let low = build.unary(Opcode::Trunc, values[wide_at], word);
2027 build.ret(&[low]);
2028
2029 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2030 let ret = func.insts(entry).last().expect("the block ends in a return");
2031 let read = func[func[ret].args][0];
2032 let at = func[entry].params.iter().position(|&value| value == read);
2033 (func.signature().clone(), at.expect("the low half is a parameter"))
2034 }
2035
2036 fn types(signature: &Signature) -> Vec<Type> {
2037 signature.params.iter().map(|param| param.ty).collect()
2038 }
2039
2040 #[test]
2041 fn a_parameter_with_one_register_left_goes_in_memory_and_the_register_stays_empty() {
2042 let word = Type::int(HALF);
2043 let (signature, low) = arrived(&[word, word, word, word, word, wide()], 5);
2044 assert_eq!(types(&signature), vec![word; 8], "five, a filler and two halves");
2045 assert_eq!(low, 6, "the halves come after the register the value skipped");
2046 }
2047
2048 #[test]
2049 fn the_register_a_wide_parameter_skipped_goes_to_the_parameter_after_it() {
2050 let word = Type::int(HALF);
2051 let (signature, low) = arrived(&[word, word, word, word, word, wide(), word], 5);
2052 assert_eq!(types(&signature), vec![word; 8], "six registers and two words, no filler");
2053 assert_eq!(low, 6, "the word after the wide value took the sixth register");
2054 }
2055
2056 #[test]
2057 fn a_wide_parameter_in_memory_starts_on_a_sixteen_byte_boundary() {
2058 let word = Type::int(HALF);
2059 let params = [word, word, word, word, word, word, word, wide()];
2060 let (signature, low) = arrived(¶ms, 7);
2061 assert_eq!(types(&signature), vec![word; 10], "the seventh word, a filler, the halves");
2062 assert_eq!(low, 8, "the filler takes the word the alignment leaves empty");
2063 }
2064
2065 #[test]
2067 fn a_call_passes_a_wide_argument_in_memory_the_way_the_callee_reads_it() {
2068 let mut names = Interner::new();
2069 let word = Type::int(HALF);
2070 let (mut func, entry, _) = shell(&mut names, &[], &[]);
2071 let callee = names.intern("g");
2072 let params = [word, word, word, word, word, wide()];
2073 let signature = func.add_signature(Signature::new().with_params(¶ms));
2074 let mut build = Builder::new(&mut func, entry);
2075 let one = build.iconst(word, 1);
2076 let big = build.iconst(wide(), 4);
2077 build.call(callee, signature, &[one, one, one, one, one, big]);
2078 build.ret(&[]);
2079
2080 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2081 let call = func.insts(entry).find(|&inst| func[inst].opcode == Opcode::Call);
2082 let call = call.expect("the call is still there");
2083 let Extra::Call(info) = func[call].extra else { unreachable!("a call has call info") };
2084 let passed = func[func[call].args].to_vec();
2085 assert_eq!(types(&func[func[info].signature]), vec![word; 8], "as the callee has it");
2086 assert_eq!(passed.len(), 8, "one argument for each parameter");
2087 assert_eq!(passed[..5], [one; 5], "the words keep their registers");
2088 let constant = |value: Value| {
2089 let Def::Result { inst, .. } = func[value].def else { return None };
2090 let Extra::Imm(imm) = func[inst].extra else { return None };
2091 Some(func[imm].unsigned())
2092 };
2093 assert_eq!(constant(passed[6]), Some(4), "the low half is the first word in memory");
2094 assert_eq!(constant(passed[7]), Some(0), "and the high half the second");
2095 }
2096
2097 #[test]
2100 fn a_variadic_call_with_a_wide_argument_in_memory_leaves_the_function_alone() {
2101 let mut names = Interner::new();
2102 let word = Type::int(HALF);
2103 let (mut func, entry, _) = shell(&mut names, &[], &[]);
2104 let callee = names.intern("g");
2105 let params = [word, word, word, word, word, wide()];
2106 let signature = Signature { variadic: true, ..Signature::new().with_params(¶ms) };
2107 let signature = func.add_signature(signature);
2108 let mut build = Builder::new(&mut func, entry);
2109 let one = build.iconst(word, 1);
2110 let big = build.iconst(wide(), 4);
2111 build.call(callee, signature, &[one, one, one, one, one, big]);
2112 build.ret(&[]);
2113 let before = printed(&func, &mut names);
2114
2115 assert!(!halves(&mut func, &mut names, &SYSV), "the named parameters cannot move");
2116 assert_eq!(printed(&func, &mut names), before, "so nothing moved");
2117 }
2118
2119 #[test]
2123 fn a_wide_argument_past_the_dots_goes_where_a_named_one_would() {
2124 let mut names = Interner::new();
2125 let word = Type::int(HALF);
2126 let (mut func, entry, _) = shell(&mut names, &[], &[]);
2127 let callee = names.intern("g");
2128 let params = [word, word, word, word, word];
2129 let signature = Signature { variadic: true, ..Signature::new().with_params(¶ms) };
2130 let signature = func.add_signature(signature);
2131 let mut build = Builder::new(&mut func, entry);
2132 let one = build.iconst(word, 1);
2133 let big = build.iconst(wide(), 4);
2134 build.call_varargs(callee, signature, &[one, one, one, one, one, big], &[Abi::Plain]);
2135 build.ret(&[]);
2136
2137 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2138 let call = func.insts(entry).find(|&inst| func[inst].opcode == Opcode::Call);
2139 let call = call.expect("the call is still there");
2140 let Extra::Call(info) = func[call].extra else { unreachable!("a call has call info") };
2141 let made = &func[func[info].signature];
2142 assert!(made.variadic, "the callee is still variadic");
2143 assert_eq!(types(made), vec![word; 8], "five words, a filler and the two halves");
2144 assert!(func[func[info].varargs].is_empty(), "every argument is named now");
2145 assert_eq!(func[func[call].args].len(), 8, "one argument for each parameter");
2146 }
2147
2148 #[test]
2151 fn a_wide_argument_past_the_dots_on_windows_leaves_the_function_alone() {
2152 let mut names = Interner::new();
2153 let word = Type::int(HALF);
2154 let (mut func, entry, _) = shell(&mut names, &[], &[]);
2155 let callee = names.intern("g");
2156 let signature = Signature { variadic: true, ..Signature::new().with_params(&[word]) };
2157 let signature = func.add_signature(signature);
2158 let mut build = Builder::new(&mut func, entry);
2159 let one = build.iconst(word, 1);
2160 let big = build.iconst(wide(), 4);
2161 build.call_varargs(callee, signature, &[one, big], &[Abi::Plain]);
2162 build.ret(&[]);
2163
2164 assert!(!halves(&mut func, &mut names, &MINGW64), "the call is left for a refusal");
2165 }
2166
2167 #[test]
2176 fn a_block_made_after_the_one_it_runs_before_is_still_split() {
2177 let mut names = Interner::new();
2178 let (mut func, entry, params) = shell(&mut names, &[wide()], &[wide()]);
2179 let tail = func.create_block();
2180 let middle = func.create_block();
2181 let mut build = Builder::new(&mut func, entry);
2182 build.jump(middle, &[]);
2183 let mut build = Builder::new(&mut func, middle);
2184 let doubled = build.binary(Opcode::Add, params[0], params[0], Flags::NONE);
2185 build.jump(tail, &[]);
2186 let mut build = Builder::new(&mut func, tail);
2187 let again = build.binary(Opcode::Add, doubled, doubled, Flags::NONE);
2188 build.ret(&[again]);
2189
2190 assert!(
2191 halves(&mut func, &mut names, &SYSV),
2192 "the definition runs before the use whatever the list says"
2193 );
2194 let text = printed(&func, &mut names);
2195 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
2196 }
2197
2198 #[test]
2199 fn a_function_with_nothing_that_wide_is_not_touched() {
2200 let mut names = Interner::new();
2201 let word = Type::int(HALF);
2202 let (mut func, entry, params) = shell(&mut names, &[word, word], &[word]);
2203 let mut build = Builder::new(&mut func, entry);
2204 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
2205 build.ret(&[sum]);
2206
2207 assert!(!halves(&mut func, &mut names, &SYSV), "there is nothing to split");
2208 }
2209
2210 #[test]
2217 fn on_windows_a_wide_operand_goes_over_as_the_address_of_a_copy() {
2218 let mut names = Interner::new();
2219 let quad = Type::float(Float::F64);
2220 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
2221 let mut build = Builder::new(&mut func, entry);
2222 let answer = build.unary(Opcode::SIToFP, params[0], quad);
2223 build.ret(&[answer]);
2224
2225 assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
2226 let text = printed(&func, &mut names);
2227 assert!(text.contains("call @__floattidf"), "{text}");
2228 assert_eq!(text.matches("alloca").count(), 1, "one slot: {text}");
2230 assert_eq!(text.matches("store").count(), 2, "a half at a time: {text}");
2231 assert_eq!(text.matches("ptr_add").count(), 1, "the high half eight bytes up: {text}");
2232 assert!(!text.contains("__floattidf(%0, %1)"), "not the two halves: {text}");
2233 }
2234
2235 #[test]
2237 fn on_windows_a_wide_answer_at_this_format_comes_back_through_a_slot() {
2238 let mut names = Interner::new();
2239 let quad = Type::float(Float::F128);
2240 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
2241 let mut build = Builder::new(&mut func, entry);
2242 let answer = build.unary(Opcode::SIToFP, params[0], quad);
2243 build.ret(&[answer]);
2244
2245 assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
2246 let text = printed(&func, &mut names);
2247 assert!(text.contains("call @__floattitf"), "{text}");
2248 assert_eq!(text.matches("alloca").count(), 2, "two slots: {text}");
2250 assert_eq!(text.matches(" = call").count(), 0, "the call answers nothing: {text}");
2251 assert_eq!(text.matches(" = load").count(), 1, "the answer is the load after it: {text}");
2252 }
2253
2254 #[test]
2256 fn the_convention_with_registers_for_both_halves_puts_nothing_on_the_frame() {
2257 let mut names = Interner::new();
2258 let double = Type::float(Float::F64);
2259 let (mut func, entry, params) = shell(&mut names, &[wide()], &[double]);
2260 let mut build = Builder::new(&mut func, entry);
2261 let answer = build.unary(Opcode::SIToFP, params[0], double);
2262 build.ret(&[answer]);
2263
2264 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2265 let text = printed(&func, &mut names);
2266 assert!(text.contains("@__floattidf(%0, %1)"), "both halves in registers: {text}");
2267 assert!(!text.contains("alloca"), "nothing goes through the frame: {text}");
2268 }
2269
2270 #[test]
2273 fn on_windows_a_wide_answer_comes_back_in_one_register_and_is_split_on_the_frame() {
2274 let mut names = Interner::new();
2275 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
2276 let mut build = Builder::new(&mut func, entry);
2277 let answer = build.binary(Opcode::SDiv, params[0], params[1], Flags::NONE);
2278 build.ret(&[answer]);
2279
2280 assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
2281 let text = printed(&func, &mut names);
2282 assert!(text.contains("call @__divti3(%4, %7) : (ptr, ptr) -> f128"), "{text}");
2285 assert_eq!(text.matches(" = call").count(), 1, "one answer: {text}");
2286 assert_eq!(text.matches("alloca").count(), 3, "three slots: {text}");
2288 assert_eq!(text.matches(" = load").count(), 2, "the two halves: {text}");
2289 }
2290
2291 #[test]
2293 fn the_convention_with_registers_for_the_answer_reads_both_halves_out_of_them() {
2294 let mut names = Interner::new();
2295 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
2296 let mut build = Builder::new(&mut func, entry);
2297 let answer = build.binary(Opcode::SDiv, params[0], params[1], Flags::NONE);
2298 build.ret(&[answer]);
2299
2300 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2301 let text = printed(&func, &mut names);
2302 assert!(text.contains("@__divti3(%0, %1, %2, %3)"), "four halves over: {text}");
2303 assert!(!text.contains("alloca"), "nothing goes through the frame: {text}");
2304 }
2305}