diff --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def index 0428b70c60206..0564a012abe63 100644 --- a/clang/include/clang/Basic/LangOptions.def +++ b/clang/include/clang/Basic/LangOptions.def @@ -220,7 +220,7 @@ BENIGN_LANGOPT(NoSignedZero , 1, 0, "Permit Floating Point optimization wit BENIGN_LANGOPT(AllowRecip , 1, 0, "Permit Floating Point reciprocal") BENIGN_LANGOPT(ApproxFunc , 1, 0, "Permit Floating Point approximation") -ENUM_LANGOPT(ComplexRange, ComplexRangeKind, 2, CX_Full, "Enable use of range reduction for complex arithmetics.") +ENUM_LANGOPT(ComplexRange, ComplexRangeKind, 2, CX_None, "Enable use of range reduction for complex arithmetics.") BENIGN_LANGOPT(ObjCGCBitmapPrint , 1, 0, "printing of GC's bitmap layout for __weak/__strong ivars") diff --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h index 9f986fce2d441..4d59bcd44cef3 100644 --- a/clang/include/clang/Basic/LangOptions.h +++ b/clang/include/clang/Basic/LangOptions.h @@ -392,7 +392,7 @@ class LangOptions : public LangOptionsBase { IncompleteOnly = 3, }; - enum ComplexRangeKind { CX_Full, CX_Limited, CX_Fortran }; + enum ComplexRangeKind { CX_Full, CX_Limited, CX_Fortran, CX_None }; public: /// The used language standard. diff --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp index e532794b71bdb..839fe16cd7725 100644 --- a/clang/lib/CodeGen/CGExprComplex.cpp +++ b/clang/lib/CodeGen/CGExprComplex.cpp @@ -892,6 +892,9 @@ ComplexPairTy ComplexExprEmitter::EmitRangeReductionDiv(llvm::Value *LHSr, llvm::Value *LHSi, llvm::Value *RHSr, llvm::Value *RHSi) { + // FIXME: This could eventually be replaced by an LLVM intrinsic to + // avoid this long IR sequence. + // (a + ib) / (c + id) = (e + if) llvm::Value *FAbsRHSr = EmitllvmFAbs(CGF, RHSr); // |c| llvm::Value *FAbsRHSi = EmitllvmFAbs(CGF, RHSi); // |d| @@ -936,7 +939,7 @@ ComplexPairTy ComplexExprEmitter::EmitRangeReductionDiv(llvm::Value *LHSr, llvm::Value *RC = Builder.CreateFMul(CdD, RHSr); // rc llvm::Value *DpRC = Builder.CreateFAdd(RHSi, RC); // tmp=d+rc - llvm::Value *T7 = Builder.CreateFMul(LHSr, RC); // ar + llvm::Value *T7 = Builder.CreateFMul(LHSr, CdD); // ar llvm::Value *T8 = Builder.CreateFAdd(T7, LHSi); // ar+b llvm::Value *DSTFr = Builder.CreateFDiv(T8, DpRC); // (ar+b)/tmp @@ -978,7 +981,10 @@ ComplexPairTy ComplexExprEmitter::EmitBinDiv(const BinOpInfo &Op) { return EmitRangeReductionDiv(LHSr, LHSi, RHSr, RHSi); else if (Op.FPFeatures.getComplexRange() == LangOptions::CX_Limited) return EmitAlgebraicDiv(LHSr, LHSi, RHSr, RHSi); - else if (!CGF.getLangOpts().FastMath) { + else if (!CGF.getLangOpts().FastMath || + // '-ffast-math' is used in the command line but followed by an + // '-fno-cx-limited-range'. + Op.FPFeatures.getComplexRange() == LangOptions::CX_Full) { LHSi = OrigLHSi; // If we have a complex operand on the RHS and FastMath is not allowed, we // delegate to a libcall to handle all of the complexities and minimize diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 997ec2d491d02..76d64f9567cc1 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -2712,9 +2712,22 @@ static void EmitComplexRangeDiag(const Driver &D, << EnumComplexRangeToStr(Range1) << EnumComplexRangeToStr(Range2); } -static std::string RenderComplexRangeOption(std::string Range) { +static std::string +RenderComplexRangeOption(LangOptions::ComplexRangeKind Range) { std::string ComplexRangeStr = "-complex-range="; - ComplexRangeStr += Range; + switch (Range) { + case LangOptions::ComplexRangeKind::CX_Full: + ComplexRangeStr += "full"; + break; + case LangOptions::ComplexRangeKind::CX_Limited: + ComplexRangeStr += "limited"; + break; + case LangOptions::ComplexRangeKind::CX_Fortran: + ComplexRangeStr += "fortran"; + break; + default: + assert("Unexpected range option"); + } return ComplexRangeStr; } @@ -2764,7 +2777,8 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D, bool StrictFPModel = false; StringRef Float16ExcessPrecision = ""; StringRef BFloat16ExcessPrecision = ""; - LangOptions::ComplexRangeKind Range = LangOptions::ComplexRangeKind::CX_Full; + LangOptions::ComplexRangeKind Range = LangOptions::ComplexRangeKind::CX_None; + std::string ComplexRangeStr = ""; if (const Arg *A = Args.getLastArg(options::OPT_flimited_precision_EQ)) { CmdArgs.push_back("-mlimit-float-precision"); @@ -2780,23 +2794,19 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D, case options::OPT_fcx_limited_range: { EmitComplexRangeDiag(D, Range, LangOptions::ComplexRangeKind::CX_Limited); Range = LangOptions::ComplexRangeKind::CX_Limited; - std::string ComplexRangeStr = RenderComplexRangeOption("limited"); - if (!ComplexRangeStr.empty()) - CmdArgs.push_back(Args.MakeArgString(ComplexRangeStr)); break; } case options::OPT_fno_cx_limited_range: + EmitComplexRangeDiag(D, Range, LangOptions::ComplexRangeKind::CX_Full); Range = LangOptions::ComplexRangeKind::CX_Full; break; case options::OPT_fcx_fortran_rules: { EmitComplexRangeDiag(D, Range, LangOptions::ComplexRangeKind::CX_Fortran); Range = LangOptions::ComplexRangeKind::CX_Fortran; - std::string ComplexRangeStr = RenderComplexRangeOption("fortran"); - if (!ComplexRangeStr.empty()) - CmdArgs.push_back(Args.MakeArgString(ComplexRangeStr)); break; } case options::OPT_fno_cx_fortran_rules: + EmitComplexRangeDiag(D, Range, LangOptions::ComplexRangeKind::CX_Full); Range = LangOptions::ComplexRangeKind::CX_Full; break; case options::OPT_ffp_model_EQ: { @@ -3068,9 +3078,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D, SeenUnsafeMathModeOption = true; // ffast-math enables fortran rules for complex multiplication and // division. - std::string ComplexRangeStr = RenderComplexRangeOption("limited"); - if (!ComplexRangeStr.empty()) - CmdArgs.push_back(Args.MakeArgString(ComplexRangeStr)); + Range = LangOptions::ComplexRangeKind::CX_Limited; break; } case options::OPT_fno_fast_math: @@ -3227,6 +3235,10 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D, options::OPT_fstrict_float_cast_overflow, false)) CmdArgs.push_back("-fno-strict-float-cast-overflow"); + if (Range != LangOptions::ComplexRangeKind::CX_None) + ComplexRangeStr = RenderComplexRangeOption(Range); + if (!ComplexRangeStr.empty()) + CmdArgs.push_back(Args.MakeArgString(ComplexRangeStr)); if (Args.hasArg(options::OPT_fcx_limited_range)) CmdArgs.push_back("-fcx-limited-range"); if (Args.hasArg(options::OPT_fcx_fortran_rules)) diff --git a/clang/test/CodeGen/cx-complex-range.c b/clang/test/CodeGen/cx-complex-range.c index 8368fa611335c..2d8507c710f20 100644 --- a/clang/test/CodeGen/cx-complex-range.c +++ b/clang/test/CodeGen/cx-complex-range.c @@ -15,9 +15,25 @@ // RUN: -ffast-math -complex-range=limited -emit-llvm -o - %s \ // RUN: | FileCheck %s --check-prefix=LMTD-FAST +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu \ +// RUN: -ffast-math -complex-range=full -emit-llvm -o - %s \ +// RUN: | FileCheck %s --check-prefix=FULL + // RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown \ // RUN: -fno-cx-fortran-rules -o - | FileCheck %s --check-prefix=FULL +// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown \ +// RUN: -fcx-limited-range -fno-cx-limited-range -o - \ +// RUN: | FileCheck %s --check-prefix=FULL + +// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown \ +// RUN: -fno-cx-limited-range -fcx-limited-range -o - \ +// RUN: | FileCheck %s --check-prefix=FULL + +// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown \ +// RUN: -fno-cx-fortran-rules -fcx-fortran-rules -o - \ +// RUN: | FileCheck %s --check-prefix=FULL + _Complex float div(_Complex float a, _Complex float b) { // LABEL: define {{.*}} @div( // FULL: call {{.*}} @__divsc3 diff --git a/clang/test/CodeGen/smiths-complex-div.c b/clang/test/CodeGen/smiths-complex-div.c new file mode 100644 index 0000000000000..75775675c9238 --- /dev/null +++ b/clang/test/CodeGen/smiths-complex-div.c @@ -0,0 +1,59 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 4 +// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown \ +// RUN: -complex-range=fortran -o - | FileCheck %s --check-prefix=FRTRN + +// FRTRN-LABEL: define dso_local <2 x float> @div( +// FRTRN-SAME: <2 x float> noundef [[A_COERCE:%.*]], <2 x float> noundef [[B_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { +// FRTRN-NEXT: entry: +// FRTRN-NEXT: [[RETVAL:%.*]] = alloca { float, float }, align 4 +// FRTRN-NEXT: [[A:%.*]] = alloca { float, float }, align 4 +// FRTRN-NEXT: [[B:%.*]] = alloca { float, float }, align 4 +// FRTRN-NEXT: store <2 x float> [[A_COERCE]], ptr [[A]], align 4 +// FRTRN-NEXT: store <2 x float> [[B_COERCE]], ptr [[B]], align 4 +// FRTRN-NEXT: [[A_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[A]], i32 0, i32 0 +// FRTRN-NEXT: [[A_REAL:%.*]] = load float, ptr [[A_REALP]], align 4 +// FRTRN-NEXT: [[A_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[A]], i32 0, i32 1 +// FRTRN-NEXT: [[A_IMAG:%.*]] = load float, ptr [[A_IMAGP]], align 4 +// FRTRN-NEXT: [[B_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[B]], i32 0, i32 0 +// FRTRN-NEXT: [[B_REAL:%.*]] = load float, ptr [[B_REALP]], align 4 +// FRTRN-NEXT: [[B_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[B]], i32 0, i32 1 +// FRTRN-NEXT: [[B_IMAG:%.*]] = load float, ptr [[B_IMAGP]], align 4 +// FRTRN-NEXT: [[TMP0:%.*]] = call float @llvm.fabs.f32(float [[B_REAL]]) +// FRTRN-NEXT: [[TMP1:%.*]] = call float @llvm.fabs.f32(float [[B_IMAG]]) +// FRTRN-NEXT: [[ABS_CMP:%.*]] = fcmp ugt float [[TMP0]], [[TMP1]] +// FRTRN-NEXT: br i1 [[ABS_CMP]], label [[ABS_RHSR_GREATER_OR_EQUAL_ABS_RHSI:%.*]], label [[ABS_RHSR_LESS_THAN_ABS_RHSI:%.*]] +// FRTRN: abs_rhsr_greater_or_equal_abs_rhsi: +// FRTRN-NEXT: [[TMP2:%.*]] = fdiv float [[B_IMAG]], [[B_REAL]] +// FRTRN-NEXT: [[TMP3:%.*]] = fmul float [[TMP2]], [[B_IMAG]] +// FRTRN-NEXT: [[TMP4:%.*]] = fadd float [[B_REAL]], [[TMP3]] +// FRTRN-NEXT: [[TMP5:%.*]] = fmul float [[A_IMAG]], [[TMP2]] +// FRTRN-NEXT: [[TMP6:%.*]] = fadd float [[A_REAL]], [[TMP5]] +// FRTRN-NEXT: [[TMP7:%.*]] = fdiv float [[TMP6]], [[TMP4]] +// FRTRN-NEXT: [[TMP8:%.*]] = fmul float [[A_REAL]], [[TMP2]] +// FRTRN-NEXT: [[TMP9:%.*]] = fsub float [[A_IMAG]], [[TMP8]] +// FRTRN-NEXT: [[TMP10:%.*]] = fdiv float [[TMP9]], [[TMP4]] +// FRTRN-NEXT: br label [[COMPLEX_DIV:%.*]] +// FRTRN: abs_rhsr_less_than_abs_rhsi: +// FRTRN-NEXT: [[TMP11:%.*]] = fdiv float [[B_REAL]], [[B_IMAG]] +// FRTRN-NEXT: [[TMP12:%.*]] = fmul float [[TMP11]], [[B_REAL]] +// FRTRN-NEXT: [[TMP13:%.*]] = fadd float [[B_IMAG]], [[TMP12]] +// FRTRN-NEXT: [[TMP14:%.*]] = fmul float [[A_REAL]], [[TMP11]] +// FRTRN-NEXT: [[TMP15:%.*]] = fadd float [[TMP14]], [[A_IMAG]] +// FRTRN-NEXT: [[TMP16:%.*]] = fdiv float [[TMP15]], [[TMP13]] +// FRTRN-NEXT: [[TMP17:%.*]] = fmul float [[A_IMAG]], [[TMP11]] +// FRTRN-NEXT: [[TMP18:%.*]] = fsub float [[TMP17]], [[A_REAL]] +// FRTRN-NEXT: [[TMP19:%.*]] = fdiv float [[TMP18]], [[TMP13]] +// FRTRN-NEXT: br label [[COMPLEX_DIV]] +// FRTRN: complex_div: +// FRTRN-NEXT: [[TMP20:%.*]] = phi float [ [[TMP7]], [[ABS_RHSR_GREATER_OR_EQUAL_ABS_RHSI]] ], [ [[TMP16]], [[ABS_RHSR_LESS_THAN_ABS_RHSI]] ] +// FRTRN-NEXT: [[TMP21:%.*]] = phi float [ [[TMP10]], [[ABS_RHSR_GREATER_OR_EQUAL_ABS_RHSI]] ], [ [[TMP19]], [[ABS_RHSR_LESS_THAN_ABS_RHSI]] ] +// FRTRN-NEXT: [[RETVAL_REALP:%.*]] = getelementptr inbounds { float, float }, ptr [[RETVAL]], i32 0, i32 0 +// FRTRN-NEXT: [[RETVAL_IMAGP:%.*]] = getelementptr inbounds { float, float }, ptr [[RETVAL]], i32 0, i32 1 +// FRTRN-NEXT: store float [[TMP20]], ptr [[RETVAL_REALP]], align 4 +// FRTRN-NEXT: store float [[TMP21]], ptr [[RETVAL_IMAGP]], align 4 +// FRTRN-NEXT: [[TMP22:%.*]] = load <2 x float>, ptr [[RETVAL]], align 4 +// FRTRN-NEXT: ret <2 x float> [[TMP22]] +// +_Complex float div(_Complex float a, _Complex float b) { + return a / b; +} diff --git a/clang/test/Driver/range.c b/clang/test/Driver/range.c index 8d456a997d696..045d9c7d3d802 100644 --- a/clang/test/Driver/range.c +++ b/clang/test/Driver/range.c @@ -6,6 +6,9 @@ // RUN: %clang -### -target x86_64 -fno-cx-limited-range -c %s 2>&1 \ // RUN: | FileCheck %s +// RUN: %clang -### -target x86_64 -fcx-limited-range -fno-cx-limited-range \ +// RUN: -c %s 2>&1 | FileCheck --check-prefix=FULL %s + // RUN: %clang -### -target x86_64 -fcx-fortran-rules -c %s 2>&1 \ // RUN: | FileCheck --check-prefix=FRTRN %s @@ -29,7 +32,11 @@ // RUN: %clang -### -target x86_64 -fcx-limited-range -ffast-math -c %s 2>&1 \ // RUN: | FileCheck --check-prefix=LMTD %s +// RUN: %clang -### -target x86_64 -ffast-math -fno-cx-limited-range -c %s 2>&1 \ +// RUN: | FileCheck --check-prefix=FULL %s + // LMTD: -complex-range=limited +// FULL: -complex-range=full // LMTD-NOT: -complex-range=fortran // CHECK-NOT: -complex-range=limited // FRTRN: -complex-range=fortran