1use std::cmp::Ordering;
41use std::collections::HashMap;
42
43use rucc_base::Interner;
44use rucc_ir::{
45 CallInfo, Def, Extra, Flags, FloatPred, Func, Imm, Inst, InstData, IntPred, MemInfo, MemOrder,
46 Opcode, Signature, Type, Value,
47};
48
49pub fn orderings(func: &mut Func, word: u32) {
86 let found: Vec<Inst> =
87 func.blocks().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
88 for inst in found {
89 match func[inst].opcode {
90 Opcode::AtomicLoad => relaxed(func, inst, Opcode::Load, word),
91 Opcode::AtomicStore => relaxed(func, inst, Opcode::Store, word),
92 _ => {}
93 }
94 }
95}
96
97fn relaxed(func: &mut Func, inst: Inst, plain: Opcode, word: u32) {
111 let Extra::Mem(mem) = func[inst].extra else { return };
112 let info = func[mem];
113 let ty = match plain {
114 Opcode::Store => match func[func[inst].args].first() {
115 Some(&value) => func[value].ty,
116 None => return,
117 },
118 _ => produced(func, inst),
119 };
120 if !indivisible(ty, info, word) {
121 return;
122 }
123 let unordered = MemInfo { order: MemOrder::NotAtomic, ..info };
124
125 if plain == Opcode::Store && info.order == MemOrder::SeqCst {
126 let [value, addr] = func[func[inst].args] else { return };
127 write(func, inst, value, addr, unordered);
128 let none = func.push_values(&[]);
129 let data = &mut func[inst];
130 data.opcode = Opcode::Fence;
131 data.args = none;
132 data.extra = Extra::Order(MemOrder::SeqCst);
133 data.flags = data.flags.intersection(Flags::legal_on(Opcode::Fence));
134 return;
135 }
136
137 let plainly = func.add_mem(unordered);
138 let data = &mut func[inst];
139 data.opcode = plain;
140 data.extra = Extra::Mem(plainly);
141 data.flags = data.flags.intersection(Flags::legal_on(plain));
142}
143
144fn indivisible(ty: Type, info: MemInfo, word: u32) -> bool {
157 let bytes = if ty.is_ptr() { word } else { ty.bits().div_ceil(8) };
158 ty.is_scalar() && bytes.is_power_of_two() && bytes <= word && info.align >= bytes
159}
160
161pub fn floats(func: &mut Func) {
175 let found: Vec<Inst> =
176 func.blocks().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
177 for inst in found {
178 match func[inst].opcode {
179 Opcode::FConst => constant(func, inst),
180 Opcode::FNeg => negate(func, inst),
181 Opcode::SIToFP | Opcode::UIToFP => widen_then_convert(func, inst),
182 Opcode::FPToSI | Opcode::FPToUI => convert_then_narrow(func, inst),
183 _ => {}
184 }
185 }
186}
187
188fn constant(func: &mut Func, inst: Inst) {
199 let ty = produced(func, inst);
200 let Extra::Imm(imm) = func[inst].extra else { return };
201 if !ty.is_float() || !ty.is_scalar() || ty.bits() > 64 {
202 return;
203 }
204 let int = Type::int(ty.bits());
205 let bits = func[imm].bits();
206 let spelled = ahead_const(func, inst, Imm::int(bits as i128, int), int);
209 becomes(func, inst, Opcode::Bitcast, &[spelled]);
210}
211
212fn negate(func: &mut Func, inst: Inst) {
228 let ty = produced(func, inst);
229 let Some(&arg) = func[func[inst].args].first() else { return };
230 if !ty.is_float() || !ty.is_scalar() || ty.bits() > 64 {
231 return;
232 }
233 let int = Type::int(ty.bits());
234 let bits = ahead(func, inst, Opcode::Bitcast, &[arg], int);
235 let mask = ahead_const(func, inst, Imm::int(1i128 << (ty.bits() - 1), int), int);
236 let flipped = ahead(func, inst, Opcode::Xor, &[bits, mask], int);
237 becomes(func, inst, Opcode::Bitcast, &[flipped]);
238}
239
240fn widen_then_convert(func: &mut Func, inst: Inst) {
247 let signed = func[inst].opcode == Opcode::SIToFP;
248 let Some(&arg) = func[func[inst].args].first() else { return };
249 let from = func[arg].ty;
250 if !from.is_int() || !from.is_scalar() {
251 return;
252 }
253 let Some(width) = holder(from.bits(), signed) else {
254 from_unsigned_word(func, inst, arg, from);
255 return;
256 };
257 if width == from.bits() {
258 return;
259 }
260 let widen = if signed { Opcode::SExt } else { Opcode::ZExt };
261 let wide = ahead(func, inst, widen, &[arg], Type::int(width));
262 becomes(func, inst, Opcode::SIToFP, &[wide]);
263}
264
265fn convert_then_narrow(func: &mut Func, inst: Inst) {
272 let signed = func[inst].opcode == Opcode::FPToSI;
273 let ty = produced(func, inst);
274 let Some(&arg) = func[func[inst].args].first() else { return };
275 if !ty.is_int() || !ty.is_scalar() {
276 return;
277 }
278 let Some(width) = holder(ty.bits(), signed) else {
279 to_unsigned_word(func, inst, arg, ty);
280 return;
281 };
282 if width == ty.bits() {
283 return;
284 }
285 let wide = ahead(func, inst, Opcode::FPToSI, &[arg], Type::int(width));
286 becomes(func, inst, Opcode::Trunc, &[wide]);
287}
288
289fn from_unsigned_word(func: &mut Func, inst: Inst, arg: Value, from: Type) {
311 let ty = produced(func, inst);
312 if !ty.is_float() || !ty.is_scalar() {
313 return;
314 }
315 if ty.bits() > 64 {
316 from_unsigned_word_wide(func, inst, arg, from);
317 return;
318 }
319 let spread = spread_top_bit(func, inst, arg, from);
320
321 let one = ahead_const(func, inst, Imm::int(1, from), from);
323 let lost = ahead(func, inst, Opcode::And, &[arg, one], from);
324 let half = ahead(func, inst, Opcode::LShr, &[arg, one], from);
325 let odd = ahead(func, inst, Opcode::Or, &[half, lost], from);
326
327 let differ = ahead(func, inst, Opcode::Xor, &[arg, odd], from);
329 let taken = ahead(func, inst, Opcode::And, &[differ, spread], from);
330 let source = ahead(func, inst, Opcode::Xor, &[arg, taken], from);
331 let converted = ahead(func, inst, Opcode::SIToFP, &[source], ty);
332
333 let bits = Type::int(ty.bits());
336 let narrow = same_width(func, inst, spread, from, bits);
337 let raw = ahead(func, inst, Opcode::Bitcast, &[converted], bits);
338 let again = ahead(func, inst, Opcode::And, &[raw, narrow], bits);
339 let addend = ahead(func, inst, Opcode::Bitcast, &[again], ty);
340 becomes(func, inst, Opcode::FAdd, &[converted, addend]);
341}
342
343fn to_unsigned_word(func: &mut Func, inst: Inst, arg: Value, ty: Type) {
356 let from = func[arg].ty;
357 if !from.is_float() || !from.is_scalar() {
358 return;
359 }
360 if from.bits() > 64 {
361 to_unsigned_word_wide(func, inst, arg, ty);
362 return;
363 }
364 let bits = Type::int(from.bits());
366 let pattern = Imm::int(half_the_range(from.bits()), bits);
367 let spelled = ahead_const(func, inst, pattern, bits);
368 let half = ahead(func, inst, Opcode::Bitcast, &[spelled], from);
369
370 let over = ahead_cmp(func, inst, Opcode::FCmp, Extra::FloatPred(FloatPred::Oge), &[arg, half]);
371 let wide = ahead(func, inst, Opcode::ZExt, &[over], bits);
372 let zero = ahead_const(func, inst, Imm::int(0, bits), bits);
373 let spread = ahead(func, inst, Opcode::Sub, &[zero, wide], bits);
374
375 let amount = ahead(func, inst, Opcode::And, &[spread, spelled], bits);
376 let taken = ahead(func, inst, Opcode::Bitcast, &[amount], from);
377 let under = ahead(func, inst, Opcode::FSub, &[arg, taken], from);
378 let low = ahead(func, inst, Opcode::FPToSI, &[under], ty);
379
380 let again = ahead(func, inst, Opcode::ZExt, &[over], ty);
382 let up = ahead_const(func, inst, Imm::int(i128::from(ty.bits() - 1), ty), ty);
383 let top = ahead(func, inst, Opcode::Shl, &[again, up], ty);
384 becomes(func, inst, Opcode::Xor, &[low, top]);
385}
386
387fn from_unsigned_word_wide(func: &mut Func, inst: Inst, arg: Value, from: Type) {
409 let ty = produced(func, inst);
410 let zero = ahead_const(func, inst, Imm::int(0, from), from);
411 let over = ahead_cmp(func, inst, Opcode::ICmp, Extra::IntPred(IntPred::Slt), &[arg, zero]);
412
413 let signed = ahead(func, inst, Opcode::SIToFP, &[arg], ty);
414 let range = ahead_float(func, inst, two_to_the(64), ty);
415 let flag = flag_as_float(func, inst, over, ty);
416 let addend = ahead(func, inst, Opcode::FMul, &[range, flag], ty);
417 becomes(func, inst, Opcode::FAdd, &[signed, addend]);
418}
419
420fn to_unsigned_word_wide(func: &mut Func, inst: Inst, arg: Value, ty: Type) {
432 let from = func[arg].ty;
433 let half = ahead_float(func, inst, two_to_the(63), from);
434 let over = ahead_cmp(func, inst, Opcode::FCmp, Extra::FloatPred(FloatPred::Oge), &[arg, half]);
435
436 let flag = flag_as_float(func, inst, over, from);
437 let taken = ahead(func, inst, Opcode::FMul, &[half, flag], from);
438 let under = ahead(func, inst, Opcode::FSub, &[arg, taken], from);
439 let low = ahead(func, inst, Opcode::FPToSI, &[under], ty);
440
441 let again = ahead(func, inst, Opcode::ZExt, &[over], ty);
443 let up = ahead_const(func, inst, Imm::int(i128::from(ty.bits() - 1), ty), ty);
444 let top = ahead(func, inst, Opcode::Shl, &[again, up], ty);
445 becomes(func, inst, Opcode::Xor, &[low, top]);
446}
447
448fn flag_as_float(func: &mut Func, inst: Inst, cond: Value, ty: Type) -> Value {
454 let wide = ahead(func, inst, Opcode::ZExt, &[cond], Type::int(64));
455 ahead(func, inst, Opcode::SIToFP, &[wide], ty)
456}
457
458const fn two_to_the(power: u32) -> u128 {
463 ((0x3fff + power as u128) << 64) | 0x8000_0000_0000_0000
464}
465
466fn spread_top_bit(func: &mut Func, inst: Inst, arg: Value, ty: Type) -> Value {
472 let zero = ahead_const(func, inst, Imm::int(0, ty), ty);
473 let set = ahead_cmp(func, inst, Opcode::ICmp, Extra::IntPred(IntPred::Slt), &[arg, zero]);
474 let wide = ahead(func, inst, Opcode::ZExt, &[set], ty);
475 ahead(func, inst, Opcode::Sub, &[zero, wide], ty)
476}
477
478fn same_width(func: &mut Func, inst: Inst, value: Value, from: Type, to: Type) -> Value {
480 match to.bits().cmp(&from.bits()) {
481 Ordering::Equal => value,
482 Ordering::Less => ahead(func, inst, Opcode::Trunc, &[value], to),
483 Ordering::Greater => ahead(func, inst, Opcode::SExt, &[value], to),
484 }
485}
486
487fn half_the_range(width: u32) -> i128 {
493 match width {
494 32 => 0x5F00_0000,
495 _ => 0x43E0_0000_0000_0000,
496 }
497}
498
499pub fn bytes(func: &mut Func) {
513 let found: Vec<Inst> =
514 func.blocks().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
515 for inst in found {
516 if func[inst].opcode == Opcode::Bswap {
517 swap(func, inst);
518 }
519 }
520}
521
522fn swap(func: &mut Func, inst: Inst) {
539 let ty = produced(func, inst);
540 let Some(&arg) = func[func[inst].args].first() else { return };
541 if !ty.is_int() || !ty.is_scalar() || ty.bits() < 16 || ty.bits() % 8 != 0 {
542 return;
543 }
544
545 let mut value = arg;
546 let mut group = ty.bits() / 2;
547 while group >= 8 {
548 let mask = alternating(ty.bits(), group);
551 let keep = ahead_const(func, inst, Imm::int(mask, ty), ty);
552 let count = ahead_const(func, inst, Imm::int(i128::from(group), ty), ty);
553 let low = ahead(func, inst, Opcode::And, &[value, keep], ty);
554 let up = ahead(func, inst, Opcode::Shl, &[low, count], ty);
555 let down = ahead(func, inst, Opcode::LShr, &[value, count], ty);
556 let high = ahead(func, inst, Opcode::And, &[down, keep], ty);
557 if group == 8 {
560 becomes(func, inst, Opcode::Or, &[up, high]);
561 return;
562 }
563 value = ahead(func, inst, Opcode::Or, &[up, high], ty);
564 group /= 2;
565 }
566}
567
568fn alternating(width: u32, group: u32) -> i128 {
579 every(width, group * 2, group)
580}
581
582fn every(width: u32, step: u32, run: u32) -> i128 {
591 let ones = (1i128 << run) - 1;
592 let mut mask = 0i128;
593 let mut at = 0;
594 while at < width {
595 mask |= ones << at;
596 at += step;
597 }
598 mask
599}
600
601pub fn counts(func: &mut Func) {
615 let found: Vec<Inst> =
616 func.blocks().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
617 for inst in found {
618 match func[inst].opcode {
619 Opcode::Ctlz => searched(func, inst, true),
620 Opcode::Cttz => searched(func, inst, false),
621 _ => {}
622 }
623 }
624 let found: Vec<Inst> =
625 func.blocks().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
626 for inst in found {
627 if func[inst].opcode == Opcode::Ctpop {
628 counted(func, inst);
629 }
630 }
631}
632
633fn searched(func: &mut Func, inst: Inst, leading: bool) {
651 let ty = produced(func, inst);
652 let Some(&arg) = func[func[inst].args].first() else { return };
653 if !countable(ty) {
654 return;
655 }
656 let ones = ahead_const(func, inst, Imm::int(-1, ty), ty);
657 if leading {
658 let mut value = arg;
659 let mut by = 1;
660 while by < ty.bits() {
661 let count = ahead_const(func, inst, Imm::int(i128::from(by), ty), ty);
662 let down = ahead(func, inst, Opcode::LShr, &[value, count], ty);
663 value = ahead(func, inst, Opcode::Or, &[value, down], ty);
664 by *= 2;
665 }
666 let above = ahead(func, inst, Opcode::Xor, &[value, ones], ty);
667 becomes(func, inst, Opcode::Ctpop, &[above]);
668 return;
669 }
670 let missing = ahead(func, inst, Opcode::Xor, &[arg, ones], ty);
671 let less = ahead(func, inst, Opcode::Add, &[arg, ones], ty);
672 let below = ahead(func, inst, Opcode::And, &[missing, less], ty);
673 becomes(func, inst, Opcode::Ctpop, &[below]);
674}
675
676fn counted(func: &mut Func, inst: Inst) {
690 let ty = produced(func, inst);
691 let Some(&arg) = func[func[inst].args].first() else { return };
692 if !countable(ty) {
693 return;
694 }
695 let width = ty.bits();
696 let pairs = ahead_const(func, inst, Imm::int(alternating(width, 1), ty), ty);
697 let two = ahead_const(func, inst, Imm::int(2, ty), ty);
698 let one = ahead_const(func, inst, Imm::int(1, ty), ty);
699 let high = ahead(func, inst, Opcode::LShr, &[arg, one], ty);
700 let odd = ahead(func, inst, Opcode::And, &[high, pairs], ty);
701 let bits = ahead(func, inst, Opcode::Sub, &[arg, odd], ty);
702
703 let quads = ahead_const(func, inst, Imm::int(alternating(width, 2), ty), ty);
704 let low = ahead(func, inst, Opcode::And, &[bits, quads], ty);
705 let up = ahead(func, inst, Opcode::LShr, &[bits, two], ty);
706 let rest = ahead(func, inst, Opcode::And, &[up, quads], ty);
707 let nibbles = ahead(func, inst, Opcode::Add, &[low, rest], ty);
708
709 let four = ahead_const(func, inst, Imm::int(4, ty), ty);
710 let bytes = ahead_const(func, inst, Imm::int(alternating(width, 4), ty), ty);
711 let folded = ahead(func, inst, Opcode::LShr, &[nibbles, four], ty);
712 let summed = ahead(func, inst, Opcode::Add, &[nibbles, folded], ty);
713 if width == 8 {
714 becomes(func, inst, Opcode::And, &[summed, bytes]);
715 return;
716 }
717 let held = ahead(func, inst, Opcode::And, &[summed, bytes], ty);
718
719 let spread = ahead_const(func, inst, Imm::int(every(width, 8, 1), ty), ty);
720 let top = ahead_const(func, inst, Imm::int(i128::from(width - 8), ty), ty);
721 let total = ahead(func, inst, Opcode::Mul, &[held, spread], ty);
722 becomes(func, inst, Opcode::LShr, &[total, top]);
723}
724
725pub fn overflows(func: &mut Func) {
738 let found: Vec<Inst> =
739 func.blocks().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
740 let mut forward = HashMap::new();
741 for inst in found {
742 let checked = match func[inst].opcode {
743 Opcode::UAddOverflow => Checked::Add(false),
744 Opcode::SAddOverflow => Checked::Add(true),
745 Opcode::USubOverflow => Checked::Sub(false),
746 Opcode::SSubOverflow => Checked::Sub(true),
747 Opcode::UMulOverflow => Checked::Mul(false),
748 Opcode::SMulOverflow => Checked::Mul(true),
749 _ => continue,
750 };
751 overflowed(func, inst, checked, &mut forward);
752 }
753 if !forward.is_empty() {
754 substitute(func, &forward);
755 }
756}
757
758#[derive(Debug, Clone, Copy)]
760enum Checked {
761 Add(bool),
763 Sub(bool),
765 Mul(bool),
767}
768
769fn overflowed(func: &mut Func, inst: Inst, checked: Checked, forward: &mut HashMap<Value, Value>) {
786 let ty = produced(func, inst);
787 let [a, b] = func[func[inst].args] else { return };
788 if !countable(ty) {
789 return;
790 }
791 let (value, bit) = match checked {
792 Checked::Add(signed) => {
793 let value = ahead(func, inst, Opcode::Add, &[a, b], ty);
794 let bit = if signed {
795 let left = ahead(func, inst, Opcode::Xor, &[a, value], ty);
796 let right = ahead(func, inst, Opcode::Xor, &[b, value], ty);
797 let both = ahead(func, inst, Opcode::And, &[left, right], ty);
798 negative(func, inst, both, ty)
799 } else {
800 compared(func, inst, IntPred::Ult, value, a)
801 };
802 (value, bit)
803 }
804 Checked::Sub(signed) => {
805 let value = ahead(func, inst, Opcode::Sub, &[a, b], ty);
806 let bit = if signed {
807 let apart = ahead(func, inst, Opcode::Xor, &[a, b], ty);
808 let moved = ahead(func, inst, Opcode::Xor, &[a, value], ty);
809 let both = ahead(func, inst, Opcode::And, &[apart, moved], ty);
810 negative(func, inst, both, ty)
811 } else {
812 compared(func, inst, IntPred::Ult, a, b)
813 };
814 (value, bit)
815 }
816 Checked::Mul(signed) => {
817 let value = ahead(func, inst, Opcode::Mul, &[a, b], ty);
818 let high = high_half(func, inst, a, b, signed, ty);
819 let bit = if signed {
820 let sign = ahead_const(func, inst, Imm::int(i128::from(ty.bits() - 1), ty), ty);
821 let wanted = ahead(func, inst, Opcode::AShr, &[value, sign], ty);
822 compared(func, inst, IntPred::Ne, high, wanted)
823 } else {
824 let zero = ahead_const(func, inst, Imm::int(0, ty), ty);
825 compared(func, inst, IntPred::Ne, high, zero)
826 };
827 (value, bit)
828 }
829 };
830 let mut answers = func[inst].results();
831 if let (Some(wrapped), Some(flag)) = (answers.next(), answers.next()) {
832 forward.insert(wrapped, value);
833 forward.insert(flag, bit);
834 }
835 func.remove_inst(inst);
836}
837
838fn high_half(func: &mut Func, inst: Inst, a: Value, b: Value, signed: bool, ty: Type) -> Value {
855 let width = ty.bits();
856 let half = width / 2;
857 let shift = ahead_const(func, inst, Imm::int(i128::from(half), ty), ty);
858 let mask = ahead_const(func, inst, Imm::int((1i128 << half) - 1, ty), ty);
859
860 let al = ahead(func, inst, Opcode::And, &[a, mask], ty);
861 let ah = ahead(func, inst, Opcode::LShr, &[a, shift], ty);
862 let bl = ahead(func, inst, Opcode::And, &[b, mask], ty);
863 let bh = ahead(func, inst, Opcode::LShr, &[b, shift], ty);
864
865 let ll = ahead(func, inst, Opcode::Mul, &[al, bl], ty);
866 let lh = ahead(func, inst, Opcode::Mul, &[al, bh], ty);
867 let hl = ahead(func, inst, Opcode::Mul, &[ah, bl], ty);
868 let hh = ahead(func, inst, Opcode::Mul, &[ah, bh], ty);
869
870 let over = ahead(func, inst, Opcode::LShr, &[ll, shift], ty);
873 let lh_low = ahead(func, inst, Opcode::And, &[lh, mask], ty);
874 let hl_low = ahead(func, inst, Opcode::And, &[hl, mask], ty);
875 let some = ahead(func, inst, Opcode::Add, &[over, lh_low], ty);
876 let carry = ahead(func, inst, Opcode::Add, &[some, hl_low], ty);
877
878 let lh_high = ahead(func, inst, Opcode::LShr, &[lh, shift], ty);
879 let hl_high = ahead(func, inst, Opcode::LShr, &[hl, shift], ty);
880 let up = ahead(func, inst, Opcode::LShr, &[carry, shift], ty);
881 let first = ahead(func, inst, Opcode::Add, &[hh, lh_high], ty);
882 let second = ahead(func, inst, Opcode::Add, &[first, hl_high], ty);
883 let high = ahead(func, inst, Opcode::Add, &[second, up], ty);
884 if !signed {
885 return high;
886 }
887 let top = ahead_const(func, inst, Imm::int(i128::from(width - 1), ty), ty);
888 let a_sign = ahead(func, inst, Opcode::AShr, &[a, top], ty);
889 let b_sign = ahead(func, inst, Opcode::AShr, &[b, top], ty);
890 let a_owes = ahead(func, inst, Opcode::And, &[a_sign, b], ty);
891 let b_owes = ahead(func, inst, Opcode::And, &[b_sign, a], ty);
892 let once = ahead(func, inst, Opcode::Sub, &[high, a_owes], ty);
893 ahead(func, inst, Opcode::Sub, &[once, b_owes], ty)
894}
895
896fn negative(func: &mut Func, inst: Inst, value: Value, ty: Type) -> Value {
898 let zero = ahead_const(func, inst, Imm::int(0, ty), ty);
899 compared(func, inst, IntPred::Slt, value, zero)
900}
901
902fn compared(func: &mut Func, inst: Inst, pred: IntPred, lhs: Value, rhs: Value) -> Value {
905 let ty = func[lhs].ty.with_lane(Type::I1);
906 let args = func.push_values(&[lhs, rhs]);
907 let extra = Extra::IntPred(pred);
908 written(func, inst, InstData { args, extra, ..InstData::new(Opcode::ICmp) }, ty)
909}
910
911fn substitute(func: &mut Func, forward: &HashMap<Value, Value>) {
918 let with = |value: Value| forward.get(&value).copied().unwrap_or(value);
919 for block in func.blocks().collect::<Vec<_>>() {
920 for inst in func.insts(block).collect::<Vec<Inst>>() {
921 let args = func[inst].args;
922 func.rewrite(args, with);
923 for call in func.successors(inst).collect::<Vec<_>>() {
924 func.rewrite(call.args, with);
925 }
926 }
927 }
928}
929
930fn countable(ty: Type) -> bool {
940 ty.is_int()
941 && ty.is_scalar()
942 && ty.bits() >= 8
943 && ty.bits() <= 64
944 && ty.bits().is_power_of_two()
945}
946
947pub const UNROLL: usize = 32;
960
961pub fn bulk(func: &mut Func, names: &mut Interner, word: u32) {
972 let found: Vec<Inst> =
973 func.blocks().flat_map(|block| func.insts(block).collect::<Vec<_>>()).collect();
974 for inst in found {
975 match func[inst].opcode {
976 Opcode::Memcpy => copy(func, names, inst, word),
977 Opcode::Memset => fill(func, names, inst, word),
978 Opcode::Memmove => library(func, names, inst, "memmove", word),
979 _ => {}
980 }
981 }
982}
983
984fn copy(func: &mut Func, names: &mut Interner, inst: Inst, word: u32) {
992 let [into, from] = func[func[inst].args] else { return };
993 let Extra::Mem(mem) = func[inst].extra else { return };
994 let info = func[mem];
995 let Some(plan) = chunks(info, word) else { return library(func, names, inst, "memcpy", word) };
996 for (at, width) in plan {
997 let ty = Type::int(width * 8);
998 let access = MemInfo { size: u64::from(width), align: width.min(info.align), ..info };
999 let there = stepped(func, inst, from, at);
1000 let word = read(func, inst, there, access, ty);
1001 let here = stepped(func, inst, into, at);
1002 write(func, inst, word, here, access);
1003 }
1004 func.remove_inst(inst);
1005}
1006
1007fn fill(func: &mut Func, names: &mut Interner, inst: Inst, word: u32) {
1014 let [into, byte] = func[func[inst].args] else { return };
1015 let Extra::Mem(mem) = func[inst].extra else { return };
1016 let info = func[mem];
1017 let Some(spelled) = literal(func, byte) else {
1018 return library(func, names, inst, "memset", word);
1019 };
1020 let Some(plan) = chunks(info, word) else { return library(func, names, inst, "memset", word) };
1021 for (at, width) in plan {
1022 let ty = Type::int(width * 8);
1023 let access = MemInfo { size: u64::from(width), align: width.min(info.align), ..info };
1024 let value = ahead_const(func, inst, Imm::int(spread(spelled, width) as i128, ty), ty);
1025 let here = stepped(func, inst, into, at);
1026 write(func, inst, value, here, access);
1027 }
1028 func.remove_inst(inst);
1029}
1030
1031fn library(func: &mut Func, names: &mut Interner, inst: Inst, routine: &str, word: u32) {
1044 let [into, second] = func[func[inst].args] else { return };
1045 let Extra::Mem(mem) = func[inst].extra else { return };
1046 let size = func[mem].size;
1047
1048 let words = Type::int(word * 8);
1052 let count = ahead_const(func, inst, Imm::int(i128::from(size), words), words);
1053 let second = match routine {
1056 "memset" => widened(func, inst, second),
1057 _ => second,
1058 };
1059
1060 let sig = func.add_signature(Signature::new().with_params(&[
1061 Type::PTR,
1062 if routine == "memset" { Type::int(32) } else { Type::PTR },
1063 words,
1064 ]));
1065 let callee = names.intern(routine);
1066 let varargs = func.push_abis(&[]);
1067 let info = func.add_call(CallInfo { callee: Some(callee), signature: sig, varargs });
1068 let args = func.push_values(&[into, second, count]);
1069 let data = &mut func[inst];
1070 data.opcode = Opcode::Call;
1071 data.args = args;
1072 data.extra = Extra::Call(info);
1073 data.flags = data.flags.intersection(Flags::legal_on(Opcode::Call));
1074}
1075
1076fn widened(func: &mut Func, inst: Inst, value: Value) -> Value {
1078 let int = Type::int(32);
1079 let ty = func[value].ty;
1080 if ty == int {
1081 return value;
1082 }
1083 ahead(func, inst, Opcode::ZExt, &[value], int)
1084}
1085
1086fn chunks(info: MemInfo, word: u32) -> Option<Vec<(u64, u32)>> {
1099 plan(info.size, info.align, word)
1100}
1101
1102pub(crate) fn plan(size: u64, align: u32, word: u32) -> Option<Vec<(u64, u32)>> {
1110 let widest = word.min(align).max(1);
1111 if !widest.is_power_of_two() {
1112 return None;
1113 }
1114 let mut plan = Vec::new();
1115 let mut at = 0;
1116 let mut width = u64::from(widest);
1117 while at < size {
1118 while width > size - at {
1119 width /= 2;
1120 }
1121 plan.push((at, u32::try_from(width).ok()?));
1122 at += width;
1123 if plan.len() > UNROLL {
1124 return None;
1125 }
1126 }
1127 Some(plan)
1128}
1129
1130fn literal(func: &Func, value: Value) -> Option<u8> {
1132 let Def::Result { inst, .. } = func[value].def else { return None };
1133 if func[inst].opcode != Opcode::IConst {
1134 return None;
1135 }
1136 let Extra::Imm(imm) = func[inst].extra else { return None };
1137 u8::try_from(func[imm].bits() & 0xff).ok()
1138}
1139
1140fn spread(byte: u8, width: u32) -> u64 {
1142 (0..width).fold(0, |word, at| word | u64::from(byte) << (at * 8))
1143}
1144
1145fn stepped(func: &mut Func, inst: Inst, block: Value, at: u64) -> Value {
1148 if at == 0 {
1149 return block;
1150 }
1151 let step = ahead_const(func, inst, Imm::int(i128::from(at), Type::int(64)), Type::int(64));
1152 ahead(func, inst, Opcode::PtrAdd, &[block, step], Type::PTR)
1153}
1154
1155fn read(func: &mut Func, inst: Inst, from: Value, info: MemInfo, ty: Type) -> Value {
1157 let extra = Extra::Mem(func.add_mem(info));
1158 let args = func.push_values(&[from]);
1159 written(func, inst, InstData { args, extra, ..InstData::new(Opcode::Load) }, ty)
1160}
1161
1162fn write(func: &mut Func, inst: Inst, value: Value, into: Value, info: MemInfo) {
1164 let span = func.span(inst);
1165 let extra = Extra::Mem(func.add_mem(info));
1166 let args = func.push_values(&[value, into]);
1167 let data = InstData { args, extra, ..InstData::new(Opcode::Store) };
1168 let made = func.create_inst(data, &[], span);
1169 func.insert_before(made, inst);
1170}
1171
1172fn holder(bits: u32, signed: bool) -> Option<u32> {
1181 match if signed { bits } else { bits + 1 } {
1182 ..=32 => Some(32),
1183 33..=64 => Some(64),
1184 _ => None,
1185 }
1186}
1187
1188fn produced(func: &Func, inst: Inst) -> Type {
1193 func[inst].first_result.map_or(Type::VOID, |value| func[value].ty)
1194}
1195
1196fn ahead(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value], ty: Type) -> Value {
1198 let args = func.push_values(args);
1199 written(func, inst, InstData { args, ..InstData::new(opcode) }, ty)
1200}
1201
1202fn ahead_cmp(func: &mut Func, inst: Inst, opcode: Opcode, extra: Extra, args: &[Value]) -> Value {
1204 let args = func.push_values(args);
1205 written(func, inst, InstData { args, extra, ..InstData::new(opcode) }, Type::I1)
1206}
1207
1208fn ahead_const(func: &mut Func, inst: Inst, imm: Imm, ty: Type) -> Value {
1210 let extra = Extra::Imm(func.add_imm(imm));
1211 written(func, inst, InstData { extra, ..InstData::new(Opcode::IConst) }, ty)
1212}
1213
1214fn ahead_float(func: &mut Func, inst: Inst, bits: u128, ty: Type) -> Value {
1216 let extra = Extra::Imm(func.add_imm(Imm::from_bits(bits)));
1217 written(func, inst, InstData { extra, ..InstData::new(Opcode::FConst) }, ty)
1218}
1219
1220fn written(func: &mut Func, inst: Inst, data: InstData, ty: Type) -> Value {
1222 let span = func.span(inst);
1223 let made = func.create_inst(data, &[ty], span);
1224 func.insert_before(made, inst);
1225 func[made].first_result.expect("an instruction created with one result has one")
1226}
1227
1228fn becomes(func: &mut Func, inst: Inst, opcode: Opcode, args: &[Value]) {
1235 let args = func.push_values(args);
1236 let data = &mut func[inst];
1237 data.opcode = opcode;
1238 data.args = args;
1239 data.extra = Extra::None;
1240 data.flags = data.flags.intersection(Flags::legal_on(opcode));
1243}
1244
1245#[cfg(test)]
1246mod tests {
1247 use rucc_base::Interner;
1248 use rucc_ir::{Builder, Flags, Float, Func, Module, Opcode, Signature, Type};
1249 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1250
1251 use rucc_ir::{Extra, InstData, MemInfo, MemOrder, Restrict};
1252
1253 use super::{
1254 UNROLL, alternating, bulk, bytes, chunks, counts, every, floats, orderings, overflows,
1255 spread,
1256 };
1257
1258 fn target() -> TargetInfo {
1259 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
1260 }
1261
1262 fn printed(func: &Func, names: &mut Interner) -> String {
1263 let module = Module::new(names.intern("sw.c"), &target());
1264 rucc_ir::print_func(&module, func, names)
1265 }
1266
1267 fn one(
1272 params: &[Type],
1273 returns: &[Type],
1274 body: impl FnOnce(&mut Builder<'_>, &[rucc_ir::Value]),
1275 ) -> (Interner, Func) {
1276 let mut names = Interner::new();
1277 let mut func = Func::new(
1278 names.intern("f"),
1279 Signature::new().with_params(params).with_returns(returns),
1280 );
1281 let entry = func.create_block();
1282 let args: Vec<_> = params.iter().map(|&ty| func.append_param(entry, ty)).collect();
1283 let mut build = Builder::new(&mut func, entry);
1284 body(&mut build, &args);
1285 (names, func)
1286 }
1287
1288 fn f64() -> Type {
1289 Type::float(Float::F64)
1290 }
1291
1292 fn f32() -> Type {
1293 Type::float(Float::F32)
1294 }
1295
1296 fn f80() -> Type {
1297 Type::float(Float::F80)
1298 }
1299
1300 const CASES: &[u64] = &[
1305 0,
1306 1,
1307 2,
1308 0x7FFF_FFFF,
1309 0x8000_0000,
1310 0xFFFF_FFFF,
1311 0x0020_0000_0000_0000,
1312 0x0020_0000_0000_0001,
1313 0x7FFF_FFFF_FFFF_FFFF,
1314 0x8000_0000_0000_0000,
1315 0x8000_0000_0000_0001,
1316 0x8000_0000_0000_0400,
1317 0xFFFF_FFFF_FFFF_F800,
1318 0xFFFF_FFFF_FFFF_FFFF,
1319 ];
1320
1321 fn valid(func: &Func, names: &mut Interner) {
1323 let module = Module::new(names.intern("f.c"), &target());
1324 rucc_ir::verify_func(&module, func, names).expect("the rewrite builds valid IR");
1325 }
1326
1327 #[test]
1329 fn a_float_constant_becomes_the_integer_that_spells_it_and_a_reading_of_those_bits() {
1330 let (mut names, mut func) = one(&[], &[f64()], |build, _| {
1331 let k = build.fconst(f64(), 0x3ff8_0000_0000_0000);
1332 build.ret(&[k]);
1333 });
1334 floats(&mut func);
1335
1336 let text = printed(&func, &mut names);
1337 assert!(!text.contains("fconst"), "the float constant is gone: {text}");
1338 assert!(text.contains("iconst.i64 4609434218613702656"), "the bits, as an integer: {text}");
1339 assert!(text.contains("bitcast"), "read back as the float: {text}");
1340 }
1341
1342 #[test]
1345 fn a_constant_at_the_narrow_format_is_an_integer_of_the_narrow_width() {
1346 let (mut names, mut func) = one(&[], &[f32()], |build, _| {
1347 let k = build.fconst(f32(), 0x4020_0000);
1348 build.ret(&[k]);
1349 });
1350 floats(&mut func);
1351 assert!(printed(&func, &mut names).contains("iconst.i32"), "an i32, not an i64");
1352 }
1353
1354 #[test]
1357 fn a_negation_flips_the_sign_bit_and_touches_no_other() {
1358 let (mut names, mut func) = one(&[f64()], &[f64()], |build, args| {
1359 let n = build.unary(Opcode::FNeg, args[0], f64());
1360 build.ret(&[n]);
1361 });
1362 floats(&mut func);
1363
1364 let text = printed(&func, &mut names);
1365 assert!(!text.contains("fneg"), "the negation is gone: {text}");
1366 assert!(!text.contains("fsub"), "and it did not become a subtraction: {text}");
1367 assert!(text.contains("iconst.i64 -9223372036854775808"), "the sign bit alone: {text}");
1368 assert_eq!(text.matches("xor").count(), 1, "one exclusive or: {text}");
1369 assert_eq!(text.matches("bitcast").count(), 2, "there and back: {text}");
1370 }
1371
1372 #[test]
1374 fn an_unsigned_integer_becoming_a_float_widens_first_and_then_converts_as_signed() {
1375 let (mut names, mut func) = one(&[Type::int(32)], &[f64()], |build, args| {
1376 let d = build.unary(Opcode::UIToFP, args[0], f64());
1377 build.ret(&[d]);
1378 });
1379 floats(&mut func);
1380
1381 let text = printed(&func, &mut names);
1382 assert!(!text.contains("uitofp"), "the unsigned conversion is gone: {text}");
1383 assert!(text.contains("zext.i64"), "widened with zeroes: {text}");
1384 assert!(text.contains("sitofp.f64"), "converted as signed: {text}");
1385 }
1386
1387 #[test]
1389 fn a_float_becoming_an_unsigned_integer_converts_as_signed_first_and_then_narrows() {
1390 let (mut names, mut func) = one(&[f64()], &[Type::int(32)], |build, args| {
1391 let n = build.unary(Opcode::FPToUI, args[0], Type::int(32));
1392 build.ret(&[n]);
1393 });
1394 floats(&mut func);
1395
1396 let text = printed(&func, &mut names);
1397 assert!(!text.contains("fptoui"), "the unsigned conversion is gone: {text}");
1398 assert!(text.contains("fptosi.i64"), "converted as signed: {text}");
1399 assert!(text.contains("trunc.i32"), "and narrowed to what was asked: {text}");
1400 }
1401
1402 #[test]
1405 fn a_conversion_narrower_than_the_machine_has_is_one_it_has_and_a_narrowing() {
1406 let (mut names, mut func) = one(&[f64()], &[Type::int(8)], |build, args| {
1407 let n = build.unary(Opcode::FPToSI, args[0], Type::int(8));
1408 build.ret(&[n]);
1409 });
1410 floats(&mut func);
1411
1412 let text = printed(&func, &mut names);
1413 assert!(text.contains("fptosi.i32"), "converted at a width there is one at: {text}");
1414 assert!(text.contains("trunc.i8"), "and narrowed to what was asked: {text}");
1415 }
1416
1417 #[test]
1419 fn a_signed_integer_narrower_than_the_machine_converts_from_is_widened_with_its_sign() {
1420 let (mut names, mut func) = one(&[Type::int(8)], &[f64()], |build, args| {
1421 let d = build.unary(Opcode::SIToFP, args[0], f64());
1422 build.ret(&[d]);
1423 });
1424 floats(&mut func);
1425
1426 let text = printed(&func, &mut names);
1427 assert!(text.contains("sext.i32"), "widened with the sign and not with zeroes: {text}");
1428 assert!(!text.contains("zext"), "widened with the sign and not with zeroes: {text}");
1429 assert!(text.contains("sitofp.f64"), "converted at a width there is one at: {text}");
1430 }
1431
1432 #[test]
1434 fn the_width_a_conversion_happens_at_is_the_narrowest_one_that_holds_the_values() {
1435 use super::holder;
1436 for bits in [1, 8, 16, 32] {
1437 assert_eq!(holder(bits, true), Some(32), "a signed {bits} bit value fits in an int");
1438 }
1439 assert_eq!(holder(64, true), Some(64));
1440 for bits in [1, 8, 16, 31] {
1441 assert_eq!(holder(bits, false), Some(32), "an unsigned {bits} bit value does too");
1442 }
1443 assert_eq!(holder(32, false), Some(64));
1445 assert_eq!(holder(64, false), None);
1446 }
1447
1448 #[test]
1452 fn the_unsigned_conversions_at_the_widest_width_become_the_signed_one_and_a_correction() {
1453 for float in [f32(), f64()] {
1454 let (mut names, mut func) = one(&[Type::int(64)], &[float], |build, args| {
1455 let d = build.unary(Opcode::UIToFP, args[0], float);
1456 build.ret(&[d]);
1457 });
1458 floats(&mut func);
1459 let text = printed(&func, &mut names);
1460 assert!(!text.contains("uitofp"), "the unsigned conversion is gone: {text}");
1461 assert!(text.contains("sitofp"), "the signed one is what is left: {text}");
1462 assert!(text.contains("lshr"), "the value is halved: {text}");
1465 assert!(text.contains("fadd"), "and doubled again afterwards: {text}");
1466 valid(&func, &mut names);
1467 }
1468
1469 for float in [f32(), f64()] {
1470 let (mut names, mut func) = one(&[float], &[Type::int(64)], |build, args| {
1471 let n = build.unary(Opcode::FPToUI, args[0], Type::int(64));
1472 build.ret(&[n]);
1473 });
1474 floats(&mut func);
1475 let text = printed(&func, &mut names);
1476 assert!(!text.contains("fptoui"), "the unsigned conversion is gone: {text}");
1477 assert!(text.contains("fptosi"), "the signed one is what is left: {text}");
1478 assert!(text.contains("fsub"), "the value is brought down: {text}");
1480 assert!(text.contains("shl"), "and the top bit goes back on: {text}");
1481 valid(&func, &mut names);
1482 }
1483 }
1484
1485 #[test]
1489 fn the_widest_unsigned_conversions_are_written_without_a_branch() {
1490 let (_, mut func) = one(&[Type::int(64)], &[f64()], |build, args| {
1491 let d = build.unary(Opcode::UIToFP, args[0], f64());
1492 build.ret(&[d]);
1493 });
1494 floats(&mut func);
1495 assert_eq!(func.blocks().count(), 1, "the conversion did not split the block");
1496
1497 let (_, mut func) = one(&[f64()], &[Type::int(64)], |build, args| {
1498 let n = build.unary(Opcode::FPToUI, args[0], Type::int(64));
1499 build.ret(&[n]);
1500 });
1501 floats(&mut func);
1502 assert_eq!(func.blocks().count(), 1, "nor did the other one");
1503 }
1504
1505 #[test]
1512 fn the_arithmetic_the_widest_unsigned_conversions_do_is_the_conversion() {
1513 for &x in CASES {
1514 let mask = if (x as i64) < 0 { u64::MAX } else { 0 };
1516 let odd = (x >> 1) | (x & 1);
1517 let source = x ^ ((x ^ odd) & mask);
1518 let converted = source as i64 as f64;
1519 let addend = f64::from_bits(converted.to_bits() & mask);
1520 assert_eq!(converted + addend, x as f64, "converting {x:#x} into a double");
1521 }
1522
1523 for &x in CASES {
1524 let d = x as f64;
1526 if d >= 18_446_744_073_709_551_616.0 {
1527 continue;
1528 }
1529 let half = f64::from_bits(0x43E0_0000_0000_0000);
1530 let mask = if d >= half { u64::MAX } else { 0 };
1531 let taken = f64::from_bits(half.to_bits() & mask);
1532 let low = (d - taken) as i64;
1533 let top = u64::from(d >= half) << 63;
1534 assert_eq!(low as u64 ^ top, d as u64, "converting {d} into an unsigned word");
1535 }
1536 }
1537
1538 #[test]
1545 fn the_unsigned_conversions_at_eighty_bits_correct_with_a_multiply_instead_of_a_mask() {
1546 let (mut names, mut func) = one(&[Type::int(64)], &[f80()], |build, args| {
1547 let d = build.unary(Opcode::UIToFP, args[0], f80());
1548 build.ret(&[d]);
1549 });
1550 floats(&mut func);
1551 let text = printed(&func, &mut names);
1552 assert!(!text.contains("uitofp"), "the unsigned conversion is gone: {text}");
1553 assert!(text.contains("sitofp.f80"), "the signed one is what is left: {text}");
1554 assert!(!text.contains("bitcast"), "and nothing reads the float as an integer: {text}");
1555 assert!(!text.contains("lshr"), "nor is the value halved, since nothing rounds: {text}");
1556 assert!(text.contains("fmul "), "the constant is taken or not by a multiply: {text}");
1557 assert!(text.contains("fadd "), "and added to what the conversion gave: {text}");
1558 assert_eq!(func.blocks().count(), 1, "the conversion did not split the block");
1559 valid(&func, &mut names);
1560
1561 let (mut names, mut func) = one(&[f80()], &[Type::int(64)], |build, args| {
1562 let n = build.unary(Opcode::FPToUI, args[0], Type::int(64));
1563 build.ret(&[n]);
1564 });
1565 floats(&mut func);
1566 let text = printed(&func, &mut names);
1567 assert!(!text.contains("fptoui"), "the unsigned conversion is gone: {text}");
1568 assert!(text.contains("fptosi.i64"), "the signed one is what is left: {text}");
1569 assert!(!text.contains("bitcast"), "and nothing reads the float as an integer: {text}");
1570 assert!(text.contains("fmul "), "the constant is taken or not by a multiply: {text}");
1571 assert!(text.contains("fsub "), "and subtracted before the conversion: {text}");
1572 assert!(text.contains("shl"), "with the top bit going back on after it: {text}");
1573 assert_eq!(func.blocks().count(), 1, "nor did the other one");
1574 valid(&func, &mut names);
1575 }
1576
1577 #[test]
1587 fn nothing_in_either_conversion_at_eighty_bits_rounds() {
1588 fn exact(v: i128) -> bool {
1590 let mag = v.unsigned_abs();
1591 mag == 0 || (mag >> mag.trailing_zeros()) < 1 << 64
1592 }
1593
1594 for &x in CASES {
1595 let signed = i128::from(x as i64);
1597 let addend = if (x as i64) < 0 { 1i128 << 64 } else { 0 };
1598 assert!(exact(signed), "the conversion of {x:#x} read as signed is exact");
1599 assert!(exact(addend), "and so is the constant it gets");
1600 assert!(exact(signed + addend), "and so is the sum");
1601 assert_eq!(signed + addend, i128::from(x), "converting {x:#x} into a long double");
1602 }
1603
1604 for &x in CASES {
1605 let value = i128::from(x);
1607 let taken = if value >= 1 << 63 { 1i128 << 63 } else { 0 };
1608 let under = value - taken;
1609 assert!(exact(under), "the subtraction that brings {x:#x} into range is exact");
1610 let top = u64::from(value >= 1 << 63) << 63;
1611 assert_eq!(under as u64 ^ top, x, "converting {x:#x} back into an unsigned word");
1612 }
1613 }
1614
1615 #[test]
1618 fn what_the_float_rewrites_leave_is_valid_ir() {
1619 let (mut names, mut func) = one(&[Type::int(32)], &[f64()], |build, args| {
1620 let k = build.fconst(f64(), 0x3ff8_0000_0000_0000);
1621 let d = build.unary(Opcode::UIToFP, args[0], f64());
1622 let n = build.unary(Opcode::FNeg, d, f64());
1623 let s = build.binary(Opcode::FAdd, n, k, Flags::NONE);
1624 build.ret(&[s]);
1625 });
1626 floats(&mut func);
1627 let module = Module::new(names.intern("f.c"), &target());
1628 rucc_ir::verify_func(&module, &func, &names).expect("the rewrite builds valid IR");
1629 }
1630
1631 #[test]
1634 fn a_function_with_no_floats_in_it_is_left_exactly_as_it_was() {
1635 let (mut names, mut func) = one(&[Type::int(32)], &[Type::int(32)], |build, args| {
1636 build.ret(&[args[0]]);
1637 });
1638 let before = printed(&func, &mut names);
1639 floats(&mut func);
1640 assert_eq!(printed(&func, &mut names), before);
1641 }
1642 fn access(size: u64, align: u32) -> MemInfo {
1643 MemInfo { size, align, order: MemOrder::NotAtomic, tbaa: None, restrict: Restrict::NONE }
1644 }
1645
1646 fn moving(opcode: Opcode, size: u64, align: u32, byte: Option<i128>) -> (Interner, Func) {
1649 one(&[Type::PTR, Type::PTR], &[], |build, args| {
1650 let second = match byte {
1651 Some(value) => build.iconst(Type::int(8), value),
1652 None => args[1],
1653 };
1654 let mem = build.func().add_mem(access(size, align));
1655 let operands = build.func().push_values(&[args[0], second]);
1656 let data = InstData { args: operands, extra: Extra::Mem(mem), ..InstData::new(opcode) };
1657 build.inst(data, &[]);
1658 build.ret(&[]);
1659 })
1660 }
1661
1662 fn copying(size: u64, align: u32) -> (Interner, Func) {
1663 moving(Opcode::Memcpy, size, align, None)
1664 }
1665
1666 fn filling(size: u64, align: u32, byte: i128) -> (Interner, Func) {
1667 moving(Opcode::Memset, size, align, Some(byte))
1668 }
1669
1670 fn widths(size: u64, align: u32) -> Option<Vec<u32>> {
1673 Some(chunks(access(size, align), 8)?.into_iter().map(|(_, width)| width).collect())
1674 }
1675
1676 #[test]
1678 fn a_copy_becomes_a_load_and_a_store_for_each_word_of_it() {
1679 let (mut names, mut func) = copying(16, 8);
1680 bulk(&mut func, &mut names, 8);
1681
1682 let text = printed(&func, &mut names);
1683 assert!(!text.contains("memcpy"), "the copy is gone: {text}");
1684 assert_eq!(text.matches("load.i64").count(), 2, "a load per word: {text}");
1685 assert_eq!(text.matches("store").count(), 2, "a store per word: {text}");
1686 assert_eq!(
1687 text.matches("ptr_add").count(),
1688 2,
1689 "no offset for the word at the front: {text}"
1690 );
1691 }
1692
1693 #[test]
1697 fn a_word_is_as_wide_as_the_block_is_aligned_to() {
1698 assert_eq!(widths(16, 8), Some(vec![8, 8]));
1699 assert_eq!(widths(16, 4), Some(vec![4, 4, 4, 4]));
1700 assert_eq!(widths(4, 1), Some(vec![1, 1, 1, 1]));
1701 }
1702
1703 #[test]
1706 fn what_is_left_over_is_narrower_words_and_not_a_run_of_bytes() {
1707 assert_eq!(widths(13, 8), Some(vec![8, 4, 1]));
1708 assert_eq!(widths(3, 8), Some(vec![2, 1]));
1709 assert_eq!(widths(1, 8), Some(vec![1]));
1710 }
1711
1712 #[test]
1715 fn every_word_starts_somewhere_it_is_aligned_for() {
1716 for (at, width) in chunks(access(13, 8), 8).expect("a plan for thirteen bytes") {
1717 assert_eq!(at % u64::from(width), 0, "{at} is a multiple of {width}");
1718 }
1719 }
1720
1721 #[test]
1723 fn a_fill_is_the_byte_spread_across_each_word() {
1724 let (mut names, mut func) = filling(16, 8, 0);
1725 bulk(&mut func, &mut names, 8);
1726
1727 let text = printed(&func, &mut names);
1728 assert!(!text.contains("memset"), "the fill is gone: {text}");
1729 assert_eq!(text.matches("store").count(), 2, "a store per word: {text}");
1730 assert!(!text.contains("load"), "a fill reads nothing: {text}");
1731 }
1732
1733 #[test]
1736 fn the_byte_is_repeated_across_the_word_it_is_stored_as() {
1737 assert_eq!(spread(0, 8), 0);
1738 assert_eq!(spread(0xff, 1), 0xff);
1739 assert_eq!(spread(0xff, 4), 0xffff_ffff);
1740 assert_eq!(spread(0xab, 2), 0xabab);
1741 assert_eq!(spread(0xab, 8), 0xabab_abab_abab_abab);
1742 }
1743
1744 #[test]
1746 fn a_copy_too_large_to_unroll_becomes_a_call_to_the_runtime() {
1747 let size = u64::try_from(UNROLL).expect("a small threshold") + 1;
1748 let (mut names, mut func) = copying(size, 1);
1749 bulk(&mut func, &mut names, 8);
1750 let text = printed(&func, &mut names);
1751 assert!(text.contains("call @memcpy"), "a call and not a bulk move: {text}");
1752
1753 let (mut names, mut func) = copying(size - 1, 1);
1756 bulk(&mut func, &mut names, 8);
1757 assert!(!printed(&func, &mut names).contains("memcpy"), "one word under it is unrolled");
1758 }
1759
1760 #[test]
1763 fn the_call_passes_the_size_that_the_instruction_carried_beside_it() {
1764 let size = u64::try_from(UNROLL).expect("a small threshold") + 1;
1765 let (mut names, mut func) = copying(size, 1);
1766 bulk(&mut func, &mut names, 8);
1767 let text = printed(&func, &mut names);
1768 assert!(text.contains(&format!("{size}")), "the size is an argument now: {text}");
1769 }
1770
1771 #[test]
1774 fn a_move_is_a_call_however_small_it_is() {
1775 let (mut names, mut func) = moving(Opcode::Memmove, 8, 8, None);
1776 bulk(&mut func, &mut names, 8);
1777 let text = printed(&func, &mut names);
1778 assert!(text.contains("call @memmove"), "a call and not a run of moves: {text}");
1779 }
1780
1781 #[test]
1784 fn a_fill_whose_byte_is_not_a_constant_becomes_a_call() {
1785 let (mut names, mut func) = one(&[Type::PTR, Type::int(8)], &[], |build, args| {
1786 let mem = build.func().add_mem(access(8, 8));
1787 let operands = build.func().push_values(&[args[0], args[1]]);
1788 let data = InstData {
1789 args: operands,
1790 extra: Extra::Mem(mem),
1791 ..InstData::new(Opcode::Memset)
1792 };
1793 build.inst(data, &[]);
1794 build.ret(&[]);
1795 });
1796 bulk(&mut func, &mut names, 8);
1797 let text = printed(&func, &mut names);
1798 assert!(text.contains("call @memset"), "a call and not a run of stores: {text}");
1799 assert!(text.contains("zext.i32"), "the byte is widened to what C passes: {text}");
1801 }
1802
1803 #[test]
1806 fn no_word_is_wider_than_the_machine_moves_at_once() {
1807 assert_eq!(chunks(access(8, 8), 4).map(|plan| plan.len()), Some(2));
1808 assert_eq!(chunks(access(8, 8), 8).map(|plan| plan.len()), Some(1));
1809 }
1810
1811 #[test]
1812 fn what_a_copy_becomes_is_ir_that_verifies() {
1813 let (mut names, mut func) = copying(13, 8);
1814 bulk(&mut func, &mut names, 8);
1815 let module = Module::new(names.intern("c.c"), &target());
1816 rucc_ir::verify_func(&module, &func, &names).expect("the rewrite builds valid IR");
1817 }
1818
1819 #[test]
1820 fn what_a_fill_becomes_is_ir_that_verifies() {
1821 let (mut names, mut func) = filling(13, 8, 0xff);
1822 bulk(&mut func, &mut names, 8);
1823 let module = Module::new(names.intern("f.c"), &target());
1824 rucc_ir::verify_func(&module, &func, &names).expect("the rewrite builds valid IR");
1825 }
1826
1827 #[test]
1828 fn what_a_copy_too_large_to_unroll_becomes_is_ir_that_verifies() {
1829 let size = u64::try_from(UNROLL).expect("a small threshold") + 1;
1830 let (mut names, mut func) = copying(size, 1);
1831 bulk(&mut func, &mut names, 8);
1832 let module = Module::new(names.intern("c.c"), &target());
1833 rucc_ir::verify_func(&module, &func, &names).expect("the call is valid IR");
1834 }
1835
1836 #[test]
1838 fn a_function_with_no_bulk_move_in_it_is_left_exactly_as_it_was() {
1839 let (mut names, mut func) = one(&[Type::int(32)], &[Type::int(32)], |build, args| {
1840 build.ret(&[args[0]]);
1841 });
1842 let before = printed(&func, &mut names);
1843 bulk(&mut func, &mut names, 8);
1844 assert_eq!(printed(&func, &mut names), before);
1845 }
1846
1847 fn swapping(width: u32) -> (Interner, Func) {
1850 let ty = Type::int(width);
1851 one(&[ty], &[ty], |build, args| {
1852 let s = build.unary(Opcode::Bswap, args[0], ty);
1853 build.ret(&[s]);
1854 })
1855 }
1856
1857 #[test]
1864 fn the_masks_are_the_alternating_runs_of_the_group_being_swapped() {
1865 assert_eq!(alternating(32, 16), 0x0000_ffff);
1866 assert_eq!(alternating(32, 8), 0x00ff_00ff);
1867 assert_eq!(alternating(16, 8), 0x00ff);
1868 assert_eq!(alternating(64, 32), 0x0000_0000_ffff_ffff);
1869 assert_eq!(alternating(64, 16), 0x0000_ffff_0000_ffff);
1870 assert_eq!(alternating(64, 8), 0x00ff_00ff_00ff_00ff);
1871 }
1872
1873 #[test]
1875 fn a_two_byte_swap_is_one_exchange_of_neighbouring_bytes() {
1876 let (mut names, mut func) = swapping(16);
1877 bytes(&mut func);
1878
1879 let text = printed(&func, &mut names);
1880 assert!(!text.contains("bswap"), "the instruction is gone: {text}");
1881 assert!(text.contains("iconst.i16 255"), "the low byte of the pair: {text}");
1882 assert_eq!(text.matches("shl").count(), 1, "one shift up: {text}");
1883 assert_eq!(text.matches("lshr").count(), 1, "one shift down: {text}");
1884 assert_eq!(text.matches(" or ").count(), 1, "and the two put together: {text}");
1885 }
1886
1887 #[test]
1890 fn a_wider_swap_is_the_same_exchange_once_per_halving() {
1891 for (width, steps) in [(16u32, 1usize), (32, 2), (64, 3)] {
1892 let (mut names, mut func) = swapping(width);
1893 bytes(&mut func);
1894 let text = printed(&func, &mut names);
1895 assert_eq!(text.matches("shl").count(), steps, "at {width}: {text}");
1896 assert_eq!(text.matches("lshr").count(), steps, "at {width}: {text}");
1897 assert_eq!(text.matches(" and ").count(), steps * 2, "at {width}: {text}");
1898 assert_eq!(text.matches(" or ").count(), steps, "at {width}: {text}");
1899 }
1900 }
1901
1902 #[test]
1905 fn the_shift_counts_are_the_group_width_halving_as_it_goes() {
1906 let (mut names, mut func) = swapping(64);
1907 bytes(&mut func);
1908 let text = printed(&func, &mut names);
1909 for count in ["iconst.i64 32", "iconst.i64 16", "iconst.i64 8"] {
1910 assert!(text.contains(count), "{count} is a step: {text}");
1911 }
1912 }
1913
1914 #[test]
1917 fn what_a_byte_swap_becomes_is_ir_that_verifies() {
1918 let (mut names, mut func) = swapping(32);
1919 bytes(&mut func);
1920 let module = Module::new(names.intern("b.c"), &target());
1921 rucc_ir::verify_func(&module, &func, &names).expect("the rewrite builds valid IR");
1922 }
1923
1924 #[test]
1927 fn a_function_with_no_byte_swap_in_it_is_left_exactly_as_it_was() {
1928 let (mut names, mut func) = one(&[Type::int(32)], &[Type::int(32)], |build, args| {
1929 build.ret(&[args[0]]);
1930 });
1931 let before = printed(&func, &mut names);
1932 bytes(&mut func);
1933 assert_eq!(printed(&func, &mut names), before);
1934 }
1935
1936 fn counting(op: Opcode, width: u32) -> (Interner, Func) {
1938 let ty = Type::int(width);
1939 one(&[ty], &[ty], |build, args| {
1940 let c = build.unary(op, args[0], ty);
1941 build.ret(&[c]);
1942 })
1943 }
1944
1945 #[test]
1948 fn the_counting_masks_are_the_ones_the_halving_sum_is_written_with() {
1949 assert_eq!(alternating(32, 1), 0x5555_5555);
1950 assert_eq!(alternating(32, 2), 0x3333_3333);
1951 assert_eq!(alternating(32, 4), 0x0f0f_0f0f);
1952 assert_eq!(every(32, 8, 1), 0x0101_0101);
1953 assert_eq!(every(64, 8, 1), 0x0101_0101_0101_0101);
1954 }
1955
1956 #[test]
1959 fn a_set_bit_count_is_the_halving_sum_and_a_multiply_that_adds_the_bytes() {
1960 let (mut names, mut func) = counting(Opcode::Ctpop, 32);
1961 counts(&mut func);
1962
1963 let text = printed(&func, &mut names);
1964 assert!(!text.contains("ctpop"), "the instruction is gone: {text}");
1965 assert!(text.contains("iconst.i32 1431655765"), "the pairs mask: {text}");
1966 assert!(text.contains("iconst.i32 858993459"), "the nibbles mask: {text}");
1967 assert!(text.contains("iconst.i32 252645135"), "the bytes mask: {text}");
1968 assert_eq!(text.matches(" mul ").count(), 1, "one multiply: {text}");
1969 assert!(text.contains("iconst.i32 24"), "and the top byte is the answer: {text}");
1970 }
1971
1972 #[test]
1974 fn a_count_of_one_byte_stops_before_the_multiply() {
1975 let (mut names, mut func) = counting(Opcode::Ctpop, 8);
1976 counts(&mut func);
1977 let text = printed(&func, &mut names);
1978 assert!(!text.contains("ctpop"), "{text}");
1979 assert!(!text.contains(" mul "), "nothing to add together: {text}");
1980 }
1981
1982 #[test]
1985 fn a_leading_zero_count_smears_the_value_down_and_counts_the_complement() {
1986 let (mut names, mut func) = counting(Opcode::Ctlz, 32);
1987 counts(&mut func);
1988
1989 let text = printed(&func, &mut names);
1990 assert!(!text.contains("ctlz"), "the instruction is gone: {text}");
1991 assert!(!text.contains("ctpop"), "and so is the count it became: {text}");
1992 for by in ["iconst.i32 1", "iconst.i32 2", "iconst.i32 4", "iconst.i32 8", "iconst.i32 16"]
1993 {
1994 assert!(text.contains(by), "{by} is a smearing step: {text}");
1995 }
1996 assert_eq!(text.matches(" xor ").count(), 1, "one complement: {text}");
1997 }
1998
1999 #[test]
2001 fn a_trailing_zero_count_masks_the_bits_below_the_lowest_set_one() {
2002 let (mut names, mut func) = counting(Opcode::Cttz, 32);
2003 counts(&mut func);
2004
2005 let text = printed(&func, &mut names);
2006 assert!(!text.contains("cttz"), "the instruction is gone: {text}");
2007 assert!(!text.contains("ctpop"), "and so is the count it became: {text}");
2008 assert!(text.contains("iconst.i32 -1"), "the complement and the decrement: {text}");
2009 assert_eq!(text.matches(" xor ").count(), 1, "one complement: {text}");
2010 assert!(text.matches(" or ").count() <= 1, "no smearing run: {text}");
2012 }
2013
2014 #[test]
2017 fn what_a_bit_count_becomes_is_ir_that_verifies() {
2018 for op in [Opcode::Ctpop, Opcode::Ctlz, Opcode::Cttz] {
2019 for width in [8u32, 16, 32, 64] {
2020 let (mut names, mut func) = counting(op, width);
2021 counts(&mut func);
2022 let module = Module::new(names.intern("c.c"), &target());
2023 rucc_ir::verify_func(&module, &func, &names)
2024 .unwrap_or_else(|e| panic!("{op:?} at {width}: {e:?}"));
2025 }
2026 }
2027 }
2028
2029 #[test]
2033 fn a_width_the_halving_sum_is_not_written_for_is_left_alone() {
2034 let (mut names, mut func) = counting(Opcode::Ctpop, 24);
2035 counts(&mut func);
2036 assert!(printed(&func, &mut names).contains("ctpop"), "left as it was");
2037 }
2038
2039 #[test]
2041 fn a_function_with_no_bit_count_in_it_is_left_exactly_as_it_was() {
2042 let (mut names, mut func) = one(&[Type::int(32)], &[Type::int(32)], |build, args| {
2043 build.ret(&[args[0]]);
2044 });
2045 let before = printed(&func, &mut names);
2046 counts(&mut func);
2047 assert_eq!(printed(&func, &mut names), before);
2048 }
2049
2050 fn checking(op: Opcode, width: u32) -> (Interner, Func) {
2053 let ty = Type::int(width);
2054 let bit = ty.with_lane(Type::I1);
2055 one(&[ty, ty], &[ty, bit], |build, args| {
2056 let (value, flag) = build.checked(op, args[0], args[1]);
2057 build.ret(&[value, flag]);
2058 })
2059 }
2060
2061 #[test]
2064 fn a_checked_unsigned_add_becomes_an_add_and_one_comparison() {
2065 let (mut names, mut func) = checking(Opcode::UAddOverflow, 32);
2066 overflows(&mut func);
2067
2068 let text = printed(&func, &mut names);
2069 assert!(!text.contains("uadd_overflow"), "the instruction is gone: {text}");
2070 assert_eq!(text.matches(" add ").count(), 1, "one add: {text}");
2071 assert_eq!(text.matches("icmp ult").count(), 1, "and one comparison: {text}");
2072 assert!(!text.contains(" xor "), "nothing about sign bits: {text}");
2073 }
2074
2075 #[test]
2078 fn a_checked_signed_add_becomes_an_add_and_the_sign_bit_of_two_exclusive_ors() {
2079 let (mut names, mut func) = checking(Opcode::SAddOverflow, 32);
2080 overflows(&mut func);
2081
2082 let text = printed(&func, &mut names);
2083 assert!(!text.contains("sadd_overflow"), "the instruction is gone: {text}");
2084 assert_eq!(text.matches(" add ").count(), 1, "one add: {text}");
2085 assert_eq!(text.matches(" xor ").count(), 2, "the answer against each operand: {text}");
2086 assert_eq!(text.matches(" and ").count(), 1, "both at once: {text}");
2087 assert!(text.contains("icmp slt"), "and its sign bit: {text}");
2088 }
2089
2090 #[test]
2093 fn a_checked_unsigned_subtract_compares_the_operands_and_not_the_answer() {
2094 let (mut names, mut func) = checking(Opcode::USubOverflow, 64);
2095 overflows(&mut func);
2096
2097 let text = printed(&func, &mut names);
2098 assert!(!text.contains("usub_overflow"), "the instruction is gone: {text}");
2099 assert_eq!(text.matches(" sub ").count(), 1, "one subtract: {text}");
2100 assert!(text.contains("icmp ult %0, %1"), "the operands, in order: {text}");
2101 }
2102
2103 #[test]
2110 fn a_checked_multiply_becomes_a_multiply_and_the_high_half_of_the_product() {
2111 let (mut names, mut func) = checking(Opcode::UMulOverflow, 64);
2112 overflows(&mut func);
2113
2114 let text = printed(&func, &mut names);
2115 assert!(!text.contains("umul_overflow"), "the instruction is gone: {text}");
2116 assert_eq!(text.matches(" mul ").count(), 5, "the answer and the four halves: {text}");
2117 assert!(text.contains("iconst.i64 32"), "split at half the width: {text}");
2118 assert!(text.contains("iconst.i64 4294967295"), "and masked to it: {text}");
2119 assert!(text.contains("icmp ne"), "the high half against zero: {text}");
2120 assert!(!text.contains("ashr"), "and nothing corrected for sign: {text}");
2121 }
2122
2123 #[test]
2126 fn a_checked_signed_multiply_corrects_the_high_half_for_each_negative_operand() {
2127 let (mut names, mut func) = checking(Opcode::SMulOverflow, 64);
2128 overflows(&mut func);
2129
2130 let text = printed(&func, &mut names);
2131 assert!(!text.contains("smul_overflow"), "the instruction is gone: {text}");
2132 assert_eq!(
2133 text.matches(" ashr ").count(),
2134 3,
2135 "each operand's sign, and the answer: {text}"
2136 );
2137 assert!(text.contains("iconst.i64 63"), "spread from the top bit: {text}");
2138 assert_eq!(text.matches(" sub ").count(), 2, "one correction per operand: {text}");
2139 }
2140
2141 #[test]
2145 fn both_results_are_substituted_into_whoever_was_reading_them() {
2146 let (mut names, mut func) = checking(Opcode::SAddOverflow, 32);
2147 overflows(&mut func);
2148
2149 let text = printed(&func, &mut names);
2153 assert_eq!(
2154 text,
2155 concat!(
2156 "func @f(i32, i32) -> (i32, i1), linkage(external) {\n",
2157 "block0(%0: i32, %1: i32):\n",
2158 " %2 = add %0, %1\n",
2159 " %3 = xor %0, %2\n",
2160 " %4 = xor %1, %2\n",
2161 " %5 = and %3, %4\n",
2162 " %6 = iconst.i32 0\n",
2163 " %7 = icmp slt %5, %6\n",
2164 " return %2, %7\n",
2165 "}\n",
2166 ),
2167 );
2168 }
2169
2170 #[test]
2173 fn what_an_overflow_check_becomes_is_ir_that_verifies() {
2174 let all = [
2175 Opcode::UAddOverflow,
2176 Opcode::SAddOverflow,
2177 Opcode::USubOverflow,
2178 Opcode::SSubOverflow,
2179 Opcode::UMulOverflow,
2180 Opcode::SMulOverflow,
2181 ];
2182 for op in all {
2183 for width in [8u32, 16, 32, 64] {
2184 let (mut names, mut func) = checking(op, width);
2185 overflows(&mut func);
2186 let module = Module::new(names.intern("c.c"), &target());
2187 rucc_ir::verify_func(&module, &func, &names)
2188 .unwrap_or_else(|e| panic!("{op:?} at {width}: {e:?}"));
2189 }
2190 }
2191 }
2192
2193 #[test]
2197 fn a_width_the_split_is_not_written_for_is_left_alone() {
2198 let (mut names, mut func) = checking(Opcode::UMulOverflow, 24);
2199 overflows(&mut func);
2200 assert!(printed(&func, &mut names).contains("umul_overflow"), "left as it was");
2201 }
2202
2203 #[test]
2205 fn a_function_with_no_overflow_check_in_it_is_left_exactly_as_it_was() {
2206 let (mut names, mut func) = one(&[Type::int(32)], &[Type::int(32)], |build, args| {
2207 build.ret(&[args[0]]);
2208 });
2209 let before = printed(&func, &mut names);
2210 overflows(&mut func);
2211 assert_eq!(printed(&func, &mut names), before);
2212 }
2213
2214 fn reading(ty: Type, align: u32, order: MemOrder) -> (Interner, Func) {
2216 one(&[Type::PTR], &[ty], |build, args| {
2217 let info = MemInfo { order, ..access(0, align) };
2218 let value = build.atomic_load(ty, args[0], info, Flags::NONE);
2219 build.ret(&[value]);
2220 })
2221 }
2222
2223 fn writing(ty: Type, align: u32, order: MemOrder) -> (Interner, Func) {
2225 one(&[Type::PTR, ty], &[], |build, args| {
2226 let info = MemInfo { order, ..access(0, align) };
2227 build.atomic_store(args[1], args[0], info, Flags::NONE);
2228 build.ret(&[]);
2229 })
2230 }
2231
2232 #[test]
2239 fn an_ordered_access_becomes_the_plain_one_this_machine_already_orders() {
2240 for order in [MemOrder::Relaxed, MemOrder::Acquire, MemOrder::SeqCst] {
2241 let (mut names, mut func) = reading(Type::int(32), 4, order);
2242 orderings(&mut func, 8);
2243 let text = printed(&func, &mut names);
2244 assert!(text.contains("load.i32"), "{order:?}: {text}");
2245 assert!(!text.contains("atomic_load"), "{order:?}: {text}");
2246 assert!(!text.contains(order.name()), "the ordering came off: {text}");
2247 }
2248
2249 for order in [MemOrder::Relaxed, MemOrder::Release] {
2250 let (mut names, mut func) = writing(Type::int(32), 4, order);
2251 orderings(&mut func, 8);
2252 let text = printed(&func, &mut names);
2253 assert!(text.contains("store %1 -> %0"), "{order:?}: {text}");
2254 assert!(!text.contains("atomic_store"), "{order:?}: {text}");
2255 assert!(!text.contains("fence"), "{order:?} costs nothing here: {text}");
2256 }
2257 }
2258
2259 #[test]
2265 fn the_strongest_store_keeps_a_barrier_behind_it() {
2266 let (mut names, mut func) = writing(Type::int(32), 4, MemOrder::SeqCst);
2267 orderings(&mut func, 8);
2268 let text = printed(&func, &mut names);
2269 let (before, after) = text.split_once("fence seq_cst").expect("a barrier");
2270 assert!(before.contains("store %1 -> %0"), "the store comes first: {text}");
2271 assert!(!after.contains("store"), "and nothing is between them: {text}");
2272 assert!(!text.contains("atomic_store"), "{text}");
2273 }
2274
2275 #[test]
2278 fn a_barrier_is_left_for_the_place_that_knows_what_one_costs() {
2279 for order in MemOrder::all().filter(|&order| order != MemOrder::NotAtomic) {
2280 let (mut names, mut func) = one(&[], &[], |build, _| {
2281 build.fence(order);
2282 build.ret(&[]);
2283 });
2284 let before = printed(&func, &mut names);
2285 orderings(&mut func, 8);
2286 assert_eq!(printed(&func, &mut names), before, "{order:?}");
2287 }
2288 }
2289
2290 #[test]
2296 fn an_access_this_machine_cannot_do_in_one_go_is_left_alone() {
2297 for (ty, align) in [(Type::int(128), 16), (Type::int(64), 4)] {
2298 let (mut names, mut func) = reading(ty, align, MemOrder::SeqCst);
2299 orderings(&mut func, 8);
2300 assert!(printed(&func, &mut names).contains("atomic_load"), "left as it was");
2301 }
2302 }
2303
2304 #[test]
2307 fn what_the_ordered_accesses_become_verifies() {
2308 for order in MemOrder::all().filter(|&order| order != MemOrder::NotAtomic) {
2309 for (mut names, mut func) in
2310 [reading(Type::int(32), 4, order), writing(Type::int(32), 4, order)]
2311 {
2312 if !order.is_valid_for_load() && !order.is_valid_for_store() {
2313 continue;
2314 }
2315 orderings(&mut func, 8);
2316 let module = Module::new(names.intern("a.c"), &target());
2317 rucc_ir::verify_func(&module, &func, &names)
2318 .unwrap_or_else(|e| panic!("{order:?}: {e:?}"));
2319 }
2320 }
2321 }
2322
2323 #[test]
2325 fn a_function_with_no_ordered_access_in_it_is_left_exactly_as_it_was() {
2326 let (mut names, mut func) = one(&[Type::int(32)], &[Type::int(32)], |build, args| {
2327 build.ret(&[args[0]]);
2328 });
2329 let before = printed(&func, &mut names);
2330 orderings(&mut func, 8);
2331 assert_eq!(printed(&func, &mut names), before);
2332 }
2333}