[clang] 83cd4be - [Clang] Teach buildFMulAdd to peek through fneg to find fmul.
Craig Topper via cfe-commits
cfe-commits at lists.llvm.org
Thu Feb 23 09:06:09 PST 2023
Author: Craig Topper
Date: 2023-02-23T09:05:59-08:00
New Revision: 83cd4bea015feb5729871832784c424b0743a803
URL: https://github.com/llvm/llvm-project/commit/83cd4bea015feb5729871832784c424b0743a803
DIFF: https://github.com/llvm/llvm-project/commit/83cd4bea015feb5729871832784c424b0743a803.diff
LOG: [Clang] Teach buildFMulAdd to peek through fneg to find fmul.
Allows us to handle expressions like -(a * b) + c
Based on the examples from D144366 that gcc seems to get.
Reviewed By: kpn
Differential Revision: https://reviews.llvm.org/D144447
Added:
Modified:
clang/lib/CodeGen/CGExprScalar.cpp
clang/test/CodeGen/constrained-math-builtins.c
clang/test/CodeGen/fp-contract-pragma.cpp
Removed:
################################################################################
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index a0dcb978b1ac..2243c75ed260 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -3734,8 +3734,6 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF,
static Value* buildFMulAdd(llvm::Instruction *MulOp, Value *Addend,
const CodeGenFunction &CGF, CGBuilderTy &Builder,
bool negMul, bool negAdd) {
- assert(!(negMul && negAdd) && "Only one of negMul and negAdd should be set.");
-
Value *MulOp0 = MulOp->getOperand(0);
Value *MulOp1 = MulOp->getOperand(1);
if (negMul)
@@ -3780,31 +3778,70 @@ static Value* tryEmitFMulAdd(const BinOpInfo &op,
if (!op.FPFeatures.allowFPContractWithinStatement())
return nullptr;
+ Value *LHS = op.LHS;
+ Value *RHS = op.RHS;
+
+ // Peek through fneg to look for fmul. Make sure fneg has no users, and that
+ // it is the only use of its operand.
+ bool NegLHS = false;
+ if (auto *LHSUnOp = dyn_cast<llvm::UnaryOperator>(LHS)) {
+ if (LHSUnOp->getOpcode() == llvm::Instruction::FNeg &&
+ LHSUnOp->use_empty() && LHSUnOp->getOperand(0)->hasOneUse()) {
+ LHS = LHSUnOp->getOperand(0);
+ NegLHS = true;
+ }
+ }
+
+ bool NegRHS = false;
+ if (auto *RHSUnOp = dyn_cast<llvm::UnaryOperator>(RHS)) {
+ if (RHSUnOp->getOpcode() == llvm::Instruction::FNeg &&
+ RHSUnOp->use_empty() && RHSUnOp->getOperand(0)->hasOneUse()) {
+ RHS = RHSUnOp->getOperand(0);
+ NegRHS = true;
+ }
+ }
+
// We have a potentially fusable op. Look for a mul on one of the operands.
// Also, make sure that the mul result isn't used directly. In that case,
// there's no point creating a muladd operation.
- if (auto *LHSBinOp = dyn_cast<llvm::BinaryOperator>(op.LHS)) {
+ if (auto *LHSBinOp = dyn_cast<llvm::BinaryOperator>(LHS)) {
if (LHSBinOp->getOpcode() == llvm::Instruction::FMul &&
- LHSBinOp->use_empty())
- return buildFMulAdd(LHSBinOp, op.RHS, CGF, Builder, false, isSub);
+ (LHSBinOp->use_empty() || NegLHS)) {
+ // If we looked through fneg, erase it.
+ if (NegLHS)
+ cast<llvm::Instruction>(op.LHS)->eraseFromParent();
+ return buildFMulAdd(LHSBinOp, op.RHS, CGF, Builder, NegLHS, isSub);
+ }
}
- if (auto *RHSBinOp = dyn_cast<llvm::BinaryOperator>(op.RHS)) {
+ if (auto *RHSBinOp = dyn_cast<llvm::BinaryOperator>(RHS)) {
if (RHSBinOp->getOpcode() == llvm::Instruction::FMul &&
- RHSBinOp->use_empty())
- return buildFMulAdd(RHSBinOp, op.LHS, CGF, Builder, isSub, false);
+ (RHSBinOp->use_empty() || NegRHS)) {
+ // If we looked through fneg, erase it.
+ if (NegRHS)
+ cast<llvm::Instruction>(op.RHS)->eraseFromParent();
+ return buildFMulAdd(RHSBinOp, op.LHS, CGF, Builder, isSub ^ NegRHS, false);
+ }
}
- if (auto *LHSBinOp = dyn_cast<llvm::CallBase>(op.LHS)) {
+ if (auto *LHSBinOp = dyn_cast<llvm::CallBase>(LHS)) {
if (LHSBinOp->getIntrinsicID() ==
llvm::Intrinsic::experimental_constrained_fmul &&
- LHSBinOp->use_empty())
- return buildFMulAdd(LHSBinOp, op.RHS, CGF, Builder, false, isSub);
+ (LHSBinOp->use_empty() || NegLHS)) {
+ // If we looked through fneg, erase it.
+ if (NegLHS)
+ cast<llvm::Instruction>(op.LHS)->eraseFromParent();
+ return buildFMulAdd(LHSBinOp, op.RHS, CGF, Builder, NegLHS, isSub);
+ }
}
- if (auto *RHSBinOp = dyn_cast<llvm::CallBase>(op.RHS)) {
+ if (auto *RHSBinOp = dyn_cast<llvm::CallBase>(RHS)) {
if (RHSBinOp->getIntrinsicID() ==
llvm::Intrinsic::experimental_constrained_fmul &&
- RHSBinOp->use_empty())
- return buildFMulAdd(RHSBinOp, op.LHS, CGF, Builder, isSub, false);
+ (RHSBinOp->use_empty() || NegRHS)) {
+ // If we looked through fneg, erase it.
+ if (NegRHS)
+ cast<llvm::Instruction>(op.RHS)->eraseFromParent();
+ return buildFMulAdd(RHSBinOp, op.LHS, CGF, Builder, isSub ^ NegRHS, false);
+ }
}
return nullptr;
diff --git a/clang/test/CodeGen/constrained-math-builtins.c b/clang/test/CodeGen/constrained-math-builtins.c
index d817cce33679..cccfd8bc7746 100644
--- a/clang/test/CodeGen/constrained-math-builtins.c
+++ b/clang/test/CodeGen/constrained-math-builtins.c
@@ -300,10 +300,17 @@ void bar(float f) {
f * f + f;
(double)f * f - f;
(long double)-f * f + f;
+ -(f * f) - f;
+ f + -(f * f);
// CHECK: call float @llvm.experimental.constrained.fmuladd.f32(float %{{.*}}, float %{{.*}}, float %{{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
// CHECK: fneg
// CHECK: call double @llvm.experimental.constrained.fmuladd.f64(double %{{.*}}, double %{{.*}}, double %{{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
// CHECK: fneg
// CHECK: call x86_fp80 @llvm.experimental.constrained.fmuladd.f80(x86_fp80 %{{.*}}, x86_fp80 %{{.*}}, x86_fp80 %{{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
+ // CHECK: fneg
+ // CHECK: fneg
+ // CHECK: call float @llvm.experimental.constrained.fmuladd.f32(float %{{.*}}, float %{{.*}}, float %{{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
+ // CHECK: fneg
+ // CHECK: call float @llvm.experimental.constrained.fmuladd.f32(float %{{.*}}, float %{{.*}}, float %{{.*}}, metadata !"round.tonearest", metadata !"fpexcept.strict")
};
diff --git a/clang/test/CodeGen/fp-contract-pragma.cpp b/clang/test/CodeGen/fp-contract-pragma.cpp
index 805cc5d9c299..a628d7c1bd22 100644
--- a/clang/test/CodeGen/fp-contract-pragma.cpp
+++ b/clang/test/CodeGen/fp-contract-pragma.cpp
@@ -89,3 +89,54 @@ float fp_contract_9(float a, float b, float c) {
#pragma STDC FP_CONTRACT ON
return c - a * b;
}
+
+float fp_contract_10(float a, float b, float c) {
+// CHECK: _Z14fp_contract_10fff
+// CHECK: fneg float %a
+// CHECK: tail call float @llvm.fmuladd
+ #pragma STDC FP_CONTRACT ON
+ return -(a * b) + c;
+}
+
+float fp_contract_11(float a, float b, float c) {
+// CHECK: _Z14fp_contract_11fff
+// CHECK: fneg float %a
+// CHECK: fneg float %c
+// CHECK: tail call float @llvm.fmuladd
+ #pragma STDC FP_CONTRACT ON
+ return -(a * b) - c;
+}
+
+float fp_contract_12(float a, float b, float c) {
+// CHECK: _Z14fp_contract_12fff
+// CHECK: fneg float %a
+// CHECK: tail call float @llvm.fmuladd
+ #pragma STDC FP_CONTRACT ON
+ return c + -(a * b);
+}
+
+float fp_contract_13(float a, float b, float c) {
+// CHECK: _Z14fp_contract_13fff
+// CHECK-NOT: fneg float %a
+// CHECK: tail call float @llvm.fmuladd
+ #pragma STDC FP_CONTRACT ON
+ return c - -(a * b);
+}
+
+float fp_contract_14(float a, float b, float c) {
+// CHECK: _Z14fp_contract_14fff
+// CHECK: %[[M:.+]] = fmul float %a, %b
+// CHECK-NEXT: %add = fsub float %c, %[[M]]
+ #pragma STDC FP_CONTRACT ON
+ float d;
+ return (d = -(a * b)) + c;
+}
+
+float fp_contract_15(float a, float b, float c) {
+// CHECK: _Z14fp_contract_15fff
+// CHECK: %[[M:.+]] = fmul float %a, %b
+// CHECK-NEXT: %add = fsub float %c, %[[M]]
+ #pragma STDC FP_CONTRACT ON
+ float d;
+ return -(d = (a * b)) + c;
+}
More information about the cfe-commits
mailing list