Skip to content

Commit 6a4f94c

Browse files
committed
[Clang] Add elementwise min/max builtins.
This patch implements __builtin_elementwise_max and __builtin_elementwise_min, as specified in D111529. Reviewed By: aaron.ballman Differential Revision: https://reviews.llvm.org/D111985 (cherry-picked from 1ef25d2)
1 parent 5ff0c29 commit 6a4f94c

File tree

8 files changed

+385
-0
lines changed

8 files changed

+385
-0
lines changed

clang/include/clang/Basic/Builtins.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,9 @@ BUILTIN(__builtin_alloca, "v*z" , "Fn")
645645
BUILTIN(__builtin_alloca_with_align, "v*zIz", "Fn")
646646
BUILTIN(__builtin_call_with_static_chain, "v.", "nt")
647647

648+
BUILTIN(__builtin_elementwise_max, "v.", "nct")
649+
BUILTIN(__builtin_elementwise_min, "v.", "nct")
650+
648651
BUILTIN(__builtin_matrix_transpose, "v.", "nFt")
649652
BUILTIN(__builtin_matrix_column_major_load, "v.", "nFt")
650653
BUILTIN(__builtin_matrix_column_major_store, "v.", "nFt")

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11352,6 +11352,9 @@ def err_builtin_launder_invalid_arg : Error<
1135211352
"%select{non-pointer|function pointer|void pointer}0 argument to "
1135311353
"'__builtin_launder' is not allowed">;
1135411354

11355+
def err_builtin_invalid_arg_type: Error <
11356+
"%ordinal0 argument must be a %1 (was %2)">;
11357+
1135511358
def err_builtin_matrix_disabled: Error<
1135611359
"matrix types extension is disabled. Pass -fenable-matrix to enable it">;
1135711360
def err_matrix_index_not_integer: Error<

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12678,6 +12678,8 @@ class Sema final {
1267812678

1267912679
bool CheckPPCMMAType(QualType Type, SourceLocation TypeLoc);
1268012680

12681+
bool SemaBuiltinElementwiseMath(CallExpr *TheCall);
12682+
1268112683
// Matrix builtin handling.
1268212684
ExprResult SemaBuiltinMatrixTranspose(CallExpr *TheCall,
1268312685
ExprResult CallResult);

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3101,6 +3101,39 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
31013101
return RValue::get(V);
31023102
}
31033103

3104+
case Builtin::BI__builtin_elementwise_max: {
3105+
Value *Op0 = EmitScalarExpr(E->getArg(0));
3106+
Value *Op1 = EmitScalarExpr(E->getArg(1));
3107+
Value *Result;
3108+
if (Op0->getType()->isIntOrIntVectorTy()) {
3109+
QualType Ty = E->getArg(0)->getType();
3110+
if (auto *VecTy = Ty->getAs<VectorType>())
3111+
Ty = VecTy->getElementType();
3112+
Result = Builder.CreateBinaryIntrinsic(Ty->isSignedIntegerType()
3113+
? llvm::Intrinsic::smax
3114+
: llvm::Intrinsic::umax,
3115+
Op0, Op1, nullptr, "elt.max");
3116+
} else
3117+
Result = Builder.CreateMaxNum(Op0, Op1, "elt.max");
3118+
return RValue::get(Result);
3119+
}
3120+
case Builtin::BI__builtin_elementwise_min: {
3121+
Value *Op0 = EmitScalarExpr(E->getArg(0));
3122+
Value *Op1 = EmitScalarExpr(E->getArg(1));
3123+
Value *Result;
3124+
if (Op0->getType()->isIntOrIntVectorTy()) {
3125+
QualType Ty = E->getArg(0)->getType();
3126+
if (auto *VecTy = Ty->getAs<VectorType>())
3127+
Ty = VecTy->getElementType();
3128+
Result = Builder.CreateBinaryIntrinsic(Ty->isSignedIntegerType()
3129+
? llvm::Intrinsic::smin
3130+
: llvm::Intrinsic::umin,
3131+
Op0, Op1, nullptr, "elt.min");
3132+
} else
3133+
Result = Builder.CreateMinNum(Op0, Op1, "elt.min");
3134+
return RValue::get(Result);
3135+
}
3136+
31043137
case Builtin::BI__builtin_matrix_transpose: {
31053138
const auto *MatrixTy = E->getArg(0)->getType()->getAs<ConstantMatrixType>();
31063139
Value *MatValue = EmitScalarExpr(E->getArg(0));

clang/lib/Sema/SemaChecking.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2288,6 +2288,11 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
22882288
break;
22892289
}
22902290

