diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp index 37ef4e6736d34..f630d1efbd7e7 100644 --- a/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineCasts.cpp @@ -1686,6 +1686,15 @@ Instruction *InstCombinerImpl::visitZExt(ZExtInst &Zext) { return BinaryOperator::CreateXor(Builder.CreateAnd(X, ZC), ZC); } + // zext(sub(0, trunc(X))) -> and(sub(0, X), mask) + if (match(Src, m_Sub(m_Zero(), m_Trunc(m_Value(X)))) && + X->getType() == DestTy) { + APInt Mask = APInt::getLowBitsSet(DestTy->getScalarSizeInBits(), + SrcTy->getScalarSizeInBits()); + Value *Neg = Builder.CreateSub(ConstantInt::get(DestTy, 0), X); + return BinaryOperator::CreateAnd(Neg, ConstantInt::get(DestTy, Mask)); + } + // If we are truncating, masking, and then zexting back to the original type, // that's just a mask. This is not handled by canEvaluateZextd if the // intermediate values have extra uses. This could be generalized further for diff --git a/llvm/test/Transforms/InstCombine/rotate.ll b/llvm/test/Transforms/InstCombine/rotate.ll index a4d4ec375954f..dd787c0c9d753 100644 --- a/llvm/test/Transforms/InstCombine/rotate.ll +++ b/llvm/test/Transforms/InstCombine/rotate.ll @@ -1086,3 +1086,35 @@ define i32 @not_rotl_i32_add_less(i32 %x, i32 %y) { %r = add i32 %shr, %shl ret i32 %r } + +; PR165306 +define <8 x i64> @fold_rot_fshr_v8i64(<8 x i64> %x, <8 x i64> %y) { +; CHECK-LABEL: @fold_rot_fshr_v8i64( +; CHECK-NEXT: [[OR:%.*]] = call <8 x i64> @llvm.fshr.v8i64(<8 x i64> [[X:%.*]], <8 x i64> [[X]], <8 x i64> [[Y:%.*]]) +; CHECK-NEXT: ret <8 x i64> [[OR]] +; + %trunc = trunc <8 x i64> %y to <8 x i6> + %neg = sub <8 x i6> zeroinitializer, %trunc + %zext = zext <8 x i6> %neg to <8 x i64> + %shl = shl <8 x i64> %x, %zext + %mask = and <8 x i64> %y, splat (i64 63) + %lshr = lshr <8 x i64> %x, %mask + %or = or <8 x i64> %shl, %lshr + ret <8 x i64> %or +} + +; PR165306 +define i32 @fold_rot_fshl_i32(i32 %x, i32 %y) { +; CHECK-LABEL: @fold_rot_fshl_i32( +; CHECK-NEXT: [[OR:%.*]] = call i32 @llvm.fshl.i32(i32 [[X:%.*]], i32 [[X]], i32 [[Y:%.*]]) +; CHECK-NEXT: ret i32 [[OR]] +; + %trunc = trunc i32 %y to i5 + %neg = sub i5 0, %trunc + %zext = zext i5 %neg to i32 + %lshr = lshr i32 %x, %zext + %mask = and i32 %y, 31 + %shl = shl i32 %x, %mask + %or = or i32 %shl, %lshr + ret i32 %or +} diff --git a/llvm/test/Transforms/InstCombine/zext-sub-trunc.ll b/llvm/test/Transforms/InstCombine/zext-sub-trunc.ll new file mode 100644 index 0000000000000..d4eaacbb8631d --- /dev/null +++ b/llvm/test/Transforms/InstCombine/zext-sub-trunc.ll @@ -0,0 +1,57 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6 +; RUN: opt < %s -passes=instcombine -S | FileCheck %s + +define i64 @test_scalar_mask(i64 %a) { +; CHECK-LABEL: define i64 @test_scalar_mask( +; CHECK-SAME: i64 [[A:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = sub i64 0, [[A]] +; CHECK-NEXT: [[ZEXT:%.*]] = and i64 [[TMP1]], 63 +; CHECK-NEXT: ret i64 [[ZEXT]] +; + %trunc = trunc i64 %a to i6 + %neg = sub i6 0, %trunc + %zext = zext i6 %neg to i64 + ret i64 %zext +} + +define <8 x i64> @test_vector_mask_v8i64(<8 x i64> %a0) { +; CHECK-LABEL: define <8 x i64> @test_vector_mask_v8i64( +; CHECK-SAME: <8 x i64> [[A0:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = sub <8 x i64> zeroinitializer, [[A0]] +; CHECK-NEXT: [[ZEXT:%.*]] = and <8 x i64> [[TMP1]], splat (i64 63) +; CHECK-NEXT: ret <8 x i64> [[ZEXT]] +; + %trunc = trunc <8 x i64> %a0 to <8 x i6> + %neg = sub <8 x i6> zeroinitializer, %trunc + %zext = zext <8 x i6> %neg to <8 x i64> + ret <8 x i64> %zext +} + +define <8 x i32> @test_vector_mask_v8i32(<8 x i32> %a0) { +; CHECK-LABEL: define <8 x i32> @test_vector_mask_v8i32( +; CHECK-SAME: <8 x i32> [[A0:%.*]]) { +; CHECK-NEXT: [[TMP1:%.*]] = sub <8 x i32> zeroinitializer, [[A0]] +; CHECK-NEXT: [[ZEXT:%.*]] = and <8 x i32> [[TMP1]], splat (i32 15) +; CHECK-NEXT: ret <8 x i32> [[ZEXT]] +; + %trunc = trunc <8 x i32> %a0 to <8 x i4> + %neg = sub <8 x i4> zeroinitializer, %trunc + %zext = zext <8 x i4> %neg to <8 x i32> + ret <8 x i32> %zext +} + +; This must not be folded because the source and dest types of trunc/zext pair +; do not match +define <8 x i64> @test_negative_vector_mask_v8i32(<8 x i32> %a0) { +; CHECK-LABEL: define <8 x i64> @test_negative_vector_mask_v8i32( +; CHECK-SAME: <8 x i32> [[A0:%.*]]) { +; CHECK-NEXT: [[TRUNC:%.*]] = trunc <8 x i32> [[A0]] to <8 x i4> +; CHECK-NEXT: [[NEG:%.*]] = sub <8 x i4> zeroinitializer, [[TRUNC]] +; CHECK-NEXT: [[ZEXT:%.*]] = zext <8 x i4> [[NEG]] to <8 x i64> +; CHECK-NEXT: ret <8 x i64> [[ZEXT]] +; + %trunc = trunc <8 x i32> %a0 to <8 x i4> + %neg = sub <8 x i4> zeroinitializer, %trunc + %zext = zext <8 x i4> %neg to <8 x i64> + ret <8 x i64> %zext +}