Author: Adam Smith
Date: 2026-08-19T16:51:57-05:00
New Revision: aacef70b0840f16ca49c27d759c01df55f25649e

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

LOG: [CIR] Move LoweringPrepare before callconv lowering (#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.

Moving LoweringPrepare before CallConvLowering lets the classifier see
everything the pass emits rather than complex alone. Complex mul and div
are the only calls it synthesizes today that need coercion, but anything
emitted there later is lowered properly too.

Assisted-by: Cursor / claude-opus-5

Added: 
    clang/test/CIR/CodeGen/complex-libcall-abi-global-init.cpp
    clang/test/CIR/CodeGen/complex-libcall-abi.c

Modified: 
    clang/lib/CIR/Lowering/CIRPasses.cpp
    clang/test/CIR/CodeGen/complex-compound-assignment.cpp
    clang/test/CIR/CodeGen/complex-mul-div.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CIR/Lowering/CIRPasses.cpp 
b/clang/lib/CIR/Lowering/CIRPasses.cpp
index 1d1fdaf42aaa4..64d82dd4e7301 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());
 
+  // LoweringPrepare synthesizes calls to runtime helpers such as __divsc3, and
+  // outlines dynamic global initializers into functions.  It must run before
+  // CallConvLowering so the classifier sees them, otherwise their signatures
+  // go unclassified and caller and callee disagree on the ABI.
+  pm.addPass(mlir::createLoweringPreparePass(&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
@@ -119,8 +125,6 @@ runCIRToCIRPasses(mlir::ModuleOp theModule, 
mlir::MLIRContext &mlirContext,
           allowsX86TargetAttrAvx(astContext), 
getX86ABICompatInfo(astContext)));
   }
 