2291+
case Builtin::BI__builtin_elementwise_min:
2292+
case Builtin::BI__builtin_elementwise_max:
2293+
if (SemaBuiltinElementwiseMath(TheCall))
2294+
return ExprError();
2295+
break;
22912296
case Builtin::BI__builtin_matrix_transpose:
22922297
return SemaBuiltinMatrixTranspose(TheCall, TheCallResult);
22932298

@@ -16737,6 +16742,49 @@ void Sema::CheckAddressOfPackedMember(Expr *rhs) {
1673716742
_2, _3, _4));
1673816743
}
1673916744

16745+
// Check if \p Ty is a valid type for the elementwise math builtins. If it is
16746+
// not a valid type, emit an error message and return true. Otherwise return
16747+
// false.
16748+
static bool checkMathBuiltinElementType(Sema &S, SourceLocation Loc,
16749+
QualType Ty) {
16750+
if (!Ty->getAs<VectorType>() && !ConstantMatrixType::isValidElementType(Ty)) {
16751+
S.Diag(Loc, diag::err_builtin_invalid_arg_type)
16752+
<< 1 << "vector, integer or floating point type" << Ty;
16753+
return true;
16754+
}
16755+
return false;
16756+
}
16757+
16758+
bool Sema::SemaBuiltinElementwiseMath(CallExpr *TheCall) {
16759+
if (checkArgCount(*this, TheCall, 2))
16760+
return true;
16761+
16762+
ExprResult A = TheCall->getArg(0);
16763+
ExprResult B = TheCall->getArg(1);
16764+
// Do standard promotions between the two arguments, returning their common
16765+
// type.
16766+
QualType Res =
16767+
UsualArithmeticConversions(A, B, TheCall->getExprLoc(), ACK_Comparison);
16768+
if (A.isInvalid() || B.isInvalid())
16769+
return true;
16770+
16771+
QualType TyA = A.get()->getType();
16772+
QualType TyB = B.get()->getType();
16773+
16774+
if (Res.isNull() || TyA.getCanonicalType() != TyB.getCanonicalType())
16775+
return Diag(A.get()->getBeginLoc(),
16776+
diag::err_typecheck_call_different_arg_types)
16777+
<< TyA << TyB;
16778+
16779+
if (checkMathBuiltinElementType(*this, A.get()->getBeginLoc(), TyA))
16780+
return true;
16781+
16782+
TheCall->setArg(0, A.get());
16783+
TheCall->setArg(1, B.get());
16784+
TheCall->setType(Res);
16785+
return false;
16786+
}
16787+
1674016788
ExprResult Sema::SemaBuiltinMatrixTranspose(CallExpr *TheCall,
1674116789
ExprResult CallResult) {
1674216790
if (checkArgCount(*this, TheCall, 1))
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
// RUN: %clang_cc1 -triple x86_64-apple-darwin %s -emit-llvm -disable-llvm-passes -o - | FileCheck %s
2+
3+
typedef float float4 __attribute__((ext_vector_type(4)));
4+
typedef short int si8 __attribute__((ext_vector_type(8)));
5+
typedef unsigned int u4 __attribute__((ext_vector_type(4)));
6+
7+
__attribute__((address_space(1))) int int_as_one;
8+
typedef int bar;
9+
bar b;
10+
11+
void test_builtin_elementwise_max(float f1, float f2, double d1, double d2,
12+
float4 vf1, float4 vf2, long long int i1,
13+
long long int i2, si8 vi1, si8 vi2,
14+
unsigned u1, unsigned u2, u4 vu1, u4 vu2) {
15+
// CHECK-LABEL: define void @test_builtin_elementwise_max(
16+
17+
// CHECK: [[F1:%.+]] = load float, float* %f1.addr, align 4
18+
// CHECK-NEXT: [[F2:%.+]] = load float, float* %f2.addr, align 4
19+
// CHECK-NEXT: call float @llvm.maxnum.f32(float %0, float %1)
20+
f1 = __builtin_elementwise_max(f1, f2);
21+
22+
// CHECK: [[D1:%.+]] = load double, double* %d1.addr, align 8
23+
// CHECK-NEXT: [[D2:%.+]] = load double, double* %d2.addr, align 8
24+
// CHECK-NEXT: call double @llvm.maxnum.f64(double [[D1]], double [[D2]])
25+
d1 = __builtin_elementwise_max(d1, d2);
26+
27+
// CHECK: [[D2:%.+]] = load double, double* %d2.addr, align 8
28+
// CHECK-NEXT: call double @llvm.maxnum.f64(double 2.000000e+01, double [[D2]])
29+
d1 = __builtin_elementwise_max(20.0, d2);
30+
31+
// CHECK: [[VF1:%.+]] = load <4 x float>, <4 x float>* %vf1.addr, align 16
32+
// CHECK-NEXT: [[VF2:%.+]] = load <4 x float>, <4 x float>* %vf2.addr, align 16
33+
// CHECK-NEXT: call <4 x float> @llvm.maxnum.v4f32(<4 x float> [[VF1]], <4 x float> [[VF2]])
34+
vf1 = __builtin_elementwise_max(vf1, vf2);
35+
36+
// CHECK: [[I1:%.+]] = load i64, i64* %i1.addr, align 8
37+
// CHECK-NEXT: [[I2:%.+]] = load i64, i64* %i2.addr, align 8
38+
// CHECK-NEXT: call i64 @llvm.smax.i64(i64 [[I1]], i64 [[I2]])
39+
i1 = __builtin_elementwise_max(i1, i2);
40+
41+
// CHECK: [[I1:%.+]] = load i64, i64* %i1.addr, align 8
42+
// CHECK-NEXT: call i64 @llvm.smax.i64(i64 [[I1]], i64 10)
43+
i1 = __builtin_elementwise_max(i1, 10);
44+
45+
// CHECK: [[VI1:%.+]] = load <8 x i16>, <8 x i16>* %vi1.addr, align 16
46+
// CHECK-NEXT: [[VI2:%.+]] = load <8 x i16>, <8 x i16>* %vi2.addr, align 16
47+
// CHECK-NEXT: call <8 x i16> @llvm.smax.v8i16(<8 x i16> [[VI1]], <8 x i16> [[VI2]])
48+
vi1 = __builtin_elementwise_max(vi1, vi2);
49+
50+
// CHECK: [[U1:%.+]] = load i32, i32* %u1.addr, align 4
51+
// CHECK-NEXT: [[U2:%.+]] = load i32, i32* %u2.addr, align 4
52+
// CHECK-NEXT: call i32 @llvm.umax.i32(i32 [[U1]], i32 [[U2]])
53+
u1 = __builtin_elementwise_max(u1, u2);
54+
55+
// CHECK: [[VU1:%.+]] = load <4 x i32>, <4 x i32>* %vu1.addr, align 16
56+
// CHECK-NEXT: [[VU2:%.+]] = load <4 x i32>, <4 x i32>* %vu2.addr, align 16
57+
// CHECK-NEXT: call <4 x i32> @llvm.umax.v4i32(<4 x i32> [[VU1]], <4 x i32> [[VU2]])
58+
vu1 = __builtin_elementwise_max(vu1, vu2);
59+
60+
// CHECK: [[CVF1:%.+]] = load <4 x float>, <4 x float>* %cvf1, align 16
61+
// CHECK-NEXT: [[VF2:%.+]] = load <4 x float>, <4 x float>* %vf2.addr, align 16
62+
// CHECK-NEXT: call <4 x float> @llvm.maxnum.v4f32(<4 x float> [[CVF1]], <4 x float> [[VF2]])
63+
const float4 cvf1 = vf1;
64+
vf1 = __builtin_elementwise_max(cvf1, vf2);
65+
66+
// CHECK: [[VF2:%.+]] = load <4 x float>, <4 x float>* %vf2.addr, align 16
67+
// CHECK-NEXT: [[CVF1:%.+]] = load <4 x float>, <4 x float>* %cvf1, align 16
68+
// CHECK-NEXT: call <4 x float> @llvm.maxnum.v4f32(<4 x float> [[VF2]], <4 x float> [[CVF1]])
69+
vf1 = __builtin_elementwise_max(vf2, cvf1);
70+
71+
// CHECK: [[IAS1:%.+]] = load i32, i32 addrspace(1)* @int_as_one, align 4
72+
// CHECK-NEXT: [[B:%.+]] = load i32, i32* @b, align 4
73+
// CHECK-NEXT: call i32 @llvm.smax.i32(i32 [[IAS1]], i32 [[B]])
74+
int_as_one = __builtin_elementwise_max(int_as_one, b);
75+
76+
// CHECK: call i32 @llvm.smax.i32(i32 1, i32 97)
77+
i1 = __builtin_elementwise_max(1, 'a');
78+
}
79+
80+
void test_builtin_elementwise_min(float f1, float f2, double d1, double d2,
81+
float4 vf1, float4 vf2, long long int i1,
82+
long long int i2, si8 vi1, si8 vi2,
83+
unsigned u1, unsigned u2, u4 vu1, u4 vu2) {
84+
// CHECK-LABEL: define void @test_builtin_elementwise_min(
85+
// CHECK: [[F1:%.+]] = load float, float* %f1.addr, align 4
86+
// CHECK-NEXT: [[F2:%.+]] = load float, float* %f2.addr, align 4
87+
// CHECK-NEXT: call float @llvm.minnum.f32(float %0, float %1)
88+
f1 = __builtin_elementwise_min(f1, f2);
89+
90+
// CHECK: [[D1:%.+]] = load double, double* %d1.addr, align 8
91+
// CHECK-NEXT: [[D2:%.+]] = load double, double* %d2.addr, align 8
92+
// CHECK-NEXT: call double @llvm.minnum.f64(double [[D1]], double [[D2]])
93+
d1 = __builtin_elementwise_min(d1, d2);
94+
95+
// CHECK: [[D1:%.+]] = load double, double* %d1.addr, align 8
96+
// CHECK-NEXT: call double @llvm.minnum.f64(double [[D1]], double 2.000000e+00)
97+
d1 = __builtin_elementwise_min(d1, 2.0);
98+
99+
// CHECK: [[VF1:%.+]] = load <4 x float>, <4 x float>* %vf1.addr, align 16
100+
// CHECK-NEXT: [[VF2:%.+]] = load <4 x float>, <4 x float>* %vf2.addr, align 16
101+
// CHECK-NEXT: call <4 x float> @llvm.minnum.v4f32(<4 x float> [[VF1]], <4 x float> [[VF2]])
102+
vf1 = __builtin_elementwise_min(vf1, vf2);
103+
104+
// CHECK: [[I1:%.+]] = load i64, i64* %i1.addr, align 8
105+
// CHECK-NEXT: [[I2:%.+]] = load i64, i64* %i2.addr, align 8
106+
// CHECK-NEXT: call i64 @llvm.smin.i64(i64 [[I1]], i64 [[I2]])
107+
i1 = __builtin_elementwise_min(i1, i2);
108+
109+
// CHECK: [[I2:%.+]] = load i64, i64* %i2.addr, align 8
110+
// CHECK-NEXT: call i64 @llvm.smin.i64(i64 -11, i64 [[I2]])
111+
i1 = __builtin_elementwise_min(-11, i2);
112+
113+
// CHECK: [[VI1:%.+]] = load <8 x i16>, <8 x i16>* %vi1.addr, align 16
114+
// CHECK-NEXT: [[VI2:%.+]] = load <8 x i16>, <8 x i16>* %vi2.addr, align 16
115+
// CHECK-NEXT: call <8 x i16> @llvm.smin.v8i16(<8 x i16> [[VI1]], <8 x i16> [[VI2]])
116+
vi1 = __builtin_elementwise_min(vi1, vi2);
117+
118+
// CHECK: [[U1:%.+]] = load i32, i32* %u1.addr, align 4
119+
// CHECK-NEXT: [[U2:%.+]] = load i32, i32* %u2.addr, align 4
120+
// CHECK-NEXT: call i32 @llvm.umin.i32(i32 [[U1]], i32 [[U2]])
121+
u1 = __builtin_elementwise_min(u1, u2);
122+
123+
// CHECK: [[U1:%.+]] = load i32, i32* %u1.addr, align 4
124+
// CHECK-NEXT: [[ZEXT_U1:%.+]] = zext i32 [[U1]] to i64
125+
// CHECK-NEXT: [[I2:%.+]] = load i64, i64* %i2.addr, align 8
126+
// CHECK-NEXT: call i64 @llvm.smin.i64(i64 [[ZEXT_U1]], i64 [[I2]])
127+
u1 = __builtin_elementwise_min(u1, i2);
128+
129+
// CHECK: [[VU1:%.+]] = load <4 x i32>, <4 x i32>* %vu1.addr, align 16
130+
// CHECK-NEXT: [[VU2:%.+]] = load <4 x i32>, <4 x i32>* %vu2.addr, align 16
131+
// CHECK-NEXT: call <4 x i32> @llvm.umin.v4i32(<4 x i32> [[VU1]], <4 x i32> [[VU2]])
132+
vu1 = __builtin_elementwise_min(vu1, vu2);
133+
134+
// CHECK: [[CVF1:%.+]] = load <4 x float>, <4 x float>* %cvf1, align 16
135+
// CHECK-NEXT: [[VF2:%.+]] = load <4 x float>, <4 x float>* %vf2.addr, align 16
136+
// CHECK-NEXT: call <4 x float> @llvm.minnum.v4f32(<4 x float> [[CVF1]], <4 x float> [[VF2]])
137+
const float4 cvf1 = vf1;
138+
vf1 = __builtin_elementwise_min(cvf1, vf2);
139+
140+
// CHECK: [[VF2:%.+]] = load <4 x float>, <4 x float>* %vf2.addr, align 16
141+
// CHECK-NEXT: [[CVF1:%.+]] = load <4 x float>, <4 x float>* %cvf1, align 16
142+
// CHECK-NEXT: call <4 x float> @llvm.minnum.v4f32(<4 x float> [[VF2]], <4 x float> [[CVF1]])
143+
vf1 = __builtin_elementwise_min(vf2, cvf1);
144+
145+
// CHECK: [[IAS1:%.+]] = load i32, i32 addrspace(1)* @int_as_one, align 4
146+
// CHECK-NEXT: [[B:%.+]] = load i32, i32* @b, align 4
147+
// CHECK-NEXT: call i32 @llvm.smin.i32(i32 [[IAS1]], i32 [[B]])
148+
int_as_one = __builtin_elementwise_min(int_as_one, b);
149+
}
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
// RUN: %clang_cc1 -std=c99 %s -pedantic -verify -triple=x86_64-apple-darwin9
2+
3+
typedef float float4 __attribute__((ext_vector_type(4)));
4+
typedef int int3 __attribute__((ext_vector_type(3)));
5+
6+
struct Foo {
7+
char *p;
8+
};
9+
10+
__attribute__((address_space(1))) int int_as_one;
11+
typedef int bar;
12+
bar b;
13+
14+
void test_builtin_elementwise_max(int i, short s, double d, float4 v, int3 iv, int *p) {
15+
i = __builtin_elementwise_max(p, d);
16+
// expected-error@-1 {{arguments are of different types ('int *' vs 'double')}}
17+
18+
struct Foo foo = __builtin_elementwise_max(i, i);
19+
// expected-error@-1 {{initializing 'struct Foo' with an expression of incompatible type 'int'}}
20+
21+
i = __builtin_elementwise_max(i);
22+
// expected-error@-1 {{too few arguments to function call, expected 2, have 1}}
23+
24+
i = __builtin_elementwise_max();
25+
// expected-error@-1 {{too few arguments to function call, expected 2, have 0}}
26+
27+
i = __builtin_elementwise_max(i, i, i);
28+
// expected-error@-1 {{too many arguments to function call, expected 2, have 3}}
29+
30+
i = __builtin_elementwise_max(v, iv);
31+
// expected-error@-1 {{arguments are of different types ('float4' (vector of 4 'float' values) vs 'int3' (vector of 3 'int' values))}}
32+
33+
s = __builtin_elementwise_max(i, s);
34+
35+
enum e { one,
36+
two };
37+
i = __builtin_elementwise_max(one, two);
38+
39+
enum f { three };
40+
enum f x = __builtin_elementwise_max(one, three);
41+
42+
_ExtInt(32) ext;
43+
ext = __builtin_elementwise_max(ext, ext);
44+
45+
const int ci;
46+
i = __builtin_elementwise_max(ci, i);
47+
i = __builtin_elementwise_max(i, ci);
48+
i = __builtin_elementwise_max(ci, ci);
49+
50+
i = __builtin_elementwise_max(i, int_as_one); // ok (attributes don't match)?
51+
i = __builtin_elementwise_max(i, b); // ok (sugar doesn't match)?
52+
53+
int A[10];
54+
A = __builtin_elementwise_max(A, A);
55+
// expected-error@-1 {{1st argument must be a vector, integer or floating point type (was 'int *')}}
56+
57+
int(ii);
58+
int j;
59+
j = __builtin_elementwise_max(i, j);
60+
61+
_Complex float c1, c2;
62+
c1 = __builtin_elementwise_max(c1, c2);
63+
// expected-error@-1 {{1st argument must be a vector, integer or floating point type (was '_Complex float')}}
64+
}
65+
66+
void test_builtin_elementwise_min(int i, short s, double d, float4 v, int3 iv, int *p) {
67+
i = __builtin_elementwise_min(p, d);
68+
// expected-error@-1 {{arguments are of different types ('int *' vs 'double')}}
69+
70+
struct Foo foo = __builtin_elementwise_min(i, i);
71+
// expected-error@-1 {{initializing 'struct Foo' with an expression of incompatible type 'int'}}
72+
73+
i = __builtin_elementwise_min(i);
74+
// expected-error@-1 {{too few arguments to function call, expected 2, have 1}}
75+
76+
i = __builtin_elementwise_min();
77+
// expected-error@-1 {{too few arguments to function call, expected 2, have 0}}
78+
79+
i = __builtin_elementwise_min(i, i, i);
80+
// expected-error@-1 {{too many arguments to function call, expected 2, have 3}}
81+
82+
i = __builtin_elementwise_min(v, iv);
83+
// expected-error@-1 {{arguments are of different types ('float4' (vector of 4 'float' values) vs 'int3' (vector of 3 'int' values))}}
84+
85+
s = __builtin_elementwise_min(i, s);
86+
87+
enum e { one,
88+
two };
89+
i = __builtin_elementwise_min(one, two);
90+
91+
enum f { three };
92+
enum f x = __builtin_elementwise_min(one, three);
93+
94+
_ExtInt(32) ext;
95+
ext = __builtin_elementwise_min(ext, ext);
96+
97+
const int ci;
98+
i = __builtin_elementwise_min(ci, i);
99+
i = __builtin_elementwise_min(i, ci);
100+
i = __builtin_elementwise_min(ci, ci);
101+
102+
i = __builtin_elementwise_min(i, int_as_one); // ok (attributes don't match)?
103+
i = __builtin_elementwise_min(i, b); // ok (sugar doesn't match)?
104+
105+
int A[10];
106+
A = __builtin_elementwise_min(A, A);
107+
// expected-error@-1 {{1st argument must be a vector, integer or floating point type (was 'int *')}}
108+
109+
int(ii);
110+
int j;
111+
j = __builtin_elementwise_min(i, j);
112+
113+
_Complex float c1, c2;
114+
c1 = __builtin_elementwise_min(c1, c2);
115+
// expected-error@-1 {{1st argument must be a vector, integer or floating point type (was '_Complex float')}}
116+
}

0 commit comments

Comments
 (0)