diff --git a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h --- a/llvm/lib/Transforms/InstCombine/InstCombineInternal.h +++ b/llvm/lib/Transforms/InstCombine/InstCombineInternal.h @@ -635,6 +635,7 @@ Value *getSelectCondition(Value *A, Value *B); Instruction *foldIntrinsicWithOverflowCommon(IntrinsicInst *II); + Instruction *foldFPSignBitOps(BinaryOperator &I); public: /// Inserts an instruction \p New before instruction \p Old diff --git a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineMulDivRem.cpp @@ -402,7 +402,7 @@ return Changed ? &I : nullptr; } -static Instruction *foldFPSignBitOps(BinaryOperator &I) { +Instruction *InstCombiner::foldFPSignBitOps(BinaryOperator &I) { BinaryOperator::BinaryOps Opcode = I.getOpcode(); assert((Opcode == Instruction::FMul || Opcode == Instruction::FDiv) && "Expected fmul or fdiv"); @@ -420,6 +420,19 @@ if (Op0 == Op1 && match(Op0, m_Intrinsic(m_Value(X)))) return BinaryOperator::CreateWithCopiedFlags(Opcode, X, X, &I); + // fabs(X) * fabs(Y) --> fabs(X * Y) + // fabs(X) / fabs(Y) --> fabs(X / Y) + if (match(Op0, m_Intrinsic(m_Value(X))) && + match(Op1, m_Intrinsic(m_Value(Y))) && + (Op0->hasOneUse() || Op1->hasOneUse())) { + IRBuilder<>::FastMathFlagGuard FMFGuard(Builder); + Builder.setFastMathFlags(I.getFastMathFlags()); + Value *XY = Builder.CreateBinOp(Opcode, X, Y); + Value *Fabs = Builder.CreateUnaryIntrinsic(Intrinsic::fabs, XY); + Fabs->takeName(&I); + return replaceInstUsesWith(I, Fabs); + } + return nullptr; } diff --git a/llvm/test/Transforms/InstCombine/fdiv.ll b/llvm/test/Transforms/InstCombine/fdiv.ll --- a/llvm/test/Transforms/InstCombine/fdiv.ll +++ b/llvm/test/Transforms/InstCombine/fdiv.ll @@ -598,9 +598,8 @@ define float @fabs_fabs(float %x, float %y) { ; CHECK-LABEL: @fabs_fabs( -; CHECK-NEXT: [[X_FABS:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]]) -; CHECK-NEXT: [[Y_FABS:%.*]] = call float @llvm.fabs.f32(float [[Y:%.*]]) -; CHECK-NEXT: [[R:%.*]] = fdiv float [[X_FABS]], [[Y_FABS]] +; CHECK-NEXT: [[TMP1:%.*]] = fdiv float [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[R:%.*]] = call float @llvm.fabs.f32(float [[TMP1]]) ; CHECK-NEXT: ret float [[R]] ; %x.fabs = call float @llvm.fabs.f32(float %x) @@ -613,8 +612,8 @@ ; CHECK-LABEL: @fabs_fabs_extra_use1( ; CHECK-NEXT: [[X_FABS:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]]) ; CHECK-NEXT: call void @use_f32(float [[X_FABS]]) -; CHECK-NEXT: [[Y_FABS:%.*]] = call float @llvm.fabs.f32(float [[Y:%.*]]) -; CHECK-NEXT: [[R:%.*]] = fdiv ninf float [[X_FABS]], [[Y_FABS]] +; CHECK-NEXT: [[TMP1:%.*]] = fdiv ninf float [[X]], [[Y:%.*]] +; CHECK-NEXT: [[R:%.*]] = call ninf float @llvm.fabs.f32(float [[TMP1]]) ; CHECK-NEXT: ret float [[R]] ; %x.fabs = call float @llvm.fabs.f32(float %x) @@ -626,10 +625,10 @@ define float @fabs_fabs_extra_use2(float %x, float %y) { ; CHECK-LABEL: @fabs_fabs_extra_use2( -; CHECK-NEXT: [[X_FABS:%.*]] = call fast float @llvm.fabs.f32(float [[X:%.*]]) ; CHECK-NEXT: [[Y_FABS:%.*]] = call fast float @llvm.fabs.f32(float [[Y:%.*]]) ; CHECK-NEXT: call void @use_f32(float [[Y_FABS]]) -; CHECK-NEXT: [[R:%.*]] = fdiv reassoc ninf float [[X_FABS]], [[Y_FABS]] +; CHECK-NEXT: [[TMP1:%.*]] = fdiv reassoc ninf float [[X:%.*]], [[Y]] +; CHECK-NEXT: [[R:%.*]] = call reassoc ninf float @llvm.fabs.f32(float [[TMP1]]) ; CHECK-NEXT: ret float [[R]] ; %x.fabs = call fast float @llvm.fabs.f32(float %x) @@ -639,6 +638,8 @@ ret float %r } +; negative test - don't create an extra instruction + define float @fabs_fabs_extra_use3(float %x, float %y) { ; CHECK-LABEL: @fabs_fabs_extra_use3( ; CHECK-NEXT: [[X_FABS:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]]) diff --git a/llvm/test/Transforms/InstCombine/fmul.ll b/llvm/test/Transforms/InstCombine/fmul.ll --- a/llvm/test/Transforms/InstCombine/fmul.ll +++ b/llvm/test/Transforms/InstCombine/fmul.ll @@ -482,9 +482,8 @@ define float @fabs_fabs(float %x, float %y) { ; CHECK-LABEL: @fabs_fabs( -; CHECK-NEXT: [[X_FABS:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]]) -; CHECK-NEXT: [[Y_FABS:%.*]] = call float @llvm.fabs.f32(float [[Y:%.*]]) -; CHECK-NEXT: [[MUL:%.*]] = fmul float [[X_FABS]], [[Y_FABS]] +; CHECK-NEXT: [[TMP1:%.*]] = fmul float [[X:%.*]], [[Y:%.*]] +; CHECK-NEXT: [[MUL:%.*]] = call float @llvm.fabs.f32(float [[TMP1]]) ; CHECK-NEXT: ret float [[MUL]] ; %x.fabs = call float @llvm.fabs.f32(float %x) @@ -497,8 +496,8 @@ ; CHECK-LABEL: @fabs_fabs_extra_use1( ; CHECK-NEXT: [[X_FABS:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]]) ; CHECK-NEXT: call void @use_f32(float [[X_FABS]]) -; CHECK-NEXT: [[Y_FABS:%.*]] = call float @llvm.fabs.f32(float [[Y:%.*]]) -; CHECK-NEXT: [[MUL:%.*]] = fmul ninf float [[X_FABS]], [[Y_FABS]] +; CHECK-NEXT: [[TMP1:%.*]] = fmul ninf float [[X]], [[Y:%.*]] +; CHECK-NEXT: [[MUL:%.*]] = call ninf float @llvm.fabs.f32(float [[TMP1]]) ; CHECK-NEXT: ret float [[MUL]] ; %x.fabs = call float @llvm.fabs.f32(float %x) @@ -510,10 +509,10 @@ define float @fabs_fabs_extra_use2(float %x, float %y) { ; CHECK-LABEL: @fabs_fabs_extra_use2( -; CHECK-NEXT: [[X_FABS:%.*]] = call fast float @llvm.fabs.f32(float [[X:%.*]]) ; CHECK-NEXT: [[Y_FABS:%.*]] = call fast float @llvm.fabs.f32(float [[Y:%.*]]) ; CHECK-NEXT: call void @use_f32(float [[Y_FABS]]) -; CHECK-NEXT: [[MUL:%.*]] = fmul reassoc ninf float [[X_FABS]], [[Y_FABS]] +; CHECK-NEXT: [[TMP1:%.*]] = fmul reassoc ninf float [[X:%.*]], [[Y]] +; CHECK-NEXT: [[MUL:%.*]] = call reassoc ninf float @llvm.fabs.f32(float [[TMP1]]) ; CHECK-NEXT: ret float [[MUL]] ; %x.fabs = call fast float @llvm.fabs.f32(float %x) @@ -523,6 +522,8 @@ ret float %mul } +; negative test - don't create an extra instruction + define float @fabs_fabs_extra_use3(float %x, float %y) { ; CHECK-LABEL: @fabs_fabs_extra_use3( ; CHECK-NEXT: [[X_FABS:%.*]] = call float @llvm.fabs.f32(float [[X:%.*]])