diff --git a/clang/include/clang/Basic/AddressSpaces.h b/clang/include/clang/Basic/AddressSpaces.h index d18bfe54931f9..c095ecf64272e 100644 --- a/clang/include/clang/Basic/AddressSpaces.h +++ b/clang/include/clang/Basic/AddressSpaces.h @@ -59,6 +59,7 @@ enum class LangAS : unsigned { // HLSL specific address spaces. hlsl_groupshared, hlsl_constant, + hlsl_device, // Wasm specific address spaces. wasm_funcref, diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 72161c06a88d4..0afc49287107a 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -94,6 +94,11 @@ bool Qualifiers::isTargetAddressSpaceSupersetOf(LangAS A, LangAS B, (A == LangAS::Default && (B == LangAS::cuda_constant || B == LangAS::cuda_device || B == LangAS::cuda_shared)) || + // In HLSL, the this pointer for member functions is in the default + // address space. This causes problem if the structure is in + // hlsl_device. We want to allow casting from hlsl_device to default + // until a proper solution for that issue is found. + (A == LangAS::Default && B == LangAS::hlsl_device) || // Conversions from target specific address spaces may be legal // depending on the target information. Ctx.getTargetInfo().isAddressSpaceSupersetOf(A, B); diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index dfd1959bbdcb0..9db12a698867d 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -2579,6 +2579,8 @@ std::string Qualifiers::getAddrSpaceAsString(LangAS AS) { return "groupshared"; case LangAS::hlsl_constant: return "hlsl_constant"; + case LangAS::hlsl_device: + return "hlsl_device"; case LangAS::wasm_funcref: return "__funcref"; default: diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h index 9b6451bd06316..ea4db8eac8060 100644 --- a/clang/lib/Basic/Targets/AArch64.h +++ b/clang/lib/Basic/Targets/AArch64.h @@ -45,6 +45,7 @@ static const unsigned ARM64AddrSpaceMap[] = { static_cast(AArch64AddrSpace::ptr64), 0, // hlsl_groupshared 0, // hlsl_constant + 0, // hlsl_device // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref diff --git a/clang/lib/Basic/Targets/AMDGPU.cpp b/clang/lib/Basic/Targets/AMDGPU.cpp index a42b4589fb5ac..30bfa8c5b6259 100644 --- a/clang/lib/Basic/Targets/AMDGPU.cpp +++ b/clang/lib/Basic/Targets/AMDGPU.cpp @@ -60,6 +60,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsGenMap = { llvm::AMDGPUAS::FLAT_ADDRESS, // ptr64 llvm::AMDGPUAS::FLAT_ADDRESS, // hlsl_groupshared llvm::AMDGPUAS::CONSTANT_ADDRESS, // hlsl_constant + llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device }; const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = { @@ -85,6 +86,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = { llvm::AMDGPUAS::FLAT_ADDRESS, // ptr64 llvm::AMDGPUAS::FLAT_ADDRESS, // hlsl_groupshared llvm::AMDGPUAS::CONSTANT_ADDRESS, // hlsl_constant + llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device }; } // namespace targets } // namespace clang diff --git a/clang/lib/Basic/Targets/DirectX.h b/clang/lib/Basic/Targets/DirectX.h index 6e3ddad626341..8f9243654a587 100644 --- a/clang/lib/Basic/Targets/DirectX.h +++ b/clang/lib/Basic/Targets/DirectX.h @@ -43,6 +43,7 @@ static const unsigned DirectXAddrSpaceMap[] = { 0, // ptr64 3, // hlsl_groupshared 2, // hlsl_constant + 1, // hlsl_device // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h index 6a868c42e1265..81c3222f4c1fc 100644 --- a/clang/lib/Basic/Targets/NVPTX.h +++ b/clang/lib/Basic/Targets/NVPTX.h @@ -47,6 +47,7 @@ static const unsigned NVPTXAddrSpaceMap[] = { 0, // ptr64 0, // hlsl_groupshared 0, // hlsl_constant + 0, // hlsl_device // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h index 78505d66d6f2f..f84606742d439 100644 --- a/clang/lib/Basic/Targets/SPIR.h +++ b/clang/lib/Basic/Targets/SPIR.h @@ -38,16 +38,17 @@ static const unsigned SPIRDefIsPrivMap[] = { 0, // cuda_constant 0, // cuda_shared // SYCL address space values for this map are dummy - 0, // sycl_global - 0, // sycl_global_device - 0, // sycl_global_host - 0, // sycl_local - 0, // sycl_private - 0, // ptr32_sptr - 0, // ptr32_uptr - 0, // ptr64 - 0, // hlsl_groupshared - 2, // hlsl_constant + 0, // sycl_global + 0, // sycl_global_device + 0, // sycl_global_host + 0, // sycl_local + 0, // sycl_private + 0, // ptr32_sptr + 0, // ptr32_uptr + 0, // ptr64 + 0, // hlsl_groupshared + 2, // hlsl_constant + 11, // hlsl_device // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref @@ -70,18 +71,19 @@ static const unsigned SPIRDefIsGenMap[] = { // cuda_constant pointer can be casted to default/"flat" pointer, but in // SPIR-V casts between constant and generic pointers are not allowed. For // this reason cuda_constant is mapped to SPIR-V CrossWorkgroup. - 1, // cuda_constant - 3, // cuda_shared - 1, // sycl_global - 5, // sycl_global_device - 6, // sycl_global_host - 3, // sycl_local - 0, // sycl_private - 0, // ptr32_sptr - 0, // ptr32_uptr - 0, // ptr64 - 0, // hlsl_groupshared - 0, // hlsl_constant + 1, // cuda_constant + 3, // cuda_shared + 1, // sycl_global + 5, // sycl_global_device + 6, // sycl_global_host + 3, // sycl_local + 0, // sycl_private + 0, // ptr32_sptr + 0, // ptr32_uptr + 0, // ptr64 + 0, // hlsl_groupshared + 2, // hlsl_constant + 11, // hlsl_device // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h index 4ca3f53f83cba..7da4d73523f30 100644 --- a/clang/lib/Basic/Targets/SystemZ.h +++ b/clang/lib/Basic/Targets/SystemZ.h @@ -43,6 +43,7 @@ static const unsigned ZOSAddressMap[] = { 0, // ptr64 0, // hlsl_groupshared 0, // hlsl_constant + 0, // hlsl_device 0 // wasm_funcref }; diff --git a/clang/lib/Basic/Targets/TCE.h b/clang/lib/Basic/Targets/TCE.h index 46c70de8f9ec1..d13055be66805 100644 --- a/clang/lib/Basic/Targets/TCE.h +++ b/clang/lib/Basic/Targets/TCE.h @@ -52,6 +52,7 @@ static const unsigned TCEOpenCLAddrSpaceMap[] = { 0, // ptr64 0, // hlsl_groupshared 0, // hlsl_constant + 0, // hlsl_device // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h index fb48c786a7edb..4e546b2b91c7a 100644 --- a/clang/lib/Basic/Targets/WebAssembly.h +++ b/clang/lib/Basic/Targets/WebAssembly.h @@ -43,6 +43,7 @@ static const unsigned WebAssemblyAddrSpaceMap[] = { 0, // ptr64 0, // hlsl_groupshared 0, // hlsl_constant + 0, // hlsl_device 20, // wasm_funcref }; diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h index 205edcab9ccb3..d40708fc04b67 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -47,6 +47,7 @@ static const unsigned X86AddrSpaceMap[] = { 272, // ptr64 0, // hlsl_groupshared 0, // hlsl_constant + 0, // hlsl_device // Wasm address space values for this target are dummy values, // as it is only enabled for Wasm targets. 20, // wasm_funcref diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index c126f88b9e3a5..f41285e289b3a 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -19573,9 +19573,7 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Value *HandleOp = EmitScalarExpr(E->getArg(0)); Value *IndexOp = EmitScalarExpr(E->getArg(1)); - // TODO: Map to an hlsl_device address space. - llvm::Type *RetTy = llvm::PointerType::getUnqual(getLLVMContext()); - + llvm::Type *RetTy = ConvertType(E->getType()); return Builder.CreateIntrinsic( RetTy, CGM.getHLSLRuntime().getCreateResourceGetPointerIntrinsic(), ArrayRef{HandleOp, IndexOp}); diff --git a/clang/lib/CodeGen/Targets/SPIR.cpp b/clang/lib/CodeGen/Targets/SPIR.cpp index 43f511e572d37..c0dfdf8a0148a 100644 --- a/clang/lib/CodeGen/Targets/SPIR.cpp +++ b/clang/lib/CodeGen/Targets/SPIR.cpp @@ -386,13 +386,27 @@ llvm::Type *CommonSPIRTargetCodeGenInfo::getHLSLType( if (ContainedTy.isNull()) return nullptr; - assert(!ResAttrs.RawBuffer && - "Raw buffers handles are not implemented for SPIR-V yet"); assert(!ResAttrs.IsROV && "Rasterizer order views not implemented for SPIR-V yet"); - // convert element type llvm::Type *ElemType = CGM.getTypes().ConvertType(ContainedTy); + if (ResAttrs.RawBuffer) { + // TODO: Handle types with layout information. + assert((ElemType->isIntegerTy() || ElemType->isFloatingPointTy() || + ElemType->isVectorTy()) && + "The element type for a SPIR-V resource must be a types that does " + "not require layout information."); + llvm::ArrayType *RuntimeArrayType = llvm::ArrayType::get(ElemType, 0); + + uint32_t StorageClass = /* StorageBuffer storage class */ 12; + bool IsWritable = + ResAttrs.ResourceClass == llvm::dxil::ResourceClass::UAV; + return llvm::TargetExtType::get(Ctx, "spirv.VulkanBuffer", + {RuntimeArrayType}, + {StorageClass, IsWritable}); + } + + // convert element type return getSPIRVImageTypeFromHLSLResource(ResAttrs, ElemType, Ctx); } case llvm::dxil::ResourceClass::CBuffer: diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index cfa49029a2fb1..d028fa914e462 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -711,13 +711,14 @@ BuiltinTypeDeclBuilder::addHandleAccessFunction(DeclarationName &Name, using PH = BuiltinTypeMethodBuilder::PlaceHolder; QualType ElemTy = getHandleElementType(); - // TODO: Map to an hlsl_device address space. - QualType ElemPtrTy = AST.getPointerType(ElemTy); - QualType ReturnTy = ElemTy; + QualType AddrSpaceElemTy = + AST.getAddrSpaceQualType(ElemTy, LangAS::hlsl_device); + QualType ElemPtrTy = AST.getPointerType(AddrSpaceElemTy); + // QualType ReturnTy = (IsRef ? AST.getLValueReferenceType(ElemTy) : ElemTy); + QualType ReturnTy = + (IsRef ? AST.getLValueReferenceType(AddrSpaceElemTy) : ElemTy); if (IsConst) ReturnTy.addConst(); - if (IsRef) - ReturnTy = AST.getLValueReferenceType(ReturnTy); return BuiltinTypeMethodBuilder(*this, Name, ReturnTy, IsConst) .addParam("Index", AST.UnsignedIntTy) @@ -731,12 +732,15 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addAppendMethod() { using PH = BuiltinTypeMethodBuilder::PlaceHolder; ASTContext &AST = SemaRef.getASTContext(); QualType ElemTy = getHandleElementType(); + QualType AddrSpaceElemTy = + AST.getAddrSpaceQualType(ElemTy, LangAS::hlsl_device); return BuiltinTypeMethodBuilder(*this, "Append", AST.VoidTy) .addParam("value", ElemTy) .callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy, PH::Handle, getConstantIntExpr(1)) .callBuiltin("__builtin_hlsl_resource_getpointer", - AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt) + AST.getPointerType(AddrSpaceElemTy), PH::Handle, + PH::LastStmt) .dereference(PH::LastStmt) .assign(PH::LastStmt, PH::_0) .finalizeMethod(); @@ -746,11 +750,14 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConsumeMethod() { using PH = BuiltinTypeMethodBuilder::PlaceHolder; ASTContext &AST = SemaRef.getASTContext(); QualType ElemTy = getHandleElementType(); + QualType AddrSpaceElemTy = + AST.getAddrSpaceQualType(ElemTy, LangAS::hlsl_device); return BuiltinTypeMethodBuilder(*this, "Consume", ElemTy) .callBuiltin("__builtin_hlsl_buffer_update_counter", AST.UnsignedIntTy, PH::Handle, getConstantIntExpr(-1)) .callBuiltin("__builtin_hlsl_resource_getpointer", - AST.getPointerType(ElemTy), PH::Handle, PH::LastStmt) + AST.getPointerType(AddrSpaceElemTy), PH::Handle, + PH::LastStmt) .dereference(PH::LastStmt) .finalizeMethod(); } diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 07d03e2c58b9a..975275e0377c9 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -2378,8 +2378,10 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { auto *ResourceTy = TheCall->getArg(0)->getType()->castAs(); QualType ContainedTy = ResourceTy->getContainedType(); - // TODO: Map to an hlsl_device address space. - TheCall->setType(getASTContext().getPointerType(ContainedTy)); + auto returnType = + SemaRef.Context.getAddrSpaceQualType(ContainedTy, LangAS::hlsl_device); + returnType = SemaRef.Context.getPointerType(returnType); + TheCall->setType(returnType); TheCall->setValueKind(VK_LValue); break; diff --git a/clang/test/AST/HLSL/OutArgExpr.hlsl b/clang/test/AST/HLSL/OutArgExpr.hlsl index b07c2efadbf4a..04ce635e721bf 100644 --- a/clang/test/AST/HLSL/OutArgExpr.hlsl +++ b/clang/test/AST/HLSL/OutArgExpr.hlsl @@ -30,10 +30,10 @@ void zero(out int Z) { Z = 0; } // AST-NEXT: ImplicitCastExpr {{.*}} 'void (*)(inout int)' // AST-NEXT: DeclRefExpr {{.*}} 'void (inout int)' lvalue Function // AST-NEXT: HLSLOutArgExpr {{.*}} 'int' lvalue inout -// AST-NEXT: OpaqueValueExpr [[LVOpV:0x[0-9a-fA-F]+]] {{.*}} 'float' lvalue -// AST-NEXT: CXXOperatorCallExpr {{.*}} 'float' lvalue '[]' -// AST-NEXT: ImplicitCastExpr {{.*}} 'float &(*)(unsigned int)' -// AST-NEXT: DeclRefExpr {{.*}} 'float &(unsigned int)' lvalue CXXMethod {{.*}} 'operator[]' 'float &(unsigned int)' +// AST-NEXT: OpaqueValueExpr [[LVOpV:0x[0-9a-fA-F]+]] {{.*}} 'hlsl_device float' lvalue +// AST-NEXT: CXXOperatorCallExpr {{.*}} 'hlsl_device float' lvalue '[]' +// AST-NEXT: ImplicitCastExpr {{.*}} 'hlsl_device float &(*)(unsigned int)' +// AST-NEXT: DeclRefExpr {{.*}} 'hlsl_device float &(unsigned int)' lvalue CXXMethod {{.*}} 'operator[]' 'hlsl_device float &(unsigned int)' // AST-NEXT: DeclRefExpr {{.*}} 'RWBuffer':'hlsl::RWBuffer' lvalue Var {{.*}} 'Buf' 'RWBuffer':'hlsl::RWBuffer' // AST-NEXT: ImplicitCastExpr {{.*}} 'uint':'unsigned int' // AST-NEXT: DeclRefExpr {{.*}} 'uint':'unsigned int' lvalue ParmVar {{.*}} 'GI' 'uint':'unsigned int' @@ -41,10 +41,10 @@ void zero(out int Z) { Z = 0; } // AST-NEXT: OpaqueValueExpr [[TmpOpV:0x[0-9a-fA-F]+]] {{.*}} 'int' lvalue // AST-NEXT: ImplicitCastExpr {{.*}} 'int' // AST-NEXT: ImplicitCastExpr {{.*}} 'float' -// AST-NEXT: OpaqueValueExpr [[LVOpV]] 'float' lvalue +// AST-NEXT: OpaqueValueExpr [[LVOpV]] 'hlsl_device float' lvalue -// AST: BinaryOperator {{.*}} 'float' lvalue '=' -// AST-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'float' lvalue +// AST: BinaryOperator {{.*}} 'hlsl_device float' lvalue '=' +// AST-NEXT: OpaqueValueExpr [[LVOpV]] {{.*}} 'hlsl_device float' lvalue // AST: ImplicitCastExpr {{.*}} 'float' // AST-NEXT: ImplicitCastExpr {{.*}} 'int' // AST-NEXT: OpaqueValueExpr [[TmpOpV]] {{.*}} 'int' lvalue diff --git a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl index dcead068f481e..75dd4fe23121b 100644 --- a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl +++ b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl @@ -89,12 +89,12 @@ RESOURCE Buffer; // CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] -// CHECK-SUBSCRIPT: CXXMethodDecl {{.*}} operator[] 'const element_type &(unsigned int) const' +// CHECK-SUBSCRIPT: CXXMethodDecl {{.*}} operator[] 'hlsl_device element_type &const (unsigned int) const' // CHECK-SUBSCRIPT-NEXT: ParmVarDecl {{.*}} Index 'unsigned int' // CHECK-SUBSCRIPT-NEXT: CompoundStmt // CHECK-SUBSCRIPT-NEXT: ReturnStmt -// CHECK-SUBSCRIPT-NEXT: UnaryOperator {{.*}} 'element_type' prefix '*' cannot overflow -// CHECK-SUBSCRIPT-NEXT: CallExpr {{.*}} 'element_type *' +// CHECK-SUBSCRIPT-NEXT: UnaryOperator {{.*}} 'hlsl_device element_type' prefix '*' cannot overflow +// CHECK-SUBSCRIPT-NEXT: CallExpr {{.*}} 'hlsl_device element_type *' // CHECK-SUBSCRIPT-NEXT: DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_hlsl_resource_getpointer' 'void (...) noexcept' // CHECK-SUBSCRIPT-NEXT: MemberExpr {{.*}} '__hlsl_resource_t // CHECK-SUBSCRIPT-SAME{LITERAL}: [[hlsl::resource_class( @@ -105,12 +105,12 @@ RESOURCE Buffer; // CHECK-SUBSCRIPT-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'Index' 'unsigned int' // CHECK-SUBSCRIPT-NEXT: AlwaysInlineAttr {{.*}} Implicit always_inline -// CHECK-SUBSCRIPT-NEXT: CXXMethodDecl {{.*}} operator[] 'element_type &(unsigned int)' +// CHECK-SUBSCRIPT-NEXT: CXXMethodDecl {{.*}} operator[] 'hlsl_device element_type &(unsigned int)' // CHECK-SUBSCRIPT-NEXT: ParmVarDecl {{.*}} Index 'unsigned int' // CHECK-SUBSCRIPT-NEXT: CompoundStmt // CHECK-SUBSCRIPT-NEXT: ReturnStmt -// CHECK-SUBSCRIPT-NEXT: UnaryOperator {{.*}} 'element_type' prefix '*' cannot overflow -// CHECK-SUBSCRIPT-NEXT: CallExpr {{.*}} 'element_type *' +// CHECK-SUBSCRIPT-NEXT: UnaryOperator {{.*}} 'hlsl_device element_type' prefix '*' cannot overflow +// CHECK-SUBSCRIPT-NEXT: CallExpr {{.*}} 'hlsl_device element_type *' // CHECK-SUBSCRIPT-NEXT: DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_hlsl_resource_getpointer' 'void (...) noexcept' // CHECK-SUBSCRIPT-NEXT: MemberExpr {{.*}} '__hlsl_resource_t // CHECK-SUBSCRIPT-SAME{LITERAL}: [[hlsl::resource_class( @@ -121,15 +121,15 @@ RESOURCE Buffer; // CHECK-SUBSCRIPT-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'Index' 'unsigned int' // CHECK-SUBSCRIPT-NEXT: AlwaysInlineAttr {{.*}} Implicit always_inline -// CHECK-NOSUBSCRIPT-NOT: CXXMethodDecl {{.*}} operator[] 'const element_type &(unsigned int) const' -// CHECK-NOSUBSCRIPT-NOT: CXXMethodDecl {{.*}} operator[] 'element_type &(unsigned int)' +// CHECK-NOSUBSCRIPT-NOT: CXXMethodDecl {{.*}} operator[] 'hlsl_device element_type &const (unsigned int) const' +// CHECK-NOSUBSCRIPT-NOT: CXXMethodDecl {{.*}} operator[] 'hlsl_device element_type &(unsigned int)' // CHECK-LOAD: CXXMethodDecl {{.*}} Load 'element_type (unsigned int)' // CHECK-LOAD-NEXT: ParmVarDecl {{.*}} Index 'unsigned int' // CHECK-LOAD-NEXT: CompoundStmt // CHECK-LOAD-NEXT: ReturnStmt -// CHECK-LOAD-NEXT: UnaryOperator {{.*}} 'element_type' prefix '*' cannot overflow -// CHECK-LOAD-NEXT: CallExpr {{.*}} 'element_type *' +// CHECK-LOAD-NEXT: UnaryOperator {{.*}} 'hlsl_device element_type' prefix '*' cannot overflow +// CHECK-LOAD-NEXT: CallExpr {{.*}} 'hlsl_device element_type *' // CHECK-LOAD-NEXT: DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_hlsl_resource_getpointer' 'void (...) noexcept' // CHECK-LOAD-NEXT: MemberExpr {{.*}} '__hlsl_resource_t // CHECK-LOAD-SAME{LITERAL}: [[hlsl::resource_class( @@ -168,9 +168,9 @@ RESOURCE Buffer; // CHECK-APPEND: CXXMethodDecl {{.*}} Append 'void (element_type)' // CHECK-APPEND-NEXT: ParmVarDecl {{.*}} value 'element_type' // CHECK-APPEND-NEXT: CompoundStmt -// CHECK-APPEND-NEXT: BinaryOperator {{.*}} 'element_type' '=' -// CHECK-APPEND-NEXT: UnaryOperator {{.*}} 'element_type' prefix '*' cannot overflow -// CHECK-APPEND-NEXT: CallExpr {{.*}} 'element_type *' +// CHECK-APPEND-NEXT: BinaryOperator {{.*}} 'hlsl_device element_type' '=' +// CHECK-APPEND-NEXT: UnaryOperator {{.*}} 'hlsl_device element_type' prefix '*' cannot overflow +// CHECK-APPEND-NEXT: CallExpr {{.*}} 'hlsl_device element_type *' // CHECK-APPEND-NEXT: DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_hlsl_resource_getpointer' 'void (...) noexcept' // CHECK-APPEND-NEXT: MemberExpr {{.*}} '__hlsl_resource_t // CHECK-APPEND-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] @@ -190,8 +190,8 @@ RESOURCE Buffer; // CHECK-CONSUME: CXXMethodDecl {{.*}} Consume 'element_type ()' // CHECK-CONSUME-NEXT: CompoundStmt // CHECK-CONSUME-NEXT: ReturnStmt -// CHECK-CONSUME-NEXT: UnaryOperator {{.*}} 'element_type' prefix '*' cannot overflow -// CHECK-CONSUME-NEXT: CallExpr {{.*}} 'element_type *' +// CHECK-CONSUME-NEXT: UnaryOperator {{.*}} 'hlsl_device element_type' prefix '*' cannot overflow +// CHECK-CONSUME-NEXT: CallExpr {{.*}} 'hlsl_device element_type *' // CHECK-CONSUME-NEXT: DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_hlsl_resource_getpointer' 'void (...) noexcept' // CHECK-CONSUME-NEXT: MemberExpr {{.*}} '__hlsl_resource_t // CHECK-CONSUME-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] diff --git a/clang/test/AST/HLSL/TypedBuffers-AST.hlsl b/clang/test/AST/HLSL/TypedBuffers-AST.hlsl index f665b06d691e8..1da474469c0c0 100644 --- a/clang/test/AST/HLSL/TypedBuffers-AST.hlsl +++ b/clang/test/AST/HLSL/TypedBuffers-AST.hlsl @@ -56,32 +56,32 @@ RESOURCE Buffer; // CHECK-UAV-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] -// CHECK: CXXMethodDecl {{.*}} operator[] 'const element_type &(unsigned int) const' +// CHECK: CXXMethodDecl {{.*}} operator[] 'hlsl_device element_type &const (unsigned int) const' // CHECK-NEXT: ParmVarDecl {{.*}} Index 'unsigned int' // CHECK-NEXT: CompoundStmt // CHECK-NEXT: ReturnStmt -// CHECK-NEXT: UnaryOperator {{.*}} 'element_type' prefix '*' cannot overflow -// CHECK-NEXT: CallExpr {{.*}} 'element_type *' +// CHECK-NEXT: UnaryOperator {{.*}} 'hlsl_device element_type' prefix '*' cannot overflow +// CHECK-NEXT: CallExpr {{.*}} 'hlsl_device element_type *' // CHECK-NEXT: DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_hlsl_resource_getpointer' 'void (...) noexcept' // CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] -// CHECK-SAME: ' lvalue .__handle {{.*}} +// CHECK-SAME: ' lvalue .__handle {{.*}} // CHECK-NEXT: CXXThisExpr {{.*}} 'const [[RESOURCE]]' lvalue implicit this // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'Index' 'unsigned int' // CHECK-NEXT: AlwaysInlineAttr {{.*}} Implicit always_inline -// CHECK-NEXT: CXXMethodDecl {{.*}} operator[] 'element_type &(unsigned int)' +// CHECK-NEXT: CXXMethodDecl {{.*}} operator[] 'hlsl_device element_type &(unsigned int)' // CHECK-NEXT: ParmVarDecl {{.*}} Index 'unsigned int' // CHECK-NEXT: CompoundStmt // CHECK-NEXT: ReturnStmt -// CHECK-NEXT: UnaryOperator {{.*}} 'element_type' prefix '*' cannot overflow -// CHECK-NEXT: CallExpr {{.*}} 'element_type *' +// CHECK-NEXT: UnaryOperator {{.*}} 'hlsl_device element_type' prefix '*' cannot overflow +// CHECK-NEXT: CallExpr {{.*}} 'hlsl_device element_type *' // CHECK-NEXT: DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_hlsl_resource_getpointer' 'void (...) noexcept' // CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] -// CHECK-SAME: ' lvalue .__handle {{.*}} +// CHECK-SAME: ' lvalue .__handle {{.*}} // CHECK-NEXT: CXXThisExpr {{.*}} '[[RESOURCE]]' lvalue implicit this // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'Index' 'unsigned int' // CHECK-NEXT: AlwaysInlineAttr {{.*}} Implicit always_inline @@ -90,13 +90,13 @@ RESOURCE Buffer; // CHECK-NEXT: ParmVarDecl {{.*}} Index 'unsigned int' // CHECK-NEXT: CompoundStmt // CHECK-NEXT: ReturnStmt -// CHECK-NEXT: UnaryOperator {{.*}} 'element_type' prefix '*' cannot overflow -// CHECK-NEXT: CallExpr {{.*}} 'element_type *' +// CHECK-NEXT: UnaryOperator {{.*}} 'hlsl_device element_type' prefix '*' cannot overflow +// CHECK-NEXT: CallExpr {{.*}} 'hlsl_device element_type *' // CHECK-NEXT: DeclRefExpr {{.*}} '' Function {{.*}} '__builtin_hlsl_resource_getpointer' 'void (...) noexcept' // CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] -// CHECK-SAME: ' lvalue .__handle {{.*}} +// CHECK-SAME: ' lvalue .__handle {{.*}} // CHECK-NEXT: CXXThisExpr {{.*}} '[[RESOURCE]]' lvalue implicit this // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' ParmVar {{.*}} 'Index' 'unsigned int' // CHECK-NEXT: AlwaysInlineAttr {{.*}} Implicit always_inline diff --git a/clang/test/CodeGenHLSL/builtins/RWBuffer-subscript.hlsl b/clang/test/CodeGenHLSL/builtins/RWBuffer-subscript.hlsl index 2ad5b82a02912..39f77eac0528d 100644 --- a/clang/test/CodeGenHLSL/builtins/RWBuffer-subscript.hlsl +++ b/clang/test/CodeGenHLSL/builtins/RWBuffer-subscript.hlsl @@ -8,19 +8,19 @@ RWBuffer Out; void main(unsigned GI : SV_GroupIndex) { // CHECK: define void @main() - // DXC: %[[INPTR:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}}) - // SPIRV: %[[INPTR:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_i32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) - // CHECK: %[[LOAD:.*]] = load i32, ptr %[[INPTR]] - // DXC: %[[OUTPTR:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}}) - // SPIRV: %[[OUTPTR:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_i32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) - // CHECK: store i32 %[[LOAD]], ptr %[[OUTPTR]] + // DXC: %[[INPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}}) + // SPIRV: %[[INPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_i32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) + // CHECK: %[[LOAD:.*]] = load i32, ptr addrspace({{[0-9]+}}) %[[INPTR]] + // DXC: %[[OUTPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}}) + // SPIRV: %[[OUTPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_i32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) + // CHECK: store i32 %[[LOAD]], ptr addrspace({{[0-9]+}}) %[[OUTPTR]] Out[GI] = In[GI]; - // DXC: %[[INPTR:.*]] = call ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}}) - // SPIRV: %[[INPTR:.*]] = call ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_i32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) - // CHECK: %[[LOAD:.*]] = load i32, ptr %[[INPTR]] - // DXC: %[[OUTPTR:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}}) - // SPIRV: %[[OUTPTR:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.spv.resource.getpointer.p0.tspirv.Image_i32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) - // CHECK: store i32 %[[LOAD]], ptr %[[OUTPTR]] + // DXC: %[[INPTR:.*]] = call ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}}) + // SPIRV: %[[INPTR:.*]] = call ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_i32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) + // CHECK: %[[LOAD:.*]] = load i32, ptr addrspace({{[0-9]+}}) %[[INPTR]] + // DXC: %[[OUTPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.TypedBuffer_i32_1_0_1t(target("dx.TypedBuffer", i32, 1, 0, 1) %{{.*}}, i32 %{{.*}}) + // SPIRV: %[[OUTPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.Image_i32_5_2_0_0_2_0t(target("spirv.Image", i32, 5, 2, 0, 0, 2, 0) %{{.*}}, i32 %{{.*}}) + // CHECK: store i32 %[[LOAD]], ptr addrspace({{[0-9]+}}) %[[OUTPTR]] Out[GI] = In.Load(GI); } diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl index 04534c5550252..8a1429fd1a6fc 100644 --- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl +++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-constructors.hlsl @@ -1,59 +1,70 @@ // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -// RUN-DISABLED: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV +// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -DSPIRV -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV -// NOTE: SPIRV codegen for resource types is not yet implemented StructuredBuffer Buf : register(t10); RWStructuredBuffer Buf2 : register(u5, space1); + +#ifndef SPIRV +// NOTE: SPIRV codegen for these resource types is not implemented yet. AppendStructuredBuffer Buf3 : register(u3); ConsumeStructuredBuffer Buf4 : register(u4); RasterizerOrderedStructuredBuffer Buf5 : register(u1, space2); +#endif + +// CHECK-DXIL: %"class.hlsl::StructuredBuffer" = type { target("dx.RawBuffer", float, 0, 0) } +// CHECK-DXIL: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) } +// CHECK-DXIL: %"class.hlsl::AppendStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) } +// CHECK-DXIL: %"class.hlsl::ConsumeStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) } +// CHECK-DXIL: %"class.hlsl::RasterizerOrderedStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 1) } + +// CHECK-SPIRV: %"class.hlsl::StructuredBuffer" = type { target("spirv.VulkanBuffer", [0 x float], 12, 0) } +// CHECK-SPIRV: %"class.hlsl::RWStructuredBuffer" = type { target("spirv.VulkanBuffer", [0 x float], 12, 1) } -// CHECK: %"class.hlsl::StructuredBuffer" = type { target("dx.RawBuffer", float, 0, 0) } -// CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) } -// CHECK: %"class.hlsl::AppendStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) } -// CHECK: %"class.hlsl::ConsumeStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0) } -// CHECK: %"class.hlsl::RasterizerOrderedStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 1) } -// CHECK: @_ZL3Buf = internal global %"class.hlsl::StructuredBuffer" poison, align 4 -// CHECK: @_ZL4Buf2 = internal global %"class.hlsl::RWStructuredBuffer" poison, align 4 -// CHECK: @_ZL4Buf3 = internal global %"class.hlsl::AppendStructuredBuffer" poison, align 4 -// CHECK: @_ZL4Buf4 = internal global %"class.hlsl::ConsumeStructuredBuffer" poison, align 4 -// CHECK: @_ZL4Buf5 = internal global %"class.hlsl::RasterizerOrderedStructuredBuffer" poison, align 4 +// CHECK: @_ZL3Buf = internal global %"class.hlsl::StructuredBuffer" poison +// CHECK: @_ZL4Buf2 = internal global %"class.hlsl::RWStructuredBuffer" poison +// CHECK-DXIL: @_ZL4Buf3 = internal global %"class.hlsl::AppendStructuredBuffer" poison, align 4 +// CHECK-DXIL: @_ZL4Buf4 = internal global %"class.hlsl::ConsumeStructuredBuffer" poison, align 4 +// CHECK-DXIL: @_ZL4Buf5 = internal global %"class.hlsl::RasterizerOrderedStructuredBuffer" poison, align 4 // CHECK: define internal void @_init_resource__ZL3Buf() // CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 0, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_0_0t(i32 0, i32 10, i32 1, i32 0, i1 false) // CHECK-DXIL: store target("dx.RawBuffer", float, 0, 0) [[H]], ptr @_ZL3Buf, align 4 +// CHECK-SPIRV: [[H:%.*]] = call target("spirv.VulkanBuffer", [0 x float], 12, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0f32_12_0t(i32 0, i32 10, i32 1, i32 0, i1 false) +// CHECK-SPIRV: store target("spirv.VulkanBuffer", [0 x float], 12, 0) [[H]], ptr @_ZL3Buf, align 8 // CHECK: define internal void @_init_resource__ZL4Buf2() // CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 1, i32 5, i32 1, i32 0, i1 false) // CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) [[H]], ptr @_ZL4Buf2, align 4 +// CHECK-SPIRV: [[H:%.*]] = call target("spirv.VulkanBuffer", [0 x float], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0f32_12_1t(i32 1, i32 5, i32 1, i32 0, i1 false) +// CHECK-SPIRV: store target("spirv.VulkanBuffer", [0 x float], 12, 1) [[H]], ptr @_ZL4Buf2, align 8 -// CHECK: define internal void @_init_resource__ZL4Buf3() +// CHECK-DXIL: define internal void @_init_resource__ZL4Buf3() // CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 0, i32 3, i32 1, i32 0, i1 false) // CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) [[H]], ptr @_ZL4Buf3, align 4 -// CHECK: define internal void @_init_resource__ZL4Buf4() +// CHECK-DXIL: define internal void @_init_resource__ZL4Buf4() // CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 0) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_0t(i32 0, i32 4, i32 1, i32 0, i1 false) // CHECK-DXIL: store target("dx.RawBuffer", float, 1, 0) [[H]], ptr @_ZL4Buf4, align 4 -// CHECK: define internal void @_init_resource__ZL4Buf5() +// CHECK-DXIL: define internal void @_init_resource__ZL4Buf5() // CHECK-DXIL: [[H:%.*]] = call target("dx.RawBuffer", float, 1, 1) @llvm.dx.resource.handlefrombinding.tdx.RawBuffer_f32_1_1t(i32 2, i32 1, i32 1, i32 0, i1 false) // CHECK-DXIL: store target("dx.RawBuffer", float, 1, 1) [[H]], ptr @_ZL4Buf5, align 4 -// CHECK: define linkonce_odr void @_ZN4hlsl16StructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) -// CHECK-NEXT: entry: -// CHECK: define linkonce_odr void @_ZN4hlsl18RWStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) -// CHECK-NEXT: entry: -// CHECK: define linkonce_odr void @_ZN4hlsl22AppendStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) -// CHECK-NEXT: entry: -// CHECK: define linkonce_odr void @_ZN4hlsl23ConsumeStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) -// CHECK: define linkonce_odr void @_ZN4hlsl33RasterizerOrderedStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK: define linkonce_odr void @_ZN4hlsl16StructuredBufferIfEC2Ev(ptr noundef nonnull align {{[48]}} dereferenceable({{[48]}}) %this) // CHECK-NEXT: entry: +// CHECK-DXIL: define linkonce_odr void @_ZN4hlsl18RWStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK-DXIL-NEXT: entry: +// CHECK-DXIL: define linkonce_odr void @_ZN4hlsl22AppendStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK-DXIL-NEXT: entry: +// CHECK-DXIL: define linkonce_odr void @_ZN4hlsl23ConsumeStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK-DXIL: define linkonce_odr void @_ZN4hlsl33RasterizerOrderedStructuredBufferIfEC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) +// CHECK-DXIL-NEXT: entry: -// CHECK: define internal void @_GLOBAL__sub_I_StructuredBuffers_constructors.hlsl() -// CHECK: call void @_init_resource__ZL3Buf() -// CHECK: call void @_init_resource__ZL4Buf2() -// CHECK: call void @_init_resource__ZL4Buf3() -// CHECK: call void @_init_resource__ZL4Buf4() -// CHECK: call void @_init_resource__ZL4Buf5() +// CHECK: define {{.*}} void @_GLOBAL__sub_I_StructuredBuffers_constructors.hlsl() +// CHECK: call {{.*}} @_init_resource__ZL3Buf() +// CHECK: call {{.*}} @_init_resource__ZL4Buf2() +// CHECK-DXIL: call void @_init_resource__ZL4Buf3() +// CHECK-DXIL: call void @_init_resource__ZL4Buf4() +// CHECK-DXIL: call void @_init_resource__ZL4Buf5() diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl index 93aa218f63ecf..489c5a50511be 100644 --- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl +++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl @@ -36,8 +36,8 @@ export void TestAppend(float value) { // CHECK: define void @_Z10TestAppendf(float noundef nofpclass(nan inf) %value) // CHECK-DXIL: %[[VALUE:.*]] = load float, ptr %value.addr, align 4 // CHECK-DXIL: %[[INDEX:.*]] = call i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i8 1) -// CHECK-DXIL: %[[RESPTR:.*]] = call ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 %[[INDEX]]) -// CHECK-DXIL: store float %[[VALUE]], ptr %[[RESPTR]], align 4 +// CHECK-DXIL: %[[RESPTR:.*]] = call ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 %[[INDEX]]) +// CHECK-DXIL: store float %[[VALUE]], ptr addrspace(1) %[[RESPTR]], align 4 export float TestConsume() { return CSB.Consume(); @@ -45,8 +45,8 @@ export float TestConsume() { // CHECK: define noundef nofpclass(nan inf) float @_Z11TestConsumev() // CHECK-DXIL: %[[INDEX:.*]] = call i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %1, i8 -1) -// CHECK-DXIL: %[[RESPTR:.*]] = call ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %0, i32 %[[INDEX]]) -// CHECK-DXIL: %[[VALUE:.*]] = load float, ptr %[[RESPTR]], align 4 +// CHECK-DXIL: %[[RESPTR:.*]] = call ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %0, i32 %[[INDEX]]) +// CHECK-DXIL: %[[VALUE:.*]] = load float, ptr addrspace(1) %[[RESPTR]], align 4 // CHECK-DXIL: ret float %[[VALUE]] export float TestLoad() { @@ -54,11 +54,11 @@ export float TestLoad() { } // CHECK: define noundef nofpclass(nan inf) float @_Z8TestLoadv() -// CHECK: %[[PTR1:.*]] = call ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 %{{[0-9]+}}) -// CHECK: %[[VALUE1:.*]] = load float, ptr %[[PTR1]] -// CHECK: %[[PTR2:.*]] = call ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_0_0t(target("dx.RawBuffer", float, 0, 0) %{{[0-9]+}}, i32 %{{[0-9]+}}) -// CHECK: %[[VALUE2:.*]] = load float, ptr %[[PTR2]] +// CHECK: %[[PTR1:.*]] = call ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 %{{[0-9]+}}) +// CHECK: %[[VALUE1:.*]] = load float, ptr addrspace(1) %[[PTR1]] +// CHECK: %[[PTR2:.*]] = call ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.RawBuffer_f32_0_0t(target("dx.RawBuffer", float, 0, 0) %{{[0-9]+}}, i32 %{{[0-9]+}}) +// CHECK: %[[VALUE2:.*]] = load float, ptr addrspace(1) %[[PTR2]] // CHECK: declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i8) -// CHECK: declare ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i32) -// CHECK: declare ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_0_0t(target("dx.RawBuffer", float, 0, 0), i32) +// CHECK: declare ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i32) +// CHECK: declare ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.RawBuffer_f32_0_0t(target("dx.RawBuffer", float, 0, 0), i32) diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl index 5b1d8e3052eae..a4e9fe8e35442 100644 --- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl +++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl @@ -29,9 +29,9 @@ export float TestLoad() { } // CHECK: define noundef nofpclass(nan inf) float @_Z8TestLoadv() -// CHECK: %[[PTR1:.*]] = call ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1) %{{[0-9]+}}, i32 %{{[0-9]+}}) -// CHECK: %[[VALUE1:.*]] = load float, ptr %[[PTR1]] +// CHECK: %[[PTR1:.*]] = call ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1) %{{[0-9]+}}, i32 %{{[0-9]+}}) +// CHECK: %[[VALUE1:.*]] = load float, ptr addrspace(1) %[[PTR1]] // CHECK: declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i8) #3 // CHECK: declare i32 @llvm.dx.resource.updatecounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i8) #3 -// CHECK: declare ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i32) #4 +// CHECK: declare ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i32) #4 diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-subscripts.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-subscripts.hlsl index 2af7c3ed3219f..3810e3b472dc7 100644 --- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-subscripts.hlsl +++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-subscripts.hlsl @@ -1,22 +1,37 @@ // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -emit-llvm -o - -O0 %s | FileCheck %s +struct S { + float f; +}; + StructuredBuffer In; RWStructuredBuffer Out1; +RWStructuredBuffer RWSB3; RasterizerOrderedStructuredBuffer Out2; [numthreads(1,1,1)] void main(unsigned GI : SV_GroupIndex) { // CHECK: define void @main() - // CHECK: %[[INPTR:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_0_0t(target("dx.RawBuffer", i32, 0, 0) %{{.*}}, i32 %{{.*}}) - // CHECK: %[[LOAD:.*]] = load i32, ptr %[[INPTR]] - // CHECK: %[[OUT1PTR:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_0t(target("dx.RawBuffer", i32, 1, 0) %{{.*}}, i32 %{{.*}}) - // CHECK: store i32 %[[LOAD]], ptr %[[OUT1PTR]] + // CHECK: %[[INPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.RawBuffer_i32_0_0t(target("dx.RawBuffer", i32, 0, 0) %{{.*}}, i32 %{{.*}}) + // CHECK: %[[LOAD:.*]] = load i32, ptr addrspace(1) %[[INPTR]] + // CHECK: %[[OUT1PTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.RawBuffer_i32_1_0t(target("dx.RawBuffer", i32, 1, 0) %{{.*}}, i32 %{{.*}}) + // CHECK: store i32 %[[LOAD]], ptr addrspace(1) %[[OUT1PTR]] Out1[GI] = In[GI]; - // CHECK: %[[INPTR:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_0_0t(target("dx.RawBuffer", i32, 0, 0) %{{.*}}, i32 %{{.*}}) - // CHECK: %[[LOAD:.*]] = load i32, ptr %[[INPTR]] - // CHECK: %[[OUT2PTR:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @llvm.dx.resource.getpointer.p0.tdx.RawBuffer_i32_1_1t(target("dx.RawBuffer", i32, 1, 1) %{{.*}}, i32 %{{.*}}) - // CHECK: store i32 %[[LOAD]], ptr %[[OUT2PTR]] + // CHECK: %[[INPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.RawBuffer_i32_0_0t(target("dx.RawBuffer", i32, 0, 0) %{{.*}}, i32 %{{.*}}) + // CHECK: %[[LOAD:.*]] = load i32, ptr addrspace(1) %[[INPTR]] + // CHECK: %[[OUT2PTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.RawBuffer_i32_1_1t(target("dx.RawBuffer", i32, 1, 1) %{{.*}}, i32 %{{.*}}) + // CHECK: store i32 %[[LOAD]], ptr addrspace(1) %[[OUT2PTR]] Out2[GI] = In[GI]; + + // The addrspacecast comes from `S::operator=` member function, which expects + // parameters in address space 0. This is why hlsl_device is a sub address + // space of the default address space. + // CHECK: %[[INPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.RawBuffer_s_struct.Ss_1_0t(target("dx.RawBuffer", %struct.S, 1, 0) %{{.*}}, i32 %{{.*}}) + // CHECK: %[[INCAST:.*]] = addrspacecast ptr addrspace(1) %[[INPTR]] to ptr + // CHECK: %[[OUTPTR:.*]] = call noundef align 4 dereferenceable(4) ptr addrspace(1) @llvm.dx.resource.getpointer.p1.tdx.RawBuffer_s_struct.Ss_1_0t(target("dx.RawBuffer", %struct.S, 1, 0) %{{.*}}, i32 %{{.*}}) + // CHECK: %[[OUTCAST:.*]] = addrspacecast ptr addrspace(1) %[[OUTPTR]] to ptr + // CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[OUTCAST]], ptr align 4 %[[INCAST]], i32 4, i1 false) + RWSB3[0] = RWSB3[1]; } diff --git a/clang/test/SemaTemplate/address_space-dependent.cpp b/clang/test/SemaTemplate/address_space-dependent.cpp index eb8dbc69a945e..3e73e909746a5 100644 --- a/clang/test/SemaTemplate/address_space-dependent.cpp +++ b/clang/test/SemaTemplate/address_space-dependent.cpp @@ -43,7 +43,7 @@ void neg() { template void tooBig() { - __attribute__((address_space(I))) int *bounds; // expected-error {{address space is larger than the maximum supported (8388585)}} + __attribute__((address_space(I))) int *bounds; // expected-error {{address space is larger than the maximum supported (8388584)}} } template @@ -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<0x7FFFE9>(); + correct<0x7FFFE8>(); tooBig<8388650>(); // expected-note {{in instantiation of function template specialization 'tooBig<8388650L>' requested here}} __attribute__((address_space(1))) char *x; diff --git a/llvm/docs/SPIRVUsage.rst b/llvm/docs/SPIRVUsage.rst index ec842db586f77..89c6203d2d66e 100644 --- a/llvm/docs/SPIRVUsage.rst +++ b/llvm/docs/SPIRVUsage.rst @@ -243,19 +243,20 @@ using target extension types and are represented as follows: .. table:: SPIR-V Opaque Types - ================== ====================== =========================================================================================== - SPIR-V Type LLVM type name LLVM type arguments - ================== ====================== =========================================================================================== - OpTypeImage ``spirv.Image`` sampled type, dimensionality, depth, arrayed, MS, sampled, image format, [access qualifier] - OpTypeSampler ``spirv.Sampler`` (none) - OpTypeSampledImage ``spirv.SampledImage`` sampled type, dimensionality, depth, arrayed, MS, sampled, image format, [access qualifier] - OpTypeEvent ``spirv.Event`` (none) - OpTypeDeviceEvent ``spirv.DeviceEvent`` (none) - OpTypeReserveId ``spirv.ReserveId`` (none) - OpTypeQueue ``spirv.Queue`` (none) - OpTypePipe ``spirv.Pipe`` access qualifier - OpTypePipeStorage ``spirv.PipeStorage`` (none) - ================== ====================== =========================================================================================== + ================== ======================= =========================================================================================== + SPIR-V Type LLVM type name LLVM type arguments + ================== ======================= =========================================================================================== + OpTypeImage ``spirv.Image`` sampled type, dimensionality, depth, arrayed, MS, sampled, image format, [access qualifier] + OpTypeSampler ``spirv.Sampler`` (none) + OpTypeSampledImage ``spirv.SampledImage`` sampled type, dimensionality, depth, arrayed, MS, sampled, image format, [access qualifier] + OpTypeEvent ``spirv.Event`` (none) + OpTypeDeviceEvent ``spirv.DeviceEvent`` (none) + OpTypeReserveId ``spirv.ReserveId`` (none) + OpTypeQueue ``spirv.Queue`` (none) + OpTypePipe ``spirv.Pipe`` access qualifier + OpTypePipeStorage ``spirv.PipeStorage`` (none) + NA ``spirv.VulkanBuffer`` ElementType, StorageClass, IsWriteable + ================== ======================= =========================================================================================== All integer arguments take the same value as they do in their `corresponding SPIR-V instruction `_. @@ -266,6 +267,9 @@ parameters of its underlying image type, so that a sampled image for the previous type has the representation ``target("spirv.SampledImage, void, 1, 1, 0, 0, 0, 0, 0)``. +See `wg-hlsl proposal 0018 `_ +for details on ``spirv.VulkanBuffer``. + .. _spirv-intrinsics: Target Intrinsics diff --git a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp index 3b8f7140d3122..ba91809d1feb9 100644 --- a/llvm/lib/Target/DirectX/DXILResourceAccess.cpp +++ b/llvm/lib/Target/DirectX/DXILResourceAccess.cpp @@ -224,6 +224,10 @@ static void replaceAccess(IntrinsicInst *II, dxil::ResourceTypeInfo &RTI) { createLoadIntrinsic(II, LI, Current.Offset, RTI); DeadInsts.push_back(LI); + } else if (auto ASC = dyn_cast(Current.Access)) { + for (User *U : ASC->users()) + Worklist.push_back({U, Current.Offset}); + DeadInsts.push_back(ASC); } else llvm_unreachable("Unhandled instruction - pointer escaped?"); } diff --git a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp index 63dcf0067b515..21a62897e6331 100644 --- a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp +++ b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp @@ -142,6 +142,9 @@ void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t Address, printRemainingVariableOps(MI, NumFixedOps, OS, false, true); break; } + case SPIRV::OpMemberDecorate: + printRemainingVariableOps(MI, NumFixedOps, OS); + break; case SPIRV::OpExecutionMode: case SPIRV::OpExecutionModeId: case SPIRV::OpLoopMerge: { diff --git a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp index 579e37f68d5d8..585d3b763019a 100644 --- a/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVBuiltins.cpp @@ -3041,6 +3041,22 @@ static SPIRVType *getSampledImageType(const TargetExtType *OpaqueType, return GR->getOrCreateOpTypeSampledImage(OpaqueImageType, MIRBuilder); } +static SPIRVType *getVulkanBufferType(const TargetExtType *ExtensionType, + MachineIRBuilder &MIRBuilder, + SPIRVGlobalRegistry *GR) { + assert(ExtensionType->getNumTypeParameters() == 1 && + "Vulkan buffers have exactly one type for the type of the buffer."); + assert(ExtensionType->getNumIntParameters() == 2 && + "Vulkan buffer have 2 integer parameters: storage class and is " + "writable."); + + auto *T = ExtensionType->getTypeParameter(0); + auto SC = static_cast( + ExtensionType->getIntParameter(0)); + bool IsWritable = ExtensionType->getIntParameter(1); + return GR->getOrCreateVulkanBufferType(MIRBuilder, T, SC, IsWritable); +} + namespace SPIRV { TargetExtType *parseBuiltinTypeNameToTargetExtType(std::string TypeName, LLVMContext &Context) { @@ -3113,39 +3129,45 @@ SPIRVType *lowerBuiltinType(const Type *OpaqueType, const StringRef Name = BuiltinType->getName(); LLVM_DEBUG(dbgs() << "Lowering builtin type: " << Name << "\n"); - // Lookup the demangled builtin type in the TableGen records. - const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name); - if (!TypeRecord) - report_fatal_error("Missing TableGen record for builtin type: " + Name); - - // "Lower" the BuiltinType into TargetType. The following get<...>Type methods - // use the implementation details from TableGen records or TargetExtType - // parameters to either create a new OpType<...> machine instruction or get an - // existing equivalent SPIRVType from GlobalRegistry. SPIRVType *TargetType; - switch (TypeRecord->Opcode) { - case SPIRV::OpTypeImage: - TargetType = getImageType(BuiltinType, AccessQual, MIRBuilder, GR); - break; - case SPIRV::OpTypePipe: - TargetType = getPipeType(BuiltinType, MIRBuilder, GR); - break; - case SPIRV::OpTypeDeviceEvent: - TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder); - break; - case SPIRV::OpTypeSampler: - TargetType = getSamplerType(MIRBuilder, GR); - break; - case SPIRV::OpTypeSampledImage: - TargetType = getSampledImageType(BuiltinType, MIRBuilder, GR); - break; - case SPIRV::OpTypeCooperativeMatrixKHR: - TargetType = getCoopMatrType(BuiltinType, MIRBuilder, GR); - break; - default: - TargetType = - getNonParameterizedType(BuiltinType, TypeRecord, MIRBuilder, GR); - break; + if (Name == "spirv.VulkanBuffer") { + TargetType = getVulkanBufferType(BuiltinType, MIRBuilder, GR); + } else { + // Lookup the demangled builtin type in the TableGen records. + const SPIRV::BuiltinType *TypeRecord = SPIRV::lookupBuiltinType(Name); + if (!TypeRecord) + report_fatal_error("Missing TableGen record for builtin type: " + Name); + + // "Lower" the BuiltinType into TargetType. The following get<...>Type + // methods use the implementation details from TableGen records or + // TargetExtType parameters to either create a new OpType<...> machine + // instruction or get an existing equivalent SPIRVType from + // GlobalRegistry. + + switch (TypeRecord->Opcode) { + case SPIRV::OpTypeImage: + TargetType = getImageType(BuiltinType, AccessQual, MIRBuilder, GR); + break; + case SPIRV::OpTypePipe: + TargetType = getPipeType(BuiltinType, MIRBuilder, GR); + break; + case SPIRV::OpTypeDeviceEvent: + TargetType = GR->getOrCreateOpTypeDeviceEvent(MIRBuilder); + break; + case SPIRV::OpTypeSampler: + TargetType = getSamplerType(MIRBuilder, GR); + break; + case SPIRV::OpTypeSampledImage: + TargetType = getSampledImageType(BuiltinType, MIRBuilder, GR); + break; + case SPIRV::OpTypeCooperativeMatrixKHR: + TargetType = getCoopMatrType(BuiltinType, MIRBuilder, GR); + break; + default: + TargetType = + getNonParameterizedType(BuiltinType, TypeRecord, MIRBuilder, GR); + break; + } } // Emit OpName instruction if a new OpType<...> instruction was added diff --git a/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.h b/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.h index 441e32c1eb695..34a6dbb337355 100644 --- a/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.h +++ b/llvm/lib/Target/SPIRV/SPIRVDuplicatesTracker.h @@ -64,6 +64,7 @@ enum SpecialTypeKind { STK_Pipe, STK_DeviceEvent, STK_Pointer, + STK_Buffer, STK_Last = -1 }; @@ -137,6 +138,29 @@ inline SpecialTypeDescriptor make_descr_pointee(const Type *ElementType, return std::make_tuple(ElementType, AddressSpace, SpecialTypeKind::STK_Pointer); } + +union BufferAttrs { + struct BitFlags { + unsigned IsStorageBuffer : 1; + unsigned IsWriteable : 1; + } Flags; + unsigned Val; + + BufferAttrs(bool IsStorageBuffer, bool IsWriteable) { + Val = 0; + Flags.IsStorageBuffer = IsStorageBuffer; + Flags.IsWriteable = IsWriteable; + } +}; + +inline SpecialTypeDescriptor make_descr_buffer(const Type *ElementType, + bool IsStorageBuffer, + bool IsWriteable) { + return std::make_tuple(ElementType, + BufferAttrs(IsStorageBuffer, IsWriteable).Val, + SpecialTypeKind::STK_Buffer); +} + } // namespace SPIRV template class SPIRVDuplicatesTrackerBase { diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp index 68651f4ee4d2f..ed96602e46ad0 100644 --- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp @@ -667,13 +667,22 @@ Type *SPIRVEmitIntrinsics::deduceElementTypeHelper( auto *II = dyn_cast(I); if (II && II->getIntrinsicID() == Intrinsic::spv_resource_getpointer) { - auto *ImageType = cast(II->getOperand(0)->getType()); - assert(ImageType->getTargetExtName() == "spirv.Image"); - (void)ImageType; - if (II->hasOneUse()) { - auto *U = *II->users().begin(); - Ty = cast(U)->getAccessType(); - assert(Ty && "Unable to get type for resource pointer."); + auto *HandleType = cast(II->getOperand(0)->getType()); + if (HandleType->getTargetExtName() == "spirv.Image") { + if (II->hasOneUse()) { + auto *U = *II->users().begin(); + Ty = cast(U)->getAccessType(); + assert(Ty && "Unable to get type for resource pointer."); + } + } else if (HandleType->getTargetExtName() == "spirv.VulkanBuffer") { + // This call is supposed to index into an array + Ty = HandleType->getTypeParameter(0); + assert(Ty->isArrayTy() && + "spv_resource_getpointer indexes into an array, so the type of " + "the buffer should be an array."); + Ty = Ty->getArrayElementType(); + } else { + llvm_unreachable("Unknown handle type for spv_resource_getpointer."); } } else if (Function *CalledF = CI->getCalledFunction()) { std::string DemangledName = diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp index cbec1c95eadc3..059dfd931679e 100644 --- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.cpp @@ -799,16 +799,18 @@ Register SPIRVGlobalRegistry::buildGlobalVariable( static std::string GetSpirvImageTypeName(const SPIRVType *Type, MachineIRBuilder &MIRBuilder, - const std::string &Prefix); + const std::string &Prefix, + SPIRVGlobalRegistry &GR); static std::string buildSpirvTypeName(const SPIRVType *Type, - MachineIRBuilder &MIRBuilder) { + MachineIRBuilder &MIRBuilder, + SPIRVGlobalRegistry &GR) { switch (Type->getOpcode()) { case SPIRV::OpTypeSampledImage: { - return GetSpirvImageTypeName(Type, MIRBuilder, "sampled_image_"); + return GetSpirvImageTypeName(Type, MIRBuilder, "sampled_image_", GR); } case SPIRV::OpTypeImage: { - return GetSpirvImageTypeName(Type, MIRBuilder, "image_"); + return GetSpirvImageTypeName(Type, MIRBuilder, "image_", GR); } case SPIRV::OpTypeArray: { MachineRegisterInfo *MRI = MIRBuilder.getMRI(); @@ -819,7 +821,7 @@ static std::string buildSpirvTypeName(const SPIRVType *Type, MachineInstr *ImmInst = MRI->getVRegDef(TypeInst->getOperand(1).getReg()); assert(ImmInst->getOpcode() == TargetOpcode::G_CONSTANT); uint32_t ArraySize = ImmInst->getOperand(1).getCImm()->getZExtValue(); - return (buildSpirvTypeName(ElementType, MIRBuilder) + Twine("[") + + return (buildSpirvTypeName(ElementType, MIRBuilder, GR) + Twine("[") + Twine(ArraySize) + Twine("]")) .str(); } @@ -831,6 +833,22 @@ static std::string buildSpirvTypeName(const SPIRVType *Type, if (Type->getOperand(2).getImm()) return ("i" + Twine(Type->getOperand(1).getImm())).str(); return ("u" + Twine(Type->getOperand(1).getImm())).str(); + case SPIRV::OpTypePointer: { + uint32_t StorageClass = GR.getPointerStorageClass(Type); + SPIRVType *PointeeType = GR.getPointeeType(Type); + return ("p_" + Twine(StorageClass) + Twine("_") + + buildSpirvTypeName(PointeeType, MIRBuilder, GR)) + .str(); + } + case SPIRV::OpTypeStruct: { + std::string TypeName = "{"; + for (uint32_t I = 2; I < Type->getNumOperands(); ++I) { + SPIRVType *MemberType = + GR.getSPIRVTypeForVReg(Type->getOperand(I).getReg()); + TypeName = '_' + buildSpirvTypeName(MemberType, MIRBuilder, GR); + } + return TypeName + "}"; + } default: llvm_unreachable("Trying to the the name of an unknown type."); } @@ -838,10 +856,12 @@ static std::string buildSpirvTypeName(const SPIRVType *Type, static std::string GetSpirvImageTypeName(const SPIRVType *Type, MachineIRBuilder &MIRBuilder, - const std::string &Prefix) { + const std::string &Prefix, + SPIRVGlobalRegistry &GR) { Register SampledTypeReg = Type->getOperand(1).getReg(); auto *SampledType = MIRBuilder.getMRI()->getUniqueVRegDef(SampledTypeReg); - std::string TypeName = Prefix + buildSpirvTypeName(SampledType, MIRBuilder); + std::string TypeName = + Prefix + buildSpirvTypeName(SampledType, MIRBuilder, GR); for (uint32_t I = 2; I < Type->getNumOperands(); ++I) { TypeName = (TypeName + '_' + Twine(Type->getOperand(I).getImm())).str(); } @@ -851,20 +871,19 @@ static std::string GetSpirvImageTypeName(const SPIRVType *Type, Register SPIRVGlobalRegistry::getOrCreateGlobalVariableWithBinding( const SPIRVType *VarType, uint32_t Set, uint32_t Binding, MachineIRBuilder &MIRBuilder) { - SPIRVType *VarPointerTypeReg = getOrCreateSPIRVPointerType( - VarType, MIRBuilder, SPIRV::StorageClass::UniformConstant); Register VarReg = MIRBuilder.getMRI()->createVirtualRegister(&SPIRV::iIDRegClass); // TODO: The name should come from the llvm-ir, but how that name will be // passed from the HLSL to the backend has not been decided. Using this place // holder for now. - std::string Name = ("__resource_" + buildSpirvTypeName(VarType, MIRBuilder) + - "_" + Twine(Set) + "_" + Twine(Binding)) - .str(); - buildGlobalVariable(VarReg, VarPointerTypeReg, Name, nullptr, - SPIRV::StorageClass::UniformConstant, nullptr, false, - false, SPIRV::LinkageType::Import, MIRBuilder, false); + std::string Name = + ("__resource_" + buildSpirvTypeName(VarType, MIRBuilder, *this) + "_" + + Twine(Set) + "_" + Twine(Binding)) + .str(); + buildGlobalVariable(VarReg, VarType, Name, nullptr, + getPointerStorageClass(VarType), nullptr, false, false, + SPIRV::LinkageType::Import, MIRBuilder, false); buildOpDecorate(VarReg, MIRBuilder, SPIRV::Decoration::DescriptorSet, {Set}); buildOpDecorate(VarReg, MIRBuilder, SPIRV::Decoration::Binding, {Binding}); @@ -878,14 +897,23 @@ SPIRVType *SPIRVGlobalRegistry::getOpTypeArray(uint32_t NumElems, assert((ElemType->getOpcode() != SPIRV::OpTypeVoid) && "Invalid array element type"); SPIRVType *SpvTypeInt32 = getOrCreateSPIRVIntegerType(32, MIRBuilder); - Register NumElementsVReg = - buildConstantInt(NumElems, MIRBuilder, SpvTypeInt32, EmitIR); - return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) { - return MIRBuilder.buildInstr(SPIRV::OpTypeArray) - .addDef(createTypeVReg(MIRBuilder)) - .addUse(getSPIRVTypeID(ElemType)) - .addUse(NumElementsVReg); - }); + + if (NumElems != 0) { + Register NumElementsVReg = + buildConstantInt(NumElems, MIRBuilder, SpvTypeInt32, EmitIR); + return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) { + return MIRBuilder.buildInstr(SPIRV::OpTypeArray) + .addDef(createTypeVReg(MIRBuilder)) + .addUse(getSPIRVTypeID(ElemType)) + .addUse(NumElementsVReg); + }); + } else { + return createOpType(MIRBuilder, [&](MachineIRBuilder &MIRBuilder) { + return MIRBuilder.buildInstr(SPIRV::OpTypeRuntimeArray) + .addDef(createTypeVReg(MIRBuilder)) + .addUse(getSPIRVTypeID(ElemType)); + }); + } } SPIRVType *SPIRVGlobalRegistry::getOpTypeOpaque(const StructType *Ty, @@ -1349,6 +1377,35 @@ SPIRVGlobalRegistry::getPointerStorageClass(const SPIRVType *Type) const { Type->getOperand(1).getImm()); } +SPIRVType *SPIRVGlobalRegistry::getOrCreateVulkanBufferType( + MachineIRBuilder &MIRBuilder, Type *ElemType, + SPIRV::StorageClass::StorageClass SC, bool IsWritable, bool EmitIr) { + auto TD = SPIRV::make_descr_buffer( + ElemType, SC == SPIRV::StorageClass::StorageBuffer, IsWritable); + if (auto *Res = checkSpecialInstr(TD, MIRBuilder)) + return Res; + + // TODO: Check that I will not collide with a similar struct that does not + // have the decorations. + auto *T = StructType::create(ElemType); + auto *BlockType = + getOrCreateSPIRVType(T, MIRBuilder, SPIRV::AccessQualifier::None, EmitIr); + + buildOpDecorate(BlockType->defs().begin()->getReg(), MIRBuilder, + SPIRV::Decoration::Block, {}); + buildOpMemberDecorate(BlockType->defs().begin()->getReg(), MIRBuilder, + SPIRV::Decoration::Offset, 0, {0}); + + if (!IsWritable) { + buildOpMemberDecorate(BlockType->defs().begin()->getReg(), MIRBuilder, + SPIRV::Decoration::NonWritable, 0, {}); + } + + SPIRVType *R = getOrCreateSPIRVPointerType(BlockType, MIRBuilder, SC); + DT.add(TD, &MIRBuilder.getMF(), getSPIRVTypeID(R)); + return R; +} + SPIRVType *SPIRVGlobalRegistry::getOrCreateOpTypeImage( MachineIRBuilder &MIRBuilder, SPIRVType *SampledType, SPIRV::Dim::Dim Dim, uint32_t Depth, uint32_t Arrayed, uint32_t Multisampled, uint32_t Sampled, diff --git a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h index 89599f17ef737..f3a0606e707eb 100644 --- a/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h +++ b/llvm/lib/Target/SPIRV/SPIRVGlobalRegistry.h @@ -592,6 +592,11 @@ class SPIRVGlobalRegistry { SPIRVType *BaseType, MachineInstr &I, const SPIRVInstrInfo &TII, SPIRV::StorageClass::StorageClass SClass = SPIRV::StorageClass::Function); + SPIRVType *getOrCreateVulkanBufferType(MachineIRBuilder &MIRBuilder, + Type *ElemType, + SPIRV::StorageClass::StorageClass SC, + bool IsWritable, bool EmitIr = false); + SPIRVType * getOrCreateOpTypeImage(MachineIRBuilder &MIRBuilder, SPIRVType *SampledType, SPIRV::Dim::Dim Dim, uint32_t Depth, uint32_t Arrayed, diff --git a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td index a8f862271dbab..4621d8dc6b8b5 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td +++ b/llvm/lib/Target/SPIRV/SPIRVInstrInfo.td @@ -127,8 +127,9 @@ def OpModuleProcessed: Op<330, (outs), (ins StringImm:$process, variable_ops), def OpDecorate: Op<71, (outs), (ins ANY:$target, Decoration:$dec, variable_ops), "OpDecorate $target $dec">; -def OpMemberDecorate: Op<72, (outs), (ins TYPE:$t, i32imm:$m, Decoration:$d, variable_ops), - "OpMemberDecorate $t $m $d">; +def OpMemberDecorate + : Op<72, (outs), (ins TYPE:$t, i32imm:$m, Decoration:$d, variable_ops), + "OpMemberDecorate $t $m $d", []>; // TODO Currently some deprecated opcodes are missing: OpDecorationGroup, // OpGroupDecorate and OpGroupMemberDecorate diff --git a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp index 3445e8fc3990c..48dd0a67f6bf3 100644 --- a/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVInstructionSelector.cpp @@ -327,9 +327,11 @@ class SPIRVInstructionSelector : public InstructionSelector { uint32_t Opcode) const; MachineInstrBuilder buildConstGenericPtr(MachineInstr &I, Register SrcPtr, SPIRVType *SrcPtrTy) const; - Register buildPointerToResource(const SPIRVType *ResType, uint32_t Set, - uint32_t Binding, uint32_t ArraySize, - Register IndexReg, bool IsNonUniform, + Register buildPointerToResource(const SPIRVType *ResType, + SPIRV::StorageClass::StorageClass SC, + uint32_t Set, uint32_t Binding, + uint32_t ArraySize, Register IndexReg, + bool IsNonUniform, MachineIRBuilder MIRBuilder) const; SPIRVType *widenTypeToVec4(const SPIRVType *Type, MachineInstr &I) const; bool extractSubvector(Register &ResVReg, const SPIRVType *ResType, @@ -1091,18 +1093,20 @@ bool SPIRVInstructionSelector::selectLoad(Register ResVReg, auto *IntPtrDef = dyn_cast(PtrDef); if (IntPtrDef && IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getpointer) { - Register ImageReg = IntPtrDef->getOperand(2).getReg(); - Register NewImageReg = - MRI->createVirtualRegister(MRI->getRegClass(ImageReg)); - auto *ImageDef = cast(getVRegDef(*MRI, ImageReg)); - if (!loadHandleBeforePosition(NewImageReg, GR.getSPIRVTypeForVReg(ImageReg), - *ImageDef, I)) { - return false; - } + Register HandleReg = IntPtrDef->getOperand(2).getReg(); + SPIRVType *HandleType = GR.getSPIRVTypeForVReg(HandleReg); + if (HandleType->getOpcode() == SPIRV::OpTypeImage) { + Register NewHandleReg = + MRI->createVirtualRegister(MRI->getRegClass(HandleReg)); + auto *HandleDef = cast(getVRegDef(*MRI, HandleReg)); + if (!loadHandleBeforePosition(NewHandleReg, HandleType, *HandleDef, I)) { + return false; + } - Register IdxReg = IntPtrDef->getOperand(3).getReg(); - return generateImageRead(ResVReg, ResType, NewImageReg, IdxReg, - I.getDebugLoc(), I); + Register IdxReg = IntPtrDef->getOperand(3).getReg(); + return generateImageRead(ResVReg, ResType, NewHandleReg, IdxReg, + I.getDebugLoc(), I); + } } auto MIB = BuildMI(*I.getParent(), I, I.getDebugLoc(), TII.get(SPIRV::OpLoad)) @@ -1130,22 +1134,24 @@ bool SPIRVInstructionSelector::selectStore(MachineInstr &I) const { auto *IntPtrDef = dyn_cast(PtrDef); if (IntPtrDef && IntPtrDef->getIntrinsicID() == Intrinsic::spv_resource_getpointer) { - Register ImageReg = IntPtrDef->getOperand(2).getReg(); - Register NewImageReg = - MRI->createVirtualRegister(MRI->getRegClass(ImageReg)); - auto *ImageDef = cast(getVRegDef(*MRI, ImageReg)); - if (!loadHandleBeforePosition(NewImageReg, GR.getSPIRVTypeForVReg(ImageReg), - *ImageDef, I)) { + Register HandleReg = IntPtrDef->getOperand(2).getReg(); + Register NewHandleReg = + MRI->createVirtualRegister(MRI->getRegClass(HandleReg)); + auto *HandleDef = cast(getVRegDef(*MRI, HandleReg)); + SPIRVType *HandleType = GR.getSPIRVTypeForVReg(HandleReg); + if (!loadHandleBeforePosition(NewHandleReg, HandleType, *HandleDef, I)) { return false; } Register IdxReg = IntPtrDef->getOperand(3).getReg(); - return BuildMI(*I.getParent(), I, I.getDebugLoc(), - TII.get(SPIRV::OpImageWrite)) - .addUse(NewImageReg) - .addUse(IdxReg) - .addUse(StoreVal) - .constrainAllUses(TII, TRI, RBI); + if (HandleType->getOpcode() == SPIRV::OpTypeImage) { + return BuildMI(*I.getParent(), I, I.getDebugLoc(), + TII.get(SPIRV::OpImageWrite)) + .addUse(NewHandleReg) + .addUse(IdxReg) + .addUse(StoreVal) + .constrainAllUses(TII, TRI, RBI); + } } MachineBasicBlock &BB = *I.getParent(); @@ -3224,7 +3230,13 @@ bool SPIRVInstructionSelector::selectIntrinsic(Register ResVReg, bool SPIRVInstructionSelector::selectHandleFromBinding(Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const { - return true; + // The images need to be loaded in the same basic block as their use. We defer + // loading the image to the intrinsic that uses it. + if (ResType->getOpcode() == SPIRV::OpTypeImage) + return true; + + return loadHandleBeforePosition(ResVReg, GR.getSPIRVTypeForVReg(ResVReg), + *cast(&I), I); } bool SPIRVInstructionSelector::selectReadImageIntrinsic( @@ -3292,20 +3304,30 @@ bool SPIRVInstructionSelector::generateImageRead(Register &ResVReg, bool SPIRVInstructionSelector::selectResourceGetPointer( Register &ResVReg, const SPIRVType *ResType, MachineInstr &I) const { -#ifdef ASSERT - // For now, the operand is an image. This will change once we start handling - // more resource types. Register ResourcePtr = I.getOperand(2).getReg(); - SPIRVType *RegType = GR.getResultType(ResourcePtr); - assert(RegType->getOpcode() == SPIRV::OpTypeImage && - "Can only handle texel buffers for now."); -#endif - - // For texel buffers, the index into the image is part of the OpImageRead or - // OpImageWrite instructions. So we will do nothing in this case. This - // intrinsic will be combined with the load or store when selecting the load - // or store. - return true; + SPIRVType *RegType = GR.getSPIRVTypeForVReg(ResourcePtr, I.getMF()); + if (RegType->getOpcode() == SPIRV::OpTypeImage) { + // For texel buffers, the index into the image is part of the OpImageRead or + // OpImageWrite instructions. So we will do nothing in this case. This + // intrinsic will be combined with the load or store when selecting the load + // or store. + return true; + } + + assert(ResType->getOpcode() == SPIRV::OpTypePointer); + MachineIRBuilder MIRBuilder(I); + + Register IndexReg = I.getOperand(3).getReg(); + Register ZeroReg = + buildZerosVal(GR.getOrCreateSPIRVIntegerType(32, I, TII), I); + return BuildMI(*I.getParent(), I, I.getDebugLoc(), + TII.get(SPIRV::OpAccessChain)) + .addDef(ResVReg) + .addUse(GR.getSPIRVTypeID(ResType)) + .addUse(ResourcePtr) + .addUse(ZeroReg) + .addUse(IndexReg) + .constrainAllUses(TII, TRI, RBI); } bool SPIRVInstructionSelector::extractSubvector( @@ -3377,22 +3399,27 @@ bool SPIRVInstructionSelector::selectImageWriteIntrinsic( } Register SPIRVInstructionSelector::buildPointerToResource( - const SPIRVType *ResType, uint32_t Set, uint32_t Binding, - uint32_t ArraySize, Register IndexReg, bool IsNonUniform, - MachineIRBuilder MIRBuilder) const { - if (ArraySize == 1) - return GR.getOrCreateGlobalVariableWithBinding(ResType, Set, Binding, + const SPIRVType *ResType, SPIRV::StorageClass::StorageClass SC, + uint32_t Set, uint32_t Binding, uint32_t ArraySize, Register IndexReg, + bool IsNonUniform, MachineIRBuilder MIRBuilder) const { + if (ArraySize == 1) { + SPIRVType *PtrType = + GR.getOrCreateSPIRVPointerType(ResType, MIRBuilder, SC); + return GR.getOrCreateGlobalVariableWithBinding(PtrType, Set, Binding, MIRBuilder); + } const SPIRVType *VarType = GR.getOrCreateSPIRVArrayType( ResType, ArraySize, *MIRBuilder.getInsertPt(), TII); + SPIRVType *VarPointerType = + GR.getOrCreateSPIRVPointerType(VarType, MIRBuilder, SC); Register VarReg = GR.getOrCreateGlobalVariableWithBinding( - VarType, Set, Binding, MIRBuilder); + VarPointerType, Set, Binding, MIRBuilder); - SPIRVType *ResPointerType = GR.getOrCreateSPIRVPointerType( - ResType, MIRBuilder, SPIRV::StorageClass::UniformConstant); + SPIRVType *ResPointerType = + GR.getOrCreateSPIRVPointerType(ResType, MIRBuilder, SC); - Register AcReg = MRI->createVirtualRegister(&SPIRV::iIDRegClass); + Register AcReg = MRI->createVirtualRegister(GR.getRegClass(ResPointerType)); if (IsNonUniform) { // It is unclear which value needs to be marked an non-uniform, so both // the index and the access changed are decorated as non-uniform. @@ -4083,19 +4110,29 @@ bool SPIRVInstructionSelector::loadHandleBeforePosition( uint32_t ArraySize = foldImm(HandleDef.getOperand(4), MRI); Register IndexReg = HandleDef.getOperand(5).getReg(); bool IsNonUniform = ArraySize > 1 && foldImm(HandleDef.getOperand(6), MRI); - + bool IsStructuredBuffer = ResType->getOpcode() == SPIRV::OpTypePointer; MachineIRBuilder MIRBuilder(HandleDef); - Register VarReg = buildPointerToResource(ResType, Set, Binding, ArraySize, + SPIRVType *VarType = ResType; + SPIRV::StorageClass::StorageClass SC = SPIRV::StorageClass::UniformConstant; + + if (IsStructuredBuffer) { + VarType = GR.getPointeeType(ResType); + SC = GR.getPointerStorageClass(ResType); + } + + Register VarReg = buildPointerToResource(VarType, SC, Set, Binding, ArraySize, IndexReg, IsNonUniform, MIRBuilder); if (IsNonUniform) buildOpDecorate(HandleReg, HandleDef, TII, SPIRV::Decoration::NonUniformEXT, {}); - // TODO: For now we assume the resource is an image, which needs to be - // loaded to get the handle. That will not be true for storage buffers. + // The handle for the buffer is the pointer to the resource. For an image, the + // handle is the image object. So images get an extra load. + uint32_t LoadOpcode = + IsStructuredBuffer ? SPIRV::OpCopyObject : SPIRV::OpLoad; return BuildMI(*Pos.getParent(), Pos, HandleDef.getDebugLoc(), - TII.get(SPIRV::OpLoad)) + TII.get(LoadOpcode)) .addDef(HandleReg) .addUse(GR.getSPIRVTypeID(ResType)) .addUse(VarReg) diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp index daa8ea52ffe03..a0d7b756eb710 100644 --- a/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVLegalizerInfo.cpp @@ -122,13 +122,15 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) { const LLT p7 = LLT::pointer(7, PSize); // Input const LLT p8 = LLT::pointer(8, PSize); // Output const LLT p10 = LLT::pointer(10, PSize); // Private + const LLT p11 = LLT::pointer(11, PSize); // StorageBuffer // TODO: remove copy-pasting here by using concatenation in some way. auto allPtrsScalarsAndVectors = { - p0, p1, p2, p3, p4, p5, p6, p7, p8, p10, - s1, s8, s16, s32, s64, v2s1, v2s8, v2s16, v2s32, v2s64, - v3s1, v3s8, v3s16, v3s32, v3s64, v4s1, v4s8, v4s16, v4s32, v4s64, - v8s1, v8s8, v8s16, v8s32, v8s64, v16s1, v16s8, v16s16, v16s32, v16s64}; + p0, p1, p2, p3, p4, p5, p6, p7, p8, + p10, p11, s1, s8, s16, s32, s64, v2s1, v2s8, + v2s16, v2s32, v2s64, v3s1, v3s8, v3s16, v3s32, v3s64, v4s1, + v4s8, v4s16, v4s32, v4s64, v8s1, v8s8, v8s16, v8s32, v8s64, + v16s1, v16s8, v16s16, v16s32, v16s64}; auto allVectors = {v2s1, v2s8, v2s16, v2s32, v2s64, v3s1, v3s8, v3s16, v3s32, v3s64, v4s1, v4s8, v4s16, v4s32, @@ -155,10 +157,10 @@ SPIRVLegalizerInfo::SPIRVLegalizerInfo(const SPIRVSubtarget &ST) { s16, s32, s64, v2s16, v2s32, v2s64, v3s16, v3s32, v3s64, v4s16, v4s32, v4s64, v8s16, v8s32, v8s64, v16s16, v16s32, v16s64}; - auto allFloatAndIntScalarsAndPtrs = {s8, s16, s32, s64, p0, p1, p2, - p3, p4, p5, p6, p7, p8, p10}; + auto allFloatAndIntScalarsAndPtrs = {s8, s16, s32, s64, p0, p1, p2, p3, + p4, p5, p6, p7, p8, p10, p11}; - auto allPtrs = {p0, p1, p2, p3, p4, p5, p6, p7, p8, p10}; + auto allPtrs = {p0, p1, p2, p3, p4, p5, p6, p7, p8, p10, p11}; bool IsExtendedInts = ST.canUseExtension( diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp index 3b8715bce6d5f..4faf935f680f6 100644 --- a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp @@ -217,6 +217,30 @@ void SPIRVModuleAnalysis::setBaseInfo(const Module &M) { } } +static InstrSignature instrToSignature(const MachineInstr &MI, + SPIRV::ModuleAnalysisInfo &MAI, + bool UseDefReg); + +// Appends the signature of the decoration instructions that decorate R to +// Signature. +static void AppendDecorationsForReg(const MachineRegisterInfo &MRI, Register R, + InstrSignature &Signature) { + for (MachineInstr &UseMI : MRI.use_instructions(R)) { + // We don't handle OpDecorateId because getting the register alias for the + // ID can cause problems, and we do not need it for now. + if (UseMI.getOpcode() != SPIRV::OpDecorate && + UseMI.getOpcode() != SPIRV::OpMemberDecorate) + continue; + + for (unsigned i = 0; i < UseMI.getNumOperands(); ++i) { + const MachineOperand &MO = UseMI.getOperand(i); + if (MO.isReg()) + continue; + Signature.push_back(hash_value(MO)); + } + } +} + // Returns a representation of an instruction as a vector of MachineOperand // hash values, see llvm::hash_value(const MachineOperand &MO) for details. // This creates a signature of the instruction with the same content @@ -224,13 +248,17 @@ void SPIRVModuleAnalysis::setBaseInfo(const Module &M) { static InstrSignature instrToSignature(const MachineInstr &MI, SPIRV::ModuleAnalysisInfo &MAI, bool UseDefReg) { + Register DefReg; InstrSignature Signature{MI.getOpcode()}; for (unsigned i = 0; i < MI.getNumOperands(); ++i) { const MachineOperand &MO = MI.getOperand(i); size_t h; if (MO.isReg()) { - if (!UseDefReg && MO.isDef()) + if (!UseDefReg && MO.isDef()) { + assert(!DefReg.isValid() && "Multiple def registers."); + DefReg = MO.getReg(); continue; + } Register RegAlias = MAI.getRegisterAlias(MI.getMF(), MO.getReg()); if (!RegAlias.isValid()) { LLVM_DEBUG({ @@ -250,6 +278,13 @@ static InstrSignature instrToSignature(const MachineInstr &MI, } Signature.push_back(h); } + + if (DefReg.isValid()) { + // Decorations change the semantics of the current instruction. So two + // identical instruction with different decorations cannot be merged. That + // is why we add the decorations to the signature. + AppendDecorationsForReg(MI.getMF()->getRegInfo(), DefReg, Signature); + } return Signature; } diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp index 6edc757a88d37..f1b4f1e3b8205 100644 --- a/llvm/lib/Target/SPIRV/SPIRVUtils.cpp +++ b/llvm/lib/Target/SPIRV/SPIRVUtils.cpp @@ -146,6 +146,30 @@ void buildOpDecorate(Register Reg, MachineInstr &I, const SPIRVInstrInfo &TII, finishBuildOpDecorate(MIB, DecArgs, StrImm); } +void buildOpMemberDecorate(Register Reg, MachineIRBuilder &MIRBuilder, + SPIRV::Decoration::Decoration Dec, uint32_t Member, + const std::vector &DecArgs, + StringRef StrImm) { + auto MIB = MIRBuilder.buildInstr(SPIRV::OpMemberDecorate) + .addUse(Reg) + .addImm(Member) + .addImm(static_cast(Dec)); + finishBuildOpDecorate(MIB, DecArgs, StrImm); +} + +void buildOpMemberDecorate(Register Reg, MachineInstr &I, + const SPIRVInstrInfo &TII, + SPIRV::Decoration::Decoration Dec, uint32_t Member, + const std::vector &DecArgs, + StringRef StrImm) { + MachineBasicBlock &MBB = *I.getParent(); + auto MIB = BuildMI(MBB, I, I.getDebugLoc(), TII.get(SPIRV::OpMemberDecorate)) + .addUse(Reg) + .addImm(Member) + .addImm(static_cast(Dec)); + finishBuildOpDecorate(MIB, DecArgs, StrImm); +} + void buildOpSpirvDecorations(Register Reg, MachineIRBuilder &MIRBuilder, const MDNode *GVarMD) { for (unsigned I = 0, E = GVarMD->getNumOperands(); I != E; ++I) { @@ -236,6 +260,8 @@ addressSpaceToStorageClass(unsigned AddrSpace, const SPIRVSubtarget &STI) { return SPIRV::StorageClass::CodeSectionINTEL; case 10: return SPIRV::StorageClass::Private; + case 11: + return SPIRV::StorageClass::StorageBuffer; default: report_fatal_error("Unknown address space"); } diff --git a/llvm/lib/Target/SPIRV/SPIRVUtils.h b/llvm/lib/Target/SPIRV/SPIRVUtils.h index 9100e6fbf39cc..998ed3c72ad2d 100644 --- a/llvm/lib/Target/SPIRV/SPIRVUtils.h +++ b/llvm/lib/Target/SPIRV/SPIRVUtils.h @@ -144,6 +144,17 @@ void buildOpDecorate(Register Reg, MachineInstr &I, const SPIRVInstrInfo &TII, const std::vector &DecArgs, StringRef StrImm = ""); +// Add an OpDecorate instruction for the given Reg. +void buildOpMemberDecorate(Register Reg, MachineIRBuilder &MIRBuilder, + SPIRV::Decoration::Decoration Dec, uint32_t Member, + const std::vector &DecArgs, + StringRef StrImm = ""); +void buildOpMemberDecorate(Register Reg, MachineInstr &I, + const SPIRVInstrInfo &TII, + SPIRV::Decoration::Decoration Dec, uint32_t Member, + const std::vector &DecArgs, + StringRef StrImm = ""); + // Add an OpDecorate instruction by "spirv.Decorations" metadata node. void buildOpSpirvDecorations(Register Reg, MachineIRBuilder &MIRBuilder, const MDNode *GVarMD); @@ -184,6 +195,8 @@ storageClassToAddressSpace(SPIRV::StorageClass::StorageClass SC) { return 9; case SPIRV::StorageClass::Private: return 10; + case SPIRV::StorageClass::StorageBuffer: + return 11; default: report_fatal_error("Unable to get address space id"); } diff --git a/llvm/test/CodeGen/DirectX/ResourceAccess/store_rawbuffer.ll b/llvm/test/CodeGen/DirectX/ResourceAccess/store_rawbuffer.ll index b19f9d04a2dff..62395c2cac714 100644 --- a/llvm/test/CodeGen/DirectX/ResourceAccess/store_rawbuffer.ll +++ b/llvm/test/CodeGen/DirectX/ResourceAccess/store_rawbuffer.ll @@ -122,3 +122,24 @@ define void @storev4f64_byte(i32 %offset, <4 x double> %data) { ret void } + +%struct.S = type { i32 } +; CHECK-LABEL: define void @copyStructWithAddrspaceCast +define void @copyStructWithAddrspaceCast() { +entry: + %buffer = tail call target("dx.RawBuffer", %struct.S, 1, 0) + @llvm.dx.resource.handlefrombinding(i32 0, i32 0, i32 1, i32 0, i1 false) + %ptr0 = call ptr addrspace(1) @llvm.dx.resource.getpointer( + target("dx.RawBuffer", %struct.S, 1, 0) %buffer, i32 1) + %cast0 = addrspacecast ptr addrspace(1) %ptr0 to ptr + %ptr1 = call ptr addrspace(1) @llvm.dx.resource.getpointer( + target("dx.RawBuffer", %struct.S, 1, 0) %buffer, i32 0) + %cast1 = addrspacecast ptr addrspace(1) %ptr1 to ptr + + ; CHECK: %[[L:.*]] = call { i32, i1 } @llvm.dx.resource.load.rawbuffer.i32.tdx.RawBuffer_s_struct.Ss_1_0t(target("dx.RawBuffer", %struct.S, 1, 0) %buffer, i32 1, i32 0) + ; CHECK: %[[V:.*]] = extractvalue { i32, i1 } %[[L]], 0 + ; CHECK: call void @llvm.dx.resource.store.rawbuffer.tdx.RawBuffer_s_struct.Ss_1_0t.i32(target("dx.RawBuffer", %struct.S, 1, 0) %{{.*}}, i32 0, i32 0, i32 %[[V]]) + %2 = load i32, ptr %cast0, align 4 + store i32 %2, ptr %cast1, align 4 + ret void +} \ No newline at end of file diff --git a/llvm/test/CodeGen/SPIRV/hlsl-resources/StructuredBuffer.ll b/llvm/test/CodeGen/SPIRV/hlsl-resources/StructuredBuffer.ll new file mode 100644 index 0000000000000..9a365a43facca --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/hlsl-resources/StructuredBuffer.ll @@ -0,0 +1,76 @@ +; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv1.6-vulkan1.3-library %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv1.6-vulkan1.3-library %s -o - -filetype=obj | spirv-val %} + +target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64-G1" + +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none) +declare target("spirv.VulkanBuffer", [0 x i32], 12, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_0t(i32, i32, i32, i32, i1) #0 + +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none) +declare target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_1t(i32, i32, i32, i32, i1) #0 + +; CHECK: OpDecorate [[BufferVar:%.+]] DescriptorSet 0 +; CHECK: OpDecorate [[BufferVar]] Binding 0 +; CHECK: OpDecorate [[BufferType:%.+]] Block +; CHECK: OpMemberDecorate [[BufferType]] 0 Offset 0 +; CHECK: OpMemberDecorate [[BufferType]] 0 NonWritable +; CHECK: OpDecorate [[RWBufferVar:%.+]] DescriptorSet 0 +; CHECK: OpDecorate [[RWBufferVar]] Binding 1 +; CHECK: OpDecorate [[RWBufferType:%.+]] Block +; CHECK: OpMemberDecorate [[RWBufferType]] 0 Offset 0 + + +; CHECK: [[int:%[0-9]+]] = OpTypeInt 32 0 +; CHECK: [[ArrayType:%.+]] = OpTypeRuntimeArray +; CHECK: [[RWBufferType]] = OpTypeStruct [[ArrayType]] +; CHECK: [[RWBufferPtrType:%.+]] = OpTypePointer StorageBuffer [[RWBufferType]] +; CHECK: [[BufferType]] = OpTypeStruct [[ArrayType]] +; CHECK: [[BufferPtrType:%.+]] = OpTypePointer StorageBuffer [[BufferType]] +; CHECK-DAG: [[zero:%[0-9]+]] = OpConstant [[int]] 0 +; CHECK-DAG: [[one:%[0-9]+]] = OpConstant [[int]] 1 +; CHECK-DAG: [[BufferVar]] = OpVariable [[BufferPtrType]] StorageBuffer +; CHECK-DAG: [[RWBufferVar]] = OpVariable [[RWBufferPtrType]] StorageBuffer + +; Function Attrs: mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) +define void @main() local_unnamed_addr #1 { +entry: + +; CHECK-DAG: [[BufferHandle:%.+]] = OpCopyObject [[BufferPtrType]] [[BufferVar]] +; CHECK-DAG: [[RWBufferHandle:%.+]] = OpCopyObject [[RWBufferPtrType]] [[RWBufferVar]] + %_ZL1i_h.i.i = tail call target("spirv.VulkanBuffer", [0 x i32], 12, 0) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_0t(i32 0, i32 0, i32 1, i32 0, i1 false) + + %_ZL1o_h.i.i = tail call target("spirv.VulkanBuffer", [0 x i32], 12, 1) @llvm.spv.resource.handlefrombinding.tspirv.VulkanBuffer_a0i32_12_1t(i32 0, i32 1, i32 1, i32 0, i1 false) + +; CHECK: [[AC:%.+]] = OpAccessChain {{.*}} [[BufferHandle]] [[zero]] [[one]] + %0 = tail call noundef nonnull align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_0t(target("spirv.VulkanBuffer", [0 x i32], 12, 0) %_ZL1i_h.i.i, i32 1) + +; CHECK: [[LD:%.+]] = OpLoad [[int]] [[AC]] Aligned 4 + %1 = load i32, ptr addrspace(11) %0, align 4, !tbaa !3 + +; CHECK: [[AC:%.+]] = OpAccessChain {{.*}} [[RWBufferHandle]] [[zero]] [[zero]] + %2 = tail call noundef nonnull align 4 dereferenceable(4) ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1) %_ZL1o_h.i.i, i32 0) + +; CHECK: OpStore [[AC]] [[LD]] + store i32 %1, ptr addrspace(11) %2, align 4, !tbaa !3 + ret void +} + +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none) +declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_1t(target("spirv.VulkanBuffer", [0 x i32], 12, 1), i32) #0 + +; Function Attrs: mustprogress nocallback nofree nosync nounwind willreturn memory(none) +declare ptr addrspace(11) @llvm.spv.resource.getpointer.p11.tspirv.VulkanBuffer_a0i32_12_0t(target("spirv.VulkanBuffer", [0 x i32], 12, 0), i32) #0 + +attributes #0 = { mustprogress nocallback nofree nosync nounwind willreturn memory(none) } +attributes #1 = { mustprogress nofree noinline norecurse nosync nounwind willreturn memory(readwrite, inaccessiblemem: none) "approx-func-fp-math"="false" "frame-pointer"="all" "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "stack-protector-buffer-size"="8" } + +!llvm.module.flags = !{!0, !1} +!llvm.ident = !{!2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"frame-pointer", i32 2} +!2 = !{!"clang version 21.0.0git (git@github.com:s-perron/llvm-project.git 6e86add06c03e328dbb4b83f99406cc832a22f86)"} +!3 = !{!4, !4, i64 0} +!4 = !{!"int", !5, i64 0} +!5 = !{!"omnipotent char", !6, i64 0} +!6 = !{!"Simple C++ TBAA"}