https://github.com/adams381 created 
https://github.com/llvm/llvm-project/pull/216498

Complex division returns a wrong imaginary part.  CIR declares `__divsc3` as 
returning `{ float, float }` where classic CodeGen coerces the return to `<2 x 
float>`, so the caller reads the two halves out of two registers while the 
callee packs both into one.  Dividing 3+4i by 1+2i gives 2.2 and 4.0 instead of 
2.2 and -0.4, with no diagnostic.  The helper call is synthesized by 
LoweringPrepare, which runs after the calling-convention pass, so it is never 
classified.

Expanding `cir.complex.mul` and `cir.complex.div` in their own pass, scheduled 
before CallConvLowering, is enough to fix it and leaves the rest of the 
lowering-prepare work where it is.  Only the float helpers change shape, since 
double and long double already returned a record that matched classic.

Assisted-by: Cursor / claude-opus-5


>From a725f0b935aef8482472662bf714e42be71379c9 Mon Sep 17 00:00:00 2001
From: Adam Smith <[email protected]>
Date: Sat, 15 Aug 2026 09:57:19 -0700
Subject: [PATCH] [CIR] Lower complex mul and div before callconv lowering

Complex division returns a wrong imaginary part.  CIR declares `__divsc3` as
returning `{ float, float }` where classic CodeGen coerces the return to
`<2 x float>`, so the caller reads the two halves out of two registers while the
callee packs both into one.  Dividing 3+4i by 1+2i gives 2.2 and 4.0 instead of
2.2 and -0.4, with no diagnostic.  The helper call is synthesized by
LoweringPrepare, which runs after the calling-convention pass, so it is never
classified.

Expanding `cir.complex.mul` and `cir.complex.div` in their own pass, scheduled
before CallConvLowering, is enough to fix it and leaves the rest of the
lowering-prepare work where it is.  Only the float helpers change shape, since
double and long double already returned a record that matched classic.

Assisted-by: Cursor / claude-opus-5
---
 clang/include/clang/CIR/Dialect/Passes.h      |   2 +
 clang/include/clang/CIR/Dialect/Passes.td     |  19 ++++
 .../Dialect/Transforms/LoweringPrepare.cpp    | 106 +++++++++++++-----
 clang/lib/CIR/Lowering/CIRPasses.cpp          |   6 +
 .../CodeGen/complex-compound-assignment.cpp   |  36 ++++--
 .../complex-libcall-abi-global-init.cpp       |  30 +++++
 clang/test/CIR/CodeGen/complex-libcall-abi.c  |  76 +++++++++++++
 clang/test/CIR/CodeGen/complex-mul-div.cpp    |  29 +++--
 8 files changed, 262 insertions(+), 42 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/complex-libcall-abi-global-init.cpp
 create mode 100644 clang/test/CIR/CodeGen/complex-libcall-abi.c

