Skip to content
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,9 @@ Bug Fixes in This Version
calls another function that requires target features not enabled in the caller. This
prevents a fatal error in the backend.
- Fixed scope of typedefs present inside a template class. (#GH91451)
- Builtin elementwise operators now accept vector arguments that have different
qualifiers on their elements. For example, vector of 4 ``const float`` values
and vector of 4 ``float`` values. (#GH155405)

Bug Fixes to Compiler Builtins
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
65 changes: 52 additions & 13 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15884,6 +15884,54 @@ static bool checkBuiltinVectorMathMixedEnums(Sema &S, Expr *LHS, Expr *RHS,
return false;
}

/// Check if all arguments have the same type. If the types don't match, emit an
/// error message and return true. Otherwise return false.
///
/// For scalars we directly compare their unqualified types. But even if we
/// compare unqualified vector types, a difference in qualifiers in the element
/// types can make the vector types be considered not equal. For example,
/// vector of 4 'const float' values vs vector of 4 'float' values.
/// So we compare unqualified types of their elements and number of elements.
static bool checkBuiltinVectorMathArgTypes(Sema &SemaRef,
ArrayRef<Expr *> Args) {
assert(!Args.empty() && "Should have at least one argument.");

Expr *Arg0 = Args.front();
QualType Ty0 = Arg0->getType();

auto EmitError = [&](Expr *ArgI) {
SemaRef.Diag(Arg0->getBeginLoc(),
diag::err_typecheck_call_different_arg_types)
<< Arg0->getType() << ArgI->getType();
};

// Compare scalar types.
if (!Ty0->isVectorType()) {
for (Expr *ArgI : Args.drop_front())
if (!SemaRef.Context.hasSameUnqualifiedType(Ty0, ArgI->getType())) {
EmitError(ArgI);
return true;
}

return false;
}

// Compare vector types.
const auto *Vec0 = Ty0->castAs<VectorType>();
for (Expr *ArgI : Args.drop_front()) {
const auto *VecI = ArgI->getType()->getAs<VectorType>();
if (!VecI ||
!SemaRef.Context.hasSameUnqualifiedType(Vec0->getElementType(),
VecI->getElementType()) ||
Vec0->getNumElements() != VecI->getNumElements()) {
EmitError(ArgI);
return true;
}
}

return false;
}

std::optional<QualType>
Sema::BuiltinVectorMath(CallExpr *TheCall,
EltwiseBuiltinArgTyRestriction ArgTyRestr) {
Expand All @@ -15905,15 +15953,12 @@ Sema::BuiltinVectorMath(CallExpr *TheCall,

SourceLocation LocA = Args[0]->getBeginLoc();
QualType TyA = Args[0]->getType();
QualType TyB = Args[1]->getType();

if (checkMathBuiltinElementType(*this, LocA, TyA, ArgTyRestr, 1))
return std::nullopt;

if (!Context.hasSameUnqualifiedType(TyA, TyB)) {
Diag(LocA, diag::err_typecheck_call_different_arg_types) << TyA << TyB;
if (checkBuiltinVectorMathArgTypes(*this, Args))
return std::nullopt;
}

TheCall->setArg(0, Args[0]);
TheCall->setArg(1, Args[1]);
Expand Down Expand Up @@ -15948,17 +15993,11 @@ bool Sema::BuiltinElementwiseTernaryMath(
return true;
}

TheCall->setArg(0, Args[0]);
for (int I = 1; I < 3; ++I) {
if (Args[0]->getType().getCanonicalType() !=
Args[I]->getType().getCanonicalType()) {
return Diag(Args[0]->getBeginLoc(),
diag::err_typecheck_call_different_arg_types)
<< Args[0]->getType() << Args[I]->getType();
}
if (checkBuiltinVectorMathArgTypes(*this, Args))
return true;

for (int I = 0; I < 3; ++I)
TheCall->setArg(I, Args[I]);
}

TheCall->setType(Args[0]->getType());
return false;
Expand Down
21 changes: 19 additions & 2 deletions clang/test/Sema/builtins-elementwise-math.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ typedef double double4 __attribute__((ext_vector_type(4)));
typedef float float2 __attribute__((ext_vector_type(2)));
typedef float float3 __attribute__((ext_vector_type(3)));
typedef float float4 __attribute__((ext_vector_type(4)));
typedef const float cfloat4 __attribute__((ext_vector_type(4)));

typedef int int2 __attribute__((ext_vector_type(2)));
typedef int int3 __attribute__((ext_vector_type(3)));
Expand Down Expand Up @@ -1330,16 +1331,32 @@ void test_builtin_elementwise_fsh(int i32, int2 v2i32, short i16, int3 v3i32,
// expected-error@-1 {{arguments are of different types ('int3' (vector of 3 'int' values) vs 'int2' (vector of 2 'int' values))}}
}

// Tests corresponding to GitHub issues #141397 and #155405.
// Type mismatch error when 'builtin-elementwise-math' arguments have
// different qualifiers, this should be well-formed.
typedef struct {
float3 b;
} struct_float3;
// This example uncovered a bug #141397 :
// Type mismatch error when 'builtin-elementwise-math' arguments have different qualifiers, this should be well-formed

float3 foo(float3 a,const struct_float3* hi) {
float3 b = __builtin_elementwise_max((float3)(0.0f), a);
return __builtin_elementwise_pow(b, hi->b.yyy);
}

float3 baz(float3 a, const struct_float3* hi) {
return __builtin_elementwise_fma(a, a, hi->b);
}

cfloat4 qux(cfloat4 x, float4 y, float4 z) {
float a = __builtin_elementwise_fma(x[0],y[0],z[0]);
return __builtin_elementwise_fma(x,y,z);
}

cfloat4 quux(cfloat4 x, float4 y) {
float a = __builtin_elementwise_pow(x[0],y[0]);
return __builtin_elementwise_pow(x,y);
}

void test_builtin_elementwise_ctlz(int i32, int2 v2i32, short i16,
double f64, double2 v2f64) {
f64 = __builtin_elementwise_ctlz(f64);
Expand Down