[clang] b40c534 - [clang] Add support for -fcx-limited-range, #pragma CX_LIMITED_RANGE and -fcx-fortran-rules. (#70244)

via cfe-commits cfe-commits at lists.llvm.org
Mon Dec 11 07:03:33 PST 2023


Author: Zahira Ammarguellat
Date: 2023-12-11T10:03:27-05:00
New Revision: b40c53465650456bfd364c123b1001e025353a33

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

LOG: [clang] Add support for -fcx-limited-range, #pragma CX_LIMITED_RANGE and -fcx-fortran-rules.  (#70244)

This patch adds the #pragma CX_LIMITED_RANGE defined in the C
specification.
It also adds the options -f[no]cx-limited-range and
-f[no]cx-fortran-rules.
-fcx-limited-range enables algebraic formulas for complex multiplication
and division. This option is enabled with -ffast-math.
-fcx-fortran-rules enables algebraic formulas for complex multiplication
and enables Smith’s algorithm for complex division (SMITH, R. L.
Algorithm 116: Complex division. Commun. ACM 5, 8 (1962)).

---------

Signed-off-by: Med Ismail Bennani <ismail at bennani.ma>
Co-authored-by: Joseph Huber <jhuber6 at vols.utk.edu>
Co-authored-by: Guray Ozen <guray.ozen at gmail.com>
Co-authored-by: Nishant Patel <nishant.b.patel at intel.com>
Co-authored-by: Jessica Clarke <jrtc27 at jrtc27.com>
Co-authored-by: Petr Hosek <phosek at google.com>
Co-authored-by: Joseph Huber <35342157+jhuber6 at users.noreply.github.com>
Co-authored-by: Craig Topper <craig.topper at sifive.com>
Co-authored-by: Alexander Yermolovich <43973793+ayermolo at users.noreply.github.com>
Co-authored-by: Usama Hameed <u_hameed at apple.com>
Co-authored-by: Philip Reames <preames at rivosinc.com>
Co-authored-by: Evgenii Kudriashov <evgenii.kudriashov at intel.com>
Co-authored-by: Fangrui Song <i at maskray.me>
Co-authored-by: Aart Bik <39774503+aartbik at users.noreply.github.com>
Co-authored-by: Valentin Clement <clementval at gmail.com>
Co-authored-by: Youngsuk Kim <youngsuk.kim at hpe.com>
Co-authored-by: Arthur Eubanks <aeubanks at google.com>
Co-authored-by: Jan Svoboda <jan_svoboda at apple.com>
Co-authored-by: Walter Erquinigo <a20012251 at gmail.com>
Co-authored-by: Eric <eric at efcs.ca>
Co-authored-by: Fazlay Rabbi <106703039+mdfazlay at users.noreply.github.com>
Co-authored-by: Pete Lawrence <plawrence at apple.com>
Co-authored-by: Jonas Devlieghere <jonas at devlieghere.com>
Co-authored-by: Adrian Prantl <aprantl at apple.com>
Co-authored-by: Owen Pan <owenpiano at gmail.com>
Co-authored-by: LLVM GN Syncbot <llvmgnsyncbot at gmail.com>
Co-authored-by: Med Ismail Bennani <ismail at bennani.ma>
Co-authored-by: Congcong Cai <congcongcai0907 at 163.com>
Co-authored-by: Rik Huijzer <github at huijzer.xyz>
Co-authored-by: Wang Pengcheng <wangpengcheng.pp at bytedance.com>
Co-authored-by: Yuanfang Chen <tabloid.adroit at gmail.com>
Co-authored-by: Kazu Hirata <kazu at google.com>
Co-authored-by: Mehdi Amini <joker.eph at gmail.com>
Co-authored-by: Aiden Grossman <agrossman154 at yahoo.com>
Co-authored-by: Rana Pratap Reddy <109514914+ranapratap55 at users.noreply.github.com>
Co-authored-by: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Co-authored-by: Piotr Zegar <me at piotrzegar.pl>
Co-authored-by: KAWASHIMA Takahiro <t-kawashima at fujitsu.com>
Co-authored-by: Tobias Hieta <tobias at hieta.se>
Co-authored-by: Luke Lau <luke at igalia.com>
Co-authored-by: Shivam Gupta <shivam98.tkg at gmail.com>
Co-authored-by: cor3ntin <corentinjabot at gmail.com>
Co-authored-by: Yeting Kuo <46629943+yetingk at users.noreply.github.com>
Co-authored-by: Stanislav Mekhanoshin <rampitec at users.noreply.github.com>
Co-authored-by: David Spickett <david.spickett at linaro.org>
Co-authored-by: Matthew Devereau <matthew.devereau at arm.com>
Co-authored-by: Martin Storsjö <martin at martin.st>
Co-authored-by: Qiu Chaofan <qiucofan at cn.ibm.com>
Co-authored-by: Pierre van Houtryve <pierre.vanhoutryve at amd.com>
Co-authored-by: Mikael Holmen <mikael.holmen at ericsson.com>
Co-authored-by: Uday Bondhugula <uday at polymagelabs.com>
Co-authored-by: Nikita Popov <npopov at redhat.com>
Co-authored-by: Johannes Reifferscheid <jreiffers at google.com>
Co-authored-by: Benjamin Kramer <benny.kra at googlemail.com>
Co-authored-by: Oliver Stannard <oliver.stannard at arm.com>
Co-authored-by: Dmitry Vyukov <dvyukov at google.com>
Co-authored-by: Benjamin Maxwell <benjamin.maxwell at arm.com>
Co-authored-by: Piotr Sobczak <piotr.sobczak at amd.com>
Co-authored-by: Simon Pilgrim <llvm-dev at redking.me.uk>
Co-authored-by: Timm Bäder <tbaeder at redhat.com>
Co-authored-by: Sunil Kuravinakop <koops at hpe.com>
Co-authored-by: zhongyunde 00443407 <zhongyunde at huawei.com>
Co-authored-by: Christudasan Devadasan <Christudasan.Devadasan at amd.com>
Co-authored-by: bjacob <jacob.benoit.1 at gmail.com>
Co-authored-by: Weining Lu <luweining at loongson.cn>
Co-authored-by: Andrzej Warzyński <andrzej.warzynski at arm.com>
Co-authored-by: Jay Foad <jay.foad at amd.com>
Co-authored-by: Markus Mützel <markus.muetzel at gmx.de>
Co-authored-by: Erik Jonsson <erik.j.jonsson at ericsson.com>
Co-authored-by: Pete Steinfeld <47540744+psteinfeld at users.noreply.github.com>
Co-authored-by: Alexey Bataev <a.bataev at outlook.com>
Co-authored-by: Louis Dionne <ldionne.2 at gmail.com>
Co-authored-by: Qizhi Hu <836744285 at qq.com>

Added: 
    clang/test/CodeGen/cx-complex-range.c
    clang/test/CodeGen/pragma-cx-limited-range.c
    clang/test/Driver/range.c

Modified: 
    clang/docs/ReleaseNotes.rst
    clang/docs/UsersManual.rst
    clang/include/clang/Basic/FPOptions.def
    clang/include/clang/Basic/Features.def
    clang/include/clang/Basic/LangOptions.def
    clang/include/clang/Basic/LangOptions.h
    clang/include/clang/Basic/TokenKinds.def
    clang/include/clang/Driver/Options.td
    clang/include/clang/Parse/Parser.h
    clang/include/clang/Sema/Sema.h
    clang/lib/CodeGen/CGExprComplex.cpp
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/lib/Parse/ParsePragma.cpp
    clang/lib/Parse/ParseStmt.cpp
    clang/lib/Parse/Parser.cpp
    clang/lib/Sema/SemaAttr.cpp
    clang/test/CodeGen/complex-math.c
    clang/test/Preprocessor/pragma_unknown.c

Removed: 
    


################################################################################
diff  --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index bc3acf165f0759..6e4009deaf8746 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -264,6 +264,16 @@ New Compiler Flags
 
 * ``-fopenacc`` was added as a part of the effort to support OpenACC in clang.
 
+* ``-fcx-limited-range`` enables the naive mathematical formulas for complex
+  division and multiplication with no NaN checking of results. The default is
+  ``-fno-cx-limited-range``, but this option is enabled by ``-ffast-math``.
+
+* ``-fcx-fortran-rules`` enables the naive mathematical formulas for complex
+  multiplication and enables application of Smith's algorithm for complex
+  division. See SMITH, R. L. Algorithm 116: Complex division. Commun. ACM 5, 8
+  (1962). The default is ``-fno-cx-fortran-rules``.
+
+
 Deprecated Compiler Flags
 -------------------------
 
@@ -1002,6 +1012,9 @@ Floating Point Support in Clang
   ``__builtin_exp10f128`` builtins.
 - Add ``__builtin_iszero``, ``__builtin_issignaling`` and
   ``__builtin_issubnormal``.
+- Add support for C99's ``#pragma STDC CX_LIMITED_RANGE`` feature.  This
+  enables the naive mathematical formulas for complex multiplication and
+  division, which are faster but do not correctly handle overflow and infinities.
 
 AST Matchers
 ------------