diff --git a/clang/include/clang/CIR/Dialect/Passes.h 
b/clang/include/clang/CIR/Dialect/Passes.h
index 674f2180c27ab..c668607dced34 100644
--- a/clang/include/clang/CIR/Dialect/Passes.h
+++ b/clang/include/clang/CIR/Dialect/Passes.h
@@ -42,6 +42,8 @@ std::unique_ptr<Pass> createCallConvLoweringPass(
 std::unique_ptr<Pass> createHoistAllocasPass();
 std::unique_ptr<Pass> createLoweringPreparePass();
 std::unique_ptr<Pass> createLoweringPreparePass(clang::ASTContext *astCtx);
+std::unique_ptr<Pass> createComplexLoweringPass();
+std::unique_ptr<Pass> createComplexLoweringPass(clang::ASTContext *astCtx);
 std::unique_ptr<Pass> createGotoSolverPass();
 std::unique_ptr<Pass> createIdiomRecognizerPass();
 std::unique_ptr<Pass> createLibOptPass();
diff --git a/clang/include/clang/CIR/Dialect/Passes.td 
b/clang/include/clang/CIR/Dialect/Passes.td
index b983cfe59112a..c0a79d6fde66c 100644
--- a/clang/include/clang/CIR/Dialect/Passes.td
+++ b/clang/include/clang/CIR/Dialect/Passes.td
@@ -184,6 +184,25 @@ def LoweringPrepare : Pass<"cir-lowering-prepare"> {
   let dependentDialects = ["cir::CIRDialect"];
 }
 
+def ComplexLowering : Pass<"cir-complex-lowering", "mlir::ModuleOp"> {
+  let summary = "Expand complex multiplication and division";
+  let description = [{
+    This pass replaces `cir.complex.mul` and `cir.complex.div` with the
+    arithmetic each one expands to, which for the full complex range is a call
+    to a runtime helper such as `__mulsc3` or `__divsc3`.
+
+    It runs before the calling-convention pass rather than alongside the rest
+    of the lowering-prepare work, because a call created after that pass has
+    run never gets its return type coerced, which places the real and
+    imaginary halves of the result in the wrong registers.
+
+    Only these two operations need the earlier position, so `cir.complex.conj`
+    and the complex casts are still expanded by `cir-lowering-prepare`.
+  }];
+  let constructor = "mlir::createComplexLoweringPass()";
+  let dependentDialects = ["cir::CIRDialect"];
+}
+
 def IdiomRecognizer : Pass<"cir-idiom-recognizer", "mlir::ModuleOp"> {
   let summary = "Raise calls to C/C++ libraries to CIR operations";
   let description = [{
diff --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 28ba14962cc7b..a71aba6c1460f 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -44,6 +44,7 @@ using namespace mlir;
 using namespace cir;
 
 namespace mlir {
+#define GEN_PASS_DEF_COMPLEXLOWERING
 #define GEN_PASS_DEF_LOWERINGPREPARE
 #include "clang/CIR/Dialect/Passes.h.inc"
 } // namespace mlir
@@ -88,8 +89,6 @@ struct LoweringPreparePass
   void runOnOp(mlir::Operation *op);
   void lowerCastOp(cir::CastOp op);
   void lowerComplexConjOp(cir::ComplexConjOp op);
-  void lowerComplexDivOp(cir::ComplexDivOp op);
-  void lowerComplexMulOp(cir::ComplexMulOp op);
   void lowerGetGlobalOp(cir::GetGlobalOp op);
   void lowerGlobalOp(cir::GlobalOp op);
   void lowerThreeWayCmpOp(cir::CmpThreeWayOp op);
@@ -505,6 +504,26 @@ struct LoweringPreparePass
   void setASTContext(clang::ASTContext *c) { astCtx = c; }
 };
 
+/// Expand `cir.complex.mul` and `cir.complex.div`.  See the pass description
+/// in Passes.td for why this cannot run with the rest of LoweringPrepare.
+struct ComplexLoweringPass
+    : public impl::ComplexLoweringBase<ComplexLoweringPass> {
+  ComplexLoweringPass() = default;
+
+  void runOnOperation() override;
+
+  void lowerComplexDivOp(cir::ComplexDivOp op);
+  void lowerComplexMulOp(cir::ComplexMulOp op);
+
+  void setASTContext(clang::ASTContext *c) { astCtx = c; }
+
+  /// Read by the promoted-range division path, which asks the target for the
+  /// semantics of a higher-precision element type.
+  clang::ASTContext *astCtx = nullptr;
+
+  mlir::ModuleOp mlirModule;
+};
+
 } // namespace
 
 cir::GlobalOp LoweringPreparePass::getOrCreateRuntimeVariable(
@@ -525,9 +544,13 @@ cir::GlobalOp 
LoweringPreparePass::getOrCreateRuntimeVariable(
   return g;
 }
 
-cir::FuncOp LoweringPreparePass::buildRuntimeFunction(
-    mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
-    cir::FuncType type, cir::GlobalLinkageKind linkage) {
+/// Declare `name` in `mlirModule` if it is not already declared there, and
+/// return the declaration.  Free-standing so that ComplexLoweringPass can
+/// reach it without a LoweringPreparePass instance.
+static cir::FuncOp buildRuntimeFunction(
+    mlir::OpBuilder &builder, mlir::ModuleOp mlirModule, llvm::StringRef name,
+    mlir::Location loc, cir::FuncType type,
+    cir::GlobalLinkageKind linkage = cir::GlobalLinkageKind::ExternalLinkage) {
   cir::FuncOp f = 
dyn_cast_or_null<FuncOp>(SymbolTable::lookupNearestSymbolFrom(
       mlirModule, StringAttr::get(mlirModule->getContext(), name)));
   if (!f) {
@@ -542,6 +565,12 @@ cir::FuncOp LoweringPreparePass::buildRuntimeFunction(
   return f;
 }
 
+cir::FuncOp LoweringPreparePass::buildRuntimeFunction(
+    mlir::OpBuilder &builder, llvm::StringRef name, mlir::Location loc,
+    cir::FuncType type, cir::GlobalLinkageKind linkage) {
+  return ::buildRuntimeFunction(builder, mlirModule, name, loc, type, linkage);
+}
+
 static mlir::Value lowerScalarToComplexCast(mlir::MLIRContext &ctx,
                                             cir::CastOp op) {
   cir::CIRBaseBuilderTy builder(ctx);
@@ -628,7 +657,7 @@ void LoweringPreparePass::lowerCastOp(cir::CastOp op) {
 }
 
 static mlir::Value buildComplexBinOpLibCall(
-    LoweringPreparePass &pass, CIRBaseBuilderTy &builder,
+    mlir::ModuleOp mlirModule, CIRBaseBuilderTy &builder,
     llvm::StringRef (*libFuncNameGetter)(llvm::APFloat::Semantics),
     mlir::Location loc, cir::ComplexType ty, mlir::Value lhsReal,
     mlir::Value lhsImag, mlir::Value rhsReal, mlir::Value rhsImag) {
@@ -646,8 +675,9 @@ static mlir::Value buildComplexBinOpLibCall(
   cir::FuncOp libFunc;
   {
     mlir::OpBuilder::InsertionGuard ipGuard{builder};
-    builder.setInsertionPointToStart(pass.mlirModule.getBody());
-    libFunc = pass.buildRuntimeFunction(builder, libFuncName, loc, libFuncTy);
+    builder.setInsertionPointToStart(mlirModule.getBody());
+    libFunc =
+        buildRuntimeFunction(builder, mlirModule, libFuncName, loc, libFuncTy);
   }
 
   cir::CallOp call =
@@ -864,7 +894,7 @@ static mlir::Type 
higherPrecisionElementTypeForComplexArithmetic(
 }
 
 static mlir::Value
-lowerComplexDiv(LoweringPreparePass &pass, CIRBaseBuilderTy &builder,
+lowerComplexDiv(mlir::ModuleOp mlirModule, CIRBaseBuilderTy &builder,
                 mlir::Location loc, cir::ComplexDivOp op, mlir::Value lhsReal,
                 mlir::Value lhsImag, mlir::Value rhsReal, mlir::Value rhsImag,
                 mlir::MLIRContext &mlirCx, clang::ASTContext &cc) {
@@ -876,9 +906,9 @@ lowerComplexDiv(LoweringPreparePass &pass, CIRBaseBuilderTy 
&builder,
                                            rhsReal, rhsImag);
 
     if (range == cir::ComplexRangeKind::Full)
-      return buildComplexBinOpLibCall(pass, builder, &getComplexDivLibCallName,
-                                      loc, complexTy, lhsReal, lhsImag, 
rhsReal,
-                                      rhsImag);
+      return buildComplexBinOpLibCall(mlirModule, builder,
+                                      &getComplexDivLibCallName, loc, 
complexTy,
+                                      lhsReal, lhsImag, rhsReal, rhsImag);
 
     if (range == cir::ComplexRangeKind::Promoted) {
       mlir::Type originalElementType = complexTy.getElementType();
@@ -918,7 +948,7 @@ lowerComplexDiv(LoweringPreparePass &pass, CIRBaseBuilderTy 
&builder,
                                   rhsImag);
 }
 
-void LoweringPreparePass::lowerComplexDivOp(cir::ComplexDivOp op) {
+void ComplexLoweringPass::lowerComplexDivOp(cir::ComplexDivOp op) {
   cir::CIRBaseBuilderTy builder(getContext());
   builder.setInsertionPointAfter(op);
   mlir::Location loc = op.getLoc();
@@ -930,7 +960,7 @@ void 
LoweringPreparePass::lowerComplexDivOp(cir::ComplexDivOp op) {
   mlir::Value rhsImag = builder.createComplexImag(loc, rhs);
 
   mlir::Value loweredResult =
-      lowerComplexDiv(*this, builder, loc, op, lhsReal, lhsImag, rhsReal,
+      lowerComplexDiv(mlirModule, builder, loc, op, lhsReal, lhsImag, rhsReal,
                       rhsImag, getContext(), *astCtx);
   op.replaceAllUsesWith(loweredResult);
   op.erase();
@@ -956,7 +986,7 @@ getComplexMulLibCallName(llvm::APFloat::Semantics 
semantics) {
   }
 }
 
-static mlir::Value lowerComplexMul(LoweringPreparePass &pass,
+static mlir::Value lowerComplexMul(mlir::ModuleOp mlirModule,
                                    CIRBaseBuilderTy &builder,
                                    mlir::Location loc, cir::ComplexMulOp op,
                                    mlir::Value lhsReal, mlir::Value lhsImag,
@@ -1004,8 +1034,8 @@ static mlir::Value lowerComplexMul(LoweringPreparePass 
&pass,
              builder, loc, resultRealAndImagAreNaN,
              [&](mlir::OpBuilder &, mlir::Location) {
                mlir::Value libCallResult = buildComplexBinOpLibCall(
-                   pass, builder, &getComplexMulLibCallName, loc, complexTy,
-                   lhsReal, lhsImag, rhsReal, rhsImag);
+                   mlirModule, builder, &getComplexMulLibCallName, loc,
+                   complexTy, lhsReal, lhsImag, rhsReal, rhsImag);
                builder.createYield(loc, libCallResult);
              },
              [&](mlir::OpBuilder &, mlir::Location) {
@@ -1014,7 +1044,7 @@ static mlir::Value lowerComplexMul(LoweringPreparePass 
&pass,
       .getResult();
 }
 
-void LoweringPreparePass::lowerComplexMulOp(cir::ComplexMulOp op) {
+void ComplexLoweringPass::lowerComplexMulOp(cir::ComplexMulOp op) {
   cir::CIRBaseBuilderTy builder(getContext());
   builder.setInsertionPointAfter(op);
   mlir::Location loc = op.getLoc();
@@ -1024,12 +1054,28 @@ void 
LoweringPreparePass::lowerComplexMulOp(cir::ComplexMulOp op) {
   mlir::Value lhsImag = builder.createComplexImag(loc, lhs);
   mlir::Value rhsReal = builder.createComplexReal(loc, rhs);
   mlir::Value rhsImag = builder.createComplexImag(loc, rhs);
-  mlir::Value loweredResult = lowerComplexMul(*this, builder, loc, op, lhsReal,
-                                              lhsImag, rhsReal, rhsImag);
+  mlir::Value loweredResult = lowerComplexMul(
+      mlirModule, builder, loc, op, lhsReal, lhsImag, rhsReal, rhsImag);
   op.replaceAllUsesWith(loweredResult);
   op.erase();
 }
 
+void ComplexLoweringPass::runOnOperation() {
+  mlirModule = cast<mlir::ModuleOp>(getOperation());
+
+  llvm::SmallVector<mlir::Operation *> opsToTransform;
+  mlirModule->walk([&](mlir::Operation *op) {
+    if (mlir::isa<cir::ComplexMulOp, cir::ComplexDivOp>(op))
+      opsToTransform.push_back(op);
+  });
+
+  for (mlir::Operation *o : opsToTransform)
+    if (auto complexDiv = mlir::dyn_cast<cir::ComplexDivOp>(o))
+      lowerComplexDivOp(complexDiv);
+    else
+      lowerComplexMulOp(mlir::cast<cir::ComplexMulOp>(o));
+}
+
 void LoweringPreparePass::lowerComplexConjOp(cir::ComplexConjOp op) {
   mlir::Location loc = op.getLoc();
   CIRBaseBuilderTy builder(getContext());
@@ -2297,10 +2343,6 @@ void LoweringPreparePass::runOnOp(mlir::Operation *op) {
     lowerCastOp(cast);
   } else if (auto complexConj = mlir::dyn_cast<cir::ComplexConjOp>(op)) {
     lowerComplexConjOp(complexConj);
-  } else if (auto complexDiv = mlir::dyn_cast<cir::ComplexDivOp>(op)) {
-    lowerComplexDivOp(complexDiv);
-  } else if (auto complexMul = mlir::dyn_cast<cir::ComplexMulOp>(op)) {
-    lowerComplexMulOp(complexMul);
   } else if (auto glob = mlir::dyn_cast<cir::GlobalOp>(op)) {
     lowerGlobalOp(glob);
     if (auto regAttr = glob->getAttrOfType<CUDAVarRegistrationInfoAttr>(
@@ -2936,9 +2978,8 @@ void LoweringPreparePass::runOnOperation() {
 
   op->walk([&](mlir::Operation *op) {
     if (mlir::isa<cir::ArrayCtor, cir::ArrayDtor, cir::CastOp,
-                  cir::ComplexConjOp, cir::ComplexMulOp, cir::ComplexDivOp,
-                  cir::DynamicCastOp, cir::FuncOp, cir::CallOp,
-                  cir::GetGlobalOp, cir::GlobalOp, cir::StoreOp,
+                  cir::ComplexConjOp, cir::DynamicCastOp, cir::FuncOp,
+                  cir::CallOp, cir::GetGlobalOp, cir::GlobalOp, cir::StoreOp,
                   cir::CmpThreeWayOp, cir::LocalInitOp, cir::StdOpInterface>(
             op))
       opsToTransform.push_back(op);
@@ -2965,3 +3006,14 @@ mlir::createLoweringPreparePass(clang::ASTContext 
*astCtx) {
   pass->setASTContext(astCtx);
   return std::move(pass);
 }
+
+std::unique_ptr<Pass> mlir::createComplexLoweringPass() {
+  return std::make_unique<ComplexLoweringPass>();
+}
+
+std::unique_ptr<Pass>
+mlir::createComplexLoweringPass(clang::ASTContext *astCtx) {
+  auto pass = std::make_unique<ComplexLoweringPass>();
+  pass->setASTContext(astCtx);
+  return std::move(pass);
+}
diff --git a/clang/lib/CIR/Lowering/CIRPasses.cpp 
b/clang/lib/CIR/Lowering/CIRPasses.cpp
index 1d1fdaf42aaa4..1d1501e3719ee 100644
--- a/clang/lib/CIR/Lowering/CIRPasses.cpp
+++ b/clang/lib/CIR/Lowering/CIRPasses.cpp
@@ -106,6 +106,12 @@ runCIRToCIRPasses(mlir::ModuleOp theModule, 
mlir::MLIRContext &mlirContext,
   pm.addPass(mlir::createTargetLoweringPass());
   pm.addPass(mlir::createCXXABILoweringPass());
 
+  // Complex multiplication and division synthesize calls to runtime helpers
+  // such as __mulsc3 and __divsc3, so they must be expanded before
+  // CallConvLowering classifies calls.  The rest of the lowering-prepare work
+  // stays after it.
+  pm.addPass(mlir::createComplexLoweringPass(&astContext));
+
   if (enableCallConvLowering) {
     // CallConvLowering rewrites signatures and call sites using the 
classifier,
     // so it must run after CXXABILowering has lowered C++ ABI types to plain
diff --git a/clang/test/CIR/CodeGen/complex-compound-assignment.cpp 
b/clang/test/CIR/CodeGen/complex-compound-assignment.cpp
index 8e58c51c570a9..e56717f064fa6 100644
--- a/clang/test/CIR/CodeGen/complex-compound-assignment.cpp
+++ b/clang/test/CIR/CodeGen/complex-compound-assignment.cpp
@@ -415,7 +415,10 @@ void foo7() {
 // CIR: %[[CONST_FALSE:.*]] = cir.const #false
 // CIR: %[[SELECT_CONDITION:.*]] = cir.select if %[[IS_C_REAL_NAN]] then 
%[[IS_C_IMAG_NAN]] else %[[CONST_FALSE]] : (!cir.bool, !cir.bool, !cir.bool) -> 
!cir.bool
 // CIR: %[[RESULT:.*]] = cir.ternary(%[[SELECT_CONDITION]], true {
-// CIR:   %[[LIBC_COMPLEX:.*]] = cir.call @__mulsc3(%[[B_REAL]], %[[B_IMAG]], 
%[[A_REAL]], %[[A_IMAG]]) : (!cir.float, !cir.float, !cir.float, !cir.float) -> 
!cir.complex<!cir.float>
+// CIR:   %[[LIBC_COERCED:.*]] = cir.call @__mulsc3(%[[B_REAL]], %[[B_IMAG]], 
%[[A_REAL]], %[[A_IMAG]]) : (!cir.float, !cir.float, !cir.float, !cir.float) -> 
!cir.vector<2 x !cir.float>
+// CIR:   cir.store %[[LIBC_COERCED]], %[[COERCE_SLOT:.*]] : !cir.vector<2 x 
!cir.float>, !cir.ptr<!cir.vector<2 x !cir.float>>
+// CIR:   %[[COERCE_PTR:.*]] = cir.cast bitcast %[[COERCE_SLOT]] : 
!cir.ptr<!cir.vector<2 x !cir.float>> -> !cir.ptr<!cir.complex<!cir.float>>
+// CIR:   %[[LIBC_COMPLEX:.*]] = cir.load %[[COERCE_PTR]] : 
!cir.ptr<!cir.complex<!cir.float>>, !cir.complex<!cir.float>
 // CIR:   cir.yield %[[LIBC_COMPLEX]] : !cir.complex<!cir.float>
 // CIR: }, false {
 // CIR:   cir.yield %[[COMPLEX]] : !cir.complex<!cir.float>
@@ -443,7 +446,9 @@ void foo7() {
 // LLVM: %[[SELECT_CONDITION:.*]] = and i1 %[[IS_C_REAL_NAN]], 
%[[IS_C_IMAG_NAN]]
 // LLVM: br i1 %[[SELECT_CONDITION]], label %[[THEN_LABEL:.*]], label 
%[[ELSE_LABEL:.*]]
 // LLVM: [[THEN_LABEL]]:
-// LLVM:  %[[LIBC_COMPLEX:.*]] = call { float, float } @__mulsc3(float 
%[[B_REAL]], float %[[B_IMAG]], float %[[A_REAL]], float %[[A_IMAG]])
+// LLVM:  %[[LIBC_COERCED:.*]] = call <2 x float> @__mulsc3(float %[[B_REAL]], 
float %[[B_IMAG]], float %[[A_REAL]], float %[[A_IMAG]])
+// LLVM:  store <2 x float> %[[LIBC_COERCED]], ptr %[[COERCE_SLOT:.*]], align 8
+// LLVM:  %[[LIBC_COMPLEX:.*]] = load { float, float }, ptr %[[COERCE_SLOT]], 
align 4
 // LLVM:  br label %[[PHI_BRANCH:.*]]
 // LLVM: [[ELSE_LABEL]]:
 // LLVM:  br label %[[PHI_BRANCH:]]
@@ -548,7 +553,10 @@ void foo10() {
 // CIR: %[[A_IMAG:.*]] = cir.complex.imag %[[TMP_A]] : 
!cir.complex<!cir.float> -> !cir.float
 // CIR: %[[B_REAL:.*]] = cir.complex.real %[[TMP_B]] : 
!cir.complex<!cir.float> -> !cir.float
 // CIR: %[[B_IMAG:.*]] = cir.complex.imag %[[TMP_B]] : 
!cir.complex<!cir.float> -> !cir.float
-// CIR: %[[RESULT:.*]] = cir.call @__divsc3(%[[A_REAL]], %[[A_IMAG]], 
%[[B_REAL]], %[[B_IMAG]]) : (!cir.float, !cir.float, !cir.float, !cir.float) -> 
!cir.complex<!cir.float>
+// CIR: %[[COERCED:.*]] = cir.call @__divsc3(%[[A_REAL]], %[[A_IMAG]], 
%[[B_REAL]], %[[B_IMAG]]) : (!cir.float, !cir.float, !cir.float, !cir.float) -> 
!cir.vector<2 x !cir.float>
+// CIR: cir.store %[[COERCED]], %[[COERCE_SLOT:.*]] : !cir.vector<2 x 
!cir.float>, !cir.ptr<!cir.vector<2 x !cir.float>>
+// CIR: %[[COERCE_PTR:.*]] = cir.cast bitcast %[[COERCE_SLOT]] : 
!cir.ptr<!cir.vector<2 x !cir.float>> -> !cir.ptr<!cir.complex<!cir.float>>
+// CIR: %[[RESULT:.*]] = cir.load %[[COERCE_PTR]] : 
!cir.ptr<!cir.complex<!cir.float>>, !cir.complex<!cir.float>
 // CIR: cir.store{{.*}} %[[RESULT]], %[[A_ADDR]] : !cir.complex<!cir.float>, 
!cir.ptr<!cir.complex<!cir.float>>
 
 // LLVM: %[[A_ADDR:.*]] = alloca { float, float }, align 4
@@ -559,7 +567,9 @@ void foo10() {
 // LLVM: %[[A_IMAG:.*]] = extractvalue { float, float } %[[TMP_A]], 1
 // LLVM: %[[B_REAL:.*]] = extractvalue { float, float } %[[TMP_B]], 0
 // LLVM: %[[B_IMAG:.*]] = extractvalue { float, float } %[[TMP_B]], 1
-// LLVM: %[[RESULT:.*]] = call { float, float } @__divsc3(float %[[A_REAL]], 
float %[[A_IMAG]], float %[[B_REAL]], float %[[B_IMAG]])
+// LLVM: %[[COERCED:.*]] = call <2 x float> @__divsc3(float %[[A_REAL]], float 
%[[A_IMAG]], float %[[B_REAL]], float %[[B_IMAG]])
+// LLVM: store <2 x float> %[[COERCED]], ptr %[[COERCE_SLOT:.*]], align 8
+// LLVM: %[[RESULT:.*]] = load { float, float }, ptr %[[COERCE_SLOT]], align 4
 // LLVM: store { float, float } %[[RESULT]], ptr %[[A_ADDR]], align 4
 
 // OGCG: %[[A_ADDR:.*]] = alloca { float, float }, align 4
@@ -725,7 +735,10 @@ void foo13() {
 // CIR: %[[A_IMAG_F32:.*]] = cir.complex.imag %[[A_COMPLEX_F32]] : 
!cir.complex<!cir.float> -> !cir.float
 // CIR: %[[B_REAL_F32:.*]] = cir.complex.real %[[B_COMPLEX_F32]] : 
!cir.complex<!cir.float> -> !cir.float
 // CIR: %[[B_IMAG_F32:.*]] = cir.complex.imag %[[B_COMPLEX_F32]] : 
!cir.complex<!cir.float> -> !cir.float
-// CIR: %[[DIV_A_B:.*]] = cir.call @__divsc3(%[[A_REAL_F32]], %[[A_IMAG_F32]], 
%[[B_REAL_F32]], %[[B_IMAG_F32]]) : (!cir.float, !cir.float, !cir.float, 
!cir.float) -> !cir.complex<!cir.float>
+// CIR: %[[DIV_A_B_COERCED:.*]] = cir.call @__divsc3(%[[A_REAL_F32]], 
%[[A_IMAG_F32]], %[[B_REAL_F32]], %[[B_IMAG_F32]]) : (!cir.float, !cir.float, 
!cir.float, !cir.float) -> !cir.vector<2 x !cir.float>
+// CIR: cir.store %[[DIV_A_B_COERCED]], %[[SLOT_AB:.*]] : !cir.vector<2 x 
!cir.float>, !cir.ptr<!cir.vector<2 x !cir.float>>
+// CIR: %[[SLOT_AB_PTR:.*]] = cir.cast bitcast %[[SLOT_AB]] : 
!cir.ptr<!cir.vector<2 x !cir.float>> -> !cir.ptr<!cir.complex<!cir.float>>
+// CIR: %[[DIV_A_B:.*]] = cir.load %[[SLOT_AB_PTR]] : 
!cir.ptr<!cir.complex<!cir.float>>, !cir.complex<!cir.float>
 // CIR: %[[TMP_B:.*]] = cir.load{{.*}} %[[B_ADDR]] : 
!cir.ptr<!cir.complex<!cir.f16>>, !cir.complex<!cir.f16>
 // CIR: %[[B_REAL:.*]] = cir.complex.real %[[TMP_B]] : !cir.complex<!cir.f16> 
-> !cir.f16
 // CIR: %[[B_IMAG:.*]] = cir.complex.imag %[[TMP_B]] : !cir.complex<!cir.f16> 
-> !cir.f16
@@ -736,7 +749,10 @@ void foo13() {
 // CIR: %[[B_IMAG_F32:.*]] = cir.complex.imag %[[B_COMPLEX_F32]] : 
!cir.complex<!cir.float> -> !cir.float
 // CIR: %[[DIV_AB_REAL:.*]] = cir.complex.real %[[DIV_A_B]] : 
!cir.complex<!cir.float> -> !cir.float
 // CIR: %[[DIV_AB_IMAG:.*]] = cir.complex.imag %[[DIV_A_B]] : 
!cir.complex<!cir.float> -> !cir.float
-// CIR: %[[RESULT:.*]] = cir.call @__divsc3(%[[B_REAL_F32]], %[[B_IMAG_F32]], 
%[[DIV_AB_REAL]], %[[DIV_AB_IMAG]]) : (!cir.float, !cir.float, !cir.float, 
!cir.float) -> !cir.complex<!cir.float>
+// CIR: %[[RESULT_COERCED:.*]] = cir.call @__divsc3(%[[B_REAL_F32]], 
%[[B_IMAG_F32]], %[[DIV_AB_REAL]], %[[DIV_AB_IMAG]]) : (!cir.float, !cir.float, 
!cir.float, !cir.float) -> !cir.vector<2 x !cir.float>
+// CIR: cir.store %[[RESULT_COERCED]], %[[SLOT_R:.*]] : !cir.vector<2 x 
!cir.float>, !cir.ptr<!cir.vector<2 x !cir.float>>
+// CIR: %[[SLOT_R_PTR:.*]] = cir.cast bitcast %[[SLOT_R]] : 
!cir.ptr<!cir.vector<2 x !cir.float>> -> !cir.ptr<!cir.complex<!cir.float>>
+// CIR: %[[RESULT:.*]] = cir.load %[[SLOT_R_PTR]] : 
!cir.ptr<!cir.complex<!cir.float>>, !cir.complex<!cir.float>
 // CIR: %[[RESULT_REAL_F32:.*]] = cir.complex.real %[[RESULT]] : 
!cir.complex<!cir.float> -> !cir.float
 // CIR: %[[RESULT_IMAG_F32:.*]] = cir.complex.imag %[[RESULT]] : 
!cir.complex<!cir.float> -> !cir.float
 // CIR: %[[RESULT_REAL_F16:.*]] = cir.cast floating %[[RESULT_REAL_F32]] : 
!cir.float -> !cir.f16
@@ -760,7 +776,9 @@ void foo13() {
 // LLVM: %[[B_IMAG_F32:.*]] = fpext half %[[B_IMAG]] to float
 // LLVM: %[[TMP_B_COMPLEX_F32:.*]] = insertvalue { float, float } {{.*}}, 
float %[[B_REAL_F32]], 0
 // LLVM: %[[B_COMPLEX_F32:.*]] = insertvalue { float, float } 
%[[TMP_B_COMPLEX_F32]], float %[[B_IMAG_F32]], 1
-// LLVM: %[[DIV_A_B:.*]] = call { float, float } @__divsc3(float 
%[[A_REAL_F32]], float %[[A_IMAG_F32]], float %[[B_REAL_F32]], float 
%[[B_IMAG_F32]])
+// LLVM: %[[DIV_A_B_COERCED:.*]] = call <2 x float> @__divsc3(float 
%[[A_REAL_F32]], float %[[A_IMAG_F32]], float %[[B_REAL_F32]], float 
%[[B_IMAG_F32]])
+// LLVM: store <2 x float> %[[DIV_A_B_COERCED]], ptr %[[SLOT_AB:.*]], align 8
+// LLVM: %[[DIV_A_B:.*]] = load { float, float }, ptr %[[SLOT_AB]], align 4
 // LLVM: %[[TMP_B:.*]] = load { half, half }, ptr %[[B_ADDR]], align 2
 // LLVM: %[[B_REAL:.*]] = extractvalue { half, half } %[[TMP_B]], 0
 // LLVM: %[[B_IMAG:.*]] = extractvalue { half, half } %[[TMP_B]], 1
@@ -770,7 +788,9 @@ void foo13() {
 // LLVM: %[[B_COMPLEX_F32:.*]] = insertvalue { float, float } 
%[[TMP_B_COMPLEX_F32]], float %[[B_IMAG_F32]], 1
 // LLVM: %[[DIV_AB_REAL:.*]] = extractvalue { float, float } %[[DIV_A_B]], 0
 // LLVM: %[[DIV_AB_IMAG:.*]] = extractvalue { float, float } %[[DIV_A_B]], 1
-// LLVM: %[[RESULT:.*]] = call { float, float } @__divsc3(float 
%[[B_REAL_F32]], float %[[B_IMAG_F32]], float %[[DIV_AB_REAL]], float 
%[[DIV_AB_IMAG]])
+// LLVM: %[[RESULT_COERCED:.*]] = call <2 x float> @__divsc3(float 
%[[B_REAL_F32]], float %[[B_IMAG_F32]], float %[[DIV_AB_REAL]], float 
%[[DIV_AB_IMAG]])
+// LLVM: store <2 x float> %[[RESULT_COERCED]], ptr %[[SLOT_R:.*]], align 8
+// LLVM: %[[RESULT:.*]] = load { float, float }, ptr %[[SLOT_R]], align 4
 // LLVM: %[[RESULT_REAL_F32:.*]] = extractvalue { float, float } %[[RESULT]], 0
 // LLVM: %[[RESULT_IMAG_F32:.*]] = extractvalue { float, float } %[[RESULT]], 1
 // LLVM: %[[RESULT_REAL_F16:.*]] = fptrunc float %[[RESULT_REAL_F32]] to half
diff --git a/clang/test/CIR/CodeGen/complex-libcall-abi-global-init.cpp 
b/clang/test/CIR/CodeGen/complex-libcall-abi-global-init.cpp
new file mode 100644
index 0000000000000..3ec35a24d3ced
--- /dev/null
+++ b/clang/test/CIR/CodeGen/complex-libcall-abi-global-init.cpp
@@ -0,0 +1,30 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir 
-emit-cir %s -o %t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -fclangir 
-emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 -emit-llvm %s 
-o %t.ll
+// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+
+extern float _Complex a;
+extern float _Complex b;
+
+// A dynamic initializer at namespace scope is still inside the global's
+// initializer region when the helper call is coerced, so the coercion slot has
+// no enclosing function to be placed in.  It has to land in the region that
+// later becomes the initializer function.
+float _Complex g = a / b;
+
+// CIR-LABEL: cir.func {{.*}}@__cxx_global_var_init
+// CIR: %[[SLOT:.*]] = cir.alloca "coerce"{{.*}} : !cir.ptr<!cir.vector<2 x 
!cir.float>>
+// CIR: %[[COERCED:.*]] = cir.call @__divsc3({{.*}}) : (!cir.float, 
!cir.float, !cir.float, !cir.float) -> !cir.vector<2 x !cir.float>
+// CIR: cir.store %[[COERCED]], %[[SLOT]] : !cir.vector<2 x !cir.float>, 
!cir.ptr<!cir.vector<2 x !cir.float>>
+// CIR: cir.store{{.*}} %{{.+}}, %{{.+}} : !cir.complex<!cir.float>, 
!cir.ptr<!cir.complex<!cir.float>>
+
+// LLVM-LABEL: @__cxx_global_var_init(
+// LLVM: %[[SLOT:.+]] = alloca <2 x float>, align 8
+// LLVM: %[[COERCED:.*]] = call <2 x float> @__divsc3(float %{{.+}}, float 
%{{.+}}, float %{{.+}}, float %{{.+}})
+// LLVM: store <2 x float> %[[COERCED]], ptr %[[SLOT]], align 8
+// LLVM: store { float, float } %{{.+}}, ptr @g, align 4
+
+// OGCG-LABEL: @__cxx_global_var_init(
+// OGCG: call noundef <2 x float> @__divsc3(float noundef %{{.+}}, float 
noundef %{{.+}}, float noundef %{{.+}}, float noundef %{{.+}})
diff --git a/clang/test/CIR/CodeGen/complex-libcall-abi.c 
b/clang/test/CIR/CodeGen/complex-libcall-abi.c
new file mode 100644
index 0000000000000..efc459a30ad64
--- /dev/null
+++ b/clang/test/CIR/CodeGen/complex-libcall-abi.c
@@ -0,0 +1,76 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o 
%t.cir
+// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o 
%t-cir.ll
+// RUN: FileCheck --check-prefixes=LLVM,LLVMCIR --input-file=%t-cir.ll %s
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefixes=LLVM,OGCG --input-file=%t.ll %s
+
+float _Complex divf(float _Complex a, float _Complex b) { return a / b; }
+
+// A float pair fits one eightbyte and returns in a single SSE register, so the
+// helper's return coerces to a vector and comes back through memory.
+
+// CIR-LABEL: cir.func {{.*}}@divf
+// CIR: %[[COERCED:.*]] = cir.call @__divsc3({{.*}}) : (!cir.float, 
!cir.float, !cir.float, !cir.float) -> !cir.vector<2 x !cir.float>
+// CIR: cir.store %[[COERCED]], %[[SLOT:.*]] : !cir.vector<2 x !cir.float>, 
!cir.ptr<!cir.vector<2 x !cir.float>>
+// CIR: %[[SLOT_PTR:.*]] = cir.cast bitcast %[[SLOT]] : !cir.ptr<!cir.vector<2 
x !cir.float>> -> !cir.ptr<!cir.complex<!cir.float>>
+// CIR: cir.load %[[SLOT_PTR]] : !cir.ptr<!cir.complex<!cir.float>>, 
!cir.complex<!cir.float>
+
+// The caller's own signature is coerced the same way on both paths.
+// LLVM: define dso_local <2 x float> @divf(<2 x float> noundef %{{.+}}, <2 x 
float> noundef %{{.+}})
+
+// LLVMCIR: %[[COERCED:.*]] = call <2 x float> @__divsc3(float %{{.+}}, float 
%{{.+}}, float %{{.+}}, float %{{.+}})
+// LLVMCIR: store <2 x float> %[[COERCED]], ptr %[[SLOT:.+]], align 8
+// LLVMCIR: load { float, float }, ptr %[[SLOT]], align 4
+// OGCG: call <2 x float> @__divsc3(float noundef %{{.+}}, float noundef 
%{{.+}}, float noundef %{{.+}}, float noundef %{{.+}})
+
+float _Complex mulf(float _Complex a, float _Complex b) { return a * b; }
+
+// CIR-LABEL: cir.func {{.*}}@mulf
+// CIR: cir.call @__mulsc3({{.*}}) : (!cir.float, !cir.float, !cir.float, 
!cir.float) -> !cir.vector<2 x !cir.float>
+
+// LLVM: define dso_local <2 x float> @mulf(<2 x float> noundef %{{.+}}, <2 x 
float> noundef %{{.+}})
+// LLVMCIR: call <2 x float> @__mulsc3(float %{{.+}}, float %{{.+}}, float 
%{{.+}}, float %{{.+}})
+// OGCG: call <2 x float> @__mulsc3(float noundef %{{.+}}, float noundef 
%{{.+}}, float noundef %{{.+}}, float noundef %{{.+}})
+
+double _Complex divd(double _Complex a, double _Complex b) { return a / b; }
+
+// A double pair spans two eightbytes and returns in two registers, so it stays
+// a two-field record and its lowered call is unchanged by the coercion.
+
+// CIR-LABEL: cir.func {{.*}}@divd
+// CIR: cir.call @__divdc3({{.*}}) : (!cir.double, !cir.double, !cir.double, 
!cir.double) -> [[REC_D:!rec_anon_struct[0-9]*]]
+
+// LLVMCIR: call { double, double } @__divdc3(double %{{.+}}, double %{{.+}}, 
double %{{.+}}, double %{{.+}})
+// OGCG: call { double, double } @__divdc3(double noundef %{{.+}}, double 
noundef %{{.+}}, double noundef %{{.+}}, double noundef %{{.+}})
+
+double _Complex muld(double _Complex a, double _Complex b) { return a * b; }
+
+// CIR-LABEL: cir.func {{.*}}@muld
+// CIR: cir.call @__muldc3({{.*}}) : (!cir.double, !cir.double, !cir.double, 
!cir.double) -> [[REC_D]]
+
+// LLVMCIR: call { double, double } @__muldc3(double %{{.+}}, double %{{.+}}, 
double %{{.+}}, double %{{.+}})
+// OGCG: call { double, double } @__muldc3(double noundef %{{.+}}, double 
noundef %{{.+}}, double noundef %{{.+}}, double noundef %{{.+}})
+
+long double _Complex divld(long double _Complex a, long double _Complex b) {
+  return a / b;
+}
+
+// A long double pair is x87-classified and returned in memory, so it also
+// keeps a two-field record.
+
+// CIR-LABEL: cir.func {{.*}}@divld
+// CIR: cir.call @__divxc3({{.*}}) : (!cir.long_double<!cir.f80>, 
!cir.long_double<!cir.f80>, !cir.long_double<!cir.f80>, 
!cir.long_double<!cir.f80>) -> [[REC_LD:!rec_anon_struct[0-9]*]]
+
+// LLVMCIR: call { x86_fp80, x86_fp80 } @__divxc3(x86_fp80 %{{.+}}, x86_fp80 
%{{.+}}, x86_fp80 %{{.+}}, x86_fp80 %{{.+}})
+// OGCG: call { x86_fp80, x86_fp80 } @__divxc3(x86_fp80 noundef %{{.+}}, 
x86_fp80 noundef %{{.+}}, x86_fp80 noundef %{{.+}}, x86_fp80 noundef %{{.+}})
+
+long double _Complex mulld(long double _Complex a, long double _Complex b) {
+  return a * b;
+}
+
+// CIR-LABEL: cir.func {{.*}}@mulld
+// CIR: cir.call @__mulxc3({{.*}}) : (!cir.long_double<!cir.f80>, 
!cir.long_double<!cir.f80>, !cir.long_double<!cir.f80>, 
!cir.long_double<!cir.f80>) -> [[REC_LD]]
+
+// LLVMCIR: call { x86_fp80, x86_fp80 } @__mulxc3(x86_fp80 %{{.+}}, x86_fp80 
%{{.+}}, x86_fp80 %{{.+}}, x86_fp80 %{{.+}})
+// OGCG: call { x86_fp80, x86_fp80 } @__mulxc3(x86_fp80 noundef %{{.+}}, 
x86_fp80 noundef %{{.+}}, x86_fp80 noundef %{{.+}}, x86_fp80 noundef %{{.+}})
diff --git a/clang/test/CIR/CodeGen/complex-mul-div.cpp 
b/clang/test/CIR/CodeGen/complex-mul-div.cpp
index 50d77e5a6c2a5..13e3b39f7f10b 100644
--- a/clang/test/CIR/CodeGen/complex-mul-div.cpp
+++ b/clang/test/CIR/CodeGen/complex-mul-div.cpp
@@ -128,7 +128,10 @@ void foo() {
 // CIR-AFTER-FULL: %[[CONST_FALSE:.*]] = cir.const #false
 // CIR-AFTER-FULL: %[[SELECT_CONDITION:.*]] = cir.select if %[[IS_C_REAL_NAN]] 
then %[[IS_C_IMAG_NAN]] else %[[CONST_FALSE]] : (!cir.bool, !cir.bool, 
!cir.bool) -> !cir.bool
 // CIR-AFTER-FULL: %[[RESULT:.*]] = cir.ternary(%[[SELECT_CONDITION]], true {
-// CIR-AFTER-FULL:   %[[LIBC_COMPLEX:.*]] = cir.call @__mulsc3(%[[A_REAL]], 
%[[A_IMAG]], %[[B_REAL]], %[[B_IMAG]]) : (!cir.float, !cir.float, !cir.float, 
!cir.float) -> !cir.complex<!cir.float>
+// CIR-AFTER-FULL:   %[[LIBC_COERCED:.*]] = cir.call @__mulsc3(%[[A_REAL]], 
%[[A_IMAG]], %[[B_REAL]], %[[B_IMAG]]) : (!cir.float, !cir.float, !cir.float, 
!cir.float) -> !cir.vector<2 x !cir.float>
+// CIR-AFTER-FULL:   cir.store %[[LIBC_COERCED]], %[[COERCE_SLOT:.*]] : 
!cir.vector<2 x !cir.float>, !cir.ptr<!cir.vector<2 x !cir.float>>
+// CIR-AFTER-FULL:   %[[COERCE_PTR:.*]] = cir.cast bitcast %[[COERCE_SLOT]] : 
!cir.ptr<!cir.vector<2 x !cir.float>> -> !cir.ptr<!cir.complex<!cir.float>>
+// CIR-AFTER-FULL:   %[[LIBC_COMPLEX:.*]] = cir.load %[[COERCE_PTR]] : 
!cir.ptr<!cir.complex<!cir.float>>, !cir.complex<!cir.float>
 // CIR-AFTER-FULL:   cir.yield %[[LIBC_COMPLEX]] : !cir.complex<!cir.float>
 // CIR-AFTER-FULL: }, false {
 // CIR-AFTER-FULL:   cir.yield %[[COMPLEX]] : !cir.complex<!cir.float>
