[llvm] 70aede2 - InstCombine: Recognize fneg(fabs) as bitcasted integer

Matt Arsenault via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 31 16:09:40 PDT 2023


Author: Matt Arsenault
Date: 2023-08-31T19:07:36-04:00
New Revision: 70aede228a1b6fca90b96bc8a6245b92e20316c6

URL: https://github.com/llvm/llvm-project/commit/70aede228a1b6fca90b96bc8a6245b92e20316c6
DIFF: https://github.com/llvm/llvm-project/commit/70aede228a1b6fca90b96bc8a6245b92e20316c6.diff

LOG: InstCombine: Recognize fneg(fabs) as bitcasted integer

Technically increases the number of instructions if the
result isn't cast back to float. Even in this case it's
still probably a better canonical form since it enables FP value
tracking.

https://reviews.llvm.org/D151939

Added: 
    

Modified: 
    llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
    llvm/test/Transforms/InstCombine/fneg-fabs-as-int.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
index eecec4764af0ba..8d72bbc14e7fde 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineAndOrXor.cpp
@@ -2458,6 +2458,9 @@ Instruction *InstCombinerImpl::visitAnd(BinaryOperator &I) {
   // If we are clearing the sign bit of a floating-point value, convert this to
   // fabs, then cast back to integer.
   //
+  // This is a generous interpretation for noimplicitfloat, this is not a true
+  // floating-point operation.
+  //
   // Assumes any IEEE-represented type has the sign bit in the high bit.
   // TODO: Unify with APInt matcher. This version allows undef unlike m_APInt
   Value *CastOp;
@@ -3739,6 +3742,31 @@ Instruction *InstCombinerImpl::visitOr(BinaryOperator &I) {
   if (Instruction *Res = foldBinOpOfDisplacedShifts(I))
     return Res;
 
+  // If we are setting the sign bit of a floating-point value, convert
+  // this to fneg(fabs), then cast back to integer.
+  //
+  // If the result isn't immediately cast back to a float, this will increase
+  // the number of instructions. This is still probably a better canonical form
+  // as it enables FP value tracking.
+  //
+  // Assumes any IEEE-represented type has the sign bit in the high bit.
+  //
+  // This is generous interpretation of noimplicitfloat, this is not a true
+  // floating-point operation.
+  Value *CastOp;
+  if (match(Op0, m_BitCast(m_Value(CastOp))) && match(Op1, m_SignMask()) &&
+      !Builder.GetInsertBlock()->getParent()->hasFnAttribute(
+          Attribute::NoImplicitFloat)) {
+    Type *EltTy = CastOp->getType()->getScalarType();
+    if (EltTy->isFloatingPointTy() && EltTy->isIEEE() &&
+        EltTy->getPrimitiveSizeInBits() ==
+        I.getType()->getScalarType()->getPrimitiveSizeInBits()) {
+      Value *FAbs = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, CastOp);
+      Value *FNegFAbs = Builder.CreateFNeg(FAbs);
+      return new BitCastInst(FNegFAbs, I.getType());
+    }
+  }
+
   return nullptr;
 }
 

diff  --git a/llvm/test/Transforms/InstCombine/fneg-fabs-as-int.ll b/llvm/test/Transforms/InstCombine/fneg-fabs-as-int.ll
index 908cb4dec9a4dd..9aa8d4944e39ab 100644
--- a/llvm/test/Transforms/InstCombine/fneg-fabs-as-int.ll
+++ b/llvm/test/Transforms/InstCombine/fneg-fabs-as-int.ll
@@ -29,10 +29,8 @@ define float @fneg_fabs_fabs_as_int_f32_and_or(float %val) {
 ; CHECK-LABEL: define float @fneg_fabs_fabs_as_int_f32_and_or
 ; CHECK-SAME: (float [[VAL:%.*]]) {
 ; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[VAL]])
