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