@@ -157,7 +160,9 @@ void foo() {
 // LLVM-FULL: %[[SELECT_CONDITION:.*]] = and i1 %[[IS_C_REAL_NAN]], 
%[[IS_C_IMAG_NAN]]
 // LLVM-FULL: br i1 %[[SELECT_CONDITION]], label %[[THEN_LABEL:.*]], label 
%[[ELSE_LABEL:.*]]
 // LLVM-FULL: [[THEN_LABEL]]:
-// LLVM-FULL:  %[[LIBC_COMPLEX:.*]] = call { float, float } @__mulsc3(float 
%[[A_REAL]], float %[[A_IMAG]], float %[[B_REAL]], float %[[B_IMAG]])
+// LLVM-FULL:  %[[LIBC_COERCED:.*]] = call <2 x float> @__mulsc3(float 
%[[A_REAL]], float %[[A_IMAG]], float %[[B_REAL]], float %[[B_IMAG]])
+// LLVM-FULL:  store <2 x float> %[[LIBC_COERCED]], ptr %[[COERCE_SLOT:.*]], 
align 8
+// LLVM-FULL:  %[[LIBC_COMPLEX:.*]] = load { float, float }, ptr 
%[[COERCE_SLOT]], align 4
 // LLVM-FULL:  br label %[[PHI_BRANCH:.*]]
 // LLVM-FULL: [[ELSE_LABEL]]:
 // LLVM-FULL:  br label %[[PHI_BRANCH:]]
