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 if format.decimal().is_some() {
1558 return None;
1559 }
1560 Some(Float::from_bits(format, func[at].bits()))
1561}
1562
1563pub(crate) fn fold_composite(func: &mut Func, inst: Inst, composite: Composite) {
1568 match composite {
1569 Composite::Always(answer) => become_constant(func, inst, answer.into()),
1570 Composite::Pred(flip) => {
1571 let args = func.push_values(&[flip.lhs, flip.rhs]);
1572 let data = &mut func[inst];
1573 data.opcode = flip.opcode;
1574 data.flags = flip.flags;
1575 data.args = args;
1576 data.extra = flip.extra;
1577 }
1578 }
1579}
1580
1581fn all_ones(func: &Func, value: Value) -> bool {
1583 let ty = func[value].ty;
1584 let Def::Result { inst, .. } = func[value].def else { return false };
1585 let data = &func[inst];
1586 let Extra::Imm(at) = data.extra else { return false };
1587 if data.opcode != Opcode::IConst {
1588 return false;
1589 }
1590 func[at].signed(ty) == -1
1593}
1594
1595#[cfg(test)]
1596mod tests {
1597 use rucc_base::Interner;
1598 use rucc_ir::{
1599 Block, Builder, Extra, Flags, Float, FloatPred, Func, IntPred, Module, Opcode, Signature,
1600 Type, Value,
1601 };
1602 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1603
1604 use super::{
1605 CANONICAL, COMPARE, EXPAND, PLANS, SELECT, Shown, TABLES, canonical, compare, identities,
1606 select, strength, width,
1607 };
1608 use crate::rules::Piece;
1609 use crate::stats::Kind;
1610 use crate::{Fuel, Pass, simplify::Simplify};
1611
1612 fn blank() -> (Interner, Func, Block) {
1614 let mut names = Interner::new();
1615 let name = names.intern("f");
1616 let mut func = Func::new(name, Signature::new().with_returns(&[Type::int(1)]));
1617 let block = func.create_block();
1618 (names, func, block)
1619 }
1620
1621 fn one_block(ty: Type) -> (Interner, Func, Block) {
1624 let mut names = Interner::new();
1625 let name = names.intern("f");
1626 let signature = Signature::new().with_params(&[ty]).with_returns(&[ty]);
1627 let mut func = Func::new(name, signature);
1628 let block = func.create_block();
1629 (names, func, block)
1630 }
1631
1632 fn simplify(func: &mut Func) -> bool {
1634 Simplify
1635 .run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited())
1636 .changed()
1637 }
1638
1639 fn came_from(func: &Func, value: Value) -> (Opcode, Extra) {
1641 let rucc_ir::Def::Result { inst, .. } = func[value].def else { panic!("not a result") };
1642 (func[inst].opcode, func[inst].extra)
1643 }
1644
1645 fn returned(func: &Func, block: Block) -> Value {
1649 let inst = func.terminator(block).expect("the block has a terminator");
1650 func[func[inst].args][0]
1651 }
1652
1653 fn operands(func: &Func, value: Value) -> Vec<Value> {
1655 let rucc_ir::Def::Result { inst, .. } = func[value].def else { panic!("not a result") };
1656 func[func[inst].args].to_vec()
1657 }
1658
1659 fn number(func: &Func, value: Value) -> i128 {
1661 let rucc_ir::Def::Result { inst, .. } = func[value].def else { panic!("not a result") };
1662 let data = &func[inst];
1663 assert_eq!(data.opcode, Opcode::IConst, "not a constant");
1664 let Extra::Imm(at) = data.extra else { panic!("a constant with no number") };
1665 func[at].signed(func[value].ty)
1666 }
1667
1668 #[test]
1674 fn every_rule_leaves_a_shape_the_pass_knows_what_to_do_with() {
1675 for (table, _) in TABLES {
1676 for rule in table.rules {
1677 let known = matches!(
1678 rule.replacement,
1679 [Piece::App { head, arity: 1 }, Piece::Var { .. }]
1680 if head.starts_with("value.")
1681 ) || matches!(
1682 rule.replacement,
1683 [Piece::App { head, arity: 1 }, Piece::Int(_)]
1684 if head.starts_with("iconst.")
1685 ) || matches!(
1686 rule.replacement,
1687 [Piece::App { arity: 2, .. }, ..] if instruction(rule.replacement)
1688 ) || conversion(rule.replacement);
1689 assert!(known, "{} leaves a shape the pass would skip", rule.pattern);
1690 }
1691 }
1692 }
1693
1694 fn conversion(pieces: &'static [Piece]) -> bool {
1698 let [Piece::App { head, arity: 1 }, rest @ ..] = pieces else { return false };
1699 let converts =
1700 matches!(super::opcode_of(head), Some(Opcode::SExt | Opcode::ZExt | Opcode::Trunc));
1701 let number = matches!(rest, [Piece::App { head, .. }, ..] if head.starts_with("iconst."));
1702 converts && !number && shape(rest).is_some_and(<[Piece]>::is_empty)
1703 }
1704
1705 #[test]
1713 fn a_width_rule_writes_a_term_that_ends_where_the_one_it_matched_ended() {
1714 for rule in width::TABLE.rules {
1715 let [Piece::App { head, .. }, ..] = rule.replacement else {
1716 panic!("{} writes no head", rule.pattern)
1717 };
1718 let wrote = head.rsplit_once('.').expect("a replacement head names a width").1;
1719 let matched = rule
1720 .pattern
1721 .trim_start_matches('(')
1722 .split([' ', ')'])
1723 .next()
1724 .and_then(|head| head.rsplit_once('.'))
1725 .expect("a pattern head names a width")
1726 .1;
1727 assert_eq!(wrote, matched, "{} ends somewhere else", rule.pattern);
1728 }
1729 }
1730
1731 fn instruction(pieces: &'static [Piece]) -> bool {
1737 let [Piece::App { head, arity: 2 }, rest @ ..] = pieces else { return false };
1738 if super::opcode_of(head).is_none() {
1739 return false;
1740 }
1741 shape(rest).and_then(shape).is_some_and(<[Piece]>::is_empty)
1742 }
1743
1744 fn shape(pieces: &'static [Piece]) -> Option<&'static [Piece]> {
1748 match pieces {
1749 [Piece::App { head, arity: 1 }, Piece::Var { .. }, rest @ ..]
1750 if head.starts_with("value.") =>
1751 {
1752 Some(rest)
1753 }
1754 [
1755 Piece::App { head, arity: 1 },
1756 Piece::Int(_) | Piece::Var { .. } | Piece::Computed { .. },
1757 rest @ ..,
1758 ] if head.starts_with("iconst.") => Some(rest),
1759 [Piece::App { head, arity }, rest @ ..]
1760 if super::opcode_of(head).is_some_and(|opcode| opcode != Opcode::IConst) =>
1761 {
1762 (0..*arity).try_fold(rest, |rest, _| shape(rest))
1763 }
1764 _ => None,
1765 }
1766 }
1767
1768 #[test]
1772 fn each_table_holds_every_rule_its_file_writes() {
1773 let tier_one = include_str!("../rules/simplify.rules");
1774 let tier_two = include_str!("../rules/strength.rules");
1775 let tier_three = include_str!("../rules/canonical.rules");
1776 let tier_four = include_str!("../rules/width.rules");
1777 let tier_five = include_str!("../rules/compare.rules");
1778 let tier_six = include_str!("../rules/select.rules");
1779 let count = |text: &str| text.matches("(rule (simplify ").count();
1780 assert_eq!(identities::TABLE.rules.len(), count(tier_one));
1781 assert_eq!(strength::TABLE.rules.len(), count(tier_two));
1782 assert_eq!(canonical::TABLE.rules.len(), count(tier_three));
1783 assert_eq!(width::TABLE.rules.len(), count(tier_four));
1784 assert_eq!(compare::TABLE.rules.len(), count(tier_five));
1785 assert_eq!(select::TABLE.rules.len(), count(tier_six));
1786 assert!(
1787 identities::TABLE.rules.len() > 100,
1788 "tier one is about a hundred rules and there are fewer"
1789 );
1790 assert!(
1791 strength::TABLE.rules.len() > 20,
1792 "tier two is the multiplications and the divisions and there are fewer"
1793 );
1794 assert_eq!(
1795 canonical::TABLE.rules.len(),
1796 20,
1797 "tier three is five commutative operators at four widths"
1798 );
1799 assert_eq!(
1800 width::TABLE.rules.len(),
1801 66,
1802 "tier four is the truncation and extension algebra over four widths, and the three \
1803 shapes of it that exist over the one bit a comparison answers in"
1804 );
1805 assert_eq!(
1806 compare::TABLE.rules.len(),
1807 72,
1808 "tier five is four predicates against each of four constants at four widths, and a \
1809 widened boolean against zero under two predicates at the same four"
1810 );
1811 assert_eq!(
1812 select::TABLE.rules.len(),
1813 32,
1814 "tier six is eight shapes of select at the four widths a select comes in"
1815 );
1816 }
1817
1818 #[test]
1821 fn a_pattern_is_reached_by_one_of_the_plans() {
1822 assert_eq!(PLANS.len(), 3);
1823 }
1824
1825 #[test]
1835 fn a_width_rule_is_only_matched_with_its_operand_expanded() {
1836 let (_, plans) = TABLES[2];
1837 assert_eq!(plans.len(), 1);
1838 assert_eq!(plans[0], EXPAND[0]);
1839 assert_eq!(plans[0][0], Shown::Expand);
1840 for plan in PLANS {
1841 assert_ne!(plan, plans[0], "no shared plan expands an operand");
1842 }
1843 assert_ne!(CANONICAL[0], plans[0]);
1844 assert_eq!(COMPARE[1][0], Shown::Expand);
1845 assert_eq!(COMPARE[1][1], Shown::Const);
1846 }
1847
1848 #[test]
1854 fn a_canonicalisation_is_only_matched_with_the_right_operand_refused() {
1855 let (_, plans) = TABLES[5];
1856 assert_eq!(plans.len(), 1);
1857 assert_eq!(plans[0], CANONICAL[0]);
1858 assert_eq!(plans[0][1], Shown::Var);
1859 for plan in PLANS {
1860 assert_ne!(plan, plans[0], "a shared plan would let a canonicalisation cycle");
1861 }
1862 }
1863
1864 #[test]
1871 fn a_comparison_rule_is_only_matched_with_the_constant_on_the_right() {
1872 let (_, plans) = TABLES[3];
1873 assert_eq!(plans.len(), 2);
1874 assert_eq!(plans, COMPARE);
1875 for plan in plans {
1876 assert_eq!(plan[1], Shown::Const);
1877 }
1878 assert_eq!(plans[0][0], Shown::Reg);
1879 assert_eq!(plans[1][0], Shown::Expand);
1880 }
1881
1882 #[test]
1889 fn a_select_rule_is_matched_with_one_arm_expanded_at_a_time() {
1890 let (_, plans) = TABLES[4];
1891 assert_eq!(plans, SELECT);
1892 for plan in plans {
1893 assert_eq!(plan[0], Shown::Reg);
1894 assert!(plan[1] != Shown::Expand || plan[2] != Shown::Expand);
1895 }
1896 }
1897
1898 fn selecting(
1901 width: u32,
1902 arms: impl FnOnce(&mut Builder<'_>, Value, Value) -> Value,
1903 ) -> (Func, Block, Value, Value) {
1904 let ty = Type::int(width);
1905 let (_, mut func, block) = blank();
1906 let x = func.append_param(block, ty);
1907 let y = func.append_param(block, ty);
1908 let mut build = Builder::new(&mut func, block);
1909 let cmp = build.icmp(IntPred::Slt, x, y);
1910 let out = arms(&mut build, cmp, x);
1911 build.ret(&[out]);
1912 (func, block, x, y)
1913 }
1914
1915 fn widened(func: &Func, value: Value) -> (IntPred, Vec<Value>) {
1917 assert_eq!(came_from(func, value).0, Opcode::ZExt);
1918 let bit = operands(func, value)[0];
1919 let (opcode, extra) = came_from(func, bit);
1920 assert_eq!(opcode, Opcode::ICmp);
1921 let Extra::IntPred(pred) = extra else { panic!("a comparison with no predicate") };
1922 (pred, operands(func, bit))
1923 }
1924
1925 #[test]
1928 fn a_select_between_one_and_zero_is_the_comparison_widened() {
1929 for width in [8u32, 16, 32, 64] {
1930 let ty = Type::int(width);
1931 for (then, other, pred) in [(1, 0, IntPred::Slt), (0, 1, IntPred::Sge)] {
1932 let (mut func, block, x, y) = selecting(width, |build, cmp, _| {
1933 let then = build.iconst(ty, then);
1934 let other = build.iconst(ty, other);
1935 build.select(cmp, then, other)
1936 });
1937 assert!(simplify(&mut func), "i{width} {then} {other} was left alone");
1938 let got = returned(&func, block);
1939 assert_eq!(func[got].ty, ty);
1940 assert_eq!(widened(&func, got), (pred, vec![x, y]), "i{width} {then} {other}");
1941 }
1942 }
1943 }
1944
1945 #[test]
1947 fn a_select_between_zero_and_one_on_a_bit_is_the_bit_negated_and_widened() {
1948 let (_, mut func, block) = blank();
1949 let bit = func.append_param(block, Type::int(1));
1950 let mut build = Builder::new(&mut func, block);
1951 let zero = build.iconst(Type::int(32), 0);
1952 let one = build.iconst(Type::int(32), 1);
1953 let out = build.select(bit, zero, one);
1954 build.ret(&[out]);
1955 assert!(simplify(&mut func));
1956 let got = returned(&func, block);
1957 assert_eq!(came_from(&func, got).0, Opcode::ZExt);
1958 let negated = operands(&func, got)[0];
1959 assert_eq!(came_from(&func, negated).0, Opcode::Xor);
1960 let args = operands(&func, negated);
1961 assert_eq!(args[0], bit);
1962 assert_eq!(func[args[1]].ty, Type::int(1));
1963 }
1964
1965 #[test]
1968 fn a_select_between_minus_one_and_zero_is_the_comparison_widened_and_moved() {
1969 for width in [8u32, 16, 32, 64] {
1970 let ty = Type::int(width);
1971 for (then, other, opcode) in [(-1, 0, Opcode::Sub), (0, -1, Opcode::Add)] {
1972 let (mut func, block, x, y) = selecting(width, |build, cmp, _| {
1973 let then = build.iconst(ty, then);
1974 let other = build.iconst(ty, other);
1975 build.select(cmp, then, other)
1976 });
1977 assert!(simplify(&mut func), "i{width} {then} {other} was left alone");
1978 let got = returned(&func, block);
1979 assert_eq!(came_from(&func, got).0, opcode, "i{width} {then} {other}");
1980 let args = operands(&func, got);
1981 let (number_at, widened_at) = if opcode == Opcode::Sub { (0, 1) } else { (1, 0) };
1982 assert_eq!(
1983 number(&func, args[number_at]),
1984 if opcode == Opcode::Sub { 0 } else { -1 }
1985 );
1986 assert_eq!(func[args[number_at]].ty, ty);
1987 assert_eq!(widened(&func, args[widened_at]), (IntPred::Slt, vec![x, y]));
1988 }
1989 }
1990 }
1991
1992 #[test]
1995 fn a_select_between_a_value_and_one_step_from_it_is_the_value_moved_by_the_comparison() {
1996 for width in [8u32, 16, 32, 64] {
1997 let ty = Type::int(width);
1998 for step in [Opcode::Add, Opcode::Sub] {
1999 for stepped_first in [true, false] {
2000 let (mut func, block, x, y) = selecting(width, |build, cmp, x| {
2001 let one = build.iconst(ty, 1);
2002 let stepped = build.binary(step, x, one, Flags::NSW);
2003 if stepped_first {
2004 build.select(cmp, stepped, x)
2005 } else {
2006 build.select(cmp, x, stepped)
2007 }
2008 });
2009 let case = format!("i{width} {step:?} first {stepped_first}");
2010 assert!(simplify(&mut func), "{case} was left alone");
2011 let got = returned(&func, block);
2012 assert_eq!(came_from(&func, got).0, step, "{case}");
2013 let rucc_ir::Def::Result { inst, .. } = func[got].def else { panic!() };
2015 assert_eq!(func[inst].flags, Flags::NONE, "{case}");
2016 let args = operands(&func, got);
2017 assert_eq!(args[0], x, "{case}");
2018 let pred = if stepped_first { IntPred::Slt } else { IntPred::Sge };
2019 assert_eq!(widened(&func, args[1]), (pred, vec![x, y]), "{case}");
2020 }
2021 }
2022 }
2023 }
2024
2025 #[test]
2027 fn a_select_between_a_value_and_two_more_is_left_alone() {
2028 let (mut func, block, _, _) = selecting(32, |build, cmp, x| {
2029 let two = build.iconst(Type::int(32), 2);
2030 let stepped = build.binary(Opcode::Add, x, two, Flags::NONE);
2031 build.select(cmp, stepped, x)
2032 });
2033 simplify(&mut func);
2034 let got = returned(&func, block);
2035 assert_eq!(came_from(&func, got).0, Opcode::Select);
2036 }
2037
2038 fn edges(width: u32) -> [(i128, bool); 4] {
2043 let signed = 1i128 << (width - 1);
2044 [(0, false), (-1, false), (-signed, true), (signed - 1, true)]
2045 }
2046
2047 #[test]
2053 fn a_comparison_against_the_edge_of_its_type_folds_to_a_bit() {
2054 for width in [8u32, 16, 32, 64] {
2055 let ty = Type::int(width);
2056 for (edge, signed) in edges(width) {
2057 let below = edge == 0 || edge == -(1i128 << (width - 1));
2060 let (false_pred, true_pred) = match (signed, below) {
2061 (false, true) => (IntPred::Ult, IntPred::Uge),
2062 (false, false) => (IntPred::Ugt, IntPred::Ule),
2063 (true, true) => (IntPred::Slt, IntPred::Sge),
2064 (true, false) => (IntPred::Sgt, IntPred::Sle),
2065 };
2066 for (pred, answer) in [(false_pred, 0), (true_pred, -1)] {
2070 let (_, mut func, block) = blank();
2071 let x = func.append_param(block, ty);
2072 let mut build = Builder::new(&mut func, block);
2073 let bound = build.iconst(ty, edge);
2074 let cmp = build.icmp(pred, x, bound);
2075 build.ret(&[cmp]);
2076 assert!(simplify(&mut func), "i{width} {pred:?} {edge} was left alone");
2077 let got = returned(&func, block);
2078 assert_eq!(
2079 came_from(&func, got).0,
2080 Opcode::IConst,
2081 "i{width} {pred:?} {edge} did not fold"
2082 );
2083 assert_eq!(number(&func, got), answer, "i{width} {pred:?} {edge}");
2084 assert_eq!(func[got].ty, Type::int(1), "i{width} {pred:?} {edge} is a bit");
2085 }
2086 }
2087 }
2088 }
2089
2090 #[test]
2096 fn a_comparison_true_for_one_value_becomes_a_test_for_that_value() {
2097 for width in [8u32, 16, 32, 64] {
2098 let ty = Type::int(width);
2099 for (edge, signed) in edges(width) {
2100 let below = edge == 0 || edge == -(1i128 << (width - 1));
2101 let (eq_pred, ne_pred) = match (signed, below) {
2104 (false, true) => (IntPred::Ule, IntPred::Ugt),
2105 (false, false) => (IntPred::Uge, IntPred::Ult),
2106 (true, true) => (IntPred::Sle, IntPred::Sgt),
2107 (true, false) => (IntPred::Sge, IntPred::Slt),
2108 };
2109 for (pred, left) in [(eq_pred, IntPred::Eq), (ne_pred, IntPred::Ne)] {
2110 let (_, mut func, block) = blank();
2111 let x = func.append_param(block, ty);
2112 let mut build = Builder::new(&mut func, block);
2113 let bound = build.iconst(ty, edge);
2114 let cmp = build.icmp(pred, x, bound);
2115 build.ret(&[cmp]);
2116 assert!(simplify(&mut func), "i{width} {pred:?} {edge} was left alone");
2117 let got = returned(&func, block);
2118 assert_eq!(
2119 came_from(&func, got),
2120 (Opcode::ICmp, Extra::IntPred(left)),
2121 "i{width} {pred:?} {edge} kept the predicate it matched"
2122 );
2123 let args = operands(&func, got);
2124 assert_eq!(args[0], x, "i{width} {pred:?} {edge} lost its value");
2125 assert_eq!(number(&func, args[1]), edge, "i{width} {pred:?} {edge}");
2126 assert_eq!(func[args[1]].ty, ty, "i{width} {pred:?} {edge} narrowed its bound");
2130 }
2131 }
2132 }
2133 }
2134
2135 #[test]
2142 fn a_widened_boolean_compared_against_zero_is_the_boolean() {
2143 for width in [8u32, 16, 32, 64] {
2144 let ty = Type::int(width);
2145 let (_, mut func, block) = blank();
2146 let x = func.append_param(block, Type::int(32));
2147 let mut build = Builder::new(&mut func, block);
2148 let seven = build.iconst(Type::int(32), 7);
2149 let flag = build.icmp(IntPred::Eq, x, seven);
2150 let wide = build.unary(Opcode::ZExt, flag, ty);
2151 let zero = build.iconst(ty, 0);
2152 let test = build.icmp(IntPred::Ne, wide, zero);
2153 build.ret(&[test]);
2154 assert!(simplify(&mut func), "i{width} was left alone");
2155 let got = returned(&func, block);
2156 assert_eq!(got, flag, "i{width} did not end up on the comparison");
2157 assert_eq!(func[got].ty, Type::int(1), "i{width} is a bit");
2158 }
2159 }
2160
2161 #[test]
2173 fn a_widened_boolean_that_is_zero_is_the_boolean_negated() {
2174 for width in [8u32, 16, 32, 64] {
2175 let ty = Type::int(width);
2176 let (_, mut func, block) = blank();
2177 let x = func.append_param(block, Type::int(32));
2178 let mut build = Builder::new(&mut func, block);
2179 let seven = build.iconst(Type::int(32), 7);
2180 let flag = build.icmp(IntPred::Eq, x, seven);
2181 let wide = build.unary(Opcode::ZExt, flag, ty);
2182 let zero = build.iconst(ty, 0);
2183 let test = build.icmp(IntPred::Eq, wide, zero);
2184 build.ret(&[test]);
2185 assert!(simplify(&mut func), "i{width} was left alone");
2186 let got = returned(&func, block);
2187 assert_eq!(came_from(&func, got).0, Opcode::Xor, "i{width} is not a negation");
2188 assert!(simplify(&mut func), "i{width} kept the exclusive or");
2189 assert_eq!(
2190 came_from(&func, got),
2191 (Opcode::ICmp, Extra::IntPred(IntPred::Ne)),
2192 "i{width} did not come out as the opposite comparison"
2193 );
2194 let args = operands(&func, got);
2195 assert_eq!(args[0], x, "i{width} lost its value");
2196 assert_eq!(number(&func, args[1]), 7, "i{width} lost its bound");
2197 }
2198 }
2199
2200 #[test]
2205 fn a_constant_on_the_left_of_a_commutative_operation_moves_to_the_right() {
2206 for opcode in [Opcode::Add, Opcode::Mul, Opcode::And, Opcode::Or, Opcode::Xor] {
2207 for width in [8, 16, 32, 64] {
2208 let ty = Type::int(width);
2209 let (_, mut func, block) = one_block(ty);
2210 let x = func.append_param(block, ty);
2211 let mut build = Builder::new(&mut func, block);
2212 let three = build.iconst(ty, 3);
2216 let value = build.binary(opcode, three, x, Flags::NONE);
2217 build.ret(&[value]);
2218 assert!(simplify(&mut func), "{opcode:?} at i{width} was left alone");
2219 let args = operands(&func, returned(&func, block));
2220 assert_eq!(came_from(&func, returned(&func, block)).0, opcode);
2221 assert_eq!(args[0], x, "{opcode:?} at i{width} kept the value on the right");
2222 assert_eq!(number(&func, args[1]), 3, "{opcode:?} at i{width} lost its constant");
2223 }
2224 }
2225 }
2226
2227 #[test]
2234 fn an_operation_on_two_constants_is_not_swapped_back_and_forth() {
2235 let i32 = Type::int(32);
2236 let (_, mut func, block) = one_block(i32);
2237 let mut build = Builder::new(&mut func, block);
2238 let three = build.iconst(i32, 3);
2239 let five = build.iconst(i32, 5);
2240 let sum = build.binary(Opcode::Add, three, five, Flags::NONE);
2241 build.ret(&[sum]);
2242 assert!(!simplify(&mut func), "the constants were rearranged rather than left to folding");
2243 let args = operands(&func, returned(&func, block));
2244 assert_eq!(number(&func, args[0]), 3);
2245 assert_eq!(number(&func, args[1]), 5);
2246 }
2247
2248 #[test]
2253 fn a_constant_already_on_the_right_is_left_alone() {
2254 let i32 = Type::int(32);
2255 let (_, mut func, block) = one_block(i32);
2256 let x = func.append_param(block, i32);
2257 let mut build = Builder::new(&mut func, block);
2258 let three = build.iconst(i32, 3);
2259 let sum = build.binary(Opcode::Add, x, three, Flags::NONE);
2260 build.ret(&[sum]);
2261 assert!(!simplify(&mut func));
2262 let args = operands(&func, returned(&func, block));
2263 assert_eq!(args[0], x);
2264 assert_eq!(number(&func, args[1]), 3);
2265 }
2266
2267 #[test]
2273 fn a_subtraction_keeps_its_operands_where_they_are() {
2274 let i32 = Type::int(32);
2275 let (_, mut func, block) = one_block(i32);
2276 let x = func.append_param(block, i32);
2277 let mut build = Builder::new(&mut func, block);
2278 let three = build.iconst(i32, 3);
2279 let difference = build.binary(Opcode::Sub, three, x, Flags::NONE);
2280 build.ret(&[difference]);
2281 assert!(!simplify(&mut func));
2282 let args = operands(&func, returned(&func, block));
2283 assert_eq!(number(&func, args[0]), 3);
2284 assert_eq!(args[1], x);
2285 }
2286
2287 fn narrow_to_wide(takes: Type, gives: Type) -> (Interner, Func, Block) {
2290 let mut names = Interner::new();
2291 let name = names.intern("f");
2292 let signature = Signature::new().with_params(&[takes]).with_returns(&[gives]);
2293 let mut func = Func::new(name, signature);
2294 let block = func.create_block();
2295 (names, func, block)
2296 }
2297
2298 fn chain(
2303 inner: Opcode,
2304 outer: Opcode,
2305 from: Type,
2306 through: Type,
2307 to: Type,
2308 ) -> (Func, Block, Value) {
2309 let (_, mut func, block) = narrow_to_wide(from, to);
2310 let x = func.append_param(block, from);
2311 let mut build = Builder::new(&mut func, block);
2312 let middle = build.unary(inner, x, through);
2313 let outside = build.unary(outer, middle, to);
2314 build.ret(&[outside]);
2315 (func, block, x)
2316 }
2317
2318 #[test]
2323 fn truncating_an_extension_back_to_its_own_width_gives_the_value_back() {
2324 for extend in [Opcode::SExt, Opcode::ZExt] {
2325 for (narrow, wide) in [(8, 16), (8, 32), (8, 64), (16, 32), (16, 64), (32, 64)] {
2326 let (from, through) = (Type::int(narrow), Type::int(wide));
2327 let (mut func, block, x) = chain(extend, Opcode::Trunc, from, through, from);
2328 assert!(simplify(&mut func), "{extend:?} i{narrow} to i{wide} was left alone");
2329 assert_eq!(
2330 returned(&func, block),
2331 x,
2332 "{extend:?} i{narrow} to i{wide} and back did not give the value back"
2333 );
2334 }
2335 }
2336 }
2337
2338 #[test]
2341 fn truncating_an_extension_above_its_source_is_a_shorter_extension() {
2342 let (mut func, block, x) =
2343 chain(Opcode::SExt, Opcode::Trunc, Type::int(8), Type::int(64), Type::int(16));
2344 assert!(simplify(&mut func));
2345 let result = returned(&func, block);
2346 assert_eq!(came_from(&func, result).0, Opcode::SExt);
2347 assert_eq!(operands(&func, result), vec![x]);
2348 assert_eq!(func[result].ty, Type::int(16));
2349 }
2350
2351 #[test]
2354 fn truncating_an_extension_below_its_source_is_a_truncation_of_the_source() {
2355 let (mut func, block, x) =
2356 chain(Opcode::ZExt, Opcode::Trunc, Type::int(16), Type::int(32), Type::int(8));
2357 assert!(simplify(&mut func));
2358 let result = returned(&func, block);
2359 assert_eq!(came_from(&func, result).0, Opcode::Trunc);
2360 assert_eq!(operands(&func, result), vec![x]);
2361 assert_eq!(func[result].ty, Type::int(8));
2362 }
2363
2364 #[test]
2367 fn an_extension_of_an_extension_is_one_extension() {
2368 for (inner, outer, want) in [
2369 (Opcode::ZExt, Opcode::ZExt, Opcode::ZExt),
2370 (Opcode::SExt, Opcode::SExt, Opcode::SExt),
2371 (Opcode::ZExt, Opcode::SExt, Opcode::ZExt),
2372 ] {
2373 let (mut func, block, x) =
2374 chain(inner, outer, Type::int(8), Type::int(16), Type::int(64));
2375 assert!(simplify(&mut func), "{outer:?} of {inner:?} was left alone");
2376 let result = returned(&func, block);
2377 assert_eq!(came_from(&func, result).0, want, "{outer:?} of {inner:?}");
2378 assert_eq!(operands(&func, result), vec![x]);
2379 assert_eq!(func[result].ty, Type::int(64));
2380 }
2381 }
2382
2383 #[test]
2392 fn a_truncation_of_a_truncation_is_one_truncation() {
2393 for (from, through, to) in [(64u32, 32u32, 16u32), (64, 32, 8), (64, 16, 8), (32, 16, 8)] {
2394 let (mut func, block, x) = chain(
2395 Opcode::Trunc,
2396 Opcode::Trunc,
2397 Type::int(from),
2398 Type::int(through),
2399 Type::int(to),
2400 );
2401 assert!(simplify(&mut func), "i{from} to i{through} to i{to} was left alone");
2402 let result = returned(&func, block);
2403 assert_eq!(came_from(&func, result).0, Opcode::Trunc, "i{from} to i{through} to i{to}");
2404 assert_eq!(operands(&func, result), vec![x]);
2405 assert_eq!(func[result].ty, Type::int(to));
2406 }
2407 }
2408
2409 #[test]
2412 fn zero_extending_a_sign_extension_is_left_alone() {
2413 let (mut func, _, _) =
2414 chain(Opcode::SExt, Opcode::ZExt, Type::int(8), Type::int(16), Type::int(64));
2415 assert!(!simplify(&mut func), "a zero extension of a sign extension was rewritten");
2416 }
2417
2418 #[test]
2427 fn zero_extending_a_truncation_is_left_alone() {
2428 let (mut func, _, _) =
2429 chain(Opcode::Trunc, Opcode::ZExt, Type::int(64), Type::int(32), Type::int(64));
2430 assert!(!simplify(&mut func), "a zero extension of a truncation became a mask");
2431 }
2432
2433 #[test]
2440 fn a_width_rule_needs_an_operand_an_instruction_computed() {
2441 let (_, mut func, block) = narrow_to_wide(Type::int(64), Type::int(32));
2442 let x = func.append_param(block, Type::int(64));
2443 let mut build = Builder::new(&mut func, block);
2444 let narrowed = build.unary(Opcode::Trunc, x, Type::int(32));
2445 build.ret(&[narrowed]);
2446 assert!(!simplify(&mut func), "a truncation of a parameter was rewritten");
2447 }
2448
2449 #[test]
2450 fn adding_nothing_points_every_reader_at_the_operand() {
2451 let i32 = Type::int(32);
2452 let (_, mut func, block) = one_block(i32);
2453 let x = func.append_param(block, i32);
2454 let mut build = Builder::new(&mut func, block);
2455 let zero = build.iconst(i32, 0);
2456 let sum = build.binary(Opcode::Add, x, zero, Flags::NONE);
2457 build.ret(&[sum]);
2458 assert!(simplify(&mut func));
2459 assert_eq!(returned(&func, block), x);
2461 assert_eq!(came_from(&func, sum).0, Opcode::Add);
2462 }
2463
2464 #[test]
2467 fn the_constant_is_found_on_either_side_of_an_identity() {
2468 for swapped in [false, true] {
2469 let i32 = Type::int(32);
2470 let (_, mut func, block) = one_block(i32);
2471 let x = func.append_param(block, i32);
2472 let mut build = Builder::new(&mut func, block);
2473 let zero = build.iconst(i32, 0);
2474 let (lhs, rhs) = if swapped { (zero, x) } else { (x, zero) };
2475 let sum = build.binary(Opcode::Add, lhs, rhs, Flags::NONE);
2476 build.ret(&[sum]);
2477 assert!(simplify(&mut func), "swapped {swapped}");
2478 assert_eq!(returned(&func, block), x, "swapped {swapped}");
2479 }
2480 }
2481
2482 #[test]
2483 fn multiplying_by_nothing_becomes_the_constant_where_it_stands() {
2484 let i32 = Type::int(32);
2485 let (_, mut func, block) = one_block(i32);
2486 let x = func.append_param(block, i32);
2487 let mut build = Builder::new(&mut func, block);
2488 let zero = build.iconst(i32, 0);
2489 let product = build.binary(Opcode::Mul, x, zero, Flags::NONE);
2490 build.ret(&[product]);
2491 assert!(simplify(&mut func));
2492 assert_eq!(returned(&func, block), product);
2494 assert_eq!(came_from(&func, product).0, Opcode::IConst);
2495 assert_eq!(number(&func, product), 0);
2496 }
2497
2498 #[test]
2501 fn a_value_against_itself() {
2502 for bits in [8, 16, 32, 64] {
2503 let ty = Type::int(bits);
2504 let (_, mut func, block) = one_block(ty);
2505 let x = func.append_param(block, ty);
2506 let mut build = Builder::new(&mut func, block);
2507 let both = build.binary(Opcode::And, x, x, Flags::NONE);
2508 build.ret(&[both]);
2509 assert!(simplify(&mut func), "{bits} bits");
2510 assert_eq!(returned(&func, block), x, "{bits} bits");
2511
2512 let (_, mut func, block) = one_block(ty);
2513 let x = func.append_param(block, ty);
2514 let mut build = Builder::new(&mut func, block);
2515 let nothing = build.binary(Opcode::Sub, x, x, Flags::NONE);
2516 build.ret(&[nothing]);
2517 assert!(simplify(&mut func), "{bits} bits");
2518 assert_eq!(number(&func, nothing), 0, "{bits} bits");
2519 }
2520 }
2521
2522 #[test]
2526 fn every_comparison_of_a_value_with_itself_is_decided() {
2527 for bits in [8, 16, 32, 64] {
2528 for pred in IntPred::all() {
2529 let mut names = Interner::new();
2530 let name = names.intern("f");
2531 let int = Type::int(bits);
2532 let signature = Signature::new().with_params(&[int]).with_returns(&[Type::int(1)]);
2533 let mut func = Func::new(name, signature);
2534 let block = func.create_block();
2535 let x = func.append_param(block, int);
2536 let mut build = Builder::new(&mut func, block);
2537 let answer = build.icmp(pred, x, x);
2538 build.ret(&[answer]);
2539 assert!(simplify(&mut func), "{pred:?} at {bits} bits");
2540 let said = number(&func, answer);
2541 if matches!(
2542 pred,
2543 IntPred::Ne | IntPred::Slt | IntPred::Sgt | IntPred::Ult | IntPred::Ugt
2544 ) {
2545 assert_eq!(said, 0, "{pred:?} at {bits} bits");
2546 } else {
2547 assert_ne!(said, 0, "{pred:?} at {bits} bits");
2548 }
2549 }
2550 }
2551 }
2552
2553 #[test]
2557 fn dividing_by_one_and_the_remainder_that_goes_with_it() {
2558 let i32 = Type::int(32);
2559 let (_, mut func, block) = one_block(i32);
2560 let x = func.append_param(block, i32);
2561 let mut build = Builder::new(&mut func, block);
2562 let one = build.iconst(i32, 1);
2563 let quotient = build.binary(Opcode::SDiv, x, one, Flags::NONE);
2564 let rest = build.binary(Opcode::SRem, x, one, Flags::NONE);
2565 let sum = build.binary(Opcode::Add, quotient, rest, Flags::NONE);
2566 build.ret(&[sum]);
2567 assert!(simplify(&mut func));
2568 assert_eq!(number(&func, rest), 0);
2569 let rucc_ir::Def::Result { inst, .. } = func[sum].def else { panic!("not a result") };
2571 assert_eq!(func[func[inst].args][0], x);
2572 }
2573
2574 #[test]
2577 fn all_ones_at_one_bit_is_the_one_the_front_end_writes() {
2578 for written in [-1, 1] {
2579 let bit = Type::int(1);
2580 let (_, mut func, block) = one_block(bit);
2581 let x = func.append_param(block, bit);
2582 let mut build = Builder::new(&mut func, block);
2583 let ones = build.iconst(bit, written);
2584 let kept = build.binary(Opcode::And, x, ones, Flags::NONE);
2585 build.ret(&[kept]);
2586 assert!(simplify(&mut func), "written as {written}");
2587 assert_eq!(returned(&func, block), x, "written as {written}");
2588 }
2589 }
2590
2591 #[test]
2595 fn one_identity_feeding_another_is_followed_to_the_end() {
2596 let i32 = Type::int(32);
2597 let (_, mut func, block) = one_block(i32);
2598 let x = func.append_param(block, i32);
2599 let mut build = Builder::new(&mut func, block);
2600 let zero = build.iconst(i32, 0);
2601 let one = build.iconst(i32, 1);
2602 let sum = build.binary(Opcode::Add, x, zero, Flags::NONE);
2603 let product = build.binary(Opcode::Mul, sum, one, Flags::NONE);
2604 let shifted = build.binary(Opcode::Shl, product, zero, Flags::NONE);
2605 build.ret(&[shifted]);
2606 assert!(simplify(&mut func));
2607 assert_eq!(returned(&func, block), x);
2608 }
2609
2610 #[test]
2614 fn shifting_nothing_and_shifting_all_ones_with_the_sign() {
2615 for bits in [8, 16, 32, 64] {
2616 let ty = Type::int(bits);
2617 let cases = [
2618 (Opcode::Shl, 0_i128, 0_i128),
2619 (Opcode::LShr, 0, 0),
2620 (Opcode::AShr, 0, 0),
2621 (Opcode::AShr, -1, -1),
2622 ];
2623 for (opcode, from, expected) in cases {
2624 let (_, mut func, block) = one_block(ty);
2625 let count = func.append_param(block, ty);
2626 let mut build = Builder::new(&mut func, block);
2627 let value = build.iconst(ty, from);
2628 let shifted = build.binary(opcode, value, count, Flags::NONE);
2629 build.ret(&[shifted]);
2630 assert!(simplify(&mut func), "{opcode:?} of {from} at {bits} bits");
2631 let said = number(&func, shifted);
2632 assert_eq!(said, expected, "{opcode:?} of {from} at {bits} bits");
2633 }
2634 }
2635 }
2636
2637 #[test]
2640 fn all_ones_shifted_right_with_zeroes_coming_in_is_left_alone() {
2641 let i32 = Type::int(32);
2642 let (_, mut func, block) = one_block(i32);
2643 let count = func.append_param(block, i32);
2644 let mut build = Builder::new(&mut func, block);
2645 let ones = build.iconst(i32, -1);
2646 let shifted = build.binary(Opcode::LShr, ones, count, Flags::NONE);
2647 build.ret(&[shifted]);
2648 assert!(!simplify(&mut func));
2649 assert_eq!(came_from(&func, shifted).0, Opcode::LShr);
2650 }
2651
2652 #[test]
2653 fn an_instruction_no_rule_is_about_is_left_alone() {
2654 let i32 = Type::int(32);
2658 let (_, mut func, block) = one_block(i32);
2659 let x = func.append_param(block, i32);
2660 let mut build = Builder::new(&mut func, block);
2661 let three = build.iconst(i32, 3);
2662 let tripled = build.binary(Opcode::Mul, x, three, Flags::NONE);
2663 build.ret(&[tripled]);
2664 assert!(!simplify(&mut func), "no rule is about multiplying by three");
2665 assert_eq!(returned(&func, block), tripled);
2666 assert_eq!(came_from(&func, tripled).0, Opcode::Mul);
2667 }
2668
2669 #[test]
2670 fn multiplying_by_two_becomes_an_addition_of_the_value_with_itself() {
2671 let i32 = Type::int(32);
2672 let (_, mut func, block) = one_block(i32);
2673 let x = func.append_param(block, i32);
2674 let mut build = Builder::new(&mut func, block);
2675 let two = build.iconst(i32, 2);
2676 let doubled = build.binary(Opcode::Mul, x, two, Flags::NONE);
2677 build.ret(&[doubled]);
2678 assert!(simplify(&mut func));
2679 assert_eq!(returned(&func, block), doubled);
2681 assert_eq!(came_from(&func, doubled).0, Opcode::Add);
2682 assert_eq!(operands(&func, doubled), [x, x]);
2683 }
2687
2688 #[test]
2689 fn multiplying_by_a_power_of_two_becomes_a_shift_by_the_count_of_its_zeros() {
2690 let i32 = Type::int(32);
2691 let (_, mut func, block) = one_block(i32);
2692 let x = func.append_param(block, i32);
2693 let mut build = Builder::new(&mut func, block);
2694 let eight = build.iconst(i32, 8);
2695 let scaled = build.binary(Opcode::Mul, x, eight, Flags::NONE);
2696 build.ret(&[scaled]);
2697 assert!(simplify(&mut func));
2698 assert_eq!(returned(&func, block), scaled);
2699 assert_eq!(came_from(&func, scaled).0, Opcode::Shl);
2700 let args = operands(&func, scaled);
2701 assert_eq!(args[0], x);
2702 assert_eq!(number(&func, args[1]), 3);
2703 }
2704
2705 #[test]
2706 fn the_power_of_two_with_the_sign_bit_set_is_one_of_them() {
2707 let i32 = Type::int(32);
2712 let (_, mut func, block) = one_block(i32);
2713 let x = func.append_param(block, i32);
2714 let mut build = Builder::new(&mut func, block);
2715 let top = build.iconst(i32, 0x8000_0000);
2716 let scaled = build.binary(Opcode::Mul, x, top, Flags::NONE);
2717 build.ret(&[scaled]);
2718 assert!(simplify(&mut func));
2719 assert_eq!(came_from(&func, scaled).0, Opcode::Shl);
2720 assert_eq!(number(&func, operands(&func, scaled)[1]), 31);
2721 }
2722
2723 #[test]
2724 fn dividing_an_unsigned_value_by_a_power_of_two_becomes_a_shift() {
2725 let i32 = Type::int(32);
2726 let (_, mut func, block) = one_block(i32);
2727 let x = func.append_param(block, i32);
2728 let mut build = Builder::new(&mut func, block);
2729 let sixteen = build.iconst(i32, 16);
2730 let quotient = build.binary(Opcode::UDiv, x, sixteen, Flags::NONE);
2731 build.ret(&[quotient]);
2732 assert!(simplify(&mut func));
2733 assert_eq!(came_from(&func, quotient).0, Opcode::LShr);
2734 let args = operands(&func, quotient);
2735 assert_eq!(args[0], x);
2736 assert_eq!(number(&func, args[1]), 4);
2737 }
2738
2739 #[test]
2740 fn dividing_a_signed_value_by_a_power_of_two_is_left_alone() {
2741 let i32 = Type::int(32);
2746 let (_, mut func, block) = one_block(i32);
2747 let x = func.append_param(block, i32);
2748 let mut build = Builder::new(&mut func, block);
2749 let sixteen = build.iconst(i32, 16);
2750 let quotient = build.binary(Opcode::SDiv, x, sixteen, Flags::NONE);
2751 build.ret(&[quotient]);
2752 assert!(!simplify(&mut func), "no rule turns a signed division into a shift");
2753 assert_eq!(came_from(&func, quotient).0, Opcode::SDiv);
2754 }
2755
2756 #[test]
2757 fn the_unsigned_remainder_of_a_power_of_two_becomes_a_mask() {
2758 let i32 = Type::int(32);
2759 let (_, mut func, block) = one_block(i32);
2760 let x = func.append_param(block, i32);
2761 let mut build = Builder::new(&mut func, block);
2762 let thirty_two = build.iconst(i32, 32);
2763 let rest = build.binary(Opcode::URem, x, thirty_two, Flags::NONE);
2764 build.ret(&[rest]);
2765 assert!(simplify(&mut func));
2766 assert_eq!(came_from(&func, rest).0, Opcode::And);
2767 let args = operands(&func, rest);
2768 assert_eq!(args[0], x);
2769 assert_eq!(number(&func, args[1]), 31);
2770 }
2771
2772 #[test]
2773 fn a_division_by_a_constant_that_is_not_a_power_of_two_is_left_alone() {
2774 let i32 = Type::int(32);
2775 let (_, mut func, block) = one_block(i32);
2776 let x = func.append_param(block, i32);
2777 let mut build = Builder::new(&mut func, block);
2778 let ten = build.iconst(i32, 10);
2779 let quotient = build.binary(Opcode::UDiv, x, ten, Flags::NONE);
2780 build.ret(&[quotient]);
2781 assert!(!simplify(&mut func), "ten is no power of two");
2782 assert_eq!(came_from(&func, quotient).0, Opcode::UDiv);
2783 }
2784
2785 #[test]
2786 fn multiplying_by_minus_one_becomes_a_subtraction_from_a_zero_the_rewrite_defines() {
2787 let i32 = Type::int(32);
2790 let (_, mut func, block) = one_block(i32);
2791 let x = func.append_param(block, i32);
2792 let mut build = Builder::new(&mut func, block);
2793 let minus = build.iconst(i32, -1);
2794 let negated = build.binary(Opcode::Mul, x, minus, Flags::NONE);
2795 build.ret(&[negated]);
2796 assert!(simplify(&mut func));
2797 assert_eq!(returned(&func, block), negated);
2798 assert_eq!(came_from(&func, negated).0, Opcode::Sub);
2799 let args = operands(&func, negated);
2800 assert_eq!(number(&func, args[0]), 0);
2801 assert_eq!(args[1], x);
2802 }
2803
2804 #[test]
2805 fn a_strength_reduction_keeps_a_promise_only_where_it_is_the_same_promise() {
2806 let i32 = Type::int(32);
2812 let both = Flags::NSW.union(Flags::NUW);
2813 for (by, left, flags, opcode, kept) in [
2814 (2, false, both, Opcode::Add, both),
2815 (-1, false, both, Opcode::Sub, Flags::NSW),
2816 (128, false, Flags::NSW, Opcode::Shl, Flags::NSW),
2817 (128, false, both, Opcode::Shl, both),
2818 (128, true, Flags::NSW, Opcode::Shl, Flags::NSW),
2819 (128, false, Flags::NONE, Opcode::Shl, Flags::NONE),
2820 (i128::from(i32::MIN), false, Flags::NSW, Opcode::Shl, Flags::NONE),
2821 ] {
2822 let (_, mut func, block) = one_block(i32);
2823 let x = func.append_param(block, i32);
2824 let mut build = Builder::new(&mut func, block);
2825 let k = build.iconst(i32, by);
2826 let (lhs, rhs) = if left { (k, x) } else { (x, k) };
2827 let product = build.binary(Opcode::Mul, lhs, rhs, flags);
2828 build.ret(&[product]);
2829 assert!(simplify(&mut func));
2830 let rucc_ir::Def::Result { inst, .. } = func[product].def else {
2831 panic!("not a result")
2832 };
2833 assert_eq!(func[inst].opcode, opcode, "{by}");
2834 assert_eq!(func[inst].flags, kept, "{by}, {flags:?}, constant on the left {left}");
2835 }
2836 }
2837
2838 #[test]
2839 fn a_strength_reduction_leaves_the_verifier_nothing_to_complain_about() {
2840 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2844 let i32 = Type::int(32);
2845 let (mut names, mut func, block) = one_block(i32);
2846 let mut module = Module::new(names.intern("test.c"), &target);
2847 let x = func.append_param(block, i32);
2848 let mut build = Builder::new(&mut func, block);
2849 let minus = build.iconst(i32, -1);
2850 let negated = build.binary(Opcode::Mul, x, minus, Flags::NONE);
2851 let two = build.iconst(i32, 2);
2852 let doubled = build.binary(Opcode::Mul, negated, two, Flags::NONE);
2853 build.ret(&[doubled]);
2854 assert!(simplify(&mut func));
2855 module.add_func(func);
2856 rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2857 }
2858
2859 #[test]
2864 fn the_pass_leaves_the_verifier_nothing_to_complain_about() {
2865 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2866 let i32 = Type::int(32);
2867 let (mut names, mut func, block) = one_block(i32);
2868 let mut module = Module::new(names.intern("test.c"), &target);
2869 let x = func.append_param(block, i32);
2870 let mut build = Builder::new(&mut func, block);
2871 let zero = build.iconst(i32, 0);
2872 let one = build.iconst(i32, 1);
2873 let sum = build.binary(Opcode::Add, x, zero, Flags::NONE);
2874 let product = build.binary(Opcode::Mul, sum, one, Flags::NONE);
2875 let gone = build.binary(Opcode::Sub, product, product, Flags::NONE);
2876 let total = build.binary(Opcode::Add, product, gone, Flags::NONE);
2877 build.ret(&[total]);
2878 assert!(simplify(&mut func));
2879 module.add_func(func);
2880 rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2881 }
2882
2883 #[test]
2884 fn fuel_stops_an_identity_and_not_the_walk() {
2885 let i32 = Type::int(32);
2886 let (_, mut func, block) = one_block(i32);
2887 let x = func.append_param(block, i32);
2888 let mut build = Builder::new(&mut func, block);
2889 let zero = build.iconst(i32, 0);
2890 let first = build.binary(Opcode::Add, x, zero, Flags::NONE);
2891 let second = build.binary(Opcode::Sub, x, zero, Flags::NONE);
2892 let sum = build.binary(Opcode::Add, first, second, Flags::NONE);
2893 build.ret(&[sum]);
2894 let stats =
2895 Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2896 assert!(stats.changed());
2897 assert_eq!(stats.total(Kind::Optimized), 1);
2898 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_RULE), 1);
2899 let rucc_ir::Def::Result { inst, .. } = func[sum].def else { panic!("not a result") };
2901 assert_eq!(func[func[inst].args], [x, second]);
2902 }
2903
2904 #[test]
2905 fn a_negated_float_comparison_becomes_the_opposite_predicate() {
2906 for pred in FloatPred::all() {
2909 let (_, mut func, block) = blank();
2910 let mut build = Builder::new(&mut func, block);
2911 let x = build.iconst(Type::int(64), 0);
2912 let x = build.unary(Opcode::Bitcast, x, Type::float(Float::F64));
2913 let y = build.iconst(Type::int(64), 1);
2915 let y = build.unary(Opcode::Bitcast, y, Type::float(Float::F64));
2916 let cmp = build.fcmp(pred, x, y, Flags::NONE);
2917 let ones = build.iconst(Type::int(1), -1);
2918 let not = build.binary(Opcode::Xor, cmp, ones, Flags::NONE);
2919 build.ret(&[not]);
2920 assert!(simplify(&mut func), "{pred:?}");
2921 assert_eq!(
2922 came_from(&func, not),
2923 (Opcode::FCmp, Extra::FloatPred(pred.inverse())),
2924 "{pred:?}"
2925 );
2926 }
2927 }
2928
2929 #[test]
2930 fn a_negated_integer_comparison_becomes_the_opposite_predicate() {
2931 for pred in IntPred::all() {
2932 let (_, mut func, block) = blank();
2933 let mut build = Builder::new(&mut func, block);
2934 let x = build.iconst(Type::int(32), 3);
2935 let y = build.iconst(Type::int(32), 4);
2936 let cmp = build.icmp(pred, x, y);
2937 let ones = build.iconst(Type::int(1), -1);
2938 let not = build.binary(Opcode::Xor, cmp, ones, Flags::NONE);
2939 build.ret(&[not]);
2940 assert!(simplify(&mut func), "{pred:?}");
2941 assert_eq!(
2942 came_from(&func, not),
2943 (Opcode::ICmp, Extra::IntPred(pred.inverse())),
2944 "{pred:?}"
2945 );
2946 }
2947 }
2948
2949 #[test]
2950 fn the_constant_is_found_on_either_side() {
2951 for swapped in [false, true] {
2952 let (_, mut func, block) = blank();
2953 let mut build = Builder::new(&mut func, block);
2954 let x = build.iconst(Type::int(32), 3);
2955 let y = build.iconst(Type::int(32), 4);
2956 let cmp = build.icmp(IntPred::Slt, x, y);
2957 let ones = build.iconst(Type::int(1), -1);
2958 let (lhs, rhs) = if swapped { (ones, cmp) } else { (cmp, ones) };
2959 let not = build.binary(Opcode::Xor, lhs, rhs, Flags::NONE);
2960 build.ret(&[not]);
2961 assert!(simplify(&mut func), "swapped {swapped}");
2962 assert_eq!(came_from(&func, not).1, Extra::IntPred(IntPred::Sge));
2963 }
2964 }
2965
2966 #[test]
2967 fn an_exclusive_or_of_two_comparisons_is_left_alone() {
2968 let (_, mut func, block) = blank();
2969 let mut build = Builder::new(&mut func, block);
2970 let x = build.iconst(Type::int(32), 3);
2971 let y = build.iconst(Type::int(32), 4);
2972 let a = build.icmp(IntPred::Slt, x, y);
2973 let b = build.icmp(IntPred::Sgt, x, y);
2974 let differ = build.binary(Opcode::Xor, a, b, Flags::NONE);
2975 build.ret(&[differ]);
2976 assert!(!simplify(&mut func));
2977 assert_eq!(came_from(&func, differ).0, Opcode::Xor);
2978 }
2979
2980 #[test]
2981 fn an_exclusive_or_of_something_that_is_not_a_comparison_is_left_alone() {
2982 let (_, mut func, block) = blank();
2983 let mut build = Builder::new(&mut func, block);
2984 let x = build.iconst(Type::int(32), 3);
2985 let narrow = build.unary(Opcode::Trunc, x, Type::int(1));
2986 let ones = build.iconst(Type::int(1), -1);
2987 let not = build.binary(Opcode::Xor, narrow, ones, Flags::NONE);
2988 build.ret(&[not]);
2989 assert!(!simplify(&mut func));
2990 assert_eq!(came_from(&func, not).0, Opcode::Xor);
2991 }
2992
2993 #[test]
2994 fn a_wider_exclusive_or_with_one_is_not_a_negation_and_is_left_alone() {
2995 let (_, mut func, block) = blank();
2996 let mut build = Builder::new(&mut func, block);
2997 let x = build.iconst(Type::int(32), 3);
2998 let y = build.iconst(Type::int(32), 4);
2999 let cmp = build.icmp(IntPred::Slt, x, y);
3000 let wide = build.unary(Opcode::ZExt, cmp, Type::int(32));
3001 let one = build.iconst(Type::int(32), 1);
3002 let flipped = build.binary(Opcode::Xor, wide, one, Flags::NONE);
3003 let narrow = build.unary(Opcode::Trunc, flipped, Type::int(1));
3004 build.ret(&[narrow]);
3005 assert!(!simplify(&mut func), "an i32 xor 1 flips one bit of thirty two");
3006 assert_eq!(came_from(&func, flipped).0, Opcode::Xor);
3007 }
3008
3009 #[test]
3010 fn the_comparisons_flags_travel_with_the_predicate() {
3011 let (_, mut func, block) = blank();
3012 let mut build = Builder::new(&mut func, block);
3013 let x = build.iconst(Type::int(64), 0);
3014 let x = build.unary(Opcode::Bitcast, x, Type::float(Float::F64));
3015 let y = build.iconst(Type::int(64), 1);
3017 let y = build.unary(Opcode::Bitcast, y, Type::float(Float::F64));
3018 let cmp = build.fcmp(FloatPred::Olt, x, y, Flags::FAST);
3019 let ones = build.iconst(Type::int(1), -1);
3020 let not = build.binary(Opcode::Xor, cmp, ones, Flags::NONE);
3021 build.ret(&[not]);
3022 assert!(simplify(&mut func));
3023 let rucc_ir::Def::Result { inst, .. } = func[not].def else { panic!("not a result") };
3024 assert_eq!(func[inst].flags, Flags::FAST);
3027 }
3028
3029 #[test]
3030 fn fuel_stops_the_transformation_and_not_the_walk() {
3031 let (_, mut func, block) = blank();
3032 let mut build = Builder::new(&mut func, block);
3033 let x = build.iconst(Type::int(32), 3);
3034 let y = build.iconst(Type::int(32), 4);
3035 let a = build.icmp(IntPred::Slt, x, y);
3036 let b = build.icmp(IntPred::Sgt, x, y);
3037 let ones = build.iconst(Type::int(1), -1);
3038 let first = build.binary(Opcode::Xor, a, ones, Flags::NONE);
3039 let second = build.binary(Opcode::Xor, b, ones, Flags::NONE);
3040 let both = build.binary(Opcode::And, first, second, Flags::NONE);
3041 build.ret(&[both]);
3042 let stats =
3043 Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
3044 assert!(stats.changed());
3045 assert_eq!(stats.count(Kind::Optimized, super::FLIPPED), 1);
3046 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
3047 assert_eq!(came_from(&func, first).0, Opcode::ICmp);
3048 assert_eq!(came_from(&func, second).0, Opcode::Xor);
3049 }
3050
3051 fn a_pair() -> (Func, Block, Value, Value) {
3053 let mut names = Interner::new();
3054 let name = names.intern("f");
3055 let int = Type::int(32);
3056 let signature = Signature::new().with_params(&[int, int]).with_returns(&[Type::int(1)]);
3057 let mut func = Func::new(name, signature);
3058 let block = func.create_block();
3059 let x = func.append_param(block, int);
3060 let y = func.append_param(block, int);
3061 (func, block, x, y)
3062 }
3063
3064 fn a_float_pair() -> (Func, Block, Value, Value) {
3066 let mut names = Interner::new();
3067 let name = names.intern("f");
3068 let float = Type::float(Float::F64);
3069 let signature = Signature::new().with_params(&[float, float]).with_returns(&[Type::int(1)]);
3070 let mut func = Func::new(name, signature);
3071 let block = func.create_block();
3072 let x = func.append_param(block, float);
3073 let y = func.append_param(block, float);
3074 (func, block, x, y)
3075 }
3076
3077 #[test]
3085 fn the_opposite_of_a_float_predicate_is_the_buckets_it_leaves_out() {
3086 for pred in FloatPred::all() {
3087 assert_eq!(
3088 super::float_buckets(pred.inverse()),
3089 super::bucket::ALL_FLOAT ^ super::float_buckets(pred),
3090 "{pred:?}"
3091 );
3092 }
3093 }
3094
3095 #[test]
3100 fn swapping_a_float_predicates_operands_exchanges_below_and_above() {
3101 for pred in FloatPred::all() {
3102 let want = super::turned(super::float_buckets(pred));
3103 assert_eq!(super::float_buckets(pred.swapped()), want, "{pred:?}");
3104 }
3105 }
3106
3107 #[test]
3110 fn every_set_of_float_buckets_is_a_predicate() {
3111 for pred in FloatPred::all() {
3112 assert_eq!(super::float_pred(super::float_buckets(pred)), Some(pred), "{pred:?}");
3113 }
3114 for buckets in 0..=super::bucket::ALL_FLOAT {
3115 assert!(super::float_pred(buckets).is_some(), "{buckets} spells nothing");
3116 }
3117 }
3118
3119 #[test]
3121 fn an_integer_predicate_agrees_with_its_own_opposite_and_its_own_swap() {
3122 use super::bucket::ALL_INT;
3123 for pred in IntPred::all() {
3124 let (before, reading) = super::int_buckets(pred);
3125 let (opposite, other) = super::int_buckets(pred.inverse());
3126 assert_eq!(opposite, ALL_INT ^ before, "the opposite of {pred:?}");
3127 assert_eq!(other, reading, "the opposite of {pred:?} reads the operands differently");
3128 let (swapped, other) = super::int_buckets(pred.swapped());
3129 assert_eq!(swapped, super::turned(before), "the swap of {pred:?}");
3130 assert_eq!(other, reading, "the swap of {pred:?} reads the operands differently");
3131 }
3132 }
3133
3134 #[test]
3137 fn every_integer_predicate_is_read_back_as_itself() {
3138 for pred in IntPred::all() {
3139 let (buckets, reading) = super::int_buckets(pred);
3140 assert_eq!(super::int_pred(buckets, reading), Some(pred), "{pred:?}");
3141 }
3142 }
3143
3144 #[test]
3145 fn two_integer_comparisons_that_agree_about_nothing_are_false() {
3146 let (mut func, block, x, y) = a_pair();
3147 let mut build = Builder::new(&mut func, block);
3148 let same = build.icmp(IntPred::Eq, x, y);
3149 let differ = build.icmp(IntPred::Ne, x, y);
3150 let both = build.binary(Opcode::And, same, differ, Flags::NONE);
3151 build.ret(&[both]);
3152 assert!(simplify(&mut func));
3153 assert_eq!(number(&func, both), 0);
3154 }
3155
3156 #[test]
3157 fn two_integer_comparisons_that_cover_everything_are_true() {
3158 let (mut func, block, x, y) = a_pair();
3159 let mut build = Builder::new(&mut func, block);
3160 let above = build.icmp(IntPred::Sge, x, y);
3161 let below = build.icmp(IntPred::Slt, x, y);
3162 let either = build.binary(Opcode::Or, above, below, Flags::NONE);
3163 build.ret(&[either]);
3164 assert!(simplify(&mut func));
3165 assert_ne!(number(&func, either), 0);
3166 }
3167
3168 #[test]
3171 fn two_integer_comparisons_that_overlap_become_one() {
3172 let (mut func, block, x, y) = a_pair();
3173 let mut build = Builder::new(&mut func, block);
3174 let below = build.icmp(IntPred::Slt, x, y);
3175 let same = build.icmp(IntPred::Eq, x, y);
3176 let either = build.binary(Opcode::Or, below, same, Flags::NONE);
3177 build.ret(&[either]);
3178 assert!(simplify(&mut func));
3179 assert_eq!(came_from(&func, either), (Opcode::ICmp, Extra::IntPred(IntPred::Sle)));
3180 assert_eq!(operands(&func, either), [x, y]);
3181 }
3182
3183 #[test]
3186 fn the_second_comparison_is_read_in_the_first_ones_operand_order() {
3187 let (mut func, block, x, y) = a_pair();
3188 let mut build = Builder::new(&mut func, block);
3189 let below = build.icmp(IntPred::Slt, x, y);
3190 let above = build.icmp(IntPred::Slt, y, x);
3191 let both = build.binary(Opcode::And, below, above, Flags::NONE);
3192 build.ret(&[both]);
3193 assert!(simplify(&mut func));
3194 assert_eq!(number(&func, both), 0);
3195 }
3196
3197 #[test]
3200 fn an_equality_takes_the_ordering_of_the_comparison_beside_it() {
3201 for (ordered, want) in [(IntPred::Ult, IntPred::Ule), (IntPred::Slt, IntPred::Sle)] {
3202 let (mut func, block, x, y) = a_pair();
3203 let mut build = Builder::new(&mut func, block);
3204 let below = build.icmp(ordered, x, y);
3205 let same = build.icmp(IntPred::Eq, x, y);
3206 let either = build.binary(Opcode::Or, below, same, Flags::NONE);
3207 build.ret(&[either]);
3208 assert!(simplify(&mut func), "{ordered:?}");
3209 assert_eq!(came_from(&func, either).1, Extra::IntPred(want), "{ordered:?}");
3210 }
3211 }
3212
3213 #[test]
3216 fn a_signed_comparison_and_an_unsigned_one_are_left_alone() {
3217 let (mut func, block, x, y) = a_pair();
3218 let mut build = Builder::new(&mut func, block);
3219 let signed = build.icmp(IntPred::Slt, x, y);
3220 let unsigned = build.icmp(IntPred::Ugt, x, y);
3221 let both = build.binary(Opcode::And, signed, unsigned, Flags::NONE);
3222 build.ret(&[both]);
3223 assert!(!simplify(&mut func));
3224 assert_eq!(came_from(&func, both).0, Opcode::And);
3225 }
3226
3227 #[test]
3228 fn two_comparisons_about_different_operands_are_left_alone() {
3229 let (mut func, block, x, y) = a_pair();
3230 let mut build = Builder::new(&mut func, block);
3231 let other = build.iconst(Type::int(32), 7);
3232 let first = build.icmp(IntPred::Slt, x, y);
3233 let second = build.icmp(IntPred::Sgt, x, other);
3234 let both = build.binary(Opcode::And, first, second, Flags::NONE);
3235 build.ret(&[both]);
3236 assert!(!simplify(&mut func));
3237 assert_eq!(came_from(&func, both).0, Opcode::And);
3238 }
3239
3240 #[test]
3244 fn two_float_comparisons_that_agree_about_nothing_are_false() {
3245 let (mut func, block, x, y) = a_float_pair();
3246 let mut build = Builder::new(&mut func, block);
3247 let same = build.fcmp(FloatPred::Oeq, x, y, Flags::NONE);
3248 let differ = build.fcmp(FloatPred::Une, x, y, Flags::NONE);
3249 let both = build.binary(Opcode::And, same, differ, Flags::NONE);
3250 build.ret(&[both]);
3251 assert!(simplify(&mut func));
3252 assert_eq!(number(&func, both), 0);
3253 }
3254
3255 #[test]
3260 fn a_three_way_float_condition_folds_one_pair_at_a_time() {
3261 let (mut func, block, x, y) = a_float_pair();
3262 let mut build = Builder::new(&mut func, block);
3263 let neither = build.fcmp(FloatPred::Uno, x, y, Flags::NONE);
3264 let above = build.fcmp(FloatPred::Oge, x, y, Flags::NONE);
3265 let below = build.fcmp(FloatPred::Olt, x, y, Flags::NONE);
3266 let first = build.binary(Opcode::Or, neither, above, Flags::NONE);
3267 let whole = build.binary(Opcode::Or, first, below, Flags::NONE);
3268 build.ret(&[whole]);
3269 assert!(simplify(&mut func));
3270 assert_eq!(came_from(&func, first).1, Extra::FloatPred(FloatPred::Uge));
3271 assert_ne!(number(&func, whole), 0);
3272 }
3273
3274 fn a_float() -> (Func, Block, Value) {
3276 let mut names = Interner::new();
3277 let name = names.intern("f");
3278 let float = Type::float(Float::F64);
3279 let signature = Signature::new().with_params(&[float]).with_returns(&[Type::int(1)]);
3280 let mut func = Func::new(name, signature);
3281 let block = func.create_block();
3282 let x = func.append_param(block, float);
3283 (func, block, x)
3284 }
3285
3286 fn magnitude_of(build: &mut Builder<'_>, x: Value) -> Value {
3288 let bits = Type::int(64);
3289 let number = build.unary(Opcode::Bitcast, x, bits);
3290 let mask = build.iconst(bits, i128::from(i64::MAX));
3291 let cleared = build.binary(Opcode::And, number, mask, Flags::NONE);
3292 build.unary(Opcode::Bitcast, cleared, Type::float(Float::F64))
3293 }
3294
3295 #[test]
3298 fn a_magnitude_is_never_below_zero() {
3299 let (mut func, block, x) = a_float();
3300 let mut build = Builder::new(&mut func, block);
3301 let p = magnitude_of(&mut build, x);
3302 let zero = build.fconst(Type::float(Float::F64), 0);
3303 let below = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
3304 build.ret(&[below]);
3305 assert!(simplify(&mut func));
3306 assert_eq!(number(&func, below), 0);
3307 }
3308
3309 #[test]
3312 fn zero_is_never_above_a_magnitude() {
3313 let (mut func, block, x) = a_float();
3314 let mut build = Builder::new(&mut func, block);
3315 let p = magnitude_of(&mut build, x);
3316 let zero = build.fconst(Type::float(Float::F64), 0);
3317 let above = build.fcmp(FloatPred::Ogt, zero, p, Flags::NONE);
3318 build.ret(&[above]);
3319 assert!(simplify(&mut func));
3320 assert_eq!(number(&func, above), 0);
3321 }
3322
3323 #[test]
3326 fn a_magnitude_at_or_below_zero_is_a_magnitude_equal_to_it() {
3327 let (mut func, block, x) = a_float();
3328 let mut build = Builder::new(&mut func, block);
3329 let p = magnitude_of(&mut build, x);
3330 let zero = build.fconst(Type::float(Float::F64), 0);
3331 let atmost = build.fcmp(FloatPred::Ole, p, zero, Flags::NONE);
3332 build.ret(&[atmost]);
3333 assert!(simplify(&mut func));
3334 assert_eq!(came_from(&func, atmost).1, Extra::FloatPred(FloatPred::Oeq));
3335 }
3336
3337 #[test]
3340 fn a_magnitude_is_never_at_or_below_a_negative_number() {
3341 let (mut func, block, x) = a_float();
3342 let mut build = Builder::new(&mut func, block);
3343 let p = magnitude_of(&mut build, x);
3344 let minus_one = build.fconst(Type::float(Float::F64), 0xbff0_0000_0000_0000);
3345 let atmost = build.fcmp(FloatPred::Ole, p, minus_one, Flags::NONE);
3346 build.ret(&[atmost]);
3347 assert!(simplify(&mut func));
3348 assert_eq!(number(&func, atmost), 0);
3349 }
3350
3351 #[test]
3355 fn a_magnitude_at_or_above_zero_is_still_a_question_about_a_nan() {
3356 let (mut func, block, x) = a_float();
3357 let mut build = Builder::new(&mut func, block);
3358 let p = magnitude_of(&mut build, x);
3359 let zero = build.fconst(Type::float(Float::F64), 0);
3360 let atleast = build.fcmp(FloatPred::Oge, p, zero, Flags::NONE);
3361 build.ret(&[atleast]);
3362 assert!(!simplify(&mut func));
3363 assert_eq!(came_from(&func, atleast).1, Extra::FloatPred(FloatPred::Oge));
3364 }
3365
3366 #[test]
3368 fn a_magnitude_against_a_positive_number_is_left_alone() {
3369 let (mut func, block, x) = a_float();
3370 let mut build = Builder::new(&mut func, block);
3371 let p = magnitude_of(&mut build, x);
3372 let one = build.fconst(Type::float(Float::F64), 0x3ff0_0000_0000_0000);
3373 let below = build.fcmp(FloatPred::Olt, p, one, Flags::NONE);
3374 build.ret(&[below]);
3375 assert!(!simplify(&mut func));
3376 assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
3377 }
3378
3379 #[test]
3382 fn a_mask_that_keeps_the_sign_bit_is_not_a_magnitude() {
3383 let (mut func, block, x) = a_float();
3384 let mut build = Builder::new(&mut func, block);
3385 let bits = Type::int(64);
3386 let number = build.unary(Opcode::Bitcast, x, bits);
3387 let mask = build.iconst(bits, -2);
3388 let cleared = build.binary(Opcode::And, number, mask, Flags::NONE);
3389 let p = build.unary(Opcode::Bitcast, cleared, Type::float(Float::F64));
3390 let zero = build.fconst(Type::float(Float::F64), 0);
3391 let below = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
3392 build.ret(&[below]);
3393 assert!(!simplify(&mut func));
3394 assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
3395 }
3396
3397 #[test]
3400 fn a_magnitude_against_a_nan_is_settled_by_the_nan() {
3401 let (mut func, block, x) = a_float();
3402 let mut build = Builder::new(&mut func, block);
3403 let p = magnitude_of(&mut build, x);
3404 let nan = build.fconst(Type::float(Float::F64), NAN);
3405 let below = build.fcmp(FloatPred::Olt, p, nan, Flags::NONE);
3406 build.ret(&[below]);
3407 let stats = Simplify.run(
3408 &mut func,
3409 &mut crate::machine::fixtures::analyses(),
3410 &mut Fuel::unlimited(),
3411 );
3412 assert_eq!(stats.count(Kind::Optimized, super::MAGNITUDE), 0);
3413 assert_eq!(stats.count(Kind::Optimized, super::BOUNDED), 1);
3414 assert_eq!(number(&func, below), 0);
3415 }
3416
3417 const NAN: u128 = 0x7ff8_0000_0000_0000;
3419
3420 const INFINITY: u128 = 0x7ff0_0000_0000_0000;
3422
3423 #[test]
3427 fn a_nan_is_unordered_against_anything() {
3428 for (pred, answer) in [
3429 (FloatPred::Oeq, false),
3430 (FloatPred::Olt, false),
3431 (FloatPred::Ogt, false),
3432 (FloatPred::Ole, false),
3433 (FloatPred::Oge, false),
3434 (FloatPred::One, false),
3435 (FloatPred::Une, true),
3436 (FloatPred::Ult, true),
3437 (FloatPred::Uno, true),
3438 ] {
3439 let (mut func, block, x) = a_float();
3440 let mut build = Builder::new(&mut func, block);
3441 let nan = build.fconst(Type::float(Float::F64), NAN);
3442 let asked = build.fcmp(pred, nan, x, Flags::NONE);
3443 build.ret(&[asked]);
3444 assert!(simplify(&mut func), "{pred:?}");
3445 assert_eq!(number(&func, asked) != 0, answer, "{pred:?}");
3446 }
3447 }
3448
3449 #[test]
3453 fn nothing_is_above_a_positive_infinity() {
3454 let (mut func, block, x) = a_float();
3455 let mut build = Builder::new(&mut func, block);
3456 let infinity = build.fconst(Type::float(Float::F64), INFINITY);
3457 let above = build.fcmp(FloatPred::Ogt, x, infinity, Flags::NONE);
3458 let atmost = build.fcmp(FloatPred::Ole, x, infinity, Flags::NONE);
3459 let below = build.fcmp(FloatPred::Olt, x, infinity, Flags::NONE);
3460 build.ret(&[above, atmost, below]);
3461 assert!(simplify(&mut func));
3462 assert_eq!(number(&func, above), 0);
3463 assert_eq!(came_from(&func, atmost).1, Extra::FloatPred(FloatPred::Ole));
3464 assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
3465 }
3466
3467 #[test]
3469 fn a_negative_infinity_is_above_nothing() {
3470 let (mut func, block, x) = a_float();
3471 let mut build = Builder::new(&mut func, block);
3472 let infinity = build.fconst(Type::float(Float::F64), INFINITY | 1 << 63);
3473 let above = build.fcmp(FloatPred::Ogt, infinity, x, Flags::NONE);
3474 build.ret(&[above]);
3475 assert!(simplify(&mut func));
3476 assert_eq!(number(&func, above), 0);
3477 }
3478
3479 #[test]
3481 fn two_float_constants_are_an_answer() {
3482 let (mut func, block, _) = a_float();
3483 let mut build = Builder::new(&mut func, block);
3484 let one = build.fconst(Type::float(Float::F64), 0x3ff0_0000_0000_0000);
3485 let two = build.fconst(Type::float(Float::F64), 0x4000_0000_0000_0000);
3486 let below = build.fcmp(FloatPred::Olt, one, two, Flags::NONE);
3487 let equal = build.fcmp(FloatPred::Ueq, one, two, Flags::NONE);
3488 build.ret(&[below, equal]);
3489 assert!(simplify(&mut func));
3490 assert_ne!(number(&func, below), 0);
3491 assert_eq!(number(&func, equal), 0);
3492 }
3493
3494 #[test]
3497 fn a_value_against_itself_is_equal_or_a_nan() {
3498 let (mut func, block, x) = a_float();
3499 let mut build = Builder::new(&mut func, block);
3500 let below = build.fcmp(FloatPred::Olt, x, x, Flags::NONE);
3501 let differs = build.fcmp(FloatPred::Une, x, x, Flags::NONE);
3502 let same = build.fcmp(FloatPred::Oeq, x, x, Flags::NONE);
3503 build.ret(&[below, differs, same]);
3504 assert!(simplify(&mut func));
3505 assert_eq!(number(&func, below), 0);
3506 assert_eq!(came_from(&func, differs).1, Extra::FloatPred(FloatPred::Uno));
3507 assert_eq!(came_from(&func, same).1, Extra::FloatPred(FloatPred::Oeq));
3508 }
3509
3510 #[test]
3516 fn a_branch_in_front_settles_the_same_pair() {
3517 let (mut func, entry, x, y) = a_float_pair();
3518 let [then, other, join] = [(); 3].map(|()| func.create_block());
3519 let mut build = Builder::new(&mut func, entry);
3520 let neither = build.fcmp(FloatPred::Uno, x, y, Flags::NONE);
3521 build.br_if(neither, then, &[], other, &[]);
3522 let mut build = Builder::new(&mut func, other);
3523 let ordered = build.fcmp(FloatPred::Ord, x, y, Flags::NONE);
3524 let turned = build.fcmp(FloatPred::Ord, y, x, Flags::NONE);
3525 let above = build.fcmp(FloatPred::Ogt, x, y, Flags::NONE);
3526 build.jump(join, &[]);
3527 let mut build = Builder::new(&mut func, then);
3528 let there = build.fcmp(FloatPred::Ord, x, y, Flags::NONE);
3529 build.jump(join, &[]);
3530 let mut build = Builder::new(&mut func, join);
3531 let both = build.binary(Opcode::And, ordered, turned, Flags::NONE);
3532 let all = build.binary(Opcode::And, both, above, Flags::NONE);
3533 let all = build.binary(Opcode::And, all, there, Flags::NONE);
3534 build.ret(&[all]);
3535 assert!(simplify(&mut func));
3536 assert_ne!(number(&func, ordered), 0);
3537 assert_ne!(number(&func, turned), 0);
3538 assert_eq!(came_from(&func, above).1, Extra::FloatPred(FloatPred::Ogt));
3539 assert_eq!(number(&func, there), 0);
3540 }
3541
3542 #[test]
3545 fn a_join_settles_nothing() {
3546 let (mut func, entry, x, y) = a_float_pair();
3547 let [then, other, join] = [(); 3].map(|()| func.create_block());
3548 let mut build = Builder::new(&mut func, entry);
3549 let neither = build.fcmp(FloatPred::Uno, x, y, Flags::NONE);
3550 build.br_if(neither, then, &[], other, &[]);
3551 Builder::new(&mut func, then).jump(join, &[]);
3552 Builder::new(&mut func, other).jump(join, &[]);
3553 let mut build = Builder::new(&mut func, join);
3554 let ordered = build.fcmp(FloatPred::Ord, x, y, Flags::NONE);
3555 build.ret(&[ordered]);
3556 assert!(!simplify(&mut func));
3557 assert_eq!(came_from(&func, ordered).1, Extra::FloatPred(FloatPred::Ord));
3558 }
3559
3560 #[test]
3562 fn a_finite_bound_is_left_alone() {
3563 let (mut func, block, x) = a_float();
3564 let mut build = Builder::new(&mut func, block);
3565 let one = build.fconst(Type::float(Float::F64), 0x3ff0_0000_0000_0000);
3566 let below = build.fcmp(FloatPred::Olt, x, one, Flags::NONE);
3567 build.ret(&[below]);
3568 assert!(!simplify(&mut func));
3569 assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
3570 }
3571
3572 #[test]
3575 fn fuel_stops_the_magnitude_fold_and_not_the_walk() {
3576 let (mut func, block, x) = a_float();
3577 let mut build = Builder::new(&mut func, block);
3578 let p = magnitude_of(&mut build, x);
3579 let zero = build.fconst(Type::float(Float::F64), 0);
3580 let below = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
3581 let also = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
3582 let both = build.binary(Opcode::Or, below, also, Flags::NONE);
3583 build.ret(&[both]);
3584 let stats =
3585 Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
3586 assert_eq!(stats.count(Kind::Optimized, super::MAGNITUDE), 1);
3587 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_MAGNITUDE), 1);
3588 assert_eq!(number(&func, below), 0);
3589 assert_eq!(came_from(&func, also).0, Opcode::FCmp);
3590 }
3591
3592 #[test]
3595 fn two_comparisons_promised_different_things_are_left_alone() {
3596 let (mut func, block, x, y) = a_float_pair();
3597 let mut build = Builder::new(&mut func, block);
3598 let below = build.fcmp(FloatPred::Olt, x, y, Flags::FAST);
3599 let same = build.fcmp(FloatPred::Oeq, x, y, Flags::NONE);
3600 let either = build.binary(Opcode::Or, below, same, Flags::NONE);
3601 build.ret(&[either]);
3602 assert!(!simplify(&mut func));
3603 assert_eq!(came_from(&func, either).0, Opcode::Or);
3604 }
3605
3606 #[test]
3607 fn the_promise_both_comparisons_were_made_under_travels_to_the_one_that_replaces_them() {
3608 let (mut func, block, x, y) = a_float_pair();
3609 let mut build = Builder::new(&mut func, block);
3610 let below = build.fcmp(FloatPred::Olt, x, y, Flags::FAST);
3611 let same = build.fcmp(FloatPred::Oeq, x, y, Flags::FAST);
3612 let either = build.binary(Opcode::Or, below, same, Flags::NONE);
3613 build.ret(&[either]);
3614 assert!(simplify(&mut func));
3615 assert_eq!(came_from(&func, either).1, Extra::FloatPred(FloatPred::Ole));
3616 let rucc_ir::Def::Result { inst, .. } = func[either].def else { panic!("not a result") };
3617 assert_eq!(func[inst].flags, Flags::FAST);
3618 }
3619
3620 #[test]
3623 fn a_wider_and_of_two_comparisons_is_left_alone() {
3624 let (mut func, block, x, y) = a_pair();
3625 let mut build = Builder::new(&mut func, block);
3626 let same = build.icmp(IntPred::Eq, x, y);
3627 let differ = build.icmp(IntPred::Ne, x, y);
3628 let first = build.unary(Opcode::ZExt, same, Type::int(32));
3629 let second = build.unary(Opcode::ZExt, differ, Type::int(32));
3630 let both = build.binary(Opcode::And, first, second, Flags::NONE);
3631 let narrow = build.unary(Opcode::Trunc, both, Type::int(1));
3632 build.ret(&[narrow]);
3633 assert!(!simplify(&mut func));
3634 assert_eq!(came_from(&func, both).0, Opcode::And);
3635 }
3636
3637 #[test]
3638 fn fuel_stops_the_composite_fold_and_not_the_walk() {
3639 let (mut func, block, x, y) = a_pair();
3640 let mut build = Builder::new(&mut func, block);
3641 let same = build.icmp(IntPred::Eq, x, y);
3642 let differ = build.icmp(IntPred::Ne, x, y);
3643 let below = build.icmp(IntPred::Slt, x, y);
3644 let above = build.icmp(IntPred::Sgt, x, y);
3645 let first = build.binary(Opcode::And, same, differ, Flags::NONE);
3646 let second = build.binary(Opcode::And, below, above, Flags::NONE);
3647 let both = build.binary(Opcode::Or, first, second, Flags::NONE);
3648 build.ret(&[both]);
3649 let stats =
3650 Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
3651 assert!(stats.changed());
3652 assert_eq!(stats.count(Kind::Optimized, super::COMPOSITE), 1);
3653 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_COMPOSITE), 1);
3654 assert_eq!(came_from(&func, first).0, Opcode::IConst);
3655 assert_eq!(came_from(&func, second).0, Opcode::And);
3656 }
3657}