diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1479,6 +1479,11 @@ let ASTNode = 0; } +def ArmMveStrictPolymorphism : TypeAttr, TargetSpecificAttr { + let Spellings = [Clang<"arm_mve_strict_polymorphism">]; + let Documentation = [ArmMveStrictPolymorphismDocs]; +} + def NoUniqueAddress : InheritableAttr, TargetSpecificAttr { let Spellings = [CXX11<"", "no_unique_address", 201803>]; let Subjects = SubjectList<[NonBitField], ErrorDiag>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -4789,3 +4789,45 @@ zx_status_t zx_handle_close(zx_handle_t handle [[clang::release_handle]]); }]; } + +def ArmMveStrictPolymorphismDocs : Documentation { + let Category = DocCatType; + let Content = [{ +This attribute is used in the implementation of the ACLE intrinsics for the Arm +MVE instruction set. It is used to define the vector types used by the MVE +intrinsics. + +Its effect is to modify the behavior of a vector type with respect to function +overloading. If a candidate function for overload resolution has a parameter +type with this attribute, then the selection of that candidate function will be +disallowed if the actual argument can only be converted via a lax vector +conversion. The aim is to prevent spurious ambiguity in ARM MVE polymorphic +intrinsics. + +.. code-block:: c++ + + void overloaded(uint16x8_t vector, uint16_t scalar); + void overloaded(int32x4_t vector, int32_t scalar); + uint16x8_t myVector; + uint16_t myScalar; + + // myScalar is promoted to int32_t as a side effect of the addition, + // so if lax vector conversions are considered for myVector, then + // the two overloads are equally good (one argument conversion + // each). But if the vector has the arm_mve_strict_polymorphism + // attribute, only the uint16x8_t,uint16_t overload will match. + overloaded(myVector, myScalar + 1); + +However, this attribute does not prohibit lax vector conversions in contexts +other than overloading. + +.. code-block:: c++ + + uint16x8_t function(); + + // This is still permitted with lax vector conversion enabled, even + // if the vector types have arm_mve_strict_polymorphism + int32x4_t result = function(); + + }]; +} diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -6582,6 +6582,8 @@ "method %0 that returns %1 declared here">; def err_attribute_arm_mve_alias : Error< "'__clang_arm_mve_alias' attribute can only be applied to an ARM MVE builtin">; +def err_attribute_arm_mve_polymorphism : Error< + "'arm_mve_strict_polymorphism' attribute can only be applied to an MVE/NEON vector type">; def warn_setter_getter_impl_required : Warning< "property %0 requires method %1 to be defined - " diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1558,6 +1558,9 @@ case attr::AcquireHandle: OS << "acquire_handle"; break; + case attr::ArmMveStrictPolymorphism: + OS << "arm_mve_strict_polymorphism"; + break; } OS << "))"; } diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -1653,9 +1653,13 @@ // 1)vector types are equivalent AltiVec and GCC vector types // 2)lax vector conversions are permitted and the vector types are of the // same size + // 3)the destination type does not have the ARM MVE strict-polymorphism + // attribute, which inhibits lax vector conversion for overload resolution + // only if (ToType->isVectorType() && FromType->isVectorType()) { if (S.Context.areCompatibleVectorTypes(FromType, ToType) || - S.isLaxVectorConversion(FromType, ToType)) { + (S.isLaxVectorConversion(FromType, ToType) && + !ToType->hasAttr(attr::ArmMveStrictPolymorphism))) { ICK = ICK_Vector_Conversion; return true; } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -7359,6 +7359,23 @@ CurType = S.Context.getVectorType(CurType, numElts, VecKind); } +static void HandleArmMveStrictPolymorphismAttr(TypeProcessingState &State, + QualType &CurType, + ParsedAttr &Attr) { + const VectorType *VT = dyn_cast(CurType); + if (!VT || VT->getVectorKind() != VectorType::NeonVector) { + State.getSema().Diag(Attr.getLoc(), + diag::err_attribute_arm_mve_polymorphism); + Attr.setInvalid(); + return; + } + + CurType = + State.getAttributedType(createSimpleAttr( + State.getSema().Context, Attr), + CurType, CurType); +} + /// Handle OpenCL Access Qualifier Attribute. static void HandleOpenCLAccessAttr(QualType &CurType, const ParsedAttr &Attr, Sema &S) { @@ -7543,6 +7560,11 @@ VectorType::NeonPolyVector); attr.setUsedAsTypeAttr(); break; + case ParsedAttr::AT_ArmMveStrictPolymorphism: { + HandleArmMveStrictPolymorphismAttr(state, type, attr); + attr.setUsedAsTypeAttr(); + break; + } case ParsedAttr::AT_OpenCLAccess: HandleOpenCLAccessAttr(type, attr, state.getSema()); attr.setUsedAsTypeAttr(); diff --git a/clang/test/Sema/overload-arm-mve.c b/clang/test/Sema/overload-arm-mve.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/overload-arm-mve.c @@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -flax-vector-conversions=all -Werror -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple thumbv8.1m.main-arm-none-eabi -target-feature +mve.fp -flax-vector-conversions=all -verify -fsyntax-only -DERROR_CHECK %s + +typedef signed short int16_t; +typedef signed int int32_t; +typedef signed long long int64_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +typedef __attribute__((neon_vector_type(8), arm_mve_strict_polymorphism)) int16_t int16x8_t; +typedef __attribute__((neon_vector_type(4), arm_mve_strict_polymorphism)) int32_t int32x4_t; +typedef __attribute__((neon_vector_type(2), arm_mve_strict_polymorphism)) int64_t int64x2_t; +typedef __attribute__((neon_vector_type(8), arm_mve_strict_polymorphism)) uint16_t uint16x8_t; +typedef __attribute__((neon_vector_type(4), arm_mve_strict_polymorphism)) uint32_t uint32x4_t; +typedef __attribute__((neon_vector_type(2), arm_mve_strict_polymorphism)) uint64_t uint64x2_t; + +__attribute__((overloadable)) +int overload(int16x8_t x, int16_t y); // expected-note {{candidate function}} +__attribute__((overloadable)) +int overload(int32x4_t x, int32_t y); // expected-note {{candidate function}} +__attribute__((overloadable)) +int overload(uint16x8_t x, uint16_t y); // expected-note {{candidate function}} +__attribute__((overloadable)) +int overload(uint32x4_t x, uint32_t y); // expected-note {{candidate function}} + +int16_t s16; +int32_t s32; +uint16_t u16; +uint32_t u32; + +int16x8_t vs16; +int32x4_t vs32; +uint16x8_t vu16; +uint32x4_t vu32; + +// ---------------------------------------------------------------------- +// Simple cases where the types are correctly matched + +// CHECK-LABEL: @test_easy_s16( +// CHECK: call i32 @_Z8overload{{[a-zA-Z0-9_]+}}_int16 +int test_easy_s16(void) { return overload(vs16, s16); } + +// CHECK-LABEL: @test_easy_u16( +// CHECK: call i32 @_Z8overload{{[a-zA-Z0-9_]+}}_uint16 +int test_easy_u16(void) { return overload(vu16, u16); } + +// CHECK-LABEL: @test_easy_s32( +// CHECK: call i32 @_Z8overload{{[a-zA-Z0-9_]+}}_int32 +int test_easy_s32(void) { return overload(vs32, s32); } + +// CHECK-LABEL: @test_easy_u32( +// CHECK: call i32 @_Z8overload{{[a-zA-Z0-9_]+}}_uint32 +int test_easy_u32(void) { return overload(vu32, u32); } + +// ---------------------------------------------------------------------- +// Do arithmetic on the scalar, and it may get promoted. We still expect the +// same overloads to be selected if that happens. + +// CHECK-LABEL: @test_promote_s16( +// CHECK: call i32 @_Z8overload{{[a-zA-Z0-9_]+}}_int16 +int test_promote_s16(void) { return overload(vs16, s16 + 1); } + +// CHECK-LABEL: @test_promote_u16( +// CHECK: call i32 @_Z8overload{{[a-zA-Z0-9_]+}}_uint16 +int test_promote_u16(void) { return overload(vu16, u16 + 1); } + +// CHECK-LABEL: @test_promote_s32( +// CHECK: call i32 @_Z8overload{{[a-zA-Z0-9_]+}}_int32 +int test_promote_s32(void) { return overload(vs32, s32 + 1); } + +// CHECK-LABEL: @test_promote_u32( +// CHECK: call i32 @_Z8overload{{[a-zA-Z0-9_]+}}_uint32 +int test_promote_u32(void) { return overload(vu32, u32 + 1); } + +// ---------------------------------------------------------------------- +// Write a simple integer literal without qualification, and expect +// the vector type to make it unambiguous which integer type you meant +// the literal to be. + +// CHECK-LABEL: @test_literal_s16( +// CHECK: call i32 @_Z8overload{{[a-zA-Z0-9_]+}}_int16 +int test_literal_s16(void) { return overload(vs16, 1); } + +// CHECK-LABEL: @test_literal_u16( +// CHECK: call i32 @_Z8overload{{[a-zA-Z0-9_]+}}_uint16 +int test_literal_u16(void) { return overload(vu16, 1); } + +// CHECK-LABEL: @test_literal_s32( +// CHECK: call i32 @_Z8overload{{[a-zA-Z0-9_]+}}_int32 +int test_literal_s32(void) { return overload(vs32, 1); } + +// CHECK-LABEL: @test_literal_u32( +// CHECK: call i32 @_Z8overload{{[a-zA-Z0-9_]+}}_uint32 +int test_literal_u32(void) { return overload(vu32, 1); } + +// ---------------------------------------------------------------------- +// All of those overload resolutions are supposed to be unambiguous even when +// lax vector conversion is enabled. Check here that a lax conversion in a +// different context still works. +int16x8_t lax_conversion(void) { return vu32; } + +// ---------------------------------------------------------------------- +// Use a vector type that there really _isn't_ any overload for, and +// make sure that we get a fatal compile error. + +#ifdef ERROR_CHECK +int expect_error(uint64x2_t v) { + return overload(v, 2); // expected-error {{no matching function for call to 'overload'}} +} + +typedef __attribute__((arm_mve_strict_polymorphism)) int i; // expected-error {{'arm_mve_strict_polymorphism' attribute can only be applied to an MVE/NEON vector type}} +typedef __attribute__((arm_mve_strict_polymorphism)) int f(); // expected-error {{'arm_mve_strict_polymorphism' attribute can only be applied to an MVE/NEON vector type}} +typedef __attribute__((arm_mve_strict_polymorphism)) struct { uint16x8_t v; } s; // expected-error {{'arm_mve_strict_polymorphism' attribute can only be applied to an MVE/NEON vector type}} +#endif diff --git a/clang/utils/TableGen/MveEmitter.cpp b/clang/utils/TableGen/MveEmitter.cpp --- a/clang/utils/TableGen/MveEmitter.cpp +++ b/clang/utils/TableGen/MveEmitter.cpp @@ -1454,8 +1454,9 @@ raw_ostream &OS = parts[ST->requiresFloat() ? Float : 0]; const VectorType *VT = getVectorType(ST); - OS << "typedef __attribute__((neon_vector_type(" << VT->lanes() << "))) " - << ST->cName() << " " << VT->cName() << ";\n"; + OS << "typedef __attribute__((__neon_vector_type__(" << VT->lanes() + << "), __arm_mve_strict_polymorphism__)) " << ST->cName() << " " + << VT->cName() << ";\n"; // Every vector type also comes with a pair of multi-vector types for // the VLD2 and VLD4 instructions.