Skip to content

Conversation

zahiraam
Copy link
Contributor

@zahiraam zahiraam commented Oct 25, 2023

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)).

This reverts commit a3a7d63.

When compiling with MSVC2022 in  C++32 mode this is giving an
error.
Compiling this simple test case:
t1.cpp:
with -std=c++23 will give the following error:

In file included from C:\Users\zahiraam\t1.cpp:1:
c:\Program files\Microsoft Visual
Studio\2022\Professional\VC\Tools\MSVC\14.35.32215\include\vector:3329:16:
error:
      compile with '-ffixed-point' to enable fixed point types
       3329 |         _Vbase _Accum = 0;
             |                ^
c:\Program files\Microsoft Visual
Studio\2022\Professional\VC\Tools\MSVC\14.35.32215\include\vector:3329:23:
error:
      expected unqualified-id
       3329 |         _Vbase _Accum = 0;
             |                       ^
Please full error in
llvm#67750 (comment)
@github-actions
Copy link

github-actions bot commented Oct 25, 2023

✅ With the latest revision this PR passed the C/C++ code formatter.

zahiraam and others added 17 commits October 25, 2023 12:28
Summary:
This should be `kind` and not `arch`.
This PR fixes the incorrect `mov` instruction in PTX. We actually move a
predicate here, not u32, so the correct instruction should be
`mov.pred`.
If gpu.alloc has no asyn deependency ( in case if gpu.alloc has
hostShared allocation), create a new stream & synchronize. This PR is
follow up to llvm#66401
When a target sets LLVM_ENABLE_RUNTIMES, we should only generate proxy
targets for those runtimes rather than using the global list which may
contain runtimes that are not supported by that particular target.
…70229)

Summary:
This patch simply adds the `-fconvergent-functions` flag to the GPU
compilation. This is in relation to the behaviour of SIMT
architectures under divergence. With the flag, we assume every function
is convergent by default and rely on the compiler's divergence analysis
to transform it if possible.