@@ -648,7 +653,10 @@ void foo3() {
 // CIR-AFTER-FULL: %[[A_IMAG:.*]] = cir.complex.imag %[[TMP_A]] : 
!cir.complex<!cir.float> -> !cir.float
 // CIR-AFTER-FULL: %[[B_REAL:.*]] = cir.complex.real %[[TMP_B]] : 
!cir.complex<!cir.float> -> !cir.float
 // CIR-AFTER-FULL: %[[B_IMAG:.*]] = cir.complex.imag %[[TMP_B]] : 
!cir.complex<!cir.float> -> !cir.float
-// CIR-AFTER-FULL: %[[RESULT:.*]] = cir.call @__divsc3(%[[A_REAL]], 
%[[A_IMAG]], %[[B_REAL]], %[[B_IMAG]]) : (!cir.float, !cir.float, !cir.float, 
!cir.float) -> !cir.complex<!cir.float>
+// CIR-AFTER-FULL: %[[COERCED:.*]] = cir.call @__divsc3(%[[A_REAL]], 
%[[A_IMAG]], %[[B_REAL]], %[[B_IMAG]]) : (!cir.float, !cir.float, !cir.float, 
!cir.float) -> !cir.vector<2 x !cir.float>
+// CIR-AFTER-FULL: cir.store %[[COERCED]], %[[COERCE_SLOT:.*]] : !cir.vector<2 
x !cir.float>, !cir.ptr<!cir.vector<2 x !cir.float>>
+// CIR-AFTER-FULL: %[[COERCE_PTR:.*]] = cir.cast bitcast %[[COERCE_SLOT]] : 
!cir.ptr<!cir.vector<2 x !cir.float>> -> !cir.ptr<!cir.complex<!cir.float>>
+// CIR-AFTER-FULL: %[[RESULT:.*]] = cir.load %[[COERCE_PTR]] : 
!cir.ptr<!cir.complex<!cir.float>>, !cir.complex<!cir.float>
 // CIR-AFTER-FULL: cir.store{{.*}} %[[RESULT]], %[[C_ADDR]] : 
!cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>>
 
 // LLVM-FULL: %[[A_ADDR:.*]] = alloca { float, float }, align 4
@@ -660,7 +668,9 @@ void foo3() {
 // LLVM-FULL: %[[A_IMAG:.*]] = extractvalue { float, float } %[[TMP_A]], 1
 // LLVM-FULL: %[[B_REAL:.*]] = extractvalue { float, float } %[[TMP_B]], 0
 // LLVM-FULL: %[[B_IMAG:.*]] = extractvalue { float, float } %[[TMP_B]], 1
-// LLVM-FULL: %[[RESULT:.*]] = call { float, float } @__divsc3(float 
%[[A_REAL]], float %[[A_IMAG]], float %[[B_REAL]], float %[[B_IMAG]])
+// LLVM-FULL: %[[COERCED:.*]] = call <2 x float> @__divsc3(float %[[A_REAL]], 
float %[[A_IMAG]], float %[[B_REAL]], float %[[B_IMAG]])
+// LLVM-FULL: store <2 x float> %[[COERCED]], ptr %[[COERCE_SLOT:.*]], align 8
+// LLVM-FULL: %[[RESULT:.*]] = load { float, float }, ptr %[[COERCE_SLOT]], 
align 4
 // LLVM-FULL: store { float, float } %[[RESULT]], ptr %[[C_ADDR]], align 4
 
 // OGCG-FULL: %[[A_ADDR:.*]] = alloca { float, float }, align 4
