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 @@ -2329,6 +2329,31 @@ } } + // A funnel shift (rotate) can be decomposed into simpler shifts. See if we + // are mixing in another shift that is redundant with the funnel shift. + + // (fshl X, ?, Y) | (shl X, Y) --> fshl X, ?, Y + // (shl X, Y) | (fshl X, ?, Y) --> fshl X, ?, Y + if (match(Op0, + m_Intrinsic(m_Value(X), m_Value(), m_Value(Y))) && + match(Op1, m_Shl(m_Specific(X), m_Specific(Y)))) + return Op0; + if (match(Op1, + m_Intrinsic(m_Value(X), m_Value(), m_Value(Y))) && + match(Op0, m_Shl(m_Specific(X), m_Specific(Y)))) + return Op1; + + // (fshr ?, X, Y) | (lshr X, Y) --> fshr ?, X, Y + // (lshr X, Y) | (fshr ?, X, Y) --> fshr ?, X, Y + if (match(Op0, + m_Intrinsic(m_Value(), m_Value(X), m_Value(Y))) && + match(Op1, m_LShr(m_Specific(X), m_Specific(Y)))) + return Op0; + if (match(Op1, + m_Intrinsic(m_Value(), m_Value(X), m_Value(Y))) && + match(Op0, m_LShr(m_Specific(X), m_Specific(Y)))) + return Op1; + if (Value *V = simplifyAndOrOfCmps(Q, Op0, Op1, false)) return V; diff --git a/llvm/test/Transforms/InstSimplify/or.ll b/llvm/test/Transforms/InstSimplify/or.ll --- a/llvm/test/Transforms/InstSimplify/or.ll +++ b/llvm/test/Transforms/InstSimplify/or.ll @@ -1045,10 +1045,8 @@ define i32 @or_shl_fshl(i32 %x, i32 %y, i32 %s) { ; CHECK-LABEL: @or_shl_fshl( -; CHECK-NEXT: [[SHY:%.*]] = shl i32 [[Y:%.*]], [[S:%.*]] -; CHECK-NEXT: [[FUN:%.*]] = call i32 @llvm.fshl.i32(i32 [[Y]], i32 [[X:%.*]], i32 [[S]]) -; CHECK-NEXT: [[OR:%.*]] = or i32 [[FUN]], [[SHY]] -; CHECK-NEXT: ret i32 [[OR]] +; CHECK-NEXT: [[FUN:%.*]] = call i32 @llvm.fshl.i32(i32 [[Y:%.*]], i32 [[X:%.*]], i32 [[S:%.*]]) +; CHECK-NEXT: ret i32 [[FUN]] ; %shy = shl i32 %y, %s %fun = call i32 @llvm.fshl.i32(i32 %y, i32 %x, i32 %s) @@ -1058,10 +1056,8 @@ define i32 @or_shl_fshl_commute(i32 %x, i32 %y, i32 %s) { ; CHECK-LABEL: @or_shl_fshl_commute( -; CHECK-NEXT: [[SHY:%.*]] = shl i32 [[Y:%.*]], [[S:%.*]] -; CHECK-NEXT: [[FUN:%.*]] = call i32 @llvm.fshl.i32(i32 [[Y]], i32 [[X:%.*]], i32 [[S]]) -; CHECK-NEXT: [[OR:%.*]] = or i32 [[SHY]], [[FUN]] -; CHECK-NEXT: ret i32 [[OR]] +; CHECK-NEXT: [[FUN:%.*]] = call i32 @llvm.fshl.i32(i32 [[Y:%.*]], i32 [[X:%.*]], i32 [[S:%.*]]) +; CHECK-NEXT: ret i32 [[FUN]] ; %shy = shl i32 %y, %s %fun = call i32 @llvm.fshl.i32(i32 %y, i32 %x, i32 %s) @@ -1069,6 +1065,8 @@ ret i32 %or } +; negative test - fshl operands are not commutative + define i32 @or_shl_fshl_wrong_order(i32 %x, i32 %y, i32 %s) { ; CHECK-LABEL: @or_shl_fshl_wrong_order( ; CHECK-NEXT: [[SHY:%.*]] = shl i32 [[Y:%.*]], [[S:%.*]] @@ -1084,10 +1082,8 @@ define i32 @or_lshr_fshr(i32 %x, i32 %y, i32 %s) { ; CHECK-LABEL: @or_lshr_fshr( -; CHECK-NEXT: [[SHY:%.*]] = lshr i32 [[Y:%.*]], [[S:%.*]] -; CHECK-NEXT: [[FUN:%.*]] = call i32 @llvm.fshr.i32(i32 [[X:%.*]], i32 [[Y]], i32 [[S]]) -; CHECK-NEXT: [[OR:%.*]] = or i32 [[FUN]], [[SHY]] -; CHECK-NEXT: ret i32 [[OR]] +; CHECK-NEXT: [[FUN:%.*]] = call i32 @llvm.fshr.i32(i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[S:%.*]]) +; CHECK-NEXT: ret i32 [[FUN]] ; %shy = lshr i32 %y, %s %fun = call i32 @llvm.fshr.i32(i32 %x, i32 %y, i32 %s) @@ -1097,10 +1093,8 @@ define i32 @or_lshr_fshr_commute(i32 %x, i32 %y, i32 %s) { ; CHECK-LABEL: @or_lshr_fshr_commute( -; CHECK-NEXT: [[SHY:%.*]] = lshr i32 [[Y:%.*]], [[S:%.*]] -; CHECK-NEXT: [[FUN:%.*]] = call i32 @llvm.fshr.i32(i32 [[X:%.*]], i32 [[Y]], i32 [[S]]) -; CHECK-NEXT: [[OR:%.*]] = or i32 [[SHY]], [[FUN]] -; CHECK-NEXT: ret i32 [[OR]] +; CHECK-NEXT: [[FUN:%.*]] = call i32 @llvm.fshr.i32(i32 [[X:%.*]], i32 [[Y:%.*]], i32 [[S:%.*]]) +; CHECK-NEXT: ret i32 [[FUN]] ; %shy = lshr i32 %y, %s %fun = call i32 @llvm.fshr.i32(i32 %x, i32 %y, i32 %s) @@ -1108,6 +1102,8 @@ ret i32 %or } +; negative test - fshr operands are not commutative + define i32 @or_lshr_fshr_wrong_order(i32 %x, i32 %y, i32 %s) { ; CHECK-LABEL: @or_lshr_fshr_wrong_order( ; CHECK-NEXT: [[SHY:%.*]] = lshr i32 [[Y:%.*]], [[S:%.*]]