Fixes: llvm#63853
…tion (llvm#70228)

Summary:
While this is technically a no-op for AMDGPU hardware, in cases where
the user would see fit to add an explicit wavefront sync on Nvidia
hardware, we should also inform the LLVM optimizer that this control
flow is convergent so we do not reorder blocks.
This includes support for using GPRs, FPRs, and stack.
…et (llvm#69399)

This is pre-cursor patch to enabling type units with DWARF5 acceleration
tables.
With this change it allows for entries to contain offsets directly, this
way type
units do not need to be preserved until .debug_names is written out.
…with F/D extensions. (llvm#69804)

This a simple patch to get initial FP support started.
…with F/D extensions. (llvm#69805)

This includes the plumbing for ValueMapping and PartialMapping.
…#69388)

[lldb] Refactor InstrumentationRuntimeAsan and add a new plugin
InstrumentationRuntimeLibsanitizers.

This commit refactors InstrumentationRuntimeASan by pulling out reusable
code into a separate ReportRetriever class. The purpose of the
refactoring is to allow reuse of the ReportRetriever class in another
plugin.

The commit also adds InstrumentationRuntimeASanLibsanitizers, a new runtime
plugin for ASan. The plugin provides the same
functionality as InstrumentationRuntimeASan, but provides a different
set of symbols/library names to search for while activating the plugin.

rdar://112491689
@zahiraam
Copy link
Contributor Author

zahiraam commented Dec 4, 2023

"The PR summary seems to say that -ffast-math enables -fcx-fortran-rules, but the GCC documentations says that it enables -fcx-limited-range. Also, where is that implemented? Should the pragma override it?"
@rjmccall Thanks for the review.
Changed the implementation so that's it's compatible with GCC: -ffast-math implies limited range.
The pragma overrides it. Code at about line #3172 in Clang.cpp.

llvm::Value *AD = Builder.CreateFMul(LHSr, RHSi); // ad
llvm::Value *DSTi = Builder.CreateFAdd(BC, AD); // bc+ad
return ComplexPairTy(DSTr, DSTi);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just do this as a check in the code below right after we emit ResR and ResI? Everything before that seems to be the same.

// supported imaginary types in addition to complex types.
CodeGenFunction::CGFPOptionsRAII FPOptsRAII(CGF, Op.FPFeatures);
if (RHSi && !CGF.getLangOpts().FastMath) {
if (RHSi && Op.FPFeatures.getComplexRange() == LangOptions::CX_Fortran) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we just hoist the !RHSi case up here? That would simplify a lot of these conditions. And if you have it early-exit, you can also have a single check for !LHSi instead of repeating it in every block.

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``, but this option is enabled by
``-ffast-math``.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also talk about this in the main documentation, and not just the release notes?

``__builtin_issubnormal``.
- ``#pragma STDC CX_LIMITED_RANGE on-off-switch`` enables the naive mathematical
formulas for complex division and multiplication with no NaN checking of
results.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion:

- 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.

I think we should add a __has_feature check for this and document it here.

Copy link
Contributor Author

@zahiraam zahiraam Dec 5, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The feature would be the pragma?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Code should be able to check for whether the pragma is supported.

@zahiraam
Copy link
Contributor Author

zahiraam commented Dec 5, 2023

@rjmccall Aaron has objected to the change I made in Pragma.cpp:992 (call to DiscardUntilEndOfDirective) but I think it's correct (I have put it back)?
If we don't discard the tokens to the end of the directive, we wind up getting some additional error messages because it keeps visiting the remaining tokens in the directive and therefore generates additional errors. With this change we are getting the expected warning. Let me know what you think.
Thanks.

Copy link
Contributor

@rjmccall rjmccall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the refactor in division looks a lot better. My comment about the multiplication path still stands. Otherwise, I think this is pretty close. Aaron, are your concerns addressed?

@zahiraam
Copy link
Contributor Author

zahiraam commented Dec 7, 2023

Thanks, the refactor in division looks a lot better. My comment about the multiplication path still stands. Otherwise, I think this is pretty close. Aaron, are your concerns addressed?

Sorry! I missed that.

Copy link
Contributor

@rjmccall rjmccall left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Please give the other reviewers a day or two in case they have more feedback.

@zahiraam
Copy link
Contributor Author

zahiraam commented Dec 7, 2023

LGTM. Please give the other reviewers a day or two in case they have more feedback.

Thank you!

Copy link
Collaborator

@AaronBallman AaronBallman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@zahiraam zahiraam merged commit b40c534 into llvm:main Dec 11, 2023
@arichardson
Copy link
Member

@zahiraam I'd suggest you edit the commit message next time when you merge, all the merged commits should not be mentioned in the co-authored-by list.

@zahiraam
Copy link
Contributor Author

@zahiraam I'd suggest you edit the commit message next time when you merge, all the merged commits should not be mentioned in the co-authored-by list.

@arichardson Sorry I didn't notice that. Is there something I can do at this point?

@arichardson
Copy link
Member

@zahiraam I'd suggest you edit the commit message next time when you merge, all the merged commits should not be mentioned in the co-authored-by list.

@arichardson Sorry I didn't notice that. Is there something I can do at this point?

No it's in the repository now so effectively immutable. It's not a big deal just letting you know for future patches.

@zahiraam
Copy link
Contributor Author

@zahiraam I'd suggest you edit the commit message next time when you merge, all the merged commits should not be mentioned in the co-authored-by list.

@arichardson Sorry I didn't notice that. Is there something I can do at this point?

No it's in the repository now so effectively immutable. It's not a big deal just letting you know for future patches.

Thanks! will watch for it next time.

@zahiraam zahiraam deleted the ComplexRange branch January 3, 2024 20:23
@Endilll Endilll added the clang Clang issues not falling into any other category label Jul 14, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 14, 2025

@llvm/pr-subscribers-clang

Author: Zahira Ammarguellat (zahiraam)

Changes

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)).


Patch is 54.66 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/70244.diff

21 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+13)
  • (modified) clang/docs/UsersManual.rst (+15)
  • (modified) clang/include/clang/Basic/FPOptions.def (+1)
  • (modified) clang/include/clang/Basic/Features.def (+1)
  • (modified) clang/include/clang/Basic/LangOptions.def (+2)
  • (modified) clang/include/clang/Basic/LangOptions.h (+3)
  • (modified) clang/include/clang/Basic/TokenKinds.def (+5)
  • (modified) clang/include/clang/Driver/Options.td (+24)
  • (modified) clang/include/clang/Parse/Parser.h (+4)
  • (modified) clang/include/clang/Sema/Sema.h (+5)
  • (modified) clang/lib/CodeGen/CGExprComplex.cpp (+134-32)
  • (modified) clang/lib/Driver/ToolChains/Clang.cpp (+69-2)
  • (modified) clang/lib/Parse/ParsePragma.cpp (+39-1)
  • (modified) clang/lib/Parse/ParseStmt.cpp (+11)
  • (modified) clang/lib/Parse/Parser.cpp (+3)
  • (modified) clang/lib/Sema/SemaAttr.cpp (+8)
  • (modified) clang/test/CodeGen/complex-math.c (+367-101)
  • (added) clang/test/CodeGen/cx-complex-range.c (+108)
  • (added) clang/test/CodeGen/pragma-cx-limited-range.c (+107)
  • (added) clang/test/Driver/range.c (+39)
  • (modified) clang/test/Preprocessor/pragma_unknown.c (+2)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 52c9d6eb69617..6566cc33e3a4a 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -243,6 +243,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
 -------------------------
 
@@ -872,6 +882,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 2e658557b0e31..f5e348bba9d8a 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 5b923a1944e50..79f04c89c9fed 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 adaf2e413f2f6..c3d1a312f0d91 100644
--- a/clang/include/clang/Basic/Features.def
+++ b/clang/include/clang/Basic/Features.def
@@ -102,6 +102,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 cd77b22bf3ace..9730e2730da65 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 2d167dd2bdf12..aa7b8306e1c36 100644
--- a/clang/include/clang/Basic/LangOptions.h
+++ b/clang/include/clang/Basic/LangOptions.h
@@ -391,6 +391,8 @@ class LangOptions : public LangOptionsBase {
     IncompleteOnly = 3,
   };
 
+  enum ComplexRangeKind { CX_Full, CX_Limited, CX_Fortran };
+
 public:
   /// The used language standard.
   LangStandard::Kind LangStd;
@@ -740,6 +742,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 3ab420821d82b..47738a71c8144 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -908,6 +908,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 df12ba8fbcb29..24aee940d7cd8 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 d20a26dbf2562..cca012f8e23d6 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -772,6 +772,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 63a9f9d4cffe2..9f54415d70331 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -10997,6 +10997,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 f3cbd1d0451eb..e532794b71bdb 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 6dec117aed105..ac456352ec5cf 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -2790,6 +2790,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,
@@ -2836,6 +2865,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_f...
[truncated]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.