diff  --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst
index f1b344ef5109b5..7c30570437e8b0 100644
--- a/clang/docs/UsersManual.rst
+++ b/clang/docs/UsersManual.rst
@@ -1468,6 +1468,7 @@ floating point semantic models: precise (the default), strict, and fast.
    With the exception of ``-ffp-contract=fast``, using any of the options
    below to disable any of the individual optimizations in ``-ffast-math``
    will cause ``__FAST_MATH__`` to no longer be set.
+   ``-ffast-math`` enables ``-fcx-limited-range``.
 
    This option implies:
 
@@ -1834,6 +1835,20 @@ floating point semantic models: precise (the default), strict, and fast.
    * ``16`` - Forces ``_Float16`` operations to be emitted without using excess
      precision arithmetic.
 
+.. option:: -fcx-limited-range:
+
+   This option enables the naive mathematical formulas for complex division and
+   multiplication with no NaN checking of results. The default is
+   ``-fno-cx-limited-range``, but this option is enabled by the ``-ffast-math``
+   option.
+
+.. option:: -fcx-fortran-rules:
+
+   This option enables the naive mathematical formulas for complex
+   multiplication and enables application of Smith's algorithm for complex
+   division. See SMITH, R. L. Algorithm 116: Complex division. Commun.
+   ACM 5, 8 (1962). The default is ``-fno-cx-fortran-rules``.
+
 .. _floating-point-environment:
 
 Accessing the floating point environment

diff  --git a/clang/include/clang/Basic/FPOptions.def b/clang/include/clang/Basic/FPOptions.def
index 5b923a1944e509..79f04c89c9fedc 100644
--- a/clang/include/clang/Basic/FPOptions.def
+++ b/clang/include/clang/Basic/FPOptions.def
@@ -28,4 +28,5 @@ OPTION(FPEvalMethod, LangOptions::FPEvalMethodKind, 2, AllowApproxFunc)
 OPTION(Float16ExcessPrecision, LangOptions::ExcessPrecisionKind, 2, FPEvalMethod)
 OPTION(BFloat16ExcessPrecision, LangOptions::ExcessPrecisionKind, 2, Float16ExcessPrecision)
 OPTION(MathErrno, bool, 1, BFloat16ExcessPrecision)
+OPTION(ComplexRange, LangOptions::ComplexRangeKind, 2, MathErrno)
 #undef OPTION

diff  --git a/clang/include/clang/Basic/Features.def b/clang/include/clang/Basic/Features.def
index df1eff8cbcc9f0..7473e00a7bd86b 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -104,6 +104,7 @@ FEATURE(scudo, LangOpts.Sanitize.hasOneOf(SanitizerKind::Scudo))
 FEATURE(swiftasynccc,
   PP.getTargetInfo().checkCallingConvention(CC_SwiftAsync) ==
   clang::TargetInfo::CCCR_OK)
+FEATURE(pragma_stdc_cx_limited_range, true)
 // Objective-C features
 FEATURE(objc_arr, LangOpts.ObjCAutoRefCount) // FIXME: REMOVE?
 FEATURE(objc_arc, LangOpts.ObjCAutoRefCount)

diff  --git a/clang/include/clang/Basic/LangOptions.def b/clang/include/clang/Basic/LangOptions.def
index c3d5399905a3fd..152d9f65f86dbe 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -220,6 +220,8 @@ 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.")
+
 BENIGN_LANGOPT(ObjCGCBitmapPrint , 1, 0, "printing of GC's bitmap layout for __weak/__strong ivars")
 
 BENIGN_LANGOPT(AccessControl     , 1, 1, "C++ access control")

diff  --git a/clang/include/clang/Basic/LangOptions.h b/clang/include/clang/Basic/LangOptions.h
index c4979b9a38c0cf..9f986fce2d4418 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -392,6 +392,8 @@ class LangOptions : public LangOptionsBase {
     IncompleteOnly = 3,
   };
 
+  enum ComplexRangeKind { CX_Full, CX_Limited, CX_Fortran };
+
 public:
   /// The used language standard.
   LangStandard::Kind LangStd;
