https://github.com/Pierre-vh updated https://github.com/llvm/llvm-project/pull/207687
>From 0efe9d7a5777dced26a75ebebc012499a88a10bf Mon Sep 17 00:00:00 2001 From: pvanhout <[email protected]> Date: Mon, 6 Jul 2026 11:33:34 +0200 Subject: [PATCH 1/7] [clang][AMDGPU] Clean-up handling of named barrier type - Do not allow the type in struct fields. This is more like a handle/resource than a real type. It does not follow the traditional C++ object model, and using it in a struct field can do some weird things if you instantiate too many of them. - Use a `hip_barrier` LangAS for this type that currently maps to the local AS. This allows easy switching to the barrier AS in a future patch. Alternative to #195612, see also #195613 --- clang/include/clang/AST/TypeBase.h | 3 ++ clang/include/clang/Basic/AddressSpaces.h | 3 ++ .../clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/lib/AST/Type.cpp | 14 ++++++- clang/lib/AST/TypePrinter.cpp | 2 + clang/lib/Basic/TargetInfo.cpp | 1 + clang/lib/Basic/Targets/AArch64.h | 1 + clang/lib/Basic/Targets/AMDGPU.cpp | 2 + clang/lib/Basic/Targets/DirectX.h | 1 + clang/lib/Basic/Targets/NVPTX.h | 1 + clang/lib/Basic/Targets/SPIR.h | 2 + clang/lib/Basic/Targets/SystemZ.h | 3 +- clang/lib/Basic/Targets/TCE.h | 1 + clang/lib/Basic/Targets/WebAssembly.h | 1 + clang/lib/Basic/Targets/X86.h | 1 + clang/lib/CodeGen/CodeGenModule.cpp | 6 +++ clang/lib/Sema/SemaDecl.cpp | 10 ++++- clang/test/CodeGenHIP/amdgpu-barrier-type.hip | 37 ++++++++++++------- clang/test/SemaCXX/amdgpu-barrier.cpp | 4 ++ clang/test/SemaHIP/amdgpu-barrier.hip | 5 +++ clang/test/SemaOpenCL/amdgpu-barrier.cl | 5 +++ .../SemaTemplate/address_space-dependent.cpp | 4 +- 22 files changed, 89 insertions(+), 20 deletions(-) diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index 3a801e2857b13..48612598735b9 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -2813,6 +2813,9 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { /// Check if the type is the CUDA device builtin texture type. bool isCUDADeviceBuiltinTextureType() const; + /// Check if the type is the AMDGPU named barrier type. + bool isAMDGPUNamedBarrierType() const; + /// Return the implicit lifetime for this type, which must not be dependent. Qualifiers::ObjCLifetime getObjCARCImplicitLifetime() const; diff --git a/clang/include/clang/Basic/AddressSpaces.h b/clang/include/clang/Basic/AddressSpaces.h index a941805423bca..d16fc8069ae43 100644 --- a/clang/include/clang/Basic/AddressSpaces.h +++ b/clang/include/clang/Basic/AddressSpaces.h @@ -68,6 +68,9 @@ enum class LangAS : unsigned { // Wasm specific address spaces. wasm_funcref, + // HIP-specific address spaces + hip_barrier, + // This denotes the count of language-specific address spaces and also // the offset added to the target-specific address spaces, which are usually // specified by address space attributes __attribute__(address_space(n))). diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 86b765fdf1fab..437f0accbb8b5 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11855,7 +11855,7 @@ def note_within_param_of_type : Note< "within parameter %0 of type %1 declared here">; def note_illegal_field_declared_here : Note< "field of illegal %select{type|pointer type}0 %1 declared here">; -def err_opencl_type_struct_or_union_field : Error< +def err_invalid_type_for_struct_or_union_field : Error< "the %0 type cannot be used to declare a structure or union field">; def err_event_t_addr_space_qual : Error< "the event_t type can only be used with __private address space qualifier">; diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 19b85d0e0af69..51417d553f29f 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -93,7 +93,7 @@ bool Qualifiers::isTargetAddressSpaceSupersetOf(LangAS A, LangAS B, // to implicitly cast into the default address space. (A == LangAS::Default && (B == LangAS::cuda_constant || B == LangAS::cuda_device || - B == LangAS::cuda_shared)) || + B == LangAS::cuda_shared || B == LangAS::hip_barrier)) || // In HLSL, the this pointer for member functions points to the default // address space. This causes a problem if the structure is in // a different address space. We want to allow casting from these @@ -5492,6 +5492,18 @@ bool Type::isCUDADeviceBuiltinTextureType() const { return false; } +bool Type::isAMDGPUNamedBarrierType() const { + const Type *Ty = getUnqualifiedDesugaredType(); + + // unwrap arrays + while (isa<ArrayType>(Ty)) + Ty = Ty->getArrayElementTypeNoTypeQual(); + + if (const auto *BT = dyn_cast<BuiltinType>(Ty->getUnqualifiedDesugaredType())) + return BT->getKind() == BuiltinType::AMDGPUNamedWorkgroupBarrier; + return false; +} + bool Type::hasSizedVLAType() const { if (!isVariablyModifiedType()) return false; diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index e8fbffb9f954d..e467397d9df52 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -2748,6 +2748,8 @@ std::string Qualifiers::getAddrSpaceAsString(LangAS AS) { return "hlsl_push_constant"; case LangAS::wasm_funcref: return "__funcref"; + case LangAS::hip_barrier: + return "hip_barrier"; default: return std::to_string(toTargetAddressSpace(AS)); } diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 9a25384347073..5e8871ace9a60 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -55,6 +55,7 @@ static const LangASMap FakeAddrSpaceMap = { 18, // hlsl_output 19, // hlsl_push_constant 20, // wasm_funcref + 21, // hip_barrier }; // TargetInfo Constructor. diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h index fc8e0e6071711..2974ddf551398 100644 --- a/clang/lib/Basic/Targets/AArch64.h +++ b/clang/lib/Basic/Targets/AArch64.h @@ -54,6 +54,7 @@ static const unsigned ARM64AddrSpaceMap[] = { // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref + 0, // hip_barrier }; using AArch64FeatureSet = llvm::SmallDenseSet<StringRef, 32>; diff --git a/clang/lib/Basic/Targets/AMDGPU.cpp b/clang/lib/Basic/Targets/AMDGPU.cpp index 50f9c1aa1aa02..1619d2bbfb8b7 100644 --- a/clang/lib/Basic/Targets/AMDGPU.cpp +++ b/clang/lib/Basic/Targets/AMDGPU.cpp @@ -56,6 +56,8 @@ const LangASMap AMDGPUTargetInfo::AMDGPUAddrSpaceMap = { llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_input llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_output llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_push_constant + llvm::AMDGPUAS::FLAT_ADDRESS, // wasm_funcref + llvm::AMDGPUAS::LOCAL_ADDRESS, // hip_barrier }; } // namespace targets diff --git a/clang/lib/Basic/Targets/DirectX.h b/clang/lib/Basic/Targets/DirectX.h index 8b21b86bac264..6c4721c22e9ee 100644 --- a/clang/lib/Basic/Targets/DirectX.h +++ b/clang/lib/Basic/Targets/DirectX.h @@ -51,6 +51,7 @@ static const unsigned DirectXAddrSpaceMap[] = { // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref + 0, // hip_barrier }; class LLVM_LIBRARY_VISIBILITY DirectXTargetInfo : public TargetInfo { diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h index 8af9cb91c47ab..8f3638578b264 100644 --- a/clang/lib/Basic/Targets/NVPTX.h +++ b/clang/lib/Basic/Targets/NVPTX.h @@ -55,6 +55,7 @@ static const unsigned NVPTXAddrSpaceMap[] = { // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref + 0, // hip_barrier }; /// The DWARF address class. Taken from diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h index 389cc075a3a0b..5ffd427c77f91 100644 --- a/clang/lib/Basic/Targets/SPIR.h +++ b/clang/lib/Basic/Targets/SPIR.h @@ -59,6 +59,7 @@ static const unsigned SPIRDefIsPrivMap[] = { // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref + 0, // hip_barrier }; // Used by both the SPIR and SPIR-V targets. @@ -97,6 +98,7 @@ static const unsigned SPIRDefIsGenMap[] = { // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref + 0, // hip_barrier }; // Base class for SPIR and SPIR-V target info. diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h index 70b529e8ca854..bb8fa1a1b3dec 100644 --- a/clang/lib/Basic/Targets/SystemZ.h +++ b/clang/lib/Basic/Targets/SystemZ.h @@ -48,7 +48,8 @@ static const unsigned ZOSAddressMap[] = { 0, // hlsl_input 0, // hlsl_output 0, // hlsl_push_constant - 0 // wasm_funcref + 0, // wasm_funcref + 0, // hip_barrier }; class LLVM_LIBRARY_VISIBILITY SystemZTargetInfo : public TargetInfo { diff --git a/clang/lib/Basic/Targets/TCE.h b/clang/lib/Basic/Targets/TCE.h index 1360298de9794..95a17e0a9356f 100644 --- a/clang/lib/Basic/Targets/TCE.h +++ b/clang/lib/Basic/Targets/TCE.h @@ -60,6 +60,7 @@ static const unsigned TCEOpenCLAddrSpaceMap[] = { // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref + 0, // hip_barrier }; class LLVM_LIBRARY_VISIBILITY TCETargetInfo : public TargetInfo { diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h index 88192a756cc4f..ab498a082302b 100644 --- a/clang/lib/Basic/Targets/WebAssembly.h +++ b/clang/lib/Basic/Targets/WebAssembly.h @@ -49,6 +49,7 @@ static const unsigned WebAssemblyAddrSpaceMap[] = { 0, // hlsl_output 0, // hlsl_push_constant 20, // wasm_funcref + 0, // hip_barrier }; class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo { diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h index b0daffbea4576..e426b9113f38a 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -55,6 +55,7 @@ static const unsigned X86AddrSpaceMap[] = { // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref + 0, // hip_barrier }; // X86 target abstract base class; x86-32 and x86-64 are very close, so diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index e155fdd752d7f..7fd54c6c34cb7 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -6171,6 +6171,12 @@ LangAS CodeGenModule::GetGlobalVarAddressSpace(const VarDecl *D) { if (LangOpts.CUDA && LangOpts.CUDAIsDevice) { if (D) { + // TOOD: Forbid type on struct fields + // llvm::dbgs() << D->getNameAsString() << ": " << + // D->getType()->isAMDGPUNamedBarrierType() << "\n"; + if (D->getType()->isAMDGPUNamedBarrierType()) + return LangAS::hip_barrier; + if (D->hasAttr<CUDAConstantAttr>()) return LangAS::cuda_constant; if (D->hasAttr<CUDASharedAttr>()) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 9948a633d7e98..b77305d575e67 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -19435,7 +19435,7 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, // used as structure or union field: image, sampler, event or block types. if (T->isEventT() || T->isImageType() || T->isSamplerT() || T->isBlockPointerType()) { - Diag(Loc, diag::err_opencl_type_struct_or_union_field) << T; + Diag(Loc, diag::err_invalid_type_for_struct_or_union_field) << T; Record->setInvalidDecl(); InvalidDecl = true; } @@ -19448,6 +19448,14 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, } } + // AMDGPU does not allows the following types to be used for structure or + // union fields: named barriers + if (T->isAMDGPUNamedBarrierType()) { + Diag(Loc, diag::err_invalid_type_for_struct_or_union_field) << T; + Record->setInvalidDecl(); + InvalidDecl = true; + } + // Anonymous bit-fields cannot be cv-qualified (CWG 2229). if (!InvalidDecl && getLangOpts().CPlusPlus && !II && BitWidth && T.hasQualifiers()) { diff --git a/clang/test/CodeGenHIP/amdgpu-barrier-type.hip b/clang/test/CodeGenHIP/amdgpu-barrier-type.hip index 947ceb56d279e..d32e2dbee2238 100644 --- a/clang/test/CodeGenHIP/amdgpu-barrier-type.hip +++ b/clang/test/CodeGenHIP/amdgpu-barrier-type.hip @@ -1,18 +1,20 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature - // REQUIRES: amdgpu-registered-target - // RUN: %clang_cc1 -triple amdgcn-unknown-unknown -target-cpu verde -emit-llvm -o - %s | FileCheck %s +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature --check-globals +// REQUIRES: amdgpu-registered-target +// RUN: %clang_cc1 -fcuda-is-device -triple amdgcn-amd-amdhsa -target-cpu gfx1250 -emit-llvm -o - %s | FileCheck %s #define __shared__ __attribute__((shared)) __shared__ __amdgpu_named_workgroup_barrier_t bar; __shared__ __amdgpu_named_workgroup_barrier_t arr[2]; -__shared__ struct { - __amdgpu_named_workgroup_barrier_t x; - __amdgpu_named_workgroup_barrier_t y; -} str; -__amdgpu_named_workgroup_barrier_t *getBar(); -void useBar(__amdgpu_named_workgroup_barrier_t *); +//. +// CHECK: @bar = addrspace(3) global target("amdgcn.named.barrier", 0) undef, align 4 +// CHECK: @arr = addrspace(3) global [2 x target("amdgcn.named.barrier", 0)] undef, align 4 +// CHECK: @__hip_cuid_ = addrspace(1) global i8 0 +// CHECK: @llvm.compiler.used = appending addrspace(1) global [1 x ptr] [ptr addrspacecast (ptr addrspace(1) @__hip_cuid_ to ptr)], section "llvm.metadata" +//. +__attribute__((device)) __amdgpu_named_workgroup_barrier_t *getBar(); +__attribute__((device)) void useBar(__amdgpu_named_workgroup_barrier_t *); // CHECK-LABEL: define {{[^@]+}}@_Z7testSemPu34__amdgpu_named_workgroup_barrier_t // CHECK-SAME: (ptr noundef [[P:%.*]]) #[[ATTR0:[0-9]+]] { @@ -22,19 +24,26 @@ void useBar(__amdgpu_named_workgroup_barrier_t *); // CHECK-NEXT: store ptr [[P]], ptr [[P_ADDR_ASCAST]], align 8 // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR_ASCAST]], align 8 // CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef [[TMP0]]) #[[ATTR2:[0-9]+]] -// CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef addrspacecast (ptr addrspace(1) @bar to ptr)) #[[ATTR2]] -// CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef getelementptr inbounds nuw (i8, ptr addrspacecast (ptr addrspace(1) @arr to ptr), i64 16)) #[[ATTR2]] -// CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef getelementptr inbounds nuw (i8, ptr addrspacecast (ptr addrspace(1) @str to ptr), i64 16)) #[[ATTR2]] +// CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef addrspacecast (ptr addrspace(3) @bar to ptr)) #[[ATTR2]] +// CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef getelementptr inbounds nuw (i8, ptr addrspacecast (ptr addrspace(3) @arr to ptr), i64 16)) #[[ATTR2]] // CHECK-NEXT: [[CALL:%.*]] = call noundef ptr @_Z6getBarv() #[[ATTR2]] // CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef [[CALL]]) #[[ATTR2]] // CHECK-NEXT: [[CALL1:%.*]] = call noundef ptr @_Z6getBarv() #[[ATTR2]] // CHECK-NEXT: ret ptr [[CALL1]] // -__amdgpu_named_workgroup_barrier_t *testSem(__amdgpu_named_workgroup_barrier_t *p) { +__attribute__((device)) __amdgpu_named_workgroup_barrier_t *testSem(__amdgpu_named_workgroup_barrier_t *p) { useBar(p); useBar(&bar); useBar(&arr[1]); - useBar(&str.y); useBar(getBar()); return getBar(); } +//. +// CHECK: attributes #[[ATTR0]] = { convergent mustprogress noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="gfx1250" "uniform-work-group-size" } +// CHECK: attributes #[[ATTR1:[0-9]+]] = { convergent nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="gfx1250" "uniform-work-group-size" } +// CHECK: attributes #[[ATTR2]] = { convergent nounwind "uniform-work-group-size" } +//. +// CHECK: [[META0:![0-9]+]] = !{i32 1, !"amdhsa_code_object_version", i32 600} +// CHECK: [[META1:![0-9]+]] = !{i32 1, !"amdgpu_printf_kind", !"hostcall"} +// CHECK: [[META2:![0-9]+]] = !{!"{{.*}}clang version {{.*}}"} +//. diff --git a/clang/test/SemaCXX/amdgpu-barrier.cpp b/clang/test/SemaCXX/amdgpu-barrier.cpp index a171433727dda..497fb96503c33 100644 --- a/clang/test/SemaCXX/amdgpu-barrier.cpp +++ b/clang/test/SemaCXX/amdgpu-barrier.cpp @@ -13,5 +13,9 @@ void foo() { void *vp = (void *)k; // expected-error {{cannot cast from type '__amdgpu_named_workgroup_barrier_t' to pointer type 'void *'}} } +struct { + __amdgpu_named_workgroup_barrier_t x; // expected-error {{the '__amdgpu_named_workgroup_barrier_t' type cannot be used to declare a structure or union field}} +} str; + static_assert(sizeof(__amdgpu_named_workgroup_barrier_t) == 16, "wrong size"); static_assert(alignof(__amdgpu_named_workgroup_barrier_t) == 4, "wrong alignment"); diff --git a/clang/test/SemaHIP/amdgpu-barrier.hip b/clang/test/SemaHIP/amdgpu-barrier.hip index ccd99b1e2c1f2..4213ea540faf6 100644 --- a/clang/test/SemaHIP/amdgpu-barrier.hip +++ b/clang/test/SemaHIP/amdgpu-barrier.hip @@ -16,5 +16,10 @@ __device__ void foo() { void *vp = (void *)k; // expected-error {{cannot cast from type '__amdgpu_named_workgroup_barrier_t' to pointer type 'void *'}} } +struct { + __amdgpu_named_workgroup_barrier_t x; // expected-error {{the '__amdgpu_named_workgroup_barrier_t' type cannot be used to declare a structure or union field}} + __amdgpu_named_workgroup_barrier_t y[2]; // expected-error {{the '__amdgpu_named_workgroup_barrier_t[2]' type cannot be used to declare a structure or union field}} +} str; + static_assert(sizeof(__amdgpu_named_workgroup_barrier_t) == 16, "wrong size"); static_assert(alignof(__amdgpu_named_workgroup_barrier_t) == 4, "wrong alignment"); diff --git a/clang/test/SemaOpenCL/amdgpu-barrier.cl b/clang/test/SemaOpenCL/amdgpu-barrier.cl index 150c311c7c593..1e2d32005c444 100644 --- a/clang/test/SemaOpenCL/amdgpu-barrier.cl +++ b/clang/test/SemaOpenCL/amdgpu-barrier.cl @@ -3,6 +3,11 @@ // RUN: %clang_cc1 -verify -cl-std=CL2.0 -triple amdgcn-amd-amdhsa -Wno-unused-value %s void foo() { + struct { + __amdgpu_named_workgroup_barrier_t x; // expected-error {{the '__amdgpu_named_workgroup_barrier_t' type cannot be used to declare a structure or union field}} + __amdgpu_named_workgroup_barrier_t y[2]; // expected-error {{the '__amdgpu_named_workgroup_barrier_t[2]' type cannot be used to declare a structure or union field}} + } str; + int n = 100; __amdgpu_named_workgroup_barrier_t v = 0; // expected-error {{initializing '__private __amdgpu_named_workgroup_barrier_t' with an expression of incompatible type 'int'}} int c = v; // expected-error {{initializing '__private int' with an expression of incompatible type '__private __amdgpu_named_workgroup_barrier_t'}} diff --git a/clang/test/SemaTemplate/address_space-dependent.cpp b/clang/test/SemaTemplate/address_space-dependent.cpp index 3fdccb2c71a76..d6f25923b69b5 100644 --- a/clang/test/SemaTemplate/address_space-dependent.cpp +++ b/clang/test/SemaTemplate/address_space-dependent.cpp @@ -43,7 +43,7 @@ void neg() { template <long int I> void tooBig() { - __attribute__((address_space(I))) int *bounds; // expected-error {{address space is larger than the maximum supported (8388580)}} + __attribute__((address_space(I))) int *bounds; // expected-error {{address space is larger than the maximum supported (8388579)}} } template <long int I> @@ -101,7 +101,7 @@ int main() { car<1, 2, 3>(); // expected-note {{in instantiation of function template specialization 'car<1, 2, 3>' requested here}} HasASTemplateFields<1> HASTF; neg<-1>(); // expected-note {{in instantiation of function template specialization 'neg<-1>' requested here}} - correct<0x7FFFE4>(); + correct<0x7FFFE3>(); tooBig<8388650>(); // expected-note {{in instantiation of function template specialization 'tooBig<8388650L>' requested here}} __attribute__((address_space(1))) char *x; >From cf4ab5958bfd190d774d6eed0043c9b5ce6782a2 Mon Sep 17 00:00:00 2001 From: pvanhout <[email protected]> Date: Mon, 6 Jul 2026 13:56:08 +0200 Subject: [PATCH 2/7] Address Comments --- clang/include/clang/Basic/AddressSpaces.h | 2 +- clang/lib/AST/Type.cpp | 7 ++++--- clang/lib/AST/TypePrinter.cpp | 4 ++-- clang/lib/Basic/TargetInfo.cpp | 2 +- clang/lib/Basic/Targets/AArch64.h | 2 +- clang/lib/Basic/Targets/AMDGPU.cpp | 2 +- clang/lib/Basic/Targets/DirectX.h | 2 +- clang/lib/Basic/Targets/NVPTX.h | 2 +- clang/lib/Basic/Targets/SPIR.h | 4 ++-- clang/lib/Basic/Targets/SystemZ.h | 2 +- clang/lib/Basic/Targets/TCE.h | 2 +- clang/lib/Basic/Targets/WebAssembly.h | 2 +- clang/lib/Basic/Targets/X86.h | 2 +- clang/lib/CodeGen/CodeGenModule.cpp | 5 +---- clang/test/SemaCXX/amdgpu-barrier.cpp | 4 ++++ clang/test/SemaHIP/amdgpu-barrier.hip | 3 +++ clang/test/SemaOpenCL/amdgpu-barrier.cl | 5 +++-- 17 files changed, 29 insertions(+), 23 deletions(-) diff --git a/clang/include/clang/Basic/AddressSpaces.h b/clang/include/clang/Basic/AddressSpaces.h index d16fc8069ae43..339fc493bfbaf 100644 --- a/clang/include/clang/Basic/AddressSpaces.h +++ b/clang/include/clang/Basic/AddressSpaces.h @@ -69,7 +69,7 @@ enum class LangAS : unsigned { wasm_funcref, // HIP-specific address spaces - hip_barrier, + amdgpu_barrier, // This denotes the count of language-specific address spaces and also // the offset added to the target-specific address spaces, which are usually diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 51417d553f29f..ab7653d457739 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -93,7 +93,7 @@ bool Qualifiers::isTargetAddressSpaceSupersetOf(LangAS A, LangAS B, // to implicitly cast into the default address space. (A == LangAS::Default && (B == LangAS::cuda_constant || B == LangAS::cuda_device || - B == LangAS::cuda_shared || B == LangAS::hip_barrier)) || + B == LangAS::cuda_shared || B == LangAS::amdgpu_barrier)) || // In HLSL, the this pointer for member functions points to the default // address space. This causes a problem if the structure is in // a different address space. We want to allow casting from these @@ -5493,9 +5493,10 @@ bool Type::isCUDADeviceBuiltinTextureType() const { } bool Type::isAMDGPUNamedBarrierType() const { - const Type *Ty = getUnqualifiedDesugaredType(); + // This query does not care about qualifiers at all. + const Type *Ty = getCanonicalTypeInternal().getTypePtr(); - // unwrap arrays + // Unwrap arrays. while (isa<ArrayType>(Ty)) Ty = Ty->getArrayElementTypeNoTypeQual(); diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index e467397d9df52..aba901e764084 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -2748,8 +2748,8 @@ std::string Qualifiers::getAddrSpaceAsString(LangAS AS) { return "hlsl_push_constant"; case LangAS::wasm_funcref: return "__funcref"; - case LangAS::hip_barrier: - return "hip_barrier"; + case LangAS::amdgpu_barrier: + return "amdgpu_barrier"; default: return std::to_string(toTargetAddressSpace(AS)); } diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 5e8871ace9a60..d4249fb065375 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -55,7 +55,7 @@ static const LangASMap FakeAddrSpaceMap = { 18, // hlsl_output 19, // hlsl_push_constant 20, // wasm_funcref - 21, // hip_barrier + 21, // amdgpu_barrier }; // TargetInfo Constructor. diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h index 2974ddf551398..1faae0b213bf6 100644 --- a/clang/lib/Basic/Targets/AArch64.h +++ b/clang/lib/Basic/Targets/AArch64.h @@ -54,7 +54,7 @@ static const unsigned ARM64AddrSpaceMap[] = { // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref - 0, // hip_barrier + 0, // amdgpu_barrier }; using AArch64FeatureSet = llvm::SmallDenseSet<StringRef, 32>; diff --git a/clang/lib/Basic/Targets/AMDGPU.cpp b/clang/lib/Basic/Targets/AMDGPU.cpp index 1619d2bbfb8b7..f9855161c0837 100644 --- a/clang/lib/Basic/Targets/AMDGPU.cpp +++ b/clang/lib/Basic/Targets/AMDGPU.cpp @@ -57,7 +57,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUAddrSpaceMap = { llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_output llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_push_constant llvm::AMDGPUAS::FLAT_ADDRESS, // wasm_funcref - llvm::AMDGPUAS::LOCAL_ADDRESS, // hip_barrier + llvm::AMDGPUAS::LOCAL_ADDRESS, // amdgpu_barrier }; } // namespace targets diff --git a/clang/lib/Basic/Targets/DirectX.h b/clang/lib/Basic/Targets/DirectX.h index 6c4721c22e9ee..7c1aa7c75f726 100644 --- a/clang/lib/Basic/Targets/DirectX.h +++ b/clang/lib/Basic/Targets/DirectX.h @@ -51,7 +51,7 @@ static const unsigned DirectXAddrSpaceMap[] = { // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref - 0, // hip_barrier + 0, // amdgpu_barrier }; class LLVM_LIBRARY_VISIBILITY DirectXTargetInfo : public TargetInfo { diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h index 8f3638578b264..0d452b7f33bd3 100644 --- a/clang/lib/Basic/Targets/NVPTX.h +++ b/clang/lib/Basic/Targets/NVPTX.h @@ -55,7 +55,7 @@ static const unsigned NVPTXAddrSpaceMap[] = { // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref - 0, // hip_barrier + 0, // amdgpu_barrier }; /// The DWARF address class. Taken from diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h index 5ffd427c77f91..f9153986b5fe1 100644 --- a/clang/lib/Basic/Targets/SPIR.h +++ b/clang/lib/Basic/Targets/SPIR.h @@ -59,7 +59,7 @@ static const unsigned SPIRDefIsPrivMap[] = { // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref - 0, // hip_barrier + 0, // amdgpu_barrier }; // Used by both the SPIR and SPIR-V targets. @@ -98,7 +98,7 @@ static const unsigned SPIRDefIsGenMap[] = { // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref - 0, // hip_barrier + 0, // amdgpu_barrier }; // Base class for SPIR and SPIR-V target info. diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h index bb8fa1a1b3dec..7b08638d7232c 100644 --- a/clang/lib/Basic/Targets/SystemZ.h +++ b/clang/lib/Basic/Targets/SystemZ.h @@ -49,7 +49,7 @@ static const unsigned ZOSAddressMap[] = { 0, // hlsl_output 0, // hlsl_push_constant 0, // wasm_funcref - 0, // hip_barrier + 0, // amdgpu_barrier }; class LLVM_LIBRARY_VISIBILITY SystemZTargetInfo : public TargetInfo { diff --git a/clang/lib/Basic/Targets/TCE.h b/clang/lib/Basic/Targets/TCE.h index 95a17e0a9356f..403b8b1bb3304 100644 --- a/clang/lib/Basic/Targets/TCE.h +++ b/clang/lib/Basic/Targets/TCE.h @@ -60,7 +60,7 @@ static const unsigned TCEOpenCLAddrSpaceMap[] = { // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref - 0, // hip_barrier + 0, // amdgpu_barrier }; class LLVM_LIBRARY_VISIBILITY TCETargetInfo : public TargetInfo { diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h index ab498a082302b..b60963abbceaf 100644 --- a/clang/lib/Basic/Targets/WebAssembly.h +++ b/clang/lib/Basic/Targets/WebAssembly.h @@ -49,7 +49,7 @@ static const unsigned WebAssemblyAddrSpaceMap[] = { 0, // hlsl_output 0, // hlsl_push_constant 20, // wasm_funcref - 0, // hip_barrier + 0, // amdgpu_barrier }; class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo { diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h index e426b9113f38a..35c8227de8d27 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -55,7 +55,7 @@ static const unsigned X86AddrSpaceMap[] = { // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref - 0, // hip_barrier + 0, // amdgpu_barrier }; // X86 target abstract base class; x86-32 and x86-64 are very close, so diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 7fd54c6c34cb7..56c39bef1b5ef 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -6171,11 +6171,8 @@ LangAS CodeGenModule::GetGlobalVarAddressSpace(const VarDecl *D) { if (LangOpts.CUDA && LangOpts.CUDAIsDevice) { if (D) { - // TOOD: Forbid type on struct fields - // llvm::dbgs() << D->getNameAsString() << ": " << - // D->getType()->isAMDGPUNamedBarrierType() << "\n"; if (D->getType()->isAMDGPUNamedBarrierType()) - return LangAS::hip_barrier; + return LangAS::amdgpu_barrier; if (D->hasAttr<CUDAConstantAttr>()) return LangAS::cuda_constant; diff --git a/clang/test/SemaCXX/amdgpu-barrier.cpp b/clang/test/SemaCXX/amdgpu-barrier.cpp index 497fb96503c33..ff0cd5432c9e7 100644 --- a/clang/test/SemaCXX/amdgpu-barrier.cpp +++ b/clang/test/SemaCXX/amdgpu-barrier.cpp @@ -13,8 +13,12 @@ void foo() { void *vp = (void *)k; // expected-error {{cannot cast from type '__amdgpu_named_workgroup_barrier_t' to pointer type 'void *'}} } +using SugaredArray = __amdgpu_named_workgroup_barrier_t[2]; + struct { __amdgpu_named_workgroup_barrier_t x; // expected-error {{the '__amdgpu_named_workgroup_barrier_t' type cannot be used to declare a structure or union field}} + __amdgpu_named_workgroup_barrier_t y[2]; // expected-error {{the '__amdgpu_named_workgroup_barrier_t[2]' type cannot be used to declare a structure or union field}} + SugaredArray z[2]; // expected-error {{the 'SugaredArray[2]' (aka '__amdgpu_named_workgroup_barrier_t[2][2]') type cannot be used to declare a structure or union field}} } str; static_assert(sizeof(__amdgpu_named_workgroup_barrier_t) == 16, "wrong size"); diff --git a/clang/test/SemaHIP/amdgpu-barrier.hip b/clang/test/SemaHIP/amdgpu-barrier.hip index 4213ea540faf6..b30ddb3845595 100644 --- a/clang/test/SemaHIP/amdgpu-barrier.hip +++ b/clang/test/SemaHIP/amdgpu-barrier.hip @@ -16,9 +16,12 @@ __device__ void foo() { void *vp = (void *)k; // expected-error {{cannot cast from type '__amdgpu_named_workgroup_barrier_t' to pointer type 'void *'}} } +using SugaredArray = __amdgpu_named_workgroup_barrier_t[2]; + struct { __amdgpu_named_workgroup_barrier_t x; // expected-error {{the '__amdgpu_named_workgroup_barrier_t' type cannot be used to declare a structure or union field}} __amdgpu_named_workgroup_barrier_t y[2]; // expected-error {{the '__amdgpu_named_workgroup_barrier_t[2]' type cannot be used to declare a structure or union field}} + SugaredArray z[2]; // expected-error {{the 'SugaredArray[2]' (aka '__amdgpu_named_workgroup_barrier_t[2][2]') type cannot be used to declare a structure or union field}} } str; static_assert(sizeof(__amdgpu_named_workgroup_barrier_t) == 16, "wrong size"); diff --git a/clang/test/SemaOpenCL/amdgpu-barrier.cl b/clang/test/SemaOpenCL/amdgpu-barrier.cl index 1e2d32005c444..1963ac1a120f1 100644 --- a/clang/test/SemaOpenCL/amdgpu-barrier.cl +++ b/clang/test/SemaOpenCL/amdgpu-barrier.cl @@ -4,8 +4,9 @@ void foo() { struct { - __amdgpu_named_workgroup_barrier_t x; // expected-error {{the '__amdgpu_named_workgroup_barrier_t' type cannot be used to declare a structure or union field}} - __amdgpu_named_workgroup_barrier_t y[2]; // expected-error {{the '__amdgpu_named_workgroup_barrier_t[2]' type cannot be used to declare a structure or union field}} + __amdgpu_named_workgroup_barrier_t x; // expected-error {{the '__amdgpu_named_workgroup_barrier_t' type cannot be used to declare a structure or union field}} + __amdgpu_named_workgroup_barrier_t y[2]; // expected-error {{the '__amdgpu_named_workgroup_barrier_t[2]' type cannot be used to declare a structure or union field}} + __amdgpu_named_workgroup_barrier_t z[2][2]; // expected-error {{the '__amdgpu_named_workgroup_barrier_t[2][2]' type cannot be used to declare a structure or union field}} } str; int n = 100; >From 3df42e0cad0708857992e8a2342d1b8aa484c34c Mon Sep 17 00:00:00 2001 From: pvanhout <[email protected]> Date: Tue, 7 Jul 2026 09:35:57 +0200 Subject: [PATCH 3/7] Re-allow wrappers around named barriers --- clang/include/clang/AST/TypeBase.h | 5 +- clang/include/clang/Basic/Attr.td | 9 ++- .../clang/Basic/DiagnosticSemaKinds.td | 11 ++++ clang/include/clang/Sema/SemaAMDGPU.h | 3 + clang/lib/AST/Type.cpp | 20 +++++-- clang/lib/CodeGen/CodeGenModule.cpp | 2 +- clang/lib/Sema/SemaAMDGPU.cpp | 59 +++++++++++++++++++ clang/lib/Sema/SemaDecl.cpp | 12 ++-- clang/test/CodeGenHIP/amdgpu-barrier-type.hip | 8 ++- clang/test/SemaCXX/amdgpu-barrier.cpp | 35 +++++++++-- clang/test/SemaHIP/amdgpu-barrier.hip | 35 +++++++++-- clang/test/SemaOpenCL/amdgpu-barrier.cl | 28 +++++++-- 12 files changed, 196 insertions(+), 31 deletions(-) diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index 48612598735b9..a22ad94a301f7 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -2813,8 +2813,11 @@ class alignas(TypeAlignment) Type : public ExtQualsTypeCommonBase { /// Check if the type is the CUDA device builtin texture type. bool isCUDADeviceBuiltinTextureType() const; - /// Check if the type is the AMDGPU named barrier type. + /// Check if the type is the AMDGPU named barrier type, or an array thereof. bool isAMDGPUNamedBarrierType() const; + /// Check if the type is the AMDGPU named barrier type/a RecordType of a named + /// barrier wrapper, or an array thereof. + bool isAMDGPUNamedBarrierTypeOrWrapper() const; /// Return the implicit lifetime for this type, which must not be dependent. Qualifiers::ObjCLifetime getObjCARCImplicitLifetime() const; diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 3f57104d474a7..59914c314630d 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2522,6 +2522,13 @@ def AMDGPUMaxNumWorkGroups : InheritableAttr { let Subjects = SubjectList<[Function], ErrorDiag, "kernel functions">; } +def AMDGPUNamedBarrierWrapper : InheritableAttr { + let Spellings = []; + let Args = []; + let Documentation = [InternalOnly]; + let SemaHandler = 0; +} + def BPFPreserveAccessIndex : InheritableAttr, TargetSpecificAttr<TargetBPF> { let Spellings = [Clang<"preserve_access_index">]; @@ -5306,7 +5313,7 @@ def HLSLVkLocation : HLSLAnnotationAttr { } // `row_major` / `column_major` are HLSL keywords that select the in-memory -// layout of a matrix-typed declaration. +// layout of a matrix-typed declaration. def HLSLRowMajor : TypeAttr { let Spellings = [CustomKeyword<"row_major">]; let LangOpts = [HLSL]; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 437f0accbb8b5..0855c267156b5 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -14263,6 +14263,17 @@ def note_acc_reduction_combiner_forming : Note<"while forming %select{|binary operator '%1'|conditional " "operator|final assignment operator}0">; +// AMDGCN type diagnostics +def err_amdgcn_invalid_field_not_a_wrapper : Error< + "%0 is only allowed as a field if the %select{struct|interface|union|class|enum}1" + " is a trivial wrapper around it">; +def note_amdgcn_not_a_wrapper_derived_class : Note< + "%0 is not a trivial wrapper because it inherits from %1">; +def note_amdgcn_not_a_wrapper_too_many_fields : Note< + "%0 is not a trivial wrapper because it has more than one field">; +def note_amdgcn_wrapper_not_final : Note< + "%0 is not a trivial wrapper because it is not marked 'final'">; + // AMDGCN builtins diagnostics def err_amdgcn_load_lds_size_invalid_value : Error<"invalid size value">; def note_amdgcn_load_lds_size_valid_value : Note<"size must be %select{1, 2, or 4|1, 2, 4, 12 or 16}0">; diff --git a/clang/include/clang/Sema/SemaAMDGPU.h b/clang/include/clang/Sema/SemaAMDGPU.h index a6205534e0de3..897b1a03dc10b 100644 --- a/clang/include/clang/Sema/SemaAMDGPU.h +++ b/clang/include/clang/Sema/SemaAMDGPU.h @@ -89,6 +89,9 @@ class SemaAMDGPU : public SemaBase { void AddPotentiallyUnguardedBuiltinUser(FunctionDecl *FD); bool HasPotentiallyUnguardedBuiltinUsage(FunctionDecl *FD) const; void DiagnoseUnguardedBuiltinUsage(FunctionDecl *FD); + + /// Called in `ActOnFields` - whenever a C/C++ Record is being finalized. + void checkNamedBarrierWrapper(RecordDecl *R); }; } // namespace clang diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index ab7653d457739..c701860129b96 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -5492,19 +5492,31 @@ bool Type::isCUDADeviceBuiltinTextureType() const { return false; } -bool Type::isAMDGPUNamedBarrierType() const { +static bool isAMDGPUNamedBarrierTypeImpl(const Type *Ty, bool AllowWrappers) { // This query does not care about qualifiers at all. - const Type *Ty = getCanonicalTypeInternal().getTypePtr(); + Ty = Ty->getUnqualifiedDesugaredType(); // Unwrap arrays. while (isa<ArrayType>(Ty)) - Ty = Ty->getArrayElementTypeNoTypeQual(); + Ty = Ty->getArrayElementTypeNoTypeQual()->getUnqualifiedDesugaredType(); - if (const auto *BT = dyn_cast<BuiltinType>(Ty->getUnqualifiedDesugaredType())) + if (const auto *BT = dyn_cast<BuiltinType>(Ty)) return BT->getKind() == BuiltinType::AMDGPUNamedWorkgroupBarrier; + if (AllowWrappers) { + if (const auto *RT = dyn_cast<RecordType>(Ty)) + return RT->getDecl()->hasAttr<AMDGPUNamedBarrierWrapperAttr>(); + } return false; } +bool Type::isAMDGPUNamedBarrierType() const { + return isAMDGPUNamedBarrierTypeImpl(this, /*AllowWrappers=*/false); +} + +bool Type::isAMDGPUNamedBarrierTypeOrWrapper() const { + return isAMDGPUNamedBarrierTypeImpl(this, /*AllowWrappers=*/true); +} + bool Type::hasSizedVLAType() const { if (!isVariablyModifiedType()) return false; diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 56c39bef1b5ef..1b16b34469f6a 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -6171,7 +6171,7 @@ LangAS CodeGenModule::GetGlobalVarAddressSpace(const VarDecl *D) { if (LangOpts.CUDA && LangOpts.CUDAIsDevice) { if (D) { - if (D->getType()->isAMDGPUNamedBarrierType()) + if (D->getType()->isAMDGPUNamedBarrierTypeOrWrapper()) return LangAS::amdgpu_barrier; if (D->hasAttr<CUDAConstantAttr>()) diff --git a/clang/lib/Sema/SemaAMDGPU.cpp b/clang/lib/Sema/SemaAMDGPU.cpp index bd9e7e7b71ed6..19dff7cfe47c5 100644 --- a/clang/lib/Sema/SemaAMDGPU.cpp +++ b/clang/lib/Sema/SemaAMDGPU.cpp @@ -1062,4 +1062,63 @@ bool DiagnoseUnguardedBuiltins::VisitCallExpr(CallExpr *CE) { void SemaAMDGPU::DiagnoseUnguardedBuiltinUsage(FunctionDecl *FD) { DiagnoseUnguardedBuiltins(SemaRef).IssueDiagnostics(FD->getBody()); } + +void SemaAMDGPU::checkNamedBarrierWrapper(RecordDecl *R) { + if (R->isInvalidDecl()) + return; + + // Check if this record has any fields that interest us. + FieldDecl *NamedBarrField = nullptr; + for (FieldDecl *FD : R->fields()) { + QualType FDTy = FD->getType(); + if (FDTy->isAMDGPUNamedBarrierType()) { + NamedBarrField = FD; + break; + } + } + + if (!NamedBarrField) + return; + + bool IsInvalid = false; + const auto OnError = [&]() { + if (!IsInvalid) { + SemaRef.Diag(NamedBarrField->getLocation(), + diag::err_amdgcn_invalid_field_not_a_wrapper) + << NamedBarrField->getType() << R->getTagKind(); + IsInvalid = true; + } + }; + + if (R->getNumFields() > 1) { + OnError(); + SemaRef.Diag(R->getLocation(), + diag::note_amdgcn_not_a_wrapper_too_many_fields) + << R->getName(); + } + + if (const auto *CxxR = dyn_cast<CXXRecordDecl>(R)) { + if (CxxR->getNumBases() != 0) { + OnError(); + for (CXXBaseSpecifier CxxBase : CxxR->bases()) { + SemaRef.Diag(CxxBase.getBaseTypeLoc(), + diag::note_amdgcn_not_a_wrapper_derived_class) + << R->getName() << CxxBase.getType(); + } + } + + if (!CxxR->hasAttr<FinalAttr>()) { + OnError(); + SemaRef.Diag(R->getLocation(), diag::note_amdgcn_wrapper_not_final) + << R->getName(); + } + } + + if (IsInvalid) + return; + + ASTContext &Context = getASTContext(); + R->addAttr(AMDGPUNamedBarrierWrapperAttr::CreateImplicit( + Context, NamedBarrField->getSourceRange())); +} } // namespace clang diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index b77305d575e67..cacf015b02b95 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -19448,14 +19448,6 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, } } - // AMDGPU does not allows the following types to be used for structure or - // union fields: named barriers - if (T->isAMDGPUNamedBarrierType()) { - Diag(Loc, diag::err_invalid_type_for_struct_or_union_field) << T; - Record->setInvalidDecl(); - InvalidDecl = true; - } - // Anonymous bit-fields cannot be cv-qualified (CWG 2229). if (!InvalidDecl && getLangOpts().CPlusPlus && !II && BitWidth && T.hasQualifiers()) { @@ -20433,6 +20425,10 @@ void Sema::ActOnFields(Scope *S, SourceLocation RecLoc, Decl *EnclosingDecl, CDecl->setIvarRBraceLoc(RBrac); } } + + if (Record) + AMDGPU().checkNamedBarrierWrapper(Record); + if (Record && !isa<ClassTemplateSpecializationDecl>(Record)) ProcessAPINotes(Record); } diff --git a/clang/test/CodeGenHIP/amdgpu-barrier-type.hip b/clang/test/CodeGenHIP/amdgpu-barrier-type.hip index d32e2dbee2238..724136597810a 100644 --- a/clang/test/CodeGenHIP/amdgpu-barrier-type.hip +++ b/clang/test/CodeGenHIP/amdgpu-barrier-type.hip @@ -6,13 +6,17 @@ __shared__ __amdgpu_named_workgroup_barrier_t bar; __shared__ __amdgpu_named_workgroup_barrier_t arr[2]; - //. // CHECK: @bar = addrspace(3) global target("amdgcn.named.barrier", 0) undef, align 4 // CHECK: @arr = addrspace(3) global [2 x target("amdgcn.named.barrier", 0)] undef, align 4 +// CHECK: @wrapper_str = addrspace(3) global %struct.WrapperStruct undef, align 4 // CHECK: @__hip_cuid_ = addrspace(1) global i8 0 // CHECK: @llvm.compiler.used = appending addrspace(1) global [1 x ptr] [ptr addrspacecast (ptr addrspace(1) @__hip_cuid_ to ptr)], section "llvm.metadata" //. +__shared__ struct WrapperStruct final { + __amdgpu_named_workgroup_barrier_t x; +} wrapper_str; + __attribute__((device)) __amdgpu_named_workgroup_barrier_t *getBar(); __attribute__((device)) void useBar(__amdgpu_named_workgroup_barrier_t *); @@ -25,6 +29,7 @@ __attribute__((device)) void useBar(__amdgpu_named_workgroup_barrier_t *); // CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[P_ADDR_ASCAST]], align 8 // CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef [[TMP0]]) #[[ATTR2:[0-9]+]] // CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef addrspacecast (ptr addrspace(3) @bar to ptr)) #[[ATTR2]] +// CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef addrspacecast (ptr addrspace(3) @wrapper_str to ptr)) #[[ATTR2]] // CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef getelementptr inbounds nuw (i8, ptr addrspacecast (ptr addrspace(3) @arr to ptr), i64 16)) #[[ATTR2]] // CHECK-NEXT: [[CALL:%.*]] = call noundef ptr @_Z6getBarv() #[[ATTR2]] // CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef [[CALL]]) #[[ATTR2]] @@ -34,6 +39,7 @@ __attribute__((device)) void useBar(__amdgpu_named_workgroup_barrier_t *); __attribute__((device)) __amdgpu_named_workgroup_barrier_t *testSem(__amdgpu_named_workgroup_barrier_t *p) { useBar(p); useBar(&bar); + useBar(&wrapper_str.x); useBar(&arr[1]); useBar(getBar()); return getBar(); diff --git a/clang/test/SemaCXX/amdgpu-barrier.cpp b/clang/test/SemaCXX/amdgpu-barrier.cpp index ff0cd5432c9e7..c94663327d75c 100644 --- a/clang/test/SemaCXX/amdgpu-barrier.cpp +++ b/clang/test/SemaCXX/amdgpu-barrier.cpp @@ -15,11 +15,36 @@ void foo() { using SugaredArray = __amdgpu_named_workgroup_barrier_t[2]; -struct { - __amdgpu_named_workgroup_barrier_t x; // expected-error {{the '__amdgpu_named_workgroup_barrier_t' type cannot be used to declare a structure or union field}} - __amdgpu_named_workgroup_barrier_t y[2]; // expected-error {{the '__amdgpu_named_workgroup_barrier_t[2]' type cannot be used to declare a structure or union field}} - SugaredArray z[2]; // expected-error {{the 'SugaredArray[2]' (aka '__amdgpu_named_workgroup_barrier_t[2][2]') type cannot be used to declare a structure or union field}} -} str; +struct TestSimple final { + __amdgpu_named_workgroup_barrier_t x; +}; + +struct TestArray final{ + __amdgpu_named_workgroup_barrier_t y[2]; +}; + +struct TestSugared final { + SugaredArray z[2]; +}; + +// Wrappers cannot have >1 field. +struct WrapperHasTooManyFields final { // expected-note {{WrapperHasTooManyFields is not a trivial wrapper because it has more than one field}} + __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} + int other; +}; + +struct WrapperIsNotFinal { // expected-note {{WrapperIsNotFinal is not a trivial wrapper because it is not marked 'final'}} + __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} +}; + +// Wrappers cannot have base class +struct WrapperBase { +}; + +struct WrapperWithBase final : public WrapperBase { // expected-note {{WrapperWithBase is not a trivial wrapper because it inherits from 'WrapperBase'}} + __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} +}; + static_assert(sizeof(__amdgpu_named_workgroup_barrier_t) == 16, "wrong size"); static_assert(alignof(__amdgpu_named_workgroup_barrier_t) == 4, "wrong alignment"); diff --git a/clang/test/SemaHIP/amdgpu-barrier.hip b/clang/test/SemaHIP/amdgpu-barrier.hip index b30ddb3845595..679d2fbcf980f 100644 --- a/clang/test/SemaHIP/amdgpu-barrier.hip +++ b/clang/test/SemaHIP/amdgpu-barrier.hip @@ -18,11 +18,36 @@ __device__ void foo() { using SugaredArray = __amdgpu_named_workgroup_barrier_t[2]; -struct { - __amdgpu_named_workgroup_barrier_t x; // expected-error {{the '__amdgpu_named_workgroup_barrier_t' type cannot be used to declare a structure or union field}} - __amdgpu_named_workgroup_barrier_t y[2]; // expected-error {{the '__amdgpu_named_workgroup_barrier_t[2]' type cannot be used to declare a structure or union field}} - SugaredArray z[2]; // expected-error {{the 'SugaredArray[2]' (aka '__amdgpu_named_workgroup_barrier_t[2][2]') type cannot be used to declare a structure or union field}} -} str; +struct TestSimple final { + __amdgpu_named_workgroup_barrier_t x; +}; + +struct TestArray final { + __amdgpu_named_workgroup_barrier_t y[2]; +}; + +struct TestSugared final { + SugaredArray z[2]; +}; + +// Wrappers cannot have >1 field. +struct WrapperHasTooManyFields final { // expected-note {{WrapperHasTooManyFields is not a trivial wrapper because it has more than one field}} + __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} + int other; +}; + +struct WrapperIsNotFinal { // expected-note {{WrapperIsNotFinal is not a trivial wrapper because it is not marked 'final'}} + __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} +}; + +// Wrappers cannot have base class +struct WrapperBase { +}; + +struct WrapperWithBase final : public WrapperBase { // expected-note {{WrapperWithBase is not a trivial wrapper because it inherits from 'WrapperBase'}} + __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} +}; + static_assert(sizeof(__amdgpu_named_workgroup_barrier_t) == 16, "wrong size"); static_assert(alignof(__amdgpu_named_workgroup_barrier_t) == 4, "wrong alignment"); diff --git a/clang/test/SemaOpenCL/amdgpu-barrier.cl b/clang/test/SemaOpenCL/amdgpu-barrier.cl index 1963ac1a120f1..2c6a305780496 100644 --- a/clang/test/SemaOpenCL/amdgpu-barrier.cl +++ b/clang/test/SemaOpenCL/amdgpu-barrier.cl @@ -3,11 +3,29 @@ // RUN: %clang_cc1 -verify -cl-std=CL2.0 -triple amdgcn-amd-amdhsa -Wno-unused-value %s void foo() { - struct { - __amdgpu_named_workgroup_barrier_t x; // expected-error {{the '__amdgpu_named_workgroup_barrier_t' type cannot be used to declare a structure or union field}} - __amdgpu_named_workgroup_barrier_t y[2]; // expected-error {{the '__amdgpu_named_workgroup_barrier_t[2]' type cannot be used to declare a structure or union field}} - __amdgpu_named_workgroup_barrier_t z[2][2]; // expected-error {{the '__amdgpu_named_workgroup_barrier_t[2][2]' type cannot be used to declare a structure or union field}} - } str; + typedef __amdgpu_named_workgroup_barrier_t SugaredArray[2]; + + struct TestSimple { + __amdgpu_named_workgroup_barrier_t x; + }; + + struct TestArray { + __amdgpu_named_workgroup_barrier_t y[2]; + }; + + struct TestSugared { + SugaredArray z[2]; + }; + + struct GoodWrapper { + __amdgpu_named_workgroup_barrier_t x; + }; + + // Wrappers cannot have >1 field. + struct WrapperHasTooManyFields { // expected-note {{WrapperHasTooManyFields is not a trivial wrapper because it has more than one field}} + __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} + int other; + }; int n = 100; __amdgpu_named_workgroup_barrier_t v = 0; // expected-error {{initializing '__private __amdgpu_named_workgroup_barrier_t' with an expression of incompatible type 'int'}} >From 4caeead4b96bab8b1282ffea02f5ab13ec6e5b80 Mon Sep 17 00:00:00 2001 From: pvanhout <[email protected]> Date: Tue, 7 Jul 2026 09:42:12 +0200 Subject: [PATCH 4/7] Add test w/ static field --- clang/test/SemaCXX/amdgpu-barrier.cpp | 5 +++++ clang/test/SemaHIP/amdgpu-barrier.hip | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/clang/test/SemaCXX/amdgpu-barrier.cpp b/clang/test/SemaCXX/amdgpu-barrier.cpp index c94663327d75c..0b054d9d2b0e0 100644 --- a/clang/test/SemaCXX/amdgpu-barrier.cpp +++ b/clang/test/SemaCXX/amdgpu-barrier.cpp @@ -27,6 +27,11 @@ struct TestSugared final { SugaredArray z[2]; }; +struct TestSimpleWithStaticField final { + __amdgpu_named_workgroup_barrier_t x; + static unsigned Harmless; +}; + // Wrappers cannot have >1 field. struct WrapperHasTooManyFields final { // expected-note {{WrapperHasTooManyFields is not a trivial wrapper because it has more than one field}} __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} diff --git a/clang/test/SemaHIP/amdgpu-barrier.hip b/clang/test/SemaHIP/amdgpu-barrier.hip index 679d2fbcf980f..5378fdc6793d0 100644 --- a/clang/test/SemaHIP/amdgpu-barrier.hip +++ b/clang/test/SemaHIP/amdgpu-barrier.hip @@ -30,6 +30,11 @@ struct TestSugared final { SugaredArray z[2]; }; +struct TestSimpleWithStaticField final { + __amdgpu_named_workgroup_barrier_t x; + static unsigned Harmless; +}; + // Wrappers cannot have >1 field. struct WrapperHasTooManyFields final { // expected-note {{WrapperHasTooManyFields is not a trivial wrapper because it has more than one field}} __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} >From 9b2d699d4caa4c0d38015f4c1aa52dfde075db01 Mon Sep 17 00:00:00 2001 From: pvanhout <[email protected]> Date: Wed, 8 Jul 2026 11:40:54 +0200 Subject: [PATCH 5/7] Update implementation --- clang/include/clang/Basic/Attr.td | 2 +- .../clang/Basic/DiagnosticSemaKinds.td | 15 ++++----- clang/lib/Sema/SemaAMDGPU.cpp | 25 +++++++-------- clang/lib/Sema/SemaDeclCXX.cpp | 13 ++++++++ clang/test/CodeGenHIP/amdgpu-barrier-type.hip | 10 +++++- clang/test/SemaCXX/amdgpu-barrier.cpp | 32 +++++++++++-------- clang/test/SemaHIP/amdgpu-barrier.hip | 32 +++++++++++-------- clang/test/SemaOpenCL/amdgpu-barrier.cl | 4 +-- 8 files changed, 82 insertions(+), 51 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 59914c314630d..71bb535c43106 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2524,7 +2524,7 @@ def AMDGPUMaxNumWorkGroups : InheritableAttr { def AMDGPUNamedBarrierWrapper : InheritableAttr { let Spellings = []; - let Args = []; + let Args = [DeclArgument<Field, "WrappedField">]; let Documentation = [InternalOnly]; let SemaHandler = 0; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 0855c267156b5..86362e6ad05f5 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -14265,14 +14265,13 @@ def note_acc_reduction_combiner_forming // AMDGCN type diagnostics def err_amdgcn_invalid_field_not_a_wrapper : Error< - "%0 is only allowed as a field if the %select{struct|interface|union|class|enum}1" - " is a trivial wrapper around it">; -def note_amdgcn_not_a_wrapper_derived_class : Note< - "%0 is not a trivial wrapper because it inherits from %1">; -def note_amdgcn_not_a_wrapper_too_many_fields : Note< - "%0 is not a trivial wrapper because it has more than one field">; -def note_amdgcn_wrapper_not_final : Note< - "%0 is not a trivial wrapper because it is not marked 'final'">; + "fields of type %0 are only allowed in named barrier wrappers">; +def note_amdgcn_not_a_named_barrier_wrapper_derived_class : Note< + "%0 is not a named barrier wrapper because it inherits from %1; named barrier wrappers may not have any bases">; +def note_amdgcn_not_a_named_barrier_wrapper_too_many_fields : Note< + "%0 is not a named barrier wrapper because it has more than one field">; +def note_amdgcn_named_barrier_wrapper_implies_final : Note< + "%0 is a named barrier wrapper around %1, which implies 'final'">; // AMDGCN builtins diagnostics def err_amdgcn_load_lds_size_invalid_value : Error<"invalid size value">; diff --git a/clang/lib/Sema/SemaAMDGPU.cpp b/clang/lib/Sema/SemaAMDGPU.cpp index 19dff7cfe47c5..863257334917e 100644 --- a/clang/lib/Sema/SemaAMDGPU.cpp +++ b/clang/lib/Sema/SemaAMDGPU.cpp @@ -1068,10 +1068,12 @@ void SemaAMDGPU::checkNamedBarrierWrapper(RecordDecl *R) { return; // Check if this record has any fields that interest us. + // We cannot codegen any kind of heterogenous struct where we named barrier + // fields mixed with other fields. FieldDecl *NamedBarrField = nullptr; for (FieldDecl *FD : R->fields()) { QualType FDTy = FD->getType(); - if (FDTy->isAMDGPUNamedBarrierType()) { + if (FDTy->isAMDGPUNamedBarrierTypeOrWrapper()) { NamedBarrField = FD; break; } @@ -1085,7 +1087,7 @@ void SemaAMDGPU::checkNamedBarrierWrapper(RecordDecl *R) { if (!IsInvalid) { SemaRef.Diag(NamedBarrField->getLocation(), diag::err_amdgcn_invalid_field_not_a_wrapper) - << NamedBarrField->getType() << R->getTagKind(); + << NamedBarrField->getType(); IsInvalid = true; } }; @@ -1093,7 +1095,7 @@ void SemaAMDGPU::checkNamedBarrierWrapper(RecordDecl *R) { if (R->getNumFields() > 1) { OnError(); SemaRef.Diag(R->getLocation(), - diag::note_amdgcn_not_a_wrapper_too_many_fields) + diag::note_amdgcn_not_a_named_barrier_wrapper_too_many_fields) << R->getName(); } @@ -1101,24 +1103,21 @@ void SemaAMDGPU::checkNamedBarrierWrapper(RecordDecl *R) { if (CxxR->getNumBases() != 0) { OnError(); for (CXXBaseSpecifier CxxBase : CxxR->bases()) { - SemaRef.Diag(CxxBase.getBaseTypeLoc(), - diag::note_amdgcn_not_a_wrapper_derived_class) + SemaRef.Diag( + CxxBase.getBaseTypeLoc(), + diag::note_amdgcn_not_a_named_barrier_wrapper_derived_class) << R->getName() << CxxBase.getType(); } } - - if (!CxxR->hasAttr<FinalAttr>()) { - OnError(); - SemaRef.Diag(R->getLocation(), diag::note_amdgcn_wrapper_not_final) - << R->getName(); - } } if (IsInvalid) return; ASTContext &Context = getASTContext(); - R->addAttr(AMDGPUNamedBarrierWrapperAttr::CreateImplicit( - Context, NamedBarrField->getSourceRange())); + SourceRange SR = NamedBarrField->getSourceRange(); + R->addAttr(FinalAttr::CreateImplicit(Context, SR)); + R->addAttr(AMDGPUNamedBarrierWrapperAttr::CreateImplicit(Context, + NamedBarrField, SR)); } } // namespace clang diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 65b46562b166e..564d850695c1f 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -2851,6 +2851,19 @@ CXXBaseSpecifier *Sema::CheckBaseSpecifier(CXXRecordDecl *Class, << BaseDecl->getDeclName() << FA->isSpelledAsSealed(); Diag(BaseDecl->getLocation(), diag::note_entity_declared_at) << BaseDecl->getDeclName() << FA->getRange(); + + // AMDGPU Named Barrier Wrappers are final. If the FinalAttr may have come + // from there, diagnose it. + if (FA->isImplicit()) { + if (auto *NBW = BaseDecl->getAttr<AMDGPUNamedBarrierWrapperAttr>()) { + FieldDecl *WrappedField = NBW->getWrappedField(); + Diag(BaseDecl->getLocation(), + diag::note_amdgcn_named_barrier_wrapper_implies_final) + << BaseDecl->getDeclName() << WrappedField->getDeclName(); + Diag(WrappedField->getLocation(), diag::note_entity_declared_at) + << WrappedField->getDeclName(); + } + } return nullptr; } diff --git a/clang/test/CodeGenHIP/amdgpu-barrier-type.hip b/clang/test/CodeGenHIP/amdgpu-barrier-type.hip index 724136597810a..3664821b80541 100644 --- a/clang/test/CodeGenHIP/amdgpu-barrier-type.hip +++ b/clang/test/CodeGenHIP/amdgpu-barrier-type.hip @@ -6,17 +6,23 @@ __shared__ __amdgpu_named_workgroup_barrier_t bar; __shared__ __amdgpu_named_workgroup_barrier_t arr[2]; + //. // CHECK: @bar = addrspace(3) global target("amdgcn.named.barrier", 0) undef, align 4 // CHECK: @arr = addrspace(3) global [2 x target("amdgcn.named.barrier", 0)] undef, align 4 // CHECK: @wrapper_str = addrspace(3) global %struct.WrapperStruct undef, align 4 +// CHECK: @wrapperwrapper_str = addrspace(3) global %struct.WrapperWrapperStruct undef, align 4 // CHECK: @__hip_cuid_ = addrspace(1) global i8 0 // CHECK: @llvm.compiler.used = appending addrspace(1) global [1 x ptr] [ptr addrspacecast (ptr addrspace(1) @__hip_cuid_ to ptr)], section "llvm.metadata" //. -__shared__ struct WrapperStruct final { +__shared__ struct WrapperStruct { __amdgpu_named_workgroup_barrier_t x; } wrapper_str; +__shared__ struct WrapperWrapperStruct { + WrapperStruct x; +} wrapperwrapper_str; + __attribute__((device)) __amdgpu_named_workgroup_barrier_t *getBar(); __attribute__((device)) void useBar(__amdgpu_named_workgroup_barrier_t *); @@ -30,6 +36,7 @@ __attribute__((device)) void useBar(__amdgpu_named_workgroup_barrier_t *); // CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef [[TMP0]]) #[[ATTR2:[0-9]+]] // CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef addrspacecast (ptr addrspace(3) @bar to ptr)) #[[ATTR2]] // CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef addrspacecast (ptr addrspace(3) @wrapper_str to ptr)) #[[ATTR2]] +// CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef addrspacecast (ptr addrspace(3) @wrapperwrapper_str to ptr)) #[[ATTR2]] // CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef getelementptr inbounds nuw (i8, ptr addrspacecast (ptr addrspace(3) @arr to ptr), i64 16)) #[[ATTR2]] // CHECK-NEXT: [[CALL:%.*]] = call noundef ptr @_Z6getBarv() #[[ATTR2]] // CHECK-NEXT: call void @_Z6useBarPu34__amdgpu_named_workgroup_barrier_t(ptr noundef [[CALL]]) #[[ATTR2]] @@ -40,6 +47,7 @@ __attribute__((device)) __amdgpu_named_workgroup_barrier_t *testSem(__amdgpu_nam useBar(p); useBar(&bar); useBar(&wrapper_str.x); + useBar(&wrapperwrapper_str.x.x); useBar(&arr[1]); useBar(getBar()); return getBar(); diff --git a/clang/test/SemaCXX/amdgpu-barrier.cpp b/clang/test/SemaCXX/amdgpu-barrier.cpp index 0b054d9d2b0e0..f9e5313bcfa26 100644 --- a/clang/test/SemaCXX/amdgpu-barrier.cpp +++ b/clang/test/SemaCXX/amdgpu-barrier.cpp @@ -15,41 +15,47 @@ void foo() { using SugaredArray = __amdgpu_named_workgroup_barrier_t[2]; -struct TestSimple final { - __amdgpu_named_workgroup_barrier_t x; +// expected-note@+2 {{'TestSimple' is a named barrier wrapper around 'x', which implies 'final'}} +// expected-note@+1 {{'TestSimple' declared here}} +struct TestSimple { + __amdgpu_named_workgroup_barrier_t x; // expected-note {{'x' declared here}} }; -struct TestArray final{ +struct TestArray{ __amdgpu_named_workgroup_barrier_t y[2]; }; -struct TestSugared final { +struct TestSugared { SugaredArray z[2]; }; -struct TestSimpleWithStaticField final { +struct TestSimpleWithStaticField { __amdgpu_named_workgroup_barrier_t x; static unsigned Harmless; }; // Wrappers cannot have >1 field. -struct WrapperHasTooManyFields final { // expected-note {{WrapperHasTooManyFields is not a trivial wrapper because it has more than one field}} - __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} +struct WrapperHasTooManyFields { // expected-note {{WrapperHasTooManyFields is not a named barrier wrapper because it has more than one field}} + __amdgpu_named_workgroup_barrier_t x; // expected-error {{fields of type '__amdgpu_named_workgroup_barrier_t' are only allowed in named barrier wrappers}} int other; }; -struct WrapperIsNotFinal { // expected-note {{WrapperIsNotFinal is not a trivial wrapper because it is not marked 'final'}} - __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} -}; - // Wrappers cannot have base class struct WrapperBase { }; -struct WrapperWithBase final : public WrapperBase { // expected-note {{WrapperWithBase is not a trivial wrapper because it inherits from 'WrapperBase'}} - __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} +struct WrapperWithBase : public WrapperBase { // expected-note {{WrapperWithBase is not a named barrier wrapper because it inherits from 'WrapperBase'; named barrier wrappers may not have any bases}} + __amdgpu_named_workgroup_barrier_t x; // expected-error {{fields of type '__amdgpu_named_workgroup_barrier_t' are only allowed in named barrier wrappers}} }; +// Wrappers imply final +struct NoWrapperInheritance : public TestSimple { }; // expected-error {{base 'TestSimple' is marked 'final'}} + +// Wrappers of Wrappers have the same restrictions. +struct WrapperOfWrapperWithTooManyFields final { // expected-note {{WrapperOfWrapperWithTooManyFields is not a named barrier wrapper because it has more than one field}} + TestSimple x; // expected-error {{fields of type 'TestSimple' are only allowed in named barrier wrappers}} + int other; +}; static_assert(sizeof(__amdgpu_named_workgroup_barrier_t) == 16, "wrong size"); static_assert(alignof(__amdgpu_named_workgroup_barrier_t) == 4, "wrong alignment"); diff --git a/clang/test/SemaHIP/amdgpu-barrier.hip b/clang/test/SemaHIP/amdgpu-barrier.hip index 5378fdc6793d0..a0baaaa3bf899 100644 --- a/clang/test/SemaHIP/amdgpu-barrier.hip +++ b/clang/test/SemaHIP/amdgpu-barrier.hip @@ -18,41 +18,47 @@ __device__ void foo() { using SugaredArray = __amdgpu_named_workgroup_barrier_t[2]; -struct TestSimple final { - __amdgpu_named_workgroup_barrier_t x; +// expected-note@+2 {{'TestSimple' is a named barrier wrapper around 'x', which implies 'final'}} +// expected-note@+1 {{'TestSimple' declared here}} +struct TestSimple { + __amdgpu_named_workgroup_barrier_t x; // expected-note {{'x' declared here}} }; -struct TestArray final { +struct TestArray{ __amdgpu_named_workgroup_barrier_t y[2]; }; -struct TestSugared final { +struct TestSugared { SugaredArray z[2]; }; -struct TestSimpleWithStaticField final { +struct TestSimpleWithStaticField { __amdgpu_named_workgroup_barrier_t x; static unsigned Harmless; }; // Wrappers cannot have >1 field. -struct WrapperHasTooManyFields final { // expected-note {{WrapperHasTooManyFields is not a trivial wrapper because it has more than one field}} - __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} +struct WrapperHasTooManyFields { // expected-note {{WrapperHasTooManyFields is not a named barrier wrapper because it has more than one field}} + __amdgpu_named_workgroup_barrier_t x; // expected-error {{fields of type '__amdgpu_named_workgroup_barrier_t' are only allowed in named barrier wrappers}} int other; }; -struct WrapperIsNotFinal { // expected-note {{WrapperIsNotFinal is not a trivial wrapper because it is not marked 'final'}} - __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} -}; - // Wrappers cannot have base class struct WrapperBase { }; -struct WrapperWithBase final : public WrapperBase { // expected-note {{WrapperWithBase is not a trivial wrapper because it inherits from 'WrapperBase'}} - __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} +struct WrapperWithBase : public WrapperBase { // expected-note {{WrapperWithBase is not a named barrier wrapper because it inherits from 'WrapperBase'; named barrier wrappers may not have any bases}} + __amdgpu_named_workgroup_barrier_t x; // expected-error {{fields of type '__amdgpu_named_workgroup_barrier_t' are only allowed in named barrier wrappers}} }; +// Wrappers imply final +struct NoWrapperInheritance : public TestSimple { }; // expected-error {{base 'TestSimple' is marked 'final'}} + +// Wrappers of Wrappers have the same restrictions. +struct WrapperOfWrapperWithTooManyFields final { // expected-note {{WrapperOfWrapperWithTooManyFields is not a named barrier wrapper because it has more than one field}} + TestSimple x; // expected-error {{fields of type 'TestSimple' are only allowed in named barrier wrappers}} + int other; +}; static_assert(sizeof(__amdgpu_named_workgroup_barrier_t) == 16, "wrong size"); static_assert(alignof(__amdgpu_named_workgroup_barrier_t) == 4, "wrong alignment"); diff --git a/clang/test/SemaOpenCL/amdgpu-barrier.cl b/clang/test/SemaOpenCL/amdgpu-barrier.cl index 2c6a305780496..2f96a6aba3a3e 100644 --- a/clang/test/SemaOpenCL/amdgpu-barrier.cl +++ b/clang/test/SemaOpenCL/amdgpu-barrier.cl @@ -22,8 +22,8 @@ void foo() { }; // Wrappers cannot have >1 field. - struct WrapperHasTooManyFields { // expected-note {{WrapperHasTooManyFields is not a trivial wrapper because it has more than one field}} - __amdgpu_named_workgroup_barrier_t x; // expected-error {{'__amdgpu_named_workgroup_barrier_t' is only allowed as a field if the struct is a trivial wrapper around it}} + struct WrapperHasTooManyFields { // expected-note {{WrapperHasTooManyFields is not a named barrier wrapper because it has more than one field}} + __amdgpu_named_workgroup_barrier_t x; // expected-error {{fields of type '__amdgpu_named_workgroup_barrier_t' are only allowed in named barrier wrappers}} int other; }; >From 011670c149dae1ef04f8b5d164f263acb3ba07f8 Mon Sep 17 00:00:00 2001 From: pvanhout <[email protected]> Date: Thu, 9 Jul 2026 10:55:22 +0200 Subject: [PATCH 6/7] Use C++ standard layout instead --- clang/include/clang/Basic/Attr.td | 2 +- .../clang/Basic/DiagnosticSemaKinds.td | 11 ++- clang/lib/Sema/SemaAMDGPU.cpp | 96 ++++++++++++------- clang/lib/Sema/SemaDeclCXX.cpp | 13 --- clang/test/SemaCXX/amdgpu-barrier.cpp | 47 +++++++-- clang/test/SemaHIP/amdgpu-barrier.hip | 48 ++++++++-- clang/test/SemaOpenCL/amdgpu-barrier.cl | 2 +- 7 files changed, 143 insertions(+), 76 deletions(-) diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 71bb535c43106..59914c314630d 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -2524,7 +2524,7 @@ def AMDGPUMaxNumWorkGroups : InheritableAttr { def AMDGPUNamedBarrierWrapper : InheritableAttr { let Spellings = []; - let Args = [DeclArgument<Field, "WrappedField">]; + let Args = []; let Documentation = [InternalOnly]; let SemaHandler = 0; } diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 86362e6ad05f5..2db846d6e7189 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -14266,12 +14266,15 @@ def note_acc_reduction_combiner_forming // AMDGCN type diagnostics def err_amdgcn_invalid_field_not_a_wrapper : Error< "fields of type %0 are only allowed in named barrier wrappers">; -def note_amdgcn_not_a_named_barrier_wrapper_derived_class : Note< - "%0 is not a named barrier wrapper because it inherits from %1; named barrier wrappers may not have any bases">; def note_amdgcn_not_a_named_barrier_wrapper_too_many_fields : Note< "%0 is not a named barrier wrapper because it has more than one field">; -def note_amdgcn_named_barrier_wrapper_implies_final : Note< - "%0 is a named barrier wrapper around %1, which implies 'final'">; + +def err_amdgcn_named_barrier_wrapper_non_standard_layout : Error< + "named barrier wrapper %0 must have a C++11 standard layout">; +def note_amdgcn_named_barrier_reason_field : Note< + "%0 is a named barrier wrapper because it has a named barrier or named barrier wrapper field %1 found here">; +def note_amdgcn_named_barrier_reason_inherited : Note< + "%0 is a named barrier wrapper because it inherits from named barrier wrapper %1">; // AMDGCN builtins diagnostics def err_amdgcn_load_lds_size_invalid_value : Error<"invalid size value">; diff --git a/clang/lib/Sema/SemaAMDGPU.cpp b/clang/lib/Sema/SemaAMDGPU.cpp index 863257334917e..134cf8e4d03ce 100644 --- a/clang/lib/Sema/SemaAMDGPU.cpp +++ b/clang/lib/Sema/SemaAMDGPU.cpp @@ -1063,61 +1063,83 @@ void SemaAMDGPU::DiagnoseUnguardedBuiltinUsage(FunctionDecl *FD) { DiagnoseUnguardedBuiltins(SemaRef).IssueDiagnostics(FD->getBody()); } -void SemaAMDGPU::checkNamedBarrierWrapper(RecordDecl *R) { - if (R->isInvalidDecl()) - return; - - // Check if this record has any fields that interest us. - // We cannot codegen any kind of heterogenous struct where we named barrier - // fields mixed with other fields. - FieldDecl *NamedBarrField = nullptr; +static FieldDecl *getNamedBarrierField(const RecordDecl *R) { for (FieldDecl *FD : R->fields()) { QualType FDTy = FD->getType(); - if (FDTy->isAMDGPUNamedBarrierTypeOrWrapper()) { - NamedBarrField = FD; - break; - } + if (FDTy->isAMDGPUNamedBarrierTypeOrWrapper()) + return FD; } - if (!NamedBarrField) + return nullptr; +} + +void SemaAMDGPU::checkNamedBarrierWrapper(RecordDecl *R) { + if (R->isInvalidDecl()) return; - bool IsInvalid = false; - const auto OnError = [&]() { - if (!IsInvalid) { + bool IsWrapper = false; + std::function<void()> DiagWrapperNote; + + // First, check if this is a named barrier wrapper by virtue of the class + // declaring a named barrier field. This covers both C and C++. + if (FieldDecl *NamedBarrField = getNamedBarrierField(R)) { + // If this record contains a named barrier field, it must have only one + // field. + if (R->getNumFields() > 1) { SemaRef.Diag(NamedBarrField->getLocation(), diag::err_amdgcn_invalid_field_not_a_wrapper) << NamedBarrField->getType(); - IsInvalid = true; + SemaRef.Diag( + R->getLocation(), + diag::note_amdgcn_not_a_named_barrier_wrapper_too_many_fields) + << R->getDeclName(); + return; } - }; - if (R->getNumFields() > 1) { - OnError(); - SemaRef.Diag(R->getLocation(), - diag::note_amdgcn_not_a_named_barrier_wrapper_too_many_fields) - << R->getName(); + IsWrapper = true; + DiagWrapperNote = [this, R, NamedBarrField]() { + SemaRef.Diag(NamedBarrField->getLocation(), + diag::note_amdgcn_named_barrier_reason_field) + << R->getDeclName() << NamedBarrField->getDeclName(); + }; } - if (const auto *CxxR = dyn_cast<CXXRecordDecl>(R)) { - if (CxxR->getNumBases() != 0) { - OnError(); - for (CXXBaseSpecifier CxxBase : CxxR->bases()) { - SemaRef.Diag( - CxxBase.getBaseTypeLoc(), - diag::note_amdgcn_not_a_named_barrier_wrapper_derived_class) - << R->getName() << CxxBase.getType(); - } + // Then, for C++ classes, check if this is a named barrier wrapper by virtue + // of inheriting one. + const auto *CxxR = dyn_cast<CXXRecordDecl>(R); + if (CxxR && !IsWrapper) { + for (CXXBaseSpecifier BS : CxxR->bases()) { + const RecordDecl *Base = BS.getType()->getAsRecordDecl(); + if (!Base || !Base->hasAttr<AMDGPUNamedBarrierWrapperAttr>()) + continue; + + IsWrapper = true; + DiagWrapperNote = [this, BS, R]() { + // Print using the CXXBaseSpecifier type as it includes the template + // parameters. + SemaRef.Diag(BS.getBeginLoc(), + diag::note_amdgcn_named_barrier_reason_inherited) + << R->getDeclName() << BS.getType(); + }; } } - if (IsInvalid) + if (!IsWrapper) return; + // Set the attribute even if the wrapper may be found to be invalid later. ASTContext &Context = getASTContext(); - SourceRange SR = NamedBarrField->getSourceRange(); - R->addAttr(FinalAttr::CreateImplicit(Context, SR)); - R->addAttr(AMDGPUNamedBarrierWrapperAttr::CreateImplicit(Context, - NamedBarrField, SR)); + R->addAttr( + AMDGPUNamedBarrierWrapperAttr::CreateImplicit(Context, SourceRange())); + + // This is a wrapper CXXRecordDecl, it must have a C++11 standard layout. + if (CxxR && !CxxR->isCXX11StandardLayout()) { + SemaRef.Diag(R->getLocation(), + diag::err_amdgcn_named_barrier_wrapper_non_standard_layout) + << R->getDeclName(); + assert(DiagWrapperNote && + "IsWrapper is set but no context diagnostic provided"); + DiagWrapperNote(); + } } } // namespace clang diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 564d850695c1f..65b46562b166e 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -2851,19 +2851,6 @@ CXXBaseSpecifier *Sema::CheckBaseSpecifier(CXXRecordDecl *Class, << BaseDecl->getDeclName() << FA->isSpelledAsSealed(); Diag(BaseDecl->getLocation(), diag::note_entity_declared_at) << BaseDecl->getDeclName() << FA->getRange(); - - // AMDGPU Named Barrier Wrappers are final. If the FinalAttr may have come - // from there, diagnose it. - if (FA->isImplicit()) { - if (auto *NBW = BaseDecl->getAttr<AMDGPUNamedBarrierWrapperAttr>()) { - FieldDecl *WrappedField = NBW->getWrappedField(); - Diag(BaseDecl->getLocation(), - diag::note_amdgcn_named_barrier_wrapper_implies_final) - << BaseDecl->getDeclName() << WrappedField->getDeclName(); - Diag(WrappedField->getLocation(), diag::note_entity_declared_at) - << WrappedField->getDeclName(); - } - } return nullptr; } diff --git a/clang/test/SemaCXX/amdgpu-barrier.cpp b/clang/test/SemaCXX/amdgpu-barrier.cpp index f9e5313bcfa26..80816fba6105e 100644 --- a/clang/test/SemaCXX/amdgpu-barrier.cpp +++ b/clang/test/SemaCXX/amdgpu-barrier.cpp @@ -15,10 +15,8 @@ void foo() { using SugaredArray = __amdgpu_named_workgroup_barrier_t[2]; -// expected-note@+2 {{'TestSimple' is a named barrier wrapper around 'x', which implies 'final'}} -// expected-note@+1 {{'TestSimple' declared here}} struct TestSimple { - __amdgpu_named_workgroup_barrier_t x; // expected-note {{'x' declared here}} + __amdgpu_named_workgroup_barrier_t x; }; struct TestArray{ @@ -35,27 +33,56 @@ struct TestSimpleWithStaticField { }; // Wrappers cannot have >1 field. -struct WrapperHasTooManyFields { // expected-note {{WrapperHasTooManyFields is not a named barrier wrapper because it has more than one field}} +struct WrapperHasTooManyFields { // expected-note {{'WrapperHasTooManyFields' is not a named barrier wrapper because it has more than one field}} __amdgpu_named_workgroup_barrier_t x; // expected-error {{fields of type '__amdgpu_named_workgroup_barrier_t' are only allowed in named barrier wrappers}} int other; }; -// Wrappers cannot have base class +// Wrappers must have standard layout struct WrapperBase { +__amdgpu_named_workgroup_barrier_t x; }; -struct WrapperWithBase : public WrapperBase { // expected-note {{WrapperWithBase is not a named barrier wrapper because it inherits from 'WrapperBase'; named barrier wrappers may not have any bases}} - __amdgpu_named_workgroup_barrier_t x; // expected-error {{fields of type '__amdgpu_named_workgroup_barrier_t' are only allowed in named barrier wrappers}} +struct WrapperWithBase : public WrapperBase { }; -// Wrappers imply final -struct NoWrapperInheritance : public TestSimple { }; // expected-error {{base 'TestSimple' is marked 'final'}} +// expected-error@+2 {{named barrier wrapper 'WrapperWithBaseNoStandardLayout' must have a C++11 standard layou}} +// expected-note@+1 {{'WrapperWithBaseNoStandardLayout' is a named barrier wrapper because it inherits from named barrier wrapper 'WrapperBase'}} +struct WrapperWithBaseNoStandardLayout : public WrapperBase { + unsigned K = 0; +}; // Wrappers of Wrappers have the same restrictions. -struct WrapperOfWrapperWithTooManyFields final { // expected-note {{WrapperOfWrapperWithTooManyFields is not a named barrier wrapper because it has more than one field}} +struct WrapperOfWrapperWithTooManyFields { // expected-note {{'WrapperOfWrapperWithTooManyFields' is not a named barrier wrapper because it has more than one field}} TestSimple x; // expected-error {{fields of type 'TestSimple' are only allowed in named barrier wrappers}} int other; }; +struct WrapperOfWrapper { + WrapperWithBase y; +}; + +// Check templated cases with a bit of complexity thrown in. +template<typename Derived> +class CRTPWrapperBase { + CRTPWrapperBase() { + static_cast<Derived*>(this)->sayHello(); + } + + WrapperOfWrapper wow; +}; + +class TemplatedWrapperImpl : public CRTPWrapperBase<TemplatedWrapperImpl> { + void sayHello() {} +}; + +// expected-error@+2 {{named barrier wrapper 'TemplatedWrapperImplNoStandardLayout' must have a C++11 standard layou}} +// expected-note@+1 {{'TemplatedWrapperImplNoStandardLayout' is a named barrier wrapper because it inherits from named barrier wrapper 'CRTPWrapperBase<TemplatedWrapperImpl>'}} +class TemplatedWrapperImplNoStandardLayout : public CRTPWrapperBase<TemplatedWrapperImpl> { + void sayHello() {} + + int k = 0; +}; + static_assert(sizeof(__amdgpu_named_workgroup_barrier_t) == 16, "wrong size"); static_assert(alignof(__amdgpu_named_workgroup_barrier_t) == 4, "wrong alignment"); diff --git a/clang/test/SemaHIP/amdgpu-barrier.hip b/clang/test/SemaHIP/amdgpu-barrier.hip index a0baaaa3bf899..e8cd52d77d4a5 100644 --- a/clang/test/SemaHIP/amdgpu-barrier.hip +++ b/clang/test/SemaHIP/amdgpu-barrier.hip @@ -18,10 +18,8 @@ __device__ void foo() { using SugaredArray = __amdgpu_named_workgroup_barrier_t[2]; -// expected-note@+2 {{'TestSimple' is a named barrier wrapper around 'x', which implies 'final'}} -// expected-note@+1 {{'TestSimple' declared here}} struct TestSimple { - __amdgpu_named_workgroup_barrier_t x; // expected-note {{'x' declared here}} + __amdgpu_named_workgroup_barrier_t x; }; struct TestArray{ @@ -38,27 +36,57 @@ struct TestSimpleWithStaticField { }; // Wrappers cannot have >1 field. -struct WrapperHasTooManyFields { // expected-note {{WrapperHasTooManyFields is not a named barrier wrapper because it has more than one field}} +struct WrapperHasTooManyFields { // expected-note {{'WrapperHasTooManyFields' is not a named barrier wrapper because it has more than one field}} __amdgpu_named_workgroup_barrier_t x; // expected-error {{fields of type '__amdgpu_named_workgroup_barrier_t' are only allowed in named barrier wrappers}} int other; }; -// Wrappers cannot have base class +// Wrappers must have standard layout struct WrapperBase { +__amdgpu_named_workgroup_barrier_t x; }; -struct WrapperWithBase : public WrapperBase { // expected-note {{WrapperWithBase is not a named barrier wrapper because it inherits from 'WrapperBase'; named barrier wrappers may not have any bases}} - __amdgpu_named_workgroup_barrier_t x; // expected-error {{fields of type '__amdgpu_named_workgroup_barrier_t' are only allowed in named barrier wrappers}} +struct WrapperWithBase : public WrapperBase { }; -// Wrappers imply final -struct NoWrapperInheritance : public TestSimple { }; // expected-error {{base 'TestSimple' is marked 'final'}} +// expected-error@+2 {{named barrier wrapper 'WrapperWithBaseNoStandardLayout' must have a C++11 standard layou}} +// expected-note@+1 {{'WrapperWithBaseNoStandardLayout' is a named barrier wrapper because it inherits from named barrier wrapper 'WrapperBase'}} +struct WrapperWithBaseNoStandardLayout : public WrapperBase { + unsigned K = 0; +}; // Wrappers of Wrappers have the same restrictions. -struct WrapperOfWrapperWithTooManyFields final { // expected-note {{WrapperOfWrapperWithTooManyFields is not a named barrier wrapper because it has more than one field}} +struct WrapperOfWrapperWithTooManyFields { // expected-note {{'WrapperOfWrapperWithTooManyFields' is not a named barrier wrapper because it has more than one field}} TestSimple x; // expected-error {{fields of type 'TestSimple' are only allowed in named barrier wrappers}} int other; }; +struct WrapperOfWrapper { + WrapperWithBase y; +}; + +// Check templated cases with a bit of complexity thrown in. +template<typename Derived> +class CRTPWrapperBase { + CRTPWrapperBase() { + static_cast<Derived*>(this)->sayHello(); + } + + WrapperOfWrapper wow; +}; + +class TemplatedWrapperImpl : public CRTPWrapperBase<TemplatedWrapperImpl> { + void sayHello() {} +}; + +// expected-error@+2 {{named barrier wrapper 'TemplatedWrapperImplNoStandardLayout' must have a C++11 standard layou}} +// expected-note@+1 {{'TemplatedWrapperImplNoStandardLayout' is a named barrier wrapper because it inherits from named barrier wrapper 'CRTPWrapperBase<TemplatedWrapperImpl>'}} +class TemplatedWrapperImplNoStandardLayout : public CRTPWrapperBase<TemplatedWrapperImpl> { + void sayHello() {} + + int k = 0; +}; + + static_assert(sizeof(__amdgpu_named_workgroup_barrier_t) == 16, "wrong size"); static_assert(alignof(__amdgpu_named_workgroup_barrier_t) == 4, "wrong alignment"); diff --git a/clang/test/SemaOpenCL/amdgpu-barrier.cl b/clang/test/SemaOpenCL/amdgpu-barrier.cl index 2f96a6aba3a3e..0898b1642f96f 100644 --- a/clang/test/SemaOpenCL/amdgpu-barrier.cl +++ b/clang/test/SemaOpenCL/amdgpu-barrier.cl @@ -22,7 +22,7 @@ void foo() { }; // Wrappers cannot have >1 field. - struct WrapperHasTooManyFields { // expected-note {{WrapperHasTooManyFields is not a named barrier wrapper because it has more than one field}} + struct WrapperHasTooManyFields { // expected-note {{'WrapperHasTooManyFields' is not a named barrier wrapper because it has more than one field}} __amdgpu_named_workgroup_barrier_t x; // expected-error {{fields of type '__amdgpu_named_workgroup_barrier_t' are only allowed in named barrier wrappers}} int other; }; >From fda169c9b8fe1d1e3e440655e6fc7241d0dd5107 Mon Sep 17 00:00:00 2001 From: pvanhout <[email protected]> Date: Thu, 9 Jul 2026 11:00:32 +0200 Subject: [PATCH 7/7] Revert earlier change --- clang/include/clang/Basic/DiagnosticSemaKinds.td | 2 +- clang/lib/Sema/SemaDecl.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 2db846d6e7189..ff38e1aca5426 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11855,7 +11855,7 @@ def note_within_param_of_type : Note< "within parameter %0 of type %1 declared here">; def note_illegal_field_declared_here : Note< "field of illegal %select{type|pointer type}0 %1 declared here">; -def err_invalid_type_for_struct_or_union_field : Error< +def err_opencl_type_struct_or_union_field : Error< "the %0 type cannot be used to declare a structure or union field">; def err_event_t_addr_space_qual : Error< "the event_t type can only be used with __private address space qualifier">; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index cacf015b02b95..295402630fddc 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -19435,7 +19435,7 @@ FieldDecl *Sema::CheckFieldDecl(DeclarationName Name, QualType T, // used as structure or union field: image, sampler, event or block types. if (T->isEventT() || T->isImageType() || T->isSamplerT() || T->isBlockPointerType()) { - Diag(Loc, diag::err_invalid_type_for_struct_or_union_field) << T; + Diag(Loc, diag::err_opencl_type_struct_or_union_field) << T; Record->setInvalidDecl(); InvalidDecl = true; } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