@@ -1140,7 +1150,10 @@ void foo6() {
 // CIR-AFTER-FULL: %[[A_IMAG:.*]] = cir.complex.imag %[[COMPLEX_A]] : 
!cir.complex<!cir.float> -> !cir.float
 // CIR-AFTER-FULL: %[[B_REAL:.*]] = cir.complex.real %[[TMP_B]] : 
!cir.complex<!cir.float> -> !cir.float
 // CIR-AFTER-FULL: %[[B_IMAG:.*]] = cir.complex.imag %[[TMP_B]] : 
!cir.complex<!cir.float> -> !cir.float
-// CIR-AFTER-FULL: %[[RESULT:.*]] = cir.call @__divsc3(%[[A_REAL]], 
%[[A_IMAG]], %[[B_REAL]], %[[B_IMAG]]) : (!cir.float, !cir.float, !cir.float, 
!cir.float) -> !cir.complex<!cir.float>
+// CIR-AFTER-FULL: %[[COERCED:.*]] = cir.call @__divsc3(%[[A_REAL]], 
%[[A_IMAG]], %[[B_REAL]], %[[B_IMAG]]) : (!cir.float, !cir.float, !cir.float, 
!cir.float) -> !cir.vector<2 x !cir.float>
+// CIR-AFTER-FULL: cir.store %[[COERCED]], %[[COERCE_SLOT:.*]] : !cir.vector<2 
x !cir.float>, !cir.ptr<!cir.vector<2 x !cir.float>>
+// CIR-AFTER-FULL: %[[COERCE_PTR:.*]] = cir.cast bitcast %[[COERCE_SLOT]] : 
!cir.ptr<!cir.vector<2 x !cir.float>> -> !cir.ptr<!cir.complex<!cir.float>>
+// CIR-AFTER-FULL: %[[RESULT:.*]] = cir.load %[[COERCE_PTR]] : 
!cir.ptr<!cir.complex<!cir.float>>, !cir.complex<!cir.float>
 // CIR-AFTER-FULL: cir.store{{.*}} %[[RESULT]], %[[C_ADDR]] : 
