Index: llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp =================================================================== --- llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp +++ llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp @@ -1786,6 +1786,17 @@ return SelectInst::Create(Cmp, ConstantInt::getNullValue(Ty), Op0); } + // For "(X + 1) % Op1" and if (X u< Op1) => (X + 1) == Op1 ? 0 : X + 1 . + if (match(Op0, m_Add(m_Value(X), m_One()))) { + Value *Val = + simplifyICmpInst(ICmpInst::ICMP_ULT, X, Op1, SQ.getWithInstruction(&I)); + if (Val && match(Val, m_One())) { + Value *FrozenOp0 = Builder.CreateFreeze(Op0, Op0->getName() + ".frozen"); + Value *Cmp = Builder.CreateICmpEQ(FrozenOp0, Op1); + return SelectInst::Create(Cmp, ConstantInt::getNullValue(Ty), FrozenOp0); + } + } + return nullptr; } Index: llvm/test/Transforms/InstCombine/urem-via-cmp-select.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/InstCombine/urem-via-cmp-select.ll @@ -0,0 +1,135 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +; https://alive2.llvm.org/ce/z/UNmz9j +define noundef i64 @urem_assume(i64 noundef %x, i64 noundef %n) { +; CHECK-LABEL: @urem_assume( +; CHECK-NEXT: start: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[X:%.*]], [[N:%.*]] +; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]]) +; CHECK-NEXT: [[ADD:%.*]] = add i64 [[X]], 1 +; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[ADD]], [[N]] +; CHECK-NEXT: [[OUT:%.*]] = select i1 [[TMP0]], i64 0, i64 [[ADD]] +; CHECK-NEXT: ret i64 [[OUT]] +; +start: + %cmp = icmp ult i64 %x, %n + tail call void @llvm.assume(i1 %cmp) + %add = add nuw i64 %x, 1 + %out = urem i64 %add, %n + ret i64 %out +} + +; https://alive2.llvm.org/ce/z/uo7HMz +define noundef i64 @urem_assume_without_nuw(i64 %x, i64 %n) { +; CHECK-LABEL: @urem_assume_without_nuw( +; CHECK-NEXT: start: +; CHECK-NEXT: [[X_FR:%.*]] = freeze i64 [[X:%.*]] +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[X_FR]], [[N:%.*]] +; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]]) +; CHECK-NEXT: [[ADD:%.*]] = add i64 [[X_FR]], 1 +; CHECK-NEXT: [[TMP0:%.*]] = icmp eq i64 [[ADD]], [[N]] +; CHECK-NEXT: [[OUT:%.*]] = select i1 [[TMP0]], i64 0, i64 [[ADD]] +; CHECK-NEXT: ret i64 [[OUT]] +; +start: + %cmp = icmp ult i64 %x, %n + tail call void @llvm.assume(i1 %cmp) + %add = add i64 %x, 1 + %out = urem i64 %add, %n + ret i64 %out +} + +define noundef i64 @urem_assume_eq(i64 noundef %x, i64 noundef %n) { +; CHECK-LABEL: @urem_assume_eq( +; CHECK-NEXT: start: +; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[X:%.*]], [[N:%.*]] +; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]]) +; CHECK-NEXT: [[ADD:%.*]] = add i64 [[X]], 1 +; CHECK-NEXT: [[OUT:%.*]] = urem i64 [[ADD]], [[N]] +; CHECK-NEXT: ret i64 [[OUT]] +; +start: + %cmp = icmp eq i64 %x, %n + tail call void @llvm.assume(i1 %cmp) + %add = add i64 %x, 1 + %out = urem i64 %add, %n + ret i64 %out +} + +; Negative test: The assume is false +define noundef i64 @urem_assume_ne(i64 noundef %x, i64 noundef %n) { +; CHECK-LABEL: @urem_assume_ne( +; CHECK-NEXT: start: +; CHECK-NEXT: [[CMP:%.*]] = icmp ne i64 [[X:%.*]], [[N:%.*]] +; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]]) +; CHECK-NEXT: [[ADD:%.*]] = add i64 [[X]], 1 +; CHECK-NEXT: [[OUT:%.*]] = urem i64 [[ADD]], [[N]] +; CHECK-NEXT: ret i64 [[OUT]] +; +start: + %cmp = icmp ne i64 %x, %n + tail call void @llvm.assume(i1 %cmp) + %add = add i64 %x, 1 + %out = urem i64 %add, %n + ret i64 %out +} + +; Negative test: The add constant is not 1 +define noundef i64 @urem_assume_with_unexpected_const(i64 %x, i64 %n) { +; CHECK-LABEL: @urem_assume_with_unexpected_const( +; CHECK-NEXT: start: +; CHECK-NEXT: [[CMP:%.*]] = icmp ult i64 [[X:%.*]], [[N:%.*]] +; CHECK-NEXT: tail call void @llvm.assume(i1 [[CMP]]) +; CHECK-NEXT: [[ADD:%.*]] = add i64 [[X]], 2 +; CHECK-NEXT: [[OUT:%.*]] = urem i64 [[ADD]], [[N]] +; CHECK-NEXT: ret i64 [[OUT]] +; +start: + %cmp = icmp ult i64 %x, %n + tail call void @llvm.assume(i1 %cmp) + %add = add i64 %x, 2 ; Transform only when the constant is 1 + %out = urem i64 %add, %n + ret i64 %out +} + +; Negative test: Need a dominating condition or assume +define noundef i64 @urem_without_assume(i64 %x, i64 %n) { +; CHECK-LABEL: @urem_without_assume( +; CHECK-NEXT: start: +; CHECK-NEXT: [[ADD:%.*]] = add i64 [[X:%.*]], 1 +; CHECK-NEXT: [[OUT:%.*]] = urem i64 [[ADD]], [[N:%.*]] +; CHECK-NEXT: ret i64 [[OUT]] +; +start: + %cmp = icmp ult i64 %x, %n + %add = add i64 %x, 1 + %out = urem i64 %add, %n + ret i64 %out +} + +; TODO: https://alive2.llvm.org/ce/z/eHkgRa +define noundef i8 @urem_with_dominating_condition(i8 %x, i8 %n) { +; CHECK-LABEL: @urem_with_dominating_condition( +; CHECK-NEXT: start: +; CHECK-NEXT: [[COND:%.*]] = icmp ult i8 [[X:%.*]], [[N:%.*]] +; CHECK-NEXT: br i1 [[COND]], label [[DOTBB0:%.*]], label [[DOTBB1:%.*]] +; CHECK: .bb0: +; CHECK-NEXT: [[ADD:%.*]] = add i8 [[X]], 1 +; CHECK-NEXT: [[OUT:%.*]] = urem i8 [[ADD]], [[N]] +; CHECK-NEXT: ret i8 [[OUT]] +; CHECK: .bb1: +; CHECK-NEXT: ret i8 0 +; +start: + %cond = icmp ult i8 %x, %n + br i1 %cond, label %.bb0, label %.bb1 ; Should also works for a dominating condition +.bb0: + %add = add i8 %x, 1 + %out = urem i8 %add, %n + ret i8 %out +.bb1: + ret i8 0 +} + +declare void @llvm.assume(i1 noundef)