@@ -741,6 +743,7 @@ class FPOptions {
       setAllowFEnvAccess(true);
     else
       setAllowFEnvAccess(LangOptions::FPM_Off);
+    setComplexRange(LO.getComplexRange());
   }
 
   bool allowFPContractWithinStatement() const {

diff  --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 5f9915d210221c..3f0e1e1a7d45ad 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -911,6 +911,11 @@ PRAGMA_ANNOTATION(pragma_fenv_access_ms)
 // handles them.
 PRAGMA_ANNOTATION(pragma_fenv_round)
 
+// Annotation for #pragma STDC CX_LIMITED_RANGE
+// The lexer produces these so that they only take effect when the parser
+// handles them.
+PRAGMA_ANNOTATION(pragma_cx_limited_range)
+
 // Annotation for #pragma float_control
 // The lexer produces these so that they only take effect when the parser
 // handles them.

diff  --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index cf969cb0b318a6..25c76cf2ad2c84 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1010,6 +1010,30 @@ defm offload_uniform_block : BoolFOption<"offload-uniform-block",
   NegFlag<SetFalse, [], [ClangOption, CC1Option], "Don't assume">,
   BothFlags<[], [ClangOption], " that kernels are launched with uniform block sizes (default true for CUDA/HIP and false otherwise)">>;
 
+def fcx_limited_range : Joined<["-"], "fcx-limited-range">,
+  Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
+  HelpText<"Basic algebraic expansions of complex arithmetic operations "
+           "involving are enabled.">;
+
+def fno_cx_limited_range : Joined<["-"], "fno-cx-limited-range">,
+  Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
+  HelpText<"Basic algebraic expansions of complex arithmetic operations "
+           "involving are disabled.">;
+
+def fcx_fortran_rules : Joined<["-"], "fcx-fortran-rules">,
+  Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
+  HelpText<"Range reduction is enabled for complex arithmetic operations.">;
+
+def fno_cx_fortran_rules : Joined<["-"], "fno-cx-fortran-rules">,
+  Group<f_Group>, Visibility<[ClangOption, CC1Option]>,
+  HelpText<"Range reduction is disabled for complex arithmetic operations.">;
+
+def complex_range_EQ : Joined<["-"], "complex-range=">, Group<f_Group>,
+  Visibility<[CC1Option]>,
+  Values<"full,limited,fortran">, NormalizedValuesScope<"LangOptions">,
+  NormalizedValues<["CX_Full", "CX_Limited", "CX_Fortran"]>,
+  MarshallingInfoEnum<LangOpts<"ComplexRange">, "CX_Full">;
+
 // OpenCL-only Options
 def cl_opt_disable : Flag<["-"], "cl-opt-disable">, Group<opencl_Group>,
   Visibility<[ClangOption, CC1Option]>,

diff  --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index 06634982351647..2dbe090bd0932f 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -769,6 +769,10 @@ class Parser : public CodeCompletionHandler {
   /// #pragma STDC FENV_ROUND...
   void HandlePragmaFEnvRound();
 
+  /// Handle the annotation token produced for
+  /// #pragma STDC CX_LIMITED_RANGE...
+  void HandlePragmaCXLimitedRange();
+
   /// Handle the annotation token produced for
   /// #pragma float_control
   void HandlePragmaFloatControl();

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index f45e0a7d3d52d4..1902d098f3c25e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -11023,6 +11023,11 @@ class Sema final {
   /// \#pragma STDC FENV_ACCESS
   void ActOnPragmaFEnvAccess(SourceLocation Loc, bool IsEnabled);
 
+  /// ActOnPragmaCXLimitedRange - Called on well formed
+  /// \#pragma STDC CX_LIMITED_RANGE
+  void ActOnPragmaCXLimitedRange(SourceLocation Loc,
+                                 LangOptions::ComplexRangeKind Range);
+
   /// Called on well formed '\#pragma clang fp' that has option 'exceptions'.
   void ActOnPragmaFPExceptions(SourceLocation Loc,
                                LangOptions::FPExceptionModeKind);

diff  --git a/clang/lib/CodeGen/CGExprComplex.cpp b/clang/lib/CodeGen/CGExprComplex.cpp
index f3cbd1d0451ebe..e532794b71bdb4 100644
--- a/clang/lib/CodeGen/CGExprComplex.cpp
+++ b/clang/lib/CodeGen/CGExprComplex.cpp
@@ -275,6 +275,10 @@ class ComplexExprEmitter
   ComplexPairTy EmitBinSub(const BinOpInfo &Op);
   ComplexPairTy EmitBinMul(const BinOpInfo &Op);
   ComplexPairTy EmitBinDiv(const BinOpInfo &Op);
+  ComplexPairTy EmitAlgebraicDiv(llvm::Value *A, llvm::Value *B, llvm::Value *C,
+                                 llvm::Value *D);
+  ComplexPairTy EmitRangeReductionDiv(llvm::Value *A, llvm::Value *B,
+                                      llvm::Value *C, llvm::Value *D);
 
   ComplexPairTy EmitComplexBinOpLibCall(StringRef LibCallName,
                                         const BinOpInfo &Op);
@@ -781,6 +785,10 @@ ComplexPairTy ComplexExprEmitter::EmitBinMul(const BinOpInfo &Op) {
       ResR = Builder.CreateFSub(AC, BD, "mul_r");
       ResI = Builder.CreateFAdd(AD, BC, "mul_i");
 
+      if (Op.FPFeatures.getComplexRange() == LangOptions::CX_Limited ||
+          Op.FPFeatures.getComplexRange() == LangOptions::CX_Fortran)
+        return ComplexPairTy(ResR, ResI);
+
       // Emit the test for the real part becoming NaN and create a branch to
       // handle it. We test for NaN by comparing the number to itself.
       Value *IsRNaN = Builder.CreateFCmpUNO(ResR, ResR, "isnan_cmp");
@@ -846,23 +854,139 @@ ComplexPairTy ComplexExprEmitter::EmitBinMul(const BinOpInfo &Op) {
   return ComplexPairTy(ResR, ResI);
 }
 
+ComplexPairTy ComplexExprEmitter::EmitAlgebraicDiv(llvm::Value *LHSr,
+                                                   llvm::Value *LHSi,
+                                                   llvm::Value *RHSr,
+                                                   llvm::Value *RHSi) {
+  // (a+ib) / (c+id) = ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd))
+  llvm::Value *DSTr, *DSTi;
+
+  llvm::Value *AC = Builder.CreateFMul(LHSr, RHSr); // a*c
+  llvm::Value *BD = Builder.CreateFMul(LHSi, RHSi); // b*d
+  llvm::Value *ACpBD = Builder.CreateFAdd(AC, BD);  // ac+bd
+
+  llvm::Value *CC = Builder.CreateFMul(RHSr, RHSr); // c*c
+  llvm::Value *DD = Builder.CreateFMul(RHSi, RHSi); // d*d
+  llvm::Value *CCpDD = Builder.CreateFAdd(CC, DD);  // cc+dd
+
+  llvm::Value *BC = Builder.CreateFMul(LHSi, RHSr); // b*c
+  llvm::Value *AD = Builder.CreateFMul(LHSr, RHSi); // a*d
+  llvm::Value *BCmAD = Builder.CreateFSub(BC, AD);  // bc-ad
+
+  DSTr = Builder.CreateFDiv(ACpBD, CCpDD);
+  DSTi = Builder.CreateFDiv(BCmAD, CCpDD);
+  return ComplexPairTy(DSTr, DSTi);
+}
+
+// EmitFAbs - Emit a call to @llvm.fabs.
+static llvm::Value *EmitllvmFAbs(CodeGenFunction &CGF, llvm::Value *Value) {
+  llvm::Function *Func =
+      CGF.CGM.getIntrinsic(llvm::Intrinsic::fabs, Value->getType());
+  llvm::Value *Call = CGF.Builder.CreateCall(Func, Value);
+  return Call;
+}
+
+// EmitRangeReductionDiv - Implements Smith's algorithm for complex division.
+// SMITH, R. L. Algorithm 116: Complex division. Commun. ACM 5, 8 (1962).
+ComplexPairTy ComplexExprEmitter::EmitRangeReductionDiv(llvm::Value *LHSr,
+                                                        llvm::Value *LHSi,
+                                                        llvm::Value *RHSr,
+                                                        llvm::Value *RHSi) {
+  // (a + ib) / (c + id) = (e + if)
+  llvm::Value *FAbsRHSr = EmitllvmFAbs(CGF, RHSr); // |c|
+  llvm::Value *FAbsRHSi = EmitllvmFAbs(CGF, RHSi); // |d|
+  // |c| >= |d|
+  llvm::Value *IsR = Builder.CreateFCmpUGT(FAbsRHSr, FAbsRHSi, "abs_cmp");
+
+  llvm::BasicBlock *TrueBB =
+      CGF.createBasicBlock("abs_rhsr_greater_or_equal_abs_rhsi");
+  llvm::BasicBlock *FalseBB =
+      CGF.createBasicBlock("abs_rhsr_less_than_abs_rhsi");
+  llvm::BasicBlock *ContBB = CGF.createBasicBlock("complex_div");
+  Builder.CreateCondBr(IsR, TrueBB, FalseBB);
+
+  CGF.EmitBlock(TrueBB);
+  // abs(c) >= abs(d)
+  // r = d/c
+  // tmp = c + rd
+  // e = (a + br)/tmp
+  // f = (b - ar)/tmp
+  llvm::Value *DdC = Builder.CreateFDiv(RHSi, RHSr); // r=d/c
+
+  llvm::Value *RD = Builder.CreateFMul(DdC, RHSi);  // rd
+  llvm::Value *CpRD = Builder.CreateFAdd(RHSr, RD); // tmp=c+rd
+
+  llvm::Value *T3 = Builder.CreateFMul(LHSi, DdC);   // br
+  llvm::Value *T4 = Builder.CreateFAdd(LHSr, T3);    // a+br
+  llvm::Value *DSTTr = Builder.CreateFDiv(T4, CpRD); // (a+br)/tmp
+
+  llvm::Value *T5 = Builder.CreateFMul(LHSr, DdC);   // ar
+  llvm::Value *T6 = Builder.CreateFSub(LHSi, T5);    // b-ar
+  llvm::Value *DSTTi = Builder.CreateFDiv(T6, CpRD); // (b-ar)/tmp
+  Builder.CreateBr(ContBB);
+
+  CGF.EmitBlock(FalseBB);
+  // abs(c) < abs(d)
+  // r = c/d
+  // tmp = d + rc
+  // e = (ar + b)/tmp
+  // f = (br - a)/tmp
+  llvm::Value *CdD = Builder.CreateFDiv(RHSr, RHSi); // r=c/d
+
+  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 *T8 = Builder.CreateFAdd(T7, LHSi);    // ar+b
+  llvm::Value *DSTFr = Builder.CreateFDiv(T8, DpRC); // (ar+b)/tmp
+
+  llvm::Value *T9 = Builder.CreateFMul(LHSi, CdD);    // br
+  llvm::Value *T10 = Builder.CreateFSub(T9, LHSr);    // br-a
+  llvm::Value *DSTFi = Builder.CreateFDiv(T10, DpRC); // (br-a)/tmp
+  Builder.CreateBr(ContBB);
+
+  // Phi together the computation paths.
+  CGF.EmitBlock(ContBB);
+  llvm::PHINode *VALr = Builder.CreatePHI(DSTTr->getType(), 2);
+  VALr->addIncoming(DSTTr, TrueBB);
+  VALr->addIncoming(DSTFr, FalseBB);
+  llvm::PHINode *VALi = Builder.CreatePHI(DSTTi->getType(), 2);
+  VALi->addIncoming(DSTTi, TrueBB);
+  VALi->addIncoming(DSTFi, FalseBB);
+  return ComplexPairTy(VALr, VALi);
+}
+
 // See C11 Annex G.5.1 for the semantics of multiplicative operators on complex
 // typed values.
 ComplexPairTy ComplexExprEmitter::EmitBinDiv(const BinOpInfo &Op) {
   llvm::Value *LHSr = Op.LHS.first, *LHSi = Op.LHS.second;
   llvm::Value *RHSr = Op.RHS.first, *RHSi = Op.RHS.second;
-
   llvm::Value *DSTr, *DSTi;
   if (LHSr->getType()->isFloatingPointTy()) {
-    // 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
-    // underflow/overflow cases. When FastMath is allowed we construct the
-    // divide inline using the same algorithm as for integer operands.
-    //
-    // FIXME: We would be able to avoid the libcall in many places if we
-    // supported imaginary types in addition to complex types.
     CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, Op.FPFeatures);
-    if (RHSi && !CGF.getLangOpts().FastMath) {
+    if (!RHSi) {
+      assert(LHSi && "Can have at most one non-complex operand!");
+
+      DSTr = Builder.CreateFDiv(LHSr, RHSr);
+      DSTi = Builder.CreateFDiv(LHSi, RHSr);
+      return ComplexPairTy(DSTr, DSTi);
+    }
+    llvm::Value *OrigLHSi = LHSi;
+    if (!LHSi)
+      LHSi = llvm::Constant::getNullValue(RHSi->getType());
+    if (Op.FPFeatures.getComplexRange() == LangOptions::CX_Fortran)
+      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) {
+      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
+      // underflow/overflow cases. When FastMath is allowed we construct the
+      // divide inline using the same algorithm as for integer operands.
+      //
+      // FIXME: We would be able to avoid the libcall in many places if we
+      // supported imaginary types in addition to complex types.
       BinOpInfo LibCallOp = Op;
       // If LHS was a real, supply a null imaginary part.
       if (!LHSi)
@@ -884,30 +1008,8 @@ ComplexPairTy ComplexExprEmitter::EmitBinDiv(const BinOpInfo &Op) {
       case llvm::Type::FP128TyID:
         return EmitComplexBinOpLibCall("__divtc3", LibCallOp);
       }
-    } else if (RHSi) {
-      if (!LHSi)
-        LHSi = llvm::Constant::getNullValue(RHSi->getType());
-
-      // (a+ib) / (c+id) = ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd))
-      llvm::Value *AC = Builder.CreateFMul(LHSr, RHSr); // a*c
-      llvm::Value *BD = Builder.CreateFMul(LHSi, RHSi); // b*d
-      llvm::Value *ACpBD = Builder.CreateFAdd(AC, BD); // ac+bd
-
-      llvm::Value *CC = Builder.CreateFMul(RHSr, RHSr); // c*c
-      llvm::Value *DD = Builder.CreateFMul(RHSi, RHSi); // d*d
-      llvm::Value *CCpDD = Builder.CreateFAdd(CC, DD); // cc+dd
-
-      llvm::Value *BC = Builder.CreateFMul(LHSi, RHSr); // b*c
-      llvm::Value *AD = Builder.CreateFMul(LHSr, RHSi); // a*d
-      llvm::Value *BCmAD = Builder.CreateFSub(BC, AD); // bc-ad
-
-      DSTr = Builder.CreateFDiv(ACpBD, CCpDD);
-      DSTi = Builder.CreateFDiv(BCmAD, CCpDD);
     } else {
-      assert(LHSi && "Can have at most one non-complex operand!");
-
-      DSTr = Builder.CreateFDiv(LHSr, RHSr);
-      DSTi = Builder.CreateFDiv(LHSi, RHSr);
+      return EmitAlgebraicDiv(LHSr, LHSi, RHSr, RHSi);
     }
   } else {
     assert(Op.LHS.second && Op.RHS.second &&

diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index eb26bfade47b7a..f95f3227aba7d0 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -2660,6 +2660,35 @@ static void CollectArgsForIntegratedAssembler(Compilation &C,
   }
 }
 
+static StringRef EnumComplexRangeToStr(LangOptions::ComplexRangeKind Range) {
+  StringRef RangeStr = "";
+  switch (Range) {
+  case LangOptions::ComplexRangeKind::CX_Limited:
+    return "-fcx-limited-range";
+    break;
+  case LangOptions::ComplexRangeKind::CX_Fortran:
+    return "-fcx-fortran-rules";
+    break;
+  default:
+    return RangeStr;
+    break;
+  }
+}
+
+static void EmitComplexRangeDiag(const Driver &D,
+                                 LangOptions::ComplexRangeKind Range1,
+                                 LangOptions::ComplexRangeKind Range2) {
+  if (Range1 != LangOptions::ComplexRangeKind::CX_Full)
+    D.Diag(clang::diag::warn_drv_overriding_option)
+        << EnumComplexRangeToStr(Range1) << EnumComplexRangeToStr(Range2);
+}
+
+static std::string RenderComplexRangeOption(std::string Range) {
+  std::string ComplexRangeStr = "-complex-range=";
+  ComplexRangeStr += Range;
+  return ComplexRangeStr;
+}
+
 static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
                                        bool OFastEnabled, const ArgList &Args,
                                        ArgStringList &CmdArgs,
@@ -2706,6 +2735,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   bool StrictFPModel = false;
   StringRef Float16ExcessPrecision = "";
   StringRef BFloat16ExcessPrecision = "";
+  LangOptions::ComplexRangeKind Range = LangOptions::ComplexRangeKind::CX_Full;
 
   if (const Arg *A = Args.getLastArg(options::OPT_flimited_precision_EQ)) {
     CmdArgs.push_back("-mlimit-float-precision");
@@ -2718,6 +2748,28 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
     switch (optID) {
     default:
       break;
+    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:
+      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:
+      Range = LangOptions::ComplexRangeKind::CX_Full;
+      break;
     case options::OPT_ffp_model_EQ: {
       // If -ffp-model= is seen, reset to fno-fast-math
       HonorINFs = true;
@@ -2772,7 +2824,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
         D.Diag(diag::err_drv_unsupported_option_argument)
             << A->getSpelling() << Val;
       break;
-      }
+    }
     }
 
     switch (optID) {
@@ -2971,7 +3023,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
       if (!OFastEnabled)
         continue;
       [[fallthrough]];
-    case options::OPT_ffast_math:
+    case options::OPT_ffast_math: {
       HonorINFs = false;
       HonorNaNs = false;
       MathErrno = false;
@@ -2985,7 +3037,13 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
       // If fast-math is set then set the fp-contract mode to fast.
       FPContract = "fast";
       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));
       break;
+    }
     case options::OPT_fno_fast_math:
       HonorINFs = true;
       HonorNaNs = true;
@@ -3139,6 +3197,15 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
   if (Args.hasFlag(options::OPT_fno_strict_float_cast_overflow,
                    options::OPT_fstrict_float_cast_overflow, false))
     CmdArgs.push_back("-fno-strict-float-cast-overflow");
+
+  if (const Arg *A = Args.getLastArg(options::OPT_fcx_limited_range))
+    CmdArgs.push_back("-fcx-limited-range");
+  if (const Arg *A = Args.getLastArg(options::OPT_fcx_fortran_rules))
+    CmdArgs.push_back("-fcx-fortran-rules");
+  if (const Arg *A = Args.getLastArg(options::OPT_fno_cx_limited_range))
+    CmdArgs.push_back("-fno-cx-limited-range");
+  if (const Arg *A = Args.getLastArg(options::OPT_fno_cx_fortran_rules))
+    CmdArgs.push_back("-fno-cx-fortran-rules");
 }
 
 static void RenderAnalyzerOptions(const ArgList &Args, ArgStringList &CmdArgs,

diff  --git a/clang/lib/Parse/ParsePragma.cpp b/clang/lib/Parse/ParsePragma.cpp
index efdf7c90f977fb..730ac1a0fee5cc 100644
--- a/clang/lib/Parse/ParsePragma.cpp
+++ b/clang/lib/Parse/ParsePragma.cpp
@@ -137,7 +137,20 @@ struct PragmaSTDC_CX_LIMITED_RANGEHandler : public PragmaHandler {
   void HandlePragma(Preprocessor &PP, PragmaIntroducer Introducer,
                     Token &Tok) override {
     tok::OnOffSwitch OOS;
-    PP.LexOnOffSwitch(OOS);
+    if (PP.LexOnOffSwitch(OOS))
+      return;
+
+    MutableArrayRef<Token> Toks(
+        PP.getPreprocessorAllocator().Allocate<Token>(1), 1);
+
+    Toks[0].startToken();
+    Toks[0].setKind(tok::annot_pragma_cx_limited_range);
+    Toks[0].setLocation(Tok.getLocation());
+    Toks[0].setAnnotationEndLoc(Tok.getLocation());
+    Toks[0].setAnnotationValue(
+        reinterpret_cast<void *>(static_cast<uintptr_t>(OOS)));
+    PP.EnterTokenStream(Toks, /*DisableMacroExpansion=*/true,
+                        /*IsReinject=*/false);
   }
 };
 
@@ -888,6 +901,31 @@ void Parser::HandlePragmaFEnvRound() {
   Actions.ActOnPragmaFEnvRound(PragmaLoc, RM);
 }
 
+void Parser::HandlePragmaCXLimitedRange() {
+  assert(Tok.is(tok::annot_pragma_cx_limited_range));
+  tok::OnOffSwitch OOS = static_cast<tok::OnOffSwitch>(
+      reinterpret_cast<uintptr_t>(Tok.getAnnotationValue()));
+
+  LangOptions::ComplexRangeKind Range;
+  switch (OOS) {
+  case tok::OOS_ON:
+    Range = LangOptions::CX_Limited;
+    break;
+  case tok::OOS_OFF:
+    Range = LangOptions::CX_Full;
+    break;
+  case tok::OOS_DEFAULT:
+    // According to ISO C99 standard chapter 7.3.4, the default value
+    // for the pragma is ``off'. -fcx-limited-range and -fcx-fortran-rules
+    // control the default value of these pragmas.
+    Range = getLangOpts().getComplexRange();
+    break;
+  }
+
+  SourceLocation PragmaLoc = ConsumeAnnotationToken();
+  Actions.ActOnPragmaCXLimitedRange(PragmaLoc, Range);
+}
+
 StmtResult Parser::HandlePragmaCaptured()
 {
   assert(Tok.is(tok::annot_pragma_captured));

diff  --git a/clang/lib/Parse/ParseStmt.cpp b/clang/lib/Parse/ParseStmt.cpp
index 918afdc2baea38..d0ff33bd1379ab 100644
--- a/clang/lib/Parse/ParseStmt.cpp
+++ b/clang/lib/Parse/ParseStmt.cpp
@@ -444,6 +444,14 @@ StmtResult Parser::ParseStatementOrDeclarationAfterAttributes(
     ConsumeAnnotationToken();
     return StmtError();
 
+  case tok::annot_pragma_cx_limited_range:
+    ProhibitAttributes(CXX11Attrs);
+    ProhibitAttributes(GNUAttrs);
+    Diag(Tok, diag::err_pragma_file_or_compound_scope)
+        << "STDC CX_LIMITED_RANGE";
+    ConsumeAnnotationToken();
+    return StmtError();
+
   case tok::annot_pragma_float_control:
     ProhibitAttributes(CXX11Attrs);
     ProhibitAttributes(GNUAttrs);
@@ -1066,6 +1074,9 @@ void Parser::ParseCompoundStatementLeadingPragmas() {
     case tok::annot_pragma_fenv_round:
       HandlePragmaFEnvRound();
       break;
+    case tok::annot_pragma_cx_limited_range:
+      HandlePragmaCXLimitedRange();
+      break;
     case tok::annot_pragma_float_control:
       HandlePragmaFloatControl();
       break;

diff  --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 1baeb2aeb021fa..ec67faf7dcaf86 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -844,6 +844,9 @@ Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
   case tok::annot_pragma_fenv_round:
     HandlePragmaFEnvRound();
     return nullptr;
+  case tok::annot_pragma_cx_limited_range:
+    HandlePragmaCXLimitedRange();
+    return nullptr;
   case tok::annot_pragma_float_control:
     HandlePragmaFloatControl();
     return nullptr;

diff  --git a/clang/lib/Sema/SemaAttr.cpp b/clang/lib/Sema/SemaAttr.cpp
index 79271c87262731..0dcf42e4899713 100644
--- a/clang/lib/Sema/SemaAttr.cpp
+++ b/clang/lib/Sema/SemaAttr.cpp
@@ -1352,6 +1352,14 @@ void Sema::ActOnPragmaFEnvAccess(SourceLocation Loc, bool IsEnabled) {
   CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts());
 }
 
+void Sema::ActOnPragmaCXLimitedRange(SourceLocation Loc,
+                                     LangOptions::ComplexRangeKind Range) {
+  FPOptionsOverride NewFPFeatures = CurFPFeatureOverrides();
+  NewFPFeatures.setComplexRangeOverride(Range);
+  FpPragmaStack.Act(Loc, PSK_Set, StringRef(), NewFPFeatures);
+  CurFPFeatures = NewFPFeatures.applyOverrides(getLangOpts());
+}
+
 void Sema::ActOnPragmaFPExceptions(SourceLocation Loc,
                                    LangOptions::FPExceptionModeKind FPE) {
   setExceptionMode(Loc, FPE);

diff  --git a/clang/test/CodeGen/complex-math.c b/clang/test/CodeGen/complex-math.c
index c59baaa452369d..a44aa0014a6587 100644
--- a/clang/test/CodeGen/complex-math.c
+++ b/clang/test/CodeGen/complex-math.c
@@ -5,7 +5,7 @@
 // RUN: %clang_cc1 %s -O0 -emit-llvm -triple armv7-none-linux-gnueabi -o - | FileCheck %s --check-prefix=ARM
 // RUN: %clang_cc1 %s -O0 -emit-llvm -triple armv7-none-linux-gnueabihf -o - | FileCheck %s --check-prefix=ARMHF
 // RUN: %clang_cc1 %s -O0 -emit-llvm -triple thumbv7k-apple-watchos2.0 -o - -target-abi aapcs16 | FileCheck %s --check-prefix=ARM7K
-// RUN: %clang_cc1 %s -O0 -emit-llvm -triple aarch64-unknown-unknown -ffast-math -ffp-contract=fast -o - | FileCheck %s --check-prefix=AARCH64-FASTMATH
+// RUN: %clang_cc1 %s -O0 -emit-llvm -triple aarch64-unknown-unknown -ffast-math -ffp-contract=fast -complex-range=fortran -o - | FileCheck %s --check-prefix=AARCH64-FASTMATH
 // RUN: %clang_cc1 %s -O0 -emit-llvm -triple spir -o - | FileCheck %s --check-prefix=SPIR
 
 float _Complex add_float_rr(float a, float b) {
@@ -135,24 +135,68 @@ float _Complex div_float_rc(float a, float _Complex b) {
 
   // SPIR: call spir_func {{.*}} @__divsc3(
 
-  // a / b = (A+iB) / (C+iD) = ((AC+BD)/(CC+DD)) + i((BC-AD)/(CC+DD))
+  // a / b = (A+iB) / (C+iD) = (E+iF)
+  // if (|C| >= |D|)
+  //   DdC = D/C
+  //   CpRD = C+DdC*D
+  //   E = (A+B*DdC)/CpRD
+  //   F = (B-A*DdC)/CpRD
+  // else
+  //   CdD = C/D
+  //   DpRC= D+CdD*C
+  //   E = (A*CdD+B)/DpRC
+  //   F = (B*CdD-A)/DpRC
   // AARCH64-FASTMATH-LABEL: @div_float_rc(float noundef nofpclass(nan inf) %a, [2 x float] noundef nofpclass(nan inf) alignstack(8) %b.coerce)
-  // A = a
-  // B = 0
-  //
-  // AARCH64-FASTMATH: [[AC:%.*]] = fmul fast float
-  // BD = 0
-  // ACpBD = AC
-  //
-  // AARCH64-FASTMATH: [[CC:%.*]] = fmul fast float
-  // AARCH64-FASTMATH: [[DD:%.*]] = fmul fast float
-  // AARCH64-FASTMATH: [[CCpDD:%.*]] = fadd fast float
-  //
-  // BC = 0
-  // AARCH64-FASTMATH: [[AD:%.*]] = fmul fast float
-  //
-  // AARCH64-FASTMATH: fdiv fast float
-  // AARCH64-FASTMATH: fdiv fast float
+  // |C|
+  // AARCH64-FASTMATH: call {{.*}}float @llvm.fabs.f32(float {{.*}})
+  // |D|
+  // AARCH64-FASTMATH-NEXT: call {{.*}}float @llvm.fabs.f32(float {{.*}})
+  // AARCH64-FASTMATH-NEXT: fcmp {{.*}}ugt float
+  // AARCH64-FASTMATH-NEXT: br i1 {{.*}}, label
+  // AARCH64-FASTMATH:      abs_rhsr_greater_or_equal_abs_rhsi:
+
+  // |C| >= |D|
+  // DdC=D/C
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}float
+
+  // CpRD=C+CdC*D
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}float
+
+  // A+BR/CpRD
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}float
+
+  // B-AR/CpRD
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fsub {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}float
+  // AARCH64-FASTMATH-NEXT: br label
+  // AARCH64-FASTMATH:      abs_rhsr_less_than_abs_rhsi:
+
+  // |C| < |D|
+  // CdD=C/D
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}float
+
+  // DpRC=D+CdD*C
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}float
+
+  // (A*CdD+B)/DpRC
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}float
+
+  // (BCdD-A)/DpRC
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fsub {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}float
+
+  // AARCH64-FASTMATH-NEXT: br label
+  // AARCH64-FASTMATH:      complex_div:
+  // AARCH64-FASTMATH-NEXT: phi {{.*}}float
+  // AARCH64-FASTMATH-NEXT: phi {{.*}}float
   // AARCH64-FASTMATH: ret
   return a / b;
 }
@@ -164,24 +208,68 @@ float _Complex div_float_cc(float _Complex a, float _Complex b) {
 
   // SPIR: call spir_func {{.*}} @__divsc3(
 
-  // a / b = (A+iB) / (C+iD) = ((AC+BD)/(CC+DD)) + i((BC-AD)/(CC+DD))
+  // a / b = (A+iB) / (C+iD) = (E+iF)
+  // if (|C| >= |D|)
+  //   DdC = D/C
+  //   CpRD = C+DdC*D
+  //   E = (A+B*DdC)/CpRD
+  //   F = (B-A*DdC)/CpRD
+  // else
+  //   CdD = C/D
+  //   DpRC= D+CdD*C
+  //   E = (A*CdD+B)/DpRC
+  //   F = (B*CdD-A)/DpRC
   // AARCH64-FASTMATH-LABEL: @div_float_cc([2 x float] noundef nofpclass(nan inf) alignstack(8) %a.coerce, [2 x float] noundef nofpclass(nan inf) alignstack(8) %b.coerce)
-  //
-  // AARCH64-FASTMATH: [[AC:%.*]] = fmul fast float
-  // AARCH64-FASTMATH: [[BD:%.*]] = fmul fast float
-  // AARCH64-FASTMATH: [[ACpBD:%.*]] = fadd fast float
-  //
-  // AARCH64-FASTMATH: [[CC:%.*]] = fmul fast float
-  // AARCH64-FASTMATH: [[DD:%.*]] = fmul fast float
-  // AARCH64-FASTMATH: [[CCpDD:%.*]] = fadd fast float
-  //
-  // AARCH64-FASTMATH: [[BC:%.*]] = fmul fast float
-  // AARCH64-FASTMATH: [[AD:%.*]] = fmul fast float
-  // AARCH64-FASTMATH: [[BCmAD:%.*]] = fsub fast float
-  //
-  // AARCH64-FASTMATH: fdiv fast float
-  // AARCH64-FASTMATH: fdiv fast float
-  // AARCH64-FASTMATH: ret
+  // |C|
+  // AARCH64-FASTMATH: call {{.*}}float @llvm.fabs.f32(float {{.*}})
+  // |D|
+  // AARCH64-FASTMATH-NEXT: call {{.*}}float @llvm.fabs.f32(float {{.*}})
+  // AARCH64-FASTMATH-NEXT: fcmp {{.*}}ugt float
+  // AARCH64-FASTMATH-NEXT: br i1 {{.*}}, label
+  // AARCH64-FASTMATH:      abs_rhsr_greater_or_equal_abs_rhsi:
+
+  // |C| >= |D|
+  // DdC=D/C
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}float
+
+  // CpRD=C+CdC*D
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}float
+
+  // A+BR/CpRD
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}float
+
+  // B-AR/CpRD
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fsub {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}float
+  // AARCH64-FASTMATH-NEXT: br label
+  // AARCH64-FASTMATH:      abs_rhsr_less_than_abs_rhsi:
+
+  // |C| < |D|
+  // CdD=C/D
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}float
+
+  // DpRC=D+CdD*C
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}float
+
+  // (A*CdD+B)/DpRC
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}float
+
+  // (BCdD-A)/DpRC
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fsub {{.*}}float
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}float
+
+  // AARCH64-FASTMATH-NEXT: br label
+  // AARCH64-FASTMATH:      complex_div:
+  // AARCH64-FASTMATH-NEXT: phi {{.*}}float
+  // AARCH64-FASTMATH-NEXT: phi {{.*}}float
   return a / b;
 }
 
