[flang-commits] [clang] [flang] [flang] Add -fcomplex-arithmetic= option and select complex division algorithm (PR #146641)

Shunsuke Watanabe via flang-commits flang-commits at lists.llvm.org
Mon Jul 7 02:43:41 PDT 2025


https://github.com/s-watanabe314 updated https://github.com/llvm/llvm-project/pull/146641

>From baf87a6002d740868daeeba0f89d55c05e85b712 Mon Sep 17 00:00:00 2001
From: s-watanabe314 <watanabe.shu-06 at fujitsu.com>
Date: Fri, 18 Apr 2025 09:04:07 +0900
Subject: [PATCH 1/6] [flang] Add -fcomplex-arithmetic= option and select
 complex division algorithm

This patch adds an option to select the method for computing complex
number division. It uses `LoweringOptions` to determine whether to lower
complex division to a runtime function call or to MLIR's `complex.div`,
and `CodeGenOptions` to select the computation algorithm for
`complex.div`. The available option values and their corresponding
algorithms are as follows:
- `full`: Lower to a runtime function call. (Default behavior)
- `improved`: Lower to `complex.div` and expand to Smith's algorithm.
- `basic`: Lower to `complex.div` and expand to the algebraic algorithm.

See also the discussion in the following discourse post:
https://discourse.llvm.org/t/optimization-of-complex-number-division/83468
---
 clang/include/clang/Driver/Options.td         |   4 +-
 clang/lib/Driver/ToolChains/Flang.cpp         |  47 ++
 .../include/flang/Frontend/CodeGenOptions.def |   1 +
 flang/include/flang/Frontend/CodeGenOptions.h |  25 +
 flang/include/flang/Lower/LoweringOptions.def |   4 +
 .../flang/Optimizer/Builder/FIRBuilder.h      |  15 +
 .../include/flang/Optimizer/CodeGen/CodeGen.h |   6 +
 flang/include/flang/Tools/CrossToolHelpers.h  |   3 +
 flang/lib/Frontend/CompilerInvocation.cpp     |  21 +
 flang/lib/Frontend/FrontendActions.cpp        |   2 +
 flang/lib/Lower/Bridge.cpp                    |   2 +
 flang/lib/Lower/ConvertExprToHLFIR.cpp        |  12 +-
 flang/lib/Optimizer/CodeGen/CodeGen.cpp       |  15 +-
 flang/lib/Optimizer/Passes/Pipelines.cpp      |   1 +
 flang/test/Driver/complex-range.f90           |  23 +
 .../complex-div-to-llvm-kind10.f90            | 131 +++++
 .../complex-div-to-llvm-kind16.f90            | 131 +++++
 .../test/Integration/complex-div-to-llvm.f90  | 508 ++++++++++++++++++
 .../HLFIR/complex-div-to-hlfir-kind10.f90     |  34 ++
 .../HLFIR/complex-div-to-hlfir-kind16.f90     |  35 ++
 .../test/Lower/HLFIR/complex-div-to-hlfir.f90 |  94 ++++
 flang/tools/bbc/bbc.cpp                       |   8 +
 22 files changed, 1117 insertions(+), 5 deletions(-)
 create mode 100644 flang/test/Driver/complex-range.f90
 create mode 100644 flang/test/Integration/complex-div-to-llvm-kind10.f90
 create mode 100644 flang/test/Integration/complex-div-to-llvm-kind16.f90
 create mode 100644 flang/test/Integration/complex-div-to-llvm.f90
 create mode 100644 flang/test/Lower/HLFIR/complex-div-to-hlfir-kind10.f90
 create mode 100644 flang/test/Lower/HLFIR/complex-div-to-hlfir-kind16.f90
 create mode 100644 flang/test/Lower/HLFIR/complex-div-to-hlfir.f90

diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 9911d752966e3..58209ceb5dc54 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1023,12 +1023,12 @@ defm offload_uniform_block : BoolFOption<"offload-uniform-block",
   BothFlags<[], [ClangOption], " that kernels are launched with uniform block sizes (default true for CUDA/HIP and false otherwise)">>;
 
 def fcomplex_arithmetic_EQ : Joined<["-"], "fcomplex-arithmetic=">, Group<f_Group>,
-  Visibility<[ClangOption, CC1Option]>,
+  Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>,
   Values<"full,improved,promoted,basic">, NormalizedValuesScope<"LangOptions">,
   NormalizedValues<["CX_Full", "CX_Improved", "CX_Promoted", "CX_Basic"]>;
 
 def complex_range_EQ : Joined<["-"], "complex-range=">, Group<f_Group>,
-  Visibility<[CC1Option]>,
+  Visibility<[CC1Option, FC1Option]>,
   Values<"full,improved,promoted,basic">, NormalizedValuesScope<"LangOptions">,
   NormalizedValues<["CX_Full", "CX_Improved", "CX_Promoted", "CX_Basic"]>,
   MarshallingInfoEnum<LangOpts<"ComplexRange">, "CX_Full">;
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index e4e321ba1e195..ba5db7e3aba43 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -595,6 +595,30 @@ void Flang::addOffloadOptions(Compilation &C, const InputInfoList &Inputs,
   addOpenMPHostOffloadingArgs(C, JA, Args, CmdArgs);
 }
 
+static std::string ComplexRangeKindToStr(LangOptions::ComplexRangeKind Range) {
+  switch (Range) {
+  case LangOptions::ComplexRangeKind::CX_Full:
+    return "full";
+    break;
+  case LangOptions::ComplexRangeKind::CX_Improved:
+    return "improved";
+    break;
+  case LangOptions::ComplexRangeKind::CX_Basic:
+    return "basic";
+    break;
+  default:
+    return "";
+  }
+}
+
+static std::string
+RenderComplexRangeOption(LangOptions::ComplexRangeKind Range) {
+  std::string ComplexRangeStr = ComplexRangeKindToStr(Range);
+  if (!ComplexRangeStr.empty())
+    return "-complex-range=" + ComplexRangeStr;
+  return ComplexRangeStr;
+}
+
 static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
                                     ArgStringList &CmdArgs) {
   StringRef FPContract;
@@ -605,6 +629,8 @@ static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
   bool AssociativeMath = false;
   bool ReciprocalMath = false;
 
+  LangOptions::ComplexRangeKind Range = LangOptions::ComplexRangeKind::CX_None;
+
   if (const Arg *A = Args.getLastArg(options::OPT_ffp_contract)) {
     const StringRef Val = A->getValue();
     if (Val == "fast" || Val == "off") {
@@ -629,6 +655,20 @@ static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
     default:
       continue;
 
+    case options::OPT_fcomplex_arithmetic_EQ: {
+      StringRef Val = A->getValue();
+      if (Val == "full")
+        Range = LangOptions::ComplexRangeKind::CX_Full;
+      else if (Val == "improved")
+        Range = LangOptions::ComplexRangeKind::CX_Improved;
+      else if (Val == "basic")
+        Range = LangOptions::ComplexRangeKind::CX_Basic;
+      else {
+        D.Diag(diag::err_drv_unsupported_option_argument)
+            << A->getSpelling() << Val;
+      }
+      break;
+    }
     case options::OPT_fhonor_infinities:
       HonorINFs = true;
       break;
@@ -699,6 +739,13 @@ static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
   if (!Recip.empty())
     CmdArgs.push_back(Args.MakeArgString("-mrecip=" + Recip));
 
+  if (Range != LangOptions::ComplexRangeKind::CX_None) {
+    std::string ComplexRangeStr = RenderComplexRangeOption(Range);
+    CmdArgs.push_back(Args.MakeArgString(ComplexRangeStr));
+    CmdArgs.push_back(Args.MakeArgString("-fcomplex-arithmetic=" +
+                                         ComplexRangeKindToStr(Range)));
+  }
+
   if (!HonorINFs && !HonorNaNs && AssociativeMath && ReciprocalMath &&
       ApproxFunc && !SignedZeros &&
       (FPContract == "fast" || FPContract.empty())) {
diff --git a/flang/include/flang/Frontend/CodeGenOptions.def b/flang/include/flang/Frontend/CodeGenOptions.def
index ae12aec518108..cdeea93c9aecb 100644
--- a/flang/include/flang/Frontend/CodeGenOptions.def
+++ b/flang/include/flang/Frontend/CodeGenOptions.def
@@ -52,6 +52,7 @@ ENUM_CODEGENOPT(RelocationModel, llvm::Reloc::Model, 3, llvm::Reloc::PIC_) ///<
 ENUM_CODEGENOPT(DebugInfo,  llvm::codegenoptions::DebugInfoKind, 4,  llvm::codegenoptions::NoDebugInfo) ///< Level of debug info to generate
 ENUM_CODEGENOPT(VecLib, llvm::driver::VectorLibrary, 4, llvm::driver::VectorLibrary::NoLibrary) ///< Vector functions library to use
 ENUM_CODEGENOPT(FramePointer, llvm::FramePointerKind, 2, llvm::FramePointerKind::None) ///< Enable the usage of frame pointers
+ENUM_CODEGENOPT(ComplexRange, ComplexRangeKind, 3, ComplexRangeKind::CX_Full) ///< Method for calculating complex number division
 
 ENUM_CODEGENOPT(DoConcurrentMapping, DoConcurrentMappingKind, 2, DoConcurrentMappingKind::DCMK_None) ///< Map `do concurrent` to OpenMP
 
diff --git a/flang/include/flang/Frontend/CodeGenOptions.h b/flang/include/flang/Frontend/CodeGenOptions.h
index bad17c8309eb8..df6063cc90340 100644
--- a/flang/include/flang/Frontend/CodeGenOptions.h
+++ b/flang/include/flang/Frontend/CodeGenOptions.h
@@ -192,6 +192,31 @@ class CodeGenOptions : public CodeGenOptionsBase {
     return getProfileUse() == llvm::driver::ProfileCSIRInstr;
   }
 
+  /// Controls the various implementations for complex division.
+  enum ComplexRangeKind {
+    /// Implementation of complex division using a call to runtime library
+    /// functions. Overflow and non-finite values are handled by the library
+    /// implementation. This is the default value.
+    CX_Full,
+
+    /// Implementation of complex division offering an improved handling
+    /// for overflow in intermediate calculations. Overflow and non-finite
+    /// values are handled by MLIR's implementation of "complex.div", but this
+    /// may change in the future.
+    CX_Improved,
+
+    /// Implementation of complex division using algebraic formulas at source
+    /// precision. No special handling to avoid overflow. NaN and infinite
+    /// values are not handled.
+    CX_Basic,
+
+    /// No range rule is enabled.
+    CX_None
+
+    /// TODO: Implemention of other values as needed. In Clang, "CX_Promoted"
+    /// is implemented. (See clang/Basic/LangOptions.h)
+  };
+
   // Define accessors/mutators for code generation options of enumeration type.
 #define CODEGENOPT(Name, Bits, Default)
 #define ENUM_CODEGENOPT(Name, Type, Bits, Default)                             \
diff --git a/flang/include/flang/Lower/LoweringOptions.def b/flang/include/flang/Lower/LoweringOptions.def
index 3263ab129d076..8135704971aa4 100644
--- a/flang/include/flang/Lower/LoweringOptions.def
+++ b/flang/include/flang/Lower/LoweringOptions.def
@@ -70,5 +70,9 @@ ENUM_LOWERINGOPT(CUDARuntimeCheck, unsigned, 1, 0)
 /// derived types defined in other compilation units.
 ENUM_LOWERINGOPT(SkipExternalRttiDefinition, unsigned, 1, 0)
 
+/// If true, convert complex number division to runtime on the frontend.
+/// If false, lower to the complex dialect of MLIR.
+/// On by default.
+ENUM_LOWERINGOPT(ComplexDivisionToRuntime, unsigned, 1, 1)
 #undef LOWERINGOPT
 #undef ENUM_LOWERINGOPT
diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
index e1eaab3346901..b1513850a9048 100644
--- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
@@ -609,6 +609,17 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
     return integerOverflowFlags;
   }
 
+  /// Set ComplexDivisionToRuntimeFlag value for whether complex number division
+  /// is lowered to a runtime function by this builder.
+  void setComplexDivisionToRuntimeFlag(bool flag) {
+    complexDivisionToRuntimeFlag = flag;
+  }
+
+  /// Get current ComplexDivisionToRuntimeFlag value.
+  bool getComplexDivisionToRuntimeFlag() const {
+    return complexDivisionToRuntimeFlag;
+  }
+
   /// Dump the current function. (debug)
   LLVM_DUMP_METHOD void dumpFunc();
 
@@ -673,6 +684,10 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
   /// mlir::arith::IntegerOverflowFlagsAttr.
   mlir::arith::IntegerOverflowFlags integerOverflowFlags{};
 
+  /// Flag to control whether complex number division is lowered to a runtime
+  /// function or to the MLIR complex dialect.
+  bool complexDivisionToRuntimeFlag = true;
+
   /// fir::GlobalOp and func::FuncOp symbol table to speed-up
   /// lookups.
   mlir::SymbolTable *symbolTable = nullptr;
diff --git a/flang/include/flang/Optimizer/CodeGen/CodeGen.h b/flang/include/flang/Optimizer/CodeGen/CodeGen.h
index 93f07d8d5d4d9..e9a07a8dde5cd 100644
--- a/flang/include/flang/Optimizer/CodeGen/CodeGen.h
+++ b/flang/include/flang/Optimizer/CodeGen/CodeGen.h
@@ -9,6 +9,7 @@
 #ifndef FORTRAN_OPTIMIZER_CODEGEN_CODEGEN_H
 #define FORTRAN_OPTIMIZER_CODEGEN_CODEGEN_H
 
+#include "flang/Frontend/CodeGenOptions.h"
 #include "mlir/IR/BuiltinOps.h"
 #include "mlir/Pass/Pass.h"
 #include "mlir/Pass/PassRegistry.h"
@@ -58,6 +59,11 @@ struct FIRToLLVMPassOptions {
   // the name of the global variable corresponding to a derived
   // type's descriptor.
   bool typeDescriptorsRenamedForAssembly = false;
+
+  // Specify the calculation method for complex number division used by the
+  // Conversion pass of the MLIR complex dialect.
+  Fortran::frontend::CodeGenOptions::ComplexRangeKind ComplexRange =
+      Fortran::frontend::CodeGenOptions::ComplexRangeKind::CX_Full;
 };
 
 /// Convert FIR to the LLVM IR dialect with default options.
diff --git a/flang/include/flang/Tools/CrossToolHelpers.h b/flang/include/flang/Tools/CrossToolHelpers.h
index 337685c82af5f..df1da27058552 100644
--- a/flang/include/flang/Tools/CrossToolHelpers.h
+++ b/flang/include/flang/Tools/CrossToolHelpers.h
@@ -140,6 +140,9 @@ struct MLIRToLLVMPassPipelineConfig : public FlangEPCallBacks {
   std::string InstrumentFunctionExit =
       ""; ///< Name of the instrument-function that is called on each
           ///< function-exit
+  Fortran::frontend::CodeGenOptions::ComplexRangeKind ComplexRange =
+      Fortran::frontend::CodeGenOptions::ComplexRangeKind::
+          CX_Full; ///< Method for calculating complex number division
 };
 
 struct OffloadModuleOpts {
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 30d81f3daa969..86ec410b1f70f 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -484,6 +484,21 @@ static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
   }
 
   parseDoConcurrentMapping(opts, args, diags);
+
+  if (const auto *arg =
+          args.getLastArg(clang::driver::options::OPT_complex_range_EQ)) {
+    llvm::StringRef argValue = llvm::StringRef(arg->getValue());
+    if (argValue == "full") {
+      opts.setComplexRange(CodeGenOptions::ComplexRangeKind::CX_Full);
+    } else if (argValue == "improved") {
+      opts.setComplexRange(CodeGenOptions::ComplexRangeKind::CX_Improved);
+    } else if (argValue == "basic") {
+      opts.setComplexRange(CodeGenOptions::ComplexRangeKind::CX_Basic);
+    } else {
+      diags.Report(clang::diag::err_drv_invalid_value)
+          << arg->getAsString(args) << arg->getValue();
+    }
+  }
 }
 
 /// Parses all target input arguments and populates the target
@@ -1811,4 +1826,10 @@ void CompilerInvocation::setLoweringOptions() {
       .setNoSignedZeros(langOptions.NoSignedZeros)
       .setAssociativeMath(langOptions.AssociativeMath)
       .setReciprocalMath(langOptions.ReciprocalMath);
+
+  if (codegenOpts.getComplexRange() ==
+          CodeGenOptions::ComplexRangeKind::CX_Improved ||
+      codegenOpts.getComplexRange() ==
+          CodeGenOptions::ComplexRangeKind::CX_Basic)
+    loweringOpts.setComplexDivisionToRuntime(false);
 }
diff --git a/flang/lib/Frontend/FrontendActions.cpp b/flang/lib/Frontend/FrontendActions.cpp
index bf15def3f3b2e..b5f4f9421f633 100644
--- a/flang/lib/Frontend/FrontendActions.cpp
+++ b/flang/lib/Frontend/FrontendActions.cpp
@@ -750,6 +750,8 @@ void CodeGenAction::generateLLVMIR() {
   if (ci.getInvocation().getLoweringOpts().getIntegerWrapAround())
     config.NSWOnLoopVarInc = false;
 
+  config.ComplexRange = opts.getComplexRange();
+
   // Create the pass pipeline
   fir::createMLIRToLLVMPassPipeline(pm, config, getCurrentFile());
   (void)mlir::applyPassManagerCLOptions(pm);
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index ff35840a6668c..7e95c640e73b0 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -5746,6 +5746,8 @@ class FirConverter : public Fortran::lower::AbstractConverter {
     builder =
         new fir::FirOpBuilder(func, bridge.getKindMap(), &mlirSymbolTable);
     assert(builder && "FirOpBuilder did not instantiate");
+    builder->setComplexDivisionToRuntimeFlag(
+        bridge.getLoweringOptions().getComplexDivisionToRuntime());
     builder->setFastMathFlags(bridge.getLoweringOptions().getMathOptions());
     builder->setInsertionPointToStart(&func.front());
     if (funit.parent.isA<Fortran::lower::pft::FunctionLikeUnit>()) {
diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp
index df8dfbc72c030..cb338618dbf3b 100644
--- a/flang/lib/Lower/ConvertExprToHLFIR.cpp
+++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp
@@ -1066,8 +1066,16 @@ struct BinaryOp<Fortran::evaluate::Divide<
     mlir::Type ty = Fortran::lower::getFIRType(
         builder.getContext(), Fortran::common::TypeCategory::Complex, KIND,
         /*params=*/{});
-    return hlfir::EntityWithAttributes{
-        fir::genDivC(builder, loc, ty, lhs, rhs)};
+
+    // TODO: Ideally, complex number division operations should always be
+    // lowered to MLIR. However, converting them to the runtime via MLIR causes
+    // ABI issues.
+    if (builder.getComplexDivisionToRuntimeFlag())
+      return hlfir::EntityWithAttributes{
+          fir::genDivC(builder, loc, ty, lhs, rhs)};
+    else
+      return hlfir::EntityWithAttributes{
+          builder.create<mlir::complex::DivOp>(loc, lhs, rhs)};
   }
 };
 
diff --git a/flang/lib/Optimizer/CodeGen/CodeGen.cpp b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
index 2b018912b40e4..4cf533f195e69 100644
--- a/flang/lib/Optimizer/CodeGen/CodeGen.cpp
+++ b/flang/lib/Optimizer/CodeGen/CodeGen.cpp
@@ -4119,7 +4119,20 @@ class FIRToLLVMLowering
     mathToFuncsOptions.minWidthOfFPowIExponent = 33;
     mathConvertionPM.addPass(
         mlir::createConvertMathToFuncs(mathToFuncsOptions));
-    mathConvertionPM.addPass(mlir::createConvertComplexToStandardPass());
+
+    mlir::ConvertComplexToStandardPassOptions complexToStandardOptions{};
+    if (options.ComplexRange ==
+        Fortran::frontend::CodeGenOptions::ComplexRangeKind::CX_Basic) {
+      complexToStandardOptions.complexRange =
+          mlir::complex::ComplexRangeFlags::basic;
+    } else if (options.ComplexRange == Fortran::frontend::CodeGenOptions::
+                                           ComplexRangeKind::CX_Improved) {
+      complexToStandardOptions.complexRange =
+          mlir::complex::ComplexRangeFlags::improved;
+    }
+    mathConvertionPM.addPass(
+        mlir::createConvertComplexToStandardPass(complexToStandardOptions));
+
     // Convert Math dialect operations into LLVM dialect operations.
     // There is no way to prefer MathToLLVM patterns over MathToLibm
     // patterns (applied below), so we have to run MathToLLVM conversion here.
diff --git a/flang/lib/Optimizer/Passes/Pipelines.cpp b/flang/lib/Optimizer/Passes/Pipelines.cpp
index 42d9e7ba2418f..d940934f9f6ad 100644
--- a/flang/lib/Optimizer/Passes/Pipelines.cpp
+++ b/flang/lib/Optimizer/Passes/Pipelines.cpp
@@ -113,6 +113,7 @@ void addFIRToLLVMPass(mlir::PassManager &pm,
   options.forceUnifiedTBAATree = useOldAliasTags;
   options.typeDescriptorsRenamedForAssembly =
       !disableCompilerGeneratedNamesConversion;
+  options.ComplexRange = config.ComplexRange;
   addPassConditionally(pm, disableFirToLlvmIr,
                        [&]() { return fir::createFIRToLLVMPass(options); });
   // The dialect conversion framework may leave dead unrealized_conversion_cast
diff --git a/flang/test/Driver/complex-range.f90 b/flang/test/Driver/complex-range.f90
new file mode 100644
index 0000000000000..e5a1ba9068ac9
--- /dev/null
+++ b/flang/test/Driver/complex-range.f90
@@ -0,0 +1,23 @@
+! Test range options for complex multiplication and division.
+
+! RUN: %flang -### -c %s 2>&1 \
+! RUN:   | FileCheck %s --check-prefix=RANGE
+
+! RUN: %flang -### -fcomplex-arithmetic=full -c %s 2>&1 \
+! RUN:   | FileCheck %s --check-prefix=FULL
+
+! RUN: %flang -### -fcomplex-arithmetic=improved -c %s 2>&1 \
+! RUN:   | FileCheck %s --check-prefix=IMPRVD
+
+! RUN: %flang -### -fcomplex-arithmetic=basic -c %s 2>&1 \
+! RUN:   | FileCheck %s --check-prefix=BASIC
+
+! RUN: not %flang -### -fcomplex-arithmetic=foo -c %s 2>&1 \
+! RUN:   | FileCheck %s --check-prefix=ERR
+
+! RANGE-NOT: -complex-range=
+! FULL: -complex-range=full
+! IMPRVD: -complex-range=improved
+! BASIC: -complex-range=basic
+
+! ERR: error: unsupported argument 'foo' to option '-fcomplex-arithmetic='
diff --git a/flang/test/Integration/complex-div-to-llvm-kind10.f90 b/flang/test/Integration/complex-div-to-llvm-kind10.f90
new file mode 100644
index 0000000000000..04d1f7ed9b024
--- /dev/null
+++ b/flang/test/Integration/complex-div-to-llvm-kind10.f90
@@ -0,0 +1,131 @@
+! Test lowering complex division to llvm ir according to options
+
+! REQUIRES: target=x86_64{{.*}}
+! RUN: %flang -fcomplex-arithmetic=improved -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,IMPRVD
+! RUN: %flang -fcomplex-arithmetic=basic -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,BASIC
+
+
+! CHECK-LABEL: @div_test_extended
+! CHECK-SAME: ptr %[[RET:.*]], ptr %[[LHS:.*]], ptr %[[RHS:.*]])
+! CHECK: %[[LOAD_LHS:.*]] = load { x86_fp80, x86_fp80 }, ptr %[[LHS]], align 16
+! CHECK: %[[LOAD_RHS:.*]] = load { x86_fp80, x86_fp80 }, ptr %[[RHS]], align 16
+! CHECK: %[[LHS_REAL:.*]] = extractvalue { x86_fp80, x86_fp80 } %[[LOAD_LHS]], 0
+! CHECK: %[[LHS_IMAG:.*]] = extractvalue { x86_fp80, x86_fp80 } %[[LOAD_LHS]], 1
+! CHECK: %[[RHS_REAL:.*]] = extractvalue { x86_fp80, x86_fp80 } %[[LOAD_RHS]], 0
+! CHECK: %[[RHS_IMAG:.*]] = extractvalue { x86_fp80, x86_fp80 } %[[LOAD_RHS]], 1
+
+! IMPRVD: %[[RHS_REAL_IMAG_RATIO:.*]] = fdiv contract x86_fp80 %[[RHS_REAL]], %[[RHS_IMAG]]
+! IMPRVD: %[[RHS_REAL_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract x86_fp80 %[[RHS_REAL_IMAG_RATIO]], %[[RHS_REAL]]
+! IMPRVD: %[[RHS_REAL_IMAG_DENOM:.*]] = fadd contract x86_fp80 %[[RHS_IMAG]], %[[RHS_REAL_TIMES_RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[LHS_REAL_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract x86_fp80 %[[LHS_REAL]], %[[RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[REAL_NUMERATOR_1:.*]] = fadd contract x86_fp80 %[[LHS_REAL_TIMES_RHS_REAL_IMAG_RATIO]], %[[LHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_1:.*]] = fdiv contract x86_fp80 %[[REAL_NUMERATOR_1]], %[[RHS_REAL_IMAG_DENOM]]
+! IMPRVD: %[[LHS_IMAG_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract x86_fp80 %[[LHS_IMAG]], %[[RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[IMAG_NUMERATOR_1:.*]] = fsub contract x86_fp80 %[[LHS_IMAG_TIMES_RHS_REAL_IMAG_RATIO]], %[[LHS_REAL]]
+! IMPRVD: %[[RESULT_IMAG_1:.*]] = fdiv contract x86_fp80 %[[IMAG_NUMERATOR_1]], %[[RHS_REAL_IMAG_DENOM]]
+! IMPRVD: %[[RHS_IMAG_REAL_RATIO:.*]] = fdiv contract x86_fp80 %[[RHS_IMAG]], %[[RHS_REAL]]
+! IMPRVD: %[[RHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract x86_fp80 %[[RHS_IMAG_REAL_RATIO]], %[[RHS_IMAG]]
+! IMPRVD: %[[RHS_IMAG_REAL_DENOM:.*]] = fadd contract x86_fp80 %[[RHS_REAL]], %[[RHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[LHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract x86_fp80 %[[LHS_IMAG]], %[[RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[REAL_NUMERATOR_2:.*]] = fadd contract x86_fp80 %[[LHS_REAL]], %[[LHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[RESULT_REAL_2:.*]] = fdiv contract x86_fp80 %[[REAL_NUMERATOR_2]], %[[RHS_IMAG_REAL_DENOM]]
+! IMPRVD: %[[LHS_REAL_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract x86_fp80 %[[LHS_REAL]], %[[RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[IMAG_NUMERATOR_2:.*]] = fsub contract x86_fp80 %[[LHS_IMAG]], %[[LHS_REAL_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[RESULT_IMAG_2:.*]] = fdiv contract x86_fp80 %[[IMAG_NUMERATOR_2]], %[[RHS_IMAG_REAL_DENOM]]
+
+! Case 1. Zero denominator, numerator contains at most one NaN value.
+! IMPRVD: %[[RHS_REAL_ABS:.*]] = call contract x86_fp80 @llvm.fabs.f80(x86_fp80 %[[RHS_REAL]])
+! IMPRVD: %[[RHS_REAL_ABS_IS_ZERO:.*]] = fcmp oeq x86_fp80 %[[RHS_REAL_ABS]], 0xK00000000000000000000
+! IMPRVD: %[[RHS_IMAG_ABS:.*]] = call contract x86_fp80 @llvm.fabs.f80(x86_fp80 %[[RHS_IMAG]])
+! IMPRVD: %[[RHS_IMAG_ABS_IS_ZERO:.*]] = fcmp oeq x86_fp80 %[[RHS_IMAG_ABS]], 0xK00000000000000000000
+! IMPRVD: %[[LHS_REAL_IS_NOT_NAN:.*]] = fcmp ord x86_fp80 %[[LHS_REAL]], 0xK00000000000000000000
+! IMPRVD: %[[LHS_IMAG_IS_NOT_NAN:.*]] = fcmp ord x86_fp80 %[[LHS_IMAG]], 0xK00000000000000000000
+! IMPRVD: %[[LHS_CONTAINS_NOT_NAN_VALUE:.*]] = or i1 %[[LHS_REAL_IS_NOT_NAN]], %[[LHS_IMAG_IS_NOT_NAN]]
+! IMPRVD: %[[RHS_IS_ZERO:.*]] = and i1 %[[RHS_REAL_ABS_IS_ZERO]], %[[RHS_IMAG_ABS_IS_ZERO]]
+! IMPRVD: %[[RESULT_IS_INFINITY:.*]] = and i1 %[[LHS_CONTAINS_NOT_NAN_VALUE]], %[[RHS_IS_ZERO]]
+! IMPRVD: %[[INF_WITH_SIGN_OF_RHS_REAL:.*]] = call x86_fp80 @llvm.copysign.f80(x86_fp80 0xK7FFF8000000000000000, x86_fp80 %[[RHS_REAL]])
+! IMPRVD: %[[INFINITY_RESULT_REAL:.*]] = fmul contract x86_fp80 %[[INF_WITH_SIGN_OF_RHS_REAL]], %[[LHS_REAL]]
+! IMPRVD: %[[INFINITY_RESULT_IMAG:.*]] = fmul contract x86_fp80 %[[INF_WITH_SIGN_OF_RHS_REAL]], %[[LHS_IMAG]]
+
+! Case 2. Infinite numerator, finite denominator.
+! IMPRVD: %[[RHS_REAL_FINITE:.*]] = fcmp one x86_fp80 %[[RHS_REAL_ABS]], 0xK7FFF8000000000000000
+! IMPRVD: %[[RHS_IMAG_FINITE:.*]] = fcmp one x86_fp80 %[[RHS_IMAG_ABS]], 0xK7FFF8000000000000000
+! IMPRVD: %[[RHS_IS_FINITE:.*]] = and i1 %[[RHS_REAL_FINITE]], %[[RHS_IMAG_FINITE]]
+! IMPRVD: %[[LHS_REAL_ABS:.*]] = call contract x86_fp80 @llvm.fabs.f80(x86_fp80 %[[LHS_REAL]])
+! IMPRVD: %[[LHS_REAL_INFINITE:.*]] = fcmp oeq x86_fp80 %[[LHS_REAL_ABS]], 0xK7FFF8000000000000000
+! IMPRVD: %[[LHS_IMAG_ABS:.*]] = call contract x86_fp80 @llvm.fabs.f80(x86_fp80 %[[LHS_IMAG]])
+! IMPRVD: %[[LHS_IMAG_INFINITE:.*]] = fcmp oeq x86_fp80 %[[LHS_IMAG_ABS]], 0xK7FFF8000000000000000
+! IMPRVD: %[[LHS_IS_INFINITE:.*]] = or i1 %[[LHS_REAL_INFINITE]], %[[LHS_IMAG_INFINITE]]
+! IMPRVD: %[[INF_NUM_FINITE_DENOM:.*]] = and i1 %[[LHS_IS_INFINITE]], %[[RHS_IS_FINITE]]
+! IMPRVD: %[[LHS_REAL_IS_INF:.*]] = select i1 %[[LHS_REAL_INFINITE]], x86_fp80 0xK3FFF8000000000000000, x86_fp80 0xK00000000000000000000
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN:.*]] = call x86_fp80 @llvm.copysign.f80(x86_fp80 %[[LHS_REAL_IS_INF]], x86_fp80 %[[LHS_REAL]])
+! IMPRVD: %[[LHS_IMAG_IS_INF:.*]] = select i1 %[[LHS_IMAG_INFINITE]], x86_fp80 0xK3FFF8000000000000000, x86_fp80 0xK00000000000000000000
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN:.*]] = call x86_fp80 @llvm.copysign.f80(x86_fp80 %[[LHS_IMAG_IS_INF]], x86_fp80 %[[LHS_IMAG]])
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_REAL:.*]] = fmul contract x86_fp80 %[[LHS_REAL_IS_INF_WITH_SIGN]], %[[RHS_REAL]]
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_IMAG:.*]] = fmul contract x86_fp80 %[[LHS_IMAG_IS_INF_WITH_SIGN]], %[[RHS_IMAG]]
+! IMPRVD: %[[INF_MULTIPLICATOR_1:.*]] = fadd contract x86_fp80 %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_REAL]], %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_3:.*]] = fmul contract x86_fp80 %[[INF_MULTIPLICATOR_1]], 0xK7FFF8000000000000000
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_IMAG:.*]] = fmul contract x86_fp80 %[[LHS_REAL_IS_INF_WITH_SIGN]], %[[RHS_IMAG]]
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_REAL:.*]] = fmul contract x86_fp80 %[[LHS_IMAG_IS_INF_WITH_SIGN]], %[[RHS_REAL]]
+! IMPRVD: %[[INF_MULTIPLICATOR_2:.*]] = fsub contract x86_fp80 %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_REAL]], %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_IMAG]]
+! IMPRVD: %[[RESULT_IMAG_3:.*]] = fmul contract x86_fp80 %[[INF_MULTIPLICATOR_2]], 0xK7FFF8000000000000000
+
+! Case 3. Finite numerator, infinite denominator.
+! IMPRVD: %[[LHS_REAL_FINITE:.*]] = fcmp one x86_fp80 %[[LHS_REAL_ABS]], 0xK7FFF8000000000000000
+! IMPRVD: %[[LHS_IMAG_FINITE:.*]] = fcmp one x86_fp80 %[[LHS_IMAG_ABS]], 0xK7FFF8000000000000000
+! IMPRVD: %[[LHS_IS_FINITE:.*]] = and i1 %[[LHS_REAL_FINITE]], %[[LHS_IMAG_FINITE]]
+! IMPRVD: %[[RHS_REAL_INFINITE:.*]] = fcmp oeq x86_fp80 %[[RHS_REAL_ABS]], 0xK7FFF8000000000000000
+! IMPRVD: %[[RHS_IMAG_INFINITE:.*]] = fcmp oeq x86_fp80 %[[RHS_IMAG_ABS]], 0xK7FFF8000000000000000
+! IMPRVD: %[[RHS_IS_INFINITE:.*]] = or i1 %[[RHS_REAL_INFINITE]], %[[RHS_IMAG_INFINITE]]
+! IMPRVD: %[[FINITE_NUM_INFINITE_DENOM:.*]] = and i1 %[[LHS_IS_FINITE]], %[[RHS_IS_INFINITE]]
+! IMPRVD: %[[RHS_REAL_IS_INF:.*]] = select i1 %[[RHS_REAL_INFINITE]], x86_fp80 0xK3FFF8000000000000000, x86_fp80 0xK00000000000000000000
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN:.*]] = call x86_fp80 @llvm.copysign.f80(x86_fp80 %[[RHS_REAL_IS_INF]], x86_fp80 %[[RHS_REAL]])
+! IMPRVD: %[[RHS_IMAG_IS_INF:.*]] = select i1 %[[RHS_IMAG_INFINITE]], x86_fp80 0xK3FFF8000000000000000, x86_fp80 0xK00000000000000000000
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN:.*]] = call x86_fp80 @llvm.copysign.f80(x86_fp80 %[[RHS_IMAG_IS_INF]], x86_fp80 %[[RHS_IMAG]])
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_REAL:.*]] = fmul contract x86_fp80 %[[LHS_REAL]], %[[RHS_REAL_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_IMAG:.*]] = fmul contract x86_fp80 %[[LHS_IMAG]], %[[RHS_IMAG_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[ZERO_MULTIPLICATOR_1:.*]] = fadd contract x86_fp80 %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_REAL]], %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_4:.*]] = fmul contract x86_fp80 %[[ZERO_MULTIPLICATOR_1]], 0xK00000000000000000000
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_IMAG:.*]] = fmul contract x86_fp80 %[[LHS_IMAG]], %[[RHS_REAL_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_REAL:.*]] = fmul contract x86_fp80 %[[LHS_REAL]], %[[RHS_IMAG_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[ZERO_MULTIPLICATOR_2:.*]] = fsub contract x86_fp80 %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_IMAG]], %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_REAL]]
+! IMPRVD: %[[RESULT_IMAG_4:.*]] = fmul contract x86_fp80 %[[ZERO_MULTIPLICATOR_2]], 0xK00000000000000000000
+
+! IMPRVD: %[[REAL_ABS_SMALLER_THAN_IMAG_ABS:.*]] = fcmp olt x86_fp80 %[[RHS_REAL_ABS]], %[[RHS_IMAG_ABS]]
+! IMPRVD: %[[RESULT_REAL:.*]] = select i1 %[[REAL_ABS_SMALLER_THAN_IMAG_ABS]], x86_fp80 %[[RESULT_REAL_1]], x86_fp80 %[[RESULT_REAL_2]]
+! IMPRVD: %[[RESULT_IMAG:.*]] = select i1 %[[REAL_ABS_SMALLER_THAN_IMAG_ABS]], x86_fp80 %[[RESULT_IMAG_1]], x86_fp80 %[[RESULT_IMAG_2]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_3:.*]] = select i1 %[[FINITE_NUM_INFINITE_DENOM]], x86_fp80 %[[RESULT_REAL_4]], x86_fp80 %[[RESULT_REAL]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_3:.*]] = select i1 %[[FINITE_NUM_INFINITE_DENOM]], x86_fp80 %[[RESULT_IMAG_4]], x86_fp80 %[[RESULT_IMAG]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_2:.*]] = select i1 %[[INF_NUM_FINITE_DENOM]], x86_fp80 %[[RESULT_REAL_3]], x86_fp80 %[[RESULT_REAL_SPECIAL_CASE_3]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_2:.*]] = select i1 %[[INF_NUM_FINITE_DENOM]], x86_fp80 %[[RESULT_IMAG_3]], x86_fp80 %[[RESULT_IMAG_SPECIAL_CASE_3]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_1:.*]] = select i1 %[[RESULT_IS_INFINITY]], x86_fp80 %[[INFINITY_RESULT_REAL]], x86_fp80 %[[RESULT_REAL_SPECIAL_CASE_2]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_1:.*]] = select i1 %[[RESULT_IS_INFINITY]], x86_fp80 %[[INFINITY_RESULT_IMAG]], x86_fp80 %[[RESULT_IMAG_SPECIAL_CASE_2]]
+! IMPRVD: %[[RESULT_REAL_IS_NAN:.*]] = fcmp uno x86_fp80 %[[RESULT_REAL]], 0xK00000000000000000000
+! IMPRVD: %[[RESULT_IMAG_IS_NAN:.*]] = fcmp uno x86_fp80 %[[RESULT_IMAG]], 0xK00000000000000000000
+! IMPRVD: %[[RESULT_IS_NAN:.*]] = and i1 %[[RESULT_REAL_IS_NAN]], %[[RESULT_IMAG_IS_NAN]]
+! IMPRVD: %[[RESULT_REAL_WITH_SPECIAL_CASES:.*]] = select i1 %[[RESULT_IS_NAN]], x86_fp80 %[[RESULT_REAL_SPECIAL_CASE_1]], x86_fp80 %[[RESULT_REAL]]
+! IMPRVD: %[[RESULT_IMAG_WITH_SPECIAL_CASES:.*]] = select i1 %[[RESULT_IS_NAN]], x86_fp80 %[[RESULT_IMAG_SPECIAL_CASE_1]], x86_fp80 %[[RESULT_IMAG]]
+! IMPRVD: %[[RESULT_1:.*]] = insertvalue { x86_fp80, x86_fp80 } poison, x86_fp80 %[[RESULT_REAL_WITH_SPECIAL_CASES]], 0
+! IMPRVD: %[[RESULT_2:.*]] = insertvalue { x86_fp80, x86_fp80 } %[[RESULT_1]], x86_fp80 %[[RESULT_IMAG_WITH_SPECIAL_CASES]], 1
+! IMPRVD: store { x86_fp80, x86_fp80 } %[[RESULT_2]], ptr %[[RET]], align 16
+
+! BASIC-DAG: %[[RHS_REAL_SQ:.*]] = fmul contract x86_fp80 %[[RHS_REAL]], %[[RHS_REAL]]
+! BASIC-DAG: %[[RHS_IMAG_SQ:.*]] = fmul contract x86_fp80 %[[RHS_IMAG]], %[[RHS_IMAG]]
+! BASIC: %[[SQ_NORM:.*]] = fadd contract x86_fp80 %[[RHS_REAL_SQ]], %[[RHS_IMAG_SQ]]
+! BASIC-DAG: %[[REAL_TMP_0:.*]] = fmul contract x86_fp80 %[[LHS_REAL]], %[[RHS_REAL]]
+! BASIC-DAG: %[[REAL_TMP_1:.*]] = fmul contract x86_fp80 %[[LHS_IMAG]], %[[RHS_IMAG]]
+! BASIC: %[[REAL_TMP_2:.*]] = fadd contract x86_fp80 %[[REAL_TMP_0]], %[[REAL_TMP_1]]
+! BASIC-DAG: %[[IMAG_TMP_0:.*]] = fmul contract x86_fp80 %[[LHS_IMAG]], %[[RHS_REAL]]
+! BASIC-DAG: %[[IMAG_TMP_1:.*]] = fmul contract x86_fp80 %[[LHS_REAL]], %[[RHS_IMAG]]
+! BASIC: %[[IMAG_TMP_2:.*]] = fsub contract x86_fp80 %[[IMAG_TMP_0]], %[[IMAG_TMP_1]]
+! BASIC: %[[REAL:.*]] = fdiv contract x86_fp80 %[[REAL_TMP_2]], %[[SQ_NORM]]
+! BASIC: %[[IMAG:.*]] = fdiv contract x86_fp80 %[[IMAG_TMP_2]], %[[SQ_NORM]]
+! BASIC: %[[RESULT_1:.*]] = insertvalue { x86_fp80, x86_fp80 } poison, x86_fp80 %[[REAL]], 0
+! BASIC: %[[RESULT_2:.*]] = insertvalue { x86_fp80, x86_fp80 } %[[RESULT_1]], x86_fp80 %[[IMAG]], 1
+! BASIC: store { x86_fp80, x86_fp80 } %[[RESULT_2]], ptr %[[RET]], align 16
+
+! CHECK: ret void
+subroutine div_test_extended(a,b,c)
+  complex(kind=10) :: a, b, c
+  a = b / c
+end subroutine div_test_extended
diff --git a/flang/test/Integration/complex-div-to-llvm-kind16.f90 b/flang/test/Integration/complex-div-to-llvm-kind16.f90
new file mode 100644
index 0000000000000..887a7972dc1be
--- /dev/null
+++ b/flang/test/Integration/complex-div-to-llvm-kind16.f90
@@ -0,0 +1,131 @@
+! Test lowering complex division to llvm ir according to options
+
+! REQUIRES: flang-supports-f128-math
+! RUN: %flang -fcomplex-arithmetic=improved -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,IMPRVD
+! RUN: %flang -fcomplex-arithmetic=basic -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,BASIC
+
+
+! CHECK-LABEL: @div_test_quad
+! CHECK-SAME: ptr %[[RET:.*]], ptr %[[LHS:.*]], ptr %[[RHS:.*]])
+! CHECK: %[[LOAD_LHS:.*]] = load { fp128, fp128 }, ptr %[[LHS]], align 16
+! CHECK: %[[LOAD_RHS:.*]] = load { fp128, fp128 }, ptr %[[RHS]], align 16
+! CHECK: %[[LHS_REAL:.*]] = extractvalue { fp128, fp128 } %[[LOAD_LHS]], 0
+! CHECK: %[[LHS_IMAG:.*]] = extractvalue { fp128, fp128 } %[[LOAD_LHS]], 1
+! CHECK: %[[RHS_REAL:.*]] = extractvalue { fp128, fp128 } %[[LOAD_RHS]], 0
+! CHECK: %[[RHS_IMAG:.*]] = extractvalue { fp128, fp128 } %[[LOAD_RHS]], 1
+
+! IMPRVD: %[[RHS_REAL_IMAG_RATIO:.*]] = fdiv contract fp128 %[[RHS_REAL]], %[[RHS_IMAG]]
+! IMPRVD: %[[RHS_REAL_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract fp128 %[[RHS_REAL_IMAG_RATIO]], %[[RHS_REAL]]
+! IMPRVD: %[[RHS_REAL_IMAG_DENOM:.*]] = fadd contract fp128 %[[RHS_IMAG]], %[[RHS_REAL_TIMES_RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[LHS_REAL_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract fp128 %[[LHS_REAL]], %[[RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[REAL_NUMERATOR_1:.*]] = fadd contract fp128 %[[LHS_REAL_TIMES_RHS_REAL_IMAG_RATIO]], %[[LHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_1:.*]] = fdiv contract fp128 %[[REAL_NUMERATOR_1]], %[[RHS_REAL_IMAG_DENOM]]
+! IMPRVD: %[[LHS_IMAG_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract fp128 %[[LHS_IMAG]], %[[RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[IMAG_NUMERATOR_1:.*]] = fsub contract fp128 %[[LHS_IMAG_TIMES_RHS_REAL_IMAG_RATIO]], %[[LHS_REAL]]
+! IMPRVD: %[[RESULT_IMAG_1:.*]] = fdiv contract fp128 %[[IMAG_NUMERATOR_1]], %[[RHS_REAL_IMAG_DENOM]]
+! IMPRVD: %[[RHS_IMAG_REAL_RATIO:.*]] = fdiv contract fp128 %[[RHS_IMAG]], %[[RHS_REAL]]
+! IMPRVD: %[[RHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract fp128 %[[RHS_IMAG_REAL_RATIO]], %[[RHS_IMAG]]
+! IMPRVD: %[[RHS_IMAG_REAL_DENOM:.*]] = fadd contract fp128 %[[RHS_REAL]], %[[RHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[LHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract fp128 %[[LHS_IMAG]], %[[RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[REAL_NUMERATOR_2:.*]] = fadd contract fp128 %[[LHS_REAL]], %[[LHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[RESULT_REAL_2:.*]] = fdiv contract fp128 %[[REAL_NUMERATOR_2]], %[[RHS_IMAG_REAL_DENOM]]
+! IMPRVD: %[[LHS_REAL_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract fp128 %[[LHS_REAL]], %[[RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[IMAG_NUMERATOR_2:.*]] = fsub contract fp128 %[[LHS_IMAG]], %[[LHS_REAL_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[RESULT_IMAG_2:.*]] = fdiv contract fp128 %[[IMAG_NUMERATOR_2]], %[[RHS_IMAG_REAL_DENOM]]
+
+! Case 1. Zero denominator, numerator contains at most one NaN value.
+! IMPRVD: %[[RHS_REAL_ABS:.*]] = call contract fp128 @llvm.fabs.f128(fp128 %[[RHS_REAL]])
+! IMPRVD: %[[RHS_REAL_ABS_IS_ZERO:.*]] = fcmp oeq fp128 %[[RHS_REAL_ABS]], 0xL00000000000000000000000000000000
+! IMPRVD: %[[RHS_IMAG_ABS:.*]] = call contract fp128 @llvm.fabs.f128(fp128 %[[RHS_IMAG]])
+! IMPRVD: %[[RHS_IMAG_ABS_IS_ZERO:.*]] = fcmp oeq fp128 %[[RHS_IMAG_ABS]], 0xL00000000000000000000000000000000
+! IMPRVD: %[[LHS_REAL_IS_NOT_NAN:.*]] = fcmp ord fp128 %[[LHS_REAL]], 0xL00000000000000000000000000000000
+! IMPRVD: %[[LHS_IMAG_IS_NOT_NAN:.*]] = fcmp ord fp128 %[[LHS_IMAG]], 0xL00000000000000000000000000000000
+! IMPRVD: %[[LHS_CONTAINS_NOT_NAN_VALUE:.*]] = or i1 %[[LHS_REAL_IS_NOT_NAN]], %[[LHS_IMAG_IS_NOT_NAN]]
+! IMPRVD: %[[RHS_IS_ZERO:.*]] = and i1 %[[RHS_REAL_ABS_IS_ZERO]], %[[RHS_IMAG_ABS_IS_ZERO]]
+! IMPRVD: %[[RESULT_IS_INFINITY:.*]] = and i1 %[[LHS_CONTAINS_NOT_NAN_VALUE]], %[[RHS_IS_ZERO]]
+! IMPRVD: %[[INF_WITH_SIGN_OF_RHS_REAL:.*]] = call fp128 @llvm.copysign.f128(fp128 0xL00000000000000007FFF000000000000, fp128 %[[RHS_REAL]])
+! IMPRVD: %[[INFINITY_RESULT_REAL:.*]] = fmul contract fp128 %[[INF_WITH_SIGN_OF_RHS_REAL]], %[[LHS_REAL]]
+! IMPRVD: %[[INFINITY_RESULT_IMAG:.*]] = fmul contract fp128 %[[INF_WITH_SIGN_OF_RHS_REAL]], %[[LHS_IMAG]]
+
+! Case 2. Infinite numerator, finite denominator.
+! IMPRVD: %[[RHS_REAL_FINITE:.*]] = fcmp one fp128 %[[RHS_REAL_ABS]], 0xL00000000000000007FFF000000000000
+! IMPRVD: %[[RHS_IMAG_FINITE:.*]] = fcmp one fp128 %[[RHS_IMAG_ABS]], 0xL00000000000000007FFF000000000000
+! IMPRVD: %[[RHS_IS_FINITE:.*]] = and i1 %[[RHS_REAL_FINITE]], %[[RHS_IMAG_FINITE]]
+! IMPRVD: %[[LHS_REAL_ABS:.*]] = call contract fp128 @llvm.fabs.f128(fp128 %[[LHS_REAL]])
+! IMPRVD: %[[LHS_REAL_INFINITE:.*]] = fcmp oeq fp128 %[[LHS_REAL_ABS]], 0xL00000000000000007FFF000000000000
+! IMPRVD: %[[LHS_IMAG_ABS:.*]] = call contract fp128 @llvm.fabs.f128(fp128 %[[LHS_IMAG]])
+! IMPRVD: %[[LHS_IMAG_INFINITE:.*]] = fcmp oeq fp128 %[[LHS_IMAG_ABS]], 0xL00000000000000007FFF000000000000
+! IMPRVD: %[[LHS_IS_INFINITE:.*]] = or i1 %[[LHS_REAL_INFINITE]], %[[LHS_IMAG_INFINITE]]
+! IMPRVD: %[[INF_NUM_FINITE_DENOM:.*]] = and i1 %[[LHS_IS_INFINITE]], %[[RHS_IS_FINITE]]
+! IMPRVD: %[[LHS_REAL_IS_INF:.*]] = select i1 %[[LHS_REAL_INFINITE]], fp128 0xL00000000000000003FFF000000000000, fp128 0xL00000000000000000000000000000000
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN:.*]] = call fp128 @llvm.copysign.f128(fp128 %[[LHS_REAL_IS_INF]], fp128 %[[LHS_REAL]])
+! IMPRVD: %[[LHS_IMAG_IS_INF:.*]] = select i1 %[[LHS_IMAG_INFINITE]], fp128 0xL00000000000000003FFF000000000000, fp128 0xL00000000000000000000000000000000
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN:.*]] = call fp128 @llvm.copysign.f128(fp128 %[[LHS_IMAG_IS_INF]], fp128 %[[LHS_IMAG]])
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_REAL:.*]] = fmul contract fp128 %[[LHS_REAL_IS_INF_WITH_SIGN]], %[[RHS_REAL]]
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_IMAG:.*]] = fmul contract fp128 %[[LHS_IMAG_IS_INF_WITH_SIGN]], %[[RHS_IMAG]]
+! IMPRVD: %[[INF_MULTIPLICATOR_1:.*]] = fadd contract fp128 %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_REAL]], %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_3:.*]] = fmul contract fp128 %[[INF_MULTIPLICATOR_1]], 0xL00000000000000007FFF000000000000
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_IMAG:.*]] = fmul contract fp128 %[[LHS_REAL_IS_INF_WITH_SIGN]], %[[RHS_IMAG]]
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_REAL:.*]] = fmul contract fp128 %[[LHS_IMAG_IS_INF_WITH_SIGN]], %[[RHS_REAL]]
+! IMPRVD: %[[INF_MULTIPLICATOR_2:.*]] = fsub contract fp128 %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_REAL]], %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_IMAG]]
+! IMPRVD: %[[RESULT_IMAG_3:.*]] = fmul contract fp128 %[[INF_MULTIPLICATOR_2]], 0xL00000000000000007FFF000000000000
+
+! Case 3. Finite numerator, infinite denominator.
+! IMPRVD: %[[LHS_REAL_FINITE:.*]] = fcmp one fp128 %[[LHS_REAL_ABS]], 0xL00000000000000007FFF000000000000
+! IMPRVD: %[[LHS_IMAG_FINITE:.*]] = fcmp one fp128 %[[LHS_IMAG_ABS]], 0xL00000000000000007FFF000000000000
+! IMPRVD: %[[LHS_IS_FINITE:.*]] = and i1 %[[LHS_REAL_FINITE]], %[[LHS_IMAG_FINITE]]
+! IMPRVD: %[[RHS_REAL_INFINITE:.*]] = fcmp oeq fp128 %[[RHS_REAL_ABS]], 0xL00000000000000007FFF000000000000
+! IMPRVD: %[[RHS_IMAG_INFINITE:.*]] = fcmp oeq fp128 %[[RHS_IMAG_ABS]], 0xL00000000000000007FFF000000000000
+! IMPRVD: %[[RHS_IS_INFINITE:.*]] = or i1 %[[RHS_REAL_INFINITE]], %[[RHS_IMAG_INFINITE]]
+! IMPRVD: %[[FINITE_NUM_INFINITE_DENOM:.*]] = and i1 %[[LHS_IS_FINITE]], %[[RHS_IS_INFINITE]]
+! IMPRVD: %[[RHS_REAL_IS_INF:.*]] = select i1 %[[RHS_REAL_INFINITE]], fp128 0xL00000000000000003FFF000000000000, fp128 0xL00000000000000000000000000000000
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN:.*]] = call fp128 @llvm.copysign.f128(fp128 %[[RHS_REAL_IS_INF]], fp128 %[[RHS_REAL]])
+! IMPRVD: %[[RHS_IMAG_IS_INF:.*]] = select i1 %[[RHS_IMAG_INFINITE]], fp128 0xL00000000000000003FFF000000000000, fp128 0xL00000000000000000000000000000000
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN:.*]] = call fp128 @llvm.copysign.f128(fp128 %[[RHS_IMAG_IS_INF]], fp128 %[[RHS_IMAG]])
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_REAL:.*]] = fmul contract fp128 %[[LHS_REAL]], %[[RHS_REAL_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_IMAG:.*]] = fmul contract fp128 %[[LHS_IMAG]], %[[RHS_IMAG_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[ZERO_MULTIPLICATOR_1:.*]] = fadd contract fp128 %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_REAL]], %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_4:.*]] = fmul contract fp128 %[[ZERO_MULTIPLICATOR_1]], 0xL00000000000000000000000000000000
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_IMAG:.*]] = fmul contract fp128 %[[LHS_IMAG]], %[[RHS_REAL_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_REAL:.*]] = fmul contract fp128 %[[LHS_REAL]], %[[RHS_IMAG_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[ZERO_MULTIPLICATOR_2:.*]] = fsub contract fp128 %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_IMAG]], %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_REAL]]
+! IMPRVD: %[[RESULT_IMAG_4:.*]] = fmul contract fp128 %[[ZERO_MULTIPLICATOR_2]], 0xL00000000000000000000000000000000
+
+! IMPRVD: %[[REAL_ABS_SMALLER_THAN_IMAG_ABS:.*]] = fcmp olt fp128 %[[RHS_REAL_ABS]], %[[RHS_IMAG_ABS]]
+! IMPRVD: %[[RESULT_REAL:.*]] = select i1 %[[REAL_ABS_SMALLER_THAN_IMAG_ABS]], fp128 %[[RESULT_REAL_1]], fp128 %[[RESULT_REAL_2]]
+! IMPRVD: %[[RESULT_IMAG:.*]] = select i1 %[[REAL_ABS_SMALLER_THAN_IMAG_ABS]], fp128 %[[RESULT_IMAG_1]], fp128 %[[RESULT_IMAG_2]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_3:.*]] = select i1 %[[FINITE_NUM_INFINITE_DENOM]], fp128 %[[RESULT_REAL_4]], fp128 %[[RESULT_REAL]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_3:.*]] = select i1 %[[FINITE_NUM_INFINITE_DENOM]], fp128 %[[RESULT_IMAG_4]], fp128 %[[RESULT_IMAG]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_2:.*]] = select i1 %[[INF_NUM_FINITE_DENOM]], fp128 %[[RESULT_REAL_3]], fp128 %[[RESULT_REAL_SPECIAL_CASE_3]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_2:.*]] = select i1 %[[INF_NUM_FINITE_DENOM]], fp128 %[[RESULT_IMAG_3]], fp128 %[[RESULT_IMAG_SPECIAL_CASE_3]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_1:.*]] = select i1 %[[RESULT_IS_INFINITY]], fp128 %[[INFINITY_RESULT_REAL]], fp128 %[[RESULT_REAL_SPECIAL_CASE_2]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_1:.*]] = select i1 %[[RESULT_IS_INFINITY]], fp128 %[[INFINITY_RESULT_IMAG]], fp128 %[[RESULT_IMAG_SPECIAL_CASE_2]]
+! IMPRVD: %[[RESULT_REAL_IS_NAN:.*]] = fcmp uno fp128 %[[RESULT_REAL]], 0xL00000000000000000000000000000000
+! IMPRVD: %[[RESULT_IMAG_IS_NAN:.*]] = fcmp uno fp128 %[[RESULT_IMAG]], 0xL00000000000000000000000000000000
+! IMPRVD: %[[RESULT_IS_NAN:.*]] = and i1 %[[RESULT_REAL_IS_NAN]], %[[RESULT_IMAG_IS_NAN]]
+! IMPRVD: %[[RESULT_REAL_WITH_SPECIAL_CASES:.*]] = select i1 %[[RESULT_IS_NAN]], fp128 %[[RESULT_REAL_SPECIAL_CASE_1]], fp128 %[[RESULT_REAL]]
+! IMPRVD: %[[RESULT_IMAG_WITH_SPECIAL_CASES:.*]] = select i1 %[[RESULT_IS_NAN]], fp128 %[[RESULT_IMAG_SPECIAL_CASE_1]], fp128 %[[RESULT_IMAG]]
+! IMPRVD: %[[RESULT_1:.*]] = insertvalue { fp128, fp128 } poison, fp128 %[[RESULT_REAL_WITH_SPECIAL_CASES]], 0
+! IMPRVD: %[[RESULT_2:.*]] = insertvalue { fp128, fp128 } %[[RESULT_1]], fp128 %[[RESULT_IMAG_WITH_SPECIAL_CASES]], 1
+! IMPRVD: store { fp128, fp128 } %[[RESULT_2]], ptr %[[RET]], align 16
+
+! BASIC-DAG: %[[RHS_REAL_SQ:.*]] = fmul contract fp128 %[[RHS_REAL]], %[[RHS_REAL]]
+! BASIC-DAG: %[[RHS_IMAG_SQ:.*]] = fmul contract fp128 %[[RHS_IMAG]], %[[RHS_IMAG]]
+! BASIC: %[[SQ_NORM:.*]] = fadd contract fp128 %[[RHS_REAL_SQ]], %[[RHS_IMAG_SQ]]
+! BASIC-DAG: %[[REAL_TMP_0:.*]] = fmul contract fp128 %[[LHS_REAL]], %[[RHS_REAL]]
+! BASIC-DAG: %[[REAL_TMP_1:.*]] = fmul contract fp128 %[[LHS_IMAG]], %[[RHS_IMAG]]
+! BASIC: %[[REAL_TMP_2:.*]] = fadd contract fp128 %[[REAL_TMP_0]], %[[REAL_TMP_1]]
+! BASIC-DAG: %[[IMAG_TMP_0:.*]] = fmul contract fp128 %[[LHS_IMAG]], %[[RHS_REAL]]
+! BASIC-DAG: %[[IMAG_TMP_1:.*]] = fmul contract fp128 %[[LHS_REAL]], %[[RHS_IMAG]]
+! BASIC: %[[IMAG_TMP_2:.*]] = fsub contract fp128 %[[IMAG_TMP_0]], %[[IMAG_TMP_1]]
+! BASIC: %[[REAL:.*]] = fdiv contract fp128 %[[REAL_TMP_2]], %[[SQ_NORM]]
+! BASIC: %[[IMAG:.*]] = fdiv contract fp128 %[[IMAG_TMP_2]], %[[SQ_NORM]]
+! BASIC: %[[RESULT_1:.*]] = insertvalue { fp128, fp128 } poison, fp128 %[[REAL]], 0
+! BASIC: %[[RESULT_2:.*]] = insertvalue { fp128, fp128 } %[[RESULT_1]], fp128 %[[IMAG]], 1
+! BASIC: store { fp128, fp128 } %[[RESULT_2]], ptr %[[RET]], align 16
+
+! CHECK: ret void
+subroutine div_test_quad(a,b,c)
+  complex(kind=16) :: a, b, c
+  a = b / c
+end subroutine div_test_quad
diff --git a/flang/test/Integration/complex-div-to-llvm.f90 b/flang/test/Integration/complex-div-to-llvm.f90
new file mode 100644
index 0000000000000..01782a565f24c
--- /dev/null
+++ b/flang/test/Integration/complex-div-to-llvm.f90
@@ -0,0 +1,508 @@
+! Test lowering complex division to llvm ir according to options
+
+! RUN: %flang -fcomplex-arithmetic=improved -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,IMPRVD
+! RUN: %flang -fcomplex-arithmetic=basic -S -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,BASIC
+
+
+! CHECK-LABEL: @div_test_half
+! CHECK-SAME: ptr %[[RET:.*]], ptr %[[LHS:.*]], ptr %[[RHS:.*]])
+! CHECK: %[[LOAD_LHS:.*]] = load { half, half }, ptr %[[LHS]], align 2
+! CHECK: %[[LOAD_RHS:.*]] = load { half, half }, ptr %[[RHS]], align 2
+! CHECK: %[[LHS_REAL:.*]] = extractvalue { half, half } %[[LOAD_LHS]], 0
+! CHECK: %[[LHS_IMAG:.*]] = extractvalue { half, half } %[[LOAD_LHS]], 1
+! CHECK: %[[RHS_REAL:.*]] = extractvalue { half, half } %[[LOAD_RHS]], 0
+! CHECK: %[[RHS_IMAG:.*]] = extractvalue { half, half } %[[LOAD_RHS]], 1
+
+! IMPRVD: %[[RHS_REAL_IMAG_RATIO:.*]] = fdiv contract half %[[RHS_REAL]], %[[RHS_IMAG]]
+! IMPRVD: %[[RHS_REAL_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract half %[[RHS_REAL_IMAG_RATIO]], %[[RHS_REAL]]
+! IMPRVD: %[[RHS_REAL_IMAG_DENOM:.*]] = fadd contract half %[[RHS_IMAG]], %[[RHS_REAL_TIMES_RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[LHS_REAL_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract half %[[LHS_REAL]], %[[RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[REAL_NUMERATOR_1:.*]] = fadd contract half %[[LHS_REAL_TIMES_RHS_REAL_IMAG_RATIO]], %[[LHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_1:.*]] = fdiv contract half %[[REAL_NUMERATOR_1]], %[[RHS_REAL_IMAG_DENOM]]
+! IMPRVD: %[[LHS_IMAG_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract half %[[LHS_IMAG]], %[[RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[IMAG_NUMERATOR_1:.*]] = fsub contract half %[[LHS_IMAG_TIMES_RHS_REAL_IMAG_RATIO]], %[[LHS_REAL]]
+! IMPRVD: %[[RESULT_IMAG_1:.*]] = fdiv contract half %[[IMAG_NUMERATOR_1]], %[[RHS_REAL_IMAG_DENOM]]
+! IMPRVD: %[[RHS_IMAG_REAL_RATIO:.*]] = fdiv contract half %[[RHS_IMAG]], %[[RHS_REAL]]
+! IMPRVD: %[[RHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract half %[[RHS_IMAG_REAL_RATIO]], %[[RHS_IMAG]]
+! IMPRVD: %[[RHS_IMAG_REAL_DENOM:.*]] = fadd contract half %[[RHS_REAL]], %[[RHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[LHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract half %[[LHS_IMAG]], %[[RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[REAL_NUMERATOR_2:.*]] = fadd contract half %[[LHS_REAL]], %[[LHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[RESULT_REAL_2:.*]] = fdiv contract half %[[REAL_NUMERATOR_2]], %[[RHS_IMAG_REAL_DENOM]]
+! IMPRVD: %[[LHS_REAL_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract half %[[LHS_REAL]], %[[RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[IMAG_NUMERATOR_2:.*]] = fsub contract half %[[LHS_IMAG]], %[[LHS_REAL_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[RESULT_IMAG_2:.*]] = fdiv contract half %[[IMAG_NUMERATOR_2]], %[[RHS_IMAG_REAL_DENOM]]
+
+! Case 1. Zero denominator, numerator contains at most one NaN value.
+! IMPRVD: %[[RHS_REAL_ABS:.*]] = call contract half @llvm.fabs.f16(half %[[RHS_REAL]])
+! IMPRVD: %[[RHS_REAL_ABS_IS_ZERO:.*]] = fcmp oeq half %[[RHS_REAL_ABS]], 0xH0000
+! IMPRVD: %[[RHS_IMAG_ABS:.*]] = call contract half @llvm.fabs.f16(half %[[RHS_IMAG]])
+! IMPRVD: %[[RHS_IMAG_ABS_IS_ZERO:.*]] = fcmp oeq half %[[RHS_IMAG_ABS]], 0xH0000
+! IMPRVD: %[[LHS_REAL_IS_NOT_NAN:.*]] = fcmp ord half %[[LHS_REAL]], 0xH0000
+! IMPRVD: %[[LHS_IMAG_IS_NOT_NAN:.*]] = fcmp ord half %[[LHS_IMAG]], 0xH0000
+! IMPRVD: %[[LHS_CONTAINS_NOT_NAN_VALUE:.*]] = or i1 %[[LHS_REAL_IS_NOT_NAN]], %[[LHS_IMAG_IS_NOT_NAN]]
+! IMPRVD: %[[RHS_IS_ZERO:.*]] = and i1 %[[RHS_REAL_ABS_IS_ZERO]], %[[RHS_IMAG_ABS_IS_ZERO]]
+! IMPRVD: %[[RESULT_IS_INFINITY:.*]] = and i1 %[[LHS_CONTAINS_NOT_NAN_VALUE]], %[[RHS_IS_ZERO]]
+! IMPRVD: %[[INF_WITH_SIGN_OF_RHS_REAL:.*]] = call half @llvm.copysign.f16(half 0xH7C00, half %[[RHS_REAL]])
+! IMPRVD: %[[INFINITY_RESULT_REAL:.*]] = fmul contract half %[[INF_WITH_SIGN_OF_RHS_REAL]], %[[LHS_REAL]]
+! IMPRVD: %[[INFINITY_RESULT_IMAG:.*]] = fmul contract half %[[INF_WITH_SIGN_OF_RHS_REAL]], %[[LHS_IMAG]]
+
+! Case 2. Infinite numerator, finite denominator.
+! IMPRVD: %[[RHS_REAL_FINITE:.*]] = fcmp one half %[[RHS_REAL_ABS]], 0xH7C00
+! IMPRVD: %[[RHS_IMAG_FINITE:.*]] = fcmp one half %[[RHS_IMAG_ABS]], 0xH7C00
+! IMPRVD: %[[RHS_IS_FINITE:.*]] = and i1 %[[RHS_REAL_FINITE]], %[[RHS_IMAG_FINITE]]
+! IMPRVD: %[[LHS_REAL_ABS:.*]] = call contract half @llvm.fabs.f16(half %[[LHS_REAL]])
+! IMPRVD: %[[LHS_REAL_INFINITE:.*]] = fcmp oeq half %[[LHS_REAL_ABS]], 0xH7C00
+! IMPRVD: %[[LHS_IMAG_ABS:.*]] = call contract half @llvm.fabs.f16(half %[[LHS_IMAG]])
+! IMPRVD: %[[LHS_IMAG_INFINITE:.*]] = fcmp oeq half %[[LHS_IMAG_ABS]], 0xH7C00
+! IMPRVD: %[[LHS_IS_INFINITE:.*]] = or i1 %[[LHS_REAL_INFINITE]], %[[LHS_IMAG_INFINITE]]
+! IMPRVD: %[[INF_NUM_FINITE_DENOM:.*]] = and i1 %[[LHS_IS_INFINITE]], %[[RHS_IS_FINITE]]
+! IMPRVD: %[[LHS_REAL_IS_INF:.*]] = select i1 %[[LHS_REAL_INFINITE]], half 0xH3C00, half 0xH0000
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN:.*]] = call half @llvm.copysign.f16(half %[[LHS_REAL_IS_INF]], half %[[LHS_REAL]])
+! IMPRVD: %[[LHS_IMAG_IS_INF:.*]] = select i1 %[[LHS_IMAG_INFINITE]], half 0xH3C00, half 0xH0000
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN:.*]] = call half @llvm.copysign.f16(half %[[LHS_IMAG_IS_INF]], half %[[LHS_IMAG]])
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_REAL:.*]] = fmul contract half %[[LHS_REAL_IS_INF_WITH_SIGN]], %[[RHS_REAL]]
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_IMAG:.*]] = fmul contract half %[[LHS_IMAG_IS_INF_WITH_SIGN]], %[[RHS_IMAG]]
+! IMPRVD: %[[INF_MULTIPLICATOR_1:.*]] = fadd contract half %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_REAL]], %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_3:.*]] = fmul contract half %[[INF_MULTIPLICATOR_1]], 0xH7C00
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_IMAG:.*]] = fmul contract half %[[LHS_REAL_IS_INF_WITH_SIGN]], %[[RHS_IMAG]]
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_REAL:.*]] = fmul contract half %[[LHS_IMAG_IS_INF_WITH_SIGN]], %[[RHS_REAL]]
+! IMPRVD: %[[INF_MULTIPLICATOR_2:.*]] = fsub contract half %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_REAL]], %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_IMAG]]
+! IMPRVD: %[[RESULT_IMAG_3:.*]] = fmul contract half %[[INF_MULTIPLICATOR_2]], 0xH7C00
+
+! Case 3. Finite numerator, infinite denominator.
+! IMPRVD: %[[LHS_REAL_FINITE:.*]] = fcmp one half %[[LHS_REAL_ABS]], 0xH7C00
+! IMPRVD: %[[LHS_IMAG_FINITE:.*]] = fcmp one half %[[LHS_IMAG_ABS]], 0xH7C00
+! IMPRVD: %[[LHS_IS_FINITE:.*]] = and i1 %[[LHS_REAL_FINITE]], %[[LHS_IMAG_FINITE]]
+! IMPRVD: %[[RHS_REAL_INFINITE:.*]] = fcmp oeq half %[[RHS_REAL_ABS]], 0xH7C00
+! IMPRVD: %[[RHS_IMAG_INFINITE:.*]] = fcmp oeq half %[[RHS_IMAG_ABS]], 0xH7C00
+! IMPRVD: %[[RHS_IS_INFINITE:.*]] = or i1 %[[RHS_REAL_INFINITE]], %[[RHS_IMAG_INFINITE]]
+! IMPRVD: %[[FINITE_NUM_INFINITE_DENOM:.*]] = and i1 %[[LHS_IS_FINITE]], %[[RHS_IS_INFINITE]]
+! IMPRVD: %[[RHS_REAL_IS_INF:.*]] = select i1 %[[RHS_REAL_INFINITE]], half 0xH3C00, half 0xH0000
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN:.*]] = call half @llvm.copysign.f16(half %[[RHS_REAL_IS_INF]], half %[[RHS_REAL]])
+! IMPRVD: %[[RHS_IMAG_IS_INF:.*]] = select i1 %[[RHS_IMAG_INFINITE]], half 0xH3C00, half 0xH0000
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN:.*]] = call half @llvm.copysign.f16(half %[[RHS_IMAG_IS_INF]], half %[[RHS_IMAG]])
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_REAL:.*]] = fmul contract half %[[LHS_REAL]], %[[RHS_REAL_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_IMAG:.*]] = fmul contract half %[[LHS_IMAG]], %[[RHS_IMAG_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[ZERO_MULTIPLICATOR_1:.*]] = fadd contract half %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_REAL]], %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_4:.*]] = fmul contract half %[[ZERO_MULTIPLICATOR_1]], 0xH0000
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_IMAG:.*]] = fmul contract half %[[LHS_IMAG]], %[[RHS_REAL_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_REAL:.*]] = fmul contract half %[[LHS_REAL]], %[[RHS_IMAG_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[ZERO_MULTIPLICATOR_2:.*]] = fsub contract half %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_IMAG]], %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_REAL]]
+! IMPRVD: %[[RESULT_IMAG_4:.*]] = fmul contract half %[[ZERO_MULTIPLICATOR_2]], 0xH0000
+
+! IMPRVD: %[[REAL_ABS_SMALLER_THAN_IMAG_ABS:.*]] = fcmp olt half %[[RHS_REAL_ABS]], %[[RHS_IMAG_ABS]]
+! IMPRVD: %[[RESULT_REAL:.*]] = select i1 %[[REAL_ABS_SMALLER_THAN_IMAG_ABS]], half %[[RESULT_REAL_1]], half %[[RESULT_REAL_2]]
+! IMPRVD: %[[RESULT_IMAG:.*]] = select i1 %[[REAL_ABS_SMALLER_THAN_IMAG_ABS]], half %[[RESULT_IMAG_1]], half %[[RESULT_IMAG_2]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_3:.*]] = select i1 %[[FINITE_NUM_INFINITE_DENOM]], half %[[RESULT_REAL_4]], half %[[RESULT_REAL]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_3:.*]] = select i1 %[[FINITE_NUM_INFINITE_DENOM]], half %[[RESULT_IMAG_4]], half %[[RESULT_IMAG]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_2:.*]] = select i1 %[[INF_NUM_FINITE_DENOM]], half %[[RESULT_REAL_3]], half %[[RESULT_REAL_SPECIAL_CASE_3]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_2:.*]] = select i1 %[[INF_NUM_FINITE_DENOM]], half %[[RESULT_IMAG_3]], half %[[RESULT_IMAG_SPECIAL_CASE_3]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_1:.*]] = select i1 %[[RESULT_IS_INFINITY]], half %[[INFINITY_RESULT_REAL]], half %[[RESULT_REAL_SPECIAL_CASE_2]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_1:.*]] = select i1 %[[RESULT_IS_INFINITY]], half %[[INFINITY_RESULT_IMAG]], half %[[RESULT_IMAG_SPECIAL_CASE_2]]
+! IMPRVD: %[[RESULT_REAL_IS_NAN:.*]] = fcmp uno half %[[RESULT_REAL]], 0xH0000
+! IMPRVD: %[[RESULT_IMAG_IS_NAN:.*]] = fcmp uno half %[[RESULT_IMAG]], 0xH0000
+! IMPRVD: %[[RESULT_IS_NAN:.*]] = and i1 %[[RESULT_REAL_IS_NAN]], %[[RESULT_IMAG_IS_NAN]]
+! IMPRVD: %[[RESULT_REAL_WITH_SPECIAL_CASES:.*]] = select i1 %[[RESULT_IS_NAN]], half %[[RESULT_REAL_SPECIAL_CASE_1]], half %[[RESULT_REAL]]
+! IMPRVD: %[[RESULT_IMAG_WITH_SPECIAL_CASES:.*]] = select i1 %[[RESULT_IS_NAN]], half %[[RESULT_IMAG_SPECIAL_CASE_1]], half %[[RESULT_IMAG]]
+! IMPRVD: %[[RESULT_1:.*]] = insertvalue { half, half } poison, half %[[RESULT_REAL_WITH_SPECIAL_CASES]], 0
+! IMPRVD: %[[RESULT_2:.*]] = insertvalue { half, half } %[[RESULT_1]], half %[[RESULT_IMAG_WITH_SPECIAL_CASES]], 1
+! IMPRVD: store { half, half } %[[RESULT_2]], ptr %[[RET]], align 2
+
+! BASIC-DAG: %[[RHS_REAL_SQ:.*]] = fmul contract half %[[RHS_REAL]], %[[RHS_REAL]]
+! BASIC-DAG: %[[RHS_IMAG_SQ:.*]] = fmul contract half %[[RHS_IMAG]], %[[RHS_IMAG]]
+! BASIC: %[[SQ_NORM:.*]] = fadd contract half %[[RHS_REAL_SQ]], %[[RHS_IMAG_SQ]]
+! BASIC-DAG: %[[REAL_TMP_0:.*]] = fmul contract half %[[LHS_REAL]], %[[RHS_REAL]]
+! BASIC-DAG: %[[REAL_TMP_1:.*]] = fmul contract half %[[LHS_IMAG]], %[[RHS_IMAG]]
+! BASIC: %[[REAL_TMP_2:.*]] = fadd contract half %[[REAL_TMP_0]], %[[REAL_TMP_1]]
+! BASIC-DAG: %[[IMAG_TMP_0:.*]] = fmul contract half %[[LHS_IMAG]], %[[RHS_REAL]]
+! BASIC-DAG: %[[IMAG_TMP_1:.*]] = fmul contract half %[[LHS_REAL]], %[[RHS_IMAG]]
+! BASIC: %[[IMAG_TMP_2:.*]] = fsub contract half %[[IMAG_TMP_0]], %[[IMAG_TMP_1]]
+! BASIC: %[[REAL:.*]] = fdiv contract half %[[REAL_TMP_2]], %[[SQ_NORM]]
+! BASIC: %[[IMAG:.*]] = fdiv contract half %[[IMAG_TMP_2]], %[[SQ_NORM]]
+! BASIC: %[[RESULT_1:.*]] = insertvalue { half, half } poison, half %[[REAL]], 0
+! BASIC: %[[RESULT_2:.*]] = insertvalue { half, half } %[[RESULT_1]], half %[[IMAG]], 1
+! BASIC: store { half, half } %[[RESULT_2]], ptr %[[RET]], align 2
+
+! CHECK: ret void
+subroutine div_test_half(a,b,c)
+  complex(kind=2) :: a, b, c
+  a = b / c
+end subroutine div_test_half
+
+
+! CHECK-LABEL: @div_test_bfloat
+! CHECK-SAME: ptr %[[RET:.*]], ptr %[[LHS:.*]], ptr %[[RHS:.*]])
+! CHECK: %[[LOAD_LHS:.*]] = load { bfloat, bfloat }, ptr %[[LHS]], align 2
+! CHECK: %[[LOAD_RHS:.*]] = load { bfloat, bfloat }, ptr %[[RHS]], align 2
+! CHECK: %[[LHS_REAL:.*]] = extractvalue { bfloat, bfloat } %[[LOAD_LHS]], 0
+! CHECK: %[[LHS_IMAG:.*]] = extractvalue { bfloat, bfloat } %[[LOAD_LHS]], 1
+! CHECK: %[[RHS_REAL:.*]] = extractvalue { bfloat, bfloat } %[[LOAD_RHS]], 0
+! CHECK: %[[RHS_IMAG:.*]] = extractvalue { bfloat, bfloat } %[[LOAD_RHS]], 1
+
+! IMPRVD: %[[RHS_REAL_IMAG_RATIO:.*]] = fdiv contract bfloat %[[RHS_REAL]], %[[RHS_IMAG]]
+! IMPRVD: %[[RHS_REAL_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract bfloat %[[RHS_REAL_IMAG_RATIO]], %[[RHS_REAL]]
+! IMPRVD: %[[RHS_REAL_IMAG_DENOM:.*]] = fadd contract bfloat %[[RHS_IMAG]], %[[RHS_REAL_TIMES_RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[LHS_REAL_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract bfloat %[[LHS_REAL]], %[[RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[REAL_NUMERATOR_1:.*]] = fadd contract bfloat %[[LHS_REAL_TIMES_RHS_REAL_IMAG_RATIO]], %[[LHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_1:.*]] = fdiv contract bfloat %[[REAL_NUMERATOR_1]], %[[RHS_REAL_IMAG_DENOM]]
+! IMPRVD: %[[LHS_IMAG_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract bfloat %[[LHS_IMAG]], %[[RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[IMAG_NUMERATOR_1:.*]] = fsub contract bfloat %[[LHS_IMAG_TIMES_RHS_REAL_IMAG_RATIO]], %[[LHS_REAL]]
+! IMPRVD: %[[RESULT_IMAG_1:.*]] = fdiv contract bfloat %[[IMAG_NUMERATOR_1]], %[[RHS_REAL_IMAG_DENOM]]
+! IMPRVD: %[[RHS_IMAG_REAL_RATIO:.*]] = fdiv contract bfloat %[[RHS_IMAG]], %[[RHS_REAL]]
+! IMPRVD: %[[RHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract bfloat %[[RHS_IMAG_REAL_RATIO]], %[[RHS_IMAG]]
+! IMPRVD: %[[RHS_IMAG_REAL_DENOM:.*]] = fadd contract bfloat %[[RHS_REAL]], %[[RHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[LHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract bfloat %[[LHS_IMAG]], %[[RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[REAL_NUMERATOR_2:.*]] = fadd contract bfloat %[[LHS_REAL]], %[[LHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[RESULT_REAL_2:.*]] = fdiv contract bfloat %[[REAL_NUMERATOR_2]], %[[RHS_IMAG_REAL_DENOM]]
+! IMPRVD: %[[LHS_REAL_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract bfloat %[[LHS_REAL]], %[[RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[IMAG_NUMERATOR_2:.*]] = fsub contract bfloat %[[LHS_IMAG]], %[[LHS_REAL_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[RESULT_IMAG_2:.*]] = fdiv contract bfloat %[[IMAG_NUMERATOR_2]], %[[RHS_IMAG_REAL_DENOM]]
+
+! Case 1. Zero denominator, numerator contains at most one NaN value.
+! IMPRVD: %[[RHS_REAL_ABS:.*]] = call contract bfloat @llvm.fabs.bf16(bfloat %[[RHS_REAL]])
+! IMPRVD: %[[RHS_REAL_ABS_IS_ZERO:.*]] = fcmp oeq bfloat %[[RHS_REAL_ABS]], 0xR0000
+! IMPRVD: %[[RHS_IMAG_ABS:.*]] = call contract bfloat @llvm.fabs.bf16(bfloat %[[RHS_IMAG]])
+! IMPRVD: %[[RHS_IMAG_ABS_IS_ZERO:.*]] = fcmp oeq bfloat %[[RHS_IMAG_ABS]], 0xR0000
+! IMPRVD: %[[LHS_REAL_IS_NOT_NAN:.*]] = fcmp ord bfloat %[[LHS_REAL]], 0xR0000
+! IMPRVD: %[[LHS_IMAG_IS_NOT_NAN:.*]] = fcmp ord bfloat %[[LHS_IMAG]], 0xR0000
+! IMPRVD: %[[LHS_CONTAINS_NOT_NAN_VALUE:.*]] = or i1 %[[LHS_REAL_IS_NOT_NAN]], %[[LHS_IMAG_IS_NOT_NAN]]
+! IMPRVD: %[[RHS_IS_ZERO:.*]] = and i1 %[[RHS_REAL_ABS_IS_ZERO]], %[[RHS_IMAG_ABS_IS_ZERO]]
+! IMPRVD: %[[RESULT_IS_INFINITY:.*]] = and i1 %[[LHS_CONTAINS_NOT_NAN_VALUE]], %[[RHS_IS_ZERO]]
+! IMPRVD: %[[INF_WITH_SIGN_OF_RHS_REAL:.*]] = call bfloat @llvm.copysign.bf16(bfloat 0xR7F80, bfloat %[[RHS_REAL]])
+! IMPRVD: %[[INFINITY_RESULT_REAL:.*]] = fmul contract bfloat %[[INF_WITH_SIGN_OF_RHS_REAL]], %[[LHS_REAL]]
+! IMPRVD: %[[INFINITY_RESULT_IMAG:.*]] = fmul contract bfloat %[[INF_WITH_SIGN_OF_RHS_REAL]], %[[LHS_IMAG]]
+
+! Case 2. Infinite numerator, finite denominator.
+! IMPRVD: %[[RHS_REAL_FINITE:.*]] = fcmp one bfloat %[[RHS_REAL_ABS]], 0xR7F80
+! IMPRVD: %[[RHS_IMAG_FINITE:.*]] = fcmp one bfloat %[[RHS_IMAG_ABS]], 0xR7F80
+! IMPRVD: %[[RHS_IS_FINITE:.*]] = and i1 %[[RHS_REAL_FINITE]], %[[RHS_IMAG_FINITE]]
+! IMPRVD: %[[LHS_REAL_ABS:.*]] = call contract bfloat @llvm.fabs.bf16(bfloat %[[LHS_REAL]])
+! IMPRVD: %[[LHS_REAL_INFINITE:.*]] = fcmp oeq bfloat %[[LHS_REAL_ABS]], 0xR7F80
+! IMPRVD: %[[LHS_IMAG_ABS:.*]] = call contract bfloat @llvm.fabs.bf16(bfloat %[[LHS_IMAG]])
+! IMPRVD: %[[LHS_IMAG_INFINITE:.*]] = fcmp oeq bfloat %[[LHS_IMAG_ABS]], 0xR7F80
+! IMPRVD: %[[LHS_IS_INFINITE:.*]] = or i1 %[[LHS_REAL_INFINITE]], %[[LHS_IMAG_INFINITE]]
+! IMPRVD: %[[INF_NUM_FINITE_DENOM:.*]] = and i1 %[[LHS_IS_INFINITE]], %[[RHS_IS_FINITE]]
+! IMPRVD: %[[LHS_REAL_IS_INF:.*]] = select i1 %[[LHS_REAL_INFINITE]], bfloat 0xR3F80, bfloat 0xR0000
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN:.*]] = call bfloat @llvm.copysign.bf16(bfloat %[[LHS_REAL_IS_INF]], bfloat %[[LHS_REAL]])
+! IMPRVD: %[[LHS_IMAG_IS_INF:.*]] = select i1 %[[LHS_IMAG_INFINITE]], bfloat 0xR3F80, bfloat 0xR0000
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN:.*]] = call bfloat @llvm.copysign.bf16(bfloat %[[LHS_IMAG_IS_INF]], bfloat %[[LHS_IMAG]])
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_REAL:.*]] = fmul contract bfloat %[[LHS_REAL_IS_INF_WITH_SIGN]], %[[RHS_REAL]]
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_IMAG:.*]] = fmul contract bfloat %[[LHS_IMAG_IS_INF_WITH_SIGN]], %[[RHS_IMAG]]
+! IMPRVD: %[[INF_MULTIPLICATOR_1:.*]] = fadd contract bfloat %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_REAL]], %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_3:.*]] = fmul contract bfloat %[[INF_MULTIPLICATOR_1]], 0xR7F80
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_IMAG:.*]] = fmul contract bfloat %[[LHS_REAL_IS_INF_WITH_SIGN]], %[[RHS_IMAG]]
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_REAL:.*]] = fmul contract bfloat %[[LHS_IMAG_IS_INF_WITH_SIGN]], %[[RHS_REAL]]
+! IMPRVD: %[[INF_MULTIPLICATOR_2:.*]] = fsub contract bfloat %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_REAL]], %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_IMAG]]
+! IMPRVD: %[[RESULT_IMAG_3:.*]] = fmul contract bfloat %[[INF_MULTIPLICATOR_2]], 0xR7F80
+
+! Case 3. Finite numerator, infinite denominator.
+! IMPRVD: %[[LHS_REAL_FINITE:.*]] = fcmp one bfloat %[[LHS_REAL_ABS]], 0xR7F80
+! IMPRVD: %[[LHS_IMAG_FINITE:.*]] = fcmp one bfloat %[[LHS_IMAG_ABS]], 0xR7F80
+! IMPRVD: %[[LHS_IS_FINITE:.*]] = and i1 %[[LHS_REAL_FINITE]], %[[LHS_IMAG_FINITE]]
+! IMPRVD: %[[RHS_REAL_INFINITE:.*]] = fcmp oeq bfloat %[[RHS_REAL_ABS]], 0xR7F80
+! IMPRVD: %[[RHS_IMAG_INFINITE:.*]] = fcmp oeq bfloat %[[RHS_IMAG_ABS]], 0xR7F80
+! IMPRVD: %[[RHS_IS_INFINITE:.*]] = or i1 %[[RHS_REAL_INFINITE]], %[[RHS_IMAG_INFINITE]]
+! IMPRVD: %[[FINITE_NUM_INFINITE_DENOM:.*]] = and i1 %[[LHS_IS_FINITE]], %[[RHS_IS_INFINITE]]
+! IMPRVD: %[[RHS_REAL_IS_INF:.*]] = select i1 %[[RHS_REAL_INFINITE]], bfloat 0xR3F80, bfloat 0xR0000
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN:.*]] = call bfloat @llvm.copysign.bf16(bfloat %[[RHS_REAL_IS_INF]], bfloat %[[RHS_REAL]])
+! IMPRVD: %[[RHS_IMAG_IS_INF:.*]] = select i1 %[[RHS_IMAG_INFINITE]], bfloat 0xR3F80, bfloat 0xR0000
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN:.*]] = call bfloat @llvm.copysign.bf16(bfloat %[[RHS_IMAG_IS_INF]], bfloat %[[RHS_IMAG]])
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_REAL:.*]] = fmul contract bfloat %[[LHS_REAL]], %[[RHS_REAL_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_IMAG:.*]] = fmul contract bfloat %[[LHS_IMAG]], %[[RHS_IMAG_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[ZERO_MULTIPLICATOR_1:.*]] = fadd contract bfloat %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_REAL]], %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_4:.*]] = fmul contract bfloat %[[ZERO_MULTIPLICATOR_1]], 0xR0000
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_IMAG:.*]] = fmul contract bfloat %[[LHS_IMAG]], %[[RHS_REAL_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_REAL:.*]] = fmul contract bfloat %[[LHS_REAL]], %[[RHS_IMAG_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[ZERO_MULTIPLICATOR_2:.*]] = fsub contract bfloat %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_IMAG]], %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_REAL]]
+! IMPRVD: %[[RESULT_IMAG_4:.*]] = fmul contract bfloat %[[ZERO_MULTIPLICATOR_2]], 0xR0000
+
+! IMPRVD: %[[REAL_ABS_SMALLER_THAN_IMAG_ABS:.*]] = fcmp olt bfloat %[[RHS_REAL_ABS]], %[[RHS_IMAG_ABS]]
+! IMPRVD: %[[RESULT_REAL:.*]] = select i1 %[[REAL_ABS_SMALLER_THAN_IMAG_ABS]], bfloat %[[RESULT_REAL_1]], bfloat %[[RESULT_REAL_2]]
+! IMPRVD: %[[RESULT_IMAG:.*]] = select i1 %[[REAL_ABS_SMALLER_THAN_IMAG_ABS]], bfloat %[[RESULT_IMAG_1]], bfloat %[[RESULT_IMAG_2]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_3:.*]] = select i1 %[[FINITE_NUM_INFINITE_DENOM]], bfloat %[[RESULT_REAL_4]], bfloat %[[RESULT_REAL]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_3:.*]] = select i1 %[[FINITE_NUM_INFINITE_DENOM]], bfloat %[[RESULT_IMAG_4]], bfloat %[[RESULT_IMAG]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_2:.*]] = select i1 %[[INF_NUM_FINITE_DENOM]], bfloat %[[RESULT_REAL_3]], bfloat %[[RESULT_REAL_SPECIAL_CASE_3]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_2:.*]] = select i1 %[[INF_NUM_FINITE_DENOM]], bfloat %[[RESULT_IMAG_3]], bfloat %[[RESULT_IMAG_SPECIAL_CASE_3]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_1:.*]] = select i1 %[[RESULT_IS_INFINITY]], bfloat %[[INFINITY_RESULT_REAL]], bfloat %[[RESULT_REAL_SPECIAL_CASE_2]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_1:.*]] = select i1 %[[RESULT_IS_INFINITY]], bfloat %[[INFINITY_RESULT_IMAG]], bfloat %[[RESULT_IMAG_SPECIAL_CASE_2]]
+! IMPRVD: %[[RESULT_REAL_IS_NAN:.*]] = fcmp uno bfloat %[[RESULT_REAL]], 0xR0000
+! IMPRVD: %[[RESULT_IMAG_IS_NAN:.*]] = fcmp uno bfloat %[[RESULT_IMAG]], 0xR0000
+! IMPRVD: %[[RESULT_IS_NAN:.*]] = and i1 %[[RESULT_REAL_IS_NAN]], %[[RESULT_IMAG_IS_NAN]]
+! IMPRVD: %[[RESULT_REAL_WITH_SPECIAL_CASES:.*]] = select i1 %[[RESULT_IS_NAN]], bfloat %[[RESULT_REAL_SPECIAL_CASE_1]], bfloat %[[RESULT_REAL]]
+! IMPRVD: %[[RESULT_IMAG_WITH_SPECIAL_CASES:.*]] = select i1 %[[RESULT_IS_NAN]], bfloat %[[RESULT_IMAG_SPECIAL_CASE_1]], bfloat %[[RESULT_IMAG]]
+! IMPRVD: %[[RESULT_1:.*]] = insertvalue { bfloat, bfloat } poison, bfloat %[[RESULT_REAL_WITH_SPECIAL_CASES]], 0
+! IMPRVD: %[[RESULT_2:.*]] = insertvalue { bfloat, bfloat } %[[RESULT_1]], bfloat %[[RESULT_IMAG_WITH_SPECIAL_CASES]], 1
+! IMPRVD: store { bfloat, bfloat } %[[RESULT_2]], ptr %[[RET]], align 2
+
+! BASIC-DAG: %[[RHS_REAL_SQ:.*]] = fmul contract bfloat %[[RHS_REAL]], %[[RHS_REAL]]
+! BASIC-DAG: %[[RHS_IMAG_SQ:.*]] = fmul contract bfloat %[[RHS_IMAG]], %[[RHS_IMAG]]
+! BASIC: %[[SQ_NORM:.*]] = fadd contract bfloat %[[RHS_REAL_SQ]], %[[RHS_IMAG_SQ]]
+! BASIC-DAG: %[[REAL_TMP_0:.*]] = fmul contract bfloat %[[LHS_REAL]], %[[RHS_REAL]]
+! BASIC-DAG: %[[REAL_TMP_1:.*]] = fmul contract bfloat %[[LHS_IMAG]], %[[RHS_IMAG]]
+! BASIC: %[[REAL_TMP_2:.*]] = fadd contract bfloat %[[REAL_TMP_0]], %[[REAL_TMP_1]]
+! BASIC-DAG: %[[IMAG_TMP_0:.*]] = fmul contract bfloat %[[LHS_IMAG]], %[[RHS_REAL]]
+! BASIC-DAG: %[[IMAG_TMP_1:.*]] = fmul contract bfloat %[[LHS_REAL]], %[[RHS_IMAG]]
+! BASIC: %[[IMAG_TMP_2:.*]] = fsub contract bfloat %[[IMAG_TMP_0]], %[[IMAG_TMP_1]]
+! BASIC: %[[REAL:.*]] = fdiv contract bfloat %[[REAL_TMP_2]], %[[SQ_NORM]]
+! BASIC: %[[IMAG:.*]] = fdiv contract bfloat %[[IMAG_TMP_2]], %[[SQ_NORM]]
+! BASIC: %[[RESULT_1:.*]] = insertvalue { bfloat, bfloat } poison, bfloat %[[REAL]], 0
+! BASIC: %[[RESULT_2:.*]] = insertvalue { bfloat, bfloat } %[[RESULT_1]], bfloat %[[IMAG]], 1
+! BASIC: store { bfloat, bfloat } %[[RESULT_2]], ptr %[[RET]], align 2
+
+! CHECK: ret void
+subroutine div_test_bfloat(a,b,c)
+  complex(kind=3) :: a, b, c
+  a = b / c
+end subroutine div_test_bfloat
+
+
+! CHECK-LABEL: @div_test_single
+! CHECK-SAME: ptr %[[RET:.*]], ptr %[[LHS:.*]], ptr %[[RHS:.*]])
+! CHECK: %[[LOAD_LHS:.*]] = load { float, float }, ptr %[[LHS]], align 4
+! CHECK: %[[LOAD_RHS:.*]] = load { float, float }, ptr %[[RHS]], align 4
+! CHECK: %[[LHS_REAL:.*]] = extractvalue { float, float } %[[LOAD_LHS]], 0
+! CHECK: %[[LHS_IMAG:.*]] = extractvalue { float, float } %[[LOAD_LHS]], 1
+! CHECK: %[[RHS_REAL:.*]] = extractvalue { float, float } %[[LOAD_RHS]], 0
+! CHECK: %[[RHS_IMAG:.*]] = extractvalue { float, float } %[[LOAD_RHS]], 1
+
+! IMPRVD: %[[RHS_REAL_IMAG_RATIO:.*]] = fdiv contract float %[[RHS_REAL]], %[[RHS_IMAG]]
+! IMPRVD: %[[RHS_REAL_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract float %[[RHS_REAL_IMAG_RATIO]], %[[RHS_REAL]]
+! IMPRVD: %[[RHS_REAL_IMAG_DENOM:.*]] = fadd contract float %[[RHS_IMAG]], %[[RHS_REAL_TIMES_RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[LHS_REAL_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract float %[[LHS_REAL]], %[[RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[REAL_NUMERATOR_1:.*]] = fadd contract float %[[LHS_REAL_TIMES_RHS_REAL_IMAG_RATIO]], %[[LHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_1:.*]] = fdiv contract float %[[REAL_NUMERATOR_1]], %[[RHS_REAL_IMAG_DENOM]]
+! IMPRVD: %[[LHS_IMAG_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract float %[[LHS_IMAG]], %[[RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[IMAG_NUMERATOR_1:.*]] = fsub contract float %[[LHS_IMAG_TIMES_RHS_REAL_IMAG_RATIO]], %[[LHS_REAL]]
+! IMPRVD: %[[RESULT_IMAG_1:.*]] = fdiv contract float %[[IMAG_NUMERATOR_1]], %[[RHS_REAL_IMAG_DENOM]]
+! IMPRVD: %[[RHS_IMAG_REAL_RATIO:.*]] = fdiv contract float %[[RHS_IMAG]], %[[RHS_REAL]]
+! IMPRVD: %[[RHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract float %[[RHS_IMAG_REAL_RATIO]], %[[RHS_IMAG]]
+! IMPRVD: %[[RHS_IMAG_REAL_DENOM:.*]] = fadd contract float %[[RHS_REAL]], %[[RHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[LHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract float %[[LHS_IMAG]], %[[RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[REAL_NUMERATOR_2:.*]] = fadd contract float %[[LHS_REAL]], %[[LHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[RESULT_REAL_2:.*]] = fdiv contract float %[[REAL_NUMERATOR_2]], %[[RHS_IMAG_REAL_DENOM]]
+! IMPRVD: %[[LHS_REAL_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract float %[[LHS_REAL]], %[[RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[IMAG_NUMERATOR_2:.*]] = fsub contract float %[[LHS_IMAG]], %[[LHS_REAL_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[RESULT_IMAG_2:.*]] = fdiv contract float %[[IMAG_NUMERATOR_2]], %[[RHS_IMAG_REAL_DENOM]]
+
+! Case 1. Zero denominator, numerator contains at most one NaN value.
+! IMPRVD: %[[RHS_REAL_ABS:.*]] = call contract float @llvm.fabs.f32(float %[[RHS_REAL]])
+! IMPRVD: %[[RHS_REAL_ABS_IS_ZERO:.*]] = fcmp oeq float %[[RHS_REAL_ABS]], 0.000000e+00
+! IMPRVD: %[[RHS_IMAG_ABS:.*]] = call contract float @llvm.fabs.f32(float %[[RHS_IMAG]])
+! IMPRVD: %[[RHS_IMAG_ABS_IS_ZERO:.*]] = fcmp oeq float %[[RHS_IMAG_ABS]], 0.000000e+00
+! IMPRVD: %[[LHS_REAL_IS_NOT_NAN:.*]] = fcmp ord float %[[LHS_REAL]], 0.000000e+00
+! IMPRVD: %[[LHS_IMAG_IS_NOT_NAN:.*]] = fcmp ord float %[[LHS_IMAG]], 0.000000e+00
+! IMPRVD: %[[LHS_CONTAINS_NOT_NAN_VALUE:.*]] = or i1 %[[LHS_REAL_IS_NOT_NAN]], %[[LHS_IMAG_IS_NOT_NAN]]
+! IMPRVD: %[[RHS_IS_ZERO:.*]] = and i1 %[[RHS_REAL_ABS_IS_ZERO]], %[[RHS_IMAG_ABS_IS_ZERO]]
+! IMPRVD: %[[RESULT_IS_INFINITY:.*]] = and i1 %[[LHS_CONTAINS_NOT_NAN_VALUE]], %[[RHS_IS_ZERO]]
+! IMPRVD: %[[INF_WITH_SIGN_OF_RHS_REAL:.*]] = call float @llvm.copysign.f32(float 0x7FF0000000000000, float %[[RHS_REAL]])
+! IMPRVD: %[[INFINITY_RESULT_REAL:.*]] = fmul contract float %[[INF_WITH_SIGN_OF_RHS_REAL]], %[[LHS_REAL]]
+! IMPRVD: %[[INFINITY_RESULT_IMAG:.*]] = fmul contract float %[[INF_WITH_SIGN_OF_RHS_REAL]], %[[LHS_IMAG]]
+
+! Case 2. Infinite numerator, finite denominator.
+! IMPRVD: %[[RHS_REAL_FINITE:.*]] = fcmp one float %[[RHS_REAL_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[RHS_IMAG_FINITE:.*]] = fcmp one float %[[RHS_IMAG_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[RHS_IS_FINITE:.*]] = and i1 %[[RHS_REAL_FINITE]], %[[RHS_IMAG_FINITE]]
+! IMPRVD: %[[LHS_REAL_ABS:.*]] = call contract float @llvm.fabs.f32(float %[[LHS_REAL]])
+! IMPRVD: %[[LHS_REAL_INFINITE:.*]] = fcmp oeq float %[[LHS_REAL_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[LHS_IMAG_ABS:.*]] = call contract float @llvm.fabs.f32(float %[[LHS_IMAG]])
+! IMPRVD: %[[LHS_IMAG_INFINITE:.*]] = fcmp oeq float %[[LHS_IMAG_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[LHS_IS_INFINITE:.*]] = or i1 %[[LHS_REAL_INFINITE]], %[[LHS_IMAG_INFINITE]]
+! IMPRVD: %[[INF_NUM_FINITE_DENOM:.*]] = and i1 %[[LHS_IS_INFINITE]], %[[RHS_IS_FINITE]]
+! IMPRVD: %[[LHS_REAL_IS_INF:.*]] = select i1 %[[LHS_REAL_INFINITE]], float 1.000000e+00, float 0.000000e+00
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN:.*]] = call float @llvm.copysign.f32(float %[[LHS_REAL_IS_INF]], float %[[LHS_REAL]])
+! IMPRVD: %[[LHS_IMAG_IS_INF:.*]] = select i1 %[[LHS_IMAG_INFINITE]], float 1.000000e+00, float 0.000000e+00
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN:.*]] = call float @llvm.copysign.f32(float %[[LHS_IMAG_IS_INF]], float %[[LHS_IMAG]])
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_REAL:.*]] = fmul contract float %[[LHS_REAL_IS_INF_WITH_SIGN]], %[[RHS_REAL]]
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_IMAG:.*]] = fmul contract float %[[LHS_IMAG_IS_INF_WITH_SIGN]], %[[RHS_IMAG]]
+! IMPRVD: %[[INF_MULTIPLICATOR_1:.*]] = fadd contract float %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_REAL]], %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_3:.*]] = fmul contract float %[[INF_MULTIPLICATOR_1]], 0x7FF0000000000000
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_IMAG:.*]] = fmul contract float %[[LHS_REAL_IS_INF_WITH_SIGN]], %[[RHS_IMAG]]
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_REAL:.*]] = fmul contract float %[[LHS_IMAG_IS_INF_WITH_SIGN]], %[[RHS_REAL]]
+! IMPRVD: %[[INF_MULTIPLICATOR_2:.*]] = fsub contract float %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_REAL]], %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_IMAG]]
+! IMPRVD: %[[RESULT_IMAG_3:.*]] = fmul contract float %[[INF_MULTIPLICATOR_2]], 0x7FF0000000000000
+
+! Case 3. Finite numerator, infinite denominator.
+! IMPRVD: %[[LHS_REAL_FINITE:.*]] = fcmp one float %[[LHS_REAL_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[LHS_IMAG_FINITE:.*]] = fcmp one float %[[LHS_IMAG_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[LHS_IS_FINITE:.*]] = and i1 %[[LHS_REAL_FINITE]], %[[LHS_IMAG_FINITE]]
+! IMPRVD: %[[RHS_REAL_INFINITE:.*]] = fcmp oeq float %[[RHS_REAL_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[RHS_IMAG_INFINITE:.*]] = fcmp oeq float %[[RHS_IMAG_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[RHS_IS_INFINITE:.*]] = or i1 %[[RHS_REAL_INFINITE]], %[[RHS_IMAG_INFINITE]]
+! IMPRVD: %[[FINITE_NUM_INFINITE_DENOM:.*]] = and i1 %[[LHS_IS_FINITE]], %[[RHS_IS_INFINITE]]
+! IMPRVD: %[[RHS_REAL_IS_INF:.*]] = select i1 %[[RHS_REAL_INFINITE]], float 1.000000e+00, float 0.000000e+00
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN:.*]] = call float @llvm.copysign.f32(float %[[RHS_REAL_IS_INF]], float %[[RHS_REAL]])
+! IMPRVD: %[[RHS_IMAG_IS_INF:.*]] = select i1 %[[RHS_IMAG_INFINITE]], float 1.000000e+00, float 0.000000e+00
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN:.*]] = call float @llvm.copysign.f32(float %[[RHS_IMAG_IS_INF]], float %[[RHS_IMAG]])
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_REAL:.*]] = fmul contract float %[[LHS_REAL]], %[[RHS_REAL_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_IMAG:.*]] = fmul contract float %[[LHS_IMAG]], %[[RHS_IMAG_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[ZERO_MULTIPLICATOR_1:.*]] = fadd contract float %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_REAL]], %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_4:.*]] = fmul contract float %[[ZERO_MULTIPLICATOR_1]], 0.000000e+00
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_IMAG:.*]] = fmul contract float %[[LHS_IMAG]], %[[RHS_REAL_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_REAL:.*]] = fmul contract float %[[LHS_REAL]], %[[RHS_IMAG_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[ZERO_MULTIPLICATOR_2:.*]] = fsub contract float %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_IMAG]], %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_REAL]]
+! IMPRVD: %[[RESULT_IMAG_4:.*]] = fmul contract float %[[ZERO_MULTIPLICATOR_2]], 0.000000e+00
+
+! IMPRVD: %[[REAL_ABS_SMALLER_THAN_IMAG_ABS:.*]] = fcmp olt float %[[RHS_REAL_ABS]], %[[RHS_IMAG_ABS]]
+! IMPRVD: %[[RESULT_REAL:.*]] = select i1 %[[REAL_ABS_SMALLER_THAN_IMAG_ABS]], float %[[RESULT_REAL_1]], float %[[RESULT_REAL_2]]
+! IMPRVD: %[[RESULT_IMAG:.*]] = select i1 %[[REAL_ABS_SMALLER_THAN_IMAG_ABS]], float %[[RESULT_IMAG_1]], float %[[RESULT_IMAG_2]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_3:.*]] = select i1 %[[FINITE_NUM_INFINITE_DENOM]], float %[[RESULT_REAL_4]], float %[[RESULT_REAL]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_3:.*]] = select i1 %[[FINITE_NUM_INFINITE_DENOM]], float %[[RESULT_IMAG_4]], float %[[RESULT_IMAG]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_2:.*]] = select i1 %[[INF_NUM_FINITE_DENOM]], float %[[RESULT_REAL_3]], float %[[RESULT_REAL_SPECIAL_CASE_3]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_2:.*]] = select i1 %[[INF_NUM_FINITE_DENOM]], float %[[RESULT_IMAG_3]], float %[[RESULT_IMAG_SPECIAL_CASE_3]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_1:.*]] = select i1 %[[RESULT_IS_INFINITY]], float %[[INFINITY_RESULT_REAL]], float %[[RESULT_REAL_SPECIAL_CASE_2]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_1:.*]] = select i1 %[[RESULT_IS_INFINITY]], float %[[INFINITY_RESULT_IMAG]], float %[[RESULT_IMAG_SPECIAL_CASE_2]]
+! IMPRVD: %[[RESULT_REAL_IS_NAN:.*]] = fcmp uno float %[[RESULT_REAL]], 0.000000e+00
+! IMPRVD: %[[RESULT_IMAG_IS_NAN:.*]] = fcmp uno float %[[RESULT_IMAG]], 0.000000e+00
+! IMPRVD: %[[RESULT_IS_NAN:.*]] = and i1 %[[RESULT_REAL_IS_NAN]], %[[RESULT_IMAG_IS_NAN]]
+! IMPRVD: %[[RESULT_REAL_WITH_SPECIAL_CASES:.*]] = select i1 %[[RESULT_IS_NAN]], float %[[RESULT_REAL_SPECIAL_CASE_1]], float %[[RESULT_REAL]]
+! IMPRVD: %[[RESULT_IMAG_WITH_SPECIAL_CASES:.*]] = select i1 %[[RESULT_IS_NAN]], float %[[RESULT_IMAG_SPECIAL_CASE_1]], float %[[RESULT_IMAG]]
+! IMPRVD: %[[RESULT_1:.*]] = insertvalue { float, float } poison, float %[[RESULT_REAL_WITH_SPECIAL_CASES]], 0
+! IMPRVD: %[[RESULT_2:.*]] = insertvalue { float, float } %[[RESULT_1]], float %[[RESULT_IMAG_WITH_SPECIAL_CASES]], 1
+! IMPRVD: store { float, float } %[[RESULT_2]], ptr %[[RET]], align 4
+
+! BASIC-DAG: %[[RHS_REAL_SQ:.*]] = fmul contract float %[[RHS_REAL]], %[[RHS_REAL]]
+! BASIC-DAG: %[[RHS_IMAG_SQ:.*]] = fmul contract float %[[RHS_IMAG]], %[[RHS_IMAG]]
+! BASIC: %[[SQ_NORM:.*]] = fadd contract float %[[RHS_REAL_SQ]], %[[RHS_IMAG_SQ]]
+! BASIC-DAG: %[[REAL_TMP_0:.*]] = fmul contract float %[[LHS_REAL]], %[[RHS_REAL]]
+! BASIC-DAG: %[[REAL_TMP_1:.*]] = fmul contract float %[[LHS_IMAG]], %[[RHS_IMAG]]
+! BASIC: %[[REAL_TMP_2:.*]] = fadd contract float %[[REAL_TMP_0]], %[[REAL_TMP_1]]
+! BASIC-DAG: %[[IMAG_TMP_0:.*]] = fmul contract float %[[LHS_IMAG]], %[[RHS_REAL]]
+! BASIC-DAG: %[[IMAG_TMP_1:.*]] = fmul contract float %[[LHS_REAL]], %[[RHS_IMAG]]
+! BASIC: %[[IMAG_TMP_2:.*]] = fsub contract float %[[IMAG_TMP_0]], %[[IMAG_TMP_1]]
+! BASIC: %[[REAL:.*]] = fdiv contract float %[[REAL_TMP_2]], %[[SQ_NORM]]
+! BASIC: %[[IMAG:.*]] = fdiv contract float %[[IMAG_TMP_2]], %[[SQ_NORM]]
+! BASIC: %[[RESULT_1:.*]] = insertvalue { float, float } poison, float %[[REAL]], 0
+! BASIC: %[[RESULT_2:.*]] = insertvalue { float, float } %[[RESULT_1]], float %[[IMAG]], 1
+! BASIC: store { float, float } %[[RESULT_2]], ptr %[[RET]], align 4
+
+! CHECK: ret void
+subroutine div_test_single(a,b,c)
+  complex(kind=4) :: a, b, c
+  a = b / c
+end subroutine div_test_single
+
+
+! CHECK-LABEL: @div_test_double
+! CHECK-SAME: ptr %[[RET:.*]], ptr %[[LHS:.*]], ptr %[[RHS:.*]])
+! CHECK: %[[LOAD_LHS:.*]] = load { double, double }, ptr %[[LHS]], align 8
+! CHECK: %[[LOAD_RHS:.*]] = load { double, double }, ptr %[[RHS]], align 8
+! CHECK: %[[LHS_REAL:.*]] = extractvalue { double, double } %[[LOAD_LHS]], 0
+! CHECK: %[[LHS_IMAG:.*]] = extractvalue { double, double } %[[LOAD_LHS]], 1
+! CHECK: %[[RHS_REAL:.*]] = extractvalue { double, double } %[[LOAD_RHS]], 0
+! CHECK: %[[RHS_IMAG:.*]] = extractvalue { double, double } %[[LOAD_RHS]], 1
+
+! IMPRVD: %[[RHS_REAL_IMAG_RATIO:.*]] = fdiv contract double %[[RHS_REAL]], %[[RHS_IMAG]]
+! IMPRVD: %[[RHS_REAL_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract double %[[RHS_REAL_IMAG_RATIO]], %[[RHS_REAL]]
+! IMPRVD: %[[RHS_REAL_IMAG_DENOM:.*]] = fadd contract double %[[RHS_IMAG]], %[[RHS_REAL_TIMES_RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[LHS_REAL_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract double %[[LHS_REAL]], %[[RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[REAL_NUMERATOR_1:.*]] = fadd contract double %[[LHS_REAL_TIMES_RHS_REAL_IMAG_RATIO]], %[[LHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_1:.*]] = fdiv contract double %[[REAL_NUMERATOR_1]], %[[RHS_REAL_IMAG_DENOM]]
+! IMPRVD: %[[LHS_IMAG_TIMES_RHS_REAL_IMAG_RATIO:.*]] = fmul contract double %[[LHS_IMAG]], %[[RHS_REAL_IMAG_RATIO]]
+! IMPRVD: %[[IMAG_NUMERATOR_1:.*]] = fsub contract double %[[LHS_IMAG_TIMES_RHS_REAL_IMAG_RATIO]], %[[LHS_REAL]]
+! IMPRVD: %[[RESULT_IMAG_1:.*]] = fdiv contract double %[[IMAG_NUMERATOR_1]], %[[RHS_REAL_IMAG_DENOM]]
+! IMPRVD: %[[RHS_IMAG_REAL_RATIO:.*]] = fdiv contract double %[[RHS_IMAG]], %[[RHS_REAL]]
+! IMPRVD: %[[RHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract double %[[RHS_IMAG_REAL_RATIO]], %[[RHS_IMAG]]
+! IMPRVD: %[[RHS_IMAG_REAL_DENOM:.*]] = fadd contract double %[[RHS_REAL]], %[[RHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[LHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract double %[[LHS_IMAG]], %[[RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[REAL_NUMERATOR_2:.*]] = fadd contract double %[[LHS_REAL]], %[[LHS_IMAG_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[RESULT_REAL_2:.*]] = fdiv contract double %[[REAL_NUMERATOR_2]], %[[RHS_IMAG_REAL_DENOM]]
+! IMPRVD: %[[LHS_REAL_TIMES_RHS_IMAG_REAL_RATIO:.*]] = fmul contract double %[[LHS_REAL]], %[[RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[IMAG_NUMERATOR_2:.*]] = fsub contract double %[[LHS_IMAG]], %[[LHS_REAL_TIMES_RHS_IMAG_REAL_RATIO]]
+! IMPRVD: %[[RESULT_IMAG_2:.*]] = fdiv contract double %[[IMAG_NUMERATOR_2]], %[[RHS_IMAG_REAL_DENOM]]
+
+! Case 1. Zero denominator, numerator contains at most one NaN value.
+! IMPRVD: %[[RHS_REAL_ABS:.*]] = call contract double @llvm.fabs.f64(double %[[RHS_REAL]])
+! IMPRVD: %[[RHS_REAL_ABS_IS_ZERO:.*]] = fcmp oeq double %[[RHS_REAL_ABS]], 0.000000e+00
+! IMPRVD: %[[RHS_IMAG_ABS:.*]] = call contract double @llvm.fabs.f64(double %[[RHS_IMAG]])
+! IMPRVD: %[[RHS_IMAG_ABS_IS_ZERO:.*]] = fcmp oeq double %[[RHS_IMAG_ABS]], 0.000000e+00
+! IMPRVD: %[[LHS_REAL_IS_NOT_NAN:.*]] = fcmp ord double %[[LHS_REAL]], 0.000000e+00
+! IMPRVD: %[[LHS_IMAG_IS_NOT_NAN:.*]] = fcmp ord double %[[LHS_IMAG]], 0.000000e+00
+! IMPRVD: %[[LHS_CONTAINS_NOT_NAN_VALUE:.*]] = or i1 %[[LHS_REAL_IS_NOT_NAN]], %[[LHS_IMAG_IS_NOT_NAN]]
+! IMPRVD: %[[RHS_IS_ZERO:.*]] = and i1 %[[RHS_REAL_ABS_IS_ZERO]], %[[RHS_IMAG_ABS_IS_ZERO]]
+! IMPRVD: %[[RESULT_IS_INFINITY:.*]] = and i1 %[[LHS_CONTAINS_NOT_NAN_VALUE]], %[[RHS_IS_ZERO]]
+! IMPRVD: %[[INF_WITH_SIGN_OF_RHS_REAL:.*]] = call double @llvm.copysign.f64(double 0x7FF0000000000000, double %[[RHS_REAL]])
+! IMPRVD: %[[INFINITY_RESULT_REAL:.*]] = fmul contract double %[[INF_WITH_SIGN_OF_RHS_REAL]], %[[LHS_REAL]]
+! IMPRVD: %[[INFINITY_RESULT_IMAG:.*]] = fmul contract double %[[INF_WITH_SIGN_OF_RHS_REAL]], %[[LHS_IMAG]]
+
+! Case 2. Infinite numerator, finite denominator.
+! IMPRVD: %[[RHS_REAL_FINITE:.*]] = fcmp one double %[[RHS_REAL_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[RHS_IMAG_FINITE:.*]] = fcmp one double %[[RHS_IMAG_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[RHS_IS_FINITE:.*]] = and i1 %[[RHS_REAL_FINITE]], %[[RHS_IMAG_FINITE]]
+! IMPRVD: %[[LHS_REAL_ABS:.*]] = call contract double @llvm.fabs.f64(double %[[LHS_REAL]])
+! IMPRVD: %[[LHS_REAL_INFINITE:.*]] = fcmp oeq double %[[LHS_REAL_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[LHS_IMAG_ABS:.*]] = call contract double @llvm.fabs.f64(double %[[LHS_IMAG]])
+! IMPRVD: %[[LHS_IMAG_INFINITE:.*]] = fcmp oeq double %[[LHS_IMAG_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[LHS_IS_INFINITE:.*]] = or i1 %[[LHS_REAL_INFINITE]], %[[LHS_IMAG_INFINITE]]
+! IMPRVD: %[[INF_NUM_FINITE_DENOM:.*]] = and i1 %[[LHS_IS_INFINITE]], %[[RHS_IS_FINITE]]
+! IMPRVD: %[[LHS_REAL_IS_INF:.*]] = select i1 %[[LHS_REAL_INFINITE]], double 1.000000e+00, double 0.000000e+00
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN:.*]] = call double @llvm.copysign.f64(double %[[LHS_REAL_IS_INF]], double %[[LHS_REAL]])
+! IMPRVD: %[[LHS_IMAG_IS_INF:.*]] = select i1 %[[LHS_IMAG_INFINITE]], double 1.000000e+00, double 0.000000e+00
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN:.*]] = call double @llvm.copysign.f64(double %[[LHS_IMAG_IS_INF]], double %[[LHS_IMAG]])
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_REAL:.*]] = fmul contract double %[[LHS_REAL_IS_INF_WITH_SIGN]], %[[RHS_REAL]]
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_IMAG:.*]] = fmul contract double %[[LHS_IMAG_IS_INF_WITH_SIGN]], %[[RHS_IMAG]]
+! IMPRVD: %[[INF_MULTIPLICATOR_1:.*]] = fadd contract double %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_REAL]], %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_3:.*]] = fmul contract double %[[INF_MULTIPLICATOR_1]], 0x7FF0000000000000
+! IMPRVD: %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_IMAG:.*]] = fmul contract double %[[LHS_REAL_IS_INF_WITH_SIGN]], %[[RHS_IMAG]]
+! IMPRVD: %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_REAL:.*]] = fmul contract double %[[LHS_IMAG_IS_INF_WITH_SIGN]], %[[RHS_REAL]]
+! IMPRVD: %[[INF_MULTIPLICATOR_2:.*]] = fsub contract double %[[LHS_IMAG_IS_INF_WITH_SIGN_TIMES_RHS_REAL]], %[[LHS_REAL_IS_INF_WITH_SIGN_TIMES_RHS_IMAG]]
+! IMPRVD: %[[RESULT_IMAG_3:.*]] = fmul contract double %[[INF_MULTIPLICATOR_2]], 0x7FF0000000000000
+
+! Case 3. Finite numerator, infinite denominator.
+! IMPRVD: %[[LHS_REAL_FINITE:.*]] = fcmp one double %[[LHS_REAL_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[LHS_IMAG_FINITE:.*]] = fcmp one double %[[LHS_IMAG_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[LHS_IS_FINITE:.*]] = and i1 %[[LHS_REAL_FINITE]], %[[LHS_IMAG_FINITE]]
+! IMPRVD: %[[RHS_REAL_INFINITE:.*]] = fcmp oeq double %[[RHS_REAL_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[RHS_IMAG_INFINITE:.*]] = fcmp oeq double %[[RHS_IMAG_ABS]], 0x7FF0000000000000
+! IMPRVD: %[[RHS_IS_INFINITE:.*]] = or i1 %[[RHS_REAL_INFINITE]], %[[RHS_IMAG_INFINITE]]
+! IMPRVD: %[[FINITE_NUM_INFINITE_DENOM:.*]] = and i1 %[[LHS_IS_FINITE]], %[[RHS_IS_INFINITE]]
+! IMPRVD: %[[RHS_REAL_IS_INF:.*]] = select i1 %[[RHS_REAL_INFINITE]], double 1.000000e+00, double 0.000000e+00
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN:.*]] = call double @llvm.copysign.f64(double %[[RHS_REAL_IS_INF]], double %[[RHS_REAL]])
+! IMPRVD: %[[RHS_IMAG_IS_INF:.*]] = select i1 %[[RHS_IMAG_INFINITE]], double 1.000000e+00, double 0.000000e+00
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN:.*]] = call double @llvm.copysign.f64(double %[[RHS_IMAG_IS_INF]], double %[[RHS_IMAG]])
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_REAL:.*]] = fmul contract double %[[LHS_REAL]], %[[RHS_REAL_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_IMAG:.*]] = fmul contract double %[[LHS_IMAG]], %[[RHS_IMAG_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[ZERO_MULTIPLICATOR_1:.*]] = fadd contract double %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_REAL]], %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_IMAG]]
+! IMPRVD: %[[RESULT_REAL_4:.*]] = fmul contract double %[[ZERO_MULTIPLICATOR_1]], 0.000000e+00
+! IMPRVD: %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_IMAG:.*]] = fmul contract double %[[LHS_IMAG]], %[[RHS_REAL_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_REAL:.*]] = fmul contract double %[[LHS_REAL]], %[[RHS_IMAG_IS_INF_WITH_SIGN]]
+! IMPRVD: %[[ZERO_MULTIPLICATOR_2:.*]] = fsub contract double %[[RHS_REAL_IS_INF_WITH_SIGN_TIMES_LHS_IMAG]], %[[RHS_IMAG_IS_INF_WITH_SIGN_TIMES_LHS_REAL]]
+! IMPRVD: %[[RESULT_IMAG_4:.*]] = fmul contract double %[[ZERO_MULTIPLICATOR_2]], 0.000000e+00
+
+! IMPRVD: %[[REAL_ABS_SMALLER_THAN_IMAG_ABS:.*]] = fcmp olt double %[[RHS_REAL_ABS]], %[[RHS_IMAG_ABS]]
+! IMPRVD: %[[RESULT_REAL:.*]] = select i1 %[[REAL_ABS_SMALLER_THAN_IMAG_ABS]], double %[[RESULT_REAL_1]], double %[[RESULT_REAL_2]]
+! IMPRVD: %[[RESULT_IMAG:.*]] = select i1 %[[REAL_ABS_SMALLER_THAN_IMAG_ABS]], double %[[RESULT_IMAG_1]], double %[[RESULT_IMAG_2]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_3:.*]] = select i1 %[[FINITE_NUM_INFINITE_DENOM]], double %[[RESULT_REAL_4]], double %[[RESULT_REAL]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_3:.*]] = select i1 %[[FINITE_NUM_INFINITE_DENOM]], double %[[RESULT_IMAG_4]], double %[[RESULT_IMAG]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_2:.*]] = select i1 %[[INF_NUM_FINITE_DENOM]], double %[[RESULT_REAL_3]], double %[[RESULT_REAL_SPECIAL_CASE_3]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_2:.*]] = select i1 %[[INF_NUM_FINITE_DENOM]], double %[[RESULT_IMAG_3]], double %[[RESULT_IMAG_SPECIAL_CASE_3]]
+! IMPRVD: %[[RESULT_REAL_SPECIAL_CASE_1:.*]] = select i1 %[[RESULT_IS_INFINITY]], double %[[INFINITY_RESULT_REAL]], double %[[RESULT_REAL_SPECIAL_CASE_2]]
+! IMPRVD: %[[RESULT_IMAG_SPECIAL_CASE_1:.*]] = select i1 %[[RESULT_IS_INFINITY]], double %[[INFINITY_RESULT_IMAG]], double %[[RESULT_IMAG_SPECIAL_CASE_2]]
+! IMPRVD: %[[RESULT_REAL_IS_NAN:.*]] = fcmp uno double %[[RESULT_REAL]], 0.000000e+00
+! IMPRVD: %[[RESULT_IMAG_IS_NAN:.*]] = fcmp uno double %[[RESULT_IMAG]], 0.000000e+00
+! IMPRVD: %[[RESULT_IS_NAN:.*]] = and i1 %[[RESULT_REAL_IS_NAN]], %[[RESULT_IMAG_IS_NAN]]
+! IMPRVD: %[[RESULT_REAL_WITH_SPECIAL_CASES:.*]] = select i1 %[[RESULT_IS_NAN]], double %[[RESULT_REAL_SPECIAL_CASE_1]], double %[[RESULT_REAL]]
+! IMPRVD: %[[RESULT_IMAG_WITH_SPECIAL_CASES:.*]] = select i1 %[[RESULT_IS_NAN]], double %[[RESULT_IMAG_SPECIAL_CASE_1]], double %[[RESULT_IMAG]]
+! IMPRVD: %[[RESULT_1:.*]] = insertvalue { double, double } poison, double %[[RESULT_REAL_WITH_SPECIAL_CASES]], 0
+! IMPRVD: %[[RESULT_2:.*]] = insertvalue { double, double } %[[RESULT_1]], double %[[RESULT_IMAG_WITH_SPECIAL_CASES]], 1
+! IMPRVD: store { double, double } %[[RESULT_2]], ptr %[[RET]], align 8
+
+! BASIC-DAG: %[[RHS_REAL_SQ:.*]] = fmul contract double %[[RHS_REAL]], %[[RHS_REAL]]
+! BASIC-DAG: %[[RHS_IMAG_SQ:.*]] = fmul contract double %[[RHS_IMAG]], %[[RHS_IMAG]]
+! BASIC: %[[SQ_NORM:.*]] = fadd contract double %[[RHS_REAL_SQ]], %[[RHS_IMAG_SQ]]
+! BASIC-DAG: %[[REAL_TMP_0:.*]] = fmul contract double %[[LHS_REAL]], %[[RHS_REAL]]
+! BASIC-DAG: %[[REAL_TMP_1:.*]] = fmul contract double %[[LHS_IMAG]], %[[RHS_IMAG]]
+! BASIC: %[[REAL_TMP_2:.*]] = fadd contract double %[[REAL_TMP_0]], %[[REAL_TMP_1]]
+! BASIC-DAG: %[[IMAG_TMP_0:.*]] = fmul contract double %[[LHS_IMAG]], %[[RHS_REAL]]
+! BASIC-DAG: %[[IMAG_TMP_1:.*]] = fmul contract double %[[LHS_REAL]], %[[RHS_IMAG]]
+! BASIC: %[[IMAG_TMP_2:.*]] = fsub contract double %[[IMAG_TMP_0]], %[[IMAG_TMP_1]]
+! BASIC: %[[REAL:.*]] = fdiv contract double %[[REAL_TMP_2]], %[[SQ_NORM]]
+! BASIC: %[[IMAG:.*]] = fdiv contract double %[[IMAG_TMP_2]], %[[SQ_NORM]]
+! BASIC: %[[RESULT_1:.*]] = insertvalue { double, double } poison, double %[[REAL]], 0
+! BASIC: %[[RESULT_2:.*]] = insertvalue { double, double } %[[RESULT_1]], double %[[IMAG]], 1
+! BASIC: store { double, double } %[[RESULT_2]], ptr %[[RET]], align 8
+
+! CHECK: ret void
+subroutine div_test_double(a,b,c)
+  complex(kind=8) :: a, b, c
+  a = b / c
+end subroutine div_test_double
diff --git a/flang/test/Lower/HLFIR/complex-div-to-hlfir-kind10.f90 b/flang/test/Lower/HLFIR/complex-div-to-hlfir-kind10.f90
new file mode 100644
index 0000000000000..0e219e30ec94b
--- /dev/null
+++ b/flang/test/Lower/HLFIR/complex-div-to-hlfir-kind10.f90
@@ -0,0 +1,34 @@
+! Test lowering of complex division according to options
+
+! REQUIRES: target=x86_64{{.*}}
+! RUN: bbc -complex-range=full -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,FULL
+! RUN: bbc -complex-range=improved -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,IMPRVD
+! RUN: bbc -complex-range=basic -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,BASIC
+! RUN: %flang_fc1 -complex-range=full -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,FULL
+! RUN: %flang_fc1 -complex-range=improved -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,IMPRVD
+! RUN: %flang_fc1 -complex-range=basic -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,BASIC
+
+! CHECK-LABEL: @_QPdiv_test_extended
+! CHECK-SAME: %[[REF_0:.*]]: !fir.ref<complex<f80>> {{.*}}, %[[REF_1:.*]]: !fir.ref<complex<f80>> {{.*}}, %[[REF_2:.*]]: !fir.ref<complex<f80>> {{.*}})
+! CHECK: %[[VAL_3:.*]] = fir.dummy_scope : !fir.dscope
+! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[REF_0]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_extendedEa"} : (!fir.ref<complex<f80>>, !fir.dscope) -> (!fir.ref<complex<f80>>, !fir.ref<complex<f80>>)
+! CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %[[REF_1]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_extendedEb"} : (!fir.ref<complex<f80>>, !fir.dscope) -> (!fir.ref<complex<f80>>, !fir.ref<complex<f80>>)
+! CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %[[REF_2]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_extendedEc"} : (!fir.ref<complex<f80>>, !fir.dscope) -> (!fir.ref<complex<f80>>, !fir.ref<complex<f80>>)
+! CHECK: %[[VAL_7:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref<complex<f80>>
+! CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_6]]#0 : !fir.ref<complex<f80>>
+
+! FULL: %[[VAL_9:.*]] = fir.extract_value %[[VAL_7]], [0 : index] : (complex<f80>) -> f80
+! FULL: %[[VAL_10:.*]] = fir.extract_value %[[VAL_7]], [1 : index] : (complex<f80>) -> f80
+! FULL: %[[VAL_11:.*]] = fir.extract_value %[[VAL_8]], [0 : index] : (complex<f80>) -> f80
+! FULL: %[[VAL_12:.*]] = fir.extract_value %[[VAL_8]], [1 : index] : (complex<f80>) -> f80
+! FULL: %[[VAL_RET:.*]] = fir.call @__divxc3(%[[VAL_9]], %[[VAL_10]], %[[VAL_11]], %[[VAL_12]]) fastmath<contract> : (f80, f80, f80, f80) -> complex<f80>
+
+! IMPRVD: %[[VAL_RET:.*]] = complex.div %[[VAL_7]], %[[VAL_8]] fastmath<contract> : complex<f80>
+! BASIC: %[[VAL_RET:.*]] = complex.div %[[VAL_7]], %[[VAL_8]] fastmath<contract> : complex<f80>
+
+! CHECK: hlfir.assign %[[VAL_RET]] to %[[VAL_4]]#0 : complex<f80>, !fir.ref<complex<f80>>
+! CHECK: return
+subroutine div_test_extended(a,b,c)
+  complex(kind=10) :: a, b, c
+  a = b / c
+end subroutine div_test_extended
diff --git a/flang/test/Lower/HLFIR/complex-div-to-hlfir-kind16.f90 b/flang/test/Lower/HLFIR/complex-div-to-hlfir-kind16.f90
new file mode 100644
index 0000000000000..3e158b0bed998
--- /dev/null
+++ b/flang/test/Lower/HLFIR/complex-div-to-hlfir-kind16.f90
@@ -0,0 +1,35 @@
+! Test lowering of complex division according to options
+
+! REQUIRES: flang-supports-f128-math
+! RUN: bbc -complex-range=full -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,FULL
+! RUN: bbc -complex-range=improved -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,IMPRVD
+! RUN: bbc -complex-range=basic -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,BASIC
+! RUN: %flang_fc1 -complex-range=full -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,FULL
+! RUN: %flang_fc1 -complex-range=improved -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,IMPRVD
+! RUN: %flang_fc1 -complex-range=basic -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,BASIC
+
+
+! CHECK-LABEL: @_QPdiv_test_quad
+! CHECK-SAME: %[[REF_0:.*]]: !fir.ref<complex<f128>> {{.*}}, %[[REF_1:.*]]: !fir.ref<complex<f128>> {{.*}}, %[[REF_2:.*]]: !fir.ref<complex<f128>> {{.*}})
+! CHECK: %[[VAL_3:.*]] = fir.dummy_scope : !fir.dscope
+! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[REF_0]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_quadEa"} : (!fir.ref<complex<f128>>, !fir.dscope) -> (!fir.ref<complex<f128>>, !fir.ref<complex<f128>>)
+! CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %[[REF_1]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_quadEb"} : (!fir.ref<complex<f128>>, !fir.dscope) -> (!fir.ref<complex<f128>>, !fir.ref<complex<f128>>)
+! CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %[[REF_2]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_quadEc"} : (!fir.ref<complex<f128>>, !fir.dscope) -> (!fir.ref<complex<f128>>, !fir.ref<complex<f128>>)
+! CHECK: %[[VAL_7:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref<complex<f128>>
+! CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_6]]#0 : !fir.ref<complex<f128>>
+
+! FULL: %[[VAL_9:.*]] = fir.extract_value %[[VAL_7]], [0 : index] : (complex<f128>) -> f128
+! FULL: %[[VAL_10:.*]] = fir.extract_value %[[VAL_7]], [1 : index] : (complex<f128>) -> f128
+! FULL: %[[VAL_11:.*]] = fir.extract_value %[[VAL_8]], [0 : index] : (complex<f128>) -> f128
+! FULL: %[[VAL_12:.*]] = fir.extract_value %[[VAL_8]], [1 : index] : (complex<f128>) -> f128
+! FULL: %[[VAL_RET:.*]] = fir.call @__divtc3(%[[VAL_9]], %[[VAL_10]], %[[VAL_11]], %[[VAL_12]]) fastmath<contract> : (f128, f128, f128, f128) -> complex<f128>
+
+! IMPRVD: %[[VAL_RET:.*]] = complex.div %[[VAL_7]], %[[VAL_8]] fastmath<contract> : complex<f128>
+! BASIC: %[[VAL_RET:.*]] = complex.div %[[VAL_7]], %[[VAL_8]] fastmath<contract> : complex<f128>
+
+! CHECK: hlfir.assign %[[VAL_RET]] to %[[VAL_4]]#0 : complex<f128>, !fir.ref<complex<f128>>
+! CHECK: return
+subroutine div_test_quad(a,b,c)
+  complex(kind=16) :: a, b, c
+  a = b / c
+end subroutine div_test_quad
\ No newline at end of file
diff --git a/flang/test/Lower/HLFIR/complex-div-to-hlfir.f90 b/flang/test/Lower/HLFIR/complex-div-to-hlfir.f90
new file mode 100644
index 0000000000000..b488bfde4ee85
--- /dev/null
+++ b/flang/test/Lower/HLFIR/complex-div-to-hlfir.f90
@@ -0,0 +1,94 @@
+! Test lowering of complex division according to options
+
+! RUN: bbc -complex-range=full -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,FULL
+! RUN: bbc -complex-range=improved -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,IMPRVD
+! RUN: bbc -complex-range=basic -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,BASIC
+! RUN: %flang_fc1 -complex-range=full -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,FULL
+! RUN: %flang_fc1 -complex-range=improved -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,IMPRVD
+! RUN: %flang_fc1 -complex-range=basic -emit-hlfir %s -o - | FileCheck %s --check-prefixes=CHECK,BASIC
+
+
+! CHECK-LABEL: @_QPdiv_test_half
+! CHECK-SAME: %[[REF_0:.*]]: !fir.ref<complex<f16>> {{.*}}, %[[REF_1:.*]]: !fir.ref<complex<f16>> {{.*}}, %[[REF_2:.*]]: !fir.ref<complex<f16>> {{.*}})
+! CHECK: %[[VAL_3:.*]] = fir.dummy_scope : !fir.dscope
+! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[REF_0]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_halfEa"} : (!fir.ref<complex<f16>>, !fir.dscope) -> (!fir.ref<complex<f16>>, !fir.ref<complex<f16>>)
+! CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %[[REF_1]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_halfEb"} : (!fir.ref<complex<f16>>, !fir.dscope) -> (!fir.ref<complex<f16>>, !fir.ref<complex<f16>>)
+! CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %[[REF_2]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_halfEc"} : (!fir.ref<complex<f16>>, !fir.dscope) -> (!fir.ref<complex<f16>>, !fir.ref<complex<f16>>)
+! CHECK: %[[VAL_7:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref<complex<f16>>
+! CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_6]]#0 : !fir.ref<complex<f16>>
+! CHECK: %[[VAL_9:.*]] = complex.div %[[VAL_7]], %[[VAL_8]] fastmath<contract> : complex<f16>
+! CHECK: hlfir.assign %[[VAL_9]] to %[[VAL_4]]#0 : complex<f16>, !fir.ref<complex<f16>>
+! CHECK: return
+subroutine div_test_half(a,b,c)
+  complex(kind=2) :: a, b, c
+  a = b / c
+end subroutine div_test_half
+
+
+! CHECK-LABEL: @_QPdiv_test_bfloat
+! CHECK-SAME: %[[REF_0:.*]]: !fir.ref<complex<bf16>> {{.*}}, %[[REF_1:.*]]: !fir.ref<complex<bf16>> {{.*}}, %[[REF_2:.*]]: !fir.ref<complex<bf16>> {{.*}})
+! CHECK: %[[VAL_3:.*]] = fir.dummy_scope : !fir.dscope
+! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[REF_0]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_bfloatEa"} : (!fir.ref<complex<bf16>>, !fir.dscope) -> (!fir.ref<complex<bf16>>, !fir.ref<complex<bf16>>)
+! CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %[[REF_1]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_bfloatEb"} : (!fir.ref<complex<bf16>>, !fir.dscope) -> (!fir.ref<complex<bf16>>, !fir.ref<complex<bf16>>)
+! CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %[[REF_2]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_bfloatEc"} : (!fir.ref<complex<bf16>>, !fir.dscope) -> (!fir.ref<complex<bf16>>, !fir.ref<complex<bf16>>)
+! CHECK: %[[VAL_7:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref<complex<bf16>>
+! CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_6]]#0 : !fir.ref<complex<bf16>>
+! CHECK: %[[VAL_9:.*]] = complex.div %[[VAL_7]], %[[VAL_8]] fastmath<contract> : complex<bf16>
+! CHECK: hlfir.assign %[[VAL_9]] to %[[VAL_4]]#0 : complex<bf16>, !fir.ref<complex<bf16>>
+! CHECK: return
+subroutine div_test_bfloat(a,b,c)
+  complex(kind=3) :: a, b, c
+  a = b / c
+end subroutine div_test_bfloat
+
+
+! CHECK-LABEL: @_QPdiv_test_single
+! CHECK-SAME: %[[REF_0:.*]]: !fir.ref<complex<f32>> {{.*}}, %[[REF_1:.*]]: !fir.ref<complex<f32>> {{.*}}, %[[REF_2:.*]]: !fir.ref<complex<f32>> {{.*}})
+! CHECK: %[[VAL_3:.*]] = fir.dummy_scope : !fir.dscope
+! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[REF_0]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_singleEa"} : (!fir.ref<complex<f32>>, !fir.dscope) -> (!fir.ref<complex<f32>>, !fir.ref<complex<f32>>)
+! CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %[[REF_1]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_singleEb"} : (!fir.ref<complex<f32>>, !fir.dscope) -> (!fir.ref<complex<f32>>, !fir.ref<complex<f32>>)
+! CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %[[REF_2]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_singleEc"} : (!fir.ref<complex<f32>>, !fir.dscope) -> (!fir.ref<complex<f32>>, !fir.ref<complex<f32>>)
+! CHECK: %[[VAL_7:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref<complex<f32>>
+! CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_6]]#0 : !fir.ref<complex<f32>>
+
+! FULL: %[[VAL_9:.*]] = fir.extract_value %[[VAL_7]], [0 : index] : (complex<f32>) -> f32
+! FULL: %[[VAL_10:.*]] = fir.extract_value %[[VAL_7]], [1 : index] : (complex<f32>) -> f32
+! FULL: %[[VAL_11:.*]] = fir.extract_value %[[VAL_8]], [0 : index] : (complex<f32>) -> f32
+! FULL: %[[VAL_12:.*]] = fir.extract_value %[[VAL_8]], [1 : index] : (complex<f32>) -> f32
+! FULL: %[[VAL_RET:.*]] = fir.call @__divsc3(%[[VAL_9]], %[[VAL_10]], %[[VAL_11]], %[[VAL_12]]) fastmath<contract> : (f32, f32, f32, f32) -> complex<f32>
+
+! IMPRVD: %[[VAL_RET:.*]] = complex.div %[[VAL_7]], %[[VAL_8]] fastmath<contract> : complex<f32>
+! BASIC: %[[VAL_RET:.*]] = complex.div %[[VAL_7]], %[[VAL_8]] fastmath<contract> : complex<f32>
+
+! CHECK: hlfir.assign %[[VAL_RET]] to %[[VAL_4]]#0 : complex<f32>, !fir.ref<complex<f32>>
+! CHECK: return
+subroutine div_test_single(a,b,c)
+  complex(kind=4) :: a, b, c
+  a = b / c
+end subroutine div_test_single
+
+
+! CHECK-LABEL: @_QPdiv_test_double
+! CHECK-SAME: %[[REF_0:.*]]: !fir.ref<complex<f64>> {{.*}}, %[[REF_1:.*]]: !fir.ref<complex<f64>> {{.*}}, %[[REF_2:.*]]: !fir.ref<complex<f64>> {{.*}})
+! CHECK: %[[VAL_3:.*]] = fir.dummy_scope : !fir.dscope
+! CHECK: %[[VAL_4:.*]]:2 = hlfir.declare %[[REF_0]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_doubleEa"} : (!fir.ref<complex<f64>>, !fir.dscope) -> (!fir.ref<complex<f64>>, !fir.ref<complex<f64>>)
+! CHECK: %[[VAL_5:.*]]:2 = hlfir.declare %[[REF_1]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_doubleEb"} : (!fir.ref<complex<f64>>, !fir.dscope) -> (!fir.ref<complex<f64>>, !fir.ref<complex<f64>>)
+! CHECK: %[[VAL_6:.*]]:2 = hlfir.declare %[[REF_2]] dummy_scope %[[VAL_3]] {uniq_name = "_QFdiv_test_doubleEc"} : (!fir.ref<complex<f64>>, !fir.dscope) -> (!fir.ref<complex<f64>>, !fir.ref<complex<f64>>)
+! CHECK: %[[VAL_7:.*]] = fir.load %[[VAL_5]]#0 : !fir.ref<complex<f64>>
+! CHECK: %[[VAL_8:.*]] = fir.load %[[VAL_6]]#0 : !fir.ref<complex<f64>>
+
+! FULL: %[[VAL_9:.*]] = fir.extract_value %[[VAL_7]], [0 : index] : (complex<f64>) -> f64
+! FULL: %[[VAL_10:.*]] = fir.extract_value %[[VAL_7]], [1 : index] : (complex<f64>) -> f64
+! FULL: %[[VAL_11:.*]] = fir.extract_value %[[VAL_8]], [0 : index] : (complex<f64>) -> f64
+! FULL: %[[VAL_12:.*]] = fir.extract_value %[[VAL_8]], [1 : index] : (complex<f64>) -> f64
+! FULL: %[[VAL_RET:.*]] = fir.call @__divdc3(%[[VAL_9]], %[[VAL_10]], %[[VAL_11]], %[[VAL_12]]) fastmath<contract> : (f64, f64, f64, f64) -> complex<f64>
+
+! IMPRVD: %[[VAL_RET:.*]] = complex.div %[[VAL_7]], %[[VAL_8]] fastmath<contract> : complex<f64>
+! BASIC: %[[VAL_RET:.*]] = complex.div %[[VAL_7]], %[[VAL_8]] fastmath<contract> : complex<f64>
+
+! CHECK: hlfir.assign %[[VAL_RET]] to %[[VAL_4]]#0 : complex<f64>, !fir.ref<complex<f64>>
+! CHECK: return
+subroutine div_test_double(a,b,c)
+  complex(kind=8) :: a, b, c
+  a = b / c
+end subroutine div_test_double
diff --git a/flang/tools/bbc/bbc.cpp b/flang/tools/bbc/bbc.cpp
index 59372a8eb58ed..2ccbe8b96d805 100644
--- a/flang/tools/bbc/bbc.cpp
+++ b/flang/tools/bbc/bbc.cpp
@@ -276,6 +276,12 @@ static llvm::cl::opt<bool>
                                      "leading dimension will be repacked"),
                       llvm::cl::init(true));
 
+static llvm::cl::opt<std::string> complexRange(
+    "complex-range",
+    llvm::cl::desc("Controls the various implementations for complex "
+                   "multiplication and division [full|improved|basic]"),
+    llvm::cl::init(""));
+
 #define FLANG_EXCLUDE_CODEGEN
 #include "flang/Optimizer/Passes/CommandLineOpts.h"
 #include "flang/Optimizer/Passes/Pipelines.h"
@@ -437,6 +443,8 @@ static llvm::LogicalResult convertFortranSourceToMLIR(
   loweringOptions.setSkipExternalRttiDefinition(skipExternalRttiDefinition);
   if (enableCUDA)
     loweringOptions.setCUDARuntimeCheck(true);
+  if (complexRange == "improved" || complexRange == "basic")
+    loweringOptions.setComplexDivisionToRuntime(false);
   std::vector<Fortran::lower::EnvironmentDefault> envDefaults = {};
   Fortran::frontend::TargetOptions targetOpts;
   Fortran::frontend::CodeGenOptions cgOpts;

>From 60e0cd9ece16f0f29ce2525d72e2ba37c0ae8447 Mon Sep 17 00:00:00 2001
From: s-watanabe314 <watanabe.shu-06 at fujitsu.com>
Date: Fri, 4 Jul 2025 10:19:12 +0900
Subject: [PATCH 2/6] Add help text

---
 clang/include/clang/Driver/Options.td | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index 58209ceb5dc54..a1350e69a202e 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -1025,7 +1025,8 @@ defm offload_uniform_block : BoolFOption<"offload-uniform-block",
 def fcomplex_arithmetic_EQ : Joined<["-"], "fcomplex-arithmetic=">, Group<f_Group>,
   Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>,
   Values<"full,improved,promoted,basic">, NormalizedValuesScope<"LangOptions">,
-  NormalizedValues<["CX_Full", "CX_Improved", "CX_Promoted", "CX_Basic"]>;
+  NormalizedValues<["CX_Full", "CX_Improved", "CX_Promoted", "CX_Basic"]>,
+  HelpText<"Controls the calculation methods of complex number multiplication and division.">;
 
 def complex_range_EQ : Joined<["-"], "complex-range=">, Group<f_Group>,
   Visibility<[CC1Option, FC1Option]>,

>From bddb2d33369ca4bdec50c3e927004555a7940d9a Mon Sep 17 00:00:00 2001
From: s-watanabe314 <watanabe.shu-06 at fujitsu.com>
Date: Fri, 4 Jul 2025 12:01:16 +0900
Subject: [PATCH 3/6] Move common functions to CommonArgs.cpp

---
 clang/include/clang/Driver/CommonArgs.h    |  6 +++++
 clang/lib/Driver/ToolChains/Clang.cpp      | 27 ----------------------
 clang/lib/Driver/ToolChains/CommonArgs.cpp | 27 ++++++++++++++++++++++
 clang/lib/Driver/ToolChains/Flang.cpp      | 24 -------------------
 4 files changed, 33 insertions(+), 51 deletions(-)

diff --git a/clang/include/clang/Driver/CommonArgs.h b/clang/include/clang/Driver/CommonArgs.h
index 26aa3ccf84786..cd1f3b2eb36ea 100644
--- a/clang/include/clang/Driver/CommonArgs.h
+++ b/clang/include/clang/Driver/CommonArgs.h
@@ -283,6 +283,12 @@ StringRef parseMPreferVectorWidthOption(clang::DiagnosticsEngine &Diags,
 StringRef parseMRecipOption(clang::DiagnosticsEngine &Diags,
                             const llvm::opt::ArgList &Args);
 
+// Convert ComplexRangeKind to a string that can be passed as a frontend option.
+std::string ComplexRangeKindToStr(LangOptions::ComplexRangeKind Range);
+
+// Render a frontend option corresponding to ComplexRangeKind.
+std::string RenderComplexRangeOption(LangOptions::ComplexRangeKind Range);
+
 } // end namespace tools
 } // end namespace driver
 } // end namespace clang
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index fea4ee909ff46..5d42ce56926d8 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -2740,25 +2740,6 @@ static void CollectArgsForIntegratedAssembler(Compilation &C,
   }
 }
 
-static std::string ComplexRangeKindToStr(LangOptions::ComplexRangeKind Range) {
-  switch (Range) {
-  case LangOptions::ComplexRangeKind::CX_Full:
-    return "full";
-    break;
-  case LangOptions::ComplexRangeKind::CX_Basic:
-    return "basic";
-    break;
-  case LangOptions::ComplexRangeKind::CX_Improved:
-    return "improved";
-    break;
-  case LangOptions::ComplexRangeKind::CX_Promoted:
-    return "promoted";
-    break;
-  default:
-    return "";
-  }
-}
-
 static std::string ComplexArithmeticStr(LangOptions::ComplexRangeKind Range) {
   return (Range == LangOptions::ComplexRangeKind::CX_None)
              ? ""
@@ -2772,14 +2753,6 @@ static void EmitComplexRangeDiag(const Driver &D, std::string str1,
   }
 }
 
-static std::string
-RenderComplexRangeOption(LangOptions::ComplexRangeKind Range) {
-  std::string ComplexRangeStr = ComplexRangeKindToStr(Range);
-  if (!ComplexRangeStr.empty())
-    return "-complex-range=" + ComplexRangeStr;
-  return ComplexRangeStr;
-}
-
 static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
                                        bool OFastEnabled, const ArgList &Args,
                                        ArgStringList &CmdArgs,
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index 070901f037823..a6735e5e412d4 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -3409,3 +3409,30 @@ StringRef tools::parseMRecipOption(clang::DiagnosticsEngine &Diags,
 
   return Out;
 }
+
+std::string tools::ComplexRangeKindToStr(LangOptions::ComplexRangeKind Range) {
+  switch (Range) {
+  case LangOptions::ComplexRangeKind::CX_Full:
+    return "full";
+    break;
+  case LangOptions::ComplexRangeKind::CX_Basic:
+    return "basic";
+    break;
+  case LangOptions::ComplexRangeKind::CX_Improved:
+    return "improved";
+    break;
+  case LangOptions::ComplexRangeKind::CX_Promoted:
+    return "promoted";
+    break;
+  default:
+    return "";
+  }
+}
+
+std::string
+tools::RenderComplexRangeOption(LangOptionsBase::ComplexRangeKind Range) {
+  std::string ComplexRangeStr = ComplexRangeKindToStr(Range);
+  if (!ComplexRangeStr.empty())
+    return "-complex-range=" + ComplexRangeStr;
+  return ComplexRangeStr;
+}
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index ba5db7e3aba43..b5767918b86d1 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -595,30 +595,6 @@ void Flang::addOffloadOptions(Compilation &C, const InputInfoList &Inputs,
   addOpenMPHostOffloadingArgs(C, JA, Args, CmdArgs);
 }
 
-static std::string ComplexRangeKindToStr(LangOptions::ComplexRangeKind Range) {
-  switch (Range) {
-  case LangOptions::ComplexRangeKind::CX_Full:
-    return "full";
-    break;
-  case LangOptions::ComplexRangeKind::CX_Improved:
-    return "improved";
-    break;
-  case LangOptions::ComplexRangeKind::CX_Basic:
-    return "basic";
-    break;
-  default:
-    return "";
-  }
-}
-
-static std::string
-RenderComplexRangeOption(LangOptions::ComplexRangeKind Range) {
-  std::string ComplexRangeStr = ComplexRangeKindToStr(Range);
-  if (!ComplexRangeStr.empty())
-    return "-complex-range=" + ComplexRangeStr;
-  return ComplexRangeStr;
-}
-
 static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
                                     ArgStringList &CmdArgs) {
   StringRef FPContract;

>From 65c522ccd201fd48c481990ac0b1ec9b8be6d163 Mon Sep 17 00:00:00 2001
From: s-watanabe314 <watanabe.shu-06 at fujitsu.com>
Date: Fri, 4 Jul 2025 12:01:54 +0900
Subject: [PATCH 4/6] Minor fixes

---
 flang/lib/Frontend/CompilerInvocation.cpp              | 2 +-
 flang/lib/Lower/ConvertExprToHLFIR.cpp                 | 5 +++--
 flang/test/Lower/HLFIR/complex-div-to-hlfir-kind16.f90 | 2 +-
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index 86ec410b1f70f..910e0f5dcebd5 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -485,7 +485,7 @@ static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
 
   parseDoConcurrentMapping(opts, args, diags);
 
-  if (const auto *arg =
+  if (const llvm::opt::Arg *arg =
           args.getLastArg(clang::driver::options::OPT_complex_range_EQ)) {
     llvm::StringRef argValue = llvm::StringRef(arg->getValue());
     if (argValue == "full") {
diff --git a/flang/lib/Lower/ConvertExprToHLFIR.cpp b/flang/lib/Lower/ConvertExprToHLFIR.cpp
index cb338618dbf3b..9689f920840fb 100644
--- a/flang/lib/Lower/ConvertExprToHLFIR.cpp
+++ b/flang/lib/Lower/ConvertExprToHLFIR.cpp
@@ -1070,12 +1070,13 @@ struct BinaryOp<Fortran::evaluate::Divide<
     // TODO: Ideally, complex number division operations should always be
     // lowered to MLIR. However, converting them to the runtime via MLIR causes
     // ABI issues.
-    if (builder.getComplexDivisionToRuntimeFlag())
+    if (builder.getComplexDivisionToRuntimeFlag()) {
       return hlfir::EntityWithAttributes{
           fir::genDivC(builder, loc, ty, lhs, rhs)};
-    else
+    } else {
       return hlfir::EntityWithAttributes{
           builder.create<mlir::complex::DivOp>(loc, lhs, rhs)};
+    }
   }
 };
 
diff --git a/flang/test/Lower/HLFIR/complex-div-to-hlfir-kind16.f90 b/flang/test/Lower/HLFIR/complex-div-to-hlfir-kind16.f90
index 3e158b0bed998..fe4a7256a4a16 100644
--- a/flang/test/Lower/HLFIR/complex-div-to-hlfir-kind16.f90
+++ b/flang/test/Lower/HLFIR/complex-div-to-hlfir-kind16.f90
@@ -32,4 +32,4 @@
 subroutine div_test_quad(a,b,c)
   complex(kind=16) :: a, b, c
   a = b / c
-end subroutine div_test_quad
\ No newline at end of file
+end subroutine div_test_quad

>From 528e1defac6714baad3f80a6e031fe02fb18011c Mon Sep 17 00:00:00 2001
From: Shunsuke Watanabe <watanabe.shu-06 at fujitsu.com>
Date: Fri, 4 Jul 2025 17:41:45 +0900
Subject: [PATCH 5/6] Update flang/include/flang/Optimizer/Builder/FIRBuilder.h

Co-authored-by: Tarun Prabhu <tarunprabhu at gmail.com>
---
 flang/include/flang/Optimizer/Builder/FIRBuilder.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/flang/include/flang/Optimizer/Builder/FIRBuilder.h b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
index b1513850a9048..004ad00b6f470 100644
--- a/flang/include/flang/Optimizer/Builder/FIRBuilder.h
+++ b/flang/include/flang/Optimizer/Builder/FIRBuilder.h
@@ -609,8 +609,8 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
     return integerOverflowFlags;
   }
 
-  /// Set ComplexDivisionToRuntimeFlag value for whether complex number division
-  /// is lowered to a runtime function by this builder.
+  /// Set ComplexDivisionToRuntimeFlag value. If set to true, complex number
+  /// division is lowered to a runtime function by this builder.
   void setComplexDivisionToRuntimeFlag(bool flag) {
     complexDivisionToRuntimeFlag = flag;
   }

>From eea4a5f402cddae02c31a1217af6930fa4981874 Mon Sep 17 00:00:00 2001
From: s-watanabe314 <watanabe.shu-06 at fujitsu.com>
Date: Mon, 7 Jul 2025 18:27:04 +0900
Subject: [PATCH 6/6] Add lowering information to ComplexOperations.md

---
 flang/docs/ComplexOperations.md | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/flang/docs/ComplexOperations.md b/flang/docs/ComplexOperations.md
index 03566035c28a3..b7d2ecaa85e1c 100644
--- a/flang/docs/ComplexOperations.md
+++ b/flang/docs/ComplexOperations.md
@@ -74,4 +74,26 @@ The ComplexToStandard dialect does still call into libm for some floating
 point math operations, however these don't have the same ABI issues as the
 complex libm functions.
 
+The flang driver option `-fcomplex-arithmetic=` allows you to select whether
+complex number division is lowered to function calls or to the `complex.div`
+operation in the MLIR complex dialect. To avoid the ABI issues mentioned above,
+the selection between function calls and the `complex.div` is made during the
+lowering phase. The behavior of this option is as follows:
+
+- `basic`: Lowers to `complex.div` and is converted to algebraic formulas. No
+special handling to avoid overflow. NaN and infinite values are not handled.
+- `improved`: Lowers to `complex.div` and is converted to Smith's algorithm. See
+SMITH, R. L. Algorithm 116: Complex division. Commun. ACM 5, 8 (1962). This
+value offers improved handling for overflow in intermediate calculations, but
+overflow may occur. NaN and infinite values are handled.
+- `full`: Lowers to a call to runtime library functions. Overflow and non-finite
+values are handled by the library implementation. This is the default value.
+
+While [the same option in clang][2] allows specifying `promoted`, this is not
+implemented in Flang. Also, in the case of `improved`, clang does not handle NaN
+and infinite values, but Flang does. These behavioral differences arise because
+the transformation of complex division calculations depends on the implementation
+of ComplexToStandard, which may change in the future.
+
 [1]: https://discourse.llvm.org/t/rfc-change-lowering-of-fortran-math-intrinsics/63971
+[2]: https://clang.llvm.org/docs/UsersManual.html#cmdoption-fcomplex-arithmetic



More information about the flang-commits mailing list