1use std::cmp::Ordering;
194use std::collections::HashMap;
195use std::sync::OnceLock;
196
197use rucc_base::float::Float;
198use rucc_ir::term::{PLAIN, Plan, Shown, Term, Terms};
199use rucc_ir::{
200 Block, Def, Extra, Flags, FloatPred, Func, Imm, Inst, InstData, IntPred, Opcode, Type, Value,
201};
202
203use crate::cfg::Cfg;
204use crate::discharge::constant;
205use crate::rules::{
206 Match, Piece, Subject, Table, canonical, compare, identities, select, strength, width,
207};
208use crate::uses::{count, substitute};
209use crate::{Analyses, Analysis, Fuel, Pass, Preserved, Stats};
210
211const FLIPPED: &str = "comparison negated by an exclusive or rewritten as the opposite comparison";
213
214const NO_FUEL: &str = "negated comparison left alone, the pass ran out of fuel";
216
217const COMPOSITE: &str = "two comparisons over the same operands combined into one";
219
220const NO_FUEL_COMPOSITE: &str = "pair of comparisons left alone, the pass ran out of fuel";
222
223const MAGNITUDE: &str = "comparison against a value whose sign bit is clear settled by the sign";
225
226const NO_FUEL_MAGNITUDE: &str =
228 "comparison against a magnitude left alone, the pass ran out of fuel";
229
230const BOUNDED: &str = "floating point comparison settled by a constant or by one operand twice";
232
233const NO_FUEL_BOUNDED: &str =
235 "floating point comparison against a bound left alone, the pass ran out of fuel";
236
237const NO_FUEL_RULE: &str = "rewrite left alone, the pass ran out of fuel";
239
240const PLANS: [Plan; 3] =
248 [[Shown::Reg, Shown::Const, Shown::Reg], [Shown::Const, Shown::Reg, Shown::Reg], PLAIN];
249
250const CANONICAL: [Plan; 1] = [[Shown::Const, Shown::Var, Shown::Reg]];
260
261const EXPAND: [Plan; 1] = [[Shown::Expand, Shown::Reg, Shown::Reg]];
271
272const COMPARE: [Plan; 2] =
292 [[Shown::Reg, Shown::Const, Shown::Reg], [Shown::Expand, Shown::Const, Shown::Reg]];
293
294const SELECT: [Plan; 3] = [
305 [Shown::Reg, Shown::Const, Shown::Const],
306 [Shown::Reg, Shown::Expand, Shown::Reg],
307 [Shown::Reg, Shown::Reg, Shown::Expand],
308];
309
310const TABLES: [(&Table, &[Plan]); 6] = [
332 (&identities::TABLE, &PLANS),
333 (&strength::TABLE, &PLANS),
334 (&width::TABLE, &EXPAND),
335 (&compare::TABLE, &COMPARE),
336 (&select::TABLE, &SELECT),
337 (&canonical::TABLE, &CANONICAL),
338];
339
340#[derive(Debug, Clone, Copy, PartialEq, Eq)]
342pub struct Simplify;
343
344impl Pass for Simplify {
345 fn name(&self) -> &'static str {
346 "simplify"
347 }
348
349 fn describe(&self) -> &'static str {
350 "the identities, the strength reductions, the canonicalisations, and the four comparison \
351 rewrites written by hand"
352 }
353
354 fn preserves(&self) -> Preserved {
355 Preserved::ALL.without(Analysis::Liveness)
369 }
370
371 fn run(&self, func: &mut Func, _an: &mut Analyses, fuel: &mut Fuel) -> Stats {
372 let mut stats = Stats::new();
373 let mut forward: HashMap<Value, Value> = HashMap::new();
378 let uses = count(func);
390 let cfg = Cfg::new(func);
393 let dead = |func: &Func, inst: Inst| match func[inst].first_result {
394 Some(result) => uses[result.index()] == 0,
395 None => false,
396 };
397 for block in func.blocks().collect::<Vec<Block>>() {
398 for inst in func.insts(block).collect::<Vec<Inst>>() {
399 if dead(func, inst) {
400 continue;
401 }
402 if let Some(flip) = negated_comparison(func, inst) {
403 if !fuel.take() {
404 stats.missed(NO_FUEL);
408 continue;
409 }
410 become_flipped(func, inst, &flip);
411 stats.optimized(FLIPPED);
412 continue;
413 }
414 if let Some(composite) = composite_comparison(func, inst) {
415 if !fuel.take() {
416 stats.missed(NO_FUEL_COMPOSITE);
417 continue;
418 }
419 fold_composite(func, inst, composite);
420 stats.optimized(COMPOSITE);
421 continue;
422 }
423 if let Some(settled) = magnitude_comparison(func, inst) {
424 if !fuel.take() {
425 stats.missed(NO_FUEL_MAGNITUDE);
426 continue;
427 }
428 fold_composite(func, inst, settled);
429 stats.optimized(MAGNITUDE);
430 continue;
431 }
432 if let Some(settled) = bounded_comparison(func, &cfg, inst) {
433 if !fuel.take() {
434 stats.missed(NO_FUEL_BOUNDED);
435 continue;
436 }
437 fold_composite(func, inst, settled);
438 stats.optimized(BOUNDED);
439 continue;
440 }
441 let Some((rewrite, pattern)) = identity(func, inst) else { continue };
442 if !fuel.take() {
443 stats.missed(NO_FUEL_RULE);
444 continue;
445 }
446 match rewrite {
447 Rewrite::Value(value) => {
448 let result = func[inst].first_result.expect("the rule matched a result");
449 forward.insert(result, value);
450 }
451 Rewrite::Constant(number) => become_constant(func, inst, number),
452 Rewrite::Built { opcode, pred, lhs, rhs } => {
453 become_instruction(func, inst, opcode, pred, lhs, rhs);
454 }
455 Rewrite::Converted { opcode, from } => {
456 let ty =
457 func[func[inst].first_result.expect("the rule matched a result")].ty;
458 let from = defined(func, inst, ty, from);
459 become_conversion(func, inst, opcode, from);
460 }
461 }
462 stats.optimized(pattern);
463 }
464 }
465 if !forward.is_empty() {
466 substitute(func, &forward);
467 }
468 stats
469 }
470}
471
472#[derive(Clone, Debug, PartialEq, Eq)]
474enum Rewrite {
475 Value(Value),
477 Constant(i128),
479 Built {
481 opcode: Opcode,
483 pred: Option<IntPred>,
490 lhs: Operand,
492 rhs: Operand,
494 },
495 Converted {
503 opcode: Opcode,
505 from: Operand,
507 },
508}
509
510#[derive(Clone, Debug, PartialEq, Eq)]
512enum Operand {
513 Value(Value),
515 Constant {
519 number: i128,
521 bits: u32,
529 },
530 Built(Box<Nested>),
532}
533
534#[derive(Clone, Debug, PartialEq, Eq)]
536struct Nested {
537 opcode: Opcode,
539 pred: Option<IntPred>,
541 bits: u32,
543 args: Vec<Operand>,
545}
546
547fn identity(func: &Func, inst: Inst) -> Option<(Rewrite, &'static str)> {
553 let result = func[inst].first_result?;
554 for (table, plan) in
555 TABLES.into_iter().flat_map(|(table, plans)| plans.iter().map(move |&plan| (table, plan)))
556 {
557 let terms = Terms::new(func, inst, plan);
558 let Some(found) = table.find(&terms, Term::Root) else { continue };
559 let rule = table.rule(&found);
560 let rewrite = match rule.replacement {
561 [Piece::App { head, arity: 1 }, Piece::Var { index, .. }]
564 if head.starts_with("value.") =>
565 {
566 match found.bindings.get(*index) {
567 Some(&Term::Reg(value)) => Rewrite::Value(value),
568 _ => continue,
569 }
570 }
571 [Piece::App { head, arity: 1 }, Piece::Int(number)]
575 if head.starts_with("iconst.") && func[result].ty.is_int() =>
576 {
577 Rewrite::Constant(*number)
578 }
579 pieces => match built(pieces, &found, &matched(&terms, &found)) {
583 Some(rewrite) if nests(&rewrite) && func[result].ty.is_vector() => continue,
586 Some(rewrite) => rewrite,
587 None => continue,
591 },
592 };
593 return Some((rewrite, rule.pattern));
594 }
595 None
596}
597
598fn built(
605 pieces: &'static [Piece],
606 found: &Match<Term>,
607 matched: &[Option<i128>],
608) -> Option<Rewrite> {
609 if let Some(rewrite) = converted(pieces, found, matched) {
610 return Some(rewrite);
611 }
612 let [Piece::App { head, arity: 2 }, rest @ ..] = pieces else { return None };
613 let opcode = opcode_of(head)?;
614 let pred = rucc_ir::term::int_pred(head);
617 if (opcode == Opcode::ICmp) != pred.is_some() {
618 return None;
622 }
623 let (lhs, rest) = operand(rest, found, matched)?;
624 let (rhs, rest) = operand(rest, found, matched)?;
625 rest.is_empty().then_some(Rewrite::Built { opcode, pred, lhs, rhs })
626}
627
628fn matched(terms: &Terms<'_>, found: &Match<Term>) -> Vec<Option<i128>> {
634 found.bindings.iter().map(|&node| terms.int(node)).collect()
635}
636
637fn converted(
649 pieces: &'static [Piece],
650 found: &Match<Term>,
651 matched: &[Option<i128>],
652) -> Option<Rewrite> {
653 let [Piece::App { head, arity: 1 }, rest @ ..] = pieces else { return None };
654 let opcode = match opcode_of(head)? {
655 opcode @ (Opcode::SExt | Opcode::ZExt | Opcode::Trunc) => opcode,
656 _ => return None,
657 };
658 match operand(rest, found, matched)? {
659 (Operand::Constant { .. }, _) => None,
660 (from, []) => Some(Rewrite::Converted { opcode, from }),
661 _ => None,
662 }
663}
664
665fn nests(rewrite: &Rewrite) -> bool {
667 match rewrite {
668 Rewrite::Built { lhs, rhs, .. } => {
669 matches!(lhs, Operand::Built(_)) || matches!(rhs, Operand::Built(_))
670 }
671 Rewrite::Converted { from, .. } => matches!(from, Operand::Built(_)),
672 Rewrite::Value(_) | Rewrite::Constant(_) => false,
673 }
674}
675
676fn operand(
678 pieces: &'static [Piece],
679 found: &Match<Term>,
680 matched: &[Option<i128>],
681) -> Option<(Operand, &'static [Piece])> {
682 match pieces {
683 [Piece::App { head, arity: 1 }, Piece::Var { index, .. }, rest @ ..]
684 if head.starts_with("value.") =>
685 {
686 match found.bindings.get(*index) {
687 Some(&Term::Reg(value)) => Some((Operand::Value(value), rest)),
688 _ => None,
689 }
690 }
691 [Piece::App { head, arity: 1 }, Piece::Int(number), rest @ ..]
692 if head.starts_with("iconst.") =>
693 {
694 Some((Operand::Constant { number: *number, bits: bits_of(head)? }, rest))
695 }
696 [Piece::App { head, arity: 1 }, Piece::Computed { work, .. }, rest @ ..]
701 if head.starts_with("iconst.") =>
702 {
703 let number = work(matched)?;
704 Some((Operand::Constant { number, bits: bits_of(head)? }, rest))
705 }
706 [Piece::App { head, arity: 1 }, Piece::Var { index, .. }, rest @ ..]
710 if head.starts_with("iconst.") =>
711 {
712 match found.bindings.get(*index) {
713 Some(&Term::Num(number)) => {
714 Some((Operand::Constant { number, bits: bits_of(head)? }, rest))
715 }
716 _ => None,
717 }
718 }
719 [Piece::App { head, arity }, rest @ ..] => nested(head, *arity, rest, found, matched),
720 _ => None,
721 }
722}
723
724fn nested(
731 head: &str,
732 arity: usize,
733 pieces: &'static [Piece],
734 found: &Match<Term>,
735 matched: &[Option<i128>],
736) -> Option<(Operand, &'static [Piece])> {
737 let opcode = opcode_of(head)?;
738 let pred = rucc_ir::term::int_pred(head);
739 let converts = matches!(opcode, Opcode::SExt | Opcode::ZExt | Opcode::Trunc);
740 if opcode == Opcode::IConst
741 || (opcode == Opcode::ICmp) != pred.is_some()
742 || converts != (arity == 1)
743 || !(1..=2).contains(&arity)
744 {
745 return None;
746 }
747 let bits = bits_of(head)?;
748 let mut args = Vec::with_capacity(arity);
749 let mut rest = pieces;
750 for _ in 0..arity {
751 let (arg, after) = operand(rest, found, matched)?;
752 if converts && matches!(arg, Operand::Constant { .. }) {
753 return None;
754 }
755 args.push(arg);
756 rest = after;
757 }
758 Some((Operand::Built(Box::new(Nested { opcode, pred, bits, args })), rest))
759}
760
761fn bits_of(head: &str) -> Option<u32> {
768 head.rsplit_once('.')?.1.strip_prefix('i')?.parse().ok()
769}
770
771fn opcode_of(head: &str) -> Option<Opcode> {
782 static NAMES: OnceLock<HashMap<&'static str, Opcode>> = OnceLock::new();
783 let names = NAMES.get_or_init(|| {
784 let mut names = HashMap::new();
785 for (opcode, name) in rucc_ir::term::heads() {
786 names.entry(name).or_insert(opcode);
787 }
788 names
789 });
790 names.get(head).copied()
791}
792
793fn become_instruction(
798 func: &mut Func,
799 inst: Inst,
800 opcode: Opcode,
801 pred: Option<IntPred>,
802 lhs: Operand,
803 rhs: Operand,
804) {
805 let result = func[inst].first_result.expect("the rule matched a result");
806 let ty = func[result].ty;
807 let kept = carried(func, inst, opcode, &lhs, &rhs);
808 let lhs = defined(func, inst, ty, lhs);
809 let rhs = defined(func, inst, ty, rhs);
810 let args = func.push_values(&[lhs, rhs]);
811 let data = &mut func[inst];
812 data.opcode = opcode;
813 data.args = args;
814 data.extra = match pred {
820 Some(pred) => Extra::IntPred(pred),
821 None => Extra::None,
822 };
823 data.flags = kept;
830}
831
832fn carried(func: &Func, inst: Inst, now: Opcode, lhs: &Operand, rhs: &Operand) -> Flags {
854 let data = func[inst];
855 let args = &func[data.args];
856 let (Opcode::Mul, Some(&first), Some(&second)) = (data.opcode, args.first(), args.get(1))
857 else {
858 return Flags::NONE;
859 };
860 let (x, k) = match (constant(func, first), constant(func, second)) {
861 (None, Some(k)) => (first, k),
862 (Some(k), None) => (second, k),
863 _ => return Flags::NONE,
864 };
865 let both = data.flags.intersection(Flags::NSW.union(Flags::NUW));
866 match (now, lhs, rhs) {
867 (Opcode::Mul, &Operand::Value(v), &Operand::Constant { number, bits })
868 if v == x && bits < i128::BITS && (number ^ k) & ((1 << bits) - 1) == 0 =>
869 {
870 both
871 }
872 (Opcode::Add, &Operand::Value(v), &Operand::Value(w)) if v == x && w == x && k == 2 => both,
873 (Opcode::Sub, &Operand::Constant { number: 0, .. }, &Operand::Value(v))
874 if v == x && k == -1 =>
875 {
876 data.flags.intersection(Flags::NSW)
877 }
878 (Opcode::Shl, &Operand::Value(v), &Operand::Constant { number, bits })
879 if v == x && (0..i128::from(bits) - 1).contains(&number) && k == 1 << number =>
880 {
881 both
882 }
883 _ => Flags::NONE,
884 }
885}
886
887fn become_conversion(func: &mut Func, inst: Inst, opcode: Opcode, from: Value) {
897 let args = func.push_values(&[from]);
898 let data = &mut func[inst];
899 data.opcode = opcode;
900 data.args = args;
901 data.extra = Extra::None;
904 data.flags = Flags::NONE;
905}
906
907fn defined(func: &mut Func, before: Inst, ty: Type, operand: Operand) -> Value {
915 match operand {
916 Operand::Value(value) => value,
917 Operand::Constant { number, bits } => {
918 let ty = if ty.lane() == Type::int(bits) { ty } else { Type::int(bits) };
919 let at = func.add_imm(Imm::int(number, ty.lane()));
920 let data = InstData { extra: Extra::Imm(at), ..InstData::new(Opcode::IConst) };
921 let span = func.span(before);
922 let iconst = func.create_inst(data, &[ty], span);
923 func.insert_before(iconst, before);
924 func[iconst].first_result.expect("one result was asked for")
925 }
926 Operand::Built(nested) => {
927 let Nested { opcode, pred, bits, args } = *nested;
928 let ty = Type::int(bits);
929 let args: Vec<Value> =
930 args.into_iter().map(|arg| defined(func, before, ty, arg)).collect();
931 let args = func.push_values(&args);
932 let extra = pred.map_or(Extra::None, Extra::IntPred);
933 let data = InstData { args, extra, ..InstData::new(opcode) };
934 let span = func.span(before);
935 let inst = func.create_inst(data, &[ty], span);
936 func.insert_before(inst, before);
937 if let Some(flip) = negated_comparison(func, inst) {
940 become_flipped(func, inst, &flip);
941 }
942 func[inst].first_result.expect("one result was asked for")
943 }
944 }
945}
946
947fn become_flipped(func: &mut Func, inst: Inst, flip: &Flip) {
949 let args = func.push_values(&[flip.lhs, flip.rhs]);
950 let data = &mut func[inst];
951 data.opcode = flip.opcode;
952 data.flags = flip.flags;
953 data.args = args;
954 data.extra = flip.extra;
955}
956
957fn become_constant(func: &mut Func, inst: Inst, number: i128) {
962 let result = func[inst].first_result.expect("the rule matched a result");
963 let ty = func[result].ty;
964 let imm = func.add_imm(Imm::int(number, ty.lane()));
965 let args = func.push_values(&[]);
966 let data = &mut func[inst];
967 data.opcode = Opcode::IConst;
968 data.args = args;
969 data.extra = Extra::Imm(imm);
970 data.flags = Flags::NONE;
973}
974
975pub(crate) struct Flip {
977 opcode: Opcode,
979 flags: Flags,
981 extra: Extra,
983 lhs: Value,
985 rhs: Value,
987}
988
989fn negated_comparison(func: &Func, inst: Inst) -> Option<Flip> {
996 let data = &func[inst];
997 if data.opcode != Opcode::Xor {
998 return None;
999 }
1000 let args = &func[data.args];
1001 let (&first, &second) = (args.first()?, args.get(1)?);
1002 if func[first].ty != Type::int(1) {
1003 return None;
1004 }
1005 let cmp = match (all_ones(func, first), all_ones(func, second)) {
1006 (true, false) => second,
1007 (false, true) => first,
1008 _ => return None,
1011 };
1012 let Def::Result { inst: cmp, .. } = func[cmp].def else { return None };
1013 let data = &func[cmp];
1014 let extra = match (data.opcode, data.extra) {
1015 (Opcode::ICmp, Extra::IntPred(pred)) => Extra::IntPred(pred.inverse()),
1016 (Opcode::FCmp, Extra::FloatPred(pred)) => Extra::FloatPred(pred.inverse()),
1017 _ => return None,
1018 };
1019 let args = &func[data.args];
1020 Some(Flip {
1021 opcode: data.opcode,
1022 flags: data.flags,
1023 extra,
1024 lhs: *args.first()?,
1025 rhs: *args.get(1)?,
1026 })
1027}
1028
1029mod bucket {
1042 pub(super) const LT: u8 = 1;
1044 pub(super) const EQ: u8 = 2;
1046 pub(super) const GT: u8 = 4;
1048 pub(super) const UN: u8 = 8;
1050 pub(super) const ALL_INT: u8 = LT | EQ | GT;
1052 pub(super) const ALL_FLOAT: u8 = LT | EQ | GT | UN;
1054}
1055
1056#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1064enum Reading {
1065 Signed,
1067 Unsigned,
1069 Neither,
1071}
1072
1073impl Reading {
1074 const fn shared(self, other: Self) -> Option<Self> {
1076 match (self, other) {
1077 (Self::Neither, same) | (same, Self::Neither) => Some(same),
1078 (Self::Signed, Self::Signed) => Some(Self::Signed),
1079 (Self::Unsigned, Self::Unsigned) => Some(Self::Unsigned),
1080 (Self::Signed, Self::Unsigned) | (Self::Unsigned, Self::Signed) => None,
1081 }
1082 }
1083}
1084
1085const fn int_buckets(pred: IntPred) -> (u8, Reading) {
1087 use bucket::{EQ, GT, LT};
1088 match pred {
1089 IntPred::Eq => (EQ, Reading::Neither),
1090 IntPred::Ne => (LT | GT, Reading::Neither),
1091 IntPred::Slt => (LT, Reading::Signed),
1092 IntPred::Sle => (LT | EQ, Reading::Signed),
1093 IntPred::Sgt => (GT, Reading::Signed),
1094 IntPred::Sge => (GT | EQ, Reading::Signed),
1095 IntPred::Ult => (LT, Reading::Unsigned),
1096 IntPred::Ule => (LT | EQ, Reading::Unsigned),
1097 IntPred::Ugt => (GT, Reading::Unsigned),
1098 IntPred::Uge => (GT | EQ, Reading::Unsigned),
1099 }
1100}
1101
1102const fn int_pred(buckets: u8, reading: Reading) -> Option<IntPred> {
1110 use bucket::{EQ, GT, LT};
1111 match (buckets, reading) {
1112 (EQ, _) => Some(IntPred::Eq),
1113 (b, _) if b == LT | GT => Some(IntPred::Ne),
1114 (LT, Reading::Signed) => Some(IntPred::Slt),
1115 (GT, Reading::Signed) => Some(IntPred::Sgt),
1116 (b, Reading::Signed) if b == LT | EQ => Some(IntPred::Sle),
1117 (b, Reading::Signed) if b == GT | EQ => Some(IntPred::Sge),
1118 (LT, Reading::Unsigned) => Some(IntPred::Ult),
1119 (GT, Reading::Unsigned) => Some(IntPred::Ugt),
1120 (b, Reading::Unsigned) if b == LT | EQ => Some(IntPred::Ule),
1121 (b, Reading::Unsigned) if b == GT | EQ => Some(IntPred::Uge),
1122 _ => None,
1123 }
1124}
1125
1126const fn float_buckets(pred: FloatPred) -> u8 {
1131 use bucket::{ALL_FLOAT, EQ, GT, LT, UN};
1132 match pred {
1133 FloatPred::False => 0,
1134 FloatPred::Oeq => EQ,
1135 FloatPred::Ogt => GT,
1136 FloatPred::Oge => GT | EQ,
1137 FloatPred::Olt => LT,
1138 FloatPred::Ole => LT | EQ,
1139 FloatPred::One => LT | GT,
1140 FloatPred::Ord => LT | EQ | GT,
1141 FloatPred::Uno => UN,
1142 FloatPred::Ueq => EQ | UN,
1143 FloatPred::Ugt => GT | UN,
1144 FloatPred::Uge => GT | EQ | UN,
1145 FloatPred::Ult => LT | UN,
1146 FloatPred::Ule => LT | EQ | UN,
1147 FloatPred::Une => LT | GT | UN,
1148 FloatPred::True => ALL_FLOAT,
1149 }
1150}
1151
1152fn float_pred(buckets: u8) -> Option<FloatPred> {
1154 FloatPred::all().find(|pred| float_buckets(*pred) == buckets)
1155}
1156
1157struct Side {
1159 opcode: Opcode,
1161 flags: Flags,
1165 buckets: u8,
1167 reading: Reading,
1170 lhs: Value,
1172 rhs: Value,
1174}
1175
1176fn side(func: &Func, value: Value) -> Option<Side> {
1178 let Def::Result { inst, .. } = func[value].def else { return None };
1179 let data = &func[inst];
1180 let (buckets, reading) = match (data.opcode, data.extra) {
1181 (Opcode::ICmp, Extra::IntPred(pred)) => int_buckets(pred),
1182 (Opcode::FCmp, Extra::FloatPred(pred)) => (float_buckets(pred), Reading::Neither),
1183 _ => return None,
1184 };
1185 let args = &func[data.args];
1186 Some(Side {
1187 opcode: data.opcode,
1188 flags: data.flags,
1189 buckets,
1190 reading,
1191 lhs: *args.first()?,
1192 rhs: *args.get(1)?,
1193 })
1194}
1195
1196const fn turned(buckets: u8) -> u8 {
1201 use bucket::{GT, LT};
1202 let mut out = buckets & !(LT | GT);
1203 if buckets & LT != 0 {
1204 out |= GT;
1205 }
1206 if buckets & GT != 0 {
1207 out |= LT;
1208 }
1209 out
1210}
1211
1212fn aligned(first: &Side, second: Side) -> Option<Side> {
1218 if first.lhs == second.lhs && first.rhs == second.rhs {
1219 return Some(second);
1220 }
1221 if first.lhs != second.rhs || first.rhs != second.lhs {
1222 return None;
1223 }
1224 let buckets = turned(second.buckets);
1225 Some(Side { buckets, lhs: first.lhs, rhs: first.rhs, ..second })
1226}
1227
1228pub(crate) enum Composite {
1230 Always(bool),
1232 Pred(Flip),
1234}
1235
1236fn composite_comparison(func: &Func, inst: Inst) -> Option<Composite> {
1254 let data = &func[inst];
1255 if func[data.first_result?].ty != Type::int(1) {
1256 return None;
1257 }
1258 let args = &func[data.args];
1259 composite(func, data.opcode, *args.first()?, *args.get(1)?)
1260}
1261
1262pub(crate) fn composite(func: &Func, opcode: Opcode, lhs: Value, rhs: Value) -> Option<Composite> {
1269 let intersect = match opcode {
1270 Opcode::And => true,
1271 Opcode::Or => false,
1272 _ => return None,
1273 };
1274 let first = side(func, lhs)?;
1275 let second = aligned(&first, side(func, rhs)?)?;
1276 if first.opcode != second.opcode || first.flags != second.flags {
1277 return None;
1278 }
1279 let reading = first.reading.shared(second.reading)?;
1280 let buckets = match intersect {
1281 true => first.buckets & second.buckets,
1282 false => first.buckets | second.buckets,
1283 };
1284 let whole = match first.opcode {
1285 Opcode::ICmp => bucket::ALL_INT,
1286 _ => bucket::ALL_FLOAT,
1287 };
1288 if buckets == 0 {
1289 return Some(Composite::Always(false));
1290 }
1291 if buckets == whole {
1292 return Some(Composite::Always(true));
1293 }
1294 let extra = match first.opcode {
1295 Opcode::ICmp => Extra::IntPred(int_pred(buckets, reading)?),
1296 _ => Extra::FloatPred(float_pred(buckets)?),
1297 };
1298 Some(Composite::Pred(Flip {
1299 opcode: first.opcode,
1300 flags: first.flags,
1301 extra,
1302 lhs: first.lhs,
1303 rhs: first.rhs,
1304 }))
1305}
1306
1307fn magnitude(func: &Func, value: Value) -> bool {
1321 let Def::Result { inst, .. } = func[value].def else { return false };
1322 let data = &func[inst];
1323 if data.opcode != Opcode::Bitcast {
1324 return false;
1325 }
1326 let Some(&bits) = func[data.args].first() else { return false };
1327 let Def::Result { inst: masked, .. } = func[bits].def else { return false };
1328 let data = &func[masked];
1329 if data.opcode != Opcode::And {
1330 return false;
1331 }
1332 func[data.args].iter().any(|&arg| clears_the_sign(func, arg))
1333}
1334
1335fn clears_the_sign(func: &Func, value: Value) -> bool {
1337 let ty = func[value].ty;
1338 let Def::Result { inst, .. } = func[value].def else { return false };
1339 let data = &func[inst];
1340 let Extra::Imm(at) = data.extra else { return false };
1341 data.opcode == Opcode::IConst && ty.is_int() && func[at].signed(ty) >= 0
1342}
1343
1344fn against(func: &Func, value: Value) -> Option<u8> {
1354 use bucket::{EQ, GT, UN};
1355 let number = float_constant(func, value)?;
1356 match number.compare(Float::zero(number.format(), false))? {
1357 Ordering::Less => Some(GT | UN),
1358 Ordering::Equal => Some(GT | EQ | UN),
1359 Ordering::Greater => None,
1360 }
1361}
1362
1363fn magnitude_comparison(func: &Func, inst: Inst) -> Option<Composite> {
1378 let data = &func[inst];
1379 let Extra::FloatPred(pred) = data.extra else { return None };
1380 if data.opcode != Opcode::FCmp {
1381 return None;
1382 }
1383 let args = &func[data.args];
1384 let lhs = *args.first()?;
1385 let rhs = *args.get(1)?;
1386 let possible = if magnitude(func, lhs) {
1387 against(func, rhs)?
1388 } else if magnitude(func, rhs) {
1389 turned(against(func, lhs)?)
1390 } else {
1391 return None;
1392 };
1393 let asked = float_buckets(pred);
1394 let buckets = asked & possible;
1395 if buckets == asked {
1396 return None;
1397 }
1398 if buckets == 0 {
1399 return Some(Composite::Always(false));
1400 }
1401 Some(Composite::Pred(Flip {
1402 opcode: Opcode::FCmp,
1403 flags: data.flags,
1404 extra: Extra::FloatPred(float_pred(buckets)?),
1405 lhs,
1406 rhs,
1407 }))
1408}
1409
1410fn bounded_comparison(func: &Func, cfg: &Cfg, inst: Inst) -> Option<Composite> {
1428 use bucket::{ALL_FLOAT, EQ, GT, LT, UN};
1429 let data = &func[inst];
1430 let Extra::FloatPred(pred) = data.extra else { return None };
1431 if data.opcode != Opcode::FCmp {
1432 return None;
1433 }
1434 let args = &func[data.args];
1435 let lhs = *args.first()?;
1436 let rhs = *args.get(1)?;
1437 let left = float_constant(func, lhs);
1438 let right = float_constant(func, rhs);
1439 let possible = match (left, right) {
1440 _ if left.is_some_and(Float::is_nan) || right.is_some_and(Float::is_nan) => UN,
1441 (Some(left), Some(right)) => match left.compare(right)? {
1442 Ordering::Less => LT,
1443 Ordering::Equal => EQ,
1444 Ordering::Greater => GT,
1445 },
1446 (None, Some(bound)) => past(bound).unwrap_or(ALL_FLOAT),
1447 (Some(bound), None) => turned(past(bound).unwrap_or(ALL_FLOAT)),
1448 (None, None) if lhs == rhs => EQ | UN,
1449 (None, None) => ALL_FLOAT,
1450 };
1451 let possible = possible & guarded(func, cfg, func.block_of(inst)?, lhs, rhs);
1452 if possible == ALL_FLOAT {
1453 return None;
1454 }
1455 let asked = float_buckets(pred);
1456 let buckets = asked & possible;
1457 if buckets == 0 {
1458 return Some(Composite::Always(false));
1459 }
1460 if buckets == possible {
1461 return Some(Composite::Always(true));
1462 }
1463 if buckets == asked {
1464 return None;
1465 }
1466 Some(Composite::Pred(Flip {
1467 opcode: Opcode::FCmp,
1468 flags: data.flags,
1469 extra: Extra::FloatPred(float_pred(buckets)?),
1470 lhs,
1471 rhs,
1472 }))
1473}
1474
1475const GUARDS: u32 = 8;
1477
1478fn guarded(func: &Func, cfg: &Cfg, block: Block, lhs: Value, rhs: Value) -> u8 {
1485 let mut possible = bucket::ALL_FLOAT;
1486 let mut at = block;
1487 for _ in 0..GUARDS {
1488 let &[from] = cfg.predecessors(at) else { break };
1489 if let Some(buckets) = edge(func, from, at, lhs, rhs) {
1490 possible &= buckets;
1491 }
1492 at = from;
1493 }
1494 possible
1495}
1496
1497fn edge(func: &Func, from: Block, to: Block, lhs: Value, rhs: Value) -> Option<u8> {
1501 let term = func.terminator(from)?;
1502 if func[term].opcode != Opcode::BrIf {
1503 return None;
1504 }
1505 let calls: Vec<_> = func.successors(term).collect();
1506 let (then, other) = (calls.first()?, calls.get(1)?);
1507 if then.block == other.block {
1508 return None;
1509 }
1510 let cond = *func[func[term].args].first()?;
1511 let Def::Result { inst, .. } = func[cond].def else { return None };
1512 let data = &func[inst];
1513 let Extra::FloatPred(pred) = data.extra else { return None };
1514 if data.opcode != Opcode::FCmp {
1515 return None;
1516 }
1517 let args = &func[data.args];
1518 let (&left, &right) = (args.first()?, args.get(1)?);
1519 let accepted = if then.block == to {
1520 float_buckets(pred)
1521 } else {
1522 bucket::ALL_FLOAT & !float_buckets(pred)
1523 };
1524 if (left, right) == (lhs, rhs) {
1525 Some(accepted)
1526 } else if (left, right) == (rhs, lhs) {
1527 Some(turned(accepted))
1528 } else {
1529 None
1530 }
1531}
1532
1533fn past(bound: Float) -> Option<u8> {
1538 use bucket::{EQ, GT, LT, UN};
1539 if !bound.is_infinite() {
1540 return None;
1541 }
1542 Some(if bound.is_negative() { GT | EQ | UN } else { LT | EQ | UN })
1543}
1544
1545fn float_constant(func: &Func, value: Value) -> Option<Float> {
1547 let Def::Result { inst, .. } = func[value].def else { return None };
1548 let data = &func[inst];
1549 if data.opcode != Opcode::FConst {
1550 return None;
1551 }
1552 let Extra::Imm(at) = data.extra else { return None };
1553 let format = func[value].ty.format()?.encoding();
1554 Some(Float::from_bits(format, func[at].bits()))
1555}
1556
1557pub(crate) fn fold_composite(func: &mut Func, inst: Inst, composite: Composite) {
1562 match composite {
1563 Composite::Always(answer) => become_constant(func, inst, answer.into()),
1564 Composite::Pred(flip) => {
1565 let args = func.push_values(&[flip.lhs, flip.rhs]);
1566 let data = &mut func[inst];
1567 data.opcode = flip.opcode;
1568 data.flags = flip.flags;
1569 data.args = args;
1570 data.extra = flip.extra;
1571 }
1572 }
1573}
1574
1575fn all_ones(func: &Func, value: Value) -> bool {
1577 let ty = func[value].ty;
1578 let Def::Result { inst, .. } = func[value].def else { return false };
1579 let data = &func[inst];
1580 let Extra::Imm(at) = data.extra else { return false };
1581 if data.opcode != Opcode::IConst {
1582 return false;
1583 }
1584 func[at].signed(ty) == -1
1587}
1588
1589#[cfg(test)]
1590mod tests {
1591 use rucc_base::Interner;
1592 use rucc_ir::{
1593 Block, Builder, Extra, Flags, Float, FloatPred, Func, IntPred, Module, Opcode, Signature,
1594 Type, Value,
1595 };
1596 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1597
1598 use super::{
1599 CANONICAL, COMPARE, EXPAND, PLANS, SELECT, Shown, TABLES, canonical, compare, identities,
1600 select, strength, width,
1601 };
1602 use crate::rules::Piece;
1603 use crate::stats::Kind;
1604 use crate::{Fuel, Pass, simplify::Simplify};
1605
1606 fn blank() -> (Interner, Func, Block) {
1608 let mut names = Interner::new();
1609 let name = names.intern("f");
1610 let mut func = Func::new(name, Signature::new().with_returns(&[Type::int(1)]));
1611 let block = func.create_block();
1612 (names, func, block)
1613 }
1614
1615 fn one_block(ty: Type) -> (Interner, Func, Block) {
1618 let mut names = Interner::new();
1619 let name = names.intern("f");
1620 let signature = Signature::new().with_params(&[ty]).with_returns(&[ty]);
1621 let mut func = Func::new(name, signature);
1622 let block = func.create_block();
1623 (names, func, block)
1624 }
1625
1626 fn simplify(func: &mut Func) -> bool {
1628 Simplify
1629 .run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited())
1630 .changed()
1631 }
1632
1633 fn came_from(func: &Func, value: Value) -> (Opcode, Extra) {
1635 let rucc_ir::Def::Result { inst, .. } = func[value].def else { panic!("not a result") };
1636 (func[inst].opcode, func[inst].extra)
1637 }
1638
1639 fn returned(func: &Func, block: Block) -> Value {
1643 let inst = func.terminator(block).expect("the block has a terminator");
1644 func[func[inst].args][0]
1645 }
1646
1647 fn operands(func: &Func, value: Value) -> Vec<Value> {
1649 let rucc_ir::Def::Result { inst, .. } = func[value].def else { panic!("not a result") };
1650 func[func[inst].args].to_vec()
1651 }
1652
1653 fn number(func: &Func, value: Value) -> i128 {
1655 let rucc_ir::Def::Result { inst, .. } = func[value].def else { panic!("not a result") };
1656 let data = &func[inst];
1657 assert_eq!(data.opcode, Opcode::IConst, "not a constant");
1658 let Extra::Imm(at) = data.extra else { panic!("a constant with no number") };
1659 func[at].signed(func[value].ty)
1660 }
1661
1662 #[test]
1668 fn every_rule_leaves_a_shape_the_pass_knows_what_to_do_with() {
1669 for (table, _) in TABLES {
1670 for rule in table.rules {
1671 let known = matches!(
1672 rule.replacement,
1673 [Piece::App { head, arity: 1 }, Piece::Var { .. }]
1674 if head.starts_with("value.")
1675 ) || matches!(
1676 rule.replacement,
1677 [Piece::App { head, arity: 1 }, Piece::Int(_)]
1678 if head.starts_with("iconst.")
1679 ) || matches!(
1680 rule.replacement,
1681 [Piece::App { arity: 2, .. }, ..] if instruction(rule.replacement)
1682 ) || conversion(rule.replacement);
1683 assert!(known, "{} leaves a shape the pass would skip", rule.pattern);
1684 }
1685 }
1686 }
1687
1688 fn conversion(pieces: &'static [Piece]) -> bool {
1692 let [Piece::App { head, arity: 1 }, rest @ ..] = pieces else { return false };
1693 let converts =
1694 matches!(super::opcode_of(head), Some(Opcode::SExt | Opcode::ZExt | Opcode::Trunc));
1695 let number = matches!(rest, [Piece::App { head, .. }, ..] if head.starts_with("iconst."));
1696 converts && !number && shape(rest).is_some_and(<[Piece]>::is_empty)
1697 }
1698
1699 #[test]
1707 fn a_width_rule_writes_a_term_that_ends_where_the_one_it_matched_ended() {
1708 for rule in width::TABLE.rules {
1709 let [Piece::App { head, .. }, ..] = rule.replacement else {
1710 panic!("{} writes no head", rule.pattern)
1711 };
1712 let wrote = head.rsplit_once('.').expect("a replacement head names a width").1;
1713 let matched = rule
1714 .pattern
1715 .trim_start_matches('(')
1716 .split([' ', ')'])
1717 .next()
1718 .and_then(|head| head.rsplit_once('.'))
1719 .expect("a pattern head names a width")
1720 .1;
1721 assert_eq!(wrote, matched, "{} ends somewhere else", rule.pattern);
1722 }
1723 }
1724
1725 fn instruction(pieces: &'static [Piece]) -> bool {
1731 let [Piece::App { head, arity: 2 }, rest @ ..] = pieces else { return false };
1732 if super::opcode_of(head).is_none() {
1733 return false;
1734 }
1735 shape(rest).and_then(shape).is_some_and(<[Piece]>::is_empty)
1736 }
1737
1738 fn shape(pieces: &'static [Piece]) -> Option<&'static [Piece]> {
1742 match pieces {
1743 [Piece::App { head, arity: 1 }, Piece::Var { .. }, rest @ ..]
1744 if head.starts_with("value.") =>
1745 {
1746 Some(rest)
1747 }
1748 [
1749 Piece::App { head, arity: 1 },
1750 Piece::Int(_) | Piece::Var { .. } | Piece::Computed { .. },
1751 rest @ ..,
1752 ] if head.starts_with("iconst.") => Some(rest),
1753 [Piece::App { head, arity }, rest @ ..]
1754 if super::opcode_of(head).is_some_and(|opcode| opcode != Opcode::IConst) =>
1755 {
1756 (0..*arity).try_fold(rest, |rest, _| shape(rest))
1757 }
1758 _ => None,
1759 }
1760 }
1761
1762 #[test]
1766 fn each_table_holds_every_rule_its_file_writes() {
1767 let tier_one = include_str!("../rules/simplify.rules");
1768 let tier_two = include_str!("../rules/strength.rules");
1769 let tier_three = include_str!("../rules/canonical.rules");
1770 let tier_four = include_str!("../rules/width.rules");
1771 let tier_five = include_str!("../rules/compare.rules");
1772 let tier_six = include_str!("../rules/select.rules");
1773 let count = |text: &str| text.matches("(rule (simplify ").count();
1774 assert_eq!(identities::TABLE.rules.len(), count(tier_one));
1775 assert_eq!(strength::TABLE.rules.len(), count(tier_two));
1776 assert_eq!(canonical::TABLE.rules.len(), count(tier_three));
1777 assert_eq!(width::TABLE.rules.len(), count(tier_four));
1778 assert_eq!(compare::TABLE.rules.len(), count(tier_five));
1779 assert_eq!(select::TABLE.rules.len(), count(tier_six));
1780 assert!(
1781 identities::TABLE.rules.len() > 100,
1782 "tier one is about a hundred rules and there are fewer"
1783 );
1784 assert!(
1785 strength::TABLE.rules.len() > 20,
1786 "tier two is the multiplications and the divisions and there are fewer"
1787 );
1788 assert_eq!(
1789 canonical::TABLE.rules.len(),
1790 20,
1791 "tier three is five commutative operators at four widths"
1792 );
1793 assert_eq!(
1794 width::TABLE.rules.len(),
1795 66,
1796 "tier four is the truncation and extension algebra over four widths, and the three \
1797 shapes of it that exist over the one bit a comparison answers in"
1798 );
1799 assert_eq!(
1800 compare::TABLE.rules.len(),
1801 72,
1802 "tier five is four predicates against each of four constants at four widths, and a \
1803 widened boolean against zero under two predicates at the same four"
1804 );
1805 assert_eq!(
1806 select::TABLE.rules.len(),
1807 32,
1808 "tier six is eight shapes of select at the four widths a select comes in"
1809 );
1810 }
1811
1812 #[test]
1815 fn a_pattern_is_reached_by_one_of_the_plans() {
1816 assert_eq!(PLANS.len(), 3);
1817 }
1818
1819 #[test]
1829 fn a_width_rule_is_only_matched_with_its_operand_expanded() {
1830 let (_, plans) = TABLES[2];
1831 assert_eq!(plans.len(), 1);
1832 assert_eq!(plans[0], EXPAND[0]);
1833 assert_eq!(plans[0][0], Shown::Expand);
1834 for plan in PLANS {
1835 assert_ne!(plan, plans[0], "no shared plan expands an operand");
1836 }
1837 assert_ne!(CANONICAL[0], plans[0]);
1838 assert_eq!(COMPARE[1][0], Shown::Expand);
1839 assert_eq!(COMPARE[1][1], Shown::Const);
1840 }
1841
1842 #[test]
1848 fn a_canonicalisation_is_only_matched_with_the_right_operand_refused() {
1849 let (_, plans) = TABLES[5];
1850 assert_eq!(plans.len(), 1);
1851 assert_eq!(plans[0], CANONICAL[0]);
1852 assert_eq!(plans[0][1], Shown::Var);
1853 for plan in PLANS {
1854 assert_ne!(plan, plans[0], "a shared plan would let a canonicalisation cycle");
1855 }
1856 }
1857
1858 #[test]
1865 fn a_comparison_rule_is_only_matched_with_the_constant_on_the_right() {
1866 let (_, plans) = TABLES[3];
1867 assert_eq!(plans.len(), 2);
1868 assert_eq!(plans, COMPARE);
1869 for plan in plans {
1870 assert_eq!(plan[1], Shown::Const);
1871 }
1872 assert_eq!(plans[0][0], Shown::Reg);
1873 assert_eq!(plans[1][0], Shown::Expand);
1874 }
1875
1876 #[test]
1883 fn a_select_rule_is_matched_with_one_arm_expanded_at_a_time() {
1884 let (_, plans) = TABLES[4];
1885 assert_eq!(plans, SELECT);
1886 for plan in plans {
1887 assert_eq!(plan[0], Shown::Reg);
1888 assert!(plan[1] != Shown::Expand || plan[2] != Shown::Expand);
1889 }
1890 }
1891
1892 fn selecting(
1895 width: u32,
1896 arms: impl FnOnce(&mut Builder<'_>, Value, Value) -> Value,
1897 ) -> (Func, Block, Value, Value) {
1898 let ty = Type::int(width);
1899 let (_, mut func, block) = blank();
1900 let x = func.append_param(block, ty);
1901 let y = func.append_param(block, ty);
1902 let mut build = Builder::new(&mut func, block);
1903 let cmp = build.icmp(IntPred::Slt, x, y);
1904 let out = arms(&mut build, cmp, x);
1905 build.ret(&[out]);
1906 (func, block, x, y)
1907 }
1908
1909 fn widened(func: &Func, value: Value) -> (IntPred, Vec<Value>) {
1911 assert_eq!(came_from(func, value).0, Opcode::ZExt);
1912 let bit = operands(func, value)[0];
1913 let (opcode, extra) = came_from(func, bit);
1914 assert_eq!(opcode, Opcode::ICmp);
1915 let Extra::IntPred(pred) = extra else { panic!("a comparison with no predicate") };
1916 (pred, operands(func, bit))
1917 }
1918
1919 #[test]
1922 fn a_select_between_one_and_zero_is_the_comparison_widened() {
1923 for width in [8u32, 16, 32, 64] {
1924 let ty = Type::int(width);
1925 for (then, other, pred) in [(1, 0, IntPred::Slt), (0, 1, IntPred::Sge)] {
1926 let (mut func, block, x, y) = selecting(width, |build, cmp, _| {
1927 let then = build.iconst(ty, then);
1928 let other = build.iconst(ty, other);
1929 build.select(cmp, then, other)
1930 });
1931 assert!(simplify(&mut func), "i{width} {then} {other} was left alone");
1932 let got = returned(&func, block);
1933 assert_eq!(func[got].ty, ty);
1934 assert_eq!(widened(&func, got), (pred, vec![x, y]), "i{width} {then} {other}");
1935 }
1936 }
1937 }
1938
1939 #[test]
1941 fn a_select_between_zero_and_one_on_a_bit_is_the_bit_negated_and_widened() {
1942 let (_, mut func, block) = blank();
1943 let bit = func.append_param(block, Type::int(1));
1944 let mut build = Builder::new(&mut func, block);
1945 let zero = build.iconst(Type::int(32), 0);
1946 let one = build.iconst(Type::int(32), 1);
1947 let out = build.select(bit, zero, one);
1948 build.ret(&[out]);
1949 assert!(simplify(&mut func));
1950 let got = returned(&func, block);
1951 assert_eq!(came_from(&func, got).0, Opcode::ZExt);
1952 let negated = operands(&func, got)[0];
1953 assert_eq!(came_from(&func, negated).0, Opcode::Xor);
1954 let args = operands(&func, negated);
1955 assert_eq!(args[0], bit);
1956 assert_eq!(func[args[1]].ty, Type::int(1));
1957 }
1958
1959 #[test]
1962 fn a_select_between_minus_one_and_zero_is_the_comparison_widened_and_moved() {
1963 for width in [8u32, 16, 32, 64] {
1964 let ty = Type::int(width);
1965 for (then, other, opcode) in [(-1, 0, Opcode::Sub), (0, -1, Opcode::Add)] {
1966 let (mut func, block, x, y) = selecting(width, |build, cmp, _| {
1967 let then = build.iconst(ty, then);
1968 let other = build.iconst(ty, other);
1969 build.select(cmp, then, other)
1970 });
1971 assert!(simplify(&mut func), "i{width} {then} {other} was left alone");
1972 let got = returned(&func, block);
1973 assert_eq!(came_from(&func, got).0, opcode, "i{width} {then} {other}");
1974 let args = operands(&func, got);
1975 let (number_at, widened_at) = if opcode == Opcode::Sub { (0, 1) } else { (1, 0) };
1976 assert_eq!(
1977 number(&func, args[number_at]),
1978 if opcode == Opcode::Sub { 0 } else { -1 }
1979 );
1980 assert_eq!(func[args[number_at]].ty, ty);
1981 assert_eq!(widened(&func, args[widened_at]), (IntPred::Slt, vec![x, y]));
1982 }
1983 }
1984 }
1985
1986 #[test]
1989 fn a_select_between_a_value_and_one_step_from_it_is_the_value_moved_by_the_comparison() {
1990 for width in [8u32, 16, 32, 64] {
1991 let ty = Type::int(width);
1992 for step in [Opcode::Add, Opcode::Sub] {
1993 for stepped_first in [true, false] {
1994 let (mut func, block, x, y) = selecting(width, |build, cmp, x| {
1995 let one = build.iconst(ty, 1);
1996 let stepped = build.binary(step, x, one, Flags::NSW);
1997 if stepped_first {
1998 build.select(cmp, stepped, x)
1999 } else {
2000 build.select(cmp, x, stepped)
2001 }
2002 });
2003 let case = format!("i{width} {step:?} first {stepped_first}");
2004 assert!(simplify(&mut func), "{case} was left alone");
2005 let got = returned(&func, block);
2006 assert_eq!(came_from(&func, got).0, step, "{case}");
2007 let rucc_ir::Def::Result { inst, .. } = func[got].def else { panic!() };
2009 assert_eq!(func[inst].flags, Flags::NONE, "{case}");
2010 let args = operands(&func, got);
2011 assert_eq!(args[0], x, "{case}");
2012 let pred = if stepped_first { IntPred::Slt } else { IntPred::Sge };
2013 assert_eq!(widened(&func, args[1]), (pred, vec![x, y]), "{case}");
2014 }
2015 }
2016 }
2017 }
2018
2019 #[test]
2021 fn a_select_between_a_value_and_two_more_is_left_alone() {
2022 let (mut func, block, _, _) = selecting(32, |build, cmp, x| {
2023 let two = build.iconst(Type::int(32), 2);
2024 let stepped = build.binary(Opcode::Add, x, two, Flags::NONE);
2025 build.select(cmp, stepped, x)
2026 });
2027 simplify(&mut func);
2028 let got = returned(&func, block);
2029 assert_eq!(came_from(&func, got).0, Opcode::Select);
2030 }
2031
2032 fn edges(width: u32) -> [(i128, bool); 4] {
2037 let signed = 1i128 << (width - 1);
2038 [(0, false), (-1, false), (-signed, true), (signed - 1, true)]
2039 }
2040
2041 #[test]
2047 fn a_comparison_against_the_edge_of_its_type_folds_to_a_bit() {
2048 for width in [8u32, 16, 32, 64] {
2049 let ty = Type::int(width);
2050 for (edge, signed) in edges(width) {
2051 let below = edge == 0 || edge == -(1i128 << (width - 1));
2054 let (false_pred, true_pred) = match (signed, below) {
2055 (false, true) => (IntPred::Ult, IntPred::Uge),
2056 (false, false) => (IntPred::Ugt, IntPred::Ule),
2057 (true, true) => (IntPred::Slt, IntPred::Sge),
2058 (true, false) => (IntPred::Sgt, IntPred::Sle),
2059 };
2060 for (pred, answer) in [(false_pred, 0), (true_pred, -1)] {
2064 let (_, mut func, block) = blank();
2065 let x = func.append_param(block, ty);
2066 let mut build = Builder::new(&mut func, block);
2067 let bound = build.iconst(ty, edge);
2068 let cmp = build.icmp(pred, x, bound);
2069 build.ret(&[cmp]);
2070 assert!(simplify(&mut func), "i{width} {pred:?} {edge} was left alone");
2071 let got = returned(&func, block);
2072 assert_eq!(
2073 came_from(&func, got).0,
2074 Opcode::IConst,
2075 "i{width} {pred:?} {edge} did not fold"
2076 );
2077 assert_eq!(number(&func, got), answer, "i{width} {pred:?} {edge}");
2078 assert_eq!(func[got].ty, Type::int(1), "i{width} {pred:?} {edge} is a bit");
2079 }
2080 }
2081 }
2082 }
2083
2084 #[test]
2090 fn a_comparison_true_for_one_value_becomes_a_test_for_that_value() {
2091 for width in [8u32, 16, 32, 64] {
2092 let ty = Type::int(width);
2093 for (edge, signed) in edges(width) {
2094 let below = edge == 0 || edge == -(1i128 << (width - 1));
2095 let (eq_pred, ne_pred) = match (signed, below) {
2098 (false, true) => (IntPred::Ule, IntPred::Ugt),
2099 (false, false) => (IntPred::Uge, IntPred::Ult),
2100 (true, true) => (IntPred::Sle, IntPred::Sgt),
2101 (true, false) => (IntPred::Sge, IntPred::Slt),
2102 };
2103 for (pred, left) in [(eq_pred, IntPred::Eq), (ne_pred, IntPred::Ne)] {
2104 let (_, mut func, block) = blank();
2105 let x = func.append_param(block, ty);
2106 let mut build = Builder::new(&mut func, block);
2107 let bound = build.iconst(ty, edge);
2108 let cmp = build.icmp(pred, x, bound);
2109 build.ret(&[cmp]);
2110 assert!(simplify(&mut func), "i{width} {pred:?} {edge} was left alone");
2111 let got = returned(&func, block);
2112 assert_eq!(
2113 came_from(&func, got),
2114 (Opcode::ICmp, Extra::IntPred(left)),
2115 "i{width} {pred:?} {edge} kept the predicate it matched"
2116 );
2117 let args = operands(&func, got);
2118 assert_eq!(args[0], x, "i{width} {pred:?} {edge} lost its value");
2119 assert_eq!(number(&func, args[1]), edge, "i{width} {pred:?} {edge}");
2120 assert_eq!(func[args[1]].ty, ty, "i{width} {pred:?} {edge} narrowed its bound");
2124 }
2125 }
2126 }
2127 }
2128
2129 #[test]
2136 fn a_widened_boolean_compared_against_zero_is_the_boolean() {
2137 for width in [8u32, 16, 32, 64] {
2138 let ty = Type::int(width);
2139 let (_, mut func, block) = blank();
2140 let x = func.append_param(block, Type::int(32));
2141 let mut build = Builder::new(&mut func, block);
2142 let seven = build.iconst(Type::int(32), 7);
2143 let flag = build.icmp(IntPred::Eq, x, seven);
2144 let wide = build.unary(Opcode::ZExt, flag, ty);
2145 let zero = build.iconst(ty, 0);
2146 let test = build.icmp(IntPred::Ne, wide, zero);
2147 build.ret(&[test]);
2148 assert!(simplify(&mut func), "i{width} was left alone");
2149 let got = returned(&func, block);
2150 assert_eq!(got, flag, "i{width} did not end up on the comparison");
2151 assert_eq!(func[got].ty, Type::int(1), "i{width} is a bit");
2152 }
2153 }
2154
2155 #[test]
2167 fn a_widened_boolean_that_is_zero_is_the_boolean_negated() {
2168 for width in [8u32, 16, 32, 64] {
2169 let ty = Type::int(width);
2170 let (_, mut func, block) = blank();
2171 let x = func.append_param(block, Type::int(32));
2172 let mut build = Builder::new(&mut func, block);
2173 let seven = build.iconst(Type::int(32), 7);
2174 let flag = build.icmp(IntPred::Eq, x, seven);
2175 let wide = build.unary(Opcode::ZExt, flag, ty);
2176 let zero = build.iconst(ty, 0);
2177 let test = build.icmp(IntPred::Eq, wide, zero);
2178 build.ret(&[test]);
2179 assert!(simplify(&mut func), "i{width} was left alone");
2180 let got = returned(&func, block);
2181 assert_eq!(came_from(&func, got).0, Opcode::Xor, "i{width} is not a negation");
2182 assert!(simplify(&mut func), "i{width} kept the exclusive or");
2183 assert_eq!(
2184 came_from(&func, got),
2185 (Opcode::ICmp, Extra::IntPred(IntPred::Ne)),
2186 "i{width} did not come out as the opposite comparison"
2187 );
2188 let args = operands(&func, got);
2189 assert_eq!(args[0], x, "i{width} lost its value");
2190 assert_eq!(number(&func, args[1]), 7, "i{width} lost its bound");
2191 }
2192 }
2193
2194 #[test]
2199 fn a_constant_on_the_left_of_a_commutative_operation_moves_to_the_right() {
2200 for opcode in [Opcode::Add, Opcode::Mul, Opcode::And, Opcode::Or, Opcode::Xor] {
2201 for width in [8, 16, 32, 64] {
2202 let ty = Type::int(width);
2203 let (_, mut func, block) = one_block(ty);
2204 let x = func.append_param(block, ty);
2205 let mut build = Builder::new(&mut func, block);
2206 let three = build.iconst(ty, 3);
2210 let value = build.binary(opcode, three, x, Flags::NONE);
2211 build.ret(&[value]);
2212 assert!(simplify(&mut func), "{opcode:?} at i{width} was left alone");
2213 let args = operands(&func, returned(&func, block));
2214 assert_eq!(came_from(&func, returned(&func, block)).0, opcode);
2215 assert_eq!(args[0], x, "{opcode:?} at i{width} kept the value on the right");
2216 assert_eq!(number(&func, args[1]), 3, "{opcode:?} at i{width} lost its constant");
2217 }
2218 }
2219 }
2220
2221 #[test]
2228 fn an_operation_on_two_constants_is_not_swapped_back_and_forth() {
2229 let i32 = Type::int(32);
2230 let (_, mut func, block) = one_block(i32);
2231 let mut build = Builder::new(&mut func, block);
2232 let three = build.iconst(i32, 3);
2233 let five = build.iconst(i32, 5);
2234 let sum = build.binary(Opcode::Add, three, five, Flags::NONE);
2235 build.ret(&[sum]);
2236 assert!(!simplify(&mut func), "the constants were rearranged rather than left to folding");
2237 let args = operands(&func, returned(&func, block));
2238 assert_eq!(number(&func, args[0]), 3);
2239 assert_eq!(number(&func, args[1]), 5);
2240 }
2241
2242 #[test]
2247 fn a_constant_already_on_the_right_is_left_alone() {
2248 let i32 = Type::int(32);
2249 let (_, mut func, block) = one_block(i32);
2250 let x = func.append_param(block, i32);
2251 let mut build = Builder::new(&mut func, block);
2252 let three = build.iconst(i32, 3);
2253 let sum = build.binary(Opcode::Add, x, three, Flags::NONE);
2254 build.ret(&[sum]);
2255 assert!(!simplify(&mut func));
2256 let args = operands(&func, returned(&func, block));
2257 assert_eq!(args[0], x);
2258 assert_eq!(number(&func, args[1]), 3);
2259 }
2260
2261 #[test]
2267 fn a_subtraction_keeps_its_operands_where_they_are() {
2268 let i32 = Type::int(32);
2269 let (_, mut func, block) = one_block(i32);
2270 let x = func.append_param(block, i32);
2271 let mut build = Builder::new(&mut func, block);
2272 let three = build.iconst(i32, 3);
2273 let difference = build.binary(Opcode::Sub, three, x, Flags::NONE);
2274 build.ret(&[difference]);
2275 assert!(!simplify(&mut func));
2276 let args = operands(&func, returned(&func, block));
2277 assert_eq!(number(&func, args[0]), 3);
2278 assert_eq!(args[1], x);
2279 }
2280
2281 fn narrow_to_wide(takes: Type, gives: Type) -> (Interner, Func, Block) {
2284 let mut names = Interner::new();
2285 let name = names.intern("f");
2286 let signature = Signature::new().with_params(&[takes]).with_returns(&[gives]);
2287 let mut func = Func::new(name, signature);
2288 let block = func.create_block();
2289 (names, func, block)
2290 }
2291
2292 fn chain(
2297 inner: Opcode,
2298 outer: Opcode,
2299 from: Type,
2300 through: Type,
2301 to: Type,
2302 ) -> (Func, Block, Value) {
2303 let (_, mut func, block) = narrow_to_wide(from, to);
2304 let x = func.append_param(block, from);
2305 let mut build = Builder::new(&mut func, block);
2306 let middle = build.unary(inner, x, through);
2307 let outside = build.unary(outer, middle, to);
2308 build.ret(&[outside]);
2309 (func, block, x)
2310 }
2311
2312 #[test]
2317 fn truncating_an_extension_back_to_its_own_width_gives_the_value_back() {
2318 for extend in [Opcode::SExt, Opcode::ZExt] {
2319 for (narrow, wide) in [(8, 16), (8, 32), (8, 64), (16, 32), (16, 64), (32, 64)] {
2320 let (from, through) = (Type::int(narrow), Type::int(wide));
2321 let (mut func, block, x) = chain(extend, Opcode::Trunc, from, through, from);
2322 assert!(simplify(&mut func), "{extend:?} i{narrow} to i{wide} was left alone");
2323 assert_eq!(
2324 returned(&func, block),
2325 x,
2326 "{extend:?} i{narrow} to i{wide} and back did not give the value back"
2327 );
2328 }
2329 }
2330 }
2331
2332 #[test]
2335 fn truncating_an_extension_above_its_source_is_a_shorter_extension() {
2336 let (mut func, block, x) =
2337 chain(Opcode::SExt, Opcode::Trunc, Type::int(8), Type::int(64), Type::int(16));
2338 assert!(simplify(&mut func));
2339 let result = returned(&func, block);
2340 assert_eq!(came_from(&func, result).0, Opcode::SExt);
2341 assert_eq!(operands(&func, result), vec![x]);
2342 assert_eq!(func[result].ty, Type::int(16));
2343 }
2344
2345 #[test]
2348 fn truncating_an_extension_below_its_source_is_a_truncation_of_the_source() {
2349 let (mut func, block, x) =
2350 chain(Opcode::ZExt, Opcode::Trunc, Type::int(16), Type::int(32), Type::int(8));
2351 assert!(simplify(&mut func));
2352 let result = returned(&func, block);
2353 assert_eq!(came_from(&func, result).0, Opcode::Trunc);
2354 assert_eq!(operands(&func, result), vec![x]);
2355 assert_eq!(func[result].ty, Type::int(8));
2356 }
2357
2358 #[test]
2361 fn an_extension_of_an_extension_is_one_extension() {
2362 for (inner, outer, want) in [
2363 (Opcode::ZExt, Opcode::ZExt, Opcode::ZExt),
2364 (Opcode::SExt, Opcode::SExt, Opcode::SExt),
2365 (Opcode::ZExt, Opcode::SExt, Opcode::ZExt),
2366 ] {
2367 let (mut func, block, x) =
2368 chain(inner, outer, Type::int(8), Type::int(16), Type::int(64));
2369 assert!(simplify(&mut func), "{outer:?} of {inner:?} was left alone");
2370 let result = returned(&func, block);
2371 assert_eq!(came_from(&func, result).0, want, "{outer:?} of {inner:?}");
2372 assert_eq!(operands(&func, result), vec![x]);
2373 assert_eq!(func[result].ty, Type::int(64));
2374 }
2375 }
2376
2377 #[test]
2386 fn a_truncation_of_a_truncation_is_one_truncation() {
2387 for (from, through, to) in [(64u32, 32u32, 16u32), (64, 32, 8), (64, 16, 8), (32, 16, 8)] {
2388 let (mut func, block, x) = chain(
2389 Opcode::Trunc,
2390 Opcode::Trunc,
2391 Type::int(from),
2392 Type::int(through),
2393 Type::int(to),
2394 );
2395 assert!(simplify(&mut func), "i{from} to i{through} to i{to} was left alone");
2396 let result = returned(&func, block);
2397 assert_eq!(came_from(&func, result).0, Opcode::Trunc, "i{from} to i{through} to i{to}");
2398 assert_eq!(operands(&func, result), vec![x]);
2399 assert_eq!(func[result].ty, Type::int(to));
2400 }
2401 }
2402
2403 #[test]
2406 fn zero_extending_a_sign_extension_is_left_alone() {
2407 let (mut func, _, _) =
2408 chain(Opcode::SExt, Opcode::ZExt, Type::int(8), Type::int(16), Type::int(64));
2409 assert!(!simplify(&mut func), "a zero extension of a sign extension was rewritten");
2410 }
2411
2412 #[test]
2421 fn zero_extending_a_truncation_is_left_alone() {
2422 let (mut func, _, _) =
2423 chain(Opcode::Trunc, Opcode::ZExt, Type::int(64), Type::int(32), Type::int(64));
2424 assert!(!simplify(&mut func), "a zero extension of a truncation became a mask");
2425 }
2426
2427 #[test]
2434 fn a_width_rule_needs_an_operand_an_instruction_computed() {
2435 let (_, mut func, block) = narrow_to_wide(Type::int(64), Type::int(32));
2436 let x = func.append_param(block, Type::int(64));
2437 let mut build = Builder::new(&mut func, block);
2438 let narrowed = build.unary(Opcode::Trunc, x, Type::int(32));
2439 build.ret(&[narrowed]);
2440 assert!(!simplify(&mut func), "a truncation of a parameter was rewritten");
2441 }
2442
2443 #[test]
2444 fn adding_nothing_points_every_reader_at_the_operand() {
2445 let i32 = Type::int(32);
2446 let (_, mut func, block) = one_block(i32);
2447 let x = func.append_param(block, i32);
2448 let mut build = Builder::new(&mut func, block);
2449 let zero = build.iconst(i32, 0);
2450 let sum = build.binary(Opcode::Add, x, zero, Flags::NONE);
2451 build.ret(&[sum]);
2452 assert!(simplify(&mut func));
2453 assert_eq!(returned(&func, block), x);
2455 assert_eq!(came_from(&func, sum).0, Opcode::Add);
2456 }
2457
2458 #[test]
2461 fn the_constant_is_found_on_either_side_of_an_identity() {
2462 for swapped in [false, true] {
2463 let i32 = Type::int(32);
2464 let (_, mut func, block) = one_block(i32);
2465 let x = func.append_param(block, i32);
2466 let mut build = Builder::new(&mut func, block);
2467 let zero = build.iconst(i32, 0);
2468 let (lhs, rhs) = if swapped { (zero, x) } else { (x, zero) };
2469 let sum = build.binary(Opcode::Add, lhs, rhs, Flags::NONE);
2470 build.ret(&[sum]);
2471 assert!(simplify(&mut func), "swapped {swapped}");
2472 assert_eq!(returned(&func, block), x, "swapped {swapped}");
2473 }
2474 }
2475
2476 #[test]
2477 fn multiplying_by_nothing_becomes_the_constant_where_it_stands() {
2478 let i32 = Type::int(32);
2479 let (_, mut func, block) = one_block(i32);
2480 let x = func.append_param(block, i32);
2481 let mut build = Builder::new(&mut func, block);
2482 let zero = build.iconst(i32, 0);
2483 let product = build.binary(Opcode::Mul, x, zero, Flags::NONE);
2484 build.ret(&[product]);
2485 assert!(simplify(&mut func));
2486 assert_eq!(returned(&func, block), product);
2488 assert_eq!(came_from(&func, product).0, Opcode::IConst);
2489 assert_eq!(number(&func, product), 0);
2490 }
2491
2492 #[test]
2495 fn a_value_against_itself() {
2496 for bits in [8, 16, 32, 64] {
2497 let ty = Type::int(bits);
2498 let (_, mut func, block) = one_block(ty);
2499 let x = func.append_param(block, ty);
2500 let mut build = Builder::new(&mut func, block);
2501 let both = build.binary(Opcode::And, x, x, Flags::NONE);
2502 build.ret(&[both]);
2503 assert!(simplify(&mut func), "{bits} bits");
2504 assert_eq!(returned(&func, block), x, "{bits} bits");
2505
2506 let (_, mut func, block) = one_block(ty);
2507 let x = func.append_param(block, ty);
2508 let mut build = Builder::new(&mut func, block);
2509 let nothing = build.binary(Opcode::Sub, x, x, Flags::NONE);
2510 build.ret(&[nothing]);
2511 assert!(simplify(&mut func), "{bits} bits");
2512 assert_eq!(number(&func, nothing), 0, "{bits} bits");
2513 }
2514 }
2515
2516 #[test]
2520 fn every_comparison_of_a_value_with_itself_is_decided() {
2521 for bits in [8, 16, 32, 64] {
2522 for pred in IntPred::all() {
2523 let mut names = Interner::new();
2524 let name = names.intern("f");
2525 let int = Type::int(bits);
2526 let signature = Signature::new().with_params(&[int]).with_returns(&[Type::int(1)]);
2527 let mut func = Func::new(name, signature);
2528 let block = func.create_block();
2529 let x = func.append_param(block, int);
2530 let mut build = Builder::new(&mut func, block);
2531 let answer = build.icmp(pred, x, x);
2532 build.ret(&[answer]);
2533 assert!(simplify(&mut func), "{pred:?} at {bits} bits");
2534 let said = number(&func, answer);
2535 if matches!(
2536 pred,
2537 IntPred::Ne | IntPred::Slt | IntPred::Sgt | IntPred::Ult | IntPred::Ugt
2538 ) {
2539 assert_eq!(said, 0, "{pred:?} at {bits} bits");
2540 } else {
2541 assert_ne!(said, 0, "{pred:?} at {bits} bits");
2542 }
2543 }
2544 }
2545 }
2546
2547 #[test]
2551 fn dividing_by_one_and_the_remainder_that_goes_with_it() {
2552 let i32 = Type::int(32);
2553 let (_, mut func, block) = one_block(i32);
2554 let x = func.append_param(block, i32);
2555 let mut build = Builder::new(&mut func, block);
2556 let one = build.iconst(i32, 1);
2557 let quotient = build.binary(Opcode::SDiv, x, one, Flags::NONE);
2558 let rest = build.binary(Opcode::SRem, x, one, Flags::NONE);
2559 let sum = build.binary(Opcode::Add, quotient, rest, Flags::NONE);
2560 build.ret(&[sum]);
2561 assert!(simplify(&mut func));
2562 assert_eq!(number(&func, rest), 0);
2563 let rucc_ir::Def::Result { inst, .. } = func[sum].def else { panic!("not a result") };
2565 assert_eq!(func[func[inst].args][0], x);
2566 }
2567
2568 #[test]
2571 fn all_ones_at_one_bit_is_the_one_the_front_end_writes() {
2572 for written in [-1, 1] {
2573 let bit = Type::int(1);
2574 let (_, mut func, block) = one_block(bit);
2575 let x = func.append_param(block, bit);
2576 let mut build = Builder::new(&mut func, block);
2577 let ones = build.iconst(bit, written);
2578 let kept = build.binary(Opcode::And, x, ones, Flags::NONE);
2579 build.ret(&[kept]);
2580 assert!(simplify(&mut func), "written as {written}");
2581 assert_eq!(returned(&func, block), x, "written as {written}");
2582 }
2583 }
2584
2585 #[test]
2589 fn one_identity_feeding_another_is_followed_to_the_end() {
2590 let i32 = Type::int(32);
2591 let (_, mut func, block) = one_block(i32);
2592 let x = func.append_param(block, i32);
2593 let mut build = Builder::new(&mut func, block);
2594 let zero = build.iconst(i32, 0);
2595 let one = build.iconst(i32, 1);
2596 let sum = build.binary(Opcode::Add, x, zero, Flags::NONE);
2597 let product = build.binary(Opcode::Mul, sum, one, Flags::NONE);
2598 let shifted = build.binary(Opcode::Shl, product, zero, Flags::NONE);
2599 build.ret(&[shifted]);
2600 assert!(simplify(&mut func));
2601 assert_eq!(returned(&func, block), x);
2602 }
2603
2604 #[test]
2608 fn shifting_nothing_and_shifting_all_ones_with_the_sign() {
2609 for bits in [8, 16, 32, 64] {
2610 let ty = Type::int(bits);
2611 let cases = [
2612 (Opcode::Shl, 0_i128, 0_i128),
2613 (Opcode::LShr, 0, 0),
2614 (Opcode::AShr, 0, 0),
2615 (Opcode::AShr, -1, -1),
2616 ];
2617 for (opcode, from, expected) in cases {
2618 let (_, mut func, block) = one_block(ty);
2619 let count = func.append_param(block, ty);
2620 let mut build = Builder::new(&mut func, block);
2621 let value = build.iconst(ty, from);
2622 let shifted = build.binary(opcode, value, count, Flags::NONE);
2623 build.ret(&[shifted]);
2624 assert!(simplify(&mut func), "{opcode:?} of {from} at {bits} bits");
2625 let said = number(&func, shifted);
2626 assert_eq!(said, expected, "{opcode:?} of {from} at {bits} bits");
2627 }
2628 }
2629 }
2630
2631 #[test]
2634 fn all_ones_shifted_right_with_zeroes_coming_in_is_left_alone() {
2635 let i32 = Type::int(32);
2636 let (_, mut func, block) = one_block(i32);
2637 let count = func.append_param(block, i32);
2638 let mut build = Builder::new(&mut func, block);
2639 let ones = build.iconst(i32, -1);
2640 let shifted = build.binary(Opcode::LShr, ones, count, Flags::NONE);
2641 build.ret(&[shifted]);
2642 assert!(!simplify(&mut func));
2643 assert_eq!(came_from(&func, shifted).0, Opcode::LShr);
2644 }
2645
2646 #[test]
2647 fn an_instruction_no_rule_is_about_is_left_alone() {
2648 let i32 = Type::int(32);
2652 let (_, mut func, block) = one_block(i32);
2653 let x = func.append_param(block, i32);
2654 let mut build = Builder::new(&mut func, block);
2655 let three = build.iconst(i32, 3);
2656 let tripled = build.binary(Opcode::Mul, x, three, Flags::NONE);
2657 build.ret(&[tripled]);
2658 assert!(!simplify(&mut func), "no rule is about multiplying by three");
2659 assert_eq!(returned(&func, block), tripled);
2660 assert_eq!(came_from(&func, tripled).0, Opcode::Mul);
2661 }
2662
2663 #[test]
2664 fn multiplying_by_two_becomes_an_addition_of_the_value_with_itself() {
2665 let i32 = Type::int(32);
2666 let (_, mut func, block) = one_block(i32);
2667 let x = func.append_param(block, i32);
2668 let mut build = Builder::new(&mut func, block);
2669 let two = build.iconst(i32, 2);
2670 let doubled = build.binary(Opcode::Mul, x, two, Flags::NONE);
2671 build.ret(&[doubled]);
2672 assert!(simplify(&mut func));
2673 assert_eq!(returned(&func, block), doubled);
2675 assert_eq!(came_from(&func, doubled).0, Opcode::Add);
2676 assert_eq!(operands(&func, doubled), [x, x]);
2677 }
2681
2682 #[test]
2683 fn multiplying_by_a_power_of_two_becomes_a_shift_by_the_count_of_its_zeros() {
2684 let i32 = Type::int(32);
2685 let (_, mut func, block) = one_block(i32);
2686 let x = func.append_param(block, i32);
2687 let mut build = Builder::new(&mut func, block);
2688 let eight = build.iconst(i32, 8);
2689 let scaled = build.binary(Opcode::Mul, x, eight, Flags::NONE);
2690 build.ret(&[scaled]);
2691 assert!(simplify(&mut func));
2692 assert_eq!(returned(&func, block), scaled);
2693 assert_eq!(came_from(&func, scaled).0, Opcode::Shl);
2694 let args = operands(&func, scaled);
2695 assert_eq!(args[0], x);
2696 assert_eq!(number(&func, args[1]), 3);
2697 }
2698
2699 #[test]
2700 fn the_power_of_two_with_the_sign_bit_set_is_one_of_them() {
2701 let i32 = Type::int(32);
2706 let (_, mut func, block) = one_block(i32);
2707 let x = func.append_param(block, i32);
2708 let mut build = Builder::new(&mut func, block);
2709 let top = build.iconst(i32, 0x8000_0000);
2710 let scaled = build.binary(Opcode::Mul, x, top, Flags::NONE);
2711 build.ret(&[scaled]);
2712 assert!(simplify(&mut func));
2713 assert_eq!(came_from(&func, scaled).0, Opcode::Shl);
2714 assert_eq!(number(&func, operands(&func, scaled)[1]), 31);
2715 }
2716
2717 #[test]
2718 fn dividing_an_unsigned_value_by_a_power_of_two_becomes_a_shift() {
2719 let i32 = Type::int(32);
2720 let (_, mut func, block) = one_block(i32);
2721 let x = func.append_param(block, i32);
2722 let mut build = Builder::new(&mut func, block);
2723 let sixteen = build.iconst(i32, 16);
2724 let quotient = build.binary(Opcode::UDiv, x, sixteen, Flags::NONE);
2725 build.ret(&[quotient]);
2726 assert!(simplify(&mut func));
2727 assert_eq!(came_from(&func, quotient).0, Opcode::LShr);
2728 let args = operands(&func, quotient);
2729 assert_eq!(args[0], x);
2730 assert_eq!(number(&func, args[1]), 4);
2731 }
2732
2733 #[test]
2734 fn dividing_a_signed_value_by_a_power_of_two_is_left_alone() {
2735 let i32 = Type::int(32);
2740 let (_, mut func, block) = one_block(i32);
2741 let x = func.append_param(block, i32);
2742 let mut build = Builder::new(&mut func, block);
2743 let sixteen = build.iconst(i32, 16);
2744 let quotient = build.binary(Opcode::SDiv, x, sixteen, Flags::NONE);
2745 build.ret(&[quotient]);
2746 assert!(!simplify(&mut func), "no rule turns a signed division into a shift");
2747 assert_eq!(came_from(&func, quotient).0, Opcode::SDiv);
2748 }
2749
2750 #[test]
2751 fn the_unsigned_remainder_of_a_power_of_two_becomes_a_mask() {
2752 let i32 = Type::int(32);
2753 let (_, mut func, block) = one_block(i32);
2754 let x = func.append_param(block, i32);
2755 let mut build = Builder::new(&mut func, block);
2756 let thirty_two = build.iconst(i32, 32);
2757 let rest = build.binary(Opcode::URem, x, thirty_two, Flags::NONE);
2758 build.ret(&[rest]);
2759 assert!(simplify(&mut func));
2760 assert_eq!(came_from(&func, rest).0, Opcode::And);
2761 let args = operands(&func, rest);
2762 assert_eq!(args[0], x);
2763 assert_eq!(number(&func, args[1]), 31);
2764 }
2765
2766 #[test]
2767 fn a_division_by_a_constant_that_is_not_a_power_of_two_is_left_alone() {
2768 let i32 = Type::int(32);
2769 let (_, mut func, block) = one_block(i32);
2770 let x = func.append_param(block, i32);
2771 let mut build = Builder::new(&mut func, block);
2772 let ten = build.iconst(i32, 10);
2773 let quotient = build.binary(Opcode::UDiv, x, ten, Flags::NONE);
2774 build.ret(&[quotient]);
2775 assert!(!simplify(&mut func), "ten is no power of two");
2776 assert_eq!(came_from(&func, quotient).0, Opcode::UDiv);
2777 }
2778
2779 #[test]
2780 fn multiplying_by_minus_one_becomes_a_subtraction_from_a_zero_the_rewrite_defines() {
2781 let i32 = Type::int(32);
2784 let (_, mut func, block) = one_block(i32);
2785 let x = func.append_param(block, i32);
2786 let mut build = Builder::new(&mut func, block);
2787 let minus = build.iconst(i32, -1);
2788 let negated = build.binary(Opcode::Mul, x, minus, Flags::NONE);
2789 build.ret(&[negated]);
2790 assert!(simplify(&mut func));
2791 assert_eq!(returned(&func, block), negated);
2792 assert_eq!(came_from(&func, negated).0, Opcode::Sub);
2793 let args = operands(&func, negated);
2794 assert_eq!(number(&func, args[0]), 0);
2795 assert_eq!(args[1], x);
2796 }
2797
2798 #[test]
2799 fn a_strength_reduction_keeps_a_promise_only_where_it_is_the_same_promise() {
2800 let i32 = Type::int(32);
2806 let both = Flags::NSW.union(Flags::NUW);
2807 for (by, left, flags, opcode, kept) in [
2808 (2, false, both, Opcode::Add, both),
2809 (-1, false, both, Opcode::Sub, Flags::NSW),
2810 (128, false, Flags::NSW, Opcode::Shl, Flags::NSW),
2811 (128, false, both, Opcode::Shl, both),
2812 (128, true, Flags::NSW, Opcode::Shl, Flags::NSW),
2813 (128, false, Flags::NONE, Opcode::Shl, Flags::NONE),
2814 (i128::from(i32::MIN), false, Flags::NSW, Opcode::Shl, Flags::NONE),
2815 ] {
2816 let (_, mut func, block) = one_block(i32);
2817 let x = func.append_param(block, i32);
2818 let mut build = Builder::new(&mut func, block);
2819 let k = build.iconst(i32, by);
2820 let (lhs, rhs) = if left { (k, x) } else { (x, k) };
2821 let product = build.binary(Opcode::Mul, lhs, rhs, flags);
2822 build.ret(&[product]);
2823 assert!(simplify(&mut func));
2824 let rucc_ir::Def::Result { inst, .. } = func[product].def else {
2825 panic!("not a result")
2826 };
2827 assert_eq!(func[inst].opcode, opcode, "{by}");
2828 assert_eq!(func[inst].flags, kept, "{by}, {flags:?}, constant on the left {left}");
2829 }
2830 }
2831
2832 #[test]
2833 fn a_strength_reduction_leaves_the_verifier_nothing_to_complain_about() {
2834 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2838 let i32 = Type::int(32);
2839 let (mut names, mut func, block) = one_block(i32);
2840 let mut module = Module::new(names.intern("test.c"), &target);
2841 let x = func.append_param(block, i32);
2842 let mut build = Builder::new(&mut func, block);
2843 let minus = build.iconst(i32, -1);
2844 let negated = build.binary(Opcode::Mul, x, minus, Flags::NONE);
2845 let two = build.iconst(i32, 2);
2846 let doubled = build.binary(Opcode::Mul, negated, two, Flags::NONE);
2847 build.ret(&[doubled]);
2848 assert!(simplify(&mut func));
2849 module.add_func(func);
2850 rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2851 }
2852
2853 #[test]
2858 fn the_pass_leaves_the_verifier_nothing_to_complain_about() {
2859 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2860 let i32 = Type::int(32);
2861 let (mut names, mut func, block) = one_block(i32);
2862 let mut module = Module::new(names.intern("test.c"), &target);
2863 let x = func.append_param(block, i32);
2864 let mut build = Builder::new(&mut func, block);
2865 let zero = build.iconst(i32, 0);
2866 let one = build.iconst(i32, 1);
2867 let sum = build.binary(Opcode::Add, x, zero, Flags::NONE);
2868 let product = build.binary(Opcode::Mul, sum, one, Flags::NONE);
2869 let gone = build.binary(Opcode::Sub, product, product, Flags::NONE);
2870 let total = build.binary(Opcode::Add, product, gone, Flags::NONE);
2871 build.ret(&[total]);
2872 assert!(simplify(&mut func));
2873 module.add_func(func);
2874 rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2875 }
2876
2877 #[test]
2878 fn fuel_stops_an_identity_and_not_the_walk() {
2879 let i32 = Type::int(32);
2880 let (_, mut func, block) = one_block(i32);
2881 let x = func.append_param(block, i32);
2882 let mut build = Builder::new(&mut func, block);
2883 let zero = build.iconst(i32, 0);
2884 let first = build.binary(Opcode::Add, x, zero, Flags::NONE);
2885 let second = build.binary(Opcode::Sub, x, zero, Flags::NONE);
2886 let sum = build.binary(Opcode::Add, first, second, Flags::NONE);
2887 build.ret(&[sum]);
2888 let stats =
2889 Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2890 assert!(stats.changed());
2891 assert_eq!(stats.total(Kind::Optimized), 1);
2892 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_RULE), 1);
2893 let rucc_ir::Def::Result { inst, .. } = func[sum].def else { panic!("not a result") };
2895 assert_eq!(func[func[inst].args], [x, second]);
2896 }
2897
2898 #[test]
2899 fn a_negated_float_comparison_becomes_the_opposite_predicate() {
2900 for pred in FloatPred::all() {
2903 let (_, mut func, block) = blank();
2904 let mut build = Builder::new(&mut func, block);
2905 let x = build.iconst(Type::int(64), 0);
2906 let x = build.unary(Opcode::Bitcast, x, Type::float(Float::F64));
2907 let y = build.iconst(Type::int(64), 1);
2909 let y = build.unary(Opcode::Bitcast, y, Type::float(Float::F64));
2910 let cmp = build.fcmp(pred, x, y, Flags::NONE);
2911 let ones = build.iconst(Type::int(1), -1);
2912 let not = build.binary(Opcode::Xor, cmp, ones, Flags::NONE);
2913 build.ret(&[not]);
2914 assert!(simplify(&mut func), "{pred:?}");
2915 assert_eq!(
2916 came_from(&func, not),
2917 (Opcode::FCmp, Extra::FloatPred(pred.inverse())),
2918 "{pred:?}"
2919 );
2920 }
2921 }
2922
2923 #[test]
2924 fn a_negated_integer_comparison_becomes_the_opposite_predicate() {
2925 for pred in IntPred::all() {
2926 let (_, mut func, block) = blank();
2927 let mut build = Builder::new(&mut func, block);
2928 let x = build.iconst(Type::int(32), 3);
2929 let y = build.iconst(Type::int(32), 4);
2930 let cmp = build.icmp(pred, x, y);
2931 let ones = build.iconst(Type::int(1), -1);
2932 let not = build.binary(Opcode::Xor, cmp, ones, Flags::NONE);
2933 build.ret(&[not]);
2934 assert!(simplify(&mut func), "{pred:?}");
2935 assert_eq!(
2936 came_from(&func, not),
2937 (Opcode::ICmp, Extra::IntPred(pred.inverse())),
2938 "{pred:?}"
2939 );
2940 }
2941 }
2942
2943 #[test]
2944 fn the_constant_is_found_on_either_side() {
2945 for swapped in [false, true] {
2946 let (_, mut func, block) = blank();
2947 let mut build = Builder::new(&mut func, block);
2948 let x = build.iconst(Type::int(32), 3);
2949 let y = build.iconst(Type::int(32), 4);
2950 let cmp = build.icmp(IntPred::Slt, x, y);
2951 let ones = build.iconst(Type::int(1), -1);
2952 let (lhs, rhs) = if swapped { (ones, cmp) } else { (cmp, ones) };
2953 let not = build.binary(Opcode::Xor, lhs, rhs, Flags::NONE);
2954 build.ret(&[not]);
2955 assert!(simplify(&mut func), "swapped {swapped}");
2956 assert_eq!(came_from(&func, not).1, Extra::IntPred(IntPred::Sge));
2957 }
2958 }
2959
2960 #[test]
2961 fn an_exclusive_or_of_two_comparisons_is_left_alone() {
2962 let (_, mut func, block) = blank();
2963 let mut build = Builder::new(&mut func, block);
2964 let x = build.iconst(Type::int(32), 3);
2965 let y = build.iconst(Type::int(32), 4);
2966 let a = build.icmp(IntPred::Slt, x, y);
2967 let b = build.icmp(IntPred::Sgt, x, y);
2968 let differ = build.binary(Opcode::Xor, a, b, Flags::NONE);
2969 build.ret(&[differ]);
2970 assert!(!simplify(&mut func));
2971 assert_eq!(came_from(&func, differ).0, Opcode::Xor);
2972 }
2973
2974 #[test]
2975 fn an_exclusive_or_of_something_that_is_not_a_comparison_is_left_alone() {
2976 let (_, mut func, block) = blank();
2977 let mut build = Builder::new(&mut func, block);
2978 let x = build.iconst(Type::int(32), 3);
2979 let narrow = build.unary(Opcode::Trunc, x, Type::int(1));
2980 let ones = build.iconst(Type::int(1), -1);
2981 let not = build.binary(Opcode::Xor, narrow, ones, Flags::NONE);
2982 build.ret(&[not]);
2983 assert!(!simplify(&mut func));
2984 assert_eq!(came_from(&func, not).0, Opcode::Xor);
2985 }
2986
2987 #[test]
2988 fn a_wider_exclusive_or_with_one_is_not_a_negation_and_is_left_alone() {
2989 let (_, mut func, block) = blank();
2990 let mut build = Builder::new(&mut func, block);
2991 let x = build.iconst(Type::int(32), 3);
2992 let y = build.iconst(Type::int(32), 4);
2993 let cmp = build.icmp(IntPred::Slt, x, y);
2994 let wide = build.unary(Opcode::ZExt, cmp, Type::int(32));
2995 let one = build.iconst(Type::int(32), 1);
2996 let flipped = build.binary(Opcode::Xor, wide, one, Flags::NONE);
2997 let narrow = build.unary(Opcode::Trunc, flipped, Type::int(1));
2998 build.ret(&[narrow]);
2999 assert!(!simplify(&mut func), "an i32 xor 1 flips one bit of thirty two");
3000 assert_eq!(came_from(&func, flipped).0, Opcode::Xor);
3001 }
3002
3003 #[test]
3004 fn the_comparisons_flags_travel_with_the_predicate() {
3005 let (_, mut func, block) = blank();
3006 let mut build = Builder::new(&mut func, block);
3007 let x = build.iconst(Type::int(64), 0);
3008 let x = build.unary(Opcode::Bitcast, x, Type::float(Float::F64));
3009 let y = build.iconst(Type::int(64), 1);
3011 let y = build.unary(Opcode::Bitcast, y, Type::float(Float::F64));
3012 let cmp = build.fcmp(FloatPred::Olt, x, y, Flags::FAST);
3013 let ones = build.iconst(Type::int(1), -1);
3014 let not = build.binary(Opcode::Xor, cmp, ones, Flags::NONE);
3015 build.ret(&[not]);
3016 assert!(simplify(&mut func));
3017 let rucc_ir::Def::Result { inst, .. } = func[not].def else { panic!("not a result") };
3018 assert_eq!(func[inst].flags, Flags::FAST);
3021 }
3022
3023 #[test]
3024 fn fuel_stops_the_transformation_and_not_the_walk() {
3025 let (_, mut func, block) = blank();
3026 let mut build = Builder::new(&mut func, block);
3027 let x = build.iconst(Type::int(32), 3);
3028 let y = build.iconst(Type::int(32), 4);
3029 let a = build.icmp(IntPred::Slt, x, y);
3030 let b = build.icmp(IntPred::Sgt, x, y);
3031 let ones = build.iconst(Type::int(1), -1);
3032 let first = build.binary(Opcode::Xor, a, ones, Flags::NONE);
3033 let second = build.binary(Opcode::Xor, b, ones, Flags::NONE);
3034 let both = build.binary(Opcode::And, first, second, Flags::NONE);
3035 build.ret(&[both]);
3036 let stats =
3037 Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
3038 assert!(stats.changed());
3039 assert_eq!(stats.count(Kind::Optimized, super::FLIPPED), 1);
3040 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
3041 assert_eq!(came_from(&func, first).0, Opcode::ICmp);
3042 assert_eq!(came_from(&func, second).0, Opcode::Xor);
3043 }
3044
3045 fn a_pair() -> (Func, Block, Value, Value) {
3047 let mut names = Interner::new();
3048 let name = names.intern("f");
3049 let int = Type::int(32);
3050 let signature = Signature::new().with_params(&[int, int]).with_returns(&[Type::int(1)]);
3051 let mut func = Func::new(name, signature);
3052 let block = func.create_block();
3053 let x = func.append_param(block, int);
3054 let y = func.append_param(block, int);
3055 (func, block, x, y)
3056 }
3057
3058 fn a_float_pair() -> (Func, Block, Value, Value) {
3060 let mut names = Interner::new();
3061 let name = names.intern("f");
3062 let float = Type::float(Float::F64);
3063 let signature = Signature::new().with_params(&[float, float]).with_returns(&[Type::int(1)]);
3064 let mut func = Func::new(name, signature);
3065 let block = func.create_block();
3066 let x = func.append_param(block, float);
3067 let y = func.append_param(block, float);
3068 (func, block, x, y)
3069 }
3070
3071 #[test]
3079 fn the_opposite_of_a_float_predicate_is_the_buckets_it_leaves_out() {
3080 for pred in FloatPred::all() {
3081 assert_eq!(
3082 super::float_buckets(pred.inverse()),
3083 super::bucket::ALL_FLOAT ^ super::float_buckets(pred),
3084 "{pred:?}"
3085 );
3086 }
3087 }
3088
3089 #[test]
3094 fn swapping_a_float_predicates_operands_exchanges_below_and_above() {
3095 for pred in FloatPred::all() {
3096 let want = super::turned(super::float_buckets(pred));
3097 assert_eq!(super::float_buckets(pred.swapped()), want, "{pred:?}");
3098 }
3099 }
3100
3101 #[test]
3104 fn every_set_of_float_buckets_is_a_predicate() {
3105 for pred in FloatPred::all() {
3106 assert_eq!(super::float_pred(super::float_buckets(pred)), Some(pred), "{pred:?}");
3107 }
3108 for buckets in 0..=super::bucket::ALL_FLOAT {
3109 assert!(super::float_pred(buckets).is_some(), "{buckets} spells nothing");
3110 }
3111 }
3112
3113 #[test]
3115 fn an_integer_predicate_agrees_with_its_own_opposite_and_its_own_swap() {
3116 use super::bucket::ALL_INT;
3117 for pred in IntPred::all() {
3118 let (before, reading) = super::int_buckets(pred);
3119 let (opposite, other) = super::int_buckets(pred.inverse());
3120 assert_eq!(opposite, ALL_INT ^ before, "the opposite of {pred:?}");
3121 assert_eq!(other, reading, "the opposite of {pred:?} reads the operands differently");
3122 let (swapped, other) = super::int_buckets(pred.swapped());
3123 assert_eq!(swapped, super::turned(before), "the swap of {pred:?}");
3124 assert_eq!(other, reading, "the swap of {pred:?} reads the operands differently");
3125 }
3126 }
3127
3128 #[test]
3131 fn every_integer_predicate_is_read_back_as_itself() {
3132 for pred in IntPred::all() {
3133 let (buckets, reading) = super::int_buckets(pred);
3134 assert_eq!(super::int_pred(buckets, reading), Some(pred), "{pred:?}");
3135 }
3136 }
3137
3138 #[test]
3139 fn two_integer_comparisons_that_agree_about_nothing_are_false() {
3140 let (mut func, block, x, y) = a_pair();
3141 let mut build = Builder::new(&mut func, block);
3142 let same = build.icmp(IntPred::Eq, x, y);
3143 let differ = build.icmp(IntPred::Ne, x, y);
3144 let both = build.binary(Opcode::And, same, differ, Flags::NONE);
3145 build.ret(&[both]);
3146 assert!(simplify(&mut func));
3147 assert_eq!(number(&func, both), 0);
3148 }
3149
3150 #[test]
3151 fn two_integer_comparisons_that_cover_everything_are_true() {
3152 let (mut func, block, x, y) = a_pair();
3153 let mut build = Builder::new(&mut func, block);
3154 let above = build.icmp(IntPred::Sge, x, y);
3155 let below = build.icmp(IntPred::Slt, x, y);
3156 let either = build.binary(Opcode::Or, above, below, Flags::NONE);
3157 build.ret(&[either]);
3158 assert!(simplify(&mut func));
3159 assert_ne!(number(&func, either), 0);
3160 }
3161
3162 #[test]
3165 fn two_integer_comparisons_that_overlap_become_one() {
3166 let (mut func, block, x, y) = a_pair();
3167 let mut build = Builder::new(&mut func, block);
3168 let below = build.icmp(IntPred::Slt, x, y);
3169 let same = build.icmp(IntPred::Eq, x, y);
3170 let either = build.binary(Opcode::Or, below, same, Flags::NONE);
3171 build.ret(&[either]);
3172 assert!(simplify(&mut func));
3173 assert_eq!(came_from(&func, either), (Opcode::ICmp, Extra::IntPred(IntPred::Sle)));
3174 assert_eq!(operands(&func, either), [x, y]);
3175 }
3176
3177 #[test]
3180 fn the_second_comparison_is_read_in_the_first_ones_operand_order() {
3181 let (mut func, block, x, y) = a_pair();
3182 let mut build = Builder::new(&mut func, block);
3183 let below = build.icmp(IntPred::Slt, x, y);
3184 let above = build.icmp(IntPred::Slt, y, x);
3185 let both = build.binary(Opcode::And, below, above, Flags::NONE);
3186 build.ret(&[both]);
3187 assert!(simplify(&mut func));
3188 assert_eq!(number(&func, both), 0);
3189 }
3190
3191 #[test]
3194 fn an_equality_takes_the_ordering_of_the_comparison_beside_it() {
3195 for (ordered, want) in [(IntPred::Ult, IntPred::Ule), (IntPred::Slt, IntPred::Sle)] {
3196 let (mut func, block, x, y) = a_pair();
3197 let mut build = Builder::new(&mut func, block);
3198 let below = build.icmp(ordered, x, y);
3199 let same = build.icmp(IntPred::Eq, x, y);
3200 let either = build.binary(Opcode::Or, below, same, Flags::NONE);
3201 build.ret(&[either]);
3202 assert!(simplify(&mut func), "{ordered:?}");
3203 assert_eq!(came_from(&func, either).1, Extra::IntPred(want), "{ordered:?}");
3204 }
3205 }
3206
3207 #[test]
3210 fn a_signed_comparison_and_an_unsigned_one_are_left_alone() {
3211 let (mut func, block, x, y) = a_pair();
3212 let mut build = Builder::new(&mut func, block);
3213 let signed = build.icmp(IntPred::Slt, x, y);
3214 let unsigned = build.icmp(IntPred::Ugt, x, y);
3215 let both = build.binary(Opcode::And, signed, unsigned, Flags::NONE);
3216 build.ret(&[both]);
3217 assert!(!simplify(&mut func));
3218 assert_eq!(came_from(&func, both).0, Opcode::And);
3219 }
3220
3221 #[test]
3222 fn two_comparisons_about_different_operands_are_left_alone() {
3223 let (mut func, block, x, y) = a_pair();
3224 let mut build = Builder::new(&mut func, block);
3225 let other = build.iconst(Type::int(32), 7);
3226 let first = build.icmp(IntPred::Slt, x, y);
3227 let second = build.icmp(IntPred::Sgt, x, other);
3228 let both = build.binary(Opcode::And, first, second, Flags::NONE);
3229 build.ret(&[both]);
3230 assert!(!simplify(&mut func));
3231 assert_eq!(came_from(&func, both).0, Opcode::And);
3232 }
3233
3234 #[test]
3238 fn two_float_comparisons_that_agree_about_nothing_are_false() {
3239 let (mut func, block, x, y) = a_float_pair();
3240 let mut build = Builder::new(&mut func, block);
3241 let same = build.fcmp(FloatPred::Oeq, x, y, Flags::NONE);
3242 let differ = build.fcmp(FloatPred::Une, x, y, Flags::NONE);
3243 let both = build.binary(Opcode::And, same, differ, Flags::NONE);
3244 build.ret(&[both]);
3245 assert!(simplify(&mut func));
3246 assert_eq!(number(&func, both), 0);
3247 }
3248
3249 #[test]
3254 fn a_three_way_float_condition_folds_one_pair_at_a_time() {
3255 let (mut func, block, x, y) = a_float_pair();
3256 let mut build = Builder::new(&mut func, block);
3257 let neither = build.fcmp(FloatPred::Uno, x, y, Flags::NONE);
3258 let above = build.fcmp(FloatPred::Oge, x, y, Flags::NONE);
3259 let below = build.fcmp(FloatPred::Olt, x, y, Flags::NONE);
3260 let first = build.binary(Opcode::Or, neither, above, Flags::NONE);
3261 let whole = build.binary(Opcode::Or, first, below, Flags::NONE);
3262 build.ret(&[whole]);
3263 assert!(simplify(&mut func));
3264 assert_eq!(came_from(&func, first).1, Extra::FloatPred(FloatPred::Uge));
3265 assert_ne!(number(&func, whole), 0);
3266 }
3267
3268 fn a_float() -> (Func, Block, Value) {
3270 let mut names = Interner::new();
3271 let name = names.intern("f");
3272 let float = Type::float(Float::F64);
3273 let signature = Signature::new().with_params(&[float]).with_returns(&[Type::int(1)]);
3274 let mut func = Func::new(name, signature);
3275 let block = func.create_block();
3276 let x = func.append_param(block, float);
3277 (func, block, x)
3278 }
3279
3280 fn magnitude_of(build: &mut Builder<'_>, x: Value) -> Value {
3282 let bits = Type::int(64);
3283 let number = build.unary(Opcode::Bitcast, x, bits);
3284 let mask = build.iconst(bits, i128::from(i64::MAX));
3285 let cleared = build.binary(Opcode::And, number, mask, Flags::NONE);
3286 build.unary(Opcode::Bitcast, cleared, Type::float(Float::F64))
3287 }
3288
3289 #[test]
3292 fn a_magnitude_is_never_below_zero() {
3293 let (mut func, block, x) = a_float();
3294 let mut build = Builder::new(&mut func, block);
3295 let p = magnitude_of(&mut build, x);
3296 let zero = build.fconst(Type::float(Float::F64), 0);
3297 let below = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
3298 build.ret(&[below]);
3299 assert!(simplify(&mut func));
3300 assert_eq!(number(&func, below), 0);
3301 }
3302
3303 #[test]
3306 fn zero_is_never_above_a_magnitude() {
3307 let (mut func, block, x) = a_float();
3308 let mut build = Builder::new(&mut func, block);
3309 let p = magnitude_of(&mut build, x);
3310 let zero = build.fconst(Type::float(Float::F64), 0);
3311 let above = build.fcmp(FloatPred::Ogt, zero, p, Flags::NONE);
3312 build.ret(&[above]);
3313 assert!(simplify(&mut func));
3314 assert_eq!(number(&func, above), 0);
3315 }
3316
3317 #[test]
3320 fn a_magnitude_at_or_below_zero_is_a_magnitude_equal_to_it() {
3321 let (mut func, block, x) = a_float();
3322 let mut build = Builder::new(&mut func, block);
3323 let p = magnitude_of(&mut build, x);
3324 let zero = build.fconst(Type::float(Float::F64), 0);
3325 let atmost = build.fcmp(FloatPred::Ole, p, zero, Flags::NONE);
3326 build.ret(&[atmost]);
3327 assert!(simplify(&mut func));
3328 assert_eq!(came_from(&func, atmost).1, Extra::FloatPred(FloatPred::Oeq));
3329 }
3330
3331 #[test]
3334 fn a_magnitude_is_never_at_or_below_a_negative_number() {
3335 let (mut func, block, x) = a_float();
3336 let mut build = Builder::new(&mut func, block);
3337 let p = magnitude_of(&mut build, x);
3338 let minus_one = build.fconst(Type::float(Float::F64), 0xbff0_0000_0000_0000);
3339 let atmost = build.fcmp(FloatPred::Ole, p, minus_one, Flags::NONE);
3340 build.ret(&[atmost]);
3341 assert!(simplify(&mut func));
3342 assert_eq!(number(&func, atmost), 0);
3343 }
3344
3345 #[test]
3349 fn a_magnitude_at_or_above_zero_is_still_a_question_about_a_nan() {
3350 let (mut func, block, x) = a_float();
3351 let mut build = Builder::new(&mut func, block);
3352 let p = magnitude_of(&mut build, x);
3353 let zero = build.fconst(Type::float(Float::F64), 0);
3354 let atleast = build.fcmp(FloatPred::Oge, p, zero, Flags::NONE);
3355 build.ret(&[atleast]);
3356 assert!(!simplify(&mut func));
3357 assert_eq!(came_from(&func, atleast).1, Extra::FloatPred(FloatPred::Oge));
3358 }
3359
3360 #[test]
3362 fn a_magnitude_against_a_positive_number_is_left_alone() {
3363 let (mut func, block, x) = a_float();
3364 let mut build = Builder::new(&mut func, block);
3365 let p = magnitude_of(&mut build, x);
3366 let one = build.fconst(Type::float(Float::F64), 0x3ff0_0000_0000_0000);
3367 let below = build.fcmp(FloatPred::Olt, p, one, Flags::NONE);
3368 build.ret(&[below]);
3369 assert!(!simplify(&mut func));
3370 assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
3371 }
3372
3373 #[test]
3376 fn a_mask_that_keeps_the_sign_bit_is_not_a_magnitude() {
3377 let (mut func, block, x) = a_float();
3378 let mut build = Builder::new(&mut func, block);
3379 let bits = Type::int(64);
3380 let number = build.unary(Opcode::Bitcast, x, bits);
3381 let mask = build.iconst(bits, -2);
3382 let cleared = build.binary(Opcode::And, number, mask, Flags::NONE);
3383 let p = build.unary(Opcode::Bitcast, cleared, Type::float(Float::F64));
3384 let zero = build.fconst(Type::float(Float::F64), 0);
3385 let below = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
3386 build.ret(&[below]);
3387 assert!(!simplify(&mut func));
3388 assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
3389 }
3390
3391 #[test]
3394 fn a_magnitude_against_a_nan_is_settled_by_the_nan() {
3395 let (mut func, block, x) = a_float();
3396 let mut build = Builder::new(&mut func, block);
3397 let p = magnitude_of(&mut build, x);
3398 let nan = build.fconst(Type::float(Float::F64), NAN);
3399 let below = build.fcmp(FloatPred::Olt, p, nan, Flags::NONE);
3400 build.ret(&[below]);
3401 let stats = Simplify.run(
3402 &mut func,
3403 &mut crate::machine::fixtures::analyses(),
3404 &mut Fuel::unlimited(),
3405 );
3406 assert_eq!(stats.count(Kind::Optimized, super::MAGNITUDE), 0);
3407 assert_eq!(stats.count(Kind::Optimized, super::BOUNDED), 1);
3408 assert_eq!(number(&func, below), 0);
3409 }
3410
3411 const NAN: u128 = 0x7ff8_0000_0000_0000;
3413
3414 const INFINITY: u128 = 0x7ff0_0000_0000_0000;
3416
3417 #[test]
3421 fn a_nan_is_unordered_against_anything() {
3422 for (pred, answer) in [
3423 (FloatPred::Oeq, false),
3424 (FloatPred::Olt, false),
3425 (FloatPred::Ogt, false),
3426 (FloatPred::Ole, false),
3427 (FloatPred::Oge, false),
3428 (FloatPred::One, false),
3429 (FloatPred::Une, true),
3430 (FloatPred::Ult, true),
3431 (FloatPred::Uno, true),
3432 ] {
3433 let (mut func, block, x) = a_float();
3434 let mut build = Builder::new(&mut func, block);
3435 let nan = build.fconst(Type::float(Float::F64), NAN);
3436 let asked = build.fcmp(pred, nan, x, Flags::NONE);
3437 build.ret(&[asked]);
3438 assert!(simplify(&mut func), "{pred:?}");
3439 assert_eq!(number(&func, asked) != 0, answer, "{pred:?}");
3440 }
3441 }
3442
3443 #[test]
3447 fn nothing_is_above_a_positive_infinity() {
3448 let (mut func, block, x) = a_float();
3449 let mut build = Builder::new(&mut func, block);
3450 let infinity = build.fconst(Type::float(Float::F64), INFINITY);
3451 let above = build.fcmp(FloatPred::Ogt, x, infinity, Flags::NONE);
3452 let atmost = build.fcmp(FloatPred::Ole, x, infinity, Flags::NONE);
3453 let below = build.fcmp(FloatPred::Olt, x, infinity, Flags::NONE);
3454 build.ret(&[above, atmost, below]);
3455 assert!(simplify(&mut func));
3456 assert_eq!(number(&func, above), 0);
3457 assert_eq!(came_from(&func, atmost).1, Extra::FloatPred(FloatPred::Ole));
3458 assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
3459 }
3460
3461 #[test]
3463 fn a_negative_infinity_is_above_nothing() {
3464 let (mut func, block, x) = a_float();
3465 let mut build = Builder::new(&mut func, block);
3466 let infinity = build.fconst(Type::float(Float::F64), INFINITY | 1 << 63);
3467 let above = build.fcmp(FloatPred::Ogt, infinity, x, Flags::NONE);
3468 build.ret(&[above]);
3469 assert!(simplify(&mut func));
3470 assert_eq!(number(&func, above), 0);
3471 }
3472
3473 #[test]
3475 fn two_float_constants_are_an_answer() {
3476 let (mut func, block, _) = a_float();
3477 let mut build = Builder::new(&mut func, block);
3478 let one = build.fconst(Type::float(Float::F64), 0x3ff0_0000_0000_0000);
3479 let two = build.fconst(Type::float(Float::F64), 0x4000_0000_0000_0000);
3480 let below = build.fcmp(FloatPred::Olt, one, two, Flags::NONE);
3481 let equal = build.fcmp(FloatPred::Ueq, one, two, Flags::NONE);
3482 build.ret(&[below, equal]);
3483 assert!(simplify(&mut func));
3484 assert_ne!(number(&func, below), 0);
3485 assert_eq!(number(&func, equal), 0);
3486 }
3487
3488 #[test]
3491 fn a_value_against_itself_is_equal_or_a_nan() {
3492 let (mut func, block, x) = a_float();
3493 let mut build = Builder::new(&mut func, block);
3494 let below = build.fcmp(FloatPred::Olt, x, x, Flags::NONE);
3495 let differs = build.fcmp(FloatPred::Une, x, x, Flags::NONE);
3496 let same = build.fcmp(FloatPred::Oeq, x, x, Flags::NONE);
3497 build.ret(&[below, differs, same]);
3498 assert!(simplify(&mut func));
3499 assert_eq!(number(&func, below), 0);
3500 assert_eq!(came_from(&func, differs).1, Extra::FloatPred(FloatPred::Uno));
3501 assert_eq!(came_from(&func, same).1, Extra::FloatPred(FloatPred::Oeq));
3502 }
3503
3504 #[test]
3510 fn a_branch_in_front_settles_the_same_pair() {
3511 let (mut func, entry, x, y) = a_float_pair();
3512 let [then, other, join] = [(); 3].map(|()| func.create_block());
3513 let mut build = Builder::new(&mut func, entry);
3514 let neither = build.fcmp(FloatPred::Uno, x, y, Flags::NONE);
3515 build.br_if(neither, then, &[], other, &[]);
3516 let mut build = Builder::new(&mut func, other);
3517 let ordered = build.fcmp(FloatPred::Ord, x, y, Flags::NONE);
3518 let turned = build.fcmp(FloatPred::Ord, y, x, Flags::NONE);
3519 let above = build.fcmp(FloatPred::Ogt, x, y, Flags::NONE);
3520 build.jump(join, &[]);
3521 let mut build = Builder::new(&mut func, then);
3522 let there = build.fcmp(FloatPred::Ord, x, y, Flags::NONE);
3523 build.jump(join, &[]);
3524 let mut build = Builder::new(&mut func, join);
3525 let both = build.binary(Opcode::And, ordered, turned, Flags::NONE);
3526 let all = build.binary(Opcode::And, both, above, Flags::NONE);
3527 let all = build.binary(Opcode::And, all, there, Flags::NONE);
3528 build.ret(&[all]);
3529 assert!(simplify(&mut func));
3530 assert_ne!(number(&func, ordered), 0);
3531 assert_ne!(number(&func, turned), 0);
3532 assert_eq!(came_from(&func, above).1, Extra::FloatPred(FloatPred::Ogt));
3533 assert_eq!(number(&func, there), 0);
3534 }
3535
3536 #[test]
3539 fn a_join_settles_nothing() {
3540 let (mut func, entry, x, y) = a_float_pair();
3541 let [then, other, join] = [(); 3].map(|()| func.create_block());
3542 let mut build = Builder::new(&mut func, entry);
3543 let neither = build.fcmp(FloatPred::Uno, x, y, Flags::NONE);
3544 build.br_if(neither, then, &[], other, &[]);
3545 Builder::new(&mut func, then).jump(join, &[]);
3546 Builder::new(&mut func, other).jump(join, &[]);
3547 let mut build = Builder::new(&mut func, join);
3548 let ordered = build.fcmp(FloatPred::Ord, x, y, Flags::NONE);
3549 build.ret(&[ordered]);
3550 assert!(!simplify(&mut func));
3551 assert_eq!(came_from(&func, ordered).1, Extra::FloatPred(FloatPred::Ord));
3552 }
3553
3554 #[test]
3556 fn a_finite_bound_is_left_alone() {
3557 let (mut func, block, x) = a_float();
3558 let mut build = Builder::new(&mut func, block);
3559 let one = build.fconst(Type::float(Float::F64), 0x3ff0_0000_0000_0000);
3560 let below = build.fcmp(FloatPred::Olt, x, one, Flags::NONE);
3561 build.ret(&[below]);
3562 assert!(!simplify(&mut func));
3563 assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
3564 }
3565
3566 #[test]
3569 fn fuel_stops_the_magnitude_fold_and_not_the_walk() {
3570 let (mut func, block, x) = a_float();
3571 let mut build = Builder::new(&mut func, block);
3572 let p = magnitude_of(&mut build, x);
3573 let zero = build.fconst(Type::float(Float::F64), 0);
3574 let below = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
3575 let also = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
3576 let both = build.binary(Opcode::Or, below, also, Flags::NONE);
3577 build.ret(&[both]);
3578 let stats =
3579 Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
3580 assert_eq!(stats.count(Kind::Optimized, super::MAGNITUDE), 1);
3581 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_MAGNITUDE), 1);
3582 assert_eq!(number(&func, below), 0);
3583 assert_eq!(came_from(&func, also).0, Opcode::FCmp);
3584 }
3585
3586 #[test]
3589 fn two_comparisons_promised_different_things_are_left_alone() {
3590 let (mut func, block, x, y) = a_float_pair();
3591 let mut build = Builder::new(&mut func, block);
3592 let below = build.fcmp(FloatPred::Olt, x, y, Flags::FAST);
3593 let same = build.fcmp(FloatPred::Oeq, x, y, Flags::NONE);
3594 let either = build.binary(Opcode::Or, below, same, Flags::NONE);
3595 build.ret(&[either]);
3596 assert!(!simplify(&mut func));
3597 assert_eq!(came_from(&func, either).0, Opcode::Or);
3598 }
3599
3600 #[test]
3601 fn the_promise_both_comparisons_were_made_under_travels_to_the_one_that_replaces_them() {
3602 let (mut func, block, x, y) = a_float_pair();
3603 let mut build = Builder::new(&mut func, block);
3604 let below = build.fcmp(FloatPred::Olt, x, y, Flags::FAST);
3605 let same = build.fcmp(FloatPred::Oeq, x, y, Flags::FAST);
3606 let either = build.binary(Opcode::Or, below, same, Flags::NONE);
3607 build.ret(&[either]);
3608 assert!(simplify(&mut func));
3609 assert_eq!(came_from(&func, either).1, Extra::FloatPred(FloatPred::Ole));
3610 let rucc_ir::Def::Result { inst, .. } = func[either].def else { panic!("not a result") };
3611 assert_eq!(func[inst].flags, Flags::FAST);
3612 }
3613
3614 #[test]
3617 fn a_wider_and_of_two_comparisons_is_left_alone() {
3618 let (mut func, block, x, y) = a_pair();
3619 let mut build = Builder::new(&mut func, block);
3620 let same = build.icmp(IntPred::Eq, x, y);
3621 let differ = build.icmp(IntPred::Ne, x, y);
3622 let first = build.unary(Opcode::ZExt, same, Type::int(32));
3623 let second = build.unary(Opcode::ZExt, differ, Type::int(32));
3624 let both = build.binary(Opcode::And, first, second, Flags::NONE);
3625 let narrow = build.unary(Opcode::Trunc, both, Type::int(1));
3626 build.ret(&[narrow]);
3627 assert!(!simplify(&mut func));
3628 assert_eq!(came_from(&func, both).0, Opcode::And);
3629 }
3630
3631 #[test]
3632 fn fuel_stops_the_composite_fold_and_not_the_walk() {
3633 let (mut func, block, x, y) = a_pair();
3634 let mut build = Builder::new(&mut func, block);
3635 let same = build.icmp(IntPred::Eq, x, y);
3636 let differ = build.icmp(IntPred::Ne, x, y);
3637 let below = build.icmp(IntPred::Slt, x, y);
3638 let above = build.icmp(IntPred::Sgt, x, y);
3639 let first = build.binary(Opcode::And, same, differ, Flags::NONE);
3640 let second = build.binary(Opcode::And, below, above, Flags::NONE);
3641 let both = build.binary(Opcode::Or, first, second, Flags::NONE);
3642 build.ret(&[both]);
3643 let stats =
3644 Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
3645 assert!(stats.changed());
3646 assert_eq!(stats.count(Kind::Optimized, super::COMPOSITE), 1);
3647 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_COMPOSITE), 1);
3648 assert_eq!(came_from(&func, first).0, Opcode::IConst);
3649 assert_eq!(came_from(&func, second).0, Opcode::And);
3650 }
3651}