@@ -312,24 +400,68 @@ double _Complex div_double_rc(double a, double _Complex b) {
 
   // SPIR: call spir_func {{.*}} @__divdc3(
 
-  // a / b = (A+iB) / (C+iD) = ((AC+BD)/(CC+DD)) + i((BC-AD)/(CC+DD))
+  // a / b = (A+iB) / (C+iD) = (E+iF)
+  // if (|C| >= |D|)
+  //   DdC = D/C
+  //   CpRD = C+DdC*D
+  //   E = (A+B*DdC)/CpRD
+  //   F = (B-A*DdC)/CpRD
+  // else
+  //   CdD = C/D
+  //   DpRC= D+CdD*C
+  //   E = (A*CdD+B)/DpRC
+  //   F = (B*CdD-A)/DpRC
   // AARCH64-FASTMATH-LABEL: @div_double_rc(double noundef nofpclass(nan inf) %a, [2 x double] noundef nofpclass(nan inf) alignstack(8) %b.coerce)
-  // A = a
-  // B = 0
-  //
-  // AARCH64-FASTMATH: [[AC:%.*]] = fmul fast double
-  // BD = 0
-  // ACpBD = AC
-  //
-  // AARCH64-FASTMATH: [[CC:%.*]] = fmul fast double
-  // AARCH64-FASTMATH: [[DD:%.*]] = fmul fast double
-  // AARCH64-FASTMATH: [[CCpDD:%.*]] = fadd fast double
-  //
-  // BC = 0
-  // AARCH64-FASTMATH: [[AD:%.*]] = fmul fast double
-  //
-  // AARCH64-FASTMATH: fdiv fast double
-  // AARCH64-FASTMATH: fdiv fast double
+  // |C|
+  // AARCH64-FASTMATH: call {{.*}}double @llvm.fabs.f64(double {{.*}})
+  // |D|
+  // AARCH64-FASTMATH-NEXT: call {{.*}}double @llvm.fabs.f64(double {{.*}})
+  // AARCH64-FASTMATH-NEXT: fcmp {{.*}}ugt double
+  // AARCH64-FASTMATH-NEXT: br i1 {{.*}}, label
+  // AARCH64-FASTMATH:      abs_rhsr_greater_or_equal_abs_rhsi:
+
+  // |C| >= |D|
+  // DdC=D/C
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}double
+
+  // CpRD=C+CdC*D
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}double
+
+  // A+BR/CpRD
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}double
+
+  // B-AR/CpRD
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fsub {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}double
+  // AARCH64-FASTMATH-NEXT: br label
+  // AARCH64-FASTMATH:      abs_rhsr_less_than_abs_rhsi:
+
+  // |C| < |D|
+  // CdD=C/D
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}double
+
+  // DpRC=D+CdD*C
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}double
+
+  // (A*CdD+B)/DpRC
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}double
+
+  // (BCdD-A)/DpRC
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fsub {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}double
+
+  // AARCH64-FASTMATH-NEXT: br label
+  // AARCH64-FASTMATH:      complex_div:
+  // AARCH64-FASTMATH-NEXT: phi {{.*}}double
+  // AARCH64-FASTMATH-NEXT: phi {{.*}}double
   // AARCH64-FASTMATH: ret
   return a / b;
 }
