diff --git a/llvm/lib/Analysis/InstructionSimplify.cpp b/llvm/lib/Analysis/InstructionSimplify.cpp --- a/llvm/lib/Analysis/InstructionSimplify.cpp +++ b/llvm/lib/Analysis/InstructionSimplify.cpp @@ -992,6 +992,29 @@ return ::simplifyMulInst(Op0, Op1, IsNSW, IsNUW, Q, RecursionLimit); } +Value *simplifyRemShifts(Value *Op0, Value *Op1, bool IsSigned, + const SimplifyQuery &Q) { + // rem X << S1, X << S2 + // if (S1 >= S2) -> 0; else -> X << S1 + Value *ShiftX; + ConstantInt *S1, *S2; + if (!match(Op0, m_Shl(m_Value(ShiftX), m_ConstantInt(S1))) || + !match(Op1, m_Shl(m_Deferred(ShiftX), m_ConstantInt(S2)))) + return nullptr; + + auto Shift = cast(Op0); + if (S1 >= S2 && ((IsSigned && Q.IIQ.hasNoSignedWrap(Shift)) || + (!IsSigned && Q.IIQ.hasNoUnsignedWrap(Shift)))) + return Constant::getNullValue(Shift->getType()); + + Shift = cast(Op1); + if ((IsSigned && Q.IIQ.hasNoSignedWrap(Shift)) || + (!IsSigned && Q.IIQ.hasNoUnsignedWrap(Shift))) + return Op0; + + return nullptr; +} + /// Check for common or similar folds of integer division or integer remainder. /// This applies to all 4 opcodes (sdiv/udiv/srem/urem). static Value *simplifyDivRem(Instruction::BinaryOps Opcode, Value *Op0, @@ -1074,6 +1097,9 @@ if (Value *V = simplifyByDomEq(Opcode, Op0, Op1, Q, MaxRecurse)) return V; + if (!IsDiv) + if (Value *V = simplifyRemShifts(Op0, Op1, IsSigned, Q)) + return V; return nullptr; } diff --git a/llvm/test/Transforms/InstSimplify/rem.ll b/llvm/test/Transforms/InstSimplify/rem.ll --- a/llvm/test/Transforms/InstSimplify/rem.ll +++ b/llvm/test/Transforms/InstSimplify/rem.ll @@ -488,3 +488,97 @@ %mod = urem i8 %mul, %y ret i8 %mod } + +define i8 @urem_shl(i8 %x){ +; CHECK-LABEL: @urem_shl( +; CHECK-NEXT: ret i8 0 +; + %x1 = shl i8 %x, 1 + %x2 = shl nuw i8 %x, 2 + %1 = urem i8 %x2, %x1 + ret i8 %1 +} + +define i8 @neg_urem_shl(i8 %x){ +; CHECK-LABEL: @neg_urem_shl( +; CHECK-NEXT: [[X1:%.*]] = shl i8 [[X:%.*]], 1 +; CHECK-NEXT: [[X2:%.*]] = shl i8 [[X]], 2 +; CHECK-NEXT: [[TMP1:%.*]] = urem i8 [[X2]], [[X1]] +; CHECK-NEXT: ret i8 [[TMP1]] +; + %x1 = shl i8 %x, 1 + %x2 = shl i8 %x, 2 + %1 = urem i8 %x2, %x1 + ret i8 %1 +} + +define i8 @urem_shl_2(i8 %x){ +; CHECK-LABEL: @urem_shl_2( +; CHECK-NEXT: [[X1:%.*]] = shl i8 [[X:%.*]], 1 +; CHECK-NEXT: ret i8 [[X1]] +; + %x1 = shl i8 %x, 1 + %x2 = shl nuw i8 %x, 2 + %1 = urem i8 %x1, %x2 + ret i8 %1 +} + +define i8 @neg_urem_shl_2(i8 %x){ +; CHECK-LABEL: @neg_urem_shl_2( +; CHECK-NEXT: [[X1:%.*]] = shl i8 [[X:%.*]], 1 +; CHECK-NEXT: [[X2:%.*]] = shl i8 [[X]], 2 +; CHECK-NEXT: [[TMP1:%.*]] = urem i8 [[X1]], [[X2]] +; CHECK-NEXT: ret i8 [[TMP1]] +; + %x1 = shl i8 %x, 1 + %x2 = shl i8 %x, 2 + %1 = urem i8 %x1, %x2 + ret i8 %1 +} + +define i8 @srem_shl(i8 %x){ +; CHECK-LABEL: @srem_shl( +; CHECK-NEXT: ret i8 0 +; + %x1 = shl i8 %x, 1 + %x2 = shl nsw i8 %x, 2 + %1 = srem i8 %x2, %x1 + ret i8 %1 +} + +define i8 @neg_srem_shl(i8 %x){ +; CHECK-LABEL: @neg_srem_shl( +; CHECK-NEXT: [[X1:%.*]] = shl i8 [[X:%.*]], 1 +; CHECK-NEXT: [[X2:%.*]] = shl i8 [[X]], 2 +; CHECK-NEXT: [[TMP1:%.*]] = srem i8 [[X2]], [[X1]] +; CHECK-NEXT: ret i8 [[TMP1]] +; + %x1 = shl i8 %x, 1 + %x2 = shl i8 %x, 2 + %1 = srem i8 %x2, %x1 + ret i8 %1 +} + +define i8 @srem_shl_2(i8 %x){ +; CHECK-LABEL: @srem_shl_2( +; CHECK-NEXT: [[X1:%.*]] = shl i8 [[X:%.*]], 1 +; CHECK-NEXT: [[X2:%.*]] = shl i8 [[X]], 2 +; CHECK-NEXT: [[TMP1:%.*]] = srem i8 [[X1]], [[X2]] +; CHECK-NEXT: ret i8 [[TMP1]] +; + %x1 = shl i8 %x, 1 + %x2 = shl i8 %x, 2 + %1 = srem i8 %x1, %x2 + ret i8 %1 +} + +define i8 @neg_srem_shl_2(i8 %x){ +; CHECK-LABEL: @neg_srem_shl_2( +; CHECK-NEXT: [[X1:%.*]] = shl i8 [[X:%.*]], 1 +; CHECK-NEXT: ret i8 [[X1]] +; + %x1 = shl i8 %x, 1 + %x2 = shl nsw i8 %x, 2 + %1 = srem i8 %x1, %x2 + ret i8 %1 +}