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, Convention, Places, Variadic, 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 let Some(conv) = conv.under(site.convention) else { return false };
311 let in_memory = conv.abi.variadic == Variadic::AlwaysMemory;
312 if variadic && (conv.shared_positions || in_memory || plan(&site, conv).is_none()) {
313 return false;
314 }
315 }
316 reads.iter().filter(|value| wide(value)).all(|&value| match func[value].def {
320 Def::Result { inst, .. } => order.get(&inst).is_some_and(|&def| def < at),
321 Def::Param { .. } => true,
322 })
323}
324
325fn converted(func: &Func, inst: Inst) -> Option<Float> {
332 let data = func[inst];
333 let mut floats = func[data.args]
334 .iter()
335 .copied()
336 .chain(data.results())
337 .map(|value| func[value].ty)
338 .filter(|ty| ty.is_float());
339 let only = floats.next()?;
340 if floats.next().is_some() {
341 return None;
342 }
343 match only.format() {
344 Some(format @ (Float::F32 | Float::F64 | Float::F128)) => Some(format),
345 _ => None,
346 }
347}
348
349fn operands(func: &Func, inst: Inst) -> Vec<Value> {
355 let mut reads = func[func[inst].args].to_vec();
356 for call in func.successors(inst).collect::<Vec<_>>() {
357 reads.extend_from_slice(&func[call.args]);
358 }
359 reads
360}
361
362#[derive(Debug, Clone, Copy, PartialEq, Eq)]
364enum Slot {
365 Whole(usize),
367 Low(usize),
369 High(usize),
371 Filler(Type),
373}
374
375fn plan(signature: &Signature, conv: &CallRegs) -> Option<Vec<Slot>> {
406 let conv = conv.under(signature.convention)?;
407 let word = Param::new(half());
408 let mut places = Places::new(conv);
409 let mut meant: Vec<(Slot, Param, Where)> = Vec::new();
410 let mut moved = false;
411 for (index, ¶m) in signature.params.iter().enumerate() {
412 if !is_wide(param.ty) {
413 meant.push((Slot::Whole(index), param, place(&mut places, param, conv)));
414 continue;
415 }
416 let scalars = conv.abi.scalars;
417 let mut ahead = places.clone();
418 let skipped = (scalars.wide_integer_starts_even && ahead.integers() % 2 == 1)
420 .then(|| ahead.integer(HALF / 8));
421 if let (low @ Where::Reg(_), high @ Where::Reg(_)) =
422 (ahead.integer(HALF / 8), ahead.integer(HALF / 8))
423 {
424 places = ahead;
425 if let Some(at) = skipped {
426 meant.push((Slot::Filler(word.ty), word, at));
427 }
428 meant.push((Slot::Low(index), word, low));
429 meant.push((Slot::High(index), word, high));
430 continue;
431 }
432 if scalars.wide_integer_drains {
433 places.drain_integers();
434 }
435 let Where::Stack(at) = places.on_stack(WIDE / 8, WIDE / 8) else { return None };
436 meant.push((Slot::Low(index), word, Where::Stack(at)));
437 meant.push((Slot::High(index), word, Where::Stack(at + HALF / 8)));
438 moved = true;
439 }
440 if !moved {
441 return Some(meant.into_iter().map(|(slot, _, _)| slot).collect());
442 }
443 if signature.variadic || conv.shared_positions {
444 return None;
445 }
446
447 let in_reg = |at: &Where| matches!(at, Where::Reg(_));
448 let mut stacked: Vec<&(Slot, Param, Where)> =
449 meant.iter().filter(|(_, _, at)| !in_reg(at)).collect();
450 stacked.sort_by_key(|(_, _, at)| match at {
451 Where::Stack(up) => *up,
452 Where::Reg(_) => 0,
453 });
454 let mut order: Vec<(Slot, Param, Option<Where>)> = Vec::new();
455 for float in [false, true] {
456 let kind = |param: &Param| scalar(*param) && param.ty.is_float() == float;
457 order.extend(
458 meant
459 .iter()
460 .filter(|(_, param, at)| kind(param) && in_reg(at))
461 .map(|&(slot, param, at)| (slot, param, Some(at))),
462 );
463 let (count, filler) = match float {
464 false => (conv.int_args.len(), word),
465 true => (conv.sse_args.len(), Param::new(Type::float(Float::F64))),
466 };
467 if stacked.iter().any(|(_, param, _)| kind(param)) {
468 let took = order.iter().filter(|(_, param, _)| kind(param)).count();
469 let padding = count.saturating_sub(took);
470 order.extend((0..padding).map(|_| (Slot::Filler(filler.ty), filler, None)));
471 }
472 }
473
474 let mut places = Places::new(conv);
475 let mut slots = Vec::with_capacity(order.len() + stacked.len());
476 for (slot, param, meant) in order {
477 let at = place(&mut places, param, conv);
478 if !in_reg(&at) || meant.is_some_and(|meant| meant != at) {
479 return None;
480 }
481 slots.push(slot);
482 }
483 for &&(slot, param, meant) in &stacked {
484 let Where::Stack(up) = meant else { return None };
485 while places.size() < up {
486 if in_reg(&place(&mut places, word, conv)) {
487 return None;
488 }
489 slots.push(Slot::Filler(word.ty));
490 }
491 if place(&mut places, param, conv) != meant {
492 return None;
493 }
494 slots.push(slot);
495 }
496 Some(slots)
497}
498
499fn place(places: &mut Places<'_>, param: Param, conv: &CallRegs) -> Where {
501 if let Abi::ByVal { size, align, drains } = param.abi {
505 let at = places.object(u32::try_from(size).unwrap_or(u32::MAX), align);
506 crate::abi::drain(places, drains);
507 at
508 } else if crate::abi::on_the_stack(param.ty) {
509 let (size, align) = crate::abi::X87_AREA;
510 places.on_stack(size, align)
511 } else if param.ty.is_float() {
512 places.float(crate::abi::float_bytes(param.ty))
513 } else {
514 places.integer(crate::abi::int_bytes(param.ty, conv))
515 }
516}
517
518fn scalar(param: Param) -> bool {
520 !matches!(param.abi, Abi::ByVal { .. }) && !crate::abi::on_the_stack(param.ty)
521}
522
523fn planned(signature: &Signature, slots: &[Slot]) -> Signature {
525 let params = slots
526 .iter()
527 .map(|&slot| match slot {
528 Slot::Whole(index) => signature.params[index],
529 Slot::Low(_) | Slot::High(_) => Param::new(half()),
530 Slot::Filler(ty) => Param::new(ty),
531 })
532 .collect();
533 Signature { params, ..split_signature(signature) }
534}
535
536fn params(func: &mut Func, block: Block, halves: &mut Halves, forward: &mut HashMap<Value, Value>) {
543 let old: Vec<Value> = func[block].params.clone();
544 if !old.iter().any(|&value| is_wide(func[value].ty)) {
545 return;
546 }
547 for &value in &old {
548 if is_wide(func[value].ty) {
549 let low = func.append_param(block, half());
550 let high = func.append_param(block, half());
551 halves.insert(value, (low, high));
552 } else {
553 let again = func.append_param(block, func[value].ty);
554 forward.insert(value, again);
555 }
556 }
557 func.retain_params(block, |value| !old.contains(&value));
558}
559
560fn arrive(
562 func: &mut Func,
563 block: Block,
564 slots: &[Slot],
565 halves: &mut Halves,
566 forward: &mut HashMap<Value, Value>,
567) {
568 let old: Vec<Value> = func[block].params.clone();
569 if !old.iter().any(|&value| is_wide(func[value].ty)) {
570 return;
571 }
572 let mut lows = HashMap::new();
573 for &slot in slots {
574 match slot {
575 Slot::Whole(index) => {
576 let again = func.append_param(block, func[old[index]].ty);
577 forward.insert(old[index], again);
578 }
579 Slot::Low(index) => {
580 lows.insert(index, func.append_param(block, half()));
581 }
582 Slot::High(index) => {
583 let high = func.append_param(block, half());
584 halves.insert(old[index], (lows[&index], high));
585 }
586 Slot::Filler(ty) => {
587 func.append_param(block, ty);
588 }
589 }
590 }
591 func.retain_params(block, |value| !old.contains(&value));
592}
593
594fn rewrite(
596 func: &mut Func,
597 names: &mut Interner,
598 conv: &CallRegs,
599 halves: &mut Halves,
600 forward: &mut HashMap<Value, Value>,
601 inst: Inst,
602) {
603 let abi = conv.under(Convention::Target).unwrap_or(conv).abi;
606 let data = func[inst];
607 let produces = data.results().any(|value| is_wide(func[value].ty));
608 let takes = func[data.args].iter().any(|&value| is_wide(func[value].ty));
609 match data.opcode {
610 Opcode::IConst if produces => constant(func, halves, inst),
611 Opcode::Load if produces => load(func, halves, inst),
612 Opcode::Store if takes => store(func, halves, inst),
613 Opcode::Add | Opcode::Sub if produces => carried(func, halves, inst, data.opcode),
614 Opcode::Mul if produces => multiply(func, halves, inst),
615 Opcode::UDiv | Opcode::SDiv | Opcode::URem | Opcode::SRem if produces => {
616 divide(func, names, abi, halves, inst, data.opcode);
617 }
618 Opcode::Shl | Opcode::LShr | Opcode::AShr if produces => {
619 shifted(func, halves, inst, data.opcode);
620 }
621 Opcode::And | Opcode::Or | Opcode::Xor if produces => {
622 bitwise(func, halves, inst, data.opcode);
623 }
624 Opcode::SIToFP | Opcode::UIToFP if takes => {
625 to_float(func, names, abi, halves, forward, inst, data.opcode == Opcode::SIToFP);
626 }
627 Opcode::FPToSI | Opcode::FPToUI if produces => {
628 from_float(func, names, abi, halves, inst, data.opcode == Opcode::FPToSI);
629 }
630 Opcode::ICmp if takes => compare(func, halves, forward, inst),
631 Opcode::Select if produces => choose(func, halves, inst),
632 Opcode::Trunc if takes => truncate(func, halves, forward, inst),
633 Opcode::SExt | Opcode::ZExt if produces => {
634 extend(func, halves, inst, data.opcode == Opcode::SExt);
635 }
636 Opcode::Call | Opcode::CallIndirect if produces || takes => {
637 call(func, conv, halves, forward, inst);
638 }
639 Opcode::Return if takes => flatten(func, halves, inst),
640 Opcode::Jump | Opcode::BrIf => edges(func, halves, inst),
641 _ => {}
642 }
643}
644
645fn constant(func: &mut Func, halves: &mut Halves, inst: Inst) {
647 let Extra::Imm(imm) = func[inst].extra else { return };
648 let bits = func[imm].unsigned();
649 #[expect(clippy::cast_possible_truncation, reason = "the halves are what this is taking")]
650 let (low, high) = (bits as u64, (bits >> HALF) as u64);
651 let low = ahead_const(func, inst, i128::from(low));
652 let high = ahead_const(func, inst, i128::from(high));
653 replace(func, halves, inst, low, high);
654}
655
656fn load(func: &mut Func, halves: &mut Halves, inst: Inst) {
662 let data = func[inst];
663 let Extra::Mem(mem) = data.extra else { return };
664 let info = func[mem];
665 let Some(&from) = func[data.args].first() else { return };
666 let low = read(func, inst, from, word(info, 0), data.flags);
667 let up = stepped(func, inst, from);
668 let high = read(func, inst, up, word(info, STEP), data.flags);
669 replace(func, halves, inst, low, high);
670}
671
672fn store(func: &mut Func, halves: &mut Halves, inst: Inst) {
674 let data = func[inst];
675 let Extra::Mem(mem) = data.extra else { return };
676 let info = func[mem];
677 let args = func[data.args].to_vec();
678 let [value, into] = args[..] else { return };
679 let Some(&(low, high)) = halves.get(&value) else { return };
680 write(func, inst, low, into, word(info, 0), data.flags);
681 let up = stepped(func, inst, into);
682 write(func, inst, high, up, word(info, STEP), data.flags);
683 func.remove_inst(inst);
684}
685
686fn carried(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
696 let args = func[func[inst].args].to_vec();
697 let [a, b] = args[..] else { return };
698 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
699 return;
700 };
701 let low = ahead(func, inst, opcode, &[a_low, b_low]);
702 let carried = if opcode == Opcode::Add {
703 compared(func, inst, IntPred::Ult, low, a_low)
704 } else {
705 compared(func, inst, IntPred::Ult, a_low, b_low)
706 };
707 let carry = ahead(func, inst, Opcode::ZExt, &[carried]);
708 let high = ahead(func, inst, opcode, &[a_high, b_high]);
709 let high = ahead(func, inst, opcode, &[high, carry]);
710 replace(func, halves, inst, low, high);
711}
712
713fn multiply(func: &mut Func, halves: &mut Halves, inst: Inst) {
733 let args = func[func[inst].args].to_vec();
734 let [a, b] = args[..] else { return };
735 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
736 return;
737 };
738 let low = ahead(func, inst, Opcode::Mul, &[a_low, b_low]);
739 let carried = expand::high_half(func, inst, a_low, b_low, false, half());
740 let cross = ahead(func, inst, Opcode::Mul, &[a_low, b_high]);
741 let other = ahead(func, inst, Opcode::Mul, &[a_high, b_low]);
742 let high = ahead(func, inst, Opcode::Add, &[carried, cross]);
743 let high = ahead(func, inst, Opcode::Add, &[high, other]);
744 replace(func, halves, inst, low, high);
745}
746
747fn divide(
765 func: &mut Func,
766 names: &mut Interner,
767 abi: &'static AbiDescription,
768 halves: &mut Halves,
769 inst: Inst,
770 opcode: Opcode,
771) {
772 let args = func[func[inst].args].to_vec();
773 let [a, b] = args[..] else { return };
774 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
775 return;
776 };
777 let Some(routine) = capability::libcall(opcode, MODE) else { return };
781 let args = [Operand::Split(a_low, a_high), Operand::Split(b_low, b_high)];
782 let made = runtime(func, names, abi, inst, routine, &args, &[half(), half()]);
783 let [low, high] = made[..] else { return };
784 replace(func, halves, inst, low, high);
785}
786
787fn to_float(
797 func: &mut Func,
798 names: &mut Interner,
799 abi: &'static AbiDescription,
800 halves: &Halves,
801 forward: &mut HashMap<Value, Value>,
802 inst: Inst,
803 signed: bool,
804) {
805 let Some(&arg) = func[func[inst].args].first() else { return };
806 let Some(&(low, high)) = halves.get(&arg) else { return };
807 let (Some(result), Some(format)) = (func[inst].first_result, converted(func, inst)) else {
808 return;
809 };
810 let routine = going_up(signed, format);
811 let args = [Operand::Split(low, high)];
812 let made = runtime(func, names, abi, inst, routine, &args, &[func[result].ty]);
813 if let [answer] = made[..] {
814 forward.insert(result, answer);
815 }
816 func.remove_inst(inst);
817}
818
819fn from_float(
829 func: &mut Func,
830 names: &mut Interner,
831 abi: &'static AbiDescription,
832 halves: &mut Halves,
833 inst: Inst,
834 signed: bool,
835) {
836 let Some(&arg) = func[func[inst].args].first() else { return };
837 let Some(format) = converted(func, inst) else { return };
838 let routine = coming_down(signed, format);
839 let args = [Operand::Whole(arg)];
840 let made = runtime(func, names, abi, inst, routine, &args, &[half(), half()]);
841 let [low, high] = made[..] else { return };
842 replace(func, halves, inst, low, high);
843}
844
845fn going_up(signed: bool, format: Float) -> &'static str {
851 let mode = match format {
852 Float::F32 => "i128.f32",
853 Float::F64 => "i128.f64",
854 _ => "i128.f128",
855 };
856 routine(if signed { Opcode::SIToFP } else { Opcode::UIToFP }, mode)
857}
858
859fn coming_down(signed: bool, format: Float) -> &'static str {
861 let mode = match format {
862 Float::F32 => "f32.i128",
863 Float::F64 => "f64.i128",
864 _ => "f128.i128",
865 };
866 routine(if signed { Opcode::FPToSI } else { Opcode::FPToUI }, mode)
867}
868
869fn routine(opcode: Opcode, mode: &str) -> &'static str {
874 capability::libcall(opcode, mode)
875 .unwrap_or_else(|| panic!("no routine for `{}` at `{mode}`", opcode.name()))
876}
877
878#[derive(Clone, Copy)]
884enum Operand {
885 Whole(Value),
887 Split(Value, Value),
889}
890
891fn runtime(
916 func: &mut Func,
917 names: &mut Interner,
918 abi: &'static AbiDescription,
919 inst: Inst,
920 routine: &str,
921 args: &[Operand],
922 results: &[Type],
923) -> Vec<Value> {
924 let mut params: Vec<Param> = Vec::new();
925 let mut values: Vec<Value> = Vec::new();
926 let mut answer = Answer::Registers;
927 match *results {
929 [ty] if abi.scalar_is_by_reference(bytes(ty)) => {
930 let size = bytes(ty);
931 let align = align(size);
932 let slot = room(func, inst, size, align);
933 params.push(Param::with_abi(Type::PTR, Abi::Sret { size, align }));
934 values.push(slot);
935 answer = Answer::Slot(slot, ty);
936 }
937 [low, high] if low == half() && high == half() => {
938 if let Some(format) = packed(abi) {
939 answer = Answer::Packed(format);
940 }
941 }
942 _ => {}
943 }
944 for &arg in args {
945 handed(func, abi, inst, arg, &mut params, &mut values);
946 }
947 let answers = match answer {
948 Answer::Registers => results.to_vec(),
949 Answer::Slot(..) => Vec::new(),
950 Answer::Packed(format) => vec![Type::float(format)],
951 };
952 let returns = answers.iter().map(|&ty| Param::new(ty)).collect();
953 let signature = func.add_signature(Signature { params, returns, ..Signature::new() });
955 let callee = Some(names.intern(routine));
956 let varargs = func.push_abis(&[]);
957 let extra = Extra::Call(func.add_call(CallInfo { callee, signature, varargs }));
958 let pushed = func.push_values(&values);
959 let span = func.span(inst);
960 let data = InstData { args: pushed, extra, ..InstData::new(Opcode::Call) };
961 let made = func.create_inst(data, &answers, span);
962 func.insert_before(made, inst);
963 match answer {
964 Answer::Registers => func[made].results().collect(),
965 Answer::Slot(slot, ty) => {
966 let size = bytes(ty);
967 let info = whole(size, align(size));
968 let extra = Extra::Mem(func.add_mem(info));
969 let args = func.push_values(&[slot]);
970 let data = InstData { args, extra, ..InstData::new(Opcode::Load) };
971 vec![written(func, inst, data, ty)]
972 }
973 Answer::Packed(format) => unpacked(func, inst, made, format),
974 }
975}
976
977#[derive(Clone, Copy)]
979enum Answer {
980 Registers,
983 Slot(Value, Type),
986 Packed(Float),
988}
989
990fn packed(abi: &'static AbiDescription) -> Option<Float> {
997 let format = abi.wide_integer_returns_in(u64::from(WIDE / 8))?;
998 Float::from_bits(format.width())
999}
1000
1001fn unpacked(func: &mut Func, inst: Inst, call: Inst, format: Float) -> Vec<Value> {
1009 let Some(value) = func[call].first_result else { return Vec::new() };
1010 let size = bytes(Type::float(format));
1011 let align = align(size);
1012 let slot = room(func, inst, size, align);
1013 let info = whole(size, align);
1014 write(func, inst, value, slot, info, Flags::NONE);
1015 let low = read(func, inst, slot, word(info, 0), Flags::NONE);
1016 let up = stepped(func, inst, slot);
1017 let high = read(func, inst, up, word(info, STEP), Flags::NONE);
1018 vec![low, high]
1019}
1020
1021fn handed(
1023 func: &mut Func,
1024 abi: &'static AbiDescription,
1025 inst: Inst,
1026 arg: Operand,
1027 params: &mut Vec<Param>,
1028 values: &mut Vec<Value>,
1029) {
1030 match arg {
1031 Operand::Whole(value) => {
1032 let ty = func[value].ty;
1033 let size = bytes(ty);
1034 if !abi.scalar_is_by_reference(size) {
1035 params.push(Param::new(ty));
1036 values.push(value);
1037 return;
1038 }
1039 let align = align(size);
1040 let slot = room(func, inst, size, align);
1041 write(func, inst, value, slot, whole(size, align), Flags::NONE);
1042 params.push(Param::new(Type::PTR));
1043 values.push(slot);
1044 }
1045 Operand::Split(low, high) => {
1046 let size = u64::from(WIDE / 8);
1047 if !abi.scalar_is_by_reference(size) {
1048 params.push(Param::new(half()));
1049 values.push(low);
1050 params.push(Param::new(half()));
1051 values.push(high);
1052 return;
1053 }
1054 let align = align(size);
1055 let slot = room(func, inst, size, align);
1056 let info = whole(size, align);
1057 write(func, inst, low, slot, word(info, 0), Flags::NONE);
1058 let up = stepped(func, inst, slot);
1059 write(func, inst, high, up, word(info, STEP), Flags::NONE);
1060 params.push(Param::new(Type::PTR));
1061 values.push(slot);
1062 }
1063 }
1064}
1065
1066fn bytes(ty: Type) -> u64 {
1068 u64::from(ty.bits().div_ceil(8))
1069}
1070
1071fn align(size: u64) -> u32 {
1073 u32::try_from(size).unwrap_or(u32::MAX)
1074}
1075
1076fn whole(size: u64, align: u32) -> MemInfo {
1078 MemInfo {
1079 size,
1080 align,
1081 order: MemOrder::NotAtomic,
1082 tbaa: None,
1083 owns: 0,
1084 restrict: Restrict::NONE,
1085 }
1086}
1087
1088fn room(func: &mut Func, inst: Inst, size: u64, align: u32) -> Value {
1090 let extra = Extra::Mem(func.add_mem(whole(size, align)));
1091 written(func, inst, InstData { extra, ..InstData::new(Opcode::Alloca) }, Type::PTR)
1092}
1093
1094fn shifted(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
1114 let args = func[func[inst].args].to_vec();
1115 let [a, b] = args[..] else { return };
1116 let (Some(&(a_low, a_high)), Some(&(count, _))) = (halves.get(&a), halves.get(&b)) else {
1117 return;
1118 };
1119 let top = ahead_const(func, inst, i128::from(HALF - 1));
1120 let places = ahead(func, inst, Opcode::And, &[count, top]);
1121 let back = ahead(func, inst, Opcode::Sub, &[top, places]);
1122 let one = ahead_const(func, inst, 1);
1123 let zero = ahead_const(func, inst, 0);
1124 let bit = ahead_const(func, inst, i128::from(HALF));
1125 let reach = ahead(func, inst, Opcode::And, &[count, bit]);
1126 let whole = compared(func, inst, IntPred::Ne, reach, zero);
1127
1128 let (low, high) = if opcode == Opcode::Shl {
1129 let moved = ahead(func, inst, Opcode::Shl, &[a_low, places]);
1130 let edge = ahead(func, inst, Opcode::LShr, &[a_low, one]);
1131 let across = ahead(func, inst, Opcode::LShr, &[edge, back]);
1132 let above = ahead(func, inst, Opcode::Shl, &[a_high, places]);
1133 let joined = ahead(func, inst, Opcode::Or, &[above, across]);
1134 let low = ahead(func, inst, Opcode::Select, &[whole, zero, moved]);
1135 let high = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
1136 (low, high)
1137 } else {
1138 let moved = ahead(func, inst, opcode, &[a_high, places]);
1139 let edge = ahead(func, inst, Opcode::Shl, &[a_high, one]);
1140 let across = ahead(func, inst, Opcode::Shl, &[edge, back]);
1141 let below = ahead(func, inst, Opcode::LShr, &[a_low, places]);
1142 let joined = ahead(func, inst, Opcode::Or, &[below, across]);
1143 let spent = if opcode == Opcode::AShr {
1146 ahead(func, inst, Opcode::AShr, &[a_high, top])
1147 } else {
1148 zero
1149 };
1150 let low = ahead(func, inst, Opcode::Select, &[whole, moved, joined]);
1151 let high = ahead(func, inst, Opcode::Select, &[whole, spent, moved]);
1152 (low, high)
1153 };
1154 replace(func, halves, inst, low, high);
1155}
1156
1157fn bitwise(func: &mut Func, halves: &mut Halves, inst: Inst, opcode: Opcode) {
1160 let args = func[func[inst].args].to_vec();
1161 let [a, b] = args[..] else { return };
1162 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
1163 return;
1164 };
1165 let low = ahead(func, inst, opcode, &[a_low, b_low]);
1166 let high = ahead(func, inst, opcode, &[a_high, b_high]);
1167 replace(func, halves, inst, low, high);
1168}
1169
1170fn compare(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
1185 let Extra::IntPred(pred) = func[inst].extra else { return };
1186 let args = func[func[inst].args].to_vec();
1187 let [a, b] = args[..] else { return };
1188 let (Some(&(a_low, a_high)), Some(&(b_low, b_high))) = (halves.get(&a), halves.get(&b)) else {
1189 return;
1190 };
1191 let answer = if matches!(pred, IntPred::Eq | IntPred::Ne) {
1192 let low = ahead(func, inst, Opcode::Xor, &[a_low, b_low]);
1193 let high = ahead(func, inst, Opcode::Xor, &[a_high, b_high]);
1194 let both = ahead(func, inst, Opcode::Or, &[low, high]);
1195 let zero = ahead_const(func, inst, 0);
1196 compared(func, inst, pred, both, zero)
1197 } else {
1198 let above = compared(func, inst, strict(pred), a_high, b_high);
1199 let below = compared(func, inst, unsigned(pred), a_low, b_low);
1200 let same = compared(func, inst, IntPred::Eq, a_high, b_high);
1201 let tail = bit(func, inst, Opcode::And, same, below);
1202 bit(func, inst, Opcode::Or, above, tail)
1203 };
1204 if let Some(result) = func[inst].first_result {
1205 forward.insert(result, answer);
1206 }
1207 func.remove_inst(inst);
1208}
1209
1210fn strict(pred: IntPred) -> IntPred {
1212 match pred {
1213 IntPred::Sle => IntPred::Slt,
1214 IntPred::Sge => IntPred::Sgt,
1215 IntPred::Ule => IntPred::Ult,
1216 IntPred::Uge => IntPred::Ugt,
1217 other => other,
1218 }
1219}
1220
1221fn unsigned(pred: IntPred) -> IntPred {
1223 match pred {
1224 IntPred::Slt => IntPred::Ult,
1225 IntPred::Sle => IntPred::Ule,
1226 IntPred::Sgt => IntPred::Ugt,
1227 IntPred::Sge => IntPred::Uge,
1228 other => other,
1229 }
1230}
1231
1232fn choose(func: &mut Func, halves: &mut Halves, inst: Inst) {
1238 let args = func[func[inst].args].to_vec();
1239 let [cond, then, other] = args[..] else { return };
1240 let (Some(&(then_low, then_high)), Some(&(other_low, other_high))) =
1241 (halves.get(&then), halves.get(&other))
1242 else {
1243 return;
1244 };
1245 let low = ahead(func, inst, Opcode::Select, &[cond, then_low, other_low]);
1246 let high = ahead(func, inst, Opcode::Select, &[cond, then_high, other_high]);
1247 replace(func, halves, inst, low, high);
1248}
1249
1250fn truncate(func: &mut Func, halves: &Halves, forward: &mut HashMap<Value, Value>, inst: Inst) {
1256 let Some(&arg) = func[func[inst].args].first() else { return };
1257 let Some(&(low, _)) = halves.get(&arg) else { return };
1258 let Some(result) = func[inst].first_result else { return };
1259 if func[result].ty.bits() == HALF {
1260 forward.insert(result, low);
1261 func.remove_inst(inst);
1262 return;
1263 }
1264 becomes(func, inst, Opcode::Trunc, &[low]);
1265}
1266
1267fn extend(func: &mut Func, halves: &mut Halves, inst: Inst, signed: bool) {
1269 let Some(&arg) = func[func[inst].args].first() else { return };
1270 let low = if func[arg].ty.bits() == HALF {
1271 arg
1272 } else {
1273 let opcode = if signed { Opcode::SExt } else { Opcode::ZExt };
1274 ahead(func, inst, opcode, &[arg])
1275 };
1276 let high = if signed {
1277 let top = ahead_const(func, inst, i128::from(HALF - 1));
1278 ahead(func, inst, Opcode::AShr, &[low, top])
1279 } else {
1280 ahead_const(func, inst, 0)
1281 };
1282 replace(func, halves, inst, low, high);
1283}
1284
1285fn call(
1292 func: &mut Func,
1293 conv: &CallRegs,
1294 halves: &mut Halves,
1295 forward: &mut HashMap<Value, Value>,
1296 inst: Inst,
1297) {
1298 let data = func[inst];
1299 let Extra::Call(info) = data.extra else { return };
1300 let info = func[info];
1301 let Some((whole, variadic)) = site(func, inst) else { return };
1302 let old = func[data.args].to_vec();
1303 let skip = usize::from(data.opcode == Opcode::CallIndirect);
1305 let Some(slots) = plan(&whole, conv) else { return };
1306 let mut args = spread(&old[..skip], halves);
1307 for slot in slots.iter().copied() {
1308 let value = match slot {
1309 Slot::Whole(index) => old[skip + index],
1310 Slot::Low(index) => halves[&old[skip + index]].0,
1311 Slot::High(index) => halves[&old[skip + index]].1,
1312 Slot::Filler(ty) if ty.is_float() => {
1313 let extra = Extra::Imm(func.add_imm(Imm::from_bits(0)));
1314 written(func, inst, InstData { extra, ..InstData::new(Opcode::FConst) }, ty)
1315 }
1316 Slot::Filler(_) => ahead_const(func, inst, 0),
1317 };
1318 args.push(value);
1319 }
1320 let results: Vec<Type> = data
1321 .results()
1322 .map(|value| func[value].ty)
1323 .flat_map(|ty| if is_wide(ty) { vec![half(), half()] } else { vec![ty] })
1324 .collect();
1325 let signature = func.add_signature(planned(&Signature { variadic, ..whole }, &slots));
1326 let varargs = if variadic { func.push_abis(&[]) } else { info.varargs };
1329 let extra = Extra::Call(func.add_call(CallInfo { signature, varargs, ..info }));
1330 let args = func.push_values(&args);
1331 let span = func.span(inst);
1332 let made = func.create_inst(InstData { args, extra, ..data }, &results, span);
1333 func.insert_before(made, inst);
1334 let mut fresh = func[made].results();
1335 for old in data.results() {
1336 if is_wide(func[old].ty) {
1337 let (Some(low), Some(high)) = (fresh.next(), fresh.next()) else { return };
1338 halves.insert(old, (low, high));
1339 } else if let Some(again) = fresh.next() {
1340 forward.insert(old, again);
1341 }
1342 }
1343 func.remove_inst(inst);
1344}
1345
1346fn site(func: &Func, inst: Inst) -> Option<(Signature, bool)> {
1357 let data = func[inst];
1358 let Extra::Call(info) = data.extra else { return None };
1359 let info = func[info];
1360 let mut whole = func[info.signature].clone();
1361 let skip = usize::from(data.opcode == Opcode::CallIndirect);
1362 let args = &func[data.args];
1363 let named = skip + whole.params.len();
1364 if args.len() < named {
1365 return None;
1366 }
1367 let beyond = &func[info.varargs];
1368 for (index, &value) in args[named..].iter().enumerate() {
1369 let abi = beyond.get(index).copied().unwrap_or_default();
1370 whole.params.push(Param { ty: func[value].ty, abi });
1371 }
1372 let variadic = whole.variadic;
1373 whole.variadic = false;
1374 Some((whole, variadic))
1375}
1376
1377fn flatten(func: &mut Func, halves: &Halves, inst: Inst) {
1379 let args = spread(&func[func[inst].args], halves);
1380 func[inst].args = func.push_values(&args);
1381}
1382
1383fn edges(func: &mut Func, halves: &Halves, inst: Inst) {
1385 for at in func.target_list(inst).iter() {
1386 let call = func[at];
1387 let args = func[call.args].to_vec();
1388 if !args.iter().any(|value| halves.contains_key(value)) {
1389 continue;
1390 }
1391 let args = func.push_values(&spread(&args, halves));
1392 func.set_block_call(at, BlockCall { args, ..call });
1393 }
1394}
1395
1396fn spread(args: &[Value], halves: &Halves) -> Vec<Value> {
1398 args.iter()
1399 .flat_map(|value| match halves.get(value) {
1400 Some(&(low, high)) => vec![low, high],
1401 None => vec![*value],
1402 })
1403 .collect()
1404}
1405
1406fn split_signature(signature: &Signature) -> Signature {
1412 let split = |params: &[Param]| -> Vec<Param> {
1413 params
1414 .iter()
1415 .flat_map(|param| {
1416 if is_wide(param.ty) {
1417 vec![Param::new(half()), Param::new(half())]
1418 } else {
1419 vec![*param]
1420 }
1421 })
1422 .collect()
1423 };
1424 Signature {
1425 params: split(&signature.params),
1426 returns: split(&signature.returns),
1427 variadic: signature.variadic,
1428 convention: signature.convention,
1429 }
1430}
1431
1432fn replace(func: &mut Func, halves: &mut Halves, inst: Inst, low: Value, high: Value) {
1434 if let Some(result) = func[inst].first_result {
1435 halves.insert(result, (low, high));
1436 }
1437 func.remove_inst(inst);
1438}
1439
1440fn substitute(func: &mut Func, forward: &HashMap<Value, Value>) {
1446 if forward.is_empty() {
1447 return;
1448 }
1449 let with = |value: Value| forward.get(&value).copied().unwrap_or(value);
1450 for block in func.blocks().collect::<Vec<_>>() {
1451 for inst in func.insts(block).collect::<Vec<Inst>>() {
1452 let args = func[inst].args;
1453 func.rewrite(args, with);
1454 for call in func.successors(inst).collect::<Vec<_>>() {
1455 func.rewrite(call.args, with);
1456 }
1457 }
1458 }
1459}
1460
1461fn word(info: MemInfo, at: u64) -> MemInfo {
1463 let align = if at == 0 { info.align } else { info.align.min(8) };
1464 MemInfo { size: STEP, align, ..info }
1465}
1466
1467fn stepped(func: &mut Func, inst: Inst, from: Value) -> Value {
1469 let step = ahead_const(func, inst, i128::from(STEP));
1470 let args = func.push_values(&[from, step]);
1471 written(func, inst, InstData { args, ..InstData::new(Opcode::PtrAdd) }, Type::PTR)
1472}
1473
1474fn read(func: &mut Func, inst: Inst, from: Value, info: MemInfo, flags: Flags) -> Value {
1476 let extra = Extra::Mem(func.add_mem(info));
1477 let args = func.push_values(&[from]);
1478 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Load) };
1479 written(func, inst, data, half())
1480}
1481
1482fn write(func: &mut Func, inst: Inst, value: Value, into: Value, info: MemInfo, flags: Flags) {
1484 let span = func.span(inst);
1485 let extra = Extra::Mem(func.add_mem(info));
1486 let args = func.push_values(&[value, into]);
1487 let data = InstData { args, flags, extra, ..InstData::new(Opcode::Store) };
1488 let made = func.create_inst(data, &[], span);
1489 func.insert_before(made, inst);
1490}
1491
1492fn compared(func: &mut Func, inst: Inst, pred: IntPred, lhs: Value, rhs: Value) -> Value {
1495 let args = func.push_values(&[lhs, rhs]);
1496 let extra = Extra::IntPred(pred);
1497 written(func, inst, InstData { args, extra, ..InstData::new(Opcode::ICmp) }, Type::I1)
1498}
1499
1500fn bit(func: &mut Func, inst: Inst, opcode: Opcode, lhs: Value, rhs: Value) -> Value {
1502 let args = func.push_values(&[lhs, rhs]);
1503 written(func, inst, InstData { args, ..InstData::new(opcode) }, Type::I1)
1504}
1505
1506fn ahead(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) -> Value {
1508 let args = func.push_values(args);
1509 written(func, inst, InstData { args, ..InstData::new(opcode) }, half())
1510}
1511
1512fn ahead_const(func: &mut Func, inst: Inst, value: i128) -> Value {
1514 let extra = Extra::Imm(func.add_imm(Imm::int(value, half())));
1515 written(func, inst, InstData { extra, ..InstData::new(Opcode::IConst) }, half())
1516}
1517
1518fn written(func: &mut Func, inst: Inst, data: InstData, ty: Type) -> Value {
1520 let span = func.span(inst);
1521 let made = func.create_inst(data, &[ty], span);
1522 func.insert_before(made, inst);
1523 func[made].first_result.expect("an instruction created with one result has one")
1524}
1525
1526fn becomes(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) {
1528 let args = func.push_values(args);
1529 let data = &mut func[inst];
1530 data.opcode = opcode;
1531 data.args = args;
1532 data.extra = Extra::None;
1533 data.flags = data.flags.intersection(Flags::legal_on(opcode));
1534}
1535
1536#[cfg(test)]
1537mod tests {
1538 use rucc_base::Interner;
1539 use rucc_ir::{
1540 Abi, Block, Builder, Flags, Float, Func, MemOrder, Module, Restrict, Signature, Type, Value,
1541 };
1542 use rucc_target::x86_64::{MINGW64, SYSV};
1543 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1544
1545 use super::{Def, Extra, HALF, IntPred, MemInfo, Opcode, halves};
1546
1547 fn wide() -> Type {
1549 Type::int(super::WIDE)
1550 }
1551
1552 fn target() -> TargetInfo {
1553 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1554 }
1555
1556 fn printed(func: &Func, names: &mut Interner) -> String {
1557 let module = Module::new(names.intern("w.c"), &target());
1558 rucc_ir::print_func(&module, func, names)
1559 }
1560
1561 fn shell(names: &mut Interner, params: &[Type], returns: &[Type]) -> (Func, Block, Vec<Value>) {
1563 let signature = Signature::new().with_params(params).with_returns(returns);
1564 let mut func = Func::new(names.intern("f"), signature);
1565 let entry = func.create_block();
1566 let values = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
1567 (func, entry, values)
1568 }
1569
1570 fn info(size: u64, align: u32) -> MemInfo {
1572 MemInfo {
1573 size,
1574 align,
1575 order: MemOrder::NotAtomic,
1576 tbaa: None,
1577 owns: 0,
1578 restrict: Restrict::NONE,
1579 }
1580 }
1581
1582 #[test]
1583 fn an_add_carries_from_the_low_half_into_the_high_one() {
1584 let mut names = Interner::new();
1585 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1586 let mut build = Builder::new(&mut func, entry);
1587 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1588 build.ret(&[sum]);
1589
1590 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1591 let text = printed(&func, &mut names);
1592 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1593 assert_eq!(text.matches(" = add ").count(), 3, "three adds: {text}");
1596 assert_eq!(text.matches("icmp ult").count(), 1, "one carry: {text}");
1597 assert_eq!(text.matches(" = zext.i64 ").count(), 1, "the carry as a number: {text}");
1598 }
1599
1600 #[test]
1601 fn a_subtract_borrows_the_other_way_round() {
1602 let mut names = Interner::new();
1603 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1604 let mut build = Builder::new(&mut func, entry);
1605 let difference = build.binary(Opcode::Sub, params[0], params[1], Flags::NONE);
1606 build.ret(&[difference]);
1607
1608 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1609 let text = printed(&func, &mut names);
1610 assert_eq!(text.matches(" = sub ").count(), 3, "three subtracts: {text}");
1611 assert!(text.contains("icmp ult %0, %2"), "the operands are compared: {text}");
1614 }
1615
1616 #[test]
1617 fn the_signature_and_the_entry_block_say_the_same_thing() {
1618 let mut names = Interner::new();
1619 let (mut func, entry, params) = shell(&mut names, &[Type::int(32), wide()], &[wide()]);
1620 let mut build = Builder::new(&mut func, entry);
1621 build.ret(&[params[1]]);
1622
1623 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1624 assert_eq!(
1625 func.signature().param_types().collect::<Vec<_>>(),
1626 [Type::int(32), Type::int(HALF), Type::int(HALF)],
1627 "the wide parameter became two where it stood"
1628 );
1629 assert_eq!(
1630 func.signature().return_types().collect::<Vec<_>>(),
1631 [Type::int(HALF), Type::int(HALF)],
1632 "and so did what comes back"
1633 );
1634 let text = printed(&func, &mut names);
1635 assert!(text.contains("block0(%0: i32, %1: i64, %2: i64)"), "the block agrees: {text}");
1636 assert!(text.contains("return %1, %2"), "both halves go back: {text}");
1637 let _ = entry;
1638 }
1639
1640 #[test]
1641 fn a_read_takes_the_high_word_a_word_above_the_low_one() {
1642 let mut names = Interner::new();
1643 let (mut func, entry, params) = shell(&mut names, &[Type::PTR], &[wide()]);
1644 let mut build = Builder::new(&mut func, entry);
1645 let value = build.load(wide(), params[0], info(16, 16), Flags::NONE);
1646 build.ret(&[value]);
1647
1648 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1649 let text = printed(&func, &mut names);
1650 assert_eq!(text.matches(" = load.i64 ").count(), 2, "two reads: {text}");
1651 assert!(text.contains("ptr_add"), "the high word is a word up: {text}");
1652 assert!(text.contains("align 16"), "the low word keeps what the object had: {text}");
1655 assert!(text.contains("align 8"), "the high word knows less: {text}");
1656 }
1657
1658 #[test]
1659 fn an_equality_asks_once_about_both_halves() {
1660 let mut names = Interner::new();
1661 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1662 let mut build = Builder::new(&mut func, entry);
1663 let same = build.icmp(IntPred::Eq, params[0], params[1]);
1664 let answer = build.unary(Opcode::ZExt, same, Type::int(32));
1665 build.ret(&[answer]);
1666
1667 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1668 let text = printed(&func, &mut names);
1669 assert_eq!(text.matches("icmp").count(), 1, "one comparison: {text}");
1670 assert_eq!(text.matches(" = xor ").count(), 2, "the halves differ or they do not: {text}");
1671 }
1672
1673 #[test]
1674 fn an_ordering_reads_the_low_halves_without_a_sign() {
1675 let mut names = Interner::new();
1676 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1677 let mut build = Builder::new(&mut func, entry);
1678 let below = build.icmp(IntPred::Slt, params[0], params[1]);
1679 let answer = build.unary(Opcode::ZExt, below, Type::int(32));
1680 build.ret(&[answer]);
1681
1682 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1683 let text = printed(&func, &mut names);
1684 assert!(text.contains("icmp slt"), "the high halves keep the sign: {text}");
1685 assert!(text.contains("icmp ult"), "the low halves have none: {text}");
1686 assert!(
1687 text.contains("icmp eq"),
1688 "and the low halves only matter when the high tie: {text}"
1689 );
1690 }
1691
1692 #[test]
1699 fn an_ordering_that_allows_equality_asks_the_high_halves_a_strict_question() {
1700 let mut names = Interner::new();
1701 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[Type::int(32)]);
1702 let mut build = Builder::new(&mut func, entry);
1703 let at_least = build.icmp(IntPred::Sge, params[0], params[1]);
1704 let answer = build.unary(Opcode::ZExt, at_least, Type::int(32));
1705 build.ret(&[answer]);
1706
1707 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1708 let text = printed(&func, &mut names);
1709 assert!(text.contains("icmp sgt"), "the high halves settle it outright: {text}");
1710 assert!(!text.contains("icmp sge"), "a tie in the high halves settles nothing: {text}");
1711 assert!(text.contains("icmp uge"), "the low halves are the ones allowed to tie: {text}");
1712 }
1713
1714 #[test]
1715 fn a_widening_puts_the_sign_of_the_value_in_the_high_half() {
1716 let mut names = Interner::new();
1717 let (mut func, entry, params) = shell(&mut names, &[Type::int(32)], &[wide()]);
1718 let mut build = Builder::new(&mut func, entry);
1719 let value = build.unary(Opcode::SExt, params[0], wide());
1720 build.ret(&[value]);
1721
1722 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1723 let text = printed(&func, &mut names);
1724 assert!(text.contains("sext.i64"), "the value fills the low half: {text}");
1725 assert!(text.contains("ashr"), "and its sign fills the high one: {text}");
1726 }
1727
1728 #[test]
1729 fn a_block_parameter_becomes_two_and_every_branch_passes_two() {
1730 let mut names = Interner::new();
1731 let (mut func, entry, params) = shell(&mut names, &[wide(), Type::int(32)], &[wide()]);
1732 let tail = func.create_block();
1733 let carried = func.append_param(tail, wide());
1734 let mut build = Builder::new(&mut func, entry);
1735 let zero = build.iconst(Type::int(32), 0);
1736 let taken = build.icmp(IntPred::Ne, params[1], zero);
1737 let other = build.iconst(wide(), 7);
1738 build.br_if(taken, tail, &[params[0]], tail, &[other]);
1739 let mut build = Builder::new(&mut func, tail);
1740 build.ret(&[carried]);
1741
1742 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1743 let text = printed(&func, &mut names);
1744 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1745 assert!(text.contains("block1(%7: i64, %8: i64)"), "the block takes two: {text}");
1746 assert_eq!(text.matches("block1(").count(), 3, "and both edges pass two: {text}");
1747 }
1748
1749 #[test]
1756 fn a_multiply_is_three_multiplies_and_the_carry_out_of_the_low_ones() {
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 product = build.binary(Opcode::Mul, params[0], params[1], Flags::NONE);
1761 build.ret(&[product]);
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_eq!(text.matches(" = mul ").count(), 7, "three and the carry's four: {text}");
1767 }
1768
1769 #[test]
1776 fn each_of_the_four_divisions_calls_the_routine_of_that_name() {
1777 for (opcode, routine) in [
1778 (Opcode::UDiv, "__udivti3"),
1779 (Opcode::SDiv, "__divti3"),
1780 (Opcode::URem, "__umodti3"),
1781 (Opcode::SRem, "__modti3"),
1782 ] {
1783 let mut names = Interner::new();
1784 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1785 let mut build = Builder::new(&mut func, entry);
1786 let answer = build.binary(opcode, params[0], params[1], Flags::NONE);
1787 build.ret(&[answer]);
1788
1789 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1790 let text = printed(&func, &mut names);
1791 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1792 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1793 }
1794 }
1795
1796 #[test]
1802 fn a_divide_hands_over_four_halves_and_takes_two_back() {
1803 let mut names = Interner::new();
1804 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1805 let mut build = Builder::new(&mut func, entry);
1806 let quotient = build.binary(Opcode::UDiv, params[0], params[1], Flags::NONE);
1807 build.ret(&[quotient]);
1808
1809 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1810 let text = printed(&func, &mut names);
1811 assert!(text.contains("@__udivti3(%0, %1, %2, %3)"), "four halves go over: {text}");
1812 assert!(text.contains("return %4, %5"), "and two come back: {text}");
1813 }
1814
1815 #[test]
1821 fn a_divide_of_something_computed_calls_with_the_halves_of_it() {
1822 let mut names = Interner::new();
1823 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
1824 let mut build = Builder::new(&mut func, entry);
1825 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
1826 let quotient = build.binary(Opcode::SDiv, sum, params[1], Flags::NONE);
1827 build.ret(&[quotient]);
1828
1829 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1830 let text = printed(&func, &mut names);
1831 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1832 assert_eq!(text.matches(" = add ").count(), 3, "the sum is still a sum: {text}");
1833 assert_eq!(text.matches("call @__divti3").count(), 1, "one call: {text}");
1834 }
1835
1836 #[test]
1842 fn each_conversion_between_this_width_and_a_float_calls_the_routine_of_that_name() {
1843 let double = Type::float(Float::F64);
1844 let single = Type::float(Float::F32);
1845 let quad = Type::float(Float::F128);
1846 for (opcode, float, routine) in [
1847 (Opcode::SIToFP, double, "__floattidf"),
1848 (Opcode::SIToFP, single, "__floattisf"),
1849 (Opcode::UIToFP, double, "__floatuntidf"),
1850 (Opcode::UIToFP, single, "__floatuntisf"),
1851 (Opcode::SIToFP, quad, "__floattitf"),
1852 (Opcode::UIToFP, quad, "__floatuntitf"),
1853 ] {
1854 let mut names = Interner::new();
1855 let (mut func, entry, params) = shell(&mut names, &[wide()], &[float]);
1856 let mut build = Builder::new(&mut func, entry);
1857 let answer = build.unary(opcode, params[0], float);
1858 build.ret(&[answer]);
1859
1860 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1861 let text = printed(&func, &mut names);
1862 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1863 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1864 }
1865 for (opcode, float, routine) in [
1866 (Opcode::FPToSI, double, "__fixdfti"),
1867 (Opcode::FPToSI, single, "__fixsfti"),
1868 (Opcode::FPToUI, double, "__fixunsdfti"),
1869 (Opcode::FPToUI, single, "__fixunssfti"),
1870 (Opcode::FPToSI, quad, "__fixtfti"),
1871 (Opcode::FPToUI, quad, "__fixunstfti"),
1872 ] {
1873 let mut names = Interner::new();
1874 let (mut func, entry, params) = shell(&mut names, &[float], &[wide()]);
1875 let mut build = Builder::new(&mut func, entry);
1876 let answer = build.unary(opcode, params[0], wide());
1877 build.ret(&[answer]);
1878
1879 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1880 let text = printed(&func, &mut names);
1881 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
1882 assert!(text.contains(&format!("call @{routine}")), "{routine} is called: {text}");
1883 }
1884 }
1885
1886 #[test]
1892 fn a_conversion_hands_over_halves_one_way_and_takes_them_back_the_other() {
1893 let double = Type::float(Float::F64);
1894 let mut names = Interner::new();
1895 let (mut func, entry, params) = shell(&mut names, &[wide()], &[double]);
1896 let mut build = Builder::new(&mut func, entry);
1897 let answer = build.unary(Opcode::SIToFP, params[0], double);
1898 build.ret(&[answer]);
1899
1900 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1901 let text = printed(&func, &mut names);
1902 assert!(text.contains("@__floattidf(%0, %1)"), "two halves go over: {text}");
1903 assert!(text.contains("return %2"), "and one float comes back: {text}");
1904
1905 let mut names = Interner::new();
1906 let (mut func, entry, params) = shell(&mut names, &[double], &[wide()]);
1907 let mut build = Builder::new(&mut func, entry);
1908 let answer = build.unary(Opcode::FPToSI, params[0], wide());
1909 build.ret(&[answer]);
1910
1911 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1912 let text = printed(&func, &mut names);
1913 assert!(text.contains("@__fixdfti(%0)"), "the float goes over as it is: {text}");
1914 assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1915 }
1916
1917 #[test]
1924 fn a_conversion_against_a_quad_hands_over_the_pair_and_the_quad_whole() {
1925 let quad = Type::float(Float::F128);
1926 let mut names = Interner::new();
1927 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
1928 let mut build = Builder::new(&mut func, entry);
1929 let answer = build.unary(Opcode::UIToFP, params[0], quad);
1930 build.ret(&[answer]);
1931
1932 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1933 let text = printed(&func, &mut names);
1934 assert!(text.contains("@__floatuntitf(%0, %1)"), "two halves go over: {text}");
1935 assert!(text.contains("return %2"), "and one quad comes back: {text}");
1936
1937 let mut names = Interner::new();
1938 let (mut func, entry, params) = shell(&mut names, &[quad], &[wide()]);
1939 let mut build = Builder::new(&mut func, entry);
1940 let answer = build.unary(Opcode::FPToSI, params[0], wide());
1941 build.ret(&[answer]);
1942
1943 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
1944 let text = printed(&func, &mut names);
1945 assert!(text.contains("@__fixtfti(%0)"), "the quad goes over as it is: {text}");
1946 assert!(text.contains("return %1, %2"), "and two halves come back: {text}");
1947 }
1948
1949 #[test]
1956 fn a_conversion_at_a_width_the_runtime_has_no_routine_for_is_left_alone() {
1957 let long = Type::float(Float::F80);
1958 let mut names = Interner::new();
1959 let (mut func, entry, params) = shell(&mut names, &[wide()], &[long]);
1960 let mut build = Builder::new(&mut func, entry);
1961 let answer = build.unary(Opcode::SIToFP, params[0], long);
1962 build.ret(&[answer]);
1963
1964 assert!(!halves(&mut func, &mut names, &SYSV), "the pass does not understand this one");
1965 let text = printed(&func, &mut names);
1966 assert!(text.contains("i128"), "the width is still there: {text}");
1967 }
1968
1969 #[test]
1976 fn a_shift_left_chooses_between_a_count_that_crossed_a_half_and_one_that_did_not() {
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::Shl, 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("i128"), "nothing that wide is left: {text}");
1986 assert_eq!(
1987 text.matches(" = shl ").count(),
1988 2,
1989 "one per half, and the far case reuses one: {text}"
1990 );
1991 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
1992 assert_eq!(text.matches(" = lshr ").count(), 2, "the crossing bits, in two steps: {text}");
1993 }
1994
1995 #[test]
2002 fn the_bits_that_cross_move_one_place_and_then_the_rest_of_the_way() {
2003 let mut names = Interner::new();
2004 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
2005 let mut build = Builder::new(&mut func, entry);
2006 let moved = build.binary(Opcode::LShr, params[0], params[1], Flags::NONE);
2007 build.ret(&[moved]);
2008
2009 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2010 let text = printed(&func, &mut names);
2011 assert!(text.contains("iconst.i64 63"), "sixty three is the distance left: {text}");
2012 assert!(text.contains("iconst.i64 1"), "after the one place that comes first: {text}");
2013 assert!(text.contains(" = sub "), "the rest of the way is worked out: {text}");
2014 assert!(
2015 !text.contains("iconst.i64 127"),
2016 "and the count is not masked to the width: {text}"
2017 );
2018 }
2019
2020 #[test]
2026 fn an_arithmetic_shift_right_leaves_the_sign_bit_where_a_logical_one_leaves_zeroes() {
2027 let mut names = Interner::new();
2028 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
2029 let mut build = Builder::new(&mut func, entry);
2030 let moved = build.binary(Opcode::AShr, params[0], params[1], Flags::NONE);
2031 build.ret(&[moved]);
2032
2033 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2034 let text = printed(&func, &mut names);
2035 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
2036 assert_eq!(text.matches(" = ashr ").count(), 2, "the count and the sign: {text}");
2038 assert_eq!(text.matches(" = lshr ").count(), 1, "the low half is not signed: {text}");
2039 assert_eq!(text.matches(" = select.i64 ").count(), 2, "one choice per half: {text}");
2040 }
2041
2042 fn arrived(params: &[Type], wide_at: usize) -> (Signature, usize) {
2048 let mut names = Interner::new();
2049 let word = Type::int(HALF);
2050 let (mut func, entry, values) = shell(&mut names, params, &[word]);
2051 let mut build = Builder::new(&mut func, entry);
2052 let low = build.unary(Opcode::Trunc, values[wide_at], word);
2053 build.ret(&[low]);
2054
2055 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2056 let ret = func.insts(entry).last().expect("the block ends in a return");
2057 let read = func[func[ret].args][0];
2058 let at = func[entry].params.iter().position(|&value| value == read);
2059 (func.signature().clone(), at.expect("the low half is a parameter"))
2060 }
2061
2062 fn types(signature: &Signature) -> Vec<Type> {
2063 signature.params.iter().map(|param| param.ty).collect()
2064 }
2065
2066 #[test]
2067 fn a_parameter_with_one_register_left_goes_in_memory_and_the_register_stays_empty() {
2068 let word = Type::int(HALF);
2069 let (signature, low) = arrived(&[word, word, word, word, word, wide()], 5);
2070 assert_eq!(types(&signature), vec![word; 8], "five, a filler and two halves");
2071 assert_eq!(low, 6, "the halves come after the register the value skipped");
2072 }
2073
2074 #[test]
2075 fn the_register_a_wide_parameter_skipped_goes_to_the_parameter_after_it() {
2076 let word = Type::int(HALF);
2077 let (signature, low) = arrived(&[word, word, word, word, word, wide(), word], 5);
2078 assert_eq!(types(&signature), vec![word; 8], "six registers and two words, no filler");
2079 assert_eq!(low, 6, "the word after the wide value took the sixth register");
2080 }
2081
2082 #[test]
2083 fn a_wide_parameter_in_memory_starts_on_a_sixteen_byte_boundary() {
2084 let word = Type::int(HALF);
2085 let params = [word, word, word, word, word, word, word, wide()];
2086 let (signature, low) = arrived(¶ms, 7);
2087 assert_eq!(types(&signature), vec![word; 10], "the seventh word, a filler, the halves");
2088 assert_eq!(low, 8, "the filler takes the word the alignment leaves empty");
2089 }
2090
2091 #[test]
2093 fn a_call_passes_a_wide_argument_in_memory_the_way_the_callee_reads_it() {
2094 let mut names = Interner::new();
2095 let word = Type::int(HALF);
2096 let (mut func, entry, _) = shell(&mut names, &[], &[]);
2097 let callee = names.intern("g");
2098 let params = [word, word, word, word, word, wide()];
2099 let signature = func.add_signature(Signature::new().with_params(¶ms));
2100 let mut build = Builder::new(&mut func, entry);
2101 let one = build.iconst(word, 1);
2102 let big = build.iconst(wide(), 4);
2103 build.call(callee, signature, &[one, one, one, one, one, big]);
2104 build.ret(&[]);
2105
2106 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2107 let call = func.insts(entry).find(|&inst| func[inst].opcode == Opcode::Call);
2108 let call = call.expect("the call is still there");
2109 let Extra::Call(info) = func[call].extra else { unreachable!("a call has call info") };
2110 let passed = func[func[call].args].to_vec();
2111 assert_eq!(types(&func[func[info].signature]), vec![word; 8], "as the callee has it");
2112 assert_eq!(passed.len(), 8, "one argument for each parameter");
2113 assert_eq!(passed[..5], [one; 5], "the words keep their registers");
2114 let constant = |value: Value| {
2115 let Def::Result { inst, .. } = func[value].def else { return None };
2116 let Extra::Imm(imm) = func[inst].extra else { return None };
2117 Some(func[imm].unsigned())
2118 };
2119 assert_eq!(constant(passed[6]), Some(4), "the low half is the first word in memory");
2120 assert_eq!(constant(passed[7]), Some(0), "and the high half the second");
2121 }
2122
2123 #[test]
2126 fn a_variadic_call_with_a_wide_argument_in_memory_leaves_the_function_alone() {
2127 let mut names = Interner::new();
2128 let word = Type::int(HALF);
2129 let (mut func, entry, _) = shell(&mut names, &[], &[]);
2130 let callee = names.intern("g");
2131 let params = [word, word, word, word, word, wide()];
2132 let signature = Signature { variadic: true, ..Signature::new().with_params(¶ms) };
2133 let signature = func.add_signature(signature);
2134 let mut build = Builder::new(&mut func, entry);
2135 let one = build.iconst(word, 1);
2136 let big = build.iconst(wide(), 4);
2137 build.call(callee, signature, &[one, one, one, one, one, big]);
2138 build.ret(&[]);
2139 let before = printed(&func, &mut names);
2140
2141 assert!(!halves(&mut func, &mut names, &SYSV), "the named parameters cannot move");
2142 assert_eq!(printed(&func, &mut names), before, "so nothing moved");
2143 }
2144
2145 #[test]
2149 fn a_wide_argument_past_the_dots_goes_where_a_named_one_would() {
2150 let mut names = Interner::new();
2151 let word = Type::int(HALF);
2152 let (mut func, entry, _) = shell(&mut names, &[], &[]);
2153 let callee = names.intern("g");
2154 let params = [word, word, word, word, word];
2155 let signature = Signature { variadic: true, ..Signature::new().with_params(¶ms) };
2156 let signature = func.add_signature(signature);
2157 let mut build = Builder::new(&mut func, entry);
2158 let one = build.iconst(word, 1);
2159 let big = build.iconst(wide(), 4);
2160 build.call_varargs(callee, signature, &[one, one, one, one, one, big], &[Abi::Plain]);
2161 build.ret(&[]);
2162
2163 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2164 let call = func.insts(entry).find(|&inst| func[inst].opcode == Opcode::Call);
2165 let call = call.expect("the call is still there");
2166 let Extra::Call(info) = func[call].extra else { unreachable!("a call has call info") };
2167 let made = &func[func[info].signature];
2168 assert!(made.variadic, "the callee is still variadic");
2169 assert_eq!(types(made), vec![word; 8], "five words, a filler and the two halves");
2170 assert!(func[func[info].varargs].is_empty(), "every argument is named now");
2171 assert_eq!(func[func[call].args].len(), 8, "one argument for each parameter");
2172 }
2173
2174 #[test]
2177 fn a_wide_argument_past_the_dots_on_windows_leaves_the_function_alone() {
2178 let mut names = Interner::new();
2179 let word = Type::int(HALF);
2180 let (mut func, entry, _) = shell(&mut names, &[], &[]);
2181 let callee = names.intern("g");
2182 let signature = Signature { variadic: true, ..Signature::new().with_params(&[word]) };
2183 let signature = func.add_signature(signature);
2184 let mut build = Builder::new(&mut func, entry);
2185 let one = build.iconst(word, 1);
2186 let big = build.iconst(wide(), 4);
2187 build.call_varargs(callee, signature, &[one, big], &[Abi::Plain]);
2188 build.ret(&[]);
2189
2190 assert!(!halves(&mut func, &mut names, &MINGW64), "the call is left for a refusal");
2191 }
2192
2193 #[test]
2202 fn a_block_made_after_the_one_it_runs_before_is_still_split() {
2203 let mut names = Interner::new();
2204 let (mut func, entry, params) = shell(&mut names, &[wide()], &[wide()]);
2205 let tail = func.create_block();
2206 let middle = func.create_block();
2207 let mut build = Builder::new(&mut func, entry);
2208 build.jump(middle, &[]);
2209 let mut build = Builder::new(&mut func, middle);
2210 let doubled = build.binary(Opcode::Add, params[0], params[0], Flags::NONE);
2211 build.jump(tail, &[]);
2212 let mut build = Builder::new(&mut func, tail);
2213 let again = build.binary(Opcode::Add, doubled, doubled, Flags::NONE);
2214 build.ret(&[again]);
2215
2216 assert!(
2217 halves(&mut func, &mut names, &SYSV),
2218 "the definition runs before the use whatever the list says"
2219 );
2220 let text = printed(&func, &mut names);
2221 assert!(!text.contains("i128"), "nothing that wide is left: {text}");
2222 }
2223
2224 #[test]
2225 fn a_function_with_nothing_that_wide_is_not_touched() {
2226 let mut names = Interner::new();
2227 let word = Type::int(HALF);
2228 let (mut func, entry, params) = shell(&mut names, &[word, word], &[word]);
2229 let mut build = Builder::new(&mut func, entry);
2230 let sum = build.binary(Opcode::Add, params[0], params[1], Flags::NONE);
2231 build.ret(&[sum]);
2232
2233 assert!(!halves(&mut func, &mut names, &SYSV), "there is nothing to split");
2234 }
2235
2236 #[test]
2243 fn on_windows_a_wide_operand_goes_over_as_the_address_of_a_copy() {
2244 let mut names = Interner::new();
2245 let quad = Type::float(Float::F64);
2246 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
2247 let mut build = Builder::new(&mut func, entry);
2248 let answer = build.unary(Opcode::SIToFP, params[0], quad);
2249 build.ret(&[answer]);
2250
2251 assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
2252 let text = printed(&func, &mut names);
2253 assert!(text.contains("call @__floattidf"), "{text}");
2254 assert_eq!(text.matches("alloca").count(), 1, "one slot: {text}");
2256 assert_eq!(text.matches("store").count(), 2, "a half at a time: {text}");
2257 assert_eq!(text.matches("ptr_add").count(), 1, "the high half eight bytes up: {text}");
2258 assert!(!text.contains("__floattidf(%0, %1)"), "not the two halves: {text}");
2259 }
2260
2261 #[test]
2263 fn on_windows_a_wide_answer_at_this_format_comes_back_through_a_slot() {
2264 let mut names = Interner::new();
2265 let quad = Type::float(Float::F128);
2266 let (mut func, entry, params) = shell(&mut names, &[wide()], &[quad]);
2267 let mut build = Builder::new(&mut func, entry);
2268 let answer = build.unary(Opcode::SIToFP, params[0], quad);
2269 build.ret(&[answer]);
2270
2271 assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
2272 let text = printed(&func, &mut names);
2273 assert!(text.contains("call @__floattitf"), "{text}");
2274 assert_eq!(text.matches("alloca").count(), 2, "two slots: {text}");
2276 assert_eq!(text.matches(" = call").count(), 0, "the call answers nothing: {text}");
2277 assert_eq!(text.matches(" = load").count(), 1, "the answer is the load after it: {text}");
2278 }
2279
2280 #[test]
2282 fn the_convention_with_registers_for_both_halves_puts_nothing_on_the_frame() {
2283 let mut names = Interner::new();
2284 let double = Type::float(Float::F64);
2285 let (mut func, entry, params) = shell(&mut names, &[wide()], &[double]);
2286 let mut build = Builder::new(&mut func, entry);
2287 let answer = build.unary(Opcode::SIToFP, params[0], double);
2288 build.ret(&[answer]);
2289
2290 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2291 let text = printed(&func, &mut names);
2292 assert!(text.contains("@__floattidf(%0, %1)"), "both halves in registers: {text}");
2293 assert!(!text.contains("alloca"), "nothing goes through the frame: {text}");
2294 }
2295
2296 #[test]
2299 fn on_windows_a_wide_answer_comes_back_in_one_register_and_is_split_on_the_frame() {
2300 let mut names = Interner::new();
2301 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
2302 let mut build = Builder::new(&mut func, entry);
2303 let answer = build.binary(Opcode::SDiv, params[0], params[1], Flags::NONE);
2304 build.ret(&[answer]);
2305
2306 assert!(halves(&mut func, &mut names, &MINGW64), "there is a width to split");
2307 let text = printed(&func, &mut names);
2308 assert!(text.contains("call @__divti3(%4, %7) : (ptr, ptr) -> f128"), "{text}");
2311 assert_eq!(text.matches(" = call").count(), 1, "one answer: {text}");
2312 assert_eq!(text.matches("alloca").count(), 3, "three slots: {text}");
2314 assert_eq!(text.matches(" = load").count(), 2, "the two halves: {text}");
2315 }
2316
2317 #[test]
2319 fn the_convention_with_registers_for_the_answer_reads_both_halves_out_of_them() {
2320 let mut names = Interner::new();
2321 let (mut func, entry, params) = shell(&mut names, &[wide(), wide()], &[wide()]);
2322 let mut build = Builder::new(&mut func, entry);
2323 let answer = build.binary(Opcode::SDiv, params[0], params[1], Flags::NONE);
2324 build.ret(&[answer]);
2325
2326 assert!(halves(&mut func, &mut names, &SYSV), "there is a width to split");
2327 let text = printed(&func, &mut names);
2328 assert!(text.contains("@__divti3(%0, %1, %2, %3)"), "four halves over: {text}");
2329 assert!(!text.contains("alloca"), "nothing goes through the frame: {text}");
2330 }
2331}