@@ -341,23 +473,68 @@ double _Complex div_double_cc(double _Complex a, double _Complex b) {
 
   // SPIR: call spir_func {{.*}} @__divdc3(
 
-  // a / b = (A+iB) / (C+iD) = ((AC+BD)/(CC+DD)) + i((BC-AD)/(CC+DD))
+  // a / b = (A+iB) / (C+iD) = (E+iF)
+  // if (|C| >= |D|)
+  //   DdC = D/C
+  //   CpRD = C+DdC*D
+  //   E = (A+B*DdC)/CpRD
+  //   F = (B-A*DdC)/CpRD
+  // else
+  //   CdD = C/D
+  //   DpRC= D+CdD*C
+  //   E = (A*CdD+B)/DpRC
+  //   F = (B*CdD-A)/DpRC
   // AARCH64-FASTMATH-LABEL: @div_double_cc([2 x double] noundef nofpclass(nan inf) alignstack(8) %a.coerce, [2 x double] noundef nofpclass(nan inf) alignstack(8) %b.coerce)
-  //
-  // AARCH64-FASTMATH: [[AC:%.*]] = fmul fast double
-  // AARCH64-FASTMATH: [[BD:%.*]] = fmul fast double
-  // AARCH64-FASTMATH: [[ACpBD:%.*]] = fadd fast double
-  //
-  // AARCH64-FASTMATH: [[CC:%.*]] = fmul fast double
-  // AARCH64-FASTMATH: [[DD:%.*]] = fmul fast double
-  // AARCH64-FASTMATH: [[CCpDD:%.*]] = fadd fast double
-  //
-  // AARCH64-FASTMATH: [[BC:%.*]] = fmul fast double
-  // AARCH64-FASTMATH: [[AD:%.*]] = fmul fast double
-  // AARCH64-FASTMATH: [[BCmAD:%.*]] = fsub fast double
-  //
-  // AARCH64-FASTMATH: fdiv fast double
-  // AARCH64-FASTMATH: fdiv fast double
+  // |C|
+  // AARCH64-FASTMATH: call {{.*}}double @llvm.fabs.f64(double {{.*}})
+  // |D|
+  // AARCH64-FASTMATH-NEXT: call {{.*}}double @llvm.fabs.f64(double {{.*}})
+  // AARCH64-FASTMATH-NEXT: fcmp {{.*}}ugt double
+  // AARCH64-FASTMATH-NEXT: br i1 {{.*}}, label
+  // AARCH64-FASTMATH:      abs_rhsr_greater_or_equal_abs_rhsi:
+
+  // |C| >= |D|
+  // DdC=D/C
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}double
+
+  // CpRD=C+CdC*D
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}double
+
+  // A+BR/CpRD
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}double
+
+  // B-AR/CpRD
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fsub {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}double
+  // AARCH64-FASTMATH-NEXT: br label
+  // AARCH64-FASTMATH:      abs_rhsr_less_than_abs_rhsi:
+
+  // |C| < |D|
+  // CdD=C/D
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}double
+
+  // DpRC=D+CdD*C
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}double
+
+  // (A*CdD+B)/DpRC
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}double
+
+  // (BCdD-A)/DpRC
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fsub {{.*}}double
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}double
+
+  // AARCH64-FASTMATH-NEXT: br label
+  // AARCH64-FASTMATH:      complex_div:
+  // AARCH64-FASTMATH-NEXT: phi {{.*}}double
+  // AARCH64-FASTMATH-NEXT: phi {{.*}}double
   // AARCH64-FASTMATH: ret
   return a / b;
 }