!cir.complex<!cir.float>, !cir.ptr<!cir.complex<!cir.float>>
 
 // LLVM-FULL: %[[A_ADDR:.*]] = alloca float, align 4
@@ -1149,10 +1162,12 @@ void foo6() {
 // LLVM-FULL: %[[TMP_A:.*]] = load float, ptr %[[A_ADDR]], align 4
 // LLVM-FULL: %[[TMP_B:.*]] = load { float, float }, ptr %[[B_ADDR]], align 4
 // LLVM-FULL: %[[TMP_COMPLEX_A:.*]] = insertvalue { float, float } {{.*}}, 
float %[[TMP_A]], 0
-// LLVM-FULL: %[[COMPLEX_A:.*]] = insertvalue { float, float } %6, float 
0.000000e+00, 1
+// LLVM-FULL: %[[COMPLEX_A:.*]] = insertvalue { float, float } 
%[[TMP_COMPLEX_A]], float 0.000000e+00, 1
 // LLVM-FULL: %[[B_REAL:.*]] = extractvalue { float, float } %[[TMP_B]], 0
 // LLVM-FULL: %[[B_IMAG:.*]] = extractvalue { float, float } %[[TMP_B]], 1
-// LLVM-FULL: %[[RESULT:.*]] = call { float, float } @__divsc3(float 
%[[TMP_A]], float 0.000000e+00, float %[[B_REAL]], float %[[B_IMAG]])
+// LLVM-FULL: %[[COERCED:.*]] = call <2 x float> @__divsc3(float %[[TMP_A]], 
float 0.000000e+00, float %[[B_REAL]], float %[[B_IMAG]])
+// LLVM-FULL: store <2 x float> %[[COERCED]], ptr %[[COERCE_SLOT:.*]], align 8
+// LLVM-FULL: %[[RESULT:.*]] = load { float, float }, ptr %[[COERCE_SLOT]], 
align 4
 // LLVM-FULL: store { float, float } %[[RESULT]], ptr %[[C_ADDR]], align 4
 
 // OGCG-FULL: %[[A_ADDR:.*]] = alloca float, align 4

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to