From: Craig Topper Date: Thu, 23 Feb 2023 17:05:59 +0000 (-0800) Subject: [Clang] Teach buildFMulAdd to peek through fneg to find fmul. X-Git-Tag: upstream/17.0.6~16655 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=83cd4bea015feb5729871832784c424b0743a803;p=platform%2Fupstream%2Fllvm.git [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 --- diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index a0dcb97..2243c75 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(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(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(op.LHS)) { + if (auto *LHSBinOp = dyn_cast(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(op.LHS)->eraseFromParent(); + return buildFMulAdd(LHSBinOp, op.RHS, CGF, Builder, NegLHS, isSub); + } } - if (auto *RHSBinOp = dyn_cast(op.RHS)) { + if (auto *RHSBinOp = dyn_cast(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(op.RHS)->eraseFromParent(); + return buildFMulAdd(RHSBinOp, op.LHS, CGF, Builder, isSub ^ NegRHS, false); + } } - if (auto *LHSBinOp = dyn_cast(op.LHS)) { + if (auto *LHSBinOp = dyn_cast(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(op.LHS)->eraseFromParent(); + return buildFMulAdd(LHSBinOp, op.RHS, CGF, Builder, NegLHS, isSub); + } } - if (auto *RHSBinOp = dyn_cast(op.RHS)) { + if (auto *RHSBinOp = dyn_cast(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(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 d817cce..cccfd8b 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 805cc5d..a628d7c 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; +}