@@ -505,24 +682,68 @@ long double _Complex div_long_double_rc(long double a, long double _Complex b) {
   // PPC: ret
   // SPIR: call spir_func {{.*}} @__divdc3(
 
-  // a / b = (A+iB) / (C+iD) = ((AC+BD)/(CC+DD)) + i((BC-AD)/(CC+DD))
+  // a / b = (A+iB) / (C+iD) = (E+iF)
+  // if (|C| >= |D|)
+  //   DdC = D/C
+  //   CpRD = C+DdC*D
+  //   E = (A+B*DdC)/CpRD
+  //   F = (B-A*DdC)/CpRD
+  // else
+  //   CdD = C/D
+  //   DpRC= D+CdD*C
+  //   E = (A*CdD+B)/DpRC
+  //   F = (B*CdD-A)/DpRC
   // AARCH64-FASTMATH-LABEL: @div_long_double_rc(fp128 noundef nofpclass(nan inf) %a, [2 x fp128] noundef nofpclass(nan inf) alignstack(16) %b.coerce)
-  // A = a
-  // B = 0
-  //
-  // AARCH64-FASTMATH: [[AC:%.*]] = fmul fast fp128
-  // BD = 0
-  // ACpBD = AC
-  //
-  // AARCH64-FASTMATH: [[CC:%.*]] = fmul fast fp128
-  // AARCH64-FASTMATH: [[DD:%.*]] = fmul fast fp128
-  // AARCH64-FASTMATH: [[CCpDD:%.*]] = fadd fast fp128
-  //
-  // BC = 0
-  // AARCH64-FASTMATH: [[AD:%.*]] = fmul fast fp128
-  //
-  // AARCH64-FASTMATH: fdiv fast fp128
-  // AARCH64-FASTMATH: fdiv fast fp128
+  // |C|
+  // AARCH64-FASTMATH: call {{.*}}fp128 @llvm.fabs.f128(fp128 {{.*}})
+  // |D|
+  // AARCH64-FASTMATH-NEXT: call {{.*}}fp128 @llvm.fabs.f128(fp128 {{.*}})
+  // AARCH64-FASTMATH-NEXT: fcmp {{.*}}ugt fp128
+  // AARCH64-FASTMATH-NEXT: br i1 {{.*}}, label
+  // AARCH64-FASTMATH:      abs_rhsr_greater_or_equal_abs_rhsi:
+
+  // |C| >= |D|
+  // DdC=D/C
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}fp128
+
+  // CpRD=C+CdC*D
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}fp128
+
+  // A+BR/CpRD
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}fp128
+
+  // B-AR/CpRD
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fsub {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: br label
+  // AARCH64-FASTMATH:      abs_rhsr_less_than_abs_rhsi:
+
+  // |C| < |D|
+  // CdD=C/D
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}fp128
+
+  // DpRC=D+CdD*C
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}fp128
+
+  // (A*CdD+B)/DpRC
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}fp128
+
+  // (BCdD-A)/DpRC
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fsub {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}fp128
+
+  // AARCH64-FASTMATH-NEXT: br label
+  // AARCH64-FASTMATH:      complex_div:
+  // AARCH64-FASTMATH-NEXT: phi {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: phi {{.*}}fp128
   // AARCH64-FASTMATH: ret
   return a / b;
 }