-; CHECK-NEXT:    [[AND:%.*]] = bitcast float [[TMP1]] to i32
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[AND]], -2147483648
-; CHECK-NEXT:    [[FNEG_FABS:%.*]] = bitcast i32 [[OR]] to float
-; CHECK-NEXT:    ret float [[FNEG_FABS]]
+; CHECK-NEXT:    [[TMP2:%.*]] = fneg float [[TMP1]]
+; CHECK-NEXT:    ret float [[TMP2]]
 ;
   %bitcast = bitcast float %val to i32
   %and = and i32 %bitcast, 2147483647
@@ -44,10 +42,9 @@ define float @fneg_fabs_fabs_as_int_f32_and_or(float %val) {
 define float @fneg_fabs_as_int_f32_castback(float %val) {
 ; CHECK-LABEL: define float @fneg_fabs_as_int_f32_castback
 ; CHECK-SAME: (float [[VAL:%.*]]) {
-; CHECK-NEXT:    [[BITCAST:%.*]] = bitcast float [[VAL]] to i32
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[BITCAST]], -2147483648
-; CHECK-NEXT:    [[FNEG:%.*]] = bitcast i32 [[OR]] to float
-; CHECK-NEXT:    ret float [[FNEG]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[VAL]])
+; CHECK-NEXT:    [[TMP2:%.*]] = fneg float [[TMP1]]
+; CHECK-NEXT:    ret float [[TMP2]]
 ;
   %bitcast = bitcast float %val to i32
   %or = or i32 %bitcast, -2147483648
@@ -72,11 +69,10 @@ define float @not_fneg_fabs_as_int_f32_castback_wrongconst(float %val) {
 define float @fneg_fabs_as_int_f32_castback_multi_use(float %val, ptr %ptr) {
 ; CHECK-LABEL: define float @fneg_fabs_as_int_f32_castback_multi_use
 ; CHECK-SAME: (float [[VAL:%.*]], ptr [[PTR:%.*]]) {
-; CHECK-NEXT:    [[BITCAST:%.*]] = bitcast float [[VAL]] to i32
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[BITCAST]], -2147483648
-; CHECK-NEXT:    store i32 [[OR]], ptr [[PTR]], align 4
-; CHECK-NEXT:    [[FNEG:%.*]] = bitcast i32 [[OR]] to float
-; CHECK-NEXT:    ret float [[FNEG]]
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[VAL]])
+; CHECK-NEXT:    [[TMP2:%.*]] = fneg float [[TMP1]]
+; CHECK-NEXT:    store float [[TMP2]], ptr [[PTR]], align 4
+; CHECK-NEXT:    ret float [[TMP2]]
 ;
   %bitcast = bitcast float %val to i32
   %or = or i32 %bitcast, -2147483648
@@ -88,8 +84,9 @@ define float @fneg_fabs_as_int_f32_castback_multi_use(float %val, ptr %ptr) {
 define i64 @fneg_fabs_as_int_f64(double %x) {
 ; CHECK-LABEL: define i64 @fneg_fabs_as_int_f64
 ; CHECK-SAME: (double [[X:%.*]]) {
-; CHECK-NEXT:    [[BC:%.*]] = bitcast double [[X]] to i64
-; CHECK-NEXT:    [[OR:%.*]] = or i64 [[BC]], -9223372036854775808
+; CHECK-NEXT:    [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[X]])
+; CHECK-NEXT:    [[TMP2:%.*]] = fneg double [[TMP1]]
+; CHECK-NEXT:    [[OR:%.*]] = bitcast double [[TMP2]] to i64
 ; CHECK-NEXT:    ret i64 [[OR]]
 ;
   %bc = bitcast double %x to i64
@@ -100,8 +97,9 @@ define i64 @fneg_fabs_as_int_f64(double %x) {
 define <2 x i64> @fneg_fabs_as_int_v2f64(<2 x double> %x) {
 ; CHECK-LABEL: define <2 x i64> @fneg_fabs_as_int_v2f64
 ; CHECK-SAME: (<2 x double> [[X:%.*]]) {
-; CHECK-NEXT:    [[BC:%.*]] = bitcast <2 x double> [[X]] to <2 x i64>
-; CHECK-NEXT:    [[OR:%.*]] = or <2 x i64> [[BC]], <i64 -9223372036854775808, i64 -9223372036854775808>
+; CHECK-NEXT:    [[TMP1:%.*]] = call <2 x double> @llvm.fabs.v2f64(<2 x double> [[X]])
+; CHECK-NEXT:    [[TMP2:%.*]] = fneg <2 x double> [[TMP1]]
+; CHECK-NEXT:    [[OR:%.*]] = bitcast <2 x double> [[TMP2]] to <2 x i64>
 ; CHECK-NEXT:    ret <2 x i64> [[OR]]
 ;
   %bc = bitcast <2 x double> %x to <2 x i64>
@@ -112,8 +110,9 @@ define <2 x i64> @fneg_fabs_as_int_v2f64(<2 x double> %x) {
 define i64 @fneg_fabs_as_int_f64_swap(double %x) {
 ; CHECK-LABEL: define i64 @fneg_fabs_as_int_f64_swap
 ; CHECK-SAME: (double [[X:%.*]]) {
-; CHECK-NEXT:    [[BC:%.*]] = bitcast double [[X]] to i64
-; CHECK-NEXT:    [[OR:%.*]] = or i64 [[BC]], -9223372036854775808
+; CHECK-NEXT:    [[TMP1:%.*]] = call double @llvm.fabs.f64(double [[X]])
+; CHECK-NEXT:    [[TMP2:%.*]] = fneg double [[TMP1]]
+; CHECK-NEXT:    [[OR:%.*]] = bitcast double [[TMP2]] to i64
 ; CHECK-NEXT:    ret i64 [[OR]]
 ;
   %bc = bitcast double %x to i64
@@ -124,8 +123,9 @@ define i64 @fneg_fabs_as_int_f64_swap(double %x) {
 define i32 @fneg_fabs_as_int_f32(float %x) {
 ; CHECK-LABEL: define i32 @fneg_fabs_as_int_f32
 ; CHECK-SAME: (float [[X:%.*]]) {
-; CHECK-NEXT:    [[BC:%.*]] = bitcast float [[X]] to i32
-; CHECK-NEXT:    [[OR:%.*]] = or i32 [[BC]], -2147483648
+; CHECK-NEXT:    [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[X]])
+; CHECK-NEXT:    [[TMP2:%.*]] = fneg float [[TMP1]]
+; CHECK-NEXT:    [[OR:%.*]] = bitcast float [[TMP2]] to i32
 ; CHECK-NEXT:    ret i32 [[OR]]
 ;
   %bc = bitcast float %x to i32
@@ -136,8 +136,9 @@ define i32 @fneg_fabs_as_int_f32(float %x) {
 define <2 x i32> @fneg_fabs_as_int_v2f32(<2 x float> %x) {
 ; CHECK-LABEL: define <2 x i32> @fneg_fabs_as_int_v2f32
 ; CHECK-SAME: (<2 x float> [[X:%.*]]) {
-; CHECK-NEXT:    [[BC:%.*]] = bitcast <2 x float> [[X]] to <2 x i32>
-; CHECK-NEXT:    [[OR:%.*]] = or <2 x i32> [[BC]], <i32 -2147483648, i32 -2147483648>
+; CHECK-NEXT:    [[TMP1:%.*]] = call <2 x float> @llvm.fabs.v2f32(<2 x float> [[X]])
+; CHECK-NEXT:    [[TMP2:%.*]] = fneg <2 x float> [[TMP1]]
+; CHECK-NEXT:    [[OR:%.*]] = bitcast <2 x float> [[TMP2]] to <2 x i32>
 ; CHECK-NEXT:    ret <2 x i32> [[OR]]
 ;
   %bc = bitcast <2 x float> %x to <2 x i32>
@@ -160,8 +161,9 @@ define <2 x i32> @not_fneg_fabs_as_int_v2f32_nonsplat(<2 x float> %x) {
 define <3 x i32> @fneg_fabs_as_int_v3f32_undef(<3 x float> %x) {
 ; CHECK-LABEL: define <3 x i32> @fneg_fabs_as_int_v3f32_undef
 ; CHECK-SAME: (<3 x float> [[X:%.*]]) {
-; CHECK-NEXT:    [[BC:%.*]] = bitcast <3 x float> [[X]] to <3 x i32>
-; CHECK-NEXT:    [[OR:%.*]] = or <3 x i32> [[BC]], <i32 -2147483648, i32 undef, i32 -2147483648>
+; CHECK-NEXT:    [[TMP1:%.*]] = call <3 x float> @llvm.fabs.v3f32(<3 x float> [[X]])
+; CHECK-NEXT:    [[TMP2:%.*]] = fneg <3 x float> [[TMP1]]
+; CHECK-NEXT:    [[OR:%.*]] = bitcast <3 x float> [[TMP2]] to <3 x i32>
 ; CHECK-NEXT:    ret <3 x i32> [[OR]]
 ;
   %bc = bitcast <3 x float> %x to <3 x i32>
@@ -225,8 +227,9 @@ define i128 @fneg_fabs_as_int_fp128_f64_mask(fp128 %x) {
 define i128 @fneg_fabs_as_int_fp128_f128_mask(fp128 %x) {
 ; CHECK-LABEL: define i128 @fneg_fabs_as_int_fp128_f128_mask
 ; CHECK-SAME: (fp128 [[X:%.*]]) {
-; CHECK-NEXT:    [[BC:%.*]] = bitcast fp128 [[X]] to i128
-; CHECK-NEXT:    [[OR:%.*]] = or i128 [[BC]], -170141183460469231731687303715884105728
+; CHECK-NEXT:    [[TMP1:%.*]] = call fp128 @llvm.fabs.f128(fp128 [[X]])
+; CHECK-NEXT:    [[TMP2:%.*]] = fneg fp128 [[TMP1]]
+; CHECK-NEXT:    [[OR:%.*]] = bitcast fp128 [[TMP2]] to i128
 ; CHECK-NEXT:    ret i128 [[OR]]
 ;
   %bc = bitcast fp128 %x to i128
@@ -237,8 +240,9 @@ define i128 @fneg_fabs_as_int_fp128_f128_mask(fp128 %x) {
 define i16 @fneg_fabs_as_int_f16(half %x) {
 ; CHECK-LABEL: define i16 @fneg_fabs_as_int_f16
 ; CHECK-SAME: (half [[X:%.*]]) {
-; CHECK-NEXT:    [[BC:%.*]] = bitcast half [[X]] to i16
-; CHECK-NEXT:    [[OR:%.*]] = or i16 [[BC]], -32768
+; CHECK-NEXT:    [[TMP1:%.*]] = call half @llvm.fabs.f16(half [[X]])
+; CHECK-NEXT:    [[TMP2:%.*]] = fneg half [[TMP1]]
+; CHECK-NEXT:    [[OR:%.*]] = bitcast half [[TMP2]] to i16
 ; CHECK-NEXT:    ret i16 [[OR]]
 ;
   %bc = bitcast half %x to i16
@@ -249,8 +253,9 @@ define i16 @fneg_fabs_as_int_f16(half %x) {
 define <2 x i16> @fneg_fabs_as_int_v2f16(<2 x half> %x) {
 ; CHECK-LABEL: define <2 x i16> @fneg_fabs_as_int_v2f16
 ; CHECK-SAME: (<2 x half> [[X:%.*]]) {
-; CHECK-NEXT:    [[BC:%.*]] = bitcast <2 x half> [[X]] to <2 x i16>
-; CHECK-NEXT:    [[OR:%.*]] = or <2 x i16> [[BC]], <i16 -32768, i16 -32768>
+; CHECK-NEXT:    [[TMP1:%.*]] = call <2 x half> @llvm.fabs.v2f16(<2 x half> [[X]])
+; CHECK-NEXT:    [[TMP2:%.*]] = fneg <2 x half> [[TMP1]]
+; CHECK-NEXT:    [[OR:%.*]] = bitcast <2 x half> [[TMP2]] to <2 x i16>
 ; CHECK-NEXT:    ret <2 x i16> [[OR]]
 ;
   %bc = bitcast <2 x half> %x to <2 x i16>
@@ -261,8 +266,9 @@ define <2 x i16> @fneg_fabs_as_int_v2f16(<2 x half> %x) {
 define i16 @fneg_fabs_as_int_bf16(bfloat %x) {
 ; CHECK-LABEL: define i16 @fneg_fabs_as_int_bf16
 ; CHECK-SAME: (bfloat [[X:%.*]]) {
-; CHECK-NEXT:    [[BC:%.*]] = bitcast bfloat [[X]] to i16
-; CHECK-NEXT:    [[OR:%.*]] = or i16 [[BC]], -32768
+; CHECK-NEXT:    [[TMP1:%.*]] = call bfloat @llvm.fabs.bf16(bfloat [[X]])
+; CHECK-NEXT:    [[TMP2:%.*]] = fneg bfloat [[TMP1]]
+; CHECK-NEXT:    [[OR:%.*]] = bitcast bfloat [[TMP2]] to i16
 ; CHECK-NEXT:    ret i16 [[OR]]
 ;
   %bc = bitcast bfloat %x to i16
@@ -273,8 +279,9 @@ define i16 @fneg_fabs_as_int_bf16(bfloat %x) {
 define <2 x i16> @fneg_fabs_as_int_v2bf16(<2 x bfloat> %x) {
 ; CHECK-LABEL: define <2 x i16> @fneg_fabs_as_int_v2bf16
 ; CHECK-SAME: (<2 x bfloat> [[X:%.*]]) {
-; CHECK-NEXT:    [[BC:%.*]] = bitcast <2 x bfloat> [[X]] to <2 x i16>
-; CHECK-NEXT:    [[OR:%.*]] = or <2 x i16> [[BC]], <i16 -32768, i16 -32768>
+; CHECK-NEXT:    [[TMP1:%.*]] = call <2 x bfloat> @llvm.fabs.v2bf16(<2 x bfloat> [[X]])
+; CHECK-NEXT:    [[TMP2:%.*]] = fneg <2 x bfloat> [[TMP1]]
+; CHECK-NEXT:    [[OR:%.*]] = bitcast <2 x bfloat> [[TMP2]] to <2 x i16>
 ; CHECK-NEXT:    ret <2 x i16> [[OR]]
 ;
   %bc = bitcast <2 x bfloat> %x to <2 x i16>
@@ -285,8 +292,9 @@ define <2 x i16> @fneg_fabs_as_int_v2bf16(<2 x bfloat> %x) {
 define i80 @fneg_fabs_as_int_x86_fp80_f64_mask(x86_fp80 %x) {
 ; CHECK-LABEL: define i80 @fneg_fabs_as_int_x86_fp80_f64_mask
 ; CHECK-SAME: (x86_fp80 [[X:%.*]]) {
-; CHECK-NEXT:    [[BC:%.*]] = bitcast x86_fp80 [[X]] to i80
-; CHECK-NEXT:    [[OR:%.*]] = or i80 [[BC]], -604462909807314587353088
+; CHECK-NEXT:    [[TMP1:%.*]] = call x86_fp80 @llvm.fabs.f80(x86_fp80 [[X]])
+; CHECK-NEXT:    [[TMP2:%.*]] = fneg x86_fp80 [[TMP1]]
+; CHECK-NEXT:    [[OR:%.*]] = bitcast x86_fp80 [[TMP2]] to i80
 ; CHECK-NEXT:    ret i80 [[OR]]
 ;
   %bc = bitcast x86_fp80 %x to i80


        


More information about the llvm-commits mailing list