-  pm.addPass(mlir::createLoweringPreparePass(&astContext));
-
   pm.enableVerifier(enableVerifier);
   (void)mlir::applyPassManagerCLOptions(pm);
   return pm.run(theModule);

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..921ad1b763cb4
--- /dev/null
+++ b/clang/test/CIR/CodeGen/complex-libcall-abi-global-init.cpp
@@ -0,0 +1,51 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -std=c++20 
-complex-range=full -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 
-complex-range=full -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 -std=c++20 
-complex-range=full -emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefixes=LLVM,OGCG --input-file=%t.ll %s
+
+extern float _Complex a;
+extern float _Complex b;
+
+float _Complex g = a / b;
+
+// CIR-LABEL: cir.func {{.*}}@__cxx_global_var_init()
+// CIR: %[[SLOT:.*]] = cir.alloca "coerce" align(8) : !cir.ptr<!cir.vector<2 x 
!cir.float>>
+// CIR: %[[G:.*]] = cir.get_global @g : !cir.ptr<!cir.complex<!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: %[[SLOT_PTR:.*]] = cir.cast bitcast %[[SLOT]] : !cir.ptr<!cir.vector<2 
x !cir.float>> -> !cir.ptr<!cir.complex<!cir.float>>
+// CIR: %[[RESULT:.*]] = cir.load %[[SLOT_PTR]] : 
!cir.ptr<!cir.complex<!cir.float>>, !cir.complex<!cir.float>
+// CIR: cir.store{{.*}} %[[RESULT]], %[[G]] : !cir.complex<!cir.float>, 
!cir.ptr<!cir.complex<!cir.float>>
+
+// LLVM-LABEL: define internal void @__cxx_global_var_init()
+// LLVMCIR: %[[SLOT:.+]] = alloca <2 x float>, align 8
+// LLVMCIR: %[[COERCED:.*]] = call <2 x float> @__divsc3(float %{{.+}}, float 
%{{.+}}, float %{{.+}}, float %{{.+}})
+// LLVMCIR: store <2 x float> %[[COERCED]], ptr %[[SLOT]], align 8
+// LLVMCIR: %[[RESULT:.+]] = load { float, float }, ptr %[[SLOT]], align 4
+// LLVMCIR: store { float, float } %[[RESULT]], ptr @g, align 4
+
+// OGCG: call noundef <2 x float> @__divsc3(float noundef %{{.+}}, float 
noundef %{{.+}}, float noundef %{{.+}}, float noundef %{{.+}})
+
+float _Complex h = a * b;
+
+// Multiply puts its call inside the NaN-check region, one level further in, so
+// the slot has to be hoisted to the initializer function's entry block.
+
+// CIR-LABEL: cir.func {{.*}}@__cxx_global_var_init.1()
+// CIR: %[[SLOT:.*]] = cir.alloca "coerce" align(8) : !cir.ptr<!cir.vector<2 x 
!cir.float>>
+// CIR: %[[COERCED:.*]] = cir.call @__mulsc3({{.*}}) : (!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>>
+
+// LLVM-LABEL: define internal void @__cxx_global_var_init.1()
+// LLVMCIR: %[[SLOT:.+]] = alloca <2 x float>, align 8
+// LLVMCIR: br i1 %{{.+}}, label %[[THEN:.+]], label %{{.+}}
+// LLVMCIR: [[THEN]]:
+// LLVMCIR: %[[COERCED:.*]] = call <2 x float> @__mulsc3(float %{{.+}}, float 
%{{.+}}, float %{{.+}}, float %{{.+}})
+// LLVMCIR: store <2 x float> %[[COERCED]], ptr %[[SLOT]], align 8
+// LLVMCIR: %[[CALLED:.+]] = load { float, float }, ptr %[[SLOT]], align 4
+// LLVMCIR: %[[RESULT:.+]] = phi { float, float } [ %{{.+}}, %{{.+}} ], [ 
%[[CALLED]], %[[THEN]] ]
+// LLVMCIR: store { float, float } %[[RESULT]], ptr @h, align 4
+
+// OGCG: call noundef <2 x float> @__mulsc3(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..937eb20a6dc22
--- /dev/null
+++ b/clang/test/CIR/CodeGen/complex-libcall-abi.c
@@ -0,0 +1,81 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -complex-range=full 
-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 -complex-range=full 
-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 -complex-range=full 
-emit-llvm %s -o %t.ll
+// RUN: FileCheck --check-prefixes=LLVM,OGCG --input-file=%t.ll %s
+
+// CIR: [[REC_LD:!rec_anon_struct[0-9]*]] = !cir.struct<{data !cir.f80, data 
!cir.f80}>
+// CIR: [[REC_D:!rec_anon_struct[0-9]*]] = !cir.struct<{data !cir.double, data 
!cir.double}>
+
+float _Complex divf(float _Complex a, float _Complex b) { return a / b; }
+
+// A float pair is SSE-classified, so the helper return coerces to <2 x float>.
+// 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, so the return stays a two-field record.
+// CIR-LABEL: cir.func {{.*}}@divd
+// CIR: cir.call @__divdc3({{.*}}) : (!cir.double, !cir.double, !cir.double, 
!cir.double) -> [[REC_D]]{{[^0-9]}}
+
+// CIR does not carry noundef onto expanded parameters.
+// LLVMCIR: define dso_local { double, double } @divd(double %{{.+}}, double 
%{{.+}}, double %{{.+}}, double %{{.+}})
+// OGCG: define dso_local { double, double } @divd(double noundef %{{.+}}, 
double noundef %{{.+}}, double noundef %{{.+}}, double noundef %{{.+}})
+
+// 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]]{{[^0-9]}}
+
+// 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, so the return stays 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]]{{[^0-9]}}
+
+// An x87 pair is passed indirectly, and CIR marks those byval slots noalias.
+// LLVMCIR: define dso_local { x86_fp80, x86_fp80 } @divld(ptr noalias noundef 
byval({ x86_fp80, x86_fp80 }) align 16 %{{.+}}, ptr noalias noundef byval({ 
x86_fp80, x86_fp80 }) align 16 %{{.+}})
+// OGCG: define dso_local { x86_fp80, x86_fp80 } @divld(ptr noundef byval({ 
x86_fp80, x86_fp80 }) align 16 %{{.+}}, ptr noundef byval({ x86_fp80, x86_fp80 
}) align 16 %{{.+}})
+
+// 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]]{{[^0-9]}}
+
+// 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