@@ -537,23 +758,68 @@ long double _Complex div_long_double_cc(long double _Complex a, long double _Com
   // PPC: ret
   // SPIR: call spir_func {{.*}} @__divdc3(
 
-  // a / b = (A+iB) / (C+iD) = ((AC+BD)/(CC+DD)) + i((BC-AD)/(CC+DD))
+  // a / b = (A+iB) / (C+iD) = (E+iF)
+  // if (|C| >= |D|)
+  //   DdC = D/C
+  //   CpRD = C+DdC*D
+  //   E = (A+B*DdC)/CpRD
+  //   F = (B-A*DdC)/CpRD
+  // else
+  //   CdD = C/D
+  //   DpRC= D+CdD*C
+  //   E = (A*CdD+B)/DpRC
+  //   F = (B*CdD-A)/DpRC
   // AARCH64-FASTMATH-LABEL: @div_long_double_cc([2 x fp128] noundef nofpclass(nan inf) alignstack(16) %a.coerce, [2 x fp128] noundef nofpclass(nan inf) alignstack(16) %b.coerce)
-  //
-  // AARCH64-FASTMATH: [[AC:%.*]] = fmul fast fp128
-  // AARCH64-FASTMATH: [[BD:%.*]] = fmul fast fp128
-  // AARCH64-FASTMATH: [[ACpBD:%.*]] = fadd fast fp128
-  //
-  // AARCH64-FASTMATH: [[CC:%.*]] = fmul fast fp128
-  // AARCH64-FASTMATH: [[DD:%.*]] = fmul fast fp128
-  // AARCH64-FASTMATH: [[CCpDD:%.*]] = fadd fast fp128
-  //
-  // AARCH64-FASTMATH: [[BC:%.*]] = fmul fast fp128
-  // AARCH64-FASTMATH: [[AD:%.*]] = fmul fast fp128
-  // AARCH64-FASTMATH: [[BCmAD:%.*]] = fsub fast fp128
-  //
-  // AARCH64-FASTMATH: fdiv fast fp128
-  // AARCH64-FASTMATH: fdiv fast fp128
+  // |C|
+  // AARCH64-FASTMATH: call {{.*}}fp128 @llvm.fabs.f128(fp128 {{.*}})
+  // |D|
+  // AARCH64-FASTMATH-NEXT: call {{.*}}fp128 @llvm.fabs.f128(fp128 {{.*}})
+  // AARCH64-FASTMATH-NEXT: fcmp {{.*}}ugt fp128
+  // AARCH64-FASTMATH-NEXT: br i1 {{.*}}, label
+  // AARCH64-FASTMATH:      abs_rhsr_greater_or_equal_abs_rhsi:
+
+  // |C| >= |D|
+  // DdC=D/C
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}fp128
+
+  // CpRD=C+CdC*D
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}fp128
+
+  // A+BR/CpRD
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}fp128
+
+  // B-AR/CpRD
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fsub {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: br label
+  // AARCH64-FASTMATH:      abs_rhsr_less_than_abs_rhsi:
+
+  // |C| < |D|
+  // CdD=C/D
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}fp128
+
+  // DpRC=D+CdD*C
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}fp128
+
+  // (A*CdD+B)/DpRC
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fadd {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}fp128
+
+  // (BCdD-A)/DpRC
+  // AARCH64-FASTMATH-NEXT: fmul {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fsub {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: fdiv {{.*}}fp128
+
+  // AARCH64-FASTMATH-NEXT: br label
+  // AARCH64-FASTMATH:      complex_div:
+  // AARCH64-FASTMATH-NEXT: phi {{.*}}fp128
+  // AARCH64-FASTMATH-NEXT: phi {{.*}}fp128
   // AARCH64-FASTMATH: ret
   return a / b;
 }

diff  --git a/clang/test/CodeGen/cx-complex-range.c b/clang/test/CodeGen/cx-complex-range.c
new file mode 100644
index 00000000000000..8368fa611335cc
--- /dev/null
+++ b/clang/test/CodeGen/cx-complex-range.c
@@ -0,0 +1,108 @@
+// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown \
+// RUN: -o - | FileCheck %s --check-prefix=FULL
+
+// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown \
+// RUN: -complex-range=limited -o - | FileCheck %s --check-prefix=LMTD
+
+// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown \
+// RUN: -fno-cx-limited-range -o - | FileCheck %s --check-prefix=FULL
+
+// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown \
+// RUN: -complex-range=fortran -o - | FileCheck %s --check-prefix=FRTRN
+
+// Fast math
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu \
+// RUN: -ffast-math -complex-range=limited -emit-llvm -o - %s \
+// RUN: | FileCheck %s --check-prefix=LMTD-FAST
+
+// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown \
+// RUN: -fno-cx-fortran-rules -o - | FileCheck %s --check-prefix=FULL
+
+_Complex float div(_Complex float a, _Complex float b) {
+  // LABEL: define {{.*}} @div(
+  // FULL:  call {{.*}} @__divsc3
+
+  // LMTD:      fmul float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fadd float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fadd float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fsub float
+  // LMTD-NEXT: fdiv float
+  // LMTD-NEXT: fdiv float
+
+  // FRTRN: call {{.*}}float @llvm.fabs.f32(float {{.*}})
+  // FRTRN-NEXT: call {{.*}}float @llvm.fabs.f32(float {{.*}})
+  // FRTRN-NEXT: fcmp {{.*}}ugt float
+  // FRTRN-NEXT: br i1 {{.*}}, label
+  // FRTRN:      abs_rhsr_greater_or_equal_abs_rhsi:
+  // FRTRN-NEXT: fdiv {{.*}}float
+  // FRTRN-NEXT: fmul {{.*}}float
+  // FRTRN-NEXT: fadd {{.*}}float
+  // FRTRN-NEXT: fmul {{.*}}float
+  // FRTRN-NEXT: fadd {{.*}}float
+  // FRTRN-NEXT: fdiv {{.*}}float
+  // FRTRN-NEXT: fmul {{.*}}float
+  // FRTRN-NEXT: fsub {{.*}}float
+  // FRTRN-NEXT: fdiv {{.*}}float
+  // FRTRN-NEXT: br label
+  // FRTRN:      abs_rhsr_less_than_abs_rhsi:
+  // FRTRN-NEXT: fdiv {{.*}}float
+  // FRTRN-NEXT: fmul {{.*}}float
+  // FRTRN-NEXT: fadd {{.*}}float
+  // FRTRN-NEXT: fmul {{.*}}float
+  // FRTRN-NEXT: fadd {{.*}}float
+  // FRTRN-NEXT: fdiv {{.*}}float
+  // FRTRN-NEXT: fmul {{.*}}float
+  // FRTRN-NEXT: fsub {{.*}}float
+  // FRTRN-NEXT: fdiv {{.*}}float
+  // FRTRN-NEXT: br label
+  // FRTRN:      complex_div:
+  // FRTRN-NEXT: phi {{.*}}float
+  // FRTRN-NEXT: phi {{.*}}float
+
+  // LMTD-FAST: fmul {{.*}} float
+  // LMTD-FAST-NEXT: fmul {{.*}} float
+  // LMTD-FAST-NEXT: fadd {{.*}} float
+  // LMTD-FAST-NEXT: fmul {{.*}} float
+  // LMTD-FAST-NEXT: fmul {{.*}} float
+  // LMTD-FAST-NEXT: fadd {{.*}} float
+  // LMTD-FAST-NEXT: fmul {{.*}} float
+  // LMTD-FAST-NEXT: fmul {{.*}} float
+  // LMTD-FAST-NEXT: fsub {{.*}} float
+  // LMTD-FAST-NEXT: fdiv {{.*}} float
+  // LMTD-FAST-NEXT: fdiv {{.*}} float
+
+  return a / b;
+}
+
+_Complex float mul(_Complex float a, _Complex float b) {
+  // LABEL: define {{.*}} @mul(
+  // FULL:  call {{.*}} @__mulsc3
+
+  // LMTD: fmul float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fsub float
+  // LMTD-NEXT: fadd float
+
+  // FRTRN: fmul {{.*}}float
+  // FRTRN-NEXT: fmul {{.*}}float
+  // FRTRN-NEXT: fmul {{.*}}float
+  // FRTRN-NEXT: fmul {{.*}}float
+  // FRTRN-NEXT: fsub {{.*}}float
+  // FRTRN-NEXT: fadd {{.*}}float
+
+  // LMTD-FAST: fmul {{.*}} float
+  // LMTD-FAST-NEXT: fmul {{.*}} float
+  // LMTD-FAST-NEXT: fmul {{.*}} float
+  // LMTD-FAST-NEXT: fmul {{.*}} float
+  // LMTD-FAST-NEXT: fsub {{.*}} float
+  // LMTD-FAST-NEXT: fadd {{.*}} float
+
+  return a * b;
+}

