diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index aba00dc8ff9b6..934c7ee660e12 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -9385,11 +9385,21 @@ AssignConvertType Sema::CheckAssignmentConstraints(QualType LHSType, return AssignConvertType::Incompatible; } - // Allow scalar to ExtVector assignments, and assignments of an ExtVector type - // to the same ExtVector type. - if (LHSType->isExtVectorType()) { - if (RHSType->isExtVectorType()) + // Allow scalar to ExtVector assignments, assignment to bool, and assignments + // of an ExtVector type to the same ExtVector type. + if (auto *LHSExtType = LHSType->getAs()) { + if (auto *RHSExtType = RHSType->getAs()) { + // Implicit conversions require the same number of elements. + if (LHSExtType->getNumElements() != RHSExtType->getNumElements()) + return AssignConvertType::Incompatible; + + if (LHSType->isExtVectorBoolType() && + RHSExtType->getElementType()->isIntegerType()) { + Kind = CK_IntegralToBoolean; + return AssignConvertType::Compatible; + } return AssignConvertType::Incompatible; + } if (RHSType->isArithmeticType()) { // CK_VectorSplat does T -> vector T, so first cast to the element type. if (ConvertRHS) diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 941542247e240..ea5c4265d736d 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -2162,9 +2162,18 @@ static bool IsVectorConversion(Sema &S, QualType FromType, QualType ToType, // There are no conversions between extended vector types, only identity. if (auto *ToExtType = ToType->getAs()) { - if (FromType->getAs()) { - // There are no conversions between extended vector types other than the - // identity conversion. + if (auto *FromExtType = FromType->getAs()) { + // Implicit conversions require the same number of elements. + if (ToExtType->getNumElements() != FromExtType->getNumElements()) + return false; + + // Permit implicit conversions from integral values to boolean vectors. + if (ToType->isExtVectorBoolType() && + FromExtType->getElementType()->isIntegerType()) { + ICK = ICK_Boolean_Conversion; + return true; + } + // There are no other conversions between extended vector types. return false; } diff --git a/clang/test/CodeGen/vector-convert-boolean.cpp b/clang/test/CodeGen/vector-convert-boolean.cpp new file mode 100644 index 0000000000000..f47f608833645 --- /dev/null +++ b/clang/test/CodeGen/vector-convert-boolean.cpp @@ -0,0 +1,70 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6 +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu %s -emit-llvm -o - | FileCheck %s + +using v1i = int [[clang::ext_vector_type(1)]]; +using v1b = bool [[clang::ext_vector_type(1)]]; +using v8i = int [[clang::ext_vector_type(8)]]; +using v8b = bool [[clang::ext_vector_type(8)]]; +using v16i = short [[clang::ext_vector_type(16)]]; +using v16b = bool [[clang::ext_vector_type(16)]]; +using v32i = char [[clang::ext_vector_type(32)]]; +using v32b = bool [[clang::ext_vector_type(32)]]; + +// CHECK-LABEL: define dso_local noundef i8 @_Z3fooDv1_i( +// CHECK-SAME: i32 noundef [[V_COERCE:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca <1 x i1>, align 1 +// CHECK-NEXT: [[V:%.*]] = alloca <1 x i32>, align 4 +// CHECK-NEXT: [[V_ADDR:%.*]] = alloca <1 x i32>, align 4 +// CHECK-NEXT: store i32 [[V_COERCE]], ptr [[V]], align 4 +// CHECK-NEXT: [[V1:%.*]] = load <1 x i32>, ptr [[V]], align 4 +// CHECK-NEXT: store <1 x i32> [[V1]], ptr [[V_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load <1 x i32>, ptr [[V_ADDR]], align 4 +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne <1 x i32> [[TMP0]], zeroinitializer +// CHECK-NEXT: store <1 x i1> [[TOBOOL]], ptr [[RETVAL]], align 1 +// CHECK-NEXT: [[TMP1:%.*]] = load i8, ptr [[RETVAL]], align 1 +// CHECK-NEXT: ret i8 [[TMP1]] +// +v1b foo(v1i v) { return v; } +// CHECK-LABEL: define dso_local noundef i8 @_Z3fooDv8_i( +// CHECK-SAME: ptr noundef byval(<8 x i32>) align 32 [[TMP0:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca <8 x i1>, align 1 +// CHECK-NEXT: [[V_ADDR:%.*]] = alloca <8 x i32>, align 32 +// CHECK-NEXT: [[V:%.*]] = load <8 x i32>, ptr [[TMP0]], align 32 +// CHECK-NEXT: store <8 x i32> [[V]], ptr [[V_ADDR]], align 32 +// CHECK-NEXT: [[TMP1:%.*]] = load <8 x i32>, ptr [[V_ADDR]], align 32 +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne <8 x i32> [[TMP1]], zeroinitializer +// CHECK-NEXT: store <8 x i1> [[TOBOOL]], ptr [[RETVAL]], align 1 +// CHECK-NEXT: [[TMP2:%.*]] = load i8, ptr [[RETVAL]], align 1 +// CHECK-NEXT: ret i8 [[TMP2]] +// +v8b foo(v8i v) { return v; } +// CHECK-LABEL: define dso_local noundef i16 @_Z3fooDv16_s( +// CHECK-SAME: ptr noundef byval(<16 x i16>) align 32 [[TMP0:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca <16 x i1>, align 2 +// CHECK-NEXT: [[V_ADDR:%.*]] = alloca <16 x i16>, align 32 +// CHECK-NEXT: [[V:%.*]] = load <16 x i16>, ptr [[TMP0]], align 32 +// CHECK-NEXT: store <16 x i16> [[V]], ptr [[V_ADDR]], align 32 +// CHECK-NEXT: [[TMP1:%.*]] = load <16 x i16>, ptr [[V_ADDR]], align 32 +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne <16 x i16> [[TMP1]], zeroinitializer +// CHECK-NEXT: store <16 x i1> [[TOBOOL]], ptr [[RETVAL]], align 2 +// CHECK-NEXT: [[TMP2:%.*]] = load i16, ptr [[RETVAL]], align 2 +// CHECK-NEXT: ret i16 [[TMP2]] +// +v16b foo(v16i v) { return v; } +// CHECK-LABEL: define dso_local noundef i32 @_Z3fooDv32_c( +// CHECK-SAME: ptr noundef byval(<32 x i8>) align 32 [[TMP0:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca <32 x i1>, align 4 +// CHECK-NEXT: [[V_ADDR:%.*]] = alloca <32 x i8>, align 32 +// CHECK-NEXT: [[V:%.*]] = load <32 x i8>, ptr [[TMP0]], align 32 +// CHECK-NEXT: store <32 x i8> [[V]], ptr [[V_ADDR]], align 32 +// CHECK-NEXT: [[TMP1:%.*]] = load <32 x i8>, ptr [[V_ADDR]], align 32 +// CHECK-NEXT: [[TOBOOL:%.*]] = icmp ne <32 x i8> [[TMP1]], zeroinitializer +// CHECK-NEXT: store <32 x i1> [[TOBOOL]], ptr [[RETVAL]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[RETVAL]], align 4 +// CHECK-NEXT: ret i32 [[TMP2]] +// +v32b foo(v32i v) { return v; } diff --git a/clang/test/Sema/vector-bool-assign.c b/clang/test/Sema/vector-bool-assign.c new file mode 100644 index 0000000000000..8f9d7b17a1ecb --- /dev/null +++ b/clang/test/Sema/vector-bool-assign.c @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -triple x86_64 -fsyntax-only -verify %s + +typedef _Bool bool; + +typedef __attribute__((ext_vector_type(8))) int v8i; +typedef __attribute__((ext_vector_type(8))) bool v8b; +typedef __attribute__((ext_vector_type(4))) float v4f; +typedef __attribute__((ext_vector_type(4))) bool v4b; + +void foo(v8b); + +v8b integral(v8i v) { + v8b m1 = __builtin_convertvector(v, __attribute__((ext_vector_type(8))) int); + v8b m2 = __builtin_convertvector(v, __attribute__((ext_vector_type(8))) unsigned); + v8b m3 = __builtin_convertvector(v, __attribute__((ext_vector_type(8))) long); + v8b m4 = __builtin_convertvector(v, __attribute__((ext_vector_type(8))) unsigned long); + v8b m5 = __builtin_convertvector(v, __attribute__((ext_vector_type(8))) char); + v8b m6 = __builtin_convertvector(v, __attribute__((ext_vector_type(8))) unsigned char); + foo(v); + return v; +} + +v4b non_integral(v4f vf) { + return vf; // expected-error{{returning 'v4f' (vector of 4 'float' values) from a function with incompatible result type 'v4b' (vector of 4 'bool' values}} +} + +v4b size_mismatch(v8i v) { + return v; // expected-error{{returning 'v8i' (vector of 8 'int' values) from a function with incompatible result type 'v4b' (vector of 4 'bool' values)}} +} diff --git a/clang/test/Sema/vector-bool-assign.cpp b/clang/test/Sema/vector-bool-assign.cpp new file mode 100644 index 0000000000000..115ec50f6b448 --- /dev/null +++ b/clang/test/Sema/vector-bool-assign.cpp @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple x86_64 -fsyntax-only -verify %s + +using v8i = int [[clang::ext_vector_type(8)]]; +using v8b = bool [[clang::ext_vector_type(8)]]; +using v4f = float [[clang::ext_vector_type(4)]]; +using v4b = bool [[clang::ext_vector_type(4)]]; + +void foo(v8b); + +v8b integral(v8i v) { + v8b m1 = __builtin_convertvector(v, int [[clang::ext_vector_type(8)]]); + v8b m2 = __builtin_convertvector(v, unsigned [[clang::ext_vector_type(8)]]); + v8b m3 = __builtin_convertvector(v, long [[clang::ext_vector_type(8)]]); + v8b m4 = __builtin_convertvector(v, unsigned long [[clang::ext_vector_type(8)]]); + v8b m5 = __builtin_convertvector(v, char [[clang::ext_vector_type(8)]]); + v8b m6 = __builtin_convertvector(v, unsigned char [[clang::ext_vector_type(8)]]); + foo(v); + return v; +} + +v4b non_integral(v4f vf) { + return vf; // expected-error{{cannot initialize return object of type 'v4b' (vector of 4 'bool' values) with an lvalue of type 'v4f' (vector of 4 'float' values)}} +} + +v4b size_mismatch(v8i v) { + return v; // expected-error{{cannot initialize return object of type 'v4b' (vector of 4 'bool' values) with an lvalue of type 'v8i' (vector of 8 'int' values)}} +}