Index: llvm/trunk/lib/Analysis/InstructionSimplify.cpp =================================================================== --- llvm/trunk/lib/Analysis/InstructionSimplify.cpp +++ llvm/trunk/lib/Analysis/InstructionSimplify.cpp @@ -3837,6 +3837,28 @@ if (Value *V = simplifySelectBitTest(TrueVal, FalseVal, X, Y, Pred == ICmpInst::ICMP_EQ)) return V; + + // Test for zero-shift-guard-ops around funnel shifts. These are used to + // avoid UB from oversized shifts in raw IR rotate patterns, but the + // intrinsics do not have that problem. + Value *ShAmt; + auto isFsh = m_CombineOr(m_Intrinsic(m_Value(X), m_Value(), + m_Value(ShAmt)), + m_Intrinsic(m_Value(), m_Value(X), + m_Value(ShAmt))); + // (ShAmt != 0) ? fshl(X, *, ShAmt) : X --> fshl(X, *, ShAmt) + // (ShAmt != 0) ? fshr(*, X, ShAmt) : X --> fshr(*, X, ShAmt) + // (ShAmt == 0) ? fshl(X, *, ShAmt) : X --> X + // (ShAmt == 0) ? fshr(*, X, ShAmt) : X --> X + if (match(TrueVal, isFsh) && FalseVal == X && CmpLHS == ShAmt) + return Pred == ICmpInst::ICMP_NE ? TrueVal : X; + + // (ShAmt == 0) ? X : fshl(X, *, ShAmt) --> fshl(X, *, ShAmt) + // (ShAmt == 0) ? X : fshr(*, X, ShAmt) --> fshr(*, X, ShAmt) + // (ShAmt != 0) ? X : fshl(X, *, ShAmt) --> X + // (ShAmt != 0) ? X : fshr(*, X, ShAmt) --> X + if (match(FalseVal, isFsh) && TrueVal == X && CmpLHS == ShAmt) + return Pred == ICmpInst::ICMP_EQ ? FalseVal : X; } // Check for other compares that behave like bit test. Index: llvm/trunk/test/Transforms/InstSimplify/call.ll =================================================================== --- llvm/trunk/test/Transforms/InstSimplify/call.ll +++ llvm/trunk/test/Transforms/InstSimplify/call.ll @@ -500,12 +500,12 @@ ret <2 x i8> %z } +; When the shift amount is 0, fshl returns its 1st parameter (x), so the guard is not needed. + define i8 @fshl_zero_shift_guard(i8 %x, i8 %y, i8 %sh) { ; CHECK-LABEL: @fshl_zero_shift_guard( -; CHECK-NEXT: [[C:%.*]] = icmp eq i8 [[SH:%.*]], 0 -; CHECK-NEXT: [[F:%.*]] = call i8 @llvm.fshl.i8(i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[SH]]) -; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i8 [[X]], i8 [[F]] -; CHECK-NEXT: ret i8 [[S]] +; CHECK-NEXT: [[F:%.*]] = call i8 @llvm.fshl.i8(i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[SH:%.*]]) +; CHECK-NEXT: ret i8 [[F]] ; %c = icmp eq i8 %sh, 0 %f = call i8 @llvm.fshl.i8(i8 %x, i8 %y, i8 %sh) @@ -513,12 +513,12 @@ ret i8 %s } +; When the shift amount is 0, fshl returns its 1st parameter (x), so the guard is not needed. + define i8 @fshl_zero_shift_guard_swapped(i8 %x, i8 %y, i8 %sh) { ; CHECK-LABEL: @fshl_zero_shift_guard_swapped( -; CHECK-NEXT: [[C:%.*]] = icmp ne i8 [[SH:%.*]], 0 -; CHECK-NEXT: [[F:%.*]] = call i8 @llvm.fshl.i8(i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[SH]]) -; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i8 [[F]], i8 [[X]] -; CHECK-NEXT: ret i8 [[S]] +; CHECK-NEXT: [[F:%.*]] = call i8 @llvm.fshl.i8(i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[SH:%.*]]) +; CHECK-NEXT: ret i8 [[F]] ; %c = icmp ne i8 %sh, 0 %f = call i8 @llvm.fshl.i8(i8 %x, i8 %y, i8 %sh) @@ -526,12 +526,11 @@ ret i8 %s } +; When the shift amount is 0, fshl returns its 1st parameter (x), so everything is deleted. + define i8 @fshl_zero_shift_guard_inverted(i8 %x, i8 %y, i8 %sh) { ; CHECK-LABEL: @fshl_zero_shift_guard_inverted( -; CHECK-NEXT: [[C:%.*]] = icmp eq i8 [[SH:%.*]], 0 -; CHECK-NEXT: [[F:%.*]] = call i8 @llvm.fshl.i8(i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[SH]]) -; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i8 [[F]], i8 [[X]] -; CHECK-NEXT: ret i8 [[S]] +; CHECK-NEXT: ret i8 [[X:%.*]] ; %c = icmp eq i8 %sh, 0 %f = call i8 @llvm.fshl.i8(i8 %x, i8 %y, i8 %sh) @@ -539,12 +538,11 @@ ret i8 %s } +; When the shift amount is 0, fshl returns its 1st parameter (x), so everything is deleted. + define i8 @fshl_zero_shift_guard_inverted_swapped(i8 %x, i8 %y, i8 %sh) { ; CHECK-LABEL: @fshl_zero_shift_guard_inverted_swapped( -; CHECK-NEXT: [[C:%.*]] = icmp ne i8 [[SH:%.*]], 0 -; CHECK-NEXT: [[F:%.*]] = call i8 @llvm.fshl.i8(i8 [[X:%.*]], i8 [[Y:%.*]], i8 [[SH]]) -; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i8 [[X]], i8 [[F]] -; CHECK-NEXT: ret i8 [[S]] +; CHECK-NEXT: ret i8 [[X:%.*]] ; %c = icmp ne i8 %sh, 0 %f = call i8 @llvm.fshl.i8(i8 %x, i8 %y, i8 %sh) @@ -552,12 +550,12 @@ ret i8 %s } +; When the shift amount is 0, fshr returns its 2nd parameter (y), so the guard is not needed. + define i9 @fshr_zero_shift_guard(i9 %x, i9 %y, i9 %sh) { ; CHECK-LABEL: @fshr_zero_shift_guard( -; CHECK-NEXT: [[C:%.*]] = icmp eq i9 [[SH:%.*]], 0 -; CHECK-NEXT: [[F:%.*]] = call i9 @llvm.fshr.i9(i9 [[X:%.*]], i9 [[Y:%.*]], i9 [[SH]]) -; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i9 [[Y]], i9 [[F]] -; CHECK-NEXT: ret i9 [[S]] +; CHECK-NEXT: [[F:%.*]] = call i9 @llvm.fshr.i9(i9 [[X:%.*]], i9 [[Y:%.*]], i9 [[SH:%.*]]) +; CHECK-NEXT: ret i9 [[F]] ; %c = icmp eq i9 %sh, 0 %f = call i9 @llvm.fshr.i9(i9 %x, i9 %y, i9 %sh) @@ -565,12 +563,12 @@ ret i9 %s } +; When the shift amount is 0, fshr returns its 2nd parameter (y), so the guard is not needed. + define i9 @fshr_zero_shift_guard_swapped(i9 %x, i9 %y, i9 %sh) { ; CHECK-LABEL: @fshr_zero_shift_guard_swapped( -; CHECK-NEXT: [[C:%.*]] = icmp ne i9 [[SH:%.*]], 0 -; CHECK-NEXT: [[F:%.*]] = call i9 @llvm.fshr.i9(i9 [[X:%.*]], i9 [[Y:%.*]], i9 [[SH]]) -; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i9 [[F]], i9 [[Y]] -; CHECK-NEXT: ret i9 [[S]] +; CHECK-NEXT: [[F:%.*]] = call i9 @llvm.fshr.i9(i9 [[X:%.*]], i9 [[Y:%.*]], i9 [[SH:%.*]]) +; CHECK-NEXT: ret i9 [[F]] ; %c = icmp ne i9 %sh, 0 %f = call i9 @llvm.fshr.i9(i9 %x, i9 %y, i9 %sh) @@ -578,12 +576,11 @@ ret i9 %s } +; When the shift amount is 0, fshr returns its 2nd parameter (y), so everything is deleted. + define i9 @fshr_zero_shift_guard_inverted(i9 %x, i9 %y, i9 %sh) { ; CHECK-LABEL: @fshr_zero_shift_guard_inverted( -; CHECK-NEXT: [[C:%.*]] = icmp eq i9 [[SH:%.*]], 0 -; CHECK-NEXT: [[F:%.*]] = call i9 @llvm.fshr.i9(i9 [[X:%.*]], i9 [[Y:%.*]], i9 [[SH]]) -; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i9 [[F]], i9 [[Y]] -; CHECK-NEXT: ret i9 [[S]] +; CHECK-NEXT: ret i9 [[Y:%.*]] ; %c = icmp eq i9 %sh, 0 %f = call i9 @llvm.fshr.i9(i9 %x, i9 %y, i9 %sh) @@ -591,12 +588,11 @@ ret i9 %s } +; When the shift amount is 0, fshr returns its 2nd parameter (y), so everything is deleted. + define i9 @fshr_zero_shift_guard_inverted_swapped(i9 %x, i9 %y, i9 %sh) { ; CHECK-LABEL: @fshr_zero_shift_guard_inverted_swapped( -; CHECK-NEXT: [[C:%.*]] = icmp ne i9 [[SH:%.*]], 0 -; CHECK-NEXT: [[F:%.*]] = call i9 @llvm.fshr.i9(i9 [[X:%.*]], i9 [[Y:%.*]], i9 [[SH]]) -; CHECK-NEXT: [[S:%.*]] = select i1 [[C]], i9 [[Y]], i9 [[F]] -; CHECK-NEXT: ret i9 [[S]] +; CHECK-NEXT: ret i9 [[Y:%.*]] ; %c = icmp ne i9 %sh, 0 %f = call i9 @llvm.fshr.i9(i9 %x, i9 %y, i9 %sh) @@ -604,6 +600,8 @@ ret i9 %s } +; Negative test - make sure we're matching the correct parameter of fshl. + define i8 @fshl_zero_shift_guard_wrong_select_op(i8 %x, i8 %y, i8 %sh) { ; CHECK-LABEL: @fshl_zero_shift_guard_wrong_select_op( ; CHECK-NEXT: [[C:%.*]] = icmp eq i8 [[SH:%.*]], 0 @@ -617,12 +615,12 @@ ret i8 %s } +; Vector types work too. + define <2 x i8> @fshr_zero_shift_guard_splat(<2 x i8> %x, <2 x i8> %y, <2 x i8> %sh) { ; CHECK-LABEL: @fshr_zero_shift_guard_splat( -; CHECK-NEXT: [[C:%.*]] = icmp eq <2 x i8> [[SH:%.*]], zeroinitializer -; CHECK-NEXT: [[F:%.*]] = call <2 x i8> @llvm.fshr.v2i8(<2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]], <2 x i8> [[SH]]) -; CHECK-NEXT: [[S:%.*]] = select <2 x i1> [[C]], <2 x i8> [[Y]], <2 x i8> [[F]] -; CHECK-NEXT: ret <2 x i8> [[S]] +; CHECK-NEXT: [[F:%.*]] = call <2 x i8> @llvm.fshr.v2i8(<2 x i8> [[X:%.*]], <2 x i8> [[Y:%.*]], <2 x i8> [[SH:%.*]]) +; CHECK-NEXT: ret <2 x i8> [[F]] ; %c = icmp eq <2 x i8> %sh, zeroinitializer %f = call <2 x i8> @llvm.fshr.v2i8(<2 x i8> %x, <2 x i8> %y, <2 x i8> %sh)