diff  --git a/clang/test/CodeGen/pragma-cx-limited-range.c b/clang/test/CodeGen/pragma-cx-limited-range.c
new file mode 100644
index 00000000000000..926da8afbee558
--- /dev/null
+++ b/clang/test/CodeGen/pragma-cx-limited-range.c
@@ -0,0 +1,107 @@
+// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown \
+// RUN: -o - | FileCheck %s --check-prefix=FULL
+
+// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown \
+// RUN: -complex-range=limited -o - | FileCheck --check-prefix=LMTD %s
+
+// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown \
+// RUN: -fno-cx-limited-range -o - | FileCheck %s --check-prefix=FULL
+
+// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown \
+// RUN: -complex-range=fortran -o - | FileCheck --check-prefix=FRTRN %s
+
+// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown \
+// RUN: -fno-cx-fortran-rules -o - | FileCheck --check-prefix=FULL %s
+
+_Complex float pragma_on_mul(_Complex float a, _Complex float b) {
+#pragma STDC CX_LIMITED_RANGE ON
+  // LABEL: define {{.*}} @pragma_on_mul(
+  // FULL: fmul float
+  // FULL-NEXT: fmul float
+  // FULL-NEXT: fmul float
+  // FULL-NEXT: fmul float
+  // FULL-NEXT: fsub float
+  // FULL-NEXT: fadd float
+
+  // LMTD: fmul float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fsub float
+  // LMTD-NEXT: fadd float
+
+  // FRTRN: fmul float
+  // FRTRN-NEXT: fmul float
+  // FRTRN-NEXT: fmul float
+  // FRTRN-NEXT: fmul float
+  // FRTRN-NEXT: fsub float
+  // FRTRN-NEXT: fadd float
+
+  return a * b;
+}
+
+_Complex float pragma_off_mul(_Complex float a, _Complex float b) {
+#pragma STDC CX_LIMITED_RANGE OFF
+  // LABEL: define {{.*}} @pragma_off_mul(
+  // FULL: call {{.*}} @__mulsc3
+
+  // LMTD: call {{.*}} @__mulsc3
+
+  // FRTRN: call {{.*}} @__mulsc3
+
+  return a * b;
+}
+
+_Complex float pragma_on_div(_Complex float a, _Complex float b) {
+#pragma STDC CX_LIMITED_RANGE ON
+  // LABEL: define {{.*}} @pragma_on_div(
+  // FULL: fmul float
+  // FULL-NEXT: fmul float
+  // FULL-NEXT: fadd float
+  // FULL-NEXT: fmul float
+  // FULL-NEXT: fmul float
+  // FULL-NEXT: fadd float
+  // FULL-NEXT: fmul float
+  // FULL-NEXT: fmul float
+  // FULL-NEXT: fsub float
+  // FULL-NEXT: fdiv float
+  // FULL: fdiv float
+
+  // LMTD: fmul float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fadd float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fadd float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fmul float
+  // LMTD-NEXT: fsub float
+  // LMTD-NEXT: fdiv float
+  // LMTD-NEXT: fdiv float
+
+  // FRTRN: fmul float
+  // FRTRN-NEXT: fmul float
+  // FRTRN-NEXT: fadd float
+  // FRTRN-NEXT: fmul float
+  // FRTRN-NEXT: fmul float
+  // FRTRN-NEXT: fadd float
+  // FRTRN-NEXT: fmul float
+  // FRTRN-NEXT: fmul float
+  // FRTRN-NEXT: fsub float
+  // FRTRN-NEXT: fdiv float
+  // FRTRN-NEXT: fdiv float
+
+  return a / b;
+}
+
+_Complex float pragma_off_div(_Complex float a, _Complex float b) {
+#pragma STDC CX_LIMITED_RANGE OFF
+  // LABEL: define {{.*}} @pragma_off_div(
+  // FULL: call {{.*}} @__divsc3
+
+  // LMTD: call {{.*}} @__divsc3
+
+  // FRTRN: call {{.*}} @__divsc3
+
+  return a / b;
+}

diff  --git a/clang/test/Driver/range.c b/clang/test/Driver/range.c
new file mode 100644
index 00000000000000..8d456a997d6967
--- /dev/null
+++ b/clang/test/Driver/range.c
@@ -0,0 +1,39 @@
+// Test range options for complex multiplication and division.
+
+// RUN: %clang -### -target x86_64 -fcx-limited-range -c %s 2>&1 \
+// RUN:   | FileCheck --check-prefix=LMTD %s
+
+// RUN: %clang -### -target x86_64 -fno-cx-limited-range -c %s 2>&1 \
+// RUN:   | FileCheck %s
+
+// RUN: %clang -### -target x86_64 -fcx-fortran-rules -c %s 2>&1 \
+// RUN:   | FileCheck --check-prefix=FRTRN %s
+
+// RUN: %clang -### -target x86_64 -fno-cx-fortran-rules -c %s 2>&1 \
+// RUN:   | FileCheck  %s
+
+// RUN: %clang -### -target x86_64 -fcx-limited-range \
+// RUN: -fcx-fortran-rules -c %s 2>&1 \
+// RUN:   | FileCheck --check-prefix=WARN1 %s
+
+// RUN: %clang -### -target x86_64 -fcx-fortran-rules \
+// RUN: -fcx-limited-range  -c %s 2>&1 \
+// RUN:   | FileCheck --check-prefix=WARN2 %s
+
+// RUN: %clang -### -target x86_64 -ffast-math -c %s 2>&1 \
+// RUN:   | FileCheck --check-prefix=LMTD %s
+
+// RUN: %clang -### -target x86_64 -ffast-math -fcx-limited-range -c %s 2>&1 \
+// RUN:   | FileCheck --check-prefix=LMTD %s
+
+// RUN: %clang -### -target x86_64 -fcx-limited-range -ffast-math -c %s 2>&1 \
+// RUN:   | FileCheck --check-prefix=LMTD %s
+
+// LMTD: -complex-range=limited
+// LMTD-NOT: -complex-range=fortran
+// CHECK-NOT: -complex-range=limited
+// FRTRN: -complex-range=fortran
+// FRTRN-NOT: -complex-range=limited
+// CHECK-NOT: -complex-range=fortran
+// WARN1: warning: overriding '-fcx-limited-range' option with '-fcx-fortran-rules' [-Woverriding-option]
+// WARN2: warning: overriding '-fcx-fortran-rules' option with '-fcx-limited-range' [-Woverriding-option]

diff  --git a/clang/test/Preprocessor/pragma_unknown.c b/clang/test/Preprocessor/pragma_unknown.c
index a7c4829c279dd1..1d39f8229eb53b 100644
--- a/clang/test/Preprocessor/pragma_unknown.c
+++ b/clang/test/Preprocessor/pragma_unknown.c
@@ -27,6 +27,8 @@
 
 #pragma STDC CX_LIMITED_RANGE    // expected-warning {{expected 'ON' or 'OFF' or 'DEFAULT' in pragma}}
 #pragma STDC CX_LIMITED_RANGE ON FULL POWER  // expected-warning {{expected end of directive in pragma}}
+//expected-error at -1 {{unknown type name 'POWER'}}
+//expected-error at -2 {{expected identifier or '('}}
 // CHECK: {{^}}#pragma STDC CX_LIMITED_RANGE{{$}}
 // CHECK: {{^}}#pragma STDC CX_LIMITED_RANGE ON FULL POWER{{$}}
 


        


More information about the cfe-commits mailing list