[clang] [Clang] [Sema] Fix bug in `_Complex float`+`int` arithmetic (PR #83063)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Mar 5 07:18:33 PST 2024
https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/83063
>From 5418bec794ad1df54f49a1c8d317a75d56290239 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 26 Feb 2024 19:53:42 +0100
Subject: [PATCH 1/3] [Clang] [Sema] Remove incorrect int-to-complex-float
conversion
---
clang/lib/Sema/SemaExpr.cpp | 2 -
clang/test/CodeGen/complex-math-mixed.c | 143 ++++++++++++++++++++++++
clang/test/CodeGen/volatile.cpp | 48 ++++----
clang/test/Sema/complex-arithmetic.c | 115 +++++++++++++++++++
4 files changed, 281 insertions(+), 27 deletions(-)
create mode 100644 clang/test/CodeGen/complex-math-mixed.c
create mode 100644 clang/test/Sema/complex-arithmetic.c
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 4049ab3bf6cafb..c9647ee4a2938e 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -1114,8 +1114,6 @@ static bool handleIntegerToComplexFloatConversion(Sema &S, ExprResult &IntExpr,
if (IntTy->isIntegerType()) {
QualType fpTy = ComplexTy->castAs<ComplexType>()->getElementType();
IntExpr = S.ImpCastExprToType(IntExpr.get(), fpTy, CK_IntegralToFloating);
- IntExpr = S.ImpCastExprToType(IntExpr.get(), ComplexTy,
- CK_FloatingRealToComplex);
} else {
assert(IntTy->isComplexIntegerType());
IntExpr = S.ImpCastExprToType(IntExpr.get(), ComplexTy,
diff --git a/clang/test/CodeGen/complex-math-mixed.c b/clang/test/CodeGen/complex-math-mixed.c
new file mode 100644
index 00000000000000..44aa83fa1c0d10
--- /dev/null
+++ b/clang/test/CodeGen/complex-math-mixed.c
@@ -0,0 +1,143 @@
+// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown -o - | FileCheck %s --check-prefix=X86
+
+// Check that for 'F _Complex + int' (F = real floating-point type), we emit an
+// implicit cast from 'int' to 'F', but NOT to 'F _Complex' (i.e. that we do
+// 'F _Complex + F', NOT 'F _Complex + F _Complex'), and likewise for -/*.
+
+float _Complex add_float_ci(float _Complex a, int b) {
+ // X86-LABEL: @add_float_ci
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
+ // X86: fadd float {{.*}}, [[I]]
+ // X86-NOT: fadd
+ return a + b;
+}
+
+float _Complex add_float_ic(int a, float _Complex b) {
+ // X86-LABEL: @add_float_ic
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
+ // X86: fadd float [[I]]
+ // X86-NOT: fadd
+ return a + b;
+}
+
+float _Complex sub_float_ci(float _Complex a, int b) {
+ // X86-LABEL: @sub_float_ci
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
+ // X86: fsub float {{.*}}, [[I]]
+ // X86-NOT: fsub
+ return a - b;
+}
+
+float _Complex sub_float_ic(int a, float _Complex b) {
+ // X86-LABEL: @sub_float_ic
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
+ // X86: fsub float [[I]]
+ // X86: fneg
+ // X86-NOT: fsub
+ return a - b;
+}
+
+float _Complex mul_float_ci(float _Complex a, int b) {
+ // X86-LABEL: @mul_float_ci
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
+ // X86: fmul float {{.*}}, [[I]]
+ // X86: fmul float {{.*}}, [[I]]
+ // X86-NOT: fmul
+ return a * b;
+}
+
+float _Complex mul_float_ic(int a, float _Complex b) {
+ // X86-LABEL: @mul_float_ic
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
+ // X86: fmul float [[I]]
+ // X86: fmul float [[I]]
+ // X86-NOT: fmul
+ return a * b;
+}
+
+float _Complex div_float_ci(float _Complex a, int b) {
+ // X86-LABEL: @div_float_ci
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
+ // X86: fdiv float {{.*}}, [[I]]
+ // X86: fdiv float {{.*}}, [[I]]
+ // X86-NOT: @__divsc3
+ return a / b;
+}
+
+// There is no good way of doing this w/o converting the 'int' to a complex
+// number, so we expect complex division here.
+float _Complex div_float_ic(int a, float _Complex b) {
+ // X86-LABEL: @div_float_ic
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
+ // X86: call {{.*}} @__divsc3(float {{.*}} [[I]], float noundef 0.{{0+}}e+00, float {{.*}}, float {{.*}})
+ return a / b;
+}
+
+double _Complex add_double_ci(double _Complex a, int b) {
+ // X86-LABEL: @add_double_ci
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
+ // X86: fadd double {{.*}}, [[I]]
+ // X86-NOT: fadd
+ return a + b;
+}
+
+double _Complex add_double_ic(int a, double _Complex b) {
+ // X86-LABEL: @add_double_ic
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
+ // X86: fadd double [[I]]
+ // X86-NOT: fadd
+ return a + b;
+}
+
+double _Complex sub_double_ci(double _Complex a, int b) {
+ // X86-LABEL: @sub_double_ci
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
+ // X86: fsub double {{.*}}, [[I]]
+ // X86-NOT: fsub
+ return a - b;
+}
+
+double _Complex sub_double_ic(int a, double _Complex b) {
+ // X86-LABEL: @sub_double_ic
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
+ // X86: fsub double [[I]]
+ // X86: fneg
+ // X86-NOT: fsub
+ return a - b;
+}
+
+double _Complex mul_double_ci(double _Complex a, int b) {
+ // X86-LABEL: @mul_double_ci
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
+ // X86: fmul double {{.*}}, [[I]]
+ // X86: fmul double {{.*}}, [[I]]
+ // X86-NOT: fmul
+ return a * b;
+}
+
+double _Complex mul_double_ic(int a, double _Complex b) {
+ // X86-LABEL: @mul_double_ic
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
+ // X86: fmul double [[I]]
+ // X86: fmul double [[I]]
+ // X86-NOT: fmul
+ return a * b;
+}
+
+double _Complex div_double_ci(double _Complex a, int b) {
+ // X86-LABEL: @div_double_ci
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
+ // X86: fdiv double {{.*}}, [[I]]
+ // X86: fdiv double {{.*}}, [[I]]
+ // X86-NOT: @__divdc3
+ return a / b;
+}
+
+// There is no good way of doing this w/o converting the 'int' to a complex
+// number, so we expect complex division here.
+double _Complex div_double_ic(int a, double _Complex b) {
+ // X86-LABEL: @div_double_ic
+ // X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
+ // X86: call {{.*}} @__divdc3(double {{.*}} [[I]], double noundef 0.{{0+}}e+00, double {{.*}}, double {{.*}})
+ return a / b;
+}
diff --git a/clang/test/CodeGen/volatile.cpp b/clang/test/CodeGen/volatile.cpp
index 38724659ad8a35..70f523b93852ed 100644
--- a/clang/test/CodeGen/volatile.cpp
+++ b/clang/test/CodeGen/volatile.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -O2 -triple=x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s -check-prefix CHECK
+// RUN: %clang_cc1 -O2 -triple=x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
struct agg
{
int a ;
@@ -10,34 +10,32 @@ _Complex float cf;
int volatile vol =10;
void f0() {
const_cast<volatile _Complex float &>(cf) = const_cast<volatile _Complex float&>(cf) + 1;
-// CHECK: %cf.real = load volatile float, ptr @cf
-// CHECK: %cf.imag = load volatile float, ptr getelementptr
-// CHECK: %add.r = fadd float %cf.real, 1.000000e+00
-// CHECK: %add.i = fadd float %cf.imag, 0.000000e+00
-// CHECK: store volatile float %add.r
-// CHECK: store volatile float %add.i, ptr getelementptr
+// CHECK: [[Re1:%.*]] = load volatile float, ptr @cf
+// CHECK: [[Im1:%.*]] = load volatile float, ptr getelementptr
+// CHECK: [[Add1:%.*]] = fadd float [[Re1]], 1.000000e+00
+// CHECK: store volatile float [[Add1]], ptr @cf
+// CHECK: store volatile float [[Im1]], ptr getelementptr
static_cast<volatile _Complex float &>(cf) = static_cast<volatile _Complex float&>(cf) + 1;
-// CHECK: %cf.real1 = load volatile float, ptr @cf
-// CHECK: %cf.imag2 = load volatile float, ptr getelementptr
-// CHECK: %add.r3 = fadd float %cf.real1, 1.000000e+00
-// CHECK: %add.i4 = fadd float %cf.imag2, 0.000000e+00
-// CHECK: store volatile float %add.r3, ptr @cf
-// CHECK: store volatile float %add.i4, ptr getelementptr
+// CHECK: [[Re2:%.*]] = load volatile float, ptr @cf
+// CHECK: [[Im2:%.*]] = load volatile float, ptr getelementptr
+// CHECK: [[Add2:%.*]] = fadd float [[Re2]], 1.000000e+00
+// CHECK: store volatile float [[Add2]], ptr @cf
+// CHECK: store volatile float [[Im2]], ptr getelementptr
const_cast<volatile int &>(a.a) = const_cast<volatile int &>(t.a) ;
-// CHECK: %0 = load volatile i32, ptr @t
-// CHECK: store volatile i32 %0, ptr @a
+// CHECK: [[I1:%.*]] = load volatile i32, ptr @t
+// CHECK: store volatile i32 [[I1]], ptr @a
static_cast<volatile int &>(a.b) = static_cast<volatile int &>(t.a) ;
-// CHECK: %1 = load volatile i32, ptr @t
-// CHECK: store volatile i32 %1, ptr getelementptr
+// CHECK: [[I2:%.*]] = load volatile i32, ptr @t
+// CHECK: store volatile i32 [[I2]], ptr getelementptr
const_cast<volatile int&>(vt) = const_cast<volatile int&>(vt) + 1;
-// CHECK: %2 = load volatile i32, ptr @vt
-// CHECK: %add = add nsw i32 %2, 1
-// CHECK: store volatile i32 %add, ptr @vt
+// CHECK: [[I3:%.*]] = load volatile i32, ptr @vt
+// CHECK: [[Add3:%.*]] = add nsw i32 [[I3]], 1
+// CHECK: store volatile i32 [[Add3]], ptr @vt
static_cast<volatile int&>(vt) = static_cast<volatile int&>(vt) + 1;
-// CHECK: %3 = load volatile i32, ptr @vt
-// CHECK: %add5 = add nsw i32 %3, 1
-// CHECK: store volatile i32 %add5, ptr @vt
+// CHECK: [[I4:%.*]] = load volatile i32, ptr @vt
+// CHECK: [[Add4:%.*]] = add nsw i32 [[I4]], 1
+// CHECK: store volatile i32 [[Add4]], ptr @vt
vt = const_cast<int&>(vol);
-// %4 = load i32, ptr @vol
-// store i32 %4, ptr @vt
+// [[I5:%.*]] = load i32, ptr @vol
+// store i32 [[I5]], ptr @vt
}
diff --git a/clang/test/Sema/complex-arithmetic.c b/clang/test/Sema/complex-arithmetic.c
new file mode 100644
index 00000000000000..c9e84da6daa9dc
--- /dev/null
+++ b/clang/test/Sema/complex-arithmetic.c
@@ -0,0 +1,115 @@
+// RUN: %clang_cc1 -verify %s
+// expected-no-diagnostics
+
+// This tests evaluation of _Complex arithmetic at compile time.
+
+#define APPROX_EQ(a, b) ( \
+ __builtin_fabs(__real (a) - __real (b)) < 0.0001 && \
+ __builtin_fabs(__imag (a) - __imag (b)) < 0.0001 \
+)
+
+#define EVAL(a, b) _Static_assert(a == b, "")
+#define EVALF(a, b) _Static_assert(APPROX_EQ(a, b), "")
+
+// _Complex float + _Complex float
+void a() {
+ EVALF((2.f + 3i) + (4.f + 5i), 6.f + 8i);
+ EVALF((2.f + 3i) - (4.f + 5i), -2.f - 2i);
+ EVALF((2.f + 3i) * (4.f + 5i), -7.f + 22i);
+ EVALF((2.f + 3i) / (4.f + 5i), 0.5609f + 0.0487i);
+
+ EVALF((2. + 3i) + (4. + 5i), 6. + 8i);
+ EVALF((2. + 3i) - (4. + 5i), -2. - 2i);
+ EVALF((2. + 3i) * (4. + 5i), -7. + 22i);
+ EVALF((2. + 3i) / (4. + 5i), .5609 + .0487i);
+}
+
+// _Complex int + _Complex int
+void b() {
+ EVAL((2 + 3i) + (4 + 5i), 6 + 8i);
+ EVAL((2 + 3i) - (4 + 5i), -2 - 2i);
+ EVAL((2 + 3i) * (4 + 5i), -7 + 22i);
+ EVAL((8 + 30i) / (4 + 5i), 4 + 1i);
+}
+
+// _Complex float + float
+void c() {
+ EVALF((2.f + 4i) + 3.f, 5.f + 4i);
+ EVALF((2.f + 4i) - 3.f, -1.f + 4i);
+ EVALF((2.f + 4i) * 3.f, 6.f + 12i);
+ EVALF((2.f + 4i) / 2.f, 1.f + 2i);
+
+ EVALF(3.f + (2.f + 4i), 5.f + 4i);
+ EVALF(3.f - (2.f + 4i), 1.f - 4i);
+ EVALF(3.f * (2.f + 4i), 6.f + 12i);
+ EVALF(3.f / (2.f + 4i), .3f - 0.6i);
+
+ EVALF((2. + 4i) + 3., 5. + 4i);
+ EVALF((2. + 4i) - 3., -1. + 4i);
+ EVALF((2. + 4i) * 3., 6. + 12i);
+ EVALF((2. + 4i) / 2., 1. + 2i);
+
+ EVALF(3. + (2. + 4i), 5. + 4i);
+ EVALF(3. - (2. + 4i), 1. - 4i);
+ EVALF(3. * (2. + 4i), 6. + 12i);
+ EVALF(3. / (2. + 4i), .3 - 0.6i);
+}
+
+// _Complex int + int
+void d() {
+ EVAL((2 + 4i) + 3, 5 + 4i);
+ EVAL((2 + 4i) - 3, -1 + 4i);
+ EVAL((2 + 4i) * 3, 6 + 12i);
+ EVAL((2 + 4i) / 2, 1 + 2i);
+
+ EVAL(3 + (2 + 4i), 5 + 4i);
+ EVAL(3 - (2 + 4i), 1 - 4i);
+ EVAL(3 * (2 + 4i), 6 + 12i);
+ EVAL(20 / (2 + 4i), 2 - 4i);
+}
+
+// _Complex float + int
+void e() {
+ EVALF((2.f + 4i) + 3, 5.f + 4i);
+ EVALF((2.f + 4i) - 3, -1.f + 4i);
+ EVALF((2.f + 4i) * 3, 6.f + 12i);
+ EVALF((2.f + 4i) / 2, 1.f + 2i);
+
+ EVALF(3 + (2.f + 4i), 5.f + 4i);
+ EVALF(3 - (2.f + 4i), 1.f - 4i);
+ EVALF(3 * (2.f + 4i), 6.f + 12i);
+ EVALF(3 / (2.f + 4i), .3f - 0.6i);
+
+ EVALF((2. + 4i) + 3, 5. + 4i);
+ EVALF((2. + 4i) - 3, -1. + 4i);
+ EVALF((2. + 4i) * 3, 6. + 12i);
+ EVALF((2. + 4i) / 2, 1. + 2i);
+
+ EVALF(3 + (2. + 4i), 5. + 4i);
+ EVALF(3 - (2. + 4i), 1. - 4i);
+ EVALF(3 * (2. + 4i), 6. + 12i);
+ EVALF(3 / (2. + 4i), .3 - 0.6i);
+}
+
+// _Complex int + float
+void f() {
+ EVALF((2 + 4i) + 3.f, 5.f + 4i);
+ EVALF((2 + 4i) - 3.f, -1.f + 4i);
+ EVALF((2 + 4i) * 3.f, 6.f + 12i);
+ EVALF((2 + 4i) / 2.f, 1.f + 2i);
+
+ EVALF(3.f + (2 + 4i), 5.f + 4i);
+ EVALF(3.f - (2 + 4i), 1.f - 4i);
+ EVALF(3.f * (2 + 4i), 6.f + 12i);
+ EVALF(3.f / (2 + 4i), .3f - 0.6i);
+
+ EVALF((2 + 4i) + 3., 5. + 4i);
+ EVALF((2 + 4i) - 3., -1. + 4i);
+ EVALF((2 + 4i) * 3., 6. + 12i);
+ EVALF((2 + 4i) / 2., 1. + 2i);
+
+ EVALF(3. + (2 + 4i), 5. + 4i);
+ EVALF(3. - (2 + 4i), 1. - 4i);
+ EVALF(3. * (2 + 4i), 6. + 12i);
+ EVALF(3. / (2 + 4i), .3 - 0.6i);
+}
>From 322b22a1cf8265167935ba28bf35cea04cf34f2e Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 27 Feb 2024 19:04:42 +0100
Subject: [PATCH 2/3] [Clang] Update release notes
---
clang/docs/ReleaseNotes.rst | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index c60c5682dbd826..651ebf72859141 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -215,6 +215,13 @@ Bug Fixes in This Version
for logical operators in C23.
Fixes (`#64356 <https://github.com/llvm/llvm-project/issues/64356>`_).
+- When performing mixed arithmetic between ``_Complex`` floating-point types and integers,
+ Clang now correctly promotes the integer to its corresponding real floating-point
+ type only rather than to the complex type (e.g. ``_Complex float / int`` is now evaluated
+ as ``_Complex float / float`` rather than ``_Complex float / _Complex float``), as mandated
+ by the C standard. This significantly improves codegen of `*` and `/` especially.
+ Fixes (`#31205 <https://github.com/llvm/llvm-project/issues/31205>`_).
+
Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>From d049edb26146e609bd52a2f88d4bc68d0dab7af3 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 5 Mar 2024 16:18:09 +0100
Subject: [PATCH 3/3] [NFC] Update comments and rename function
---
clang/lib/Sema/SemaExpr.cpp | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 7fa3541618ba03..724da6ca6a7ccb 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -1099,12 +1099,13 @@ ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT,
return E;
}
-/// Converts an integer to complex float type. Helper function of
+/// Convert complex integers to complex floats and real integers to
+/// real floats as required for complex arithmetic. Helper function of
/// UsualArithmeticConversions()
///
/// \return false if the integer expression is an integer type and is
-/// successfully converted to the complex type.
-static bool handleIntegerToComplexFloatConversion(Sema &S, ExprResult &IntExpr,
+/// successfully converted to the (complex) float type.
+static bool handleComplexIntegerToFloatConversion(Sema &S, ExprResult &IntExpr,
ExprResult &ComplexExpr,
QualType IntTy,
QualType ComplexTy,
@@ -1158,11 +1159,11 @@ static QualType handleComplexFloatConversion(Sema &S, ExprResult &Shorter,
static QualType handleComplexConversion(Sema &S, ExprResult &LHS,
ExprResult &RHS, QualType LHSType,
QualType RHSType, bool IsCompAssign) {
- // if we have an integer operand, the result is the complex type.
- if (!handleIntegerToComplexFloatConversion(S, RHS, LHS, RHSType, LHSType,
+ // Handle (complex) integer types.
+ if (!handleComplexIntegerToFloatConversion(S, RHS, LHS, RHSType, LHSType,
/*SkipCast=*/false))
return LHSType;
- if (!handleIntegerToComplexFloatConversion(S, LHS, RHS, LHSType, RHSType,
+ if (!handleComplexIntegerToFloatConversion(S, LHS, RHS, LHSType, RHSType,
/*SkipCast=*/IsCompAssign))
return RHSType;
More information about the cfe-commits
mailing list