Skip to content

Commit 69afb9d

Browse files
authored
[Clang] [Sema] Fix bug in _Complex float+int arithmetic (#83063)
C23 6.3.1.8 ‘Usual arithmetic conversions’ p1 states (emphasis mine): > Otherwise, if the corresponding real type of either operand is `float`, the other operand is converted, *without change of type domain*, to a type whose corresponding real type is `float`. ‘type domain’ here refers to `_Complex` vs real (i.e. non-`_Complex`); there is another clause that states the same for `double`. Consider the following code: ```c++ _Complex float f; int x; f / x; ``` After talking this over with @AaronBallman, we came to the conclusion that `x` should be converted to `float` and *not* `_Complex float` (that is, we should perform a division of `_Complex float / float`, and *not* `_Complex float / _Complex float`; the same also applies to `-+*`). This was already being done correctly for cases where `x` was already a `float`; it’s just mixed `_Complex float`+`int` operations that currently suffer from this problem. This pr removes the extra `FloatingRealToComplex` conversion that we were erroneously inserting and adds some tests to make sure we’re actually doing `_Complex float / float` and not `_Complex float / _Complex float` (and analogously for `double` and `-+*`). The only exception here is `float / _Complex float`, which calls a library function (`__divsc3`) that takes 4 `float`s, so we end up having to convert the `float` to a `_Complex float` after all (and analogously for `double`); I don’t believe there is a way around this. Lastly, we were also missing tests for `_Complex` arithmetic at compile time, so this adds some tests for that as well.
1 parent ccd1608 commit 69afb9d

File tree

5 files changed

+298
-33
lines changed

5 files changed

+298
-33
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,13 @@ Bug Fixes in This Version
278278
- Clang now correctly generates overloads for bit-precise integer types for
279279
builtin operators in C++. Fixes #GH82998.
280280

281+
- When performing mixed arithmetic between ``_Complex`` floating-point types and integers,
282+
Clang now correctly promotes the integer to its corresponding real floating-point
283+
type only rather than to the complex type (e.g. ``_Complex float / int`` is now evaluated
284+
as ``_Complex float / float`` rather than ``_Complex float / _Complex float``), as mandated
285+
by the C standard. This significantly improves codegen of `*` and `/` especially.
286+
Fixes (`#31205 <https://github.com/llvm/llvm-project/issues/31205>`_).
287+
281288
Bug Fixes to Compiler Builtins
282289
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
283290

clang/lib/Sema/SemaExpr.cpp

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1099,12 +1099,13 @@ ExprResult Sema::DefaultVariadicArgumentPromotion(Expr *E, VariadicCallType CT,
10991099
return E;
11001100
}
11011101

1102-
/// Converts an integer to complex float type. Helper function of
1102+
/// Convert complex integers to complex floats and real integers to
1103+
/// real floats as required for complex arithmetic. Helper function of
11031104
/// UsualArithmeticConversions()
11041105
///
11051106
/// \return false if the integer expression is an integer type and is
1106-
/// successfully converted to the complex type.
1107-
static bool handleIntegerToComplexFloatConversion(Sema &S, ExprResult &IntExpr,
1107+
/// successfully converted to the (complex) float type.
1108+
static bool handleComplexIntegerToFloatConversion(Sema &S, ExprResult &IntExpr,
11081109
ExprResult &ComplexExpr,
11091110
QualType IntTy,
11101111
QualType ComplexTy,
@@ -1114,8 +1115,6 @@ static bool handleIntegerToComplexFloatConversion(Sema &S, ExprResult &IntExpr,
11141115
if (IntTy->isIntegerType()) {
11151116
QualType fpTy = ComplexTy->castAs<ComplexType>()->getElementType();
11161117
IntExpr = S.ImpCastExprToType(IntExpr.get(), fpTy, CK_IntegralToFloating);
1117-
IntExpr = S.ImpCastExprToType(IntExpr.get(), ComplexTy,
1118-
CK_FloatingRealToComplex);
11191118
} else {
11201119
assert(IntTy->isComplexIntegerType());
11211120
IntExpr = S.ImpCastExprToType(IntExpr.get(), ComplexTy,
@@ -1160,11 +1159,11 @@ static QualType handleComplexFloatConversion(Sema &S, ExprResult &Shorter,
11601159
static QualType handleComplexConversion(Sema &S, ExprResult &LHS,
11611160
ExprResult &RHS, QualType LHSType,
11621161
QualType RHSType, bool IsCompAssign) {
1163-
// if we have an integer operand, the result is the complex type.
1164-
if (!handleIntegerToComplexFloatConversion(S, RHS, LHS, RHSType, LHSType,
1162+
// Handle (complex) integer types.
1163+
if (!handleComplexIntegerToFloatConversion(S, RHS, LHS, RHSType, LHSType,
11651164
/*SkipCast=*/false))
11661165
return LHSType;
1167-
if (!handleIntegerToComplexFloatConversion(S, LHS, RHS, LHSType, RHSType,
1166+
if (!handleComplexIntegerToFloatConversion(S, LHS, RHS, LHSType, RHSType,
11681167
/*SkipCast=*/IsCompAssign))
11691168
return RHSType;
11701169

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// RUN: %clang_cc1 %s -O0 -emit-llvm -triple x86_64-unknown-unknown -o - | FileCheck %s --check-prefix=X86
2+
// RUN: %clang_cc1 %s -O0 -triple x86_64-unknown-unknown -fsyntax-only -ast-dump | FileCheck %s --check-prefix=AST
3+
4+
// Check that for 'F _Complex + int' (F = real floating-point type), we emit an
5+
// implicit cast from 'int' to 'F', but NOT to 'F _Complex' (i.e. that we do
6+
// 'F _Complex + F', NOT 'F _Complex + F _Complex'), and likewise for -/*.
7+
8+
// AST-NOT: FloatingRealToComplex
9+
10+
float _Complex add_float_ci(float _Complex a, int b) {
11+
// X86-LABEL: @add_float_ci
12+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
13+
// X86: fadd float {{.*}}, [[I]]
14+
// X86-NOT: fadd
15+
return a + b;
16+
}
17+
18+
float _Complex add_float_ic(int a, float _Complex b) {
19+
// X86-LABEL: @add_float_ic
20+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
21+
// X86: fadd float [[I]]
22+
// X86-NOT: fadd
23+
return a + b;
24+
}
25+
26+
float _Complex sub_float_ci(float _Complex a, int b) {
27+
// X86-LABEL: @sub_float_ci
28+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
29+
// X86: fsub float {{.*}}, [[I]]
30+
// X86-NOT: fsub
31+
return a - b;
32+
}
33+
34+
float _Complex sub_float_ic(int a, float _Complex b) {
35+
// X86-LABEL: @sub_float_ic
36+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
37+
// X86: fsub float [[I]]
38+
// X86: fneg
39+
// X86-NOT: fsub
40+
return a - b;
41+
}
42+
43+
float _Complex mul_float_ci(float _Complex a, int b) {
44+
// X86-LABEL: @mul_float_ci
45+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
46+
// X86: fmul float {{.*}}, [[I]]
47+
// X86: fmul float {{.*}}, [[I]]
48+
// X86-NOT: fmul
49+
return a * b;
50+
}
51+
52+
float _Complex mul_float_ic(int a, float _Complex b) {
53+
// X86-LABEL: @mul_float_ic
54+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
55+
// X86: fmul float [[I]]
56+
// X86: fmul float [[I]]
57+
// X86-NOT: fmul
58+
return a * b;
59+
}
60+
61+
float _Complex div_float_ci(float _Complex a, int b) {
62+
// X86-LABEL: @div_float_ci
63+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
64+
// X86: fdiv float {{.*}}, [[I]]
65+
// X86: fdiv float {{.*}}, [[I]]
66+
// X86-NOT: @__divsc3
67+
return a / b;
68+
}
69+
70+
// There is no good way of doing this w/o converting the 'int' to a complex
71+
// number, so we expect complex division here.
72+
float _Complex div_float_ic(int a, float _Complex b) {
73+
// X86-LABEL: @div_float_ic
74+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to float
75+
// X86: call {{.*}} @__divsc3(float {{.*}} [[I]], float noundef 0.{{0+}}e+00, float {{.*}}, float {{.*}})
76+
return a / b;
77+
}
78+
79+
double _Complex add_double_ci(double _Complex a, int b) {
80+
// X86-LABEL: @add_double_ci
81+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
82+
// X86: fadd double {{.*}}, [[I]]
83+
// X86-NOT: fadd
84+
return a + b;
85+
}
86+
87+
double _Complex add_double_ic(int a, double _Complex b) {
88+
// X86-LABEL: @add_double_ic
89+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
90+
// X86: fadd double [[I]]
91+
// X86-NOT: fadd
92+
return a + b;
93+
}
94+
95+
double _Complex sub_double_ci(double _Complex a, int b) {
96+
// X86-LABEL: @sub_double_ci
97+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
98+
// X86: fsub double {{.*}}, [[I]]
99+
// X86-NOT: fsub
100+
return a - b;
101+
}
102+
103+
double _Complex sub_double_ic(int a, double _Complex b) {
104+
// X86-LABEL: @sub_double_ic
105+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
106+
// X86: fsub double [[I]]
107+
// X86: fneg
108+
// X86-NOT: fsub
109+
return a - b;
110+
}
111+
112+
double _Complex mul_double_ci(double _Complex a, int b) {
113+
// X86-LABEL: @mul_double_ci
114+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
115+
// X86: fmul double {{.*}}, [[I]]
116+
// X86: fmul double {{.*}}, [[I]]
117+
// X86-NOT: fmul
118+
return a * b;
119+
}
120+
121+
double _Complex mul_double_ic(int a, double _Complex b) {
122+
// X86-LABEL: @mul_double_ic
123+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
124+
// X86: fmul double [[I]]
125+
// X86: fmul double [[I]]
126+
// X86-NOT: fmul
127+
return a * b;
128+
}
129+
130+
double _Complex div_double_ci(double _Complex a, int b) {
131+
// X86-LABEL: @div_double_ci
132+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
133+
// X86: fdiv double {{.*}}, [[I]]
134+
// X86: fdiv double {{.*}}, [[I]]
135+
// X86-NOT: @__divdc3
136+
return a / b;
137+
}
138+
139+
// There is no good way of doing this w/o converting the 'int' to a complex
140+
// number, so we expect complex division here.
141+
double _Complex div_double_ic(int a, double _Complex b) {
142+
// X86-LABEL: @div_double_ic
143+
// X86: [[I:%.*]] = sitofp i32 {{%.*}} to double
144+
// X86: call {{.*}} @__divdc3(double {{.*}} [[I]], double noundef 0.{{0+}}e+00, double {{.*}}, double {{.*}})
145+
return a / b;
146+
}

clang/test/CodeGen/volatile.cpp

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// RUN: %clang_cc1 -O2 -triple=x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s -check-prefix CHECK
1+
// RUN: %clang_cc1 -O2 -triple=x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s
22
struct agg
33
{
44
int a ;
@@ -10,34 +10,32 @@ _Complex float cf;
1010
int volatile vol =10;
1111
void f0() {
1212
const_cast<volatile _Complex float &>(cf) = const_cast<volatile _Complex float&>(cf) + 1;
13-
// CHECK: %cf.real = load volatile float, ptr @cf
14-
// CHECK: %cf.imag = load volatile float, ptr getelementptr
15-
// CHECK: %add.r = fadd float %cf.real, 1.000000e+00
16-
// CHECK: %add.i = fadd float %cf.imag, 0.000000e+00
17-
// CHECK: store volatile float %add.r
18-
// CHECK: store volatile float %add.i, ptr getelementptr
13+
// CHECK: [[Re1:%.*]] = load volatile float, ptr @cf
14+
// CHECK: [[Im1:%.*]] = load volatile float, ptr getelementptr
15+
// CHECK: [[Add1:%.*]] = fadd float [[Re1]], 1.000000e+00
16+
// CHECK: store volatile float [[Add1]], ptr @cf
17+
// CHECK: store volatile float [[Im1]], ptr getelementptr
1918
static_cast<volatile _Complex float &>(cf) = static_cast<volatile _Complex float&>(cf) + 1;
20-
// CHECK: %cf.real1 = load volatile float, ptr @cf
21-
// CHECK: %cf.imag2 = load volatile float, ptr getelementptr
22-
// CHECK: %add.r3 = fadd float %cf.real1, 1.000000e+00
23-
// CHECK: %add.i4 = fadd float %cf.imag2, 0.000000e+00
24-
// CHECK: store volatile float %add.r3, ptr @cf
25-
// CHECK: store volatile float %add.i4, ptr getelementptr
19+
// CHECK: [[Re2:%.*]] = load volatile float, ptr @cf
20+
// CHECK: [[Im2:%.*]] = load volatile float, ptr getelementptr
21+
// CHECK: [[Add2:%.*]] = fadd float [[Re2]], 1.000000e+00
22+
// CHECK: store volatile float [[Add2]], ptr @cf
23+
// CHECK: store volatile float [[Im2]], ptr getelementptr
2624
const_cast<volatile int &>(a.a) = const_cast<volatile int &>(t.a) ;
27-
// CHECK: %0 = load volatile i32, ptr @t
28-
// CHECK: store volatile i32 %0, ptr @a
25+
// CHECK: [[I1:%.*]] = load volatile i32, ptr @t
26+
// CHECK: store volatile i32 [[I1]], ptr @a
2927
static_cast<volatile int &>(a.b) = static_cast<volatile int &>(t.a) ;
30-
// CHECK: %1 = load volatile i32, ptr @t
31-
// CHECK: store volatile i32 %1, ptr getelementptr
28+
// CHECK: [[I2:%.*]] = load volatile i32, ptr @t
29+
// CHECK: store volatile i32 [[I2]], ptr getelementptr
3230
const_cast<volatile int&>(vt) = const_cast<volatile int&>(vt) + 1;
33-
// CHECK: %2 = load volatile i32, ptr @vt
34-
// CHECK: %add = add nsw i32 %2, 1
35-
// CHECK: store volatile i32 %add, ptr @vt
31+
// CHECK: [[I3:%.*]] = load volatile i32, ptr @vt
32+
// CHECK: [[Add3:%.*]] = add nsw i32 [[I3]], 1
33+
// CHECK: store volatile i32 [[Add3]], ptr @vt
3634
static_cast<volatile int&>(vt) = static_cast<volatile int&>(vt) + 1;
37-
// CHECK: %3 = load volatile i32, ptr @vt
38-
// CHECK: %add5 = add nsw i32 %3, 1
39-
// CHECK: store volatile i32 %add5, ptr @vt
35+
// CHECK: [[I4:%.*]] = load volatile i32, ptr @vt
36+
// CHECK: [[Add4:%.*]] = add nsw i32 [[I4]], 1
37+
// CHECK: store volatile i32 [[Add4]], ptr @vt
4038
vt = const_cast<int&>(vol);
41-
// %4 = load i32, ptr @vol
42-
// store i32 %4, ptr @vt
39+
// [[I5:%.*]] = load i32, ptr @vol
40+
// store i32 [[I5]], ptr @vt
4341
}

clang/test/Sema/complex-arithmetic.c

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// RUN: %clang_cc1 -verify %s
2+
// expected-no-diagnostics
3+
4+
// This tests evaluation of _Complex arithmetic at compile time.
5+
6+
#define APPROX_EQ(a, b) ( \
7+
__builtin_fabs(__real (a) - __real (b)) < 0.0001 && \
8+
__builtin_fabs(__imag (a) - __imag (b)) < 0.0001 \
9+
)
10+
11+
#define EVAL(a, b) _Static_assert(a == b, "")
12+
#define EVALF(a, b) _Static_assert(APPROX_EQ(a, b), "")
13+
14+
// _Complex float + _Complex float
15+
void a() {
16+
EVALF((2.f + 3i) + (4.f + 5i), 6.f + 8i);
17+
EVALF((2.f + 3i) - (4.f + 5i), -2.f - 2i);
18+
EVALF((2.f + 3i) * (4.f + 5i), -7.f + 22i);
19+
EVALF((2.f + 3i) / (4.f + 5i), 0.5609f + 0.0487i);
20+
21+
EVALF((2. + 3i) + (4. + 5i), 6. + 8i);
22+
EVALF((2. + 3i) - (4. + 5i), -2. - 2i);
23+
EVALF((2. + 3i) * (4. + 5i), -7. + 22i);
24+
EVALF((2. + 3i) / (4. + 5i), .5609 + .0487i);
25+
}
26+
27+
// _Complex int + _Complex int
28+
void b() {
29+
EVAL((2 + 3i) + (4 + 5i), 6 + 8i);
30+
EVAL((2 + 3i) - (4 + 5i), -2 - 2i);
31+
EVAL((2 + 3i) * (4 + 5i), -7 + 22i);
32+
EVAL((8 + 30i) / (4 + 5i), 4 + 1i);
33+
}
34+
35+
// _Complex float + float
36+
void c() {
37+
EVALF((2.f + 4i) + 3.f, 5.f + 4i);
38+
EVALF((2.f + 4i) - 3.f, -1.f + 4i);
39+
EVALF((2.f + 4i) * 3.f, 6.f + 12i);
40+
EVALF((2.f + 4i) / 2.f, 1.f + 2i);
41+
42+
EVALF(3.f + (2.f + 4i), 5.f + 4i);
43+
EVALF(3.f - (2.f + 4i), 1.f - 4i);
44+
EVALF(3.f * (2.f + 4i), 6.f + 12i);
45+
EVALF(3.f / (2.f + 4i), .3f - 0.6i);
46+
47+
EVALF((2. + 4i) + 3., 5. + 4i);
48+
EVALF((2. + 4i) - 3., -1. + 4i);
49+
EVALF((2. + 4i) * 3., 6. + 12i);
50+
EVALF((2. + 4i) / 2., 1. + 2i);
51+
52+
EVALF(3. + (2. + 4i), 5. + 4i);
53+
EVALF(3. - (2. + 4i), 1. - 4i);
54+
EVALF(3. * (2. + 4i), 6. + 12i);
55+
EVALF(3. / (2. + 4i), .3 - 0.6i);
56+
}
57+
58+
// _Complex int + int
59+
void d() {
60+
EVAL((2 + 4i) + 3, 5 + 4i);
61+
EVAL((2 + 4i) - 3, -1 + 4i);
62+
EVAL((2 + 4i) * 3, 6 + 12i);
63+
EVAL((2 + 4i) / 2, 1 + 2i);
64+
65+
EVAL(3 + (2 + 4i), 5 + 4i);
66+
EVAL(3 - (2 + 4i), 1 - 4i);
67+
EVAL(3 * (2 + 4i), 6 + 12i);
68+
EVAL(20 / (2 + 4i), 2 - 4i);
69+
}
70+
71+
// _Complex float + int
72+
void e() {
73+
EVALF((2.f + 4i) + 3, 5.f + 4i);
74+
EVALF((2.f + 4i) - 3, -1.f + 4i);
75+
EVALF((2.f + 4i) * 3, 6.f + 12i);
76+
EVALF((2.f + 4i) / 2, 1.f + 2i);
77+
78+
EVALF(3 + (2.f + 4i), 5.f + 4i);
79+
EVALF(3 - (2.f + 4i), 1.f - 4i);
80+
EVALF(3 * (2.f + 4i), 6.f + 12i);
81+
EVALF(3 / (2.f + 4i), .3f - 0.6i);
82+
83+
EVALF((2. + 4i) + 3, 5. + 4i);
84+
EVALF((2. + 4i) - 3, -1. + 4i);
85+
EVALF((2. + 4i) * 3, 6. + 12i);
86+
EVALF((2. + 4i) / 2, 1. + 2i);
87+
88+
EVALF(3 + (2. + 4i), 5. + 4i);
89+
EVALF(3 - (2. + 4i), 1. - 4i);
90+
EVALF(3 * (2. + 4i), 6. + 12i);
91+
EVALF(3 / (2. + 4i), .3 - 0.6i);
92+
}
93+
94+
// _Complex int + float
95+
void f() {
96+
EVALF((2 + 4i) + 3.f, 5.f + 4i);
97+
EVALF((2 + 4i) - 3.f, -1.f + 4i);
98+
EVALF((2 + 4i) * 3.f, 6.f + 12i);
99+
EVALF((2 + 4i) / 2.f, 1.f + 2i);
100+
101+
EVALF(3.f + (2 + 4i), 5.f + 4i);
102+
EVALF(3.f - (2 + 4i), 1.f - 4i);
103+
EVALF(3.f * (2 + 4i), 6.f + 12i);
104+
EVALF(3.f / (2 + 4i), .3f - 0.6i);
105+
106+
EVALF((2 + 4i) + 3., 5. + 4i);
107+
EVALF((2 + 4i) - 3., -1. + 4i);
108+
EVALF((2 + 4i) * 3., 6. + 12i);
109+
EVALF((2 + 4i) / 2., 1. + 2i);
110+
111+
EVALF(3. + (2 + 4i), 5. + 4i);
112+
EVALF(3. - (2 + 4i), 1. - 4i);
113+
EVALF(3. * (2 + 4i), 6. + 12i);
114+
EVALF(3. / (2 + 4i), .3 - 0.6i);
115+
}

0 commit comments

Comments
 (0)