1use rucc_base::Interner;
91use rucc_base::hash::{Map, Set};
92use rucc_ir::{
93 Abi, Block, BlockCall, CallInfo, Def, Extra, Flags, Float, Func, Imm, Inst, InstData, IntPred,
94 MemInfo, MemOrder, Opcode, Param, Restrict, Signature, Type, Value,
95};
96use rucc_target::{AbiDescription, CallRegs, Convention, Places, Variadic, Where};
97
98use crate::capability;
99use crate::expand;
100
101const WIDE: u32 = 128;
103
104const MODE: &str = "i128";
106
107const HALF: u32 = 64;
109
110const STEP: u64 = 8;
112
113fn is_wide(ty: Type) -> bool {
115 ty.is_int() && ty.is_scalar() && ty.bits() == WIDE
116}
117
118fn half() -> Type {
120 Type::int(HALF)
121}
122
123pub fn halves(func: &mut Func, names: &mut Interner, conv: &CallRegs) -> bool {
134 if !func.values().any(|value| is_wide(func[value].ty)) {
135 return false;
136 }
137 let insts: Vec<Inst> =
138 walk(func).into_iter().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
139 let order: Map<Inst, usize> = insts.iter().enumerate().map(|(at, &inst)| (inst, at)).collect();
140 if !insts.iter().enumerate().all(|(at, &inst)| can_split(func, conv, &order, at, inst)) {
141 return false;
142 }
143 let Some(arriving) = plan(func.signature(), conv) else { return false };
144 if !func.signatures().all(|signature| plan(signature, conv).is_some()) {
145 return false;
146 }
147
148 let mut halves: Halves = Map::default();
149 let mut forward: Map<Value, Value> = Map::default();
150 let entry = func.entry();
151 for block in func.blocks().collect::<Vec<_>>() {
152 if Some(block) == entry {
153 arrive(func, block, &arriving, &mut halves, &mut forward);
154 } else {
155 params(func, block, &mut halves, &mut forward);
156 }
157 }
158 for &inst in &insts {
159 rewrite(func, names, conv, &mut halves, &mut forward, inst);
160 }
161 substitute(func, &forward);
162 let signature = planned(func.signature(), &arriving);
163 func.set_signature(signature);
164 true
165}
166
167fn walk(func: &Func) -> Vec<Block> {
185 let Some(entry) = func.entry() else { return func.blocks().collect() };
186 let mut seen: Set<Block> = Set::default();
187 let mut order: Vec<Block> = Vec::new();
188 let mut stack: Vec<(Block, bool)> = vec![(entry, false)];
191 seen.insert(entry);
192 while let Some((block, done)) = stack.pop() {
193 if done {
194 order.push(block);
195 continue;
196 }
197 stack.push((block, true));
198 let Some(term) = func.terminator(block) else { continue };
199 for call in func.successors(term) {
200 if seen.insert(call.block) {
201 stack.push((call.block, false));
202 }
203 }
204 }
205 order.reverse();
206 order.extend(func.blocks().filter(|block| !seen.contains(block)));
207 order
208}
209
210type Halves = Map<Value, (Value, Value)>;
212
213fn understood(opcode: Opcode) -> bool {
224 matches!(
225 opcode,
226 Opcode::IConst
227 | Opcode::Load
228 | Opcode::Store
229 | Opcode::Add
230 | Opcode::Sub
231 | Opcode::Mul
232 | Opcode::UDiv
233 | Opcode::SDiv
234 | Opcode::URem
235 | Opcode::SRem
236 | Opcode::Shl
237 | Opcode::LShr
238 | Opcode::AShr
239 | Opcode::And
240 | Opcode::Or
241 | Opcode::Xor
242 | Opcode::ICmp
243 | Opcode::Select
244 | Opcode::SIToFP
245 | Opcode::UIToFP
246 | Opcode::FPToSI
247 | Opcode::FPToUI
248 | Opcode::Trunc
249 | Opcode::SExt
250 | Opcode::ZExt
251 | Opcode::Call
252 | Opcode::CallIndirect
253 | Opcode::Return
254 | Opcode::Jump
255 | Opcode::BrIf
256 )
257}
258
259fn can_split(
264 func: &Func,
265 conv: &CallRegs,
266 order: &Map<Inst, usize>,
267 at: usize,
268 inst: Inst,
269) -> bool {
270 let data = func[inst];
271 let reads = operands(func, inst);
272 let wide = |&value: &Value| is_wide(func[value].ty);
273 if !reads.iter().any(wide) && !data.results().any(|value| is_wide(func[value].ty)) {
274 return true;
275 }
276 if !understood(data.opcode) {
277 return false;
278 }
279 if func.carries_mem(inst) {
283 return false;
284 }
285 if data.opcode == Opcode::SExt && reads.iter().any(|&value| func[value].ty.bits() < 8) {
289 return false;
290 }
291 if matches!(data.opcode, Opcode::SIToFP | Opcode::UIToFP | Opcode::FPToSI | Opcode::FPToUI)
296 && converted(func, inst).is_none()
297 {
298 return false;
299 }
300 if matches!(data.opcode, Opcode::Call | Opcode::CallIndirect) {
307 let Some((site, variadic)) = site(func, inst) else { return false };
308 let Some(conv) = conv.under(site.convention) else { return false };
309 let in_memory = conv.abi.variadic == Variadic::AlwaysMemory;
310 if variadic && (conv.shared_positions || in_memory || 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>> {
404 let conv = conv.under(signature.convention)?;
405 let word = Param::new(half());
406 let mut places = Places::new(conv);
407 let mut meant: Vec<(Slot, Param, Where)> = Vec::new();
408 let mut moved = false;
409 for (index, ¶m) in signature.params.iter().enumerate() {
410 if !is_wide(param.ty) {
411 meant.push((Slot::Whole(index), param, place(&mut places, param, conv)));
412 continue;
413 }
414 let scalars = conv.abi.scalars;
415 let mut ahead = places.clone();
416 let skipped = (scalars.wide_integer_starts_even && ahead.integers() % 2 == 1)
418 .then(|| ahead.integer(HALF / 8));
419 if let (low @ Where::Reg(_), high @ Where::Reg(_)) =
420 (ahead.integer(HALF / 8), ahead.integer(HALF / 8))
421 {
422 places = ahead;
423 if let Some(at) = skipped {
424 meant.push((Slot::Filler(word.ty), word, at));
425 }
426 meant.push((Slot::Low(index), word, low));
427 meant.push((Slot::High(index), word, high));
428 continue;
429 }
430 if scalars.wide_integer_drains {
431 places.drain_integers();
432 }
433 let Where::Stack(at) = places.on_stack(WIDE / 8, WIDE / 8) else { return None };
434 meant.push((Slot::Low(index), word, Where::Stack(at)));
435 meant.push((Slot::High(index), word, Where::Stack(at + HALF / 8)));
436 moved = true;
437 }
438 if !moved {
439 return Some(meant.into_iter().map(|(slot, _, _)| slot).collect());
440 }
441 if signature.variadic || conv.shared_positions {
442 return None;
443 }
444
445 let in_reg = |at: &Where| matches!(at, Where::Reg(_));
446 let mut stacked: Vec<&(Slot, Param, Where)> =
447 meant.iter().filter(|(_, _, at)| !in_reg(at)).collect();
448 stacked.sort_by_key(|(_, _, at)| match at {
449 Where::Stack(up) => *up,
450 Where::Reg(_) => 0,
451 });
452 let mut order: Vec<(Slot, Param, Option<Where>)> = Vec::new();
453 for float in [false, true] {
454 let kind = |param: &Param| scalar(*param) && param.ty.is_float() == float;
455 order.extend(
456 meant
457 .iter()
458 .filter(|(_, param, at)| kind(param) && in_reg(at))
459 .map(|&(slot, param, at)| (slot, param, Some(at))),
460 );
461 let (count, filler) = match float {
462 false => (conv.int_args.len(), word),
463 true => (conv.sse_args.len(), Param::new(Type::float(Float::F64))),
464 };
465 if stacked.iter().any(|(_, param, _)| kind(param)) {
466 let took = order.iter().filter(|(_, param, _)| kind(param)).count();
467 let padding = count.saturating_sub(took);
468 order.extend((0..padding).map(|_| (Slot::Filler(filler.ty), filler, None)));
469 }
470 }
471
472 let mut places = Places::new(conv);
473 let mut slots = Vec::with_capacity(order.len() + stacked.len());
474 for (slot, param, meant) in order {
475 let at = place(&mut places, param, conv);
476 if !in_reg(&at) || meant.is_some_and(|meant| meant != at) {
477 return None;
478 }
479 slots.push(slot);
480 }
481 for &&(slot, param, meant) in &stacked {
482 let Where::Stack(up) = meant else { return None };
483 while places.size() < up {
484 if in_reg(&place(&mut places, word, conv)) {
485 return None;
486 }
487 slots.push(Slot::Filler(word.ty));
488 }
489 if place(&mut places, param, conv) != meant {
490 return None;
491 }
492 slots.push(slot);
493 }
494 Some(slots)
495}
496
497fn place(places: &mut Places<'_>, param: Param, conv: &CallRegs) -> Where {
499 if let Abi::ByVal { size, align, drains } = param.abi {
503 let at = places.object(u32::try_from(size).unwrap_or(u32::MAX), align);
504 crate::abi::drain(places, drains);
505 at
506 } else if crate::abi::on_the_stack(param.ty) {
507 let (size, align) = crate::abi::X87_AREA;
508 places.on_stack(size, align)
509 } else if param.ty.is_float() {
510 places.float(crate::abi::float_bytes(param.ty))
511 } else {
512 places.integer(crate::abi::int_bytes(param.ty, conv))
513 }
514}
515
516fn scalar(param: Param) -> bool {
518 !matches!(param.abi, Abi::ByVal { .. }) && !crate::abi::on_the_stack(param.ty)
519}
520
521fn planned(signature: &Signature, slots: &[Slot]) -> Signature {
523 let params = slots
524 .iter()
525 .map(|&slot| match slot {
526 Slot::Whole(index) => signature.params[index],
527 Slot::Low(_) | Slot::High(_) => Param::new(half()),
528 Slot::Filler(ty) => Param::new(ty),
529 })
530 .collect();
531 Signature { params, ..split_signature(signature) }
532}
533
534fn params(func: &mut Func, block: Block, halves: &mut Halves, forward: &mut Map<Value, Value>) {
541 let old: Vec<Value> = func[block].params.clone();
542 if !old.iter().any(|&value| is_wide(func[value].ty)) {
543 return;
544 }
545 for &value in &old {
546 if is_wide(func[value].ty) {
547 let low = func.append_param(block, half());
548 let high = func.append_param(block, half());
549 halves.insert(value, (low, high));
550 } else {
551 let again = func.append_param(block, func[value].ty);
552 forward.insert(value, again);
553 }
554 }
555 func.retain_params(block, |value| !old.contains(&value));
556}
557
558fn arrive(
560 func: &mut Func,
561 block: Block,
562 slots: &[Slot],
563 halves: &mut Halves,
564 forward: &mut Map<Value, Value>,
565) {
566 let old: Vec<Value> = func[block].params.clone();
567 if !old.iter().any(|&value| is_wide(func[value].ty)) {
568 return;
569 }
570 let mut lows = Map::default();
571 for &slot in slots {
572 match slot {
573 Slot::Whole(index) => {
574 let again = func.append_param(block, func[old[index]].ty);
575 forward.insert(old[index], again);
576 }
577 Slot::Low(index) => {
578 lows.insert(index, func.append_param(block, half()));
579 }
580 Slot::High(index) => {
581 let high = func.append_param(block, half());
582 halves.insert(old[index], (lows[&index], high));
583 }
584 Slot::Filler(ty) => {
585 func.append_param(block, ty);
586 }
587 }
588 }
589 func.retain_params(block, |value| !old.contains(&value));
590}
591
592fn rewrite(
594 func: &mut Func,
595 names: &mut Interner,
596 conv: &CallRegs,
597 halves: &mut Halves,
598 forward: &mut Map<Value, Value>,
599 inst: Inst,
600) {
601 let abi = conv.under(Convention::Target).unwrap_or(conv).abi;
604 let data = func[inst];
605 let produces = data.results().any(|value| is_wide(func[value].ty));
606 let takes = func[data.args].iter().any(|&value| is_wide(func[value].ty));
607 match data.opcode {
608 Opcode::IConst if produces => constant(func, halves, inst),
609 Opcode::Load if produces => load(func, halves, inst),
610 Opcode::Store if takes => store(func, halves, inst),
611 Opcode::Add | Opcode::Sub if produces => carried(func, halves, inst, data.opcode),
612 Opcode::Mul if produces => multiply(func, halves, inst),
613 Opcode::UDiv | Opcode::SDiv | Opcode::URem | Opcode::SRem if produces => {
614 divide(func, names, abi, halves, inst, data.opcode);
615 }
616 Opcode::Shl | Opcode::LShr | Opcode::AShr if produces => {
617 shifted(func, halves, inst, data.opcode);
618 }
619 Opcode::And | Opcode::Or | Opcode::Xor if produces => {
620 bitwise(func, halves, inst, data.opcode);
621 }
622 Opcode::SIToFP | Opcode::UIToFP if takes => {
623 to_float(func, names, abi, halves, forward, inst, data.opcode == Opcode::SIToFP);
624 }
625 Opcode::FPToSI | Opcode::FPToUI if produces => {
626 from_float(func, names, abi, halves, inst, data.opcode == Opcode::FPToSI);
627 }
628 Opcode::ICmp if takes => compare(func, halves, forward, inst),
629 Opcode::Select if produces => choose(func, halves, inst),
630 Opcode::Trunc if takes => truncate(func, halves, forward, inst),
631 Opcode::SExt | Opcode::ZExt if produces => {
632 extend(func, halves, inst, data.opcode == Opcode::SExt);
633 }
634 Opcode::Call | Opcode::CallIndirect if produces || takes => {
635 call(func, conv, halves, forward, inst);
636 }
637 Opcode::Return if takes => flatten(func, halves, inst),
638 Opcode::Jump | Opcode::BrIf => edges(func, halves, inst),
639 _ => {}
640 }
641}
642
643fn constant(func: &mut Func, halves: &mut Halves, inst: Inst) {
645 let Extra::Imm(imm) = func[inst].extra else { return };
646 let bits = func[imm].unsigned();
647 #[expect(clippy::cast_possible_truncation, reason = "the halves are what this is taking")]
648 let (low, high) = (bits as u64, (bits >> HALF) as u64);
649 let low = ahead_const(func, inst, i128::from(low));
650 let high = ahead_const(func, inst, i128::from(high));
651 replace(func, halves, inst, low, high);
652}
653
654fn load(func: &mut Func, halves: &mut Halves, inst: Inst) {
660 let data = func[inst];
661 let Extra::Mem(mem) = data.extra else { return };
662 let info = func[mem];
663 let Some(&from) = func[data.args].first() else { return };
664 let low = read(func, inst, from, word(info, 0), data.flags);
665 let up = stepped(func, inst, from);
666 let high = read(func, inst, up, word(info, STEP), data.flags);
667 replace(func, halves, inst, low, high);
668}
669
670fn store(func: &mut Func, halves: &mut Halves, inst: Inst) {
672 let data = func[inst];
673 let Extra::Mem(mem) = data.extra else { return };
674 let info = func[mem];
675 let args = func[data.args].to_vec();
676 let [value, into] = args[..] else { return };
677 let Some(&(low, high)) = halves.get(&value) else { return };
678 write(func, inst, low, into, word(info, 0), data.flags);
679 let up = stepped(func, inst, into);
680 write(func, inst, high, up, word(info, STEP), data.flags);
681 func.remove_inst(inst);
682}
683
684fn carried(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
694 let args = func[func[inst].args].to_vec();
695 let [a, b] = args[..] else { return };
696 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
697 return;
698 };
699 let low = ahead(func, inst, opcode, &[a_low, b_low]);
700 let carried = if opcode == Opcode::Add {
701 compared(func, inst, IntPred::Ult, low, a_low)
702 } else {
703 compared(func, inst, IntPred::Ult, a_low, b_low)
704 };
705 let carry = ahead(func, inst, Opcode::ZExt, &[carried]);
706 let high = ahead(func, inst, opcode, &[a_high, b_high]);
707 let high = ahead(func, inst, opcode, &[high, carry]);
708 replace(func, halves, inst, low, high);
709}
710
711fn multiply(func: &mut Func, halves: &mut Halves, inst: Inst) {
731 let args = func[func[inst].args].to_vec();
732 let [a, b] = args[..] else { return };
733 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
734 return;
735 };
736 let low = ahead(func, inst, Opcode::Mul, &[a_low, b_low]);
737 let carried = expand::high_half(func, inst, a_low, b_low, false, half());
738 let cross = ahead(func, inst, Opcode::Mul, &[a_low, b_high]);
739 let other = ahead(func, inst, Opcode::Mul, &[a_high, b_low]);
740 let high = ahead(func, inst, Opcode::Add, &[carried, cross]);
741 let high = ahead(func, inst, Opcode::Add, &[high, other]);
742 replace(func, halves, inst, low, high);
743}
744
745fn divide(
763 func: &mut Func,
764 names: &mut Interner,
765 abi: &'static AbiDescription,
766 halves: &mut Halves,
767 inst: Inst,
768 opcode: Opcode,
769) {
770 let args = func[func[inst].args].to_vec();
771 let [a, b] = args[..] else { return };
772 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
773 return;
774 };
775 let Some(routine) = capability::libcall(opcode, MODE) else { return };
779 let args = [Operand::Split(a_low, a_high), Operand::Split(b_low, b_high)];
780 let made = runtime(func, names, abi, inst, routine, &args, &[half(), half()]);
781 let [low, high] = made[..] else { return };
782 replace(func, halves, inst, low, high);
783}
784
785fn to_float(
795 func: &mut Func,
796 names: &mut Interner,
797 abi: &'static AbiDescription,
798 halves: &Halves,
799 forward: &mut Map<Value, Value>,
800 inst: Inst,
801 signed: bool,
802) {
803 let Some(&arg) = func[func[inst].args].first() else { return };
804 let Some(&(low, high)) = halves.get(&arg) else { return };
805 let (Some(result), Some(format)) = (func[inst].first_result, converted(func, inst)) else {
806 return;
807 };
808 let routine = going_up(signed, format);
809 let args = [Operand::Split(low, high)];
810 let made = runtime(func, names, abi, inst, routine, &args, &[func[result].ty]);
811 if let [answer] = made[..] {
812 forward.insert(result, answer);
813 }
814 func.remove_inst(inst);
815}
816
817fn from_float(
827 func: &mut Func,
828 names: &mut Interner,
829 abi: &'static AbiDescription,
830 halves: &mut Halves,
831 inst: Inst,
832 signed: bool,
833) {
834 let Some(&arg) = func[func[inst].args].first() else { return };
835 let Some(format) = converted(func, inst) else { return };
836 let routine = coming_down(signed, format);
837 let args = [Operand::Whole(arg)];
838 let made = runtime(func, names, abi, inst, routine, &args, &[half(), half()]);
839 let [low, high] = made[..] else { return };
840 replace(func, halves, inst, low, high);
841}
842
843fn going_up(signed: bool, format: Float) -> &'static str {
849 let mode = match format {
850 Float::F32 => "i128.f32",
851 Float::F64 => "i128.f64",
852 _ => "i128.f128",
853 };
854 routine(if signed { Opcode::SIToFP } else { Opcode::UIToFP }, mode)
855}
856
857fn coming_down(signed: bool, format: Float) -> &'static str {
859 let mode = match format {
860 Float::F32 => "f32.i128",
861 Float::F64 => "f64.i128",
862 _ => "f128.i128",
863 };
864 routine(if signed { Opcode::FPToSI } else { Opcode::FPToUI }, mode)
865}
866
867fn routine(opcode: Opcode, mode: &str) -> &'static str {
872 capability::libcall(opcode, mode)
873 .unwrap_or_else(|| panic!("no routine for `{}` at `{mode}`", opcode.name()))
874}
875
876#[derive(Clone, Copy)]
882enum Operand {
883 Whole(Value),
885 Split(Value, Value),
887}
888
889fn runtime(
914 func: &mut Func,
915 names: &mut Interner,
916 abi: &'static AbiDescription,
917 inst: Inst,
918 routine: &str,
919 args: &[Operand],
920 results: &[Type],
921) -> Vec<Value> {
922 let mut params: Vec<Param> = Vec::new();
923 let mut values: Vec<Value> = Vec::new();
924 let mut answer = Answer::Registers;
925 match *results {
927 [ty] if abi.scalar_is_by_reference(bytes(ty)) => {
928 let size = bytes(ty);
929 let align = align(size);
930 let slot = room(func, inst, size, align);
931 params.push(Param::with_abi(Type::PTR, Abi::Sret { size, align }));
932 values.push(slot);
933 answer = Answer::Slot(slot, ty);
934 }
935 [low, high] if low == half() && high == half() => {
936 if let Some(format) = packed(abi) {
937 answer = Answer::Packed(format);
938 }
939 }
940 _ => {}
941 }
942 for &arg in args {
943 handed(func, abi, inst, arg, &mut params, &mut values);
944 }
945 let answers = match answer {
946 Answer::Registers => results.to_vec(),
947 Answer::Slot(..) => Vec::new(),
948 Answer::Packed(format) => vec![Type::float(format)],
949 };
950 let returns = answers.iter().map(|&ty| Param::new(ty)).collect();
951 let signature = func.add_signature(Signature { params, returns, ..Signature::new() });
953 let callee = Some(names.intern(routine));
954 let varargs = func.push_abis(&[]);
955 let extra = Extra::Call(func.add_call(CallInfo { callee, signature, varargs }));
956 let pushed = func.push_values(&values);
957 let span = func.span(inst);
958 let data = InstData { args: pushed, extra, ..InstData::new(Opcode::Call) };
959 let made = func.create_inst(data, &answers, span);
960 func.insert_before(made, inst);
961 match answer {
962 Answer::Registers => func[made].results().collect(),
963 Answer::Slot(slot, ty) => {
964 let size = bytes(ty);
965 let info = whole(size, align(size));
966 let extra = Extra::Mem(func.add_mem(info));
967 let args = func.push_values(&[slot]);
968 let data = InstData { args, extra, ..InstData::new(Opcode::Load) };
969 vec![written(func, inst, data, ty)]
970 }
971 Answer::Packed(format) => unpacked(func, inst, made, format),
972 }
973}
974
975#[derive(Clone, Copy)]
977enum Answer {
978 Registers,
981 Slot(Value, Type),
984 Packed(Float),
986}
987
988fn packed(abi: &'static AbiDescription) -> Option<Float> {
995 let format = abi.wide_integer_returns_in(u64::from(WIDE / 8))?;
996 Float::from_bits(format.width())
997}
998
999fn unpacked(func: &mut Func, inst: Inst, call: Inst, format: Float) -> Vec<Value> {
1007 let Some(value) = func[call].first_result else { return Vec::new() };
1008 let size = bytes(Type::float(format));
1009 let align = align(size);
1010 let slot = room(func, inst, size, align);
1011 let info = whole(size, align);
1012 write(func, inst, value, slot, info, Flags::NONE);
1013 let low = read(func, inst, slot, word(info, 0), Flags::NONE);
1014 let up = stepped(func, inst, slot);
1015 let high = read(func, inst, up, word(info, STEP), Flags::NONE);
1016 vec![low, high]
1017}
1018
1019fn handed(
1021 func: &mut Func,
1022 abi: &'static AbiDescription,
1023 inst: Inst,
1024 arg: Operand,
1025 params: &mut Vec<Param>,
1026 values: &mut Vec<Value>,
1027) {
1028 match arg {
1029 Operand::Whole(value) => {
1030 let ty = func[value].ty;
1031 let size = bytes(ty);
1032 if !abi.scalar_is_by_reference(size) {
1033 params.push(Param::new(ty));
1034 values.push(value);
1035 return;
1036 }
1037 let align = align(size);
1038 let slot = room(func, inst, size, align);
1039 write(func, inst, value, slot, whole(size, align), Flags::NONE);
1040 params.push(Param::new(Type::PTR));
1041 values.push(slot);
1042 }
1043 Operand::Split(low, high) => {
1044 let size = u64::from(WIDE / 8);
1045 if !abi.scalar_is_by_reference(size) {
1046 params.push(Param::new(half()));
1047 values.push(low);
1048 params.push(Param::new(half()));
1049 values.push(high);
1050 return;
1051 }
1052 let align = align(size);
1053 let slot = room(func, inst, size, align);
1054 let info = whole(size, align);
1055 write(func, inst, low, slot, word(info, 0), Flags::NONE);
1056 let up = stepped(func, inst, slot);
1057 write(func, inst, high, up, word(info, STEP), Flags::NONE);
1058 params.push(Param::new(Type::PTR));
1059 values.push(slot);
1060 }
1061 }
1062}
1063
1064fn bytes(ty: Type) -> u64 {
1066 u64::from(ty.bits().div_ceil(8))
1067}
1068
1069fn align(size: u64) -> u32 {
1071 u32::try_from(size).unwrap_or(u32::MAX)
1072}
1073
1074fn whole(size: u64, align: u32) -> MemInfo {
1076 MemInfo {
1077 size,
1078 align,
1079 order: MemOrder::NotAtomic,
1080 tbaa: None,
1081 owns: 0,
1082 restrict: Restrict::NONE,
1083 }
1084}
1085
1086fn room(func: &mut Func, inst: Inst, size: u64, align: u32) -> Value {
1088 let extra = Extra::Mem(func.add_mem(whole(size, align)));
1089 written(func, inst, InstData { extra, ..InstData::new(Opcode::Alloca) }, Type::PTR)
1090}
1091
1092fn shifted(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
1112 let args = func[func[inst].args].to_vec();
1113 let [a, b] = args[..] else { return };
1114 let (Some(&(a_low, a_high)), Some(&(count, _))) = (halves.get(&a), halves.get(&b)) else {
1115 return;
1116 };
1117 let top = ahead_const(func, inst, i128::from(HALF - 1));
1118 let places = ahead(func, inst, Opcode::And, &[count, top]);
1119 let back = ahead(func, inst, Opcode::Sub, &[top, places]);
1120 let one = ahead_const(func, inst, 1);
1121 let zero = ahead_const(func, inst, 0);
1122 let bit = ahead_const(func, inst, i128::from(HALF));
1123 let reach = ahead(func, inst, Opcode::And, &[count, bit]);
1124 let whole = compared(func, inst, IntPred::Ne, reach, zero);
1125
1126 let (low, high) = if opcode == Opcode::Shl {
1127 let moved = ahead(func, inst, Opcode::Shl, &[a_low, places]);
1128 let edge = ahead(func, inst, Opcode::LShr, &[a_low, one]);
1129 let across = ahead(func, inst, Opcode::LShr, &[edge, back]);
1130 let above = ahead(func, inst, Opcode::Shl, &[a_high, places]);
1131 let joined = ahead(func, inst, Opcode::Or, &[above, across]);
1132 let low = ahead(func, inst, Opcode::Select, &[whole, zero, moved]);
1133 let high = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
1134 (low, high)
1135 } else {
1136 let moved = ahead(func, inst, opcode, &[a_high, places]);
1137 let edge = ahead(func, inst, Opcode::Shl, &[a_high, one]);
1138 let across = ahead(func, inst, Opcode::Shl, &[edge, back]);
1139 let below = ahead(func, inst, Opcode::LShr, &[a_low, places]);
1140 let joined = ahead(func, inst, Opcode::Or, &[below, across]);
1141 let spent = if opcode == Opcode::AShr {
1144 ahead(func, inst, Opcode::AShr, &[a_high, top])
1145 } else {
1146 zero
1147 };
1148 let low = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
1149 let high = ahead(func, inst, Opcode::Select, &[whole, spent, moved]);
1150 (low, high)
1151 };
1152 replace(func, halves, inst, low, high);
1153}
1154
1155fn bitwise(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
1158 let args = func[func[inst].args].to_vec();
1159 let [a, b] = args[..] else { return };
1160 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
1161 return;
1162 };
1163 let low = ahead(func, inst, opcode, &[a_low, b_low]);
1164 let high = ahead(func, inst, opcode, &[a_high, b_high]);
1165 replace(func, halves, inst, low, high);
1166}
1167
1168fn compare(func: &mut Func, halves: &Halves, forward: &mut Map<Value, Value>, inst: Inst) {
1183 let Extra::IntPred(pred) = func[inst].extra else { return };
1184 let args = func[func[inst].args].to_vec();
1185 let [a, b] = args[..] else { return };
1186 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
1187 return;
1188 };
1189 let answer = if matches!(pred, IntPred::Eq | IntPred::Ne) {
1190 let low = ahead(func, inst, Opcode::Xor, &[a_low, b_low]);
1191 let high = ahead(func, inst, Opcode::Xor, &[a_high, b_high]);
1192 let both = ahead(func, inst, Opcode::Or, &[low, high]);
1193 let zero = ahead_const(func, inst, 0);
1194 compared(func, inst, pred, both, zero)
1195 } else {
1196 let above = compared(func, inst, strict(pred), a_high, b_high);
1197 let below = compared(func, inst, unsigned(pred), a_low, b_low);
1198 let same = compared(func, inst, IntPred::Eq, a_high, b_high);
1199 let tail = bit(func, inst, Opcode::And, same, below);
1200 bit(func, inst, Opcode::Or, above, tail)
1201 };
1202 if let Some(result) = func[inst].first_result {
1203 forward.insert(result, answer);
1204 }
1205 func.remove_inst(inst);
1206}
1207
1208fn strict(pred: IntPred) -> IntPred {
1210 match pred {
1211 IntPred::Sle => IntPred::Slt,
1212 IntPred::Sge => IntPred::Sgt,
1213 IntPred::Ule => IntPred::Ult,
1214 IntPred::Uge => IntPred::Ugt,
1215 other => other,
1216 }
1217}
1218
1219fn unsigned(pred: IntPred) -> IntPred {
1221 match pred {
1222 IntPred::Slt => IntPred::Ult,
1223 IntPred::Sle => IntPred::Ule,
1224 IntPred::Sgt => IntPred::Ugt,
1225 IntPred::Sge => IntPred::Uge,
1226 other => other,
1227 }
1228}
1229
1230fn choose(func: &mut Func, halves: &mut Halves, inst: Inst) {
1236 let args = func[func[inst].args].to_vec();
1237 let [cond, then, other] = args[..] else { return };
1238 let (Some(&(then_low, then_high)), Some(&(other_low, other_high))) =
1239 (halves.get(&then), halves.get(&other))
1240 else {
1241 return;
1242 };
1243 let low = ahead(func, inst, Opcode::Select, &[cond, then_low, other_low]);
1244 let high = ahead(func, inst, Opcode::Select, &[cond, then_high, other_high]);
1245 replace(func, halves, inst, low, high);
1246}
1247
1248fn truncate(func: &mut Func, halves: &Halves, forward: &mut Map<Value, Value>, inst: Inst) {
1254 let Some(&arg) = func[func[inst].args].first() else { return };
1255 let Some(&(low, _)) = halves.get(&arg) else { return };
1256 let Some(result) = func[inst].first_result else { return };
1257 if func[result].ty.bits() == HALF {
1258 forward.insert(result, low);
1259 func.remove_inst(inst);
1260 return;
1261 }
1262 becomes(func, inst, Opcode::Trunc, &[low]);
1263}
1264
1265fn extend(func: &mut Func, halves: &mut Halves, inst: Inst, signed: bool) {
1267 let Some(&arg) = func[func[inst].args].first() else { return };
1268 let low = if func[arg].ty.bits() == HALF {
1269 arg
1270 } else {
1271 let opcode = if signed { Opcode::SExt } else { Opcode::ZExt };
1272 ahead(func, inst, opcode, &[arg])
1273 };
1274 let high = if signed {
1275 let top = ahead_const(func, inst, i128::from(HALF - 1));
1276 ahead(func, inst, Opcode::AShr, &[low, top])
1277 } else {
1278 ahead_const(func, inst, 0)
1279 };
1280 replace(func, halves, inst, low, high);
1281}
1282
1283fn call(
1290 func: &mut Func,
1291 conv: &CallRegs,
1292 halves: &mut Halves,
1293 forward: &mut Map<Value, Value>,
1294 inst: Inst,
1295) {
1296 let data = func[inst];
1297 let Extra::Call(info) = data.extra else { return };
1298 let info = func[info];
1299 let Some((whole, variadic)) = site(func, inst) else { return };
1300 let old = func[data.args].to_vec();
1301 let skip = usize::from(data.opcode == Opcode::CallIndirect);
1303 let Some(slots) = plan(&whole, conv) else { return };
1304 let mut args = spread(&old[..skip], halves);
1305 for slot in slots.iter().copied() {
1306 let value = match slot {
1307 Slot::Whole(index) => old[skip + index],
1308 Slot::Low(index) => halves[&old[skip + index]].0,
1309 Slot::High(index) => halves[&old[skip + index]].1,
1310 Slot::Filler(ty) if ty.is_float() => {
1311 let extra = Extra::Imm(func.add_imm(Imm::from_bits(0)));
1312 written(func, inst, InstData { extra, ..InstData::new(Opcode::FConst) }, ty)
1313 }
1314 Slot::Filler(_) => ahead_const(func, inst, 0),
1315 };
1316 args.push(value);
1317 }
1318 let results: Vec<Type> = data
1319 .results()
1320 .map(|value| func[value].ty)
1321 .flat_map(|ty| if is_wide(ty) { vec![half(), half()] } else { vec![ty] })
1322 .collect();
1323 let signature = func.add_signature(planned(&Signature { variadic, ..whole }, &slots));
1324 let varargs = if variadic { func.push_abis(&[]) } else { info.varargs };
1327 let extra = Extra::Call(func.add_call(CallInfo { signature, varargs, ..info }));
1328 let args = func.push_values(&args);
1329 let span = func.span(inst);
1330 let made = func.create_inst(InstData { args, extra, ..data }, &results, span);
1331 func.insert_before(made, inst);
1332 let mut fresh = func[made].results();
1333 for old in data.results() {
1334 if is_wide(func[old].ty) {
1335 let (Some(low), Some(high)) = (fresh.next(), fresh.next()) else { return };
1336 halves.insert(old, (low, high));
1337 } else if let Some(again) = fresh.next() {
1338 forward.insert(old, again);
1339 }
1340 }
1341 func.remove_inst(inst);
1342}
1343
1344fn site(func: &Func, inst: Inst) -> Option<(Signature, bool)> {
1355 let data = func[inst];
1356 let Extra::Call(info) = data.extra else { return None };
1357 let info = func[info];
1358 let mut whole = func[info.signature].clone();
1359 let skip = usize::from(data.opcode == Opcode::CallIndirect);
1360 let args = &func[data.args];
1361 let named = skip + whole.params.len();
1362 if args.len() < named {
1363 return None;
1364 }
1365 let beyond = &func[info.varargs];
1366 for (index, &value) in args[named..].iter().enumerate() {
1367 let abi = beyond.get(index).copied().unwrap_or_default();
1368 whole.params.push(Param { ty: func[value].ty, abi });
1369 }
1370 let variadic = whole.variadic;
1371 whole.variadic = false;
1372 Some((whole, variadic))
1373}
1374
1375fn flatten(func: &mut Func, halves: &Halves, inst: Inst) {
1377 let args = spread(&func[func[inst].args], halves);
1378 func[inst].args = func.push_values(&args);
1379}
1380
1381fn edges(func: &mut Func, halves: &Halves, inst: Inst) {
1383 for at in func.target_list(inst).iter() {
1384 let call = func[at];
1385 let args = func[call.args].to_vec();
1386 if !args.iter().any(|value| halves.contains_key(value)) {
1387 continue;
1388 }
1389 let args = func.push_values(&spread(&args, halves));
1390 func.set_block_call(at, BlockCall { args, ..call });
1391 }
1392}
1393
1394fn spread(args: &[Value], halves: &Halves) -> Vec<Value> {
1396 args.iter()
1397 .flat_map(|value| match halves.get(value) {
1398 Some(&(low, high)) => vec![low, high],
1399 None => vec![*value],
1400 })
1401 .collect()
1402}
1403
1404fn split_signature(signature: &Signature) -> Signature {
1410 let split = |params: &[Param]| -> Vec<Param> {
1411 params
1412 .iter()
1413 .flat_map(|param| {
1414 if is_wide(param.ty) {
1415 vec![Param::new(half()), Param::new(half())]
1416 } else {
1417 vec![*param]
1418 }
1419 })
1420 .collect()
1421 };
1422 Signature {
1423 params: split(&signature.params),
1424 returns: split(&signature.returns),
1425 variadic: signature.variadic,
1426 convention: signature.convention,
1427 }
1428}
1429
1430fn replace(func: &mut Func, halves: &mut Halves, inst: Inst, low: Value, high: Value) {
1432 if let Some(result) = func[inst].first_result {
1433 halves.insert(result, (low, high));
1434 }
1435 func.remove_inst(inst);
1436}
1437
1438fn substitute(func: &mut Func, forward: &Map<Value, Value>) {
1444 if forward.is_empty() {
1445 return;
1446 }
1447 let with = |value: Value| forward.get(&value).copied().unwrap_or(value);
1448 for block in func.blocks().collect::<Vec<_>>() {
1449 for inst in func.insts(block).collect::<Vec<Inst>>() {
1450 let args = func[inst].args;
1451 func.rewrite(args, with);
1452 for call in func.successors(inst).collect::<Vec<_>>() {
1453 func.rewrite(call.args, with);
1454 }
1455 }
1456 }
1457}
1458
1459fn word(info: MemInfo, at: u64) -> MemInfo {
1461 let align = if at == 0 { info.align } else { info.align.min(8) };
1462 MemInfo { size: STEP, align, ..info }
1463}
1464
1465fn stepped(func: &mut Func, inst: Inst, from: Value) -> Value {
1467 let step = ahead_const(func, inst, i128::from(STEP));
1468 let args = func.push_values(&[from, step]);
1469 written(func, inst, InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR)
1470}
1471
1472fn read(func: &mut Func, inst: Inst, from: Value, info: MemInfo, flags: Flags) -> Value {
1474 let extra = Extra::Mem(func.add_mem(info));
1475 let args = func.push_values(&[from]);
1476 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Load) };
1477 written(func, inst, data, half())
1478}
1479
1480fn write(func: &mut Func, inst: Inst, value: Value, into: Value, info: MemInfo, flags: Flags) {
1482 let span = func.span(inst);
1483 let extra = Extra::Mem(func.add_mem(info));
1484 let args = func.push_values(&[value, into]);
1485 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Store) };
1486 let made = func.create_inst(data, &[], span);
1487 func.insert_before(made, inst);
1488}
1489
1490fn compared(func: &mut Func, inst: Inst, pred: IntPred, lhs: Value, rhs: Value) -> Value {
1493 let args = func.push_values(&[lhs, rhs]);
1494 let extra = Extra::IntPred(pred);
1495 written(func, inst, InstData { args, extra, ..InstData::new(Opcode::ICmp) }, Type::I1)
1496}
1497
1498fn bit(func: &mut Func, inst: Inst, opcode: Opcode, lhs: Value, rhs: Value) -> Value {
1500 let args = func.push_values(&[lhs, rhs]);
1501 written(func, inst, InstData { args, ..InstData::new(opcode) }, Type::I1)
1502}
1503
1504fn ahead(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) -> Value {
1506 let args = func.push_values(args);
1507 written(func, inst, InstData { args, ..InstData::new(opcode) }, half())
1508}
1509
1510fn ahead_const(func: &mut Func, inst: Inst, value: i128) -> Value {
1512 let extra = Extra::Imm(func.add_imm(Imm::int(value, half())));
1513 written(func, inst, InstData { extra, ..InstData::new(Opcode::IConst) }, half())
1514}
1515
1516fn written(func: &mut Func, inst: Inst, data: InstData, ty: Type) -> Value {
1518 let span = func.span(inst);
1519 let made = func.create_inst(data, &[ty], span);
1520 func.insert_before(made, inst);
1521 func[made].first_result.expect("an instruction created with one result has one")
1522}
1523
1524fn becomes(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) {
1526 let args = func.push_values(args);
1527 let data = &mut func[inst];
1528 data.opcode = opcode;
1529 data.args = args;
1530 data.extra = Extra::None;
1531 data.flags = data.flags.intersection(Flags::legal_on(opcode));
1532}
1533
1534#[cfg(test)]
1535mod tests {
1536 use rucc_base::Interner;
1537 use rucc_ir::{
1538 Abi, Block, Builder, Flags, Float, Func, MemOrder, Module, Restrict, Signature, Type, Value,
1539 };
1540 use rucc_target::x86_64::{MINGW64, SYSV};
1541 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1542
1543 use super::{Def, Extra, HALF, IntPred, MemInfo, Opcode, halves};
1544
1545 fn wide() -> Type {
1547 Type::int(super::WIDE)
1548 }
1549
1550 fn target() -> TargetInfo {
1551 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1552 }
1553
1554 fn printed(func: &Func, names: &mut Interner) -> String {
1555 let module = Module::new(names.intern("w.c"), &target());
1556 rucc_ir::print_func(&module, func, names)
1557 }
1558
1559 fn shell(names: &mut Interner, params: &[Type], returns: &[Type]) -> (Func, Block, Vec<Value>) {
1561 let signature = Signature::new().with_params(params).with_returns(returns);
1562 let mut func = Func::new(names.intern("f"), signature);
1563 let entry = func.create_block();
1564 let values = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
1565 (func, entry, values)
1566 }
1567
1568 fn info(size: u64, align: u32) -> MemInfo {
1570 MemInfo {
1571 size,
1572 align,
1573 order: MemOrder::NotAtomic,
1574 tbaa: None,
1575 owns: 0,
1576 restrict: Restrict::NONE,
1577 }
1578 }
1579
1580 #[test]
1581 fn an_add_carries_from_the_low_half_into_the_high_one() {
1582 let mut names = Interner::new();
1583 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1584 let mut build = Builder::new(&mut func, entry);
1585 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1586 build.ret(&[sum]);
1587
1588 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1589 let text = printed(&func, &mut names);
1590 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1591 assert_eq!(text.matches(" = add ").count(), 3, "three adds: {text}");
1594 assert_eq!(text.matches("icmp ult").count(), 1, "one carry: {text}");
1595 assert_eq!(text.matches(" = zext.i64 ").count(), 1, "the carry as a number: {text}");
1596 }
1597
1598 #[test]
1599 fn a_subtract_borrows_the_other_way_round() {
1600 let mut names = Interner::new();
1601 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1602 let mut build = Builder::new(&mut func, entry);
1603 let difference = build.binary(Opcode::Sub, params[0], params[1], Flags::NONE);
1604 build.ret(&[difference]);
1605
1606 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1607 let text = printed(&func, &mut names);
1608 assert_eq!(text.matches(" = sub ").count(), 3, "three subtracts: {text}");
1609 assert!(text.contains("icmp ult %0, %2"), "the operands are compared: {text}");
1612 }
1613
1614 #[test]
1615 fn the_signature_and_the_entry_block_say_the_same_thing() {
1616 let mut names = Interner::new();
1617 let (mut func, entry, params) = shell(&mut names, &[Type::int(32), wide()], &[wide()]);
1618 let mut build = Builder::new(&mut func, entry);
1619 build.ret(&[params[1]]);
1620
1621 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1622 assert_eq!(
1623 func.signature().param_types().collect::<Vec<_>>(),
1624 [Type::int(32), Type::int(HALF), Type::int(HALF)],
1625 "the wide parameter became two where it stood"
1626 );
1627 assert_eq!(
1628 func.signature().return_types().collect::<Vec<_>>(),
1629 [Type::int(HALF), Type::int(HALF)],
1630 "and so did what comes back"
1631 );
1632 let text = printed(&func, &mut names);
1633 assert!(text.contains("block0(%0: i32, %1: i64, %2: i64)"), "the block agrees: {text}");
1634 assert!(text.contains("return %1, %2"), "both halves go back: {text}");
1635 let _ = entry;
1636 }
1637
1638 #[test]
1639 fn a_read_takes_the_high_word_a_word_above_the_low_one() {
1640 let mut names = Interner::new();
1641 let (mut func, entry, params) = shell(&mut names, &[Type::PTR], &[wide()]);
1642 let mut build = Builder::new(&mut func, entry);
1643 let value = build.load(wide(), params[0], info(16, 16), Flags::NONE);
1644 build.ret(&[value]);
1645
1646 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1647 let text = printed(&func, &mut names);
1648 assert_eq!(text.matches(" = load.i64 ").count(), 2, "two reads: {text}");
1649 assert!(text.contains("ptr_add"), "the high word is a word up: {text}");
1650 assert!(text.contains("align 16"), "the low word keeps what the object had: {text}");
1653 assert!(text.contains("align 8"), "the high word knows less: {text}");
1654 }
1655
1656 #[test]
1657 fn an_equality_asks_once_about_both_halves() {
1658 let mut names = Interner::new();
1659 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1660 let mut build = Builder::new(&mut func, entry);
1661 let same = build.icmp(IntPred::Eq, params[0], params[1]);
1662 let answer = build.unary(Opcode::ZExt, same, Type::int(32));
1663 build.ret(&[answer]);
1664
1665 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1666 let text = printed(&func, &mut names);
1667 assert_eq!(text.matches("icmp").count(), 1, "one comparison: {text}");
1668 assert_eq!(text.matches(" = xor ").count(), 2, "the halves differ or they do not: {text}");
1669 }
1670
1671 #[test]
1672 fn an_ordering_reads_the_low_halves_without_a_sign() {
1673 let mut names = Interner::new();
1674 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1675 let mut build = Builder::new(&mut func, entry);
1676 let below = build.icmp(IntPred::Slt, params[0], params[1]);
1677 let answer = build.unary(Opcode::ZExt, below, Type::int(32));
1678 build.ret(&[answer]);
1679
1680 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1681 let text = printed(&func, &mut names);
1682 assert!(text.contains("icmp slt"), "the high halves keep the sign: {text}");
1683 assert!(text.contains("icmp ult"), "the low halves have none: {text}");
1684 assert!(
1685 text.contains("icmp eq"),
1686 "and the low halves only matter when the high tie: {text}"
1687 );
1688 }
1689
1690 #[test]
1697 fn an_ordering_that_allows_equality_asks_the_high_halves_a_strict_question() {
1698 let mut names = Interner::new();
1699 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1700 let mut build = Builder::new(&mut func, entry);
1701 let at_least = build.icmp(IntPred::Sge, params[0], params[1]);
1702 let answer = build.unary(Opcode::ZExt, at_least, Type::int(32));
1703 build.ret(&[answer]);
1704
1705 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1706 let text = printed(&func, &mut names);
1707 assert!(text.contains("icmp sgt"), "the high halves settle it outright: {text}");
1708 assert!(!text.contains("icmp sge"), "a tie in the high halves settles nothing: {text}");
1709 assert!(text.contains("icmp uge"), "the low halves are the ones allowed to tie: {text}");
1710 }
1711
1712 #[test]
1713 fn a_widening_puts_the_sign_of_the_value_in_the_high_half() {
1714 let mut names = Interner::new();
1715 let (mut func, entry, params) = shell(&mut names, &[Type::int(32)], &[wide()]);
1716 let mut build = Builder::new(&mut func, entry);
1717 let value = build.unary(Opcode::SExt, params[0], wide());
1718 build.ret(&[value]);
1719
1720 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1721 let text = printed(&func, &mut names);
1722 assert!(text.contains("sext.i64"), "the value fills the low half: {text}");
1723 assert!(text.contains("ashr"), "and its sign fills the high one: {text}");
1724 }
1725
1726 #[test]
1727 fn a_block_parameter_becomes_two_and_every_branch_passes_two() {
1728 let mut names = Interner::new();
1729 let (mut func, entry, params) = shell(&mut names, &[wide(), Type::int(32)], &[wide()]);
1730 let tail = func.create_block();
1731 let carried = func.append_param(tail, wide());
1732 let mut build = Builder::new(&mut func, entry);
1733 let zero = build.iconst(Type::int(32), 0);
1734 let taken = build.icmp(IntPred::Ne, params[1], zero);
1735 let other = build.iconst(wide(), 7);
1736 build.br_if(taken, tail, &[params[0]], tail, &[other]);
1737 let mut build = Builder::new(&mut func, tail);
1738 build.ret(&[carried]);
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("i128"), "nothing that wide is left: {text}");
1743 assert!(text.contains("block1(%7: i64, %8: i64)"), "the block takes two: {text}");
1744 assert_eq!(text.matches("block1(").count(), 3, "and both edges pass two: {text}");
1745 }
1746
1747 #[test]
1754 fn a_multiply_is_three_multiplies_and_the_carry_out_of_the_low_ones() {
1755 let mut names = Interner::new();
1756 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1757 let mut build = Builder::new(&mut func, entry);
1758 let product = build.binary(Opcode::Mul, params[0], params[1], Flags::NONE);
1759 build.ret(&[product]);
1760
1761 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1762 let text = printed(&func, &mut names);
1763 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1764 assert_eq!(text.matches(" = mul ").count(), 7, "three and the carry's four: {text}");
1765 }
1766
1767 #[test]
1774 fn each_of_the_four_divisions_calls_the_routine_of_that_name() {
1775 for (opcode, routine) in [
1776 (Opcode::UDiv, "__udivti3"),
1777 (Opcode::SDiv, "__divti3"),
1778 (Opcode::URem, "__umodti3"),
1779 (Opcode::SRem, "__modti3"),
1780 ] {
1781 let mut names = Interner::new();
1782 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1783 let mut build = Builder::new(&mut func, entry);
1784 let answer = build.binary(opcode, params[0], params[1], Flags::NONE);
1785 build.ret(&[answer]);
1786
1787 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1788 let text = printed(&func, &mut names);
1789 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1790 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1791 }
1792 }
1793
1794 #[test]
1800 fn a_divide_hands_over_four_halves_and_takes_two_back() {
1801 let mut names = Interner::new();
1802 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1803 let mut build = Builder::new(&mut func, entry);
1804 let quotient = build.binary(Opcode::UDiv, params[0], params[1], Flags::NONE);
1805 build.ret(&[quotient]);
1806
1807 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1808 let text = printed(&func, &mut names);
1809 assert!(text.contains("@__udivti3(%0, %1, %2, %3)"), "four halves go over: {text}");
1810 assert!(text.contains("return %4, %5"), "and two come back: {text}");
1811 }
1812
1813 #[test]
1819 fn a_divide_of_something_computed_calls_with_the_halves_of_it() {
1820 let mut names = Interner::new();
1821 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1822 let mut build = Builder::new(&mut func, entry);
1823 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1824 let quotient = build.binary(Opcode::SDiv, sum, params[1], Flags::NONE);
1825 build.ret(&[quotient]);
1826
1827 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1828 let text = printed(&func, &mut names);
1829 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1830 assert_eq!(text.matches(" = add ").count(), 3, "the sum is still a sum: {text}");
1831 assert_eq!(text.matches("call @__divti3").count(), 1, "one call: {text}");
1832 }
1833
1834 #[test]
1840 fn each_conversion_between_this_width_and_a_float_calls_the_routine_of_that_name() {
1841 let double = Type::float(Float::F64);
1842 let single = Type::float(Float::F32);
1843 let quad = Type::float(Float::F128);
1844 for (opcode, float, routine) in [
1845 (Opcode::SIToFP, double, "__floattidf"),
1846 (Opcode::SIToFP, single, "__floattisf"),
1847 (Opcode::UIToFP, double, "__floatuntidf"),
1848 (Opcode::UIToFP, single, "__floatuntisf"),
1849 (Opcode::SIToFP, quad, "__floattitf"),
1850 (Opcode::UIToFP, quad, "__floatuntitf"),
1851 ] {
1852 let mut names = Interner::new();
1853 let (mut func, entry, params) = shell(&mut names, &[wide()], &[float]);
1854 let mut build = Builder::new(&mut func, entry);
1855 let answer = build.unary(opcode, params[0], float);
1856 build.ret(&[answer]);
1857
1858 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1859 let text = printed(&func, &mut names);
1860 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1861 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1862 }
1863 for (opcode, float, routine) in [
1864 (Opcode::FPToSI, double, "__fixdfti"),
1865 (Opcode::FPToSI, single, "__fixsfti"),
1866 (Opcode::FPToUI, double, "__fixunsdfti"),
1867 (Opcode::FPToUI, single, "__fixunssfti"),
1868 (Opcode::FPToSI, quad, "__fixtfti"),
1869 (Opcode::FPToUI, quad, "__fixunstfti"),
1870 ] {
1871 let mut names = Interner::new();
1872 let (mut func, entry, params) = shell(&mut names, &[float], &[wide()]);
1873 let mut build = Builder::new(&mut func, entry);
1874 let answer = build.unary(opcode, params[0], wide());
1875 build.ret(&[answer]);
1876
1877 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1878 let text = printed(&func, &mut names);
1879 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1880 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1881 }
1882 }
1883
1884 #[test]
1890 fn a_conversion_hands_over_halves_one_way_and_takes_them_back_the_other() {
1891 let double = Type::float(Float::F64);
1892 let mut names = Interner::new();
1893 let (mut func, entry, params) = shell(&mut names, &[wide()], &[double]);
1894 let mut build = Builder::new(&mut func, entry);
1895 let answer = build.unary(Opcode::SIToFP, params[0], double);
1896 build.ret(&[answer]);
1897
1898 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1899 let text = printed(&func, &mut names);
1900 assert!(text.contains("@__floattidf(%0, %1)"), "two halves go over: {text}");
1901 assert!(text.contains("return %2"), "and one float comes back: {text}");
1902
1903 let mut names = Interner::new();
1904 let (mut func, entry, params) = shell(&mut names, &[double], &[wide()]);
1905 let mut build = Builder::new(&mut func, entry);
1906 let answer = build.unary(Opcode::FPToSI, params[0], wide());
1907 build.ret(&[answer]);
1908
1909 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1910 let text = printed(&func, &mut names);
1911 assert!(text.contains("@__fixdfti(%0)"), "the float goes over as it is: {text}");
1912 assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1913 }
1914
1915 #[test]
1922 fn a_conversion_against_a_quad_hands_over_the_pair_and_the_quad_whole() {
1923 let quad = Type::float(Float::F128);
1924 let mut names = Interner::new();
1925 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
1926 let mut build = Builder::new(&mut func, entry);
1927 let answer = build.unary(Opcode::UIToFP, params[0], quad);
1928 build.ret(&[answer]);
1929
1930 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1931 let text = printed(&func, &mut names);
1932 assert!(text.contains("@__floatuntitf(%0, %1)"), "two halves go over: {text}");
1933 assert!(text.contains("return %2"), "and one quad comes back: {text}");
1934
1935 let mut names = Interner::new();
1936 let (mut func, entry, params) = shell(&mut names, &[quad], &[wide()]);
1937 let mut build = Builder::new(&mut func, entry);
1938 let answer = build.unary(Opcode::FPToSI, params[0], wide());
1939 build.ret(&[answer]);
1940
1941 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1942 let text = printed(&func, &mut names);
1943 assert!(text.contains("@__fixtfti(%0)"), "the quad goes over as it is: {text}");
1944 assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1945 }
1946
1947 #[test]
1954 fn a_conversion_at_a_width_the_runtime_has_no_routine_for_is_left_alone() {
1955 let long = Type::float(Float::F80);
1956 let mut names = Interner::new();
1957 let (mut func, entry, params) = shell(&mut names, &[wide()], &[long]);
1958 let mut build = Builder::new(&mut func, entry);
1959 let answer = build.unary(Opcode::SIToFP, params[0], long);
1960 build.ret(&[answer]);
1961
1962 assert!(!halves(&mut func, &mut names, &SYSV), "the pass does not understand this one");
1963 let text = printed(&func, &mut names);
1964 assert!(text.contains("i128"), "the width is still there: {text}");
1965 }
1966
1967 #[test]
1974 fn a_shift_left_chooses_between_a_count_that_crossed_a_half_and_one_that_did_not() {
1975 let mut names = Interner::new();
1976 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1977 let mut build = Builder::new(&mut func, entry);
1978 let moved = build.binary(Opcode::Shl, params[0], params[1], Flags::NONE);
1979 build.ret(&[moved]);
1980
1981 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1982 let text = printed(&func, &mut names);
1983 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1984 assert_eq!(
1985 text.matches(" = shl ").count(),
1986 2,
1987 "one per half, and the far case reuses one: {text}"
1988 );
1989 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1990 assert_eq!(text.matches(" = lshr ").count(), 2, "the crossing bits, in two steps: {text}");
1991 }
1992
1993 #[test]
2000 fn the_bits_that_cross_move_one_place_and_then_the_rest_of_the_way() {
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::LShr, 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("iconst.i64 63"), "sixty three is the distance left: {text}");
2010 assert!(text.contains("iconst.i64 1"), "after the one place that comes first: {text}");
2011 assert!(text.contains(" = sub "), "the rest of the way is worked out: {text}");
2012 assert!(
2013 !text.contains("iconst.i64 127"),
2014 "and the count is not masked to the width: {text}"
2015 );
2016 }
2017
2018 #[test]
2024 fn an_arithmetic_shift_right_leaves_the_sign_bit_where_a_logical_one_leaves_zeroes() {
2025 let mut names = Interner::new();
2026 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
2027 let mut build = Builder::new(&mut func, entry);
2028 let moved = build.binary(Opcode::AShr, params[0], params[1], Flags::NONE);
2029 build.ret(&[moved]);
2030
2031 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2032 let text = printed(&func, &mut names);
2033 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
2034 assert_eq!(text.matches(" = ashr ").count(), 2, "the count and the sign: {text}");
2036 assert_eq!(text.matches(" = lshr ").count(), 1, "the low half is not signed: {text}");
2037 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
2038 }
2039
2040 fn arrived(params: &[Type], wide_at: usize) -> (Signature, usize) {
2046 let mut names = Interner::new();
2047 let word = Type::int(HALF);
2048 let (mut func, entry, values) = shell(&mut names, params, &[word]);
2049 let mut build = Builder::new(&mut func, entry);
2050 let low = build.unary(Opcode::Trunc, values[wide_at], word);
2051 build.ret(&[low]);
2052
2053 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2054 let ret = func.insts(entry).last().expect("the block ends in a return");
2055 let read = func[func[ret].args][0];
2056 let at = func[entry].params.iter().position(|&value| value == read);
2057 (func.signature().clone(), at.expect("the low half is a parameter"))
2058 }
2059
2060 fn types(signature: &Signature) -> Vec<Type> {
2061 signature.params.iter().map(|param| param.ty).collect()
2062 }
2063
2064 #[test]
2065 fn a_parameter_with_one_register_left_goes_in_memory_and_the_register_stays_empty() {
2066 let word = Type::int(HALF);
2067 let (signature, low) = arrived(&[word, word, word, word, word, wide()], 5);
2068 assert_eq!(types(&signature), vec![word; 8], "five, a filler and two halves");
2069 assert_eq!(low, 6, "the halves come after the register the value skipped");
2070 }
2071
2072 #[test]
2073 fn the_register_a_wide_parameter_skipped_goes_to_the_parameter_after_it() {
2074 let word = Type::int(HALF);
2075 let (signature, low) = arrived(&[word, word, word, word, word, wide(), word], 5);
2076 assert_eq!(types(&signature), vec![word; 8], "six registers and two words, no filler");
2077 assert_eq!(low, 6, "the word after the wide value took the sixth register");
2078 }
2079
2080 #[test]
2081 fn a_wide_parameter_in_memory_starts_on_a_sixteen_byte_boundary() {
2082 let word = Type::int(HALF);
2083 let params = [word, word, word, word, word, word, word, wide()];
2084 let (signature, low) = arrived(¶ms, 7);
2085 assert_eq!(types(&signature), vec![word; 10], "the seventh word, a filler, the halves");
2086 assert_eq!(low, 8, "the filler takes the word the alignment leaves empty");
2087 }
2088
2089 #[test]
2091 fn a_call_passes_a_wide_argument_in_memory_the_way_the_callee_reads_it() {
2092 let mut names = Interner::new();
2093 let word = Type::int(HALF);
2094 let (mut func, entry, _) = shell(&mut names, &[], &[]);
2095 let callee = names.intern("g");
2096 let params = [word, word, word, word, word, wide()];
2097 let signature = func.add_signature(Signature::new().with_params(¶ms));
2098 let mut build = Builder::new(&mut func, entry);
2099 let one = build.iconst(word, 1);
2100 let big = build.iconst(wide(), 4);
2101 build.call(callee, signature, &[one, one, one, one, one, big]);
2102 build.ret(&[]);
2103
2104 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2105 let call = func.insts(entry).find(|&inst| func[inst].opcode == Opcode::Call);
2106 let call = call.expect("the call is still there");
2107 let Extra::Call(info) = func[call].extra else { unreachable!("a call has call info") };
2108 let passed = func[func[call].args].to_vec();
2109 assert_eq!(types(&func[func[info].signature]), vec![word; 8], "as the callee has it");
2110 assert_eq!(passed.len(), 8, "one argument for each parameter");
2111 assert_eq!(passed[..5], [one; 5], "the words keep their registers");
2112 let constant = |value: Value| {
2113 let Def::Result { inst, .. } = func[value].def else { return None };
2114 let Extra::Imm(imm) = func[inst].extra else { return None };
2115 Some(func[imm].unsigned())
2116 };
2117 assert_eq!(constant(passed[6]), Some(4), "the low half is the first word in memory");
2118 assert_eq!(constant(passed[7]), Some(0), "and the high half the second");
2119 }
2120
2121 #[test]
2124 fn a_variadic_call_with_a_wide_argument_in_memory_leaves_the_function_alone() {
2125 let mut names = Interner::new();
2126 let word = Type::int(HALF);
2127 let (mut func, entry, _) = shell(&mut names, &[], &[]);
2128 let callee = names.intern("g");
2129 let params = [word, word, word, word, word, wide()];
2130 let signature = Signature { variadic: true, ..Signature::new().with_params(¶ms) };
2131 let signature = func.add_signature(signature);
2132 let mut build = Builder::new(&mut func, entry);
2133 let one = build.iconst(word, 1);
2134 let big = build.iconst(wide(), 4);
2135 build.call(callee, signature, &[one, one, one, one, one, big]);
2136 build.ret(&[]);
2137 let before = printed(&func, &mut names);
2138
2139 assert!(!halves(&mut func, &mut names, &SYSV), "the named parameters cannot move");
2140 assert_eq!(printed(&func, &mut names), before, "so nothing moved");
2141 }
2142
2143 #[test]
2147 fn a_wide_argument_past_the_dots_goes_where_a_named_one_would() {
2148 let mut names = Interner::new();
2149 let word = Type::int(HALF);
2150 let (mut func, entry, _) = shell(&mut names, &[], &[]);
2151 let callee = names.intern("g");
2152 let params = [word, word, word, word, word];
2153 let signature = Signature { variadic: true, ..Signature::new().with_params(¶ms) };
2154 let signature = func.add_signature(signature);
2155 let mut build = Builder::new(&mut func, entry);
2156 let one = build.iconst(word, 1);
2157 let big = build.iconst(wide(), 4);
2158 build.call_varargs(callee, signature, &[one, one, one, one, one, big], &[Abi::Plain]);
2159 build.ret(&[]);
2160
2161 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2162 let call = func.insts(entry).find(|&inst| func[inst].opcode == Opcode::Call);
2163 let call = call.expect("the call is still there");
2164 let Extra::Call(info) = func[call].extra else { unreachable!("a call has call info") };
2165 let made = &func[func[info].signature];
2166 assert!(made.variadic, "the callee is still variadic");
2167 assert_eq!(types(made), vec![word; 8], "five words, a filler and the two halves");
2168 assert!(func[func[info].varargs].is_empty(), "every argument is named now");
2169 assert_eq!(func[func[call].args].len(), 8, "one argument for each parameter");
2170 }
2171
2172 #[test]
2175 fn a_wide_argument_past_the_dots_on_windows_leaves_the_function_alone() {
2176 let mut names = Interner::new();
2177 let word = Type::int(HALF);
2178 let (mut func, entry, _) = shell(&mut names, &[], &[]);
2179 let callee = names.intern("g");
2180 let signature = Signature { variadic: true, ..Signature::new().with_params(&[word]) };
2181 let signature = func.add_signature(signature);
2182 let mut build = Builder::new(&mut func, entry);
2183 let one = build.iconst(word, 1);
2184 let big = build.iconst(wide(), 4);
2185 build.call_varargs(callee, signature, &[one, big], &[Abi::Plain]);
2186 build.ret(&[]);
2187
2188 assert!(!halves(&mut func, &mut names, &MINGW64), "the call is left for a refusal");
2189 }
2190
2191 #[test]
2200 fn a_block_made_after_the_one_it_runs_before_is_still_split() {
2201 let mut names = Interner::new();
2202 let (mut func, entry, params) = shell(&mut names, &[wide()], &[wide()]);
2203 let tail = func.create_block();
2204 let middle = func.create_block();
2205 let mut build = Builder::new(&mut func, entry);
2206 build.jump(middle, &[]);
2207 let mut build = Builder::new(&mut func, middle);
2208 let doubled = build.binary(Opcode::Add, params[0], params[0], Flags::NONE);
2209 build.jump(tail, &[]);
2210 let mut build = Builder::new(&mut func, tail);
2211 let again = build.binary(Opcode::Add, doubled, doubled, Flags::NONE);
2212 build.ret(&[again]);
2213
2214 assert!(
2215 halves(&mut func, &mut names, &SYSV),
2216 "the definition runs before the use whatever the list says"
2217 );
2218 let text = printed(&func, &mut names);
2219 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
2220 }
2221
2222 #[test]
2223 fn a_function_with_nothing_that_wide_is_not_touched() {
2224 let mut names = Interner::new();
2225 let word = Type::int(HALF);
2226 let (mut func, entry, params) = shell(&mut names, &[word, word], &[word]);
2227 let mut build = Builder::new(&mut func, entry);
2228 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
2229 build.ret(&[sum]);
2230
2231 assert!(!halves(&mut func, &mut names, &SYSV), "there is nothing to split");
2232 }
2233
2234 #[test]
2241 fn on_windows_a_wide_operand_goes_over_as_the_address_of_a_copy() {
2242 let mut names = Interner::new();
2243 let quad = Type::float(Float::F64);
2244 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
2245 let mut build = Builder::new(&mut func, entry);
2246 let answer = build.unary(Opcode::SIToFP, params[0], quad);
2247 build.ret(&[answer]);
2248
2249 assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
2250 let text = printed(&func, &mut names);
2251 assert!(text.contains("call @__floattidf"), "{text}");
2252 assert_eq!(text.matches("alloca").count(), 1, "one slot: {text}");
2254 assert_eq!(text.matches("store").count(), 2, "a half at a time: {text}");
2255 assert_eq!(text.matches("ptr_add").count(), 1, "the high half eight bytes up: {text}");
2256 assert!(!text.contains("__floattidf(%0, %1)"), "not the two halves: {text}");
2257 }
2258
2259 #[test]
2261 fn on_windows_a_wide_answer_at_this_format_comes_back_through_a_slot() {
2262 let mut names = Interner::new();
2263 let quad = Type::float(Float::F128);
2264 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
2265 let mut build = Builder::new(&mut func, entry);
2266 let answer = build.unary(Opcode::SIToFP, params[0], quad);
2267 build.ret(&[answer]);
2268
2269 assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
2270 let text = printed(&func, &mut names);
2271 assert!(text.contains("call @__floattitf"), "{text}");
2272 assert_eq!(text.matches("alloca").count(), 2, "two slots: {text}");
2274 assert_eq!(text.matches(" = call").count(), 0, "the call answers nothing: {text}");
2275 assert_eq!(text.matches(" = load").count(), 1, "the answer is the load after it: {text}");
2276 }
2277
2278 #[test]
2280 fn the_convention_with_registers_for_both_halves_puts_nothing_on_the_frame() {
2281 let mut names = Interner::new();
2282 let double = Type::float(Float::F64);
2283 let (mut func, entry, params) = shell(&mut names, &[wide()], &[double]);
2284 let mut build = Builder::new(&mut func, entry);
2285 let answer = build.unary(Opcode::SIToFP, params[0], double);
2286 build.ret(&[answer]);
2287
2288 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2289 let text = printed(&func, &mut names);
2290 assert!(text.contains("@__floattidf(%0, %1)"), "both halves in registers: {text}");
2291 assert!(!text.contains("alloca"), "nothing goes through the frame: {text}");
2292 }
2293
2294 #[test]
2297 fn on_windows_a_wide_answer_comes_back_in_one_register_and_is_split_on_the_frame() {
2298 let mut names = Interner::new();
2299 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
2300 let mut build = Builder::new(&mut func, entry);
2301 let answer = build.binary(Opcode::SDiv, params[0], params[1], Flags::NONE);
2302 build.ret(&[answer]);
2303
2304 assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
2305 let text = printed(&func, &mut names);
2306 assert!(text.contains("call @__divti3(%4, %7) : (ptr, ptr) -> f128"), "{text}");
2309 assert_eq!(text.matches(" = call").count(), 1, "one answer: {text}");
2310 assert_eq!(text.matches("alloca").count(), 3, "three slots: {text}");
2312 assert_eq!(text.matches(" = load").count(), 2, "the two halves: {text}");
2313 }
2314
2315 #[test]
2317 fn the_convention_with_registers_for_the_answer_reads_both_halves_out_of_them() {
2318 let mut names = Interner::new();
2319 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
2320 let mut build = Builder::new(&mut func, entry);
2321 let answer = build.binary(Opcode::SDiv, params[0], params[1], Flags::NONE);
2322 build.ret(&[answer]);
2323
2324 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2325 let text = printed(&func, &mut names);
2326 assert!(text.contains("@__divti3(%0, %1, %2, %3)"), "four halves over: {text}");
2327 assert!(!text.contains("alloca"), "nothing goes through the frame: {text}");
2328 }
2329}