1use std::cmp::Ordering;
162use std::collections::HashMap;
163use std::sync::OnceLock;
164
165use rucc_base::float::Float;
166use rucc_ir::term::{PLAIN, Plan, Shown, Term, Terms};
167use rucc_ir::{
168 Block, Def, Extra, Flags, FloatPred, Func, Imm, Inst, InstData, IntPred, Opcode, Type, Value,
169};
170
171use crate::rules::{Match, Piece, Subject, Table, canonical, compare, identities, strength, width};
172use crate::uses::{count, substitute};
173use crate::{Analyses, Analysis, Fuel, Pass, Preserved, Stats};
174
175const FLIPPED: &str = "comparison negated by an exclusive or rewritten as the opposite comparison";
177
178const NO_FUEL: &str = "negated comparison left alone, the pass ran out of fuel";
180
181const COMPOSITE: &str = "two comparisons over the same operands combined into one";
183
184const NO_FUEL_COMPOSITE: &str = "pair of comparisons left alone, the pass ran out of fuel";
186
187const MAGNITUDE: &str = "comparison against a value whose sign bit is clear settled by the sign";
189
190const NO_FUEL_MAGNITUDE: &str =
192 "comparison against a magnitude left alone, the pass ran out of fuel";
193
194const NO_FUEL_RULE: &str = "rewrite left alone, the pass ran out of fuel";
196
197const PLANS: [Plan; 3] =
205 [[Shown::Reg, Shown::Const, Shown::Reg], [Shown::Const, Shown::Reg, Shown::Reg], PLAIN];
206
207const CANONICAL: [Plan; 1] = [[Shown::Const, Shown::Var, Shown::Reg]];
217
218const EXPAND: [Plan; 1] = [[Shown::Expand, Shown::Reg, Shown::Reg]];
228
229const COMPARE: [Plan; 2] =
249 [[Shown::Reg, Shown::Const, Shown::Reg], [Shown::Expand, Shown::Const, Shown::Reg]];
250
251const TABLES: [(&Table, &[Plan]); 5] = [
272 (&identities::TABLE, &PLANS),
273 (&strength::TABLE, &PLANS),
274 (&width::TABLE, &EXPAND),
275 (&compare::TABLE, &COMPARE),
276 (&canonical::TABLE, &CANONICAL),
277];
278
279#[derive(Debug, Clone, Copy, PartialEq, Eq)]
281pub struct Simplify;
282
283impl Pass for Simplify {
284 fn name(&self) -> &'static str {
285 "simplify"
286 }
287
288 fn describe(&self) -> &'static str {
289 "the identities, the strength reductions, the canonicalisations, and the three comparison \
290 rewrites written by hand"
291 }
292
293 fn preserves(&self) -> Preserved {
294 Preserved::ALL.without(Analysis::Liveness)
308 }
309
310 fn run(&self, func: &mut Func, _an: &mut Analyses, fuel: &mut Fuel) -> Stats {
311 let mut stats = Stats::new();
312 let mut forward: HashMap<Value, Value> = HashMap::new();
317 let uses = count(func);
329 let dead = |func: &Func, inst: Inst| match func[inst].first_result {
330 Some(result) => uses[result.index()] == 0,
331 None => false,
332 };
333 for block in func.blocks().collect::<Vec<Block>>() {
334 for inst in func.insts(block).collect::<Vec<Inst>>() {
335 if dead(func, inst) {
336 continue;
337 }
338 if let Some(flip) = negated_comparison(func, inst) {
339 if !fuel.take() {
340 stats.missed(NO_FUEL);
344 continue;
345 }
346 let args = func.push_values(&[flip.lhs, flip.rhs]);
347 let data = &mut func[inst];
348 data.opcode = flip.opcode;
349 data.flags = flip.flags;
350 data.args = args;
351 data.extra = flip.extra;
352 stats.optimized(FLIPPED);
353 continue;
354 }
355 if let Some(composite) = composite_comparison(func, inst) {
356 if !fuel.take() {
357 stats.missed(NO_FUEL_COMPOSITE);
358 continue;
359 }
360 fold_composite(func, inst, composite);
361 stats.optimized(COMPOSITE);
362 continue;
363 }
364 if let Some(settled) = magnitude_comparison(func, inst) {
365 if !fuel.take() {
366 stats.missed(NO_FUEL_MAGNITUDE);
367 continue;
368 }
369 fold_composite(func, inst, settled);
370 stats.optimized(MAGNITUDE);
371 continue;
372 }
373 let Some((rewrite, pattern)) = identity(func, inst) else { continue };
374 if !fuel.take() {
375 stats.missed(NO_FUEL_RULE);
376 continue;
377 }
378 match rewrite {
379 Rewrite::Value(value) => {
380 let result = func[inst].first_result.expect("the rule matched a result");
381 forward.insert(result, value);
382 }
383 Rewrite::Constant(number) => become_constant(func, inst, number),
384 Rewrite::Built { opcode, pred, lhs, rhs } => {
385 become_instruction(func, inst, opcode, pred, lhs, rhs);
386 }
387 Rewrite::Converted { opcode, from } => {
388 become_conversion(func, inst, opcode, from);
389 }
390 }
391 stats.optimized(pattern);
392 }
393 }
394 if !forward.is_empty() {
395 substitute(func, &forward);
396 }
397 stats
398 }
399}
400
401#[derive(Clone, Copy, Debug, PartialEq, Eq)]
403enum Rewrite {
404 Value(Value),
406 Constant(i128),
408 Built {
410 opcode: Opcode,
412 pred: Option<IntPred>,
419 lhs: Operand,
421 rhs: Operand,
423 },
424 Converted {
432 opcode: Opcode,
434 from: Value,
436 },
437}
438
439#[derive(Clone, Copy, Debug, PartialEq, Eq)]
441enum Operand {
442 Value(Value),
444 Constant {
448 number: i128,
450 bits: u32,
458 },
459}
460
461fn identity(func: &Func, inst: Inst) -> Option<(Rewrite, &'static str)> {
467 let result = func[inst].first_result?;
468 for (table, plan) in
469 TABLES.into_iter().flat_map(|(table, plans)| plans.iter().map(move |&plan| (table, plan)))
470 {
471 let terms = Terms::new(func, inst, plan);
472 let Some(found) = table.find(&terms, Term::Root) else { continue };
473 let rule = table.rule(&found);
474 let rewrite = match rule.replacement {
475 [Piece::App { head, arity: 1 }, Piece::Var { index, .. }]
478 if head.starts_with("value.") =>
479 {
480 match found.bindings.get(*index) {
481 Some(&Term::Reg(value)) => Rewrite::Value(value),
482 _ => continue,
483 }
484 }
485 [Piece::App { head, arity: 1 }, Piece::Int(number)]
489 if head.starts_with("iconst.") && func[result].ty.is_int() =>
490 {
491 Rewrite::Constant(*number)
492 }
493 pieces => match built(pieces, &found, &matched(&terms, &found)) {
498 Some(rewrite) => rewrite,
499 None => continue,
503 },
504 };
505 return Some((rewrite, rule.pattern));
506 }
507 None
508}
509
510fn built(
517 pieces: &'static [Piece],
518 found: &Match<Term>,
519 matched: &[Option<i128>],
520) -> Option<Rewrite> {
521 if let Some(rewrite) = converted(pieces, found) {
522 return Some(rewrite);
523 }
524 let [Piece::App { head, arity: 2 }, rest @ ..] = pieces else { return None };
525 let opcode = opcode_of(head)?;
526 let pred = rucc_ir::term::int_pred(head);
529 if (opcode == Opcode::ICmp) != pred.is_some() {
530 return None;
534 }
535 let (lhs, rest) = operand(rest, found, matched)?;
536 let (rhs, rest) = operand(rest, found, matched)?;
537 rest.is_empty().then_some(Rewrite::Built { opcode, pred, lhs, rhs })
538}
539
540fn matched(terms: &Terms<'_>, found: &Match<Term>) -> Vec<Option<i128>> {
546 found.bindings.iter().map(|&node| terms.int(node)).collect()
547}
548
549fn converted(pieces: &'static [Piece], found: &Match<Term>) -> Option<Rewrite> {
561 let [Piece::App { head, arity: 1 }, rest @ ..] = pieces else { return None };
562 let opcode = match opcode_of(head)? {
563 opcode @ (Opcode::SExt | Opcode::ZExt | Opcode::Trunc) => opcode,
564 _ => return None,
565 };
566 let [Piece::App { head: inner, arity: 1 }, Piece::Var { index, .. }] = rest else {
567 return None;
568 };
569 if !inner.starts_with("value.") {
570 return None;
571 }
572 match found.bindings.get(*index) {
573 Some(&Term::Reg(from)) => Some(Rewrite::Converted { opcode, from }),
574 _ => None,
575 }
576}
577
578fn operand(
580 pieces: &'static [Piece],
581 found: &Match<Term>,
582 matched: &[Option<i128>],
583) -> Option<(Operand, &'static [Piece])> {
584 match pieces {
585 [Piece::App { head, arity: 1 }, Piece::Var { index, .. }, rest @ ..]
586 if head.starts_with("value.") =>
587 {
588 match found.bindings.get(*index) {
589 Some(&Term::Reg(value)) => Some((Operand::Value(value), rest)),
590 _ => None,
591 }
592 }
593 [Piece::App { head, arity: 1 }, Piece::Int(number), rest @ ..]
594 if head.starts_with("iconst.") =>
595 {
596 Some((Operand::Constant { number: *number, bits: bits_of(head)? }, rest))
597 }
598 [Piece::App { head, arity: 1 }, Piece::Computed { work, .. }, rest @ ..]
603 if head.starts_with("iconst.") =>
604 {
605 let number = work(matched)?;
606 Some((Operand::Constant { number, bits: bits_of(head)? }, rest))
607 }
608 [Piece::App { head, arity: 1 }, Piece::Var { index, .. }, rest @ ..]
612 if head.starts_with("iconst.") =>
613 {
614 match found.bindings.get(*index) {
615 Some(&Term::Num(number)) => {
616 Some((Operand::Constant { number, bits: bits_of(head)? }, rest))
617 }
618 _ => None,
619 }
620 }
621 _ => None,
622 }
623}
624
625fn bits_of(head: &str) -> Option<u32> {
632 head.rsplit_once('.')?.1.strip_prefix('i')?.parse().ok()
633}
634
635fn opcode_of(head: &str) -> Option<Opcode> {
646 static NAMES: OnceLock<HashMap<&'static str, Opcode>> = OnceLock::new();
647 let names = NAMES.get_or_init(|| {
648 let mut names = HashMap::new();
649 for (opcode, name) in rucc_ir::term::heads() {
650 names.entry(name).or_insert(opcode);
651 }
652 names
653 });
654 names.get(head).copied()
655}
656
657fn become_instruction(
662 func: &mut Func,
663 inst: Inst,
664 opcode: Opcode,
665 pred: Option<IntPred>,
666 lhs: Operand,
667 rhs: Operand,
668) {
669 let result = func[inst].first_result.expect("the rule matched a result");
670 let ty = func[result].ty;
671 let lhs = defined(func, inst, ty, lhs);
672 let rhs = defined(func, inst, ty, rhs);
673 let args = func.push_values(&[lhs, rhs]);
674 let data = &mut func[inst];
675 data.opcode = opcode;
676 data.args = args;
677 data.extra = match pred {
683 Some(pred) => Extra::IntPred(pred),
684 None => Extra::None,
685 };
686 data.flags = Flags::NONE;
692}
693
694fn become_conversion(func: &mut Func, inst: Inst, opcode: Opcode, from: Value) {
704 let args = func.push_values(&[from]);
705 let data = &mut func[inst];
706 data.opcode = opcode;
707 data.args = args;
708 data.extra = Extra::None;
711 data.flags = Flags::NONE;
712}
713
714fn defined(func: &mut Func, before: Inst, ty: Type, operand: Operand) -> Value {
722 match operand {
723 Operand::Value(value) => value,
724 Operand::Constant { number, bits } => {
725 let ty = if ty.lane() == Type::int(bits) { ty } else { Type::int(bits) };
726 let at = func.add_imm(Imm::int(number, ty.lane()));
727 let data = InstData { extra: Extra::Imm(at), ..InstData::new(Opcode::IConst) };
728 let span = func.span(before);
729 let iconst = func.create_inst(data, &[ty], span);
730 func.insert_before(iconst, before);
731 func[iconst].first_result.expect("one result was asked for")
732 }
733 }
734}
735
736fn become_constant(func: &mut Func, inst: Inst, number: i128) {
741 let result = func[inst].first_result.expect("the rule matched a result");
742 let ty = func[result].ty;
743 let imm = func.add_imm(Imm::int(number, ty.lane()));
744 let args = func.push_values(&[]);
745 let data = &mut func[inst];
746 data.opcode = Opcode::IConst;
747 data.args = args;
748 data.extra = Extra::Imm(imm);
749 data.flags = Flags::NONE;
752}
753
754pub(crate) struct Flip {
756 opcode: Opcode,
758 flags: Flags,
760 extra: Extra,
762 lhs: Value,
764 rhs: Value,
766}
767
768fn negated_comparison(func: &Func, inst: Inst) -> Option<Flip> {
775 let data = &func[inst];
776 if data.opcode != Opcode::Xor {
777 return None;
778 }
779 let args = &func[data.args];
780 let (&first, &second) = (args.first()?, args.get(1)?);
781 if func[first].ty != Type::int(1) {
782 return None;
783 }
784 let cmp = match (all_ones(func, first), all_ones(func, second)) {
785 (true, false) => second,
786 (false, true) => first,
787 _ => return None,
790 };
791 let Def::Result { inst: cmp, .. } = func[cmp].def else { return None };
792 let data = &func[cmp];
793 let extra = match (data.opcode, data.extra) {
794 (Opcode::ICmp, Extra::IntPred(pred)) => Extra::IntPred(pred.inverse()),
795 (Opcode::FCmp, Extra::FloatPred(pred)) => Extra::FloatPred(pred.inverse()),
796 _ => return None,
797 };
798 let args = &func[data.args];
799 Some(Flip {
800 opcode: data.opcode,
801 flags: data.flags,
802 extra,
803 lhs: *args.first()?,
804 rhs: *args.get(1)?,
805 })
806}
807
808mod bucket {
821 pub(super) const LT: u8 = 1;
823 pub(super) const EQ: u8 = 2;
825 pub(super) const GT: u8 = 4;
827 pub(super) const UN: u8 = 8;
829 pub(super) const ALL_INT: u8 = LT | EQ | GT;
831 pub(super) const ALL_FLOAT: u8 = LT | EQ | GT | UN;
833}
834
835#[derive(Clone, Copy, Debug, PartialEq, Eq)]
843enum Reading {
844 Signed,
846 Unsigned,
848 Neither,
850}
851
852impl Reading {
853 const fn shared(self, other: Self) -> Option<Self> {
855 match (self, other) {
856 (Self::Neither, same) | (same, Self::Neither) => Some(same),
857 (Self::Signed, Self::Signed) => Some(Self::Signed),
858 (Self::Unsigned, Self::Unsigned) => Some(Self::Unsigned),
859 (Self::Signed, Self::Unsigned) | (Self::Unsigned, Self::Signed) => None,
860 }
861 }
862}
863
864const fn int_buckets(pred: IntPred) -> (u8, Reading) {
866 use bucket::{EQ, GT, LT};
867 match pred {
868 IntPred::Eq => (EQ, Reading::Neither),
869 IntPred::Ne => (LT | GT, Reading::Neither),
870 IntPred::Slt => (LT, Reading::Signed),
871 IntPred::Sle => (LT | EQ, Reading::Signed),
872 IntPred::Sgt => (GT, Reading::Signed),
873 IntPred::Sge => (GT | EQ, Reading::Signed),
874 IntPred::Ult => (LT, Reading::Unsigned),
875 IntPred::Ule => (LT | EQ, Reading::Unsigned),
876 IntPred::Ugt => (GT, Reading::Unsigned),
877 IntPred::Uge => (GT | EQ, Reading::Unsigned),
878 }
879}
880
881const fn int_pred(buckets: u8, reading: Reading) -> Option<IntPred> {
889 use bucket::{EQ, GT, LT};
890 match (buckets, reading) {
891 (EQ, _) => Some(IntPred::Eq),
892 (b, _) if b == LT | GT => Some(IntPred::Ne),
893 (LT, Reading::Signed) => Some(IntPred::Slt),
894 (GT, Reading::Signed) => Some(IntPred::Sgt),
895 (b, Reading::Signed) if b == LT | EQ => Some(IntPred::Sle),
896 (b, Reading::Signed) if b == GT | EQ => Some(IntPred::Sge),
897 (LT, Reading::Unsigned) => Some(IntPred::Ult),
898 (GT, Reading::Unsigned) => Some(IntPred::Ugt),
899 (b, Reading::Unsigned) if b == LT | EQ => Some(IntPred::Ule),
900 (b, Reading::Unsigned) if b == GT | EQ => Some(IntPred::Uge),
901 _ => None,
902 }
903}
904
905const fn float_buckets(pred: FloatPred) -> u8 {
910 use bucket::{ALL_FLOAT, EQ, GT, LT, UN};
911 match pred {
912 FloatPred::False => 0,
913 FloatPred::Oeq => EQ,
914 FloatPred::Ogt => GT,
915 FloatPred::Oge => GT | EQ,
916 FloatPred::Olt => LT,
917 FloatPred::Ole => LT | EQ,
918 FloatPred::One => LT | GT,
919 FloatPred::Ord => LT | EQ | GT,
920 FloatPred::Uno => UN,
921 FloatPred::Ueq => EQ | UN,
922 FloatPred::Ugt => GT | UN,
923 FloatPred::Uge => GT | EQ | UN,
924 FloatPred::Ult => LT | UN,
925 FloatPred::Ule => LT | EQ | UN,
926 FloatPred::Une => LT | GT | UN,
927 FloatPred::True => ALL_FLOAT,
928 }
929}
930
931fn float_pred(buckets: u8) -> Option<FloatPred> {
933 FloatPred::all().find(|pred| float_buckets(*pred) == buckets)
934}
935
936struct Side {
938 opcode: Opcode,
940 flags: Flags,
944 buckets: u8,
946 reading: Reading,
949 lhs: Value,
951 rhs: Value,
953}
954
955fn side(func: &Func, value: Value) -> Option<Side> {
957 let Def::Result { inst, .. } = func[value].def else { return None };
958 let data = &func[inst];
959 let (buckets, reading) = match (data.opcode, data.extra) {
960 (Opcode::ICmp, Extra::IntPred(pred)) => int_buckets(pred),
961 (Opcode::FCmp, Extra::FloatPred(pred)) => (float_buckets(pred), Reading::Neither),
962 _ => return None,
963 };
964 let args = &func[data.args];
965 Some(Side {
966 opcode: data.opcode,
967 flags: data.flags,
968 buckets,
969 reading,
970 lhs: *args.first()?,
971 rhs: *args.get(1)?,
972 })
973}
974
975const fn turned(buckets: u8) -> u8 {
980 use bucket::{GT, LT};
981 let mut out = buckets & !(LT | GT);
982 if buckets & LT != 0 {
983 out |= GT;
984 }
985 if buckets & GT != 0 {
986 out |= LT;
987 }
988 out
989}
990
991fn aligned(first: &Side, second: Side) -> Option<Side> {
997 if first.lhs == second.lhs && first.rhs == second.rhs {
998 return Some(second);
999 }
1000 if first.lhs != second.rhs || first.rhs != second.lhs {
1001 return None;
1002 }
1003 let buckets = turned(second.buckets);
1004 Some(Side { buckets, lhs: first.lhs, rhs: first.rhs, ..second })
1005}
1006
1007pub(crate) enum Composite {
1009 Always(bool),
1011 Pred(Flip),
1013}
1014
1015fn composite_comparison(func: &Func, inst: Inst) -> Option<Composite> {
1033 let data = &func[inst];
1034 if func[data.first_result?].ty != Type::int(1) {
1035 return None;
1036 }
1037 let args = &func[data.args];
1038 composite(func, data.opcode, *args.first()?, *args.get(1)?)
1039}
1040
1041pub(crate) fn composite(func: &Func, opcode: Opcode, lhs: Value, rhs: Value) -> Option<Composite> {
1048 let intersect = match opcode {
1049 Opcode::And => true,
1050 Opcode::Or => false,
1051 _ => return None,
1052 };
1053 let first = side(func, lhs)?;
1054 let second = aligned(&first, side(func, rhs)?)?;
1055 if first.opcode != second.opcode || first.flags != second.flags {
1056 return None;
1057 }
1058 let reading = first.reading.shared(second.reading)?;
1059 let buckets = match intersect {
1060 true => first.buckets & second.buckets,
1061 false => first.buckets | second.buckets,
1062 };
1063 let whole = match first.opcode {
1064 Opcode::ICmp => bucket::ALL_INT,
1065 _ => bucket::ALL_FLOAT,
1066 };
1067 if buckets == 0 {
1068 return Some(Composite::Always(false));
1069 }
1070 if buckets == whole {
1071 return Some(Composite::Always(true));
1072 }
1073 let extra = match first.opcode {
1074 Opcode::ICmp => Extra::IntPred(int_pred(buckets, reading)?),
1075 _ => Extra::FloatPred(float_pred(buckets)?),
1076 };
1077 Some(Composite::Pred(Flip {
1078 opcode: first.opcode,
1079 flags: first.flags,
1080 extra,
1081 lhs: first.lhs,
1082 rhs: first.rhs,
1083 }))
1084}
1085
1086fn magnitude(func: &Func, value: Value) -> bool {
1100 let Def::Result { inst, .. } = func[value].def else { return false };
1101 let data = &func[inst];
1102 if data.opcode != Opcode::Bitcast {
1103 return false;
1104 }
1105 let Some(&bits) = func[data.args].first() else { return false };
1106 let Def::Result { inst: masked, .. } = func[bits].def else { return false };
1107 let data = &func[masked];
1108 if data.opcode != Opcode::And {
1109 return false;
1110 }
1111 func[data.args].iter().any(|&arg| clears_the_sign(func, arg))
1112}
1113
1114fn clears_the_sign(func: &Func, value: Value) -> bool {
1116 let ty = func[value].ty;
1117 let Def::Result { inst, .. } = func[value].def else { return false };
1118 let data = &func[inst];
1119 let Extra::Imm(at) = data.extra else { return false };
1120 data.opcode == Opcode::IConst && ty.is_int() && func[at].signed(ty) >= 0
1121}
1122
1123fn against(func: &Func, value: Value) -> Option<u8> {
1135 use bucket::{EQ, GT, UN};
1136 let Def::Result { inst, .. } = func[value].def else { return None };
1137 let data = &func[inst];
1138 if data.opcode != Opcode::FConst {
1139 return None;
1140 }
1141 let Extra::Imm(at) = data.extra else { return None };
1142 let format = func[value].ty.format()?.encoding();
1143 let number = Float::from_bits(format, func[at].bits());
1144 match number.compare(Float::zero(format, false))? {
1145 Ordering::Less => Some(GT | UN),
1146 Ordering::Equal => Some(GT | EQ | UN),
1147 Ordering::Greater => None,
1148 }
1149}
1150
1151fn magnitude_comparison(func: &Func, inst: Inst) -> Option<Composite> {
1166 let data = &func[inst];
1167 let Extra::FloatPred(pred) = data.extra else { return None };
1168 if data.opcode != Opcode::FCmp {
1169 return None;
1170 }
1171 let args = &func[data.args];
1172 let lhs = *args.first()?;
1173 let rhs = *args.get(1)?;
1174 let possible = if magnitude(func, lhs) {
1175 against(func, rhs)?
1176 } else if magnitude(func, rhs) {
1177 turned(against(func, lhs)?)
1178 } else {
1179 return None;
1180 };
1181 let asked = float_buckets(pred);
1182 let buckets = asked & possible;
1183 if buckets == asked {
1184 return None;
1185 }
1186 if buckets == 0 {
1187 return Some(Composite::Always(false));
1188 }
1189 Some(Composite::Pred(Flip {
1190 opcode: Opcode::FCmp,
1191 flags: data.flags,
1192 extra: Extra::FloatPred(float_pred(buckets)?),
1193 lhs,
1194 rhs,
1195 }))
1196}
1197
1198pub(crate) fn fold_composite(func: &mut Func, inst: Inst, composite: Composite) {
1203 match composite {
1204 Composite::Always(answer) => become_constant(func, inst, answer.into()),
1205 Composite::Pred(flip) => {
1206 let args = func.push_values(&[flip.lhs, flip.rhs]);
1207 let data = &mut func[inst];
1208 data.opcode = flip.opcode;
1209 data.flags = flip.flags;
1210 data.args = args;
1211 data.extra = flip.extra;
1212 }
1213 }
1214}
1215
1216fn all_ones(func: &Func, value: Value) -> bool {
1218 let ty = func[value].ty;
1219 let Def::Result { inst, .. } = func[value].def else { return false };
1220 let data = &func[inst];
1221 let Extra::Imm(at) = data.extra else { return false };
1222 if data.opcode != Opcode::IConst {
1223 return false;
1224 }
1225 func[at].signed(ty) == -1
1228}
1229
1230#[cfg(test)]
1231mod tests {
1232 use rucc_base::Interner;
1233 use rucc_ir::{
1234 Block, Builder, Extra, Flags, Float, FloatPred, Func, IntPred, Module, Opcode, Signature,
1235 Type, Value,
1236 };
1237 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1238
1239 use super::{
1240 CANONICAL, COMPARE, EXPAND, PLANS, Shown, TABLES, canonical, compare, identities, strength,
1241 width,
1242 };
1243 use crate::rules::Piece;
1244 use crate::stats::Kind;
1245 use crate::{Fuel, Pass, simplify::Simplify};
1246
1247 fn blank() -> (Interner, Func, Block) {
1249 let mut names = Interner::new();
1250 let name = names.intern("f");
1251 let mut func = Func::new(name, Signature::new().with_returns(&[Type::int(1)]));
1252 let block = func.create_block();
1253 (names, func, block)
1254 }
1255
1256 fn one_block(ty: Type) -> (Interner, Func, Block) {
1259 let mut names = Interner::new();
1260 let name = names.intern("f");
1261 let signature = Signature::new().with_params(&[ty]).with_returns(&[ty]);
1262 let mut func = Func::new(name, signature);
1263 let block = func.create_block();
1264 (names, func, block)
1265 }
1266
1267 fn simplify(func: &mut Func) -> bool {
1269 Simplify
1270 .run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited())
1271 .changed()
1272 }
1273
1274 fn came_from(func: &Func, value: Value) -> (Opcode, Extra) {
1276 let rucc_ir::Def::Result { inst, .. } = func[value].def else { panic!("not a result") };
1277 (func[inst].opcode, func[inst].extra)
1278 }
1279
1280 fn returned(func: &Func, block: Block) -> Value {
1284 let inst = func.terminator(block).expect("the block has a terminator");
1285 func[func[inst].args][0]
1286 }
1287
1288 fn operands(func: &Func, value: Value) -> Vec<Value> {
1290 let rucc_ir::Def::Result { inst, .. } = func[value].def else { panic!("not a result") };
1291 func[func[inst].args].to_vec()
1292 }
1293
1294 fn number(func: &Func, value: Value) -> i128 {
1296 let rucc_ir::Def::Result { inst, .. } = func[value].def else { panic!("not a result") };
1297 let data = &func[inst];
1298 assert_eq!(data.opcode, Opcode::IConst, "not a constant");
1299 let Extra::Imm(at) = data.extra else { panic!("a constant with no number") };
1300 func[at].signed(func[value].ty)
1301 }
1302
1303 #[test]
1309 fn every_rule_leaves_a_shape_the_pass_knows_what_to_do_with() {
1310 for (table, _) in TABLES {
1311 for rule in table.rules {
1312 let known = matches!(
1313 rule.replacement,
1314 [Piece::App { head, arity: 1 }, Piece::Var { .. }]
1315 if head.starts_with("value.")
1316 ) || matches!(
1317 rule.replacement,
1318 [Piece::App { head, arity: 1 }, Piece::Int(_)]
1319 if head.starts_with("iconst.")
1320 ) || matches!(
1321 rule.replacement,
1322 [Piece::App { arity: 2, .. }, ..] if instruction(rule.replacement)
1323 ) || conversion(rule.replacement);
1324 assert!(known, "{} leaves a shape the pass would skip", rule.pattern);
1325 }
1326 }
1327 }
1328
1329 fn conversion(pieces: &'static [Piece]) -> bool {
1333 let [Piece::App { head, arity: 1 }, rest @ ..] = pieces else { return false };
1334 let converts =
1335 matches!(super::opcode_of(head), Some(Opcode::SExt | Opcode::ZExt | Opcode::Trunc));
1336 converts
1337 && matches!(
1338 rest,
1339 [Piece::App { head, arity: 1 }, Piece::Var { .. }] if head.starts_with("value.")
1340 )
1341 }
1342
1343 #[test]
1351 fn a_width_rule_writes_a_term_that_ends_where_the_one_it_matched_ended() {
1352 for rule in width::TABLE.rules {
1353 let [Piece::App { head, .. }, ..] = rule.replacement else {
1354 panic!("{} writes no head", rule.pattern)
1355 };
1356 let wrote = head.rsplit_once('.').expect("a replacement head names a width").1;
1357 let matched = rule
1358 .pattern
1359 .trim_start_matches('(')
1360 .split([' ', ')'])
1361 .next()
1362 .and_then(|head| head.rsplit_once('.'))
1363 .expect("a pattern head names a width")
1364 .1;
1365 assert_eq!(wrote, matched, "{} ends somewhere else", rule.pattern);
1366 }
1367 }
1368
1369 fn instruction(pieces: &'static [Piece]) -> bool {
1375 let [Piece::App { head, arity: 2 }, rest @ ..] = pieces else { return false };
1376 if super::opcode_of(head).is_none() {
1377 return false;
1378 }
1379 let operand = |pieces: &'static [Piece]| match pieces {
1380 [Piece::App { head, arity: 1 }, Piece::Var { .. }, rest @ ..]
1381 if head.starts_with("value.") =>
1382 {
1383 Some(rest)
1384 }
1385 [Piece::App { head, arity: 1 }, Piece::Int(_), rest @ ..]
1386 if head.starts_with("iconst.") =>
1387 {
1388 Some(rest)
1389 }
1390 [Piece::App { head, arity: 1 }, Piece::Var { .. }, rest @ ..]
1391 if head.starts_with("iconst.") =>
1392 {
1393 Some(rest)
1394 }
1395 [Piece::App { head, arity: 1 }, Piece::Computed { .. }, rest @ ..]
1396 if head.starts_with("iconst.") =>
1397 {
1398 Some(rest)
1399 }
1400 _ => None,
1401 };
1402 operand(rest).and_then(operand).is_some_and(<[Piece]>::is_empty)
1403 }
1404
1405 #[test]
1409 fn each_table_holds_every_rule_its_file_writes() {
1410 let tier_one = include_str!("../rules/simplify.rules");
1411 let tier_two = include_str!("../rules/strength.rules");
1412 let tier_three = include_str!("../rules/canonical.rules");
1413 let tier_four = include_str!("../rules/width.rules");
1414 let tier_five = include_str!("../rules/compare.rules");
1415 let count = |text: &str| text.matches("(rule (simplify ").count();
1416 assert_eq!(identities::TABLE.rules.len(), count(tier_one));
1417 assert_eq!(strength::TABLE.rules.len(), count(tier_two));
1418 assert_eq!(canonical::TABLE.rules.len(), count(tier_three));
1419 assert_eq!(width::TABLE.rules.len(), count(tier_four));
1420 assert_eq!(compare::TABLE.rules.len(), count(tier_five));
1421 assert!(
1422 identities::TABLE.rules.len() > 100,
1423 "tier one is about a hundred rules and there are fewer"
1424 );
1425 assert!(
1426 strength::TABLE.rules.len() > 20,
1427 "tier two is the multiplications and the divisions and there are fewer"
1428 );
1429 assert_eq!(
1430 canonical::TABLE.rules.len(),
1431 20,
1432 "tier three is five commutative operators at four widths"
1433 );
1434 assert_eq!(
1435 width::TABLE.rules.len(),
1436 66,
1437 "tier four is the truncation and extension algebra over four widths, and the three \
1438 shapes of it that exist over the one bit a comparison answers in"
1439 );
1440 assert_eq!(
1441 compare::TABLE.rules.len(),
1442 72,
1443 "tier five is four predicates against each of four constants at four widths, and a \
1444 widened boolean against zero under two predicates at the same four"
1445 );
1446 }
1447
1448 #[test]
1451 fn a_pattern_is_reached_by_one_of_the_plans() {
1452 assert_eq!(PLANS.len(), 3);
1453 }
1454
1455 #[test]
1465 fn a_width_rule_is_only_matched_with_its_operand_expanded() {
1466 let (_, plans) = TABLES[2];
1467 assert_eq!(plans.len(), 1);
1468 assert_eq!(plans[0], EXPAND[0]);
1469 assert_eq!(plans[0][0], Shown::Expand);
1470 for plan in PLANS {
1471 assert_ne!(plan, plans[0], "no shared plan expands an operand");
1472 }
1473 assert_ne!(CANONICAL[0], plans[0]);
1474 assert_eq!(COMPARE[1][0], Shown::Expand);
1475 assert_eq!(COMPARE[1][1], Shown::Const);
1476 }
1477
1478 #[test]
1484 fn a_canonicalisation_is_only_matched_with_the_right_operand_refused() {
1485 let (_, plans) = TABLES[4];
1486 assert_eq!(plans.len(), 1);
1487 assert_eq!(plans[0], CANONICAL[0]);
1488 assert_eq!(plans[0][1], Shown::Var);
1489 for plan in PLANS {
1490 assert_ne!(plan, plans[0], "a shared plan would let a canonicalisation cycle");
1491 }
1492 }
1493
1494 #[test]
1501 fn a_comparison_rule_is_only_matched_with_the_constant_on_the_right() {
1502 let (_, plans) = TABLES[3];
1503 assert_eq!(plans.len(), 2);
1504 assert_eq!(plans, COMPARE);
1505 for plan in plans {
1506 assert_eq!(plan[1], Shown::Const);
1507 }
1508 assert_eq!(plans[0][0], Shown::Reg);
1509 assert_eq!(plans[1][0], Shown::Expand);
1510 }
1511
1512 fn edges(width: u32) -> [(i128, bool); 4] {
1517 let signed = 1i128 << (width - 1);
1518 [(0, false), (-1, false), (-signed, true), (signed - 1, true)]
1519 }
1520
1521 #[test]
1527 fn a_comparison_against_the_edge_of_its_type_folds_to_a_bit() {
1528 for width in [8u32, 16, 32, 64] {
1529 let ty = Type::int(width);
1530 for (edge, signed) in edges(width) {
1531 let below = edge == 0 || edge == -(1i128 << (width - 1));
1534 let (false_pred, true_pred) = match (signed, below) {
1535 (false, true) => (IntPred::Ult, IntPred::Uge),
1536 (false, false) => (IntPred::Ugt, IntPred::Ule),
1537 (true, true) => (IntPred::Slt, IntPred::Sge),
1538 (true, false) => (IntPred::Sgt, IntPred::Sle),
1539 };
1540 for (pred, answer) in [(false_pred, 0), (true_pred, -1)] {
1544 let (_, mut func, block) = blank();
1545 let x = func.append_param(block, ty);
1546 let mut build = Builder::new(&mut func, block);
1547 let bound = build.iconst(ty, edge);
1548 let cmp = build.icmp(pred, x, bound);
1549 build.ret(&[cmp]);
1550 assert!(simplify(&mut func), "i{width} {pred:?} {edge} was left alone");
1551 let got = returned(&func, block);
1552 assert_eq!(
1553 came_from(&func, got).0,
1554 Opcode::IConst,
1555 "i{width} {pred:?} {edge} did not fold"
1556 );
1557 assert_eq!(number(&func, got), answer, "i{width} {pred:?} {edge}");
1558 assert_eq!(func[got].ty, Type::int(1), "i{width} {pred:?} {edge} is a bit");
1559 }
1560 }
1561 }
1562 }
1563
1564 #[test]
1570 fn a_comparison_true_for_one_value_becomes_a_test_for_that_value() {
1571 for width in [8u32, 16, 32, 64] {
1572 let ty = Type::int(width);
1573 for (edge, signed) in edges(width) {
1574 let below = edge == 0 || edge == -(1i128 << (width - 1));
1575 let (eq_pred, ne_pred) = match (signed, below) {
1578 (false, true) => (IntPred::Ule, IntPred::Ugt),
1579 (false, false) => (IntPred::Uge, IntPred::Ult),
1580 (true, true) => (IntPred::Sle, IntPred::Sgt),
1581 (true, false) => (IntPred::Sge, IntPred::Slt),
1582 };
1583 for (pred, left) in [(eq_pred, IntPred::Eq), (ne_pred, IntPred::Ne)] {
1584 let (_, mut func, block) = blank();
1585 let x = func.append_param(block, ty);
1586 let mut build = Builder::new(&mut func, block);
1587 let bound = build.iconst(ty, edge);
1588 let cmp = build.icmp(pred, x, bound);
1589 build.ret(&[cmp]);
1590 assert!(simplify(&mut func), "i{width} {pred:?} {edge} was left alone");
1591 let got = returned(&func, block);
1592 assert_eq!(
1593 came_from(&func, got),
1594 (Opcode::ICmp, Extra::IntPred(left)),
1595 "i{width} {pred:?} {edge} kept the predicate it matched"
1596 );
1597 let args = operands(&func, got);
1598 assert_eq!(args[0], x, "i{width} {pred:?} {edge} lost its value");
1599 assert_eq!(number(&func, args[1]), edge, "i{width} {pred:?} {edge}");
1600 assert_eq!(func[args[1]].ty, ty, "i{width} {pred:?} {edge} narrowed its bound");
1604 }
1605 }
1606 }
1607 }
1608
1609 #[test]
1616 fn a_widened_boolean_compared_against_zero_is_the_boolean() {
1617 for width in [8u32, 16, 32, 64] {
1618 let ty = Type::int(width);
1619 let (_, mut func, block) = blank();
1620 let x = func.append_param(block, Type::int(32));
1621 let mut build = Builder::new(&mut func, block);
1622 let seven = build.iconst(Type::int(32), 7);
1623 let flag = build.icmp(IntPred::Eq, x, seven);
1624 let wide = build.unary(Opcode::ZExt, flag, ty);
1625 let zero = build.iconst(ty, 0);
1626 let test = build.icmp(IntPred::Ne, wide, zero);
1627 build.ret(&[test]);
1628 assert!(simplify(&mut func), "i{width} was left alone");
1629 let got = returned(&func, block);
1630 assert_eq!(got, flag, "i{width} did not end up on the comparison");
1631 assert_eq!(func[got].ty, Type::int(1), "i{width} is a bit");
1632 }
1633 }
1634
1635 #[test]
1647 fn a_widened_boolean_that_is_zero_is_the_boolean_negated() {
1648 for width in [8u32, 16, 32, 64] {
1649 let ty = Type::int(width);
1650 let (_, mut func, block) = blank();
1651 let x = func.append_param(block, Type::int(32));
1652 let mut build = Builder::new(&mut func, block);
1653 let seven = build.iconst(Type::int(32), 7);
1654 let flag = build.icmp(IntPred::Eq, x, seven);
1655 let wide = build.unary(Opcode::ZExt, flag, ty);
1656 let zero = build.iconst(ty, 0);
1657 let test = build.icmp(IntPred::Eq, wide, zero);
1658 build.ret(&[test]);
1659 assert!(simplify(&mut func), "i{width} was left alone");
1660 let got = returned(&func, block);
1661 assert_eq!(came_from(&func, got).0, Opcode::Xor, "i{width} is not a negation");
1662 assert!(simplify(&mut func), "i{width} kept the exclusive or");
1663 assert_eq!(
1664 came_from(&func, got),
1665 (Opcode::ICmp, Extra::IntPred(IntPred::Ne)),
1666 "i{width} did not come out as the opposite comparison"
1667 );
1668 let args = operands(&func, got);
1669 assert_eq!(args[0], x, "i{width} lost its value");
1670 assert_eq!(number(&func, args[1]), 7, "i{width} lost its bound");
1671 }
1672 }
1673
1674 #[test]
1679 fn a_constant_on_the_left_of_a_commutative_operation_moves_to_the_right() {
1680 for opcode in [Opcode::Add, Opcode::Mul, Opcode::And, Opcode::Or, Opcode::Xor] {
1681 for width in [8, 16, 32, 64] {
1682 let ty = Type::int(width);
1683 let (_, mut func, block) = one_block(ty);
1684 let x = func.append_param(block, ty);
1685 let mut build = Builder::new(&mut func, block);
1686 let three = build.iconst(ty, 3);
1690 let value = build.binary(opcode, three, x, Flags::NONE);
1691 build.ret(&[value]);
1692 assert!(simplify(&mut func), "{opcode:?} at i{width} was left alone");
1693 let args = operands(&func, returned(&func, block));
1694 assert_eq!(came_from(&func, returned(&func, block)).0, opcode);
1695 assert_eq!(args[0], x, "{opcode:?} at i{width} kept the value on the right");
1696 assert_eq!(number(&func, args[1]), 3, "{opcode:?} at i{width} lost its constant");
1697 }
1698 }
1699 }
1700
1701 #[test]
1708 fn an_operation_on_two_constants_is_not_swapped_back_and_forth() {
1709 let i32 = Type::int(32);
1710 let (_, mut func, block) = one_block(i32);
1711 let mut build = Builder::new(&mut func, block);
1712 let three = build.iconst(i32, 3);
1713 let five = build.iconst(i32, 5);
1714 let sum = build.binary(Opcode::Add, three, five, Flags::NONE);
1715 build.ret(&[sum]);
1716 assert!(!simplify(&mut func), "the constants were rearranged rather than left to folding");
1717 let args = operands(&func, returned(&func, block));
1718 assert_eq!(number(&func, args[0]), 3);
1719 assert_eq!(number(&func, args[1]), 5);
1720 }
1721
1722 #[test]
1727 fn a_constant_already_on_the_right_is_left_alone() {
1728 let i32 = Type::int(32);
1729 let (_, mut func, block) = one_block(i32);
1730 let x = func.append_param(block, i32);
1731 let mut build = Builder::new(&mut func, block);
1732 let three = build.iconst(i32, 3);
1733 let sum = build.binary(Opcode::Add, x, three, Flags::NONE);
1734 build.ret(&[sum]);
1735 assert!(!simplify(&mut func));
1736 let args = operands(&func, returned(&func, block));
1737 assert_eq!(args[0], x);
1738 assert_eq!(number(&func, args[1]), 3);
1739 }
1740
1741 #[test]
1747 fn a_subtraction_keeps_its_operands_where_they_are() {
1748 let i32 = Type::int(32);
1749 let (_, mut func, block) = one_block(i32);
1750 let x = func.append_param(block, i32);
1751 let mut build = Builder::new(&mut func, block);
1752 let three = build.iconst(i32, 3);
1753 let difference = build.binary(Opcode::Sub, three, x, Flags::NONE);
1754 build.ret(&[difference]);
1755 assert!(!simplify(&mut func));
1756 let args = operands(&func, returned(&func, block));
1757 assert_eq!(number(&func, args[0]), 3);
1758 assert_eq!(args[1], x);
1759 }
1760
1761 fn narrow_to_wide(takes: Type, gives: Type) -> (Interner, Func, Block) {
1764 let mut names = Interner::new();
1765 let name = names.intern("f");
1766 let signature = Signature::new().with_params(&[takes]).with_returns(&[gives]);
1767 let mut func = Func::new(name, signature);
1768 let block = func.create_block();
1769 (names, func, block)
1770 }
1771
1772 fn chain(
1777 inner: Opcode,
1778 outer: Opcode,
1779 from: Type,
1780 through: Type,
1781 to: Type,
1782 ) -> (Func, Block, Value) {
1783 let (_, mut func, block) = narrow_to_wide(from, to);
1784 let x = func.append_param(block, from);
1785 let mut build = Builder::new(&mut func, block);
1786 let middle = build.unary(inner, x, through);
1787 let outside = build.unary(outer, middle, to);
1788 build.ret(&[outside]);
1789 (func, block, x)
1790 }
1791
1792 #[test]
1797 fn truncating_an_extension_back_to_its_own_width_gives_the_value_back() {
1798 for extend in [Opcode::SExt, Opcode::ZExt] {
1799 for (narrow, wide) in [(8, 16), (8, 32), (8, 64), (16, 32), (16, 64), (32, 64)] {
1800 let (from, through) = (Type::int(narrow), Type::int(wide));
1801 let (mut func, block, x) = chain(extend, Opcode::Trunc, from, through, from);
1802 assert!(simplify(&mut func), "{extend:?} i{narrow} to i{wide} was left alone");
1803 assert_eq!(
1804 returned(&func, block),
1805 x,
1806 "{extend:?} i{narrow} to i{wide} and back did not give the value back"
1807 );
1808 }
1809 }
1810 }
1811
1812 #[test]
1815 fn truncating_an_extension_above_its_source_is_a_shorter_extension() {
1816 let (mut func, block, x) =
1817 chain(Opcode::SExt, Opcode::Trunc, Type::int(8), Type::int(64), Type::int(16));
1818 assert!(simplify(&mut func));
1819 let result = returned(&func, block);
1820 assert_eq!(came_from(&func, result).0, Opcode::SExt);
1821 assert_eq!(operands(&func, result), vec![x]);
1822 assert_eq!(func[result].ty, Type::int(16));
1823 }
1824
1825 #[test]
1828 fn truncating_an_extension_below_its_source_is_a_truncation_of_the_source() {
1829 let (mut func, block, x) =
1830 chain(Opcode::ZExt, Opcode::Trunc, Type::int(16), Type::int(32), Type::int(8));
1831 assert!(simplify(&mut func));
1832 let result = returned(&func, block);
1833 assert_eq!(came_from(&func, result).0, Opcode::Trunc);
1834 assert_eq!(operands(&func, result), vec![x]);
1835 assert_eq!(func[result].ty, Type::int(8));
1836 }
1837
1838 #[test]
1841 fn an_extension_of_an_extension_is_one_extension() {
1842 for (inner, outer, want) in [
1843 (Opcode::ZExt, Opcode::ZExt, Opcode::ZExt),
1844 (Opcode::SExt, Opcode::SExt, Opcode::SExt),
1845 (Opcode::ZExt, Opcode::SExt, Opcode::ZExt),
1846 ] {
1847 let (mut func, block, x) =
1848 chain(inner, outer, Type::int(8), Type::int(16), Type::int(64));
1849 assert!(simplify(&mut func), "{outer:?} of {inner:?} was left alone");
1850 let result = returned(&func, block);
1851 assert_eq!(came_from(&func, result).0, want, "{outer:?} of {inner:?}");
1852 assert_eq!(operands(&func, result), vec![x]);
1853 assert_eq!(func[result].ty, Type::int(64));
1854 }
1855 }
1856
1857 #[test]
1866 fn a_truncation_of_a_truncation_is_one_truncation() {
1867 for (from, through, to) in [(64u32, 32u32, 16u32), (64, 32, 8), (64, 16, 8), (32, 16, 8)] {
1868 let (mut func, block, x) = chain(
1869 Opcode::Trunc,
1870 Opcode::Trunc,
1871 Type::int(from),
1872 Type::int(through),
1873 Type::int(to),
1874 );
1875 assert!(simplify(&mut func), "i{from} to i{through} to i{to} was left alone");
1876 let result = returned(&func, block);
1877 assert_eq!(came_from(&func, result).0, Opcode::Trunc, "i{from} to i{through} to i{to}");
1878 assert_eq!(operands(&func, result), vec![x]);
1879 assert_eq!(func[result].ty, Type::int(to));
1880 }
1881 }
1882
1883 #[test]
1886 fn zero_extending_a_sign_extension_is_left_alone() {
1887 let (mut func, _, _) =
1888 chain(Opcode::SExt, Opcode::ZExt, Type::int(8), Type::int(16), Type::int(64));
1889 assert!(!simplify(&mut func), "a zero extension of a sign extension was rewritten");
1890 }
1891
1892 #[test]
1901 fn zero_extending_a_truncation_is_left_alone() {
1902 let (mut func, _, _) =
1903 chain(Opcode::Trunc, Opcode::ZExt, Type::int(64), Type::int(32), Type::int(64));
1904 assert!(!simplify(&mut func), "a zero extension of a truncation became a mask");
1905 }
1906
1907 #[test]
1914 fn a_width_rule_needs_an_operand_an_instruction_computed() {
1915 let (_, mut func, block) = narrow_to_wide(Type::int(64), Type::int(32));
1916 let x = func.append_param(block, Type::int(64));
1917 let mut build = Builder::new(&mut func, block);
1918 let narrowed = build.unary(Opcode::Trunc, x, Type::int(32));
1919 build.ret(&[narrowed]);
1920 assert!(!simplify(&mut func), "a truncation of a parameter was rewritten");
1921 }
1922
1923 #[test]
1924 fn adding_nothing_points_every_reader_at_the_operand() {
1925 let i32 = Type::int(32);
1926 let (_, mut func, block) = one_block(i32);
1927 let x = func.append_param(block, i32);
1928 let mut build = Builder::new(&mut func, block);
1929 let zero = build.iconst(i32, 0);
1930 let sum = build.binary(Opcode::Add, x, zero, Flags::NONE);
1931 build.ret(&[sum]);
1932 assert!(simplify(&mut func));
1933 assert_eq!(returned(&func, block), x);
1935 assert_eq!(came_from(&func, sum).0, Opcode::Add);
1936 }
1937
1938 #[test]
1941 fn the_constant_is_found_on_either_side_of_an_identity() {
1942 for swapped in [false, true] {
1943 let i32 = Type::int(32);
1944 let (_, mut func, block) = one_block(i32);
1945 let x = func.append_param(block, i32);
1946 let mut build = Builder::new(&mut func, block);
1947 let zero = build.iconst(i32, 0);
1948 let (lhs, rhs) = if swapped { (zero, x) } else { (x, zero) };
1949 let sum = build.binary(Opcode::Add, lhs, rhs, Flags::NONE);
1950 build.ret(&[sum]);
1951 assert!(simplify(&mut func), "swapped {swapped}");
1952 assert_eq!(returned(&func, block), x, "swapped {swapped}");
1953 }
1954 }
1955
1956 #[test]
1957 fn multiplying_by_nothing_becomes_the_constant_where_it_stands() {
1958 let i32 = Type::int(32);
1959 let (_, mut func, block) = one_block(i32);
1960 let x = func.append_param(block, i32);
1961 let mut build = Builder::new(&mut func, block);
1962 let zero = build.iconst(i32, 0);
1963 let product = build.binary(Opcode::Mul, x, zero, Flags::NONE);
1964 build.ret(&[product]);
1965 assert!(simplify(&mut func));
1966 assert_eq!(returned(&func, block), product);
1968 assert_eq!(came_from(&func, product).0, Opcode::IConst);
1969 assert_eq!(number(&func, product), 0);
1970 }
1971
1972 #[test]
1975 fn a_value_against_itself() {
1976 for bits in [8, 16, 32, 64] {
1977 let ty = Type::int(bits);
1978 let (_, mut func, block) = one_block(ty);
1979 let x = func.append_param(block, ty);
1980 let mut build = Builder::new(&mut func, block);
1981 let both = build.binary(Opcode::And, x, x, Flags::NONE);
1982 build.ret(&[both]);
1983 assert!(simplify(&mut func), "{bits} bits");
1984 assert_eq!(returned(&func, block), x, "{bits} bits");
1985
1986 let (_, mut func, block) = one_block(ty);
1987 let x = func.append_param(block, ty);
1988 let mut build = Builder::new(&mut func, block);
1989 let nothing = build.binary(Opcode::Sub, x, x, Flags::NONE);
1990 build.ret(&[nothing]);
1991 assert!(simplify(&mut func), "{bits} bits");
1992 assert_eq!(number(&func, nothing), 0, "{bits} bits");
1993 }
1994 }
1995
1996 #[test]
2000 fn every_comparison_of_a_value_with_itself_is_decided() {
2001 for bits in [8, 16, 32, 64] {
2002 for pred in IntPred::all() {
2003 let mut names = Interner::new();
2004 let name = names.intern("f");
2005 let int = Type::int(bits);
2006 let signature = Signature::new().with_params(&[int]).with_returns(&[Type::int(1)]);
2007 let mut func = Func::new(name, signature);
2008 let block = func.create_block();
2009 let x = func.append_param(block, int);
2010 let mut build = Builder::new(&mut func, block);
2011 let answer = build.icmp(pred, x, x);
2012 build.ret(&[answer]);
2013 assert!(simplify(&mut func), "{pred:?} at {bits} bits");
2014 let said = number(&func, answer);
2015 if matches!(
2016 pred,
2017 IntPred::Ne | IntPred::Slt | IntPred::Sgt | IntPred::Ult | IntPred::Ugt
2018 ) {
2019 assert_eq!(said, 0, "{pred:?} at {bits} bits");
2020 } else {
2021 assert_ne!(said, 0, "{pred:?} at {bits} bits");
2022 }
2023 }
2024 }
2025 }
2026
2027 #[test]
2031 fn dividing_by_one_and_the_remainder_that_goes_with_it() {
2032 let i32 = Type::int(32);
2033 let (_, mut func, block) = one_block(i32);
2034 let x = func.append_param(block, i32);
2035 let mut build = Builder::new(&mut func, block);
2036 let one = build.iconst(i32, 1);
2037 let quotient = build.binary(Opcode::SDiv, x, one, Flags::NONE);
2038 let rest = build.binary(Opcode::SRem, x, one, Flags::NONE);
2039 let sum = build.binary(Opcode::Add, quotient, rest, Flags::NONE);
2040 build.ret(&[sum]);
2041 assert!(simplify(&mut func));
2042 assert_eq!(number(&func, rest), 0);
2043 let rucc_ir::Def::Result { inst, .. } = func[sum].def else { panic!("not a result") };
2045 assert_eq!(func[func[inst].args][0], x);
2046 }
2047
2048 #[test]
2051 fn all_ones_at_one_bit_is_the_one_the_front_end_writes() {
2052 for written in [-1, 1] {
2053 let bit = Type::int(1);
2054 let (_, mut func, block) = one_block(bit);
2055 let x = func.append_param(block, bit);
2056 let mut build = Builder::new(&mut func, block);
2057 let ones = build.iconst(bit, written);
2058 let kept = build.binary(Opcode::And, x, ones, Flags::NONE);
2059 build.ret(&[kept]);
2060 assert!(simplify(&mut func), "written as {written}");
2061 assert_eq!(returned(&func, block), x, "written as {written}");
2062 }
2063 }
2064
2065 #[test]
2069 fn one_identity_feeding_another_is_followed_to_the_end() {
2070 let i32 = Type::int(32);
2071 let (_, mut func, block) = one_block(i32);
2072 let x = func.append_param(block, i32);
2073 let mut build = Builder::new(&mut func, block);
2074 let zero = build.iconst(i32, 0);
2075 let one = build.iconst(i32, 1);
2076 let sum = build.binary(Opcode::Add, x, zero, Flags::NONE);
2077 let product = build.binary(Opcode::Mul, sum, one, Flags::NONE);
2078 let shifted = build.binary(Opcode::Shl, product, zero, Flags::NONE);
2079 build.ret(&[shifted]);
2080 assert!(simplify(&mut func));
2081 assert_eq!(returned(&func, block), x);
2082 }
2083
2084 #[test]
2088 fn shifting_nothing_and_shifting_all_ones_with_the_sign() {
2089 for bits in [8, 16, 32, 64] {
2090 let ty = Type::int(bits);
2091 let cases = [
2092 (Opcode::Shl, 0_i128, 0_i128),
2093 (Opcode::LShr, 0, 0),
2094 (Opcode::AShr, 0, 0),
2095 (Opcode::AShr, -1, -1),
2096 ];
2097 for (opcode, from, expected) in cases {
2098 let (_, mut func, block) = one_block(ty);
2099 let count = func.append_param(block, ty);
2100 let mut build = Builder::new(&mut func, block);
2101 let value = build.iconst(ty, from);
2102 let shifted = build.binary(opcode, value, count, Flags::NONE);
2103 build.ret(&[shifted]);
2104 assert!(simplify(&mut func), "{opcode:?} of {from} at {bits} bits");
2105 let said = number(&func, shifted);
2106 assert_eq!(said, expected, "{opcode:?} of {from} at {bits} bits");
2107 }
2108 }
2109 }
2110
2111 #[test]
2114 fn all_ones_shifted_right_with_zeroes_coming_in_is_left_alone() {
2115 let i32 = Type::int(32);
2116 let (_, mut func, block) = one_block(i32);
2117 let count = func.append_param(block, i32);
2118 let mut build = Builder::new(&mut func, block);
2119 let ones = build.iconst(i32, -1);
2120 let shifted = build.binary(Opcode::LShr, ones, count, Flags::NONE);
2121 build.ret(&[shifted]);
2122 assert!(!simplify(&mut func));
2123 assert_eq!(came_from(&func, shifted).0, Opcode::LShr);
2124 }
2125
2126 #[test]
2127 fn an_instruction_no_rule_is_about_is_left_alone() {
2128 let i32 = Type::int(32);
2132 let (_, mut func, block) = one_block(i32);
2133 let x = func.append_param(block, i32);
2134 let mut build = Builder::new(&mut func, block);
2135 let three = build.iconst(i32, 3);
2136 let tripled = build.binary(Opcode::Mul, x, three, Flags::NONE);
2137 build.ret(&[tripled]);
2138 assert!(!simplify(&mut func), "no rule is about multiplying by three");
2139 assert_eq!(returned(&func, block), tripled);
2140 assert_eq!(came_from(&func, tripled).0, Opcode::Mul);
2141 }
2142
2143 #[test]
2144 fn multiplying_by_two_becomes_an_addition_of_the_value_with_itself() {
2145 let i32 = Type::int(32);
2146 let (_, mut func, block) = one_block(i32);
2147 let x = func.append_param(block, i32);
2148 let mut build = Builder::new(&mut func, block);
2149 let two = build.iconst(i32, 2);
2150 let doubled = build.binary(Opcode::Mul, x, two, Flags::NONE);
2151 build.ret(&[doubled]);
2152 assert!(simplify(&mut func));
2153 assert_eq!(returned(&func, block), doubled);
2155 assert_eq!(came_from(&func, doubled).0, Opcode::Add);
2156 assert_eq!(operands(&func, doubled), [x, x]);
2157 }
2161
2162 #[test]
2163 fn multiplying_by_a_power_of_two_becomes_a_shift_by_the_count_of_its_zeros() {
2164 let i32 = Type::int(32);
2165 let (_, mut func, block) = one_block(i32);
2166 let x = func.append_param(block, i32);
2167 let mut build = Builder::new(&mut func, block);
2168 let eight = build.iconst(i32, 8);
2169 let scaled = build.binary(Opcode::Mul, x, eight, Flags::NONE);
2170 build.ret(&[scaled]);
2171 assert!(simplify(&mut func));
2172 assert_eq!(returned(&func, block), scaled);
2173 assert_eq!(came_from(&func, scaled).0, Opcode::Shl);
2174 let args = operands(&func, scaled);
2175 assert_eq!(args[0], x);
2176 assert_eq!(number(&func, args[1]), 3);
2177 }
2178
2179 #[test]
2180 fn the_power_of_two_with_the_sign_bit_set_is_one_of_them() {
2181 let i32 = Type::int(32);
2186 let (_, mut func, block) = one_block(i32);
2187 let x = func.append_param(block, i32);
2188 let mut build = Builder::new(&mut func, block);
2189 let top = build.iconst(i32, 0x8000_0000);
2190 let scaled = build.binary(Opcode::Mul, x, top, Flags::NONE);
2191 build.ret(&[scaled]);
2192 assert!(simplify(&mut func));
2193 assert_eq!(came_from(&func, scaled).0, Opcode::Shl);
2194 assert_eq!(number(&func, operands(&func, scaled)[1]), 31);
2195 }
2196
2197 #[test]
2198 fn dividing_an_unsigned_value_by_a_power_of_two_becomes_a_shift() {
2199 let i32 = Type::int(32);
2200 let (_, mut func, block) = one_block(i32);
2201 let x = func.append_param(block, i32);
2202 let mut build = Builder::new(&mut func, block);
2203 let sixteen = build.iconst(i32, 16);
2204 let quotient = build.binary(Opcode::UDiv, x, sixteen, Flags::NONE);
2205 build.ret(&[quotient]);
2206 assert!(simplify(&mut func));
2207 assert_eq!(came_from(&func, quotient).0, Opcode::LShr);
2208 let args = operands(&func, quotient);
2209 assert_eq!(args[0], x);
2210 assert_eq!(number(&func, args[1]), 4);
2211 }
2212
2213 #[test]
2214 fn dividing_a_signed_value_by_a_power_of_two_is_left_alone() {
2215 let i32 = Type::int(32);
2220 let (_, mut func, block) = one_block(i32);
2221 let x = func.append_param(block, i32);
2222 let mut build = Builder::new(&mut func, block);
2223 let sixteen = build.iconst(i32, 16);
2224 let quotient = build.binary(Opcode::SDiv, x, sixteen, Flags::NONE);
2225 build.ret(&[quotient]);
2226 assert!(!simplify(&mut func), "no rule turns a signed division into a shift");
2227 assert_eq!(came_from(&func, quotient).0, Opcode::SDiv);
2228 }
2229
2230 #[test]
2231 fn the_unsigned_remainder_of_a_power_of_two_becomes_a_mask() {
2232 let i32 = Type::int(32);
2233 let (_, mut func, block) = one_block(i32);
2234 let x = func.append_param(block, i32);
2235 let mut build = Builder::new(&mut func, block);
2236 let thirty_two = build.iconst(i32, 32);
2237 let rest = build.binary(Opcode::URem, x, thirty_two, Flags::NONE);
2238 build.ret(&[rest]);
2239 assert!(simplify(&mut func));
2240 assert_eq!(came_from(&func, rest).0, Opcode::And);
2241 let args = operands(&func, rest);
2242 assert_eq!(args[0], x);
2243 assert_eq!(number(&func, args[1]), 31);
2244 }
2245
2246 #[test]
2247 fn a_division_by_a_constant_that_is_not_a_power_of_two_is_left_alone() {
2248 let i32 = Type::int(32);
2249 let (_, mut func, block) = one_block(i32);
2250 let x = func.append_param(block, i32);
2251 let mut build = Builder::new(&mut func, block);
2252 let ten = build.iconst(i32, 10);
2253 let quotient = build.binary(Opcode::UDiv, x, ten, Flags::NONE);
2254 build.ret(&[quotient]);
2255 assert!(!simplify(&mut func), "ten is no power of two");
2256 assert_eq!(came_from(&func, quotient).0, Opcode::UDiv);
2257 }
2258
2259 #[test]
2260 fn multiplying_by_minus_one_becomes_a_subtraction_from_a_zero_the_rewrite_defines() {
2261 let i32 = Type::int(32);
2264 let (_, mut func, block) = one_block(i32);
2265 let x = func.append_param(block, i32);
2266 let mut build = Builder::new(&mut func, block);
2267 let minus = build.iconst(i32, -1);
2268 let negated = build.binary(Opcode::Mul, x, minus, Flags::NONE);
2269 build.ret(&[negated]);
2270 assert!(simplify(&mut func));
2271 assert_eq!(returned(&func, block), negated);
2272 assert_eq!(came_from(&func, negated).0, Opcode::Sub);
2273 let args = operands(&func, negated);
2274 assert_eq!(number(&func, args[0]), 0);
2275 assert_eq!(args[1], x);
2276 }
2277
2278 #[test]
2279 fn the_flags_of_the_instruction_a_strength_reduction_replaces_do_not_come_with_it() {
2280 let i32 = Type::int(32);
2284 let (_, mut func, block) = one_block(i32);
2285 let x = func.append_param(block, i32);
2286 let mut build = Builder::new(&mut func, block);
2287 let two = build.iconst(i32, 2);
2288 let doubled = build.binary(Opcode::Mul, x, two, Flags::NSW);
2289 build.ret(&[doubled]);
2290 assert!(simplify(&mut func));
2291 let rucc_ir::Def::Result { inst, .. } = func[doubled].def else { panic!("not a result") };
2292 assert_eq!(func[inst].flags, Flags::NONE);
2293 }
2294
2295 #[test]
2296 fn a_strength_reduction_leaves_the_verifier_nothing_to_complain_about() {
2297 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2301 let i32 = Type::int(32);
2302 let (mut names, mut func, block) = one_block(i32);
2303 let mut module = Module::new(names.intern("test.c"), &target);
2304 let x = func.append_param(block, i32);
2305 let mut build = Builder::new(&mut func, block);
2306 let minus = build.iconst(i32, -1);
2307 let negated = build.binary(Opcode::Mul, x, minus, Flags::NONE);
2308 let two = build.iconst(i32, 2);
2309 let doubled = build.binary(Opcode::Mul, negated, two, Flags::NONE);
2310 build.ret(&[doubled]);
2311 assert!(simplify(&mut func));
2312 module.add_func(func);
2313 rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2314 }
2315
2316 #[test]
2321 fn the_pass_leaves_the_verifier_nothing_to_complain_about() {
2322 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2323 let i32 = Type::int(32);
2324 let (mut names, mut func, block) = one_block(i32);
2325 let mut module = Module::new(names.intern("test.c"), &target);
2326 let x = func.append_param(block, i32);
2327 let mut build = Builder::new(&mut func, block);
2328 let zero = build.iconst(i32, 0);
2329 let one = build.iconst(i32, 1);
2330 let sum = build.binary(Opcode::Add, x, zero, Flags::NONE);
2331 let product = build.binary(Opcode::Mul, sum, one, Flags::NONE);
2332 let gone = build.binary(Opcode::Sub, product, product, Flags::NONE);
2333 let total = build.binary(Opcode::Add, product, gone, Flags::NONE);
2334 build.ret(&[total]);
2335 assert!(simplify(&mut func));
2336 module.add_func(func);
2337 rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2338 }
2339
2340 #[test]
2341 fn fuel_stops_an_identity_and_not_the_walk() {
2342 let i32 = Type::int(32);
2343 let (_, mut func, block) = one_block(i32);
2344 let x = func.append_param(block, i32);
2345 let mut build = Builder::new(&mut func, block);
2346 let zero = build.iconst(i32, 0);
2347 let first = build.binary(Opcode::Add, x, zero, Flags::NONE);
2348 let second = build.binary(Opcode::Sub, x, zero, Flags::NONE);
2349 let sum = build.binary(Opcode::Add, first, second, Flags::NONE);
2350 build.ret(&[sum]);
2351 let stats =
2352 Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2353 assert!(stats.changed());
2354 assert_eq!(stats.total(Kind::Optimized), 1);
2355 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_RULE), 1);
2356 let rucc_ir::Def::Result { inst, .. } = func[sum].def else { panic!("not a result") };
2358 assert_eq!(func[func[inst].args], [x, second]);
2359 }
2360
2361 #[test]
2362 fn a_negated_float_comparison_becomes_the_opposite_predicate() {
2363 for pred in FloatPred::all() {
2366 let (_, mut func, block) = blank();
2367 let mut build = Builder::new(&mut func, block);
2368 let x = build.iconst(Type::int(64), 0);
2369 let x = build.unary(Opcode::Bitcast, x, Type::float(Float::F64));
2370 let cmp = build.fcmp(pred, x, x, Flags::NONE);
2371 let ones = build.iconst(Type::int(1), -1);
2372 let not = build.binary(Opcode::Xor, cmp, ones, Flags::NONE);
2373 build.ret(&[not]);
2374 assert!(simplify(&mut func), "{pred:?}");
2375 assert_eq!(
2376 came_from(&func, not),
2377 (Opcode::FCmp, Extra::FloatPred(pred.inverse())),
2378 "{pred:?}"
2379 );
2380 }
2381 }
2382
2383 #[test]
2384 fn a_negated_integer_comparison_becomes_the_opposite_predicate() {
2385 for pred in IntPred::all() {
2386 let (_, mut func, block) = blank();
2387 let mut build = Builder::new(&mut func, block);
2388 let x = build.iconst(Type::int(32), 3);
2389 let y = build.iconst(Type::int(32), 4);
2390 let cmp = build.icmp(pred, x, y);
2391 let ones = build.iconst(Type::int(1), -1);
2392 let not = build.binary(Opcode::Xor, cmp, ones, Flags::NONE);
2393 build.ret(&[not]);
2394 assert!(simplify(&mut func), "{pred:?}");
2395 assert_eq!(
2396 came_from(&func, not),
2397 (Opcode::ICmp, Extra::IntPred(pred.inverse())),
2398 "{pred:?}"
2399 );
2400 }
2401 }
2402
2403 #[test]
2404 fn the_constant_is_found_on_either_side() {
2405 for swapped in [false, true] {
2406 let (_, mut func, block) = blank();
2407 let mut build = Builder::new(&mut func, block);
2408 let x = build.iconst(Type::int(32), 3);
2409 let y = build.iconst(Type::int(32), 4);
2410 let cmp = build.icmp(IntPred::Slt, x, y);
2411 let ones = build.iconst(Type::int(1), -1);
2412 let (lhs, rhs) = if swapped { (ones, cmp) } else { (cmp, ones) };
2413 let not = build.binary(Opcode::Xor, lhs, rhs, Flags::NONE);
2414 build.ret(&[not]);
2415 assert!(simplify(&mut func), "swapped {swapped}");
2416 assert_eq!(came_from(&func, not).1, Extra::IntPred(IntPred::Sge));
2417 }
2418 }
2419
2420 #[test]
2421 fn an_exclusive_or_of_two_comparisons_is_left_alone() {
2422 let (_, mut func, block) = blank();
2423 let mut build = Builder::new(&mut func, block);
2424 let x = build.iconst(Type::int(32), 3);
2425 let y = build.iconst(Type::int(32), 4);
2426 let a = build.icmp(IntPred::Slt, x, y);
2427 let b = build.icmp(IntPred::Sgt, x, y);
2428 let differ = build.binary(Opcode::Xor, a, b, Flags::NONE);
2429 build.ret(&[differ]);
2430 assert!(!simplify(&mut func));
2431 assert_eq!(came_from(&func, differ).0, Opcode::Xor);
2432 }
2433
2434 #[test]
2435 fn an_exclusive_or_of_something_that_is_not_a_comparison_is_left_alone() {
2436 let (_, mut func, block) = blank();
2437 let mut build = Builder::new(&mut func, block);
2438 let x = build.iconst(Type::int(32), 3);
2439 let narrow = build.unary(Opcode::Trunc, x, Type::int(1));
2440 let ones = build.iconst(Type::int(1), -1);
2441 let not = build.binary(Opcode::Xor, narrow, ones, Flags::NONE);
2442 build.ret(&[not]);
2443 assert!(!simplify(&mut func));
2444 assert_eq!(came_from(&func, not).0, Opcode::Xor);
2445 }
2446
2447 #[test]
2448 fn a_wider_exclusive_or_with_one_is_not_a_negation_and_is_left_alone() {
2449 let (_, mut func, block) = blank();
2450 let mut build = Builder::new(&mut func, block);
2451 let x = build.iconst(Type::int(32), 3);
2452 let y = build.iconst(Type::int(32), 4);
2453 let cmp = build.icmp(IntPred::Slt, x, y);
2454 let wide = build.unary(Opcode::ZExt, cmp, Type::int(32));
2455 let one = build.iconst(Type::int(32), 1);
2456 let flipped = build.binary(Opcode::Xor, wide, one, Flags::NONE);
2457 let narrow = build.unary(Opcode::Trunc, flipped, Type::int(1));
2458 build.ret(&[narrow]);
2459 assert!(!simplify(&mut func), "an i32 xor 1 flips one bit of thirty two");
2460 assert_eq!(came_from(&func, flipped).0, Opcode::Xor);
2461 }
2462
2463 #[test]
2464 fn the_comparisons_flags_travel_with_the_predicate() {
2465 let (_, mut func, block) = blank();
2466 let mut build = Builder::new(&mut func, block);
2467 let x = build.iconst(Type::int(64), 0);
2468 let x = build.unary(Opcode::Bitcast, x, Type::float(Float::F64));
2469 let cmp = build.fcmp(FloatPred::Olt, x, x, Flags::FAST);
2470 let ones = build.iconst(Type::int(1), -1);
2471 let not = build.binary(Opcode::Xor, cmp, ones, Flags::NONE);
2472 build.ret(&[not]);
2473 assert!(simplify(&mut func));
2474 let rucc_ir::Def::Result { inst, .. } = func[not].def else { panic!("not a result") };
2475 assert_eq!(func[inst].flags, Flags::FAST);
2478 }
2479
2480 #[test]
2481 fn fuel_stops_the_transformation_and_not_the_walk() {
2482 let (_, mut func, block) = blank();
2483 let mut build = Builder::new(&mut func, block);
2484 let x = build.iconst(Type::int(32), 3);
2485 let y = build.iconst(Type::int(32), 4);
2486 let a = build.icmp(IntPred::Slt, x, y);
2487 let b = build.icmp(IntPred::Sgt, x, y);
2488 let ones = build.iconst(Type::int(1), -1);
2489 let first = build.binary(Opcode::Xor, a, ones, Flags::NONE);
2490 let second = build.binary(Opcode::Xor, b, ones, Flags::NONE);
2491 let both = build.binary(Opcode::And, first, second, Flags::NONE);
2492 build.ret(&[both]);
2493 let stats =
2494 Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2495 assert!(stats.changed());
2496 assert_eq!(stats.count(Kind::Optimized, super::FLIPPED), 1);
2497 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2498 assert_eq!(came_from(&func, first).0, Opcode::ICmp);
2499 assert_eq!(came_from(&func, second).0, Opcode::Xor);
2500 }
2501
2502 fn a_pair() -> (Func, Block, Value, Value) {
2504 let mut names = Interner::new();
2505 let name = names.intern("f");
2506 let int = Type::int(32);
2507 let signature = Signature::new().with_params(&[int, int]).with_returns(&[Type::int(1)]);
2508 let mut func = Func::new(name, signature);
2509 let block = func.create_block();
2510 let x = func.append_param(block, int);
2511 let y = func.append_param(block, int);
2512 (func, block, x, y)
2513 }
2514
2515 fn a_float_pair() -> (Func, Block, Value, Value) {
2517 let mut names = Interner::new();
2518 let name = names.intern("f");
2519 let float = Type::float(Float::F64);
2520 let signature = Signature::new().with_params(&[float, float]).with_returns(&[Type::int(1)]);
2521 let mut func = Func::new(name, signature);
2522 let block = func.create_block();
2523 let x = func.append_param(block, float);
2524 let y = func.append_param(block, float);
2525 (func, block, x, y)
2526 }
2527
2528 #[test]
2536 fn the_opposite_of_a_float_predicate_is_the_buckets_it_leaves_out() {
2537 for pred in FloatPred::all() {
2538 assert_eq!(
2539 super::float_buckets(pred.inverse()),
2540 super::bucket::ALL_FLOAT ^ super::float_buckets(pred),
2541 "{pred:?}"
2542 );
2543 }
2544 }
2545
2546 #[test]
2551 fn swapping_a_float_predicates_operands_exchanges_below_and_above() {
2552 for pred in FloatPred::all() {
2553 let want = super::turned(super::float_buckets(pred));
2554 assert_eq!(super::float_buckets(pred.swapped()), want, "{pred:?}");
2555 }
2556 }
2557
2558 #[test]
2561 fn every_set_of_float_buckets_is_a_predicate() {
2562 for pred in FloatPred::all() {
2563 assert_eq!(super::float_pred(super::float_buckets(pred)), Some(pred), "{pred:?}");
2564 }
2565 for buckets in 0..=super::bucket::ALL_FLOAT {
2566 assert!(super::float_pred(buckets).is_some(), "{buckets} spells nothing");
2567 }
2568 }
2569
2570 #[test]
2572 fn an_integer_predicate_agrees_with_its_own_opposite_and_its_own_swap() {
2573 use super::bucket::ALL_INT;
2574 for pred in IntPred::all() {
2575 let (before, reading) = super::int_buckets(pred);
2576 let (opposite, other) = super::int_buckets(pred.inverse());
2577 assert_eq!(opposite, ALL_INT ^ before, "the opposite of {pred:?}");
2578 assert_eq!(other, reading, "the opposite of {pred:?} reads the operands differently");
2579 let (swapped, other) = super::int_buckets(pred.swapped());
2580 assert_eq!(swapped, super::turned(before), "the swap of {pred:?}");
2581 assert_eq!(other, reading, "the swap of {pred:?} reads the operands differently");
2582 }
2583 }
2584
2585 #[test]
2588 fn every_integer_predicate_is_read_back_as_itself() {
2589 for pred in IntPred::all() {
2590 let (buckets, reading) = super::int_buckets(pred);
2591 assert_eq!(super::int_pred(buckets, reading), Some(pred), "{pred:?}");
2592 }
2593 }
2594
2595 #[test]
2596 fn two_integer_comparisons_that_agree_about_nothing_are_false() {
2597 let (mut func, block, x, y) = a_pair();
2598 let mut build = Builder::new(&mut func, block);
2599 let same = build.icmp(IntPred::Eq, x, y);
2600 let differ = build.icmp(IntPred::Ne, x, y);
2601 let both = build.binary(Opcode::And, same, differ, Flags::NONE);
2602 build.ret(&[both]);
2603 assert!(simplify(&mut func));
2604 assert_eq!(number(&func, both), 0);
2605 }
2606
2607 #[test]
2608 fn two_integer_comparisons_that_cover_everything_are_true() {
2609 let (mut func, block, x, y) = a_pair();
2610 let mut build = Builder::new(&mut func, block);
2611 let above = build.icmp(IntPred::Sge, x, y);
2612 let below = build.icmp(IntPred::Slt, x, y);
2613 let either = build.binary(Opcode::Or, above, below, Flags::NONE);
2614 build.ret(&[either]);
2615 assert!(simplify(&mut func));
2616 assert_ne!(number(&func, either), 0);
2617 }
2618
2619 #[test]
2622 fn two_integer_comparisons_that_overlap_become_one() {
2623 let (mut func, block, x, y) = a_pair();
2624 let mut build = Builder::new(&mut func, block);
2625 let below = build.icmp(IntPred::Slt, x, y);
2626 let same = build.icmp(IntPred::Eq, x, y);
2627 let either = build.binary(Opcode::Or, below, same, Flags::NONE);
2628 build.ret(&[either]);
2629 assert!(simplify(&mut func));
2630 assert_eq!(came_from(&func, either), (Opcode::ICmp, Extra::IntPred(IntPred::Sle)));
2631 assert_eq!(operands(&func, either), [x, y]);
2632 }
2633
2634 #[test]
2637 fn the_second_comparison_is_read_in_the_first_ones_operand_order() {
2638 let (mut func, block, x, y) = a_pair();
2639 let mut build = Builder::new(&mut func, block);
2640 let below = build.icmp(IntPred::Slt, x, y);
2641 let above = build.icmp(IntPred::Slt, y, x);
2642 let both = build.binary(Opcode::And, below, above, Flags::NONE);
2643 build.ret(&[both]);
2644 assert!(simplify(&mut func));
2645 assert_eq!(number(&func, both), 0);
2646 }
2647
2648 #[test]
2651 fn an_equality_takes_the_ordering_of_the_comparison_beside_it() {
2652 for (ordered, want) in [(IntPred::Ult, IntPred::Ule), (IntPred::Slt, IntPred::Sle)] {
2653 let (mut func, block, x, y) = a_pair();
2654 let mut build = Builder::new(&mut func, block);
2655 let below = build.icmp(ordered, x, y);
2656 let same = build.icmp(IntPred::Eq, x, y);
2657 let either = build.binary(Opcode::Or, below, same, Flags::NONE);
2658 build.ret(&[either]);
2659 assert!(simplify(&mut func), "{ordered:?}");
2660 assert_eq!(came_from(&func, either).1, Extra::IntPred(want), "{ordered:?}");
2661 }
2662 }
2663
2664 #[test]
2667 fn a_signed_comparison_and_an_unsigned_one_are_left_alone() {
2668 let (mut func, block, x, y) = a_pair();
2669 let mut build = Builder::new(&mut func, block);
2670 let signed = build.icmp(IntPred::Slt, x, y);
2671 let unsigned = build.icmp(IntPred::Ugt, x, y);
2672 let both = build.binary(Opcode::And, signed, unsigned, Flags::NONE);
2673 build.ret(&[both]);
2674 assert!(!simplify(&mut func));
2675 assert_eq!(came_from(&func, both).0, Opcode::And);
2676 }
2677
2678 #[test]
2679 fn two_comparisons_about_different_operands_are_left_alone() {
2680 let (mut func, block, x, y) = a_pair();
2681 let mut build = Builder::new(&mut func, block);
2682 let other = build.iconst(Type::int(32), 7);
2683 let first = build.icmp(IntPred::Slt, x, y);
2684 let second = build.icmp(IntPred::Sgt, x, other);
2685 let both = build.binary(Opcode::And, first, second, Flags::NONE);
2686 build.ret(&[both]);
2687 assert!(!simplify(&mut func));
2688 assert_eq!(came_from(&func, both).0, Opcode::And);
2689 }
2690
2691 #[test]
2695 fn two_float_comparisons_that_agree_about_nothing_are_false() {
2696 let (mut func, block, x, y) = a_float_pair();
2697 let mut build = Builder::new(&mut func, block);
2698 let same = build.fcmp(FloatPred::Oeq, x, y, Flags::NONE);
2699 let differ = build.fcmp(FloatPred::Une, x, y, Flags::NONE);
2700 let both = build.binary(Opcode::And, same, differ, Flags::NONE);
2701 build.ret(&[both]);
2702 assert!(simplify(&mut func));
2703 assert_eq!(number(&func, both), 0);
2704 }
2705
2706 #[test]
2711 fn a_three_way_float_condition_folds_one_pair_at_a_time() {
2712 let (mut func, block, x, y) = a_float_pair();
2713 let mut build = Builder::new(&mut func, block);
2714 let neither = build.fcmp(FloatPred::Uno, x, y, Flags::NONE);
2715 let above = build.fcmp(FloatPred::Oge, x, y, Flags::NONE);
2716 let below = build.fcmp(FloatPred::Olt, x, y, Flags::NONE);
2717 let first = build.binary(Opcode::Or, neither, above, Flags::NONE);
2718 let whole = build.binary(Opcode::Or, first, below, Flags::NONE);
2719 build.ret(&[whole]);
2720 assert!(simplify(&mut func));
2721 assert_eq!(came_from(&func, first).1, Extra::FloatPred(FloatPred::Uge));
2722 assert_ne!(number(&func, whole), 0);
2723 }
2724
2725 fn a_float() -> (Func, Block, Value) {
2727 let mut names = Interner::new();
2728 let name = names.intern("f");
2729 let float = Type::float(Float::F64);
2730 let signature = Signature::new().with_params(&[float]).with_returns(&[Type::int(1)]);
2731 let mut func = Func::new(name, signature);
2732 let block = func.create_block();
2733 let x = func.append_param(block, float);
2734 (func, block, x)
2735 }
2736
2737 fn magnitude_of(build: &mut Builder<'_>, x: Value) -> Value {
2739 let bits = Type::int(64);
2740 let number = build.unary(Opcode::Bitcast, x, bits);
2741 let mask = build.iconst(bits, i128::from(i64::MAX));
2742 let cleared = build.binary(Opcode::And, number, mask, Flags::NONE);
2743 build.unary(Opcode::Bitcast, cleared, Type::float(Float::F64))
2744 }
2745
2746 #[test]
2749 fn a_magnitude_is_never_below_zero() {
2750 let (mut func, block, x) = a_float();
2751 let mut build = Builder::new(&mut func, block);
2752 let p = magnitude_of(&mut build, x);
2753 let zero = build.fconst(Type::float(Float::F64), 0);
2754 let below = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
2755 build.ret(&[below]);
2756 assert!(simplify(&mut func));
2757 assert_eq!(number(&func, below), 0);
2758 }
2759
2760 #[test]
2763 fn zero_is_never_above_a_magnitude() {
2764 let (mut func, block, x) = a_float();
2765 let mut build = Builder::new(&mut func, block);
2766 let p = magnitude_of(&mut build, x);
2767 let zero = build.fconst(Type::float(Float::F64), 0);
2768 let above = build.fcmp(FloatPred::Ogt, zero, p, Flags::NONE);
2769 build.ret(&[above]);
2770 assert!(simplify(&mut func));
2771 assert_eq!(number(&func, above), 0);
2772 }
2773
2774 #[test]
2777 fn a_magnitude_at_or_below_zero_is_a_magnitude_equal_to_it() {
2778 let (mut func, block, x) = a_float();
2779 let mut build = Builder::new(&mut func, block);
2780 let p = magnitude_of(&mut build, x);
2781 let zero = build.fconst(Type::float(Float::F64), 0);
2782 let atmost = build.fcmp(FloatPred::Ole, p, zero, Flags::NONE);
2783 build.ret(&[atmost]);
2784 assert!(simplify(&mut func));
2785 assert_eq!(came_from(&func, atmost).1, Extra::FloatPred(FloatPred::Oeq));
2786 }
2787
2788 #[test]
2791 fn a_magnitude_is_never_at_or_below_a_negative_number() {
2792 let (mut func, block, x) = a_float();
2793 let mut build = Builder::new(&mut func, block);
2794 let p = magnitude_of(&mut build, x);
2795 let minus_one = build.fconst(Type::float(Float::F64), 0xbff0_0000_0000_0000);
2796 let atmost = build.fcmp(FloatPred::Ole, p, minus_one, Flags::NONE);
2797 build.ret(&[atmost]);
2798 assert!(simplify(&mut func));
2799 assert_eq!(number(&func, atmost), 0);
2800 }
2801
2802 #[test]
2806 fn a_magnitude_at_or_above_zero_is_still_a_question_about_a_nan() {
2807 let (mut func, block, x) = a_float();
2808 let mut build = Builder::new(&mut func, block);
2809 let p = magnitude_of(&mut build, x);
2810 let zero = build.fconst(Type::float(Float::F64), 0);
2811 let atleast = build.fcmp(FloatPred::Oge, p, zero, Flags::NONE);
2812 build.ret(&[atleast]);
2813 assert!(!simplify(&mut func));
2814 assert_eq!(came_from(&func, atleast).1, Extra::FloatPred(FloatPred::Oge));
2815 }
2816
2817 #[test]
2819 fn a_magnitude_against_a_positive_number_is_left_alone() {
2820 let (mut func, block, x) = a_float();
2821 let mut build = Builder::new(&mut func, block);
2822 let p = magnitude_of(&mut build, x);
2823 let one = build.fconst(Type::float(Float::F64), 0x3ff0_0000_0000_0000);
2824 let below = build.fcmp(FloatPred::Olt, p, one, Flags::NONE);
2825 build.ret(&[below]);
2826 assert!(!simplify(&mut func));
2827 assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
2828 }
2829
2830 #[test]
2833 fn a_mask_that_keeps_the_sign_bit_is_not_a_magnitude() {
2834 let (mut func, block, x) = a_float();
2835 let mut build = Builder::new(&mut func, block);
2836 let bits = Type::int(64);
2837 let number = build.unary(Opcode::Bitcast, x, bits);
2838 let mask = build.iconst(bits, -2);
2839 let cleared = build.binary(Opcode::And, number, mask, Flags::NONE);
2840 let p = build.unary(Opcode::Bitcast, cleared, Type::float(Float::F64));
2841 let zero = build.fconst(Type::float(Float::F64), 0);
2842 let below = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
2843 build.ret(&[below]);
2844 assert!(!simplify(&mut func));
2845 assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
2846 }
2847
2848 #[test]
2851 fn a_magnitude_against_a_nan_is_left_alone() {
2852 let (mut func, block, x) = a_float();
2853 let mut build = Builder::new(&mut func, block);
2854 let p = magnitude_of(&mut build, x);
2855 let nan = build.fconst(Type::float(Float::F64), 0x7ff8_0000_0000_0000);
2856 let below = build.fcmp(FloatPred::Olt, p, nan, Flags::NONE);
2857 build.ret(&[below]);
2858 assert!(!simplify(&mut func));
2859 assert_eq!(came_from(&func, below).1, Extra::FloatPred(FloatPred::Olt));
2860 }
2861
2862 #[test]
2865 fn fuel_stops_the_magnitude_fold_and_not_the_walk() {
2866 let (mut func, block, x) = a_float();
2867 let mut build = Builder::new(&mut func, block);
2868 let p = magnitude_of(&mut build, x);
2869 let zero = build.fconst(Type::float(Float::F64), 0);
2870 let below = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
2871 let also = build.fcmp(FloatPred::Olt, p, zero, Flags::NONE);
2872 let both = build.binary(Opcode::Or, below, also, Flags::NONE);
2873 build.ret(&[both]);
2874 let stats =
2875 Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2876 assert_eq!(stats.count(Kind::Optimized, super::MAGNITUDE), 1);
2877 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_MAGNITUDE), 1);
2878 assert_eq!(number(&func, below), 0);
2879 assert_eq!(came_from(&func, also).0, Opcode::FCmp);
2880 }
2881
2882 #[test]
2885 fn two_comparisons_promised_different_things_are_left_alone() {
2886 let (mut func, block, x, y) = a_float_pair();
2887 let mut build = Builder::new(&mut func, block);
2888 let below = build.fcmp(FloatPred::Olt, x, y, Flags::FAST);
2889 let same = build.fcmp(FloatPred::Oeq, x, y, Flags::NONE);
2890 let either = build.binary(Opcode::Or, below, same, Flags::NONE);
2891 build.ret(&[either]);
2892 assert!(!simplify(&mut func));
2893 assert_eq!(came_from(&func, either).0, Opcode::Or);
2894 }
2895
2896 #[test]
2897 fn the_promise_both_comparisons_were_made_under_travels_to_the_one_that_replaces_them() {
2898 let (mut func, block, x, y) = a_float_pair();
2899 let mut build = Builder::new(&mut func, block);
2900 let below = build.fcmp(FloatPred::Olt, x, y, Flags::FAST);
2901 let same = build.fcmp(FloatPred::Oeq, x, y, Flags::FAST);
2902 let either = build.binary(Opcode::Or, below, same, Flags::NONE);
2903 build.ret(&[either]);
2904 assert!(simplify(&mut func));
2905 assert_eq!(came_from(&func, either).1, Extra::FloatPred(FloatPred::Ole));
2906 let rucc_ir::Def::Result { inst, .. } = func[either].def else { panic!("not a result") };
2907 assert_eq!(func[inst].flags, Flags::FAST);
2908 }
2909
2910 #[test]
2913 fn a_wider_and_of_two_comparisons_is_left_alone() {
2914 let (mut func, block, x, y) = a_pair();
2915 let mut build = Builder::new(&mut func, block);
2916 let same = build.icmp(IntPred::Eq, x, y);
2917 let differ = build.icmp(IntPred::Ne, x, y);
2918 let first = build.unary(Opcode::ZExt, same, Type::int(32));
2919 let second = build.unary(Opcode::ZExt, differ, Type::int(32));
2920 let both = build.binary(Opcode::And, first, second, Flags::NONE);
2921 let narrow = build.unary(Opcode::Trunc, both, Type::int(1));
2922 build.ret(&[narrow]);
2923 assert!(!simplify(&mut func));
2924 assert_eq!(came_from(&func, both).0, Opcode::And);
2925 }
2926
2927 #[test]
2928 fn fuel_stops_the_composite_fold_and_not_the_walk() {
2929 let (mut func, block, x, y) = a_pair();
2930 let mut build = Builder::new(&mut func, block);
2931 let same = build.icmp(IntPred::Eq, x, y);
2932 let differ = build.icmp(IntPred::Ne, x, y);
2933 let below = build.icmp(IntPred::Slt, x, y);
2934 let above = build.icmp(IntPred::Sgt, x, y);
2935 let first = build.binary(Opcode::And, same, differ, Flags::NONE);
2936 let second = build.binary(Opcode::And, below, above, Flags::NONE);
2937 let both = build.binary(Opcode::Or, first, second, Flags::NONE);
2938 build.ret(&[both]);
2939 let stats =
2940 Simplify.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2941 assert!(stats.changed());
2942 assert_eq!(stats.count(Kind::Optimized, super::COMPOSITE), 1);
2943 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_COMPOSITE), 1);
2944 assert_eq!(came_from(&func, first).0, Opcode::IConst);
2945 assert_eq!(came_from(&func, second).0, Opcode::And);
2946 }
2947}