From 0322fafdcd3f1e580d469d49c1c43eaa21398405 Mon Sep 17 00:00:00 2001 From: Dan Brown Date: Tue, 14 Jan 2025 22:52:18 -0700 Subject: [PATCH] [spirv] Implements vk::BufferPointer proposal (https://github.com/microsoft/hlsl-specs/blob/main/proposals/0010-vk-buffer-ref.md) Addresses issue #6489. --- include/dxc/HlslIntrinsicOp.h | 5 +- include/dxc/dxcapi.internal.h | 8 +- lib/HLSL/HLOperationLower.cpp | 9 ++ tools/clang/include/clang/AST/HlslTypes.h | 33 ++++- .../clang/include/clang/AST/OperationKinds.h | 5 + tools/clang/include/clang/Basic/Attr.td | 17 +++ .../clang/Basic/DiagnosticSemaKinds.td | 9 +- .../clang/include/clang/SPIRV/SpirvBuilder.h | 11 ++ .../clang/include/clang/SPIRV/SpirvContext.h | 12 ++ .../include/clang/SPIRV/SpirvInstruction.h | 52 +++++++ tools/clang/include/clang/SPIRV/SpirvType.h | 24 ++++ .../clang/include/clang/SPIRV/SpirvVisitor.h | 6 + tools/clang/lib/AST/ASTContextHLSL.cpp | 79 +++++++++++ tools/clang/lib/AST/Expr.cpp | 9 +- tools/clang/lib/AST/ExprConstant.cpp | 9 ++ tools/clang/lib/AST/HlslTypes.cpp | 47 +++++++ tools/clang/lib/Lex/PPMacroExpansion.cpp | 7 +- .../lib/SPIRV/AlignmentSizeCalculator.cpp | 25 ++-- tools/clang/lib/SPIRV/CapabilityVisitor.cpp | 9 +- tools/clang/lib/SPIRV/EmitVisitor.cpp | 44 +++++- tools/clang/lib/SPIRV/EmitVisitor.h | 25 ++-- tools/clang/lib/SPIRV/LowerTypeVisitor.cpp | 33 ++++- tools/clang/lib/SPIRV/LowerTypeVisitor.h | 4 + tools/clang/lib/SPIRV/SpirvBuilder.cpp | 37 +++++ tools/clang/lib/SPIRV/SpirvContext.cpp | 26 ++++ tools/clang/lib/SPIRV/SpirvEmitter.cpp | 133 +++++++++++++++++- tools/clang/lib/SPIRV/SpirvEmitter.h | 13 ++ tools/clang/lib/SPIRV/SpirvInstruction.cpp | 28 ++++ tools/clang/lib/Sema/SemaCast.cpp | 17 +++ tools/clang/lib/Sema/SemaExprCXX.cpp | 28 ++++ tools/clang/lib/Sema/SemaHLSL.cpp | 111 ++++++++++++++- .../vk.buffer-pointer.alias.cs.hlsl | 28 ++++ .../CodeGenSPIRV/vk.buffer-pointer.alias.hlsl | 72 ++++++++++ .../vk.buffer-pointer.atomic.hlsl | 39 +++++ .../vk.buffer-pointer.error1.hlsl | 19 +++ .../vk.buffer-pointer.error2.hlsl | 19 +++ .../vk.buffer-pointer.error3.hlsl | 19 +++ .../vk.buffer-pointer.error4.hlsl | 18 +++ .../vk.buffer-pointer.error5.hlsl | 26 ++++ .../vk.buffer-pointer.error6.hlsl | 23 +++ .../vk.buffer-pointer.linked-list.hlsl | 101 +++++++++++++ .../CodeGenSPIRV/vk.buffer-pointer.read.hlsl | 48 +++++++ .../CodeGenSPIRV/vk.buffer-pointer.write.hlsl | 52 +++++++ utils/hct/gen_intrin_main.txt | 10 +- utils/hct/hctdb.py | 12 +- utils/hct/hlsl_intrinsic_opcodes.json | 7 +- 46 files changed, 1326 insertions(+), 42 deletions(-) create mode 100644 tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.alias.cs.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.alias.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.atomic.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error1.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error2.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error3.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error4.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error5.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error6.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.linked-list.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.read.hlsl create mode 100644 tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.write.hlsl diff --git a/include/dxc/HlslIntrinsicOp.h b/include/dxc/HlslIntrinsicOp.h index 90f3fafd79..68b88822e8 100644 --- a/include/dxc/HlslIntrinsicOp.h +++ b/include/dxc/HlslIntrinsicOp.h @@ -231,6 +231,9 @@ enum class IntrinsicOp { IOP_VkReadClock = 223, IOP_Vkext_execution_mode = 224, IOP_Vkext_execution_mode_id = 225, + IOP_Vkreinterpret_pointer_cast = 360, + IOP_Vkstatic_pointer_cast = 361, + MOP_GetBufferContents = 362, MOP_Append = 226, MOP_RestartStrip = 227, MOP_CalculateLevelOfDetail = 228, @@ -366,7 +369,7 @@ enum class IntrinsicOp { IOP_usign = 355, MOP_InterlockedUMax = 356, MOP_InterlockedUMin = 357, - Num_Intrinsics = 360, + Num_Intrinsics = 363, }; inline bool HasUnsignedIntrinsicOpcode(IntrinsicOp opcode) { switch (opcode) { diff --git a/include/dxc/dxcapi.internal.h b/include/dxc/dxcapi.internal.h index bf8a040673..f183bb6cf0 100644 --- a/include/dxc/dxcapi.internal.h +++ b/include/dxc/dxcapi.internal.h @@ -7,6 +7,9 @@ // // // Provides non-public declarations for the DirectX Compiler component. // // // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. // +// All rights reserved. // +// // /////////////////////////////////////////////////////////////////////////////// #ifndef __DXC_API_INTERNAL__ @@ -35,6 +38,7 @@ typedef struct ID3D10Blob ID3D10Blob; static const BYTE INTRIN_TEMPLATE_FROM_TYPE = 0xff; static const BYTE INTRIN_TEMPLATE_VARARGS = 0xfe; static const BYTE INTRIN_TEMPLATE_FROM_FUNCTION = 0xfd; +static const BYTE INTRIN_TEMPLATE_FROM_FUNCTION_2 = 0xfc; // Use this enumeration to describe allowed templates (layouts) in intrinsics. enum LEGAL_INTRINSIC_TEMPLATES { @@ -128,7 +132,9 @@ enum LEGAL_INTRINSIC_COMPTYPES { LICOMPTYPE_HIT_OBJECT = 51, - LICOMPTYPE_COUNT = 52 + LICOMPTYPE_VK_BUFFER_POINTER = 52, + + LICOMPTYPE_COUNT = 53 }; static const BYTE IA_SPECIAL_BASE = 0xf0; diff --git a/lib/HLSL/HLOperationLower.cpp b/lib/HLSL/HLOperationLower.cpp index 3ab1f9fdec..445dbcc879 100644 --- a/lib/HLSL/HLOperationLower.cpp +++ b/lib/HLSL/HLOperationLower.cpp @@ -7,6 +7,9 @@ // // // Lower functions to lower HL operations to DXIL operations. // // // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. // +// All rights reserved. // +// // /////////////////////////////////////////////////////////////////////////////// #define _USE_MATH_DEFINES @@ -6818,6 +6821,12 @@ IntrinsicLower gLowerTable[] = { {IntrinsicOp::IOP_DxMaybeReorderThread, TranslateMaybeReorderThread, DXIL::OpCode::NumOpCodes_Dxil_1_8}, // FIXME: Just a placeholder Dxil // opcode + {IntrinsicOp::IOP_Vkstatic_pointer_cast, UnsupportedVulkanIntrinsic, + DXIL::OpCode::NumOpCodes}, + {IntrinsicOp::IOP_Vkreinterpret_pointer_cast, UnsupportedVulkanIntrinsic, + DXIL::OpCode::NumOpCodes}, + {IntrinsicOp::MOP_GetBufferContents, UnsupportedVulkanIntrinsic, + DXIL::OpCode::NumOpCodes}, }; } // namespace static_assert( diff --git a/tools/clang/include/clang/AST/HlslTypes.h b/tools/clang/include/clang/AST/HlslTypes.h index 3b517576fe..ab29e4bde7 100644 --- a/tools/clang/include/clang/AST/HlslTypes.h +++ b/tools/clang/include/clang/AST/HlslTypes.h @@ -6,6 +6,9 @@ // This file is distributed under the University of Illinois Open Source // // License. See LICENSE.TXT for details. // // // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. // +// All rights reserved. // +// // /// /// \file // /// \brief Defines the HLSL type system interface. // @@ -31,6 +34,7 @@ namespace clang { class ASTContext; class AttributeList; +class CXXConstructorDecl; class CXXMethodDecl; class CXXRecordDecl; class ClassTemplateDecl; @@ -402,6 +406,10 @@ DeclareNodeOrRecordType(clang::ASTContext &Ctx, DXIL::NodeIOKind Type, bool IsCompleteType = false); #ifdef ENABLE_SPIRV_CODEGEN +clang::CXXRecordDecl * +DeclareVkBufferPointerType(clang::ASTContext &context, + clang::DeclContext *declContext); + clang::CXXRecordDecl *DeclareInlineSpirvType(clang::ASTContext &context, clang::DeclContext *declContext, llvm::StringRef typeName, @@ -427,7 +435,7 @@ clang::VarDecl *DeclareBuiltinGlobal(llvm::StringRef name, clang::QualType Ty, /// method. AST context in which to /// work. Class in which the function template /// is declared. Function for which a -/// template is created. Declarations for templates to the /// function. Count of /// template declarations. A new function template declaration @@ -533,6 +541,29 @@ bool DoesTypeDefineOverloadedOperator(clang::QualType typeWithOperator, clang::QualType paramType); bool IsPatchConstantFunctionDecl(const clang::FunctionDecl *FD); +#ifdef ENABLE_SPIRV_CODEGEN +bool IsVKBufferPointerType(clang::QualType type); +clang::QualType GetVKBufferPointerBufferType(clang::QualType type); +unsigned GetVKBufferPointerAlignment(clang::QualType type); +#endif + +/// Adds a constructor declaration to the specified class +/// record. ASTContext that owns +/// declarations. Record declaration in which +/// to add constructor. Result type for +/// constructor. Types for constructor +/// parameters. Names for constructor +/// parameters. Name for +/// constructor. Whether the constructor is a +/// const function. The method declaration for the +/// constructor. +clang::CXXConstructorDecl *CreateConstructorDeclarationWithParams( + clang::ASTContext &context, clang::CXXRecordDecl *recordDecl, + clang::QualType resultType, llvm::ArrayRef paramTypes, + llvm::ArrayRef paramNames, + clang::DeclarationName declarationName, bool isConst, + bool isTemplateFunction = false); + /// Adds a function declaration to the specified class /// record. ASTContext that owns /// declarations. Record declaration in which diff --git a/tools/clang/include/clang/AST/OperationKinds.h b/tools/clang/include/clang/AST/OperationKinds.h index 75e665a5e9..3909c8b5e8 100644 --- a/tools/clang/include/clang/AST/OperationKinds.h +++ b/tools/clang/include/clang/AST/OperationKinds.h @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// // // This file enumerates the different kinds of operations that can be @@ -321,6 +324,8 @@ enum CastKind { CK_HLSLCC_FloatingToIntegral, CK_HLSLCC_FloatingToBoolean, CK_HLSLCC_FloatingCast, + CK_VK_BufferPointerToIntegral, + CK_VK_IntegralToBufferPointer, // HLSL Change - Made CK_Invalid an enum case because otherwise it is UB to // assign it to a value of CastKind. diff --git a/tools/clang/include/clang/Basic/Attr.td b/tools/clang/include/clang/Basic/Attr.td index 48193f7077..b110589726 100644 --- a/tools/clang/include/clang/Basic/Attr.td +++ b/tools/clang/include/clang/Basic/Attr.td @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// class DocumentationCategory { @@ -1440,6 +1443,20 @@ def VKStorageClassExt : InheritableAttr { let Documentation = [Undocumented]; } +def VKBufferPointer : InheritableAttr { + let Spellings = [CXX11<"", "hlsl_vk_buffer_pointer", 2021>]; + let LangOpts = [SPIRV]; + let Documentation = [Undocumented]; +} + +def VKAliasedPointer : InheritableAttr { + let Spellings = [CXX11<"vk", "aliased_pointer">]; + let Subjects = SubjectList<[Var, ParmVar], ErrorDiag>; + let Args = []; + let LangOpts = [SPIRV]; + let Documentation = [Undocumented]; +} + // Global variables that are of struct type def StructGlobalVar : SubsetSubjecthasGlobalStorage() && S->getType()->isStructureType()}]>; diff --git a/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td b/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td index 6ae59cac14..4f4dc28a4c 100644 --- a/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// @@ -7838,7 +7841,7 @@ def warn_hlsl_intrinsic_in_wrong_shader_model : Warning< "intrinsic %0 potentially used by '%1' requires shader model %2 or greater">, DefaultError, InGroup; def warn_hlsl_intrinsic_overload_in_wrong_shader_model : Warning< - "overload of intrinsic %0 requires shader model %1 or greater">, + "overload of intrinsic %0 requires shader model %1 or greater">, DefaultError, InGroup; def err_hlsl_intrinsic_template_arg_unsupported: Error< "Explicit template arguments on intrinsic %0 are not supported">; @@ -8004,6 +8007,10 @@ def err_hlsl_hitobject_unsupported_stage : Error< // SPIRV Change Starts def err_hlsl_vulkan_specific_feature: Error<"%0 is a Vulkan specific feature">; +def err_hlsl_vk_pointer_cast_alignment: Error< + "Vulkan buffer pointer cannot be cast to greater alignment">; +def err_hlsl_vk_static_pointer_cast_type: Error< + "vk::static_pointer_cast() content type must be base class of argument's content type">; // SPIRV Change Ends let CategoryName = "OpenMP Issue" in { diff --git a/tools/clang/include/clang/SPIRV/SpirvBuilder.h b/tools/clang/include/clang/SPIRV/SpirvBuilder.h index f03735115b..ed2cb3b6fd 100644 --- a/tools/clang/include/clang/SPIRV/SpirvBuilder.h +++ b/tools/clang/include/clang/SPIRV/SpirvBuilder.h @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_SPIRV_SPIRVBUILDER_H #define LLVM_CLANG_SPIRV_SPIRVBUILDER_H @@ -273,6 +276,14 @@ class SpirvBuilder { SpirvInstruction *sample, SourceLocation); + /// \brief Creates an OpConverPtrToU SPIR-V instruction with the given + /// parameters. + SpirvConvertPtrToU *createConvertPtrToU(SpirvInstruction *ptr, QualType type); + + /// \brief Creates an OpConverUToPtr SPIR-V instruction with the given + /// parameters. + SpirvConvertUToPtr *createConvertUToPtr(SpirvInstruction *val, QualType type); + /// \brief Creates SPIR-V instructions for sampling the given image. /// /// If compareVal is given a non-zero value, *Dref* variants of OpImageSample* diff --git a/tools/clang/include/clang/SPIRV/SpirvContext.h b/tools/clang/include/clang/SPIRV/SpirvContext.h index e65097bedb..c18c139642 100644 --- a/tools/clang/include/clang/SPIRV/SpirvContext.h +++ b/tools/clang/include/clang/SPIRV/SpirvContext.h @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_SPIRV_SPIRVCONTEXT_H #define LLVM_CLANG_SPIRV_SPIRVCONTEXT_H @@ -317,6 +320,13 @@ class SpirvContext { const HybridPointerType *getPointerType(QualType pointee, spv::StorageClass); + const ForwardPointerType *getForwardPointerType(QualType pointee); + + const SpirvPointerType *getForwardReference(QualType type); + + void registerForwardReference(QualType type, + const SpirvPointerType *pointerType); + /// Generates (or reuses an existing) OpString for the given string literal. SpirvString *getSpirvString(llvm::StringRef str); @@ -478,6 +488,8 @@ class SpirvContext { llvm::SmallVector hybridStructTypes; llvm::DenseMap pointerTypes; llvm::SmallVector hybridPointerTypes; + llvm::MapVector forwardPointerTypes; + llvm::MapVector forwardReferences; llvm::DenseSet functionTypes; llvm::DenseMap spirvIntrinsicTypesById; llvm::SmallVector spirvIntrinsicTypes; diff --git a/tools/clang/include/clang/SPIRV/SpirvInstruction.h b/tools/clang/include/clang/SPIRV/SpirvInstruction.h index 7ec1375bde..7a7ad3aa4d 100644 --- a/tools/clang/include/clang/SPIRV/SpirvInstruction.h +++ b/tools/clang/include/clang/SPIRV/SpirvInstruction.h @@ -4,6 +4,10 @@ // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. +// +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_SPIRV_SPIRVINSTRUCTION_H #define LLVM_CLANG_SPIRV_SPIRVINSTRUCTION_H @@ -67,6 +71,10 @@ class SpirvInstruction { IK_ConstantComposite, IK_ConstantNull, + // Pointer <-> uint conversions. + IK_ConvertPtrToU, + IK_ConvertUToPtr, + // OpUndef IK_Undef, @@ -1306,6 +1314,50 @@ class SpirvConstantNull : public SpirvConstant { bool operator==(const SpirvConstantNull &that) const; }; +class SpirvConvertPtrToU : public SpirvInstruction { +public: + SpirvConvertPtrToU(SpirvInstruction *ptr, QualType type, + SourceLocation loc = {}, SourceRange range = {}); + + DEFINE_RELEASE_MEMORY_FOR_CLASS(SpirvConvertPtrToU) + + // For LLVM-style RTTI + static bool classof(const SpirvInstruction *inst) { + return inst->getKind() == IK_ConvertPtrToU; + } + + bool operator==(const SpirvConvertPtrToU &that) const; + + bool invokeVisitor(Visitor *v) override; + + SpirvInstruction *getPtr() const { return ptr; } + +private: + SpirvInstruction *ptr; +}; + +class SpirvConvertUToPtr : public SpirvInstruction { +public: + SpirvConvertUToPtr(SpirvInstruction *intValue, QualType type, + SourceLocation loc = {}, SourceRange range = {}); + + DEFINE_RELEASE_MEMORY_FOR_CLASS(SpirvConvertUToPtr) + + // For LLVM-style RTTI + static bool classof(const SpirvInstruction *inst) { + return inst->getKind() == IK_ConvertUToPtr; + } + + bool operator==(const SpirvConvertUToPtr &that) const; + + bool invokeVisitor(Visitor *v) override; + + SpirvInstruction *getVal() const { return val; } + +private: + SpirvInstruction *val; +}; + class SpirvUndef : public SpirvInstruction { public: SpirvUndef(QualType type); diff --git a/tools/clang/include/clang/SPIRV/SpirvType.h b/tools/clang/include/clang/SPIRV/SpirvType.h index 221f01e5ff..00a00ef238 100644 --- a/tools/clang/include/clang/SPIRV/SpirvType.h +++ b/tools/clang/include/clang/SPIRV/SpirvType.h @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_SPIRV_SPIRVTYPE_H #define LLVM_CLANG_SPIRV_SPIRVTYPE_H @@ -53,6 +56,7 @@ class SpirvType { TK_RuntimeArray, TK_Struct, TK_Pointer, + TK_ForwardPointer, TK_Function, TK_AccelerationStructureNV, TK_RayQueryKHR, @@ -387,6 +391,26 @@ class SpirvPointerType : public SpirvType { spv::StorageClass storageClass; }; +/// Represents a SPIR-V forwarding pointer type. +class ForwardPointerType : public SpirvType { +public: + ForwardPointerType(QualType pointee) + : SpirvType(TK_ForwardPointer), pointeeType(pointee) {} + + static bool classof(const SpirvType *t) { + return t->getKind() == TK_ForwardPointer; + } + + const QualType getPointeeType() const { return pointeeType; } + + bool operator==(const ForwardPointerType &that) const { + return pointeeType == that.pointeeType; + } + +private: + const QualType pointeeType; +}; + /// Represents a SPIR-V function type. None of the parameters nor the return /// type is allowed to be a hybrid type. class FunctionType : public SpirvType { diff --git a/tools/clang/include/clang/SPIRV/SpirvVisitor.h b/tools/clang/include/clang/SPIRV/SpirvVisitor.h index 303a4600a1..93682518a1 100644 --- a/tools/clang/include/clang/SPIRV/SpirvVisitor.h +++ b/tools/clang/include/clang/SPIRV/SpirvVisitor.h @@ -4,6 +4,10 @@ // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. +// +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_SPIRV_SPIRVVISITOR_H #define LLVM_CLANG_SPIRV_SPIRVVISITOR_H @@ -89,6 +93,8 @@ class Visitor { DEFINE_VISIT_METHOD(SpirvConstantFloat) DEFINE_VISIT_METHOD(SpirvConstantComposite) DEFINE_VISIT_METHOD(SpirvConstantNull) + DEFINE_VISIT_METHOD(SpirvConvertPtrToU) + DEFINE_VISIT_METHOD(SpirvConvertUToPtr) DEFINE_VISIT_METHOD(SpirvUndef) DEFINE_VISIT_METHOD(SpirvCompositeConstruct) DEFINE_VISIT_METHOD(SpirvCompositeExtract) diff --git a/tools/clang/lib/AST/ASTContextHLSL.cpp b/tools/clang/lib/AST/ASTContextHLSL.cpp index dcd3e89e9a..964f805804 100644 --- a/tools/clang/lib/AST/ASTContextHLSL.cpp +++ b/tools/clang/lib/AST/ASTContextHLSL.cpp @@ -6,6 +6,9 @@ // This file is distributed under the University of Illinois Open Source // // License. See LICENSE.TXT for details. // // // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. // +// All rights reserved. // +// // // This file implements the ASTContext interface for HLSL. // // // /////////////////////////////////////////////////////////////////////////////// @@ -1068,6 +1071,47 @@ static void CreateConstructorDeclaration( (*constructorDecl)->setAccess(AccessSpecifier::AS_public); } +CXXConstructorDecl *hlsl::CreateConstructorDeclarationWithParams( + ASTContext &context, CXXRecordDecl *recordDecl, QualType resultType, + ArrayRef paramTypes, ArrayRef paramNames, + DeclarationName declarationName, bool isConst, bool isTemplateFunction) { + DXASSERT_NOMSG(recordDecl != nullptr); + DXASSERT_NOMSG(!resultType.isNull()); + DXASSERT_NOMSG(paramTypes.size() == paramNames.size()); + + TypeSourceInfo *tinfo; + CXXConstructorDecl *constructorDecl; + CreateConstructorDeclaration(context, recordDecl, resultType, paramTypes, + declarationName, isConst, &constructorDecl, + &tinfo); + + // Create and associate parameters to constructor. + SmallVector parmVarDecls; + if (!paramTypes.empty()) { + for (unsigned int i = 0; i < paramTypes.size(); ++i) { + IdentifierInfo *argIi = &context.Idents.get(paramNames[i]); + ParmVarDecl *parmVarDecl = ParmVarDecl::Create( + context, constructorDecl, NoLoc, NoLoc, argIi, paramTypes[i], + context.getTrivialTypeSourceInfo(paramTypes[i], NoLoc), + StorageClass::SC_None, nullptr); + parmVarDecl->setScopeInfo(0, i); + DXASSERT(parmVarDecl->getFunctionScopeIndex() == i, + "otherwise failed to set correct index"); + parmVarDecls.push_back(parmVarDecl); + } + constructorDecl->setParams(ArrayRef(parmVarDecls)); + AssociateParametersToFunctionPrototype(tinfo, &parmVarDecls.front(), + parmVarDecls.size()); + } + + // If this is going to be part of a template function decl, don't add it to + // the record because the template function decl will be added instead. + if (!isTemplateFunction) + recordDecl->addDecl(constructorDecl); + + return constructorDecl; +} + static void CreateObjectFunctionDeclaration( ASTContext &context, CXXRecordDecl *recordDecl, QualType resultType, ArrayRef args, DeclarationName declarationName, bool isConst, @@ -1320,6 +1364,41 @@ CXXRecordDecl *hlsl::DeclareNodeOrRecordType( } #ifdef ENABLE_SPIRV_CODEGEN +CXXRecordDecl *hlsl::DeclareVkBufferPointerType(ASTContext &context, + DeclContext *declContext) { + BuiltinTypeDeclBuilder Builder(declContext, "BufferPointer", + TagDecl::TagKind::TTK_Struct); + TemplateTypeParmDecl *TyParamDecl = + Builder.addTypeTemplateParam("recordtype"); + Builder.addIntegerTemplateParam("alignment", context.UnsignedIntTy, 0); + + Builder.startDefinition(); + + QualType paramType = QualType(TyParamDecl->getTypeForDecl(), 0); + CXXRecordDecl *recordDecl = Builder.getRecordDecl(); + + CXXMethodDecl *methodDecl = CreateObjectFunctionDeclarationWithParams( + context, recordDecl, context.getLValueReferenceType(paramType), {}, {}, + DeclarationName(&context.Idents.get("Get")), true); + CanQualType canQualType = + recordDecl->getTypeForDecl()->getCanonicalTypeUnqualified(); + CreateConstructorDeclarationWithParams( + context, recordDecl, context.VoidTy, + {context.getRValueReferenceType(canQualType)}, {"bufferPointer"}, + context.DeclarationNames.getCXXConstructorName(canQualType), false); + CreateConstructorDeclarationWithParams( + context, recordDecl, context.VoidTy, {context.UnsignedIntTy}, {"address"}, + context.DeclarationNames.getCXXConstructorName(canQualType), false); + + StringRef OpcodeGroup = GetHLOpcodeGroupName(HLOpcodeGroup::HLIntrinsic); + unsigned Opcode = static_cast(IntrinsicOp::MOP_GetBufferContents); + methodDecl->addAttr( + HLSLIntrinsicAttr::CreateImplicit(context, OpcodeGroup, "", Opcode)); + methodDecl->addAttr(HLSLCXXOverloadAttr::CreateImplicit(context)); + + return Builder.completeDefinition(); +} + CXXRecordDecl *hlsl::DeclareInlineSpirvType(clang::ASTContext &context, clang::DeclContext *declContext, llvm::StringRef typeName, diff --git a/tools/clang/lib/AST/Expr.cpp b/tools/clang/lib/AST/Expr.cpp index 0e2ec8c6c2..c6dc21217e 100644 --- a/tools/clang/lib/AST/Expr.cpp +++ b/tools/clang/lib/AST/Expr.cpp @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// // // This file implements the Expr class and subclasses. @@ -1716,7 +1719,11 @@ const char *CastExpr::getCastKindName() const { return "HLSLCC_FloatingToBoolean"; case CK_HLSLCC_FloatingCast: return "HLSLCC_FloatingCast"; - // HLSL Change Ends + case CK_VK_BufferPointerToIntegral: + return "VK_BufferPointerToIntegral"; + case CK_VK_IntegralToBufferPointer: + return "VK_IntegralToBufferPointer"; + // HLSL Change Ends } llvm_unreachable("Unhandled cast kind!"); diff --git a/tools/clang/lib/AST/ExprConstant.cpp b/tools/clang/lib/AST/ExprConstant.cpp index 5e8d4700bd..69e0760bce 100644 --- a/tools/clang/lib/AST/ExprConstant.cpp +++ b/tools/clang/lib/AST/ExprConstant.cpp @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// // // This file implements the Expr constant evaluator. @@ -7829,6 +7832,12 @@ bool IntExprEvaluator::VisitCastExpr(const CastExpr *E) { return false; return Success(Value, E); } + + // HLSL Change Starts + case CK_VK_BufferPointerToIntegral: { + return false; + // HLSL Change Ends + } } llvm_unreachable("unknown cast resulting in integral value"); diff --git a/tools/clang/lib/AST/HlslTypes.cpp b/tools/clang/lib/AST/HlslTypes.cpp index 8f9460ce63..60fff65199 100644 --- a/tools/clang/lib/AST/HlslTypes.cpp +++ b/tools/clang/lib/AST/HlslTypes.cpp @@ -5,6 +5,9 @@ // Copyright (C) Microsoft Corporation. All rights reserved. // // This file is distributed under the University of Illinois Open Source // // License. See LICENSE.TXT for details. // +// +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. // // /// /// \file // @@ -778,6 +781,50 @@ bool IsHLSLRayQueryType(clang::QualType type) { return false; } +#ifdef ENABLE_SPIRV_CODEGEN +static llvm::Optional> +MaybeGetVKBufferPointerParams(clang::QualType type) { + const RecordType *RT = dyn_cast(type.getCanonicalType()); + if (!RT) + return llvm::None; + + const ClassTemplateSpecializationDecl *templateDecl = + dyn_cast(RT->getAsCXXRecordDecl()); + if (!templateDecl || !templateDecl->getName().equals("BufferPointer")) + return llvm::None; + + auto *namespaceDecl = + dyn_cast_or_null(templateDecl->getDeclContext()); + if (!namespaceDecl || !namespaceDecl->getName().equals("vk")) + return llvm::None; + + const TemplateArgumentList &argList = templateDecl->getTemplateArgs(); + QualType bufferType = argList[0].getAsType(); + unsigned align = + argList.size() > 1 ? argList[1].getAsIntegral().getLimitedValue() : 0; + return std::make_pair(bufferType, align); +} + +bool IsVKBufferPointerType(clang::QualType type) { + return MaybeGetVKBufferPointerParams(type).hasValue(); +} + +QualType GetVKBufferPointerBufferType(clang::QualType type) { + auto bpParams = MaybeGetVKBufferPointerParams(type); + assert(bpParams.hasValue() && + "cannot get pointer type for type that is not a vk::BufferPointer"); + return bpParams.getValue().first; +} + +unsigned GetVKBufferPointerAlignment(clang::QualType type) { + auto bpParams = MaybeGetVKBufferPointerParams(type); + assert( + bpParams.hasValue() && + "cannot get pointer alignment for type that is not a vk::BufferPointer"); + return bpParams.getValue().second; +} +#endif + QualType GetHLSLResourceResultType(QualType type) { // Don't canonicalize the type as to not lose snorm in Buffer const RecordType *RT = type->getAs(); diff --git a/tools/clang/lib/Lex/PPMacroExpansion.cpp b/tools/clang/lib/Lex/PPMacroExpansion.cpp index 64ce8c9182..ebfb93df2e 100644 --- a/tools/clang/lib/Lex/PPMacroExpansion.cpp +++ b/tools/clang/lib/Lex/PPMacroExpansion.cpp @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// // // This file implements the top level handling of macro expansion for the @@ -1080,7 +1083,8 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("nullability", true) .Case("memory_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Memory)) .Case("thread_sanitizer", LangOpts.Sanitize.has(SanitizerKind::Thread)) - .Case("dataflow_sanitizer", LangOpts.Sanitize.has(SanitizerKind::DataFlow)) + .Case("dataflow_sanitizer", + LangOpts.Sanitize.has(SanitizerKind::DataFlow)) // Objective-C features .Case("objc_arr", LangOpts.ObjCAutoRefCount) // FIXME: REMOVE? .Case("objc_arc", LangOpts.ObjCAutoRefCount) @@ -1180,6 +1184,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) { .Case("has_trivial_constructor", LangOpts.CPlusPlus) .Case("has_trivial_destructor", LangOpts.CPlusPlus) .Case("has_virtual_destructor", LangOpts.CPlusPlus) + .Case("hlsl_vk_buffer_pointer", LangOpts.SPIRV) .Case("is_abstract", LangOpts.CPlusPlus) .Case("is_base_of", LangOpts.CPlusPlus) .Case("is_class", LangOpts.CPlusPlus) diff --git a/tools/clang/lib/SPIRV/AlignmentSizeCalculator.cpp b/tools/clang/lib/SPIRV/AlignmentSizeCalculator.cpp index 492640c493..db140f4766 100644 --- a/tools/clang/lib/SPIRV/AlignmentSizeCalculator.cpp +++ b/tools/clang/lib/SPIRV/AlignmentSizeCalculator.cpp @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// #include "AlignmentSizeCalculator.h" @@ -277,14 +280,20 @@ std::pair AlignmentSizeCalculator::getAlignmentAndSize( if (recordType != nullptr) { const llvm::StringRef name = recordType->getDecl()->getName(); - if (isTypeInVkNamespace(recordType) && name == "SpirvType") { - const ClassTemplateSpecializationDecl *templateDecl = - cast(recordType->getDecl()); - const uint64_t size = - templateDecl->getTemplateArgs()[1].getAsIntegral().getZExtValue(); - const uint64_t alignment = - templateDecl->getTemplateArgs()[2].getAsIntegral().getZExtValue(); - return {alignment, size}; + if (isTypeInVkNamespace(recordType)) { + if (name == "BufferPointer") { + return {8, 8}; // same as uint64_t + } + + if (name == "SpirvType") { + const ClassTemplateSpecializationDecl *templateDecl = + cast(recordType->getDecl()); + const uint64_t size = + templateDecl->getTemplateArgs()[1].getAsIntegral().getZExtValue(); + const uint64_t alignment = + templateDecl->getTemplateArgs()[2].getAsIntegral().getZExtValue(); + return {alignment, size}; + } } } diff --git a/tools/clang/lib/SPIRV/CapabilityVisitor.cpp b/tools/clang/lib/SPIRV/CapabilityVisitor.cpp index c2b5acff53..6fd0c6d950 100644 --- a/tools/clang/lib/SPIRV/CapabilityVisitor.cpp +++ b/tools/clang/lib/SPIRV/CapabilityVisitor.cpp @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// #include "CapabilityVisitor.h" @@ -200,8 +203,10 @@ void CapabilityVisitor::addCapabilityForType(const SpirvType *type, } // Pointer type else if (const auto *ptrType = dyn_cast(type)) { - addCapabilityForType(ptrType->getPointeeType(), loc, sc); - if (sc == spv::StorageClass::PhysicalStorageBuffer) { + addCapabilityForType(ptrType->getPointeeType(), loc, + ptrType->getStorageClass()); + if (ptrType->getStorageClass() == + spv::StorageClass::PhysicalStorageBuffer) { addExtension(Extension::KHR_physical_storage_buffer, "SPV_KHR_physical_storage_buffer", loc); addCapability(spv::Capability::PhysicalStorageBufferAddresses); diff --git a/tools/clang/lib/SPIRV/EmitVisitor.cpp b/tools/clang/lib/SPIRV/EmitVisitor.cpp index 6f6f5f88cd..9c0368f7a1 100644 --- a/tools/clang/lib/SPIRV/EmitVisitor.cpp +++ b/tools/clang/lib/SPIRV/EmitVisitor.cpp @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// // Do not change the inclusion order between "dxc/Support/*" files. @@ -488,6 +491,7 @@ std::vector EmitVisitor::takeBinary() { debugVariableBinary.end()); result.insert(result.end(), annotationsBinary.begin(), annotationsBinary.end()); + result.insert(result.end(), fwdDeclBinary.begin(), fwdDeclBinary.end()); result.insert(result.end(), typeConstantBinary.begin(), typeConstantBinary.end()); result.insert(result.end(), globalVarsBinary.begin(), globalVarsBinary.end()); @@ -1016,6 +1020,28 @@ bool EmitVisitor::visit(SpirvConstantNull *inst) { return true; } +bool EmitVisitor::visit(SpirvConvertPtrToU *inst) { + initInstruction(inst); + curInst.push_back(inst->getResultTypeId()); + curInst.push_back(getOrAssignResultId(inst)); + curInst.push_back(getOrAssignResultId(inst->getPtr())); + finalizeInstruction(&mainBinary); + emitDebugNameForInstruction(getOrAssignResultId(inst), + inst->getDebugName()); + return true; +} + +bool EmitVisitor::visit(SpirvConvertUToPtr *inst) { + initInstruction(inst); + curInst.push_back(inst->getResultTypeId()); + curInst.push_back(getOrAssignResultId(inst)); + curInst.push_back(getOrAssignResultId(inst->getVal())); + finalizeInstruction(&mainBinary); + emitDebugNameForInstruction(getOrAssignResultId(inst), + inst->getDebugName()); + return true; +} + bool EmitVisitor::visit(SpirvUndef *inst) { typeHandler.getOrCreateUndef(inst); emitDebugNameForInstruction(getOrAssignResultId(inst), @@ -2012,10 +2038,11 @@ void EmitTypeHandler::initTypeInstruction(spv::Op op) { curTypeInst.push_back(static_cast(op)); } -void EmitTypeHandler::finalizeTypeInstruction() { +void EmitTypeHandler::finalizeTypeInstruction(bool isFwdDecl) { curTypeInst[0] |= static_cast(curTypeInst.size()) << 16; - typeConstantBinary->insert(typeConstantBinary->end(), curTypeInst.begin(), - curTypeInst.end()); + auto binarySection = isFwdDecl ? fwdDeclBinary : typeConstantBinary; + binarySection->insert(binarySection->end(), curTypeInst.begin(), + curTypeInst.end()); } uint32_t EmitTypeHandler::getResultIdForType(const SpirvType *type, @@ -2594,6 +2621,17 @@ uint32_t EmitTypeHandler::emitType(const SpirvType *type) { curTypeInst.push_back(pointeeType); finalizeTypeInstruction(); } + // Forward pointer types + else if (const auto *fwdPtrType = dyn_cast(type)) { + const SpirvPointerType *ptrType = + context.getForwardReference(fwdPtrType->getPointeeType()); + const uint32_t refId = emitType(ptrType); + initTypeInstruction(spv::Op::OpTypeForwardPointer); + curTypeInst.push_back(refId); + curTypeInst.push_back(static_cast(ptrType->getStorageClass())); + finalizeTypeInstruction(true); + return refId; + } // Function types else if (const auto *fnType = dyn_cast(type)) { const uint32_t retTypeId = emitType(fnType->getReturnType()); diff --git a/tools/clang/lib/SPIRV/EmitVisitor.h b/tools/clang/lib/SPIRV/EmitVisitor.h index 2f5d99b89d..1f9b0939e6 100644 --- a/tools/clang/lib/SPIRV/EmitVisitor.h +++ b/tools/clang/lib/SPIRV/EmitVisitor.h @@ -4,6 +4,10 @@ // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. +// +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_SPIRV_EMITVISITOR_H #define LLVM_CLANG_SPIRV_EMITVISITOR_H @@ -49,15 +53,15 @@ class EmitTypeHandler { EmitTypeHandler(ASTContext &astCtx, SpirvContext &spvContext, const SpirvCodeGenOptions &opts, FeatureManager &featureMgr, std::vector *debugVec, - std::vector *decVec, + std::vector *decVec, std::vector *fwdVec, std::vector *typesVec, const std::function &takeNextIdFn) : astContext(astCtx), context(spvContext), featureManager(featureMgr), debugVariableBinary(debugVec), annotationsBinary(decVec), - typeConstantBinary(typesVec), takeNextIdFunction(takeNextIdFn), - emittedConstantInts({}), emittedConstantFloats({}), - emittedConstantComposites({}), emittedConstantNulls({}), - emittedUndef({}), emittedConstantBools() { + fwdDeclBinary(fwdVec), typeConstantBinary(typesVec), + takeNextIdFunction(takeNextIdFn), emittedConstantInts({}), + emittedConstantFloats({}), emittedConstantComposites({}), + emittedConstantNulls({}), emittedUndef({}), emittedConstantBools() { assert(decVec); assert(typesVec); } @@ -120,7 +124,7 @@ class EmitTypeHandler { private: void initTypeInstruction(spv::Op op); - void finalizeTypeInstruction(); + void finalizeTypeInstruction(bool isFwdDecl = false); // Returns the result-id for the given type and decorations. If a type with // the same decorations have already been used, it returns the existing @@ -161,6 +165,7 @@ class EmitTypeHandler { std::vector curDecorationInst; std::vector *debugVariableBinary; std::vector *annotationsBinary; + std::vector *fwdDeclBinary; std::vector *typeConstantBinary; std::function takeNextIdFunction; @@ -207,7 +212,7 @@ class EmitVisitor : public Visitor { : Visitor(opts, spvCtx), astContext(astCtx), featureManager(featureMgr), id(0), typeHandler(astCtx, spvCtx, opts, featureMgr, &debugVariableBinary, - &annotationsBinary, &typeConstantBinary, + &annotationsBinary, &fwdDeclBinary, &typeConstantBinary, [this]() -> uint32_t { return takeNextId(); }), debugMainFileId(0), debugInfoExtInstId(0), debugLineStart(0), debugLineEnd(0), debugColumnStart(0), debugColumnEnd(0), @@ -254,6 +259,8 @@ class EmitVisitor : public Visitor { bool visit(SpirvConstantFloat *) override; bool visit(SpirvConstantComposite *) override; bool visit(SpirvConstantNull *) override; + bool visit(SpirvConvertPtrToU *) override; + bool visit(SpirvConvertUToPtr *) override; bool visit(SpirvUndef *) override; bool visit(SpirvCompositeConstruct *) override; bool visit(SpirvCompositeExtract *) override; @@ -438,7 +445,9 @@ class EmitVisitor : public Visitor { // All annotation instructions: OpDecorate, OpMemberDecorate, OpGroupDecorate, // OpGroupMemberDecorate, and OpDecorationGroup. std::vector annotationsBinary; - // All type and constant instructions + // All forward pointer type declaration instructions + std::vector fwdDeclBinary; + // All other type and constant instructions std::vector typeConstantBinary; // All global variable declarations (all OpVariable instructions whose Storage // Class is not Function) diff --git a/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp b/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp index a5bc4a4aa8..b31d19b5d8 100644 --- a/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp +++ b/tools/clang/lib/SPIRV/LowerTypeVisitor.cpp @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// #include "LowerTypeVisitor.h" @@ -549,7 +552,9 @@ const SpirvType *LowerTypeVisitor::lowerType(QualType type, // checking the general struct type. if (const auto *spvType = lowerResourceType(type, rule, isRowMajor, srcLoc)) { - spvContext.registerStructDeclForSpirvType(spvType, decl); + if (!isa(spvType)) { + spvContext.registerStructDeclForSpirvType(spvType, decl); + } return spvType; } @@ -809,6 +814,32 @@ const SpirvType *LowerTypeVisitor::lowerVkTypeInVkNamespace( QualType realType = hlsl::GetHLSLResourceTemplateParamType(type); return lowerType(realType, rule, llvm::None, srcLoc); } + if (name == "BufferPointer") { + const size_t visitedTypeStackSize = visitedTypeStack.size(); + (void)visitedTypeStackSize; // suppress unused warning (used only in assert) + + for (QualType t : visitedTypeStack) { + if (t == type) { + return spvContext.getForwardPointerType(type); + } + } + + QualType realType = hlsl::GetHLSLResourceTemplateParamType(type); + if (rule == SpirvLayoutRule::Void) { + rule = spvOptions.sBufferLayoutRule; + } + visitedTypeStack.push_back(type); + + const SpirvType *spirvType = lowerType(realType, rule, llvm::None, srcLoc); + const auto *pointerType = spvContext.getPointerType( + spirvType, spv::StorageClass::PhysicalStorageBuffer); + spvContext.registerForwardReference(type, pointerType); + + assert(visitedTypeStack.back() == type); + visitedTypeStack.pop_back(); + assert(visitedTypeStack.size() == visitedTypeStackSize); + return pointerType; + } emitError("unknown type %0 in vk namespace", srcLoc) << type; return nullptr; } diff --git a/tools/clang/lib/SPIRV/LowerTypeVisitor.h b/tools/clang/lib/SPIRV/LowerTypeVisitor.h index 96235d1508..5b26b67e3a 100644 --- a/tools/clang/lib/SPIRV/LowerTypeVisitor.h +++ b/tools/clang/lib/SPIRV/LowerTypeVisitor.h @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_LIB_SPIRV_LOWERTYPEVISITOR_H @@ -137,6 +140,7 @@ class LowerTypeVisitor : public Visitor { AlignmentSizeCalculator alignmentCalc; /// alignment calculator bool useArrayForMat1xN; /// SPIR-V array for HLSL Matrix 1xN SpirvBuilder &spvBuilder; + SmallVector visitedTypeStack; // for type recursion detection }; } // end namespace spirv diff --git a/tools/clang/lib/SPIRV/SpirvBuilder.cpp b/tools/clang/lib/SPIRV/SpirvBuilder.cpp index 1275e2b252..6b3f43fc77 100644 --- a/tools/clang/lib/SPIRV/SpirvBuilder.cpp +++ b/tools/clang/lib/SPIRV/SpirvBuilder.cpp @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// #include "clang/SPIRV/SpirvBuilder.h" @@ -202,6 +205,14 @@ SpirvInstruction *SpirvBuilder::createLoad(QualType resultType, instruction->setLayoutRule(pointer->getLayoutRule()); instruction->setRValue(true); + if (pointer->getStorageClass() == spv::StorageClass::PhysicalStorageBuffer) { + AlignmentSizeCalculator alignmentCalc(astContext, spirvOptions); + uint32_t align, size, stride; + std::tie(align, size) = alignmentCalc.getAlignmentAndSize( + resultType, pointer->getLayoutRule(), llvm::None, &stride); + instruction->setAlignment(align); + } + if (pointer->containsAliasComponent() && isAKindOfStructuredOrByteBuffer(resultType)) { instruction->setStorageClass(spv::StorageClass::Uniform); @@ -300,6 +311,16 @@ SpirvStore *SpirvBuilder::createStore(SpirvInstruction *address, new (context) SpirvStore(loc, address, source, llvm::None, range); insertPoint->addInstruction(instruction); + if (address->getStorageClass() == spv::StorageClass::PhysicalStorageBuffer && + address->getAstResultType() != QualType()) { // exclude raw buffer + AlignmentSizeCalculator alignmentCalc(astContext, spirvOptions); + uint32_t align, size, stride; + std::tie(align, size) = alignmentCalc.getAlignmentAndSize( + address->getAstResultType(), address->getLayoutRule(), llvm::None, + &stride); + instruction->setAlignment(align); + } + if (address->isRasterizerOrdered()) { createEndInvocationInterlockEXT(loc, range); } @@ -491,6 +512,22 @@ SpirvImageTexelPointer *SpirvBuilder::createImageTexelPointer( return instruction; } +SpirvConvertPtrToU *SpirvBuilder::createConvertPtrToU(SpirvInstruction *ptr, + QualType type) { + auto *instruction = new (context) SpirvConvertPtrToU(ptr, type); + instruction->setRValue(true); + insertPoint->addInstruction(instruction); + return instruction; +} + +SpirvConvertUToPtr *SpirvBuilder::createConvertUToPtr(SpirvInstruction *val, + QualType type) { + auto *instruction = new (context) SpirvConvertUToPtr(val, type); + instruction->setRValue(false); + insertPoint->addInstruction(instruction); + return instruction; +} + spv::ImageOperandsMask SpirvBuilder::composeImageOperandsMask( SpirvInstruction *bias, SpirvInstruction *lod, const std::pair &grad, diff --git a/tools/clang/lib/SPIRV/SpirvContext.cpp b/tools/clang/lib/SPIRV/SpirvContext.cpp index 6af36eb691..47dfc67433 100644 --- a/tools/clang/lib/SPIRV/SpirvContext.cpp +++ b/tools/clang/lib/SPIRV/SpirvContext.cpp @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// #include @@ -328,6 +331,29 @@ const HybridPointerType *SpirvContext::getPointerType(QualType pointee, return result; } +const ForwardPointerType * +SpirvContext::getForwardPointerType(QualType pointee) { + assert(hlsl::IsVKBufferPointerType(pointee)); + + auto foundPointee = forwardPointerTypes.find(pointee); + if (foundPointee != forwardPointerTypes.end()) { + return foundPointee->second; + } + + return forwardPointerTypes[pointee] = new (this) ForwardPointerType(pointee); +} + +const SpirvPointerType *SpirvContext::getForwardReference(QualType type) { + return forwardReferences[type]; +} + +void SpirvContext::registerForwardReference( + QualType type, const SpirvPointerType *pointerType) { + assert(pointerType->getStorageClass() == + spv::StorageClass::PhysicalStorageBuffer); + forwardReferences[type] = pointerType; +} + FunctionType * SpirvContext::getFunctionType(const SpirvType *ret, llvm::ArrayRef param) { diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.cpp b/tools/clang/lib/SPIRV/SpirvEmitter.cpp index 579af04ea6..7cc84fa2fc 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.cpp +++ b/tools/clang/lib/SPIRV/SpirvEmitter.cpp @@ -4,6 +4,10 @@ // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. +// +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// // // This file implements a SPIR-V emitter class that takes in HLSL AST and emits @@ -1233,12 +1237,17 @@ SpirvInstruction *SpirvEmitter::doExpr(const Expr *expr, } else if (isa(expr)) { assert(curThis); result = curThis; - } else if (isa(expr)) { + } else if (const auto *constructExpr = dyn_cast(expr)) { // For RayQuery type, we should not explicitly initialize it using // CXXConstructExpr e.g., RayQuery<0> r = RayQuery<0>() is the same as we do // not have a variable initialization. Setting nullptr for the SPIR-V // instruction used for expr will let us skip the variable initialization. - if (!hlsl::IsHLSLRayQueryType(expr->getType())) + if (hlsl::IsVKBufferPointerType(expr->getType())) { + const Expr *arg = constructExpr->getArg(0); + SpirvInstruction *value = loadIfGLValue(arg, arg->getSourceRange()); + result = spvBuilder.createConvertUToPtr(value, expr->getType()); + result->setRValue(); + } else if (!hlsl::IsHLSLRayQueryType(expr->getType())) result = curThis; } else if (const auto *unaryExpr = dyn_cast(expr)) { result = doUnaryExprOrTypeTraitExpr(unaryExpr); @@ -1543,7 +1552,23 @@ void SpirvEmitter::doFunctionDecl(const FunctionDecl *decl) { // Create all parameters. for (uint32_t i = 0; i < decl->getNumParams(); ++i) { const ParmVarDecl *paramDecl = decl->getParamDecl(i); - (void)declIdMapper.createFnParam(paramDecl, i + 1 + isNonStaticMemberFn); + QualType paramType = paramDecl->getType(); + auto *param = + declIdMapper.createFnParam(paramDecl, i + 1 + isNonStaticMemberFn); +#ifdef ENABLE_SPIRV_CODEGEN + if (hlsl::IsVKBufferPointerType(paramType)) { + Optional isRowMajor = llvm::None; + QualType desugaredType = desugarType(paramType, &isRowMajor); + if (hlsl::IsVKBufferPointerType(desugaredType)) { + spvBuilder.decorateWithLiterals( + param, + static_cast(paramDecl->hasAttr() + ? spv::Decoration::AliasedPointer + : spv::Decoration::RestrictPointer), + {}, loc); + } + } +#endif } if (decl->hasBody()) { @@ -1644,6 +1669,15 @@ bool SpirvEmitter::validateVKAttributes(const NamedDecl *decl) { loc); success = false; } + +#ifdef ENABLE_SPIRV_CODEGEN + if (hlsl::IsVKBufferPointerType(cast(decl)->getType())) { + emitError("vk::push_constant attribute cannot be used on declarations " + "with vk::BufferPointer type", + loc); + success = false; + } +#endif } // vk::shader_record_nv is supported only on cbuffer/ConstantBuffer @@ -1951,6 +1985,11 @@ void SpirvEmitter::doVarDecl(const VarDecl *decl) { return; } + if (hlsl::IsVKBufferPointerType(decl->getType()) && !decl->hasInit()) { + emitError("vk::BufferPointer has no default constructor", loc); + return; + } + // We can have VarDecls inside cbuffer/tbuffer. For those VarDecls, we need // to emit their cbuffer/tbuffer as a whole and access each individual one // using access chains. @@ -2037,10 +2076,24 @@ void SpirvEmitter::doVarDecl(const VarDecl *decl) { needsLegalization = true; } - if (var != nullptr && decl->hasAttrs()) { - declIdMapper.decorateWithIntrinsicAttrs(decl, var); - if (auto attr = decl->getAttr()) { - var->setStorageClass(static_cast(attr->getStclass())); + if (var != nullptr) { + Optional isRowMajor = llvm::None; + QualType desugaredType = desugarType(decl->getType(), &isRowMajor); + if (hlsl::IsVKBufferPointerType(desugaredType)) { + spvBuilder.decorateWithLiterals( + var, + static_cast(decl->hasAttr() + ? spv::Decoration::AliasedPointer + : spv::Decoration::RestrictPointer), + {}, loc); + } + + if (decl->hasAttrs()) { + declIdMapper.decorateWithIntrinsicAttrs(decl, var); + if (auto attr = decl->getAttr()) { + var->setStorageClass( + static_cast(attr->getStclass())); + } } } @@ -3665,6 +3718,12 @@ SpirvInstruction *SpirvEmitter::doCastExpr(const CastExpr *expr, } case CastKind::CK_ToVoid: return nullptr; + case CastKind::CK_VK_BufferPointerToIntegral: { + return spvBuilder.createConvertPtrToU(doExpr(subExpr, range), toType); + } + case CastKind::CK_VK_IntegralToBufferPointer: { + return spvBuilder.createConvertUToPtr(doExpr(subExpr, range), toType); + } default: emitError("implicit cast kind '%0' unimplemented", expr->getExprLoc()) << expr->getCastKindName() << expr->getSourceRange(); @@ -5442,6 +5501,8 @@ SpirvEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr, case IntrinsicOp::MOP_WorldRayDirection: case IntrinsicOp::MOP_WorldRayOrigin: return processRayQueryIntrinsics(expr, opcode); + case IntrinsicOp::MOP_GetBufferContents: + return processIntrinsicGetBufferContents(expr); default: emitError("intrinsic '%0' method unimplemented", expr->getCallee()->getExprLoc()) @@ -7021,6 +7082,12 @@ SpirvInstruction *SpirvEmitter::reconstructValue(SpirvInstruction *srcVal, if (const auto *recordType = valType->getAs()) { assert(recordType->isStructureType()); + if (isTypeInVkNamespace(recordType) && + recordType->getDecl()->getName().equals("BufferPointer")) { + // Uniquely among structs, vk::BufferPointer lowers to a pointer type. + return srcVal; + } + LowerTypeVisitor lowerTypeVisitor(astContext, spvContext, spirvOptions, spvBuilder); const StructType *spirvStructType = @@ -9403,6 +9470,14 @@ SpirvEmitter::processIntrinsicCallExpr(const CallExpr *callExpr) { case hlsl::IntrinsicOp::IOP_EvaluateAttributeSnapped: { retVal = processEvaluateAttributeAt(callExpr, hlslOpcode, srcLoc, srcRange); break; + } + case hlsl::IntrinsicOp::IOP_Vkreinterpret_pointer_cast: { + retVal = processIntrinsicPointerCast(callExpr, false); + break; + } + case hlsl::IntrinsicOp::IOP_Vkstatic_pointer_cast: { + retVal = processIntrinsicPointerCast(callExpr, true); + break; } INTRINSIC_SPIRV_OP_CASE(ddx, DPdx, true); INTRINSIC_SPIRV_OP_CASE(ddx_coarse, DPdxCoarse, false); @@ -10782,6 +10857,50 @@ SpirvEmitter::processIntrinsicClamp(const CallExpr *callExpr) { loc, range); } +SpirvInstruction * +SpirvEmitter::processIntrinsicPointerCast(const CallExpr *callExpr, + bool isStatic) { + const Expr *argExpr = callExpr->getArg(0); + SpirvInstruction *ptr = doExpr(argExpr); + QualType srcType = argExpr->getType(); + QualType destType = callExpr->getType(); + QualType srcTypeArg = hlsl::GetVKBufferPointerBufferType(srcType); + QualType destTypeArg = hlsl::GetVKBufferPointerBufferType(destType); + return srcTypeArg == destTypeArg + ? ptr + : spvBuilder.createUnaryOp(spv::Op::OpBitcast, destType, ptr, + callExpr->getExprLoc(), + callExpr->getSourceRange()); +} + +SpirvInstruction *SpirvEmitter::processIntrinsicGetBufferContents( + const CXXMemberCallExpr *callExpr) { + LowerTypeVisitor lowerTypeVisitor(astContext, spvContext, spirvOptions, + spvBuilder); + Expr *obj = callExpr->getImplicitObjectArgument(); + SpirvInstruction *bufferPointer = doExpr(obj); + if (!bufferPointer) + return nullptr; + unsigned align = hlsl::GetVKBufferPointerAlignment(obj->getType()); + lowerTypeVisitor.visitInstruction(bufferPointer); + + const SpirvPointerType *bufferPointerType = + dyn_cast(bufferPointer->getResultType()); + SpirvLoad *retVal = + spvBuilder.createLoad(bufferPointerType->getPointeeType(), bufferPointer, + callExpr->getLocStart()); + if (!align) { + QualType bufferType = hlsl::GetVKBufferPointerBufferType(obj->getType()); + AlignmentSizeCalculator alignmentCalc(astContext, spirvOptions); + uint32_t stride; + std::tie(align, std::ignore) = alignmentCalc.getAlignmentAndSize( + bufferType, retVal->getLayoutRule(), llvm::None, &stride); + } + retVal->setAlignment(align); + retVal->setRValue(false); + return retVal; +} + SpirvInstruction * SpirvEmitter::processIntrinsicMemoryBarrier(const CallExpr *callExpr, bool isDevice, bool groupSync, diff --git a/tools/clang/lib/SPIRV/SpirvEmitter.h b/tools/clang/lib/SPIRV/SpirvEmitter.h index eca038527f..0a5ff308c2 100644 --- a/tools/clang/lib/SPIRV/SpirvEmitter.h +++ b/tools/clang/lib/SPIRV/SpirvEmitter.h @@ -4,6 +4,10 @@ // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. +// +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// // // This file defines a SPIR-V emitter class that takes in HLSL AST and emits @@ -491,6 +495,15 @@ class SpirvEmitter : public ASTConsumer { /// Processes the 'lit' intrinsic function. SpirvInstruction *processIntrinsicLit(const CallExpr *); + /// Processes the 'vk::static_pointer_cast' and 'vk_reinterpret_pointer_cast' + /// intrinsic functions. + SpirvInstruction *processIntrinsicPointerCast(const CallExpr *, + bool isStatic); + + /// Processes the vk::BufferPointer intrinsic function 'Get'. + SpirvInstruction * + processIntrinsicGetBufferContents(const CXXMemberCallExpr *); + /// Processes the 'GroupMemoryBarrier', 'GroupMemoryBarrierWithGroupSync', /// 'DeviceMemoryBarrier', 'DeviceMemoryBarrierWithGroupSync', /// 'AllMemoryBarrier', and 'AllMemoryBarrierWithGroupSync' intrinsic diff --git a/tools/clang/lib/SPIRV/SpirvInstruction.cpp b/tools/clang/lib/SPIRV/SpirvInstruction.cpp index 21aada9e82..6deb11d946 100644 --- a/tools/clang/lib/SPIRV/SpirvInstruction.cpp +++ b/tools/clang/lib/SPIRV/SpirvInstruction.cpp @@ -4,6 +4,10 @@ // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. +// +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// // // This file implements the in-memory representation of SPIR-V instructions. @@ -57,6 +61,8 @@ DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvConstantInteger) DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvConstantFloat) DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvConstantComposite) DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvConstantNull) +DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvConvertPtrToU) +DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvConvertUToPtr) DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvUndef) DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvCompositeConstruct) DEFINE_INVOKE_VISITOR_FOR_CLASS(SpirvCompositeExtract) @@ -620,6 +626,28 @@ bool SpirvConstantNull::operator==(const SpirvConstantNull &that) const { astResultType == that.astResultType; } +SpirvConvertPtrToU::SpirvConvertPtrToU(SpirvInstruction *ptr, QualType type, + SourceLocation loc, SourceRange range) + : SpirvInstruction(IK_ConvertPtrToU, spv::Op::OpConvertPtrToU, type, loc, + range), + ptr(ptr) {} + +bool SpirvConvertPtrToU::operator==(const SpirvConvertPtrToU &that) const { + return opcode == that.opcode && resultType == that.resultType && + astResultType == that.astResultType && ptr == that.ptr; +} + +SpirvConvertUToPtr::SpirvConvertUToPtr(SpirvInstruction *val, QualType type, + SourceLocation loc, SourceRange range) + : SpirvInstruction(IK_ConvertUToPtr, spv::Op::OpConvertUToPtr, type, loc, + range), + val(val) {} + +bool SpirvConvertUToPtr::operator==(const SpirvConvertUToPtr &that) const { + return opcode == that.opcode && resultType == that.resultType && + astResultType == that.astResultType && val == that.val; +} + SpirvUndef::SpirvUndef(QualType type) : SpirvInstruction(IK_Undef, spv::Op::OpUndef, type, /*SourceLocation*/ {}) {} diff --git a/tools/clang/lib/Sema/SemaCast.cpp b/tools/clang/lib/Sema/SemaCast.cpp index 10668dc388..f5a864e2b6 100644 --- a/tools/clang/lib/Sema/SemaCast.cpp +++ b/tools/clang/lib/Sema/SemaCast.cpp @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// // // This file implements semantic analysis for cast expressions, including @@ -1543,6 +1546,20 @@ TryStaticImplicitCast(Sema &Self, ExprResult &SrcExpr, QualType DestType, if (InitSeq.isConstructorInitialization()) Kind = CK_ConstructorConversion; +#ifdef ENABLE_SPIRV_CODEGEN + // Special cases for vk::BufferPointer. + else if (hlsl::IsVKBufferPointerType(SrcExpr.get()->getType()) && + DestType->isIntegerType() && CCK == Sema::CCK_CStyleCast) { + Kind = CK_VK_BufferPointerToIntegral; + SrcExpr = Result; + return TC_Success; + } else if (hlsl::IsVKBufferPointerType(DestType) && + SrcExpr.get()->getType()->isIntegerType()) { + Kind = CK_VK_IntegralToBufferPointer; + SrcExpr = Result; + return TC_Success; + } +#endif else Kind = CK_NoOp; diff --git a/tools/clang/lib/Sema/SemaExprCXX.cpp b/tools/clang/lib/Sema/SemaExprCXX.cpp index f46bb0ad9f..4723bc93e9 100644 --- a/tools/clang/lib/Sema/SemaExprCXX.cpp +++ b/tools/clang/lib/Sema/SemaExprCXX.cpp @@ -5,6 +5,9 @@ // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// //===----------------------------------------------------------------------===// /// /// \file @@ -1052,6 +1055,31 @@ Sema::BuildCXXTypeConstructExpr(TypeSourceInfo *TInfo, // corresponding cast expression. if (Exprs.size() == 1 && !ListInitialization) { Expr *Arg = Exprs[0]; +#ifdef ENABLE_SPIRV_CODEGEN + if (hlsl::IsVKBufferPointerType(Ty) && Arg->getType()->isIntegerType()) { + for (auto *ctor : Ty->getAsCXXRecordDecl()->ctors()) { + if (auto *functionType = ctor->getType()->getAs()) { + if (functionType->getNumParams() != 1 || + !functionType->getParamType(0)->isIntegerType()) + continue; + + CanQualType argType = Arg->getType()->getCanonicalTypeUnqualified(); + if (!Arg->isRValue()) { + Arg = ImpCastExprToType(Arg, argType, CK_LValueToRValue).get(); + } + if (argType != Context.UnsignedLongLongTy) { + Arg = ImpCastExprToType(Arg, Context.UnsignedLongLongTy, + CK_IntegralCast) + .get(); + } + return CXXConstructExpr::Create( + Context, Ty, TyBeginLoc, ctor, false, {Arg}, false, false, false, + false, CXXConstructExpr::ConstructionKind::CK_Complete, + SourceRange(LParenLoc, RParenLoc)); + } + } + } +#endif return BuildCXXFunctionalCastExpr(TInfo, LParenLoc, Arg, RParenLoc); } diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index 243471bc55..d6fc600692 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -6,6 +6,9 @@ // This file is distributed under the University of Illinois Open Source // // License. See LICENSE.TXT for details. // // // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. // +// All rights reserved. // +// // // This file implements the semantic support for HLSL. // // // /////////////////////////////////////////////////////////////////////////////// @@ -195,6 +198,7 @@ enum ArBasicKind { AR_OBJECT_VK_LITERAL, AR_OBJECT_VK_SPV_INTRINSIC_TYPE, AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID, + AR_OBJECT_VK_BUFFER_POINTER, #endif // ENABLE_SPIRV_CODEGEN // SPIRV change ends @@ -550,6 +554,7 @@ const UINT g_uBasicKindProps[] = { BPROP_OBJECT, // AR_OBJECT_VK_LITERAL, BPROP_OBJECT, // AR_OBJECT_VK_SPV_INTRINSIC_TYPE use recordType BPROP_OBJECT, // AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID use recordType + BPROP_OBJECT, // AR_OBJECT_VK_BUFFER_POINTER use recordType #endif // ENABLE_SPIRV_CODEGEN // SPIRV change ends @@ -1232,6 +1237,9 @@ static const ArBasicKind g_AnyOutputRecordCT[] = { static const ArBasicKind g_DxHitObjectCT[] = {AR_OBJECT_HIT_OBJECT, AR_BASIC_UNKNOWN}; +static const ArBasicKind g_VKBufferPointerCT[] = {AR_OBJECT_VK_BUFFER_POINTER, + AR_BASIC_UNKNOWN}; + // Basic kinds, indexed by a LEGAL_INTRINSIC_COMPTYPES value. const ArBasicKind *g_LegalIntrinsicCompTypes[] = { g_NullCT, // LICOMPTYPE_VOID @@ -1287,6 +1295,7 @@ const ArBasicKind *g_LegalIntrinsicCompTypes[] = { g_GroupNodeOutputRecordsCT, // LICOMPTYPE_GROUP_NODE_OUTPUT_RECORDS g_ThreadNodeOutputRecordsCT, // LICOMPTYPE_THREAD_NODE_OUTPUT_RECORDS g_DxHitObjectCT, // LICOMPTYPE_HIT_OBJECT + g_VKBufferPointerCT, // LICOMPTYPE_VK_BUFFER_POINTER }; static_assert( ARRAYSIZE(g_LegalIntrinsicCompTypes) == LICOMPTYPE_COUNT, @@ -1345,6 +1354,7 @@ static const ArBasicKind g_ArBasicKindsAsTypes[] = { AR_OBJECT_VK_SPIRV_TYPE, AR_OBJECT_VK_SPIRV_OPAQUE_TYPE, AR_OBJECT_VK_INTEGRAL_CONSTANT, AR_OBJECT_VK_LITERAL, AR_OBJECT_VK_SPV_INTRINSIC_TYPE, AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID, + AR_OBJECT_VK_BUFFER_POINTER, #endif // ENABLE_SPIRV_CODEGEN // SPIRV change ends @@ -1451,6 +1461,7 @@ static const uint8_t g_ArBasicKindsTemplateCount[] = { 1, // AR_OBJECT_VK_LITERAL, 1, // AR_OBJECT_VK_SPV_INTRINSIC_TYPE 1, // AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID + 2, // AR_OBJECT_VK_BUFFER_POINTER #endif // ENABLE_SPIRV_CODEGEN // SPIRV change ends @@ -1599,6 +1610,7 @@ static const SubscriptOperatorRecord g_ArBasicKindsSubscripts[] = { {0, MipsFalse, SampleFalse}, // AR_OBJECT_VK_LITERAL, {0, MipsFalse, SampleFalse}, // AR_OBJECT_VK_SPV_INTRINSIC_TYPE {0, MipsFalse, SampleFalse}, // AR_OBJECT_VK_SPV_INTRINSIC_RESULT_ID + {0, MipsFalse, SampleFalse}, // AR_OBJECT_VK_BUFFER_POINTER #endif // ENABLE_SPIRV_CODEGEN // SPIRV change ends @@ -1763,6 +1775,7 @@ static const char *g_ArBasicTypeNames[] = { "Literal", "ext_type", "ext_result_id", + "BufferPointer", #endif // ENABLE_SPIRV_CODEGEN // SPIRV change ends @@ -2964,6 +2977,7 @@ class HLSLExternalSource : public ExternalSemaSource { ClassTemplateDecl *m_vkIntegralConstantTemplateDecl; ClassTemplateDecl *m_vkLiteralTemplateDecl; + ClassTemplateDecl *m_vkBufferPointerTemplateDecl; // Declarations for Work Graph Output Record types ClassTemplateDecl *m_GroupNodeOutputRecordsTemplateDecl; @@ -3469,6 +3483,25 @@ class HLSLExternalSource : public ExternalSemaSource { templateTypeParmDecls.push_back(templateTypeParmDecl); continue; } + if (pArgs[i].uTemplateId == INTRIN_TEMPLATE_FROM_FUNCTION_2) { + if (TInfo == nullptr) { + TInfo = m_sema->getASTContext().CreateTypeSourceInfo( + m_context->UnsignedIntTy, 0); + } + IdentifierInfo *idT = &context.Idents.get("T"); + IdentifierInfo *idA = &context.Idents.get("A"); + TemplateTypeParmDecl *templateTypeParmDecl = + TemplateTypeParmDecl::Create(context, m_vkNSDecl, NoLoc, NoLoc, 0, + 0, idT, TypenameTrue, + ParameterPackFalse); + NonTypeTemplateParmDecl *nonTypeTemplateParmDecl = + NonTypeTemplateParmDecl::Create(context, m_vkNSDecl, NoLoc, NoLoc, + 0, 1, idA, context.UnsignedIntTy, + ParameterPackFalse, TInfo); + templateTypeParmDecl->setDefaultArgument(TInfo); + templateTypeParmDecls.push_back(templateTypeParmDecl); + templateTypeParmDecls.push_back(nonTypeTemplateParmDecl); + } } return templateTypeParmDecls; } @@ -3537,6 +3570,19 @@ class HLSLExternalSource : public ExternalSemaSource { case LICOMPTYPE_HIT_OBJECT: paramTypes.push_back(GetBasicKindType(AR_OBJECT_HIT_OBJECT)); break; + case LICOMPTYPE_VK_BUFFER_POINTER: { + const ArBasicKind *match = + std::find(g_ArBasicKindsAsTypes, + &g_ArBasicKindsAsTypes[_countof(g_ArBasicKindsAsTypes)], + AR_OBJECT_VK_BUFFER_POINTER); + DXASSERT(match != + &g_ArBasicKindsAsTypes[_countof(g_ArBasicKindsAsTypes)], + "otherwise can't find constant in basic kinds"); + size_t index = match - g_ArBasicKindsAsTypes; + paramTypes.push_back( + m_sema->getASTContext().getTypeDeclType(m_objectTypeDecls[index])); + break; + } default: DXASSERT(false, "Argument type of intrinsic function is not " "supported"); @@ -3915,6 +3961,12 @@ class HLSLExternalSource : public ExternalSemaSource { recordDecl = DeclareTemplateTypeWithHandleInDeclContext( *m_context, m_vkNSDecl, typeName, 1, nullptr); recordDecl->setImplicit(true); + } else if (kind == AR_OBJECT_VK_BUFFER_POINTER) { + if (!m_vkNSDecl) + continue; + recordDecl = DeclareVkBufferPointerType(*m_context, m_vkNSDecl); + recordDecl->setImplicit(true); + m_vkBufferPointerTemplateDecl = recordDecl->getDescribedClassTemplate(); } #endif else if (templateArgCount == 0) { @@ -4027,7 +4079,8 @@ class HLSLExternalSource : public ExternalSemaSource { HLSLExternalSource() : m_matrixTemplateDecl(nullptr), m_vectorTemplateDecl(nullptr), m_vkIntegralConstantTemplateDecl(nullptr), - m_vkLiteralTemplateDecl(nullptr), m_hlslNSDecl(nullptr), + m_vkLiteralTemplateDecl(nullptr), + m_vkBufferPointerTemplateDecl(nullptr), m_hlslNSDecl(nullptr), m_vkNSDecl(nullptr), m_dxNSDecl(nullptr), m_context(nullptr), m_sema(nullptr), m_hlslStringTypedef(nullptr) { memset(m_matrixTypes, 0, sizeof(m_matrixTypes)); @@ -4785,7 +4838,8 @@ class HLSLExternalSource : public ExternalSemaSource { case AR_OBJECT_NODE_OUTPUT_ARRAY: case AR_OBJECT_EMPTY_NODE_OUTPUT_ARRAY: case AR_OBJECT_THREAD_NODE_OUTPUT_RECORDS: - case AR_OBJECT_GROUP_NODE_OUTPUT_RECORDS: { + case AR_OBJECT_GROUP_NODE_OUTPUT_RECORDS: + case AR_OBJECT_VK_BUFFER_POINTER: { const ArBasicKind *match = std::find( g_ArBasicKindsAsTypes, &g_ArBasicKindsAsTypes[_countof(g_ArBasicKindsAsTypes)], kind); @@ -5301,6 +5355,8 @@ class HLSLExternalSource : public ExternalSemaSource { << type << GetMatrixOrVectorElementType(type); } return valid; + } else if (hlsl::IsVKBufferPointerType(qt)) { + return true; } else if (qt->isStructureOrClassType()) { const RecordType *recordType = qt->getAs(); objectKind = ClassifyRecordType(recordType); @@ -6773,6 +6829,7 @@ bool HLSLExternalSource::MatchArguments( if (pIntrinsic->pArgs[0].qwUsage && pIntrinsic->pArgs[0].uTemplateId != INTRIN_TEMPLATE_FROM_TYPE && pIntrinsic->pArgs[0].uTemplateId != INTRIN_TEMPLATE_FROM_FUNCTION && + pIntrinsic->pArgs[0].uTemplateId != INTRIN_TEMPLATE_FROM_FUNCTION_2 && pIntrinsic->pArgs[0].uComponentTypeId != INTRIN_COMPTYPE_FROM_NODEOUTPUT) { CAB(pIntrinsic->pArgs[0].uTemplateId < MaxIntrinsicArgs, 0); @@ -6813,7 +6870,8 @@ bool HLSLExternalSource::MatchArguments( // Check template. if (pArgument->uTemplateId == INTRIN_TEMPLATE_FROM_TYPE || - pArgument->uTemplateId == INTRIN_TEMPLATE_FROM_FUNCTION) { + pArgument->uTemplateId == INTRIN_TEMPLATE_FROM_FUNCTION || + pArgument->uTemplateId == INTRIN_TEMPLATE_FROM_FUNCTION_2) { continue; // Already verified that this is available. } if (pArgument->uLegalComponentTypes == LICOMPTYPE_USER_DEFINED_TYPE) { @@ -6982,6 +7040,14 @@ bool HLSLExternalSource::MatchArguments( } else { pNewType = functionTemplateTypeArg; } + } else if (pArgument->uTemplateId == INTRIN_TEMPLATE_FROM_FUNCTION_2) { + if (i == 0 && + (builtinOp == hlsl::IntrinsicOp::IOP_Vkreinterpret_pointer_cast || + builtinOp == hlsl::IntrinsicOp::IOP_Vkstatic_pointer_cast)) { + pNewType = Args[0]->getType(); + } else { + badArgIdx = std::min(badArgIdx, i); + } } else if (pArgument->uLegalComponentTypes == LICOMPTYPE_USER_DEFINED_TYPE) { if (objectElement.isNull()) { @@ -9668,6 +9734,11 @@ bool HLSLExternalSource::CanConvert(SourceLocation loc, Expr *sourceExpr, return false; } + // Cast vk::BufferPointer to pointer address. + if (SourceInfo.EltKind == AR_OBJECT_VK_BUFFER_POINTER) { + return TargetInfo.EltKind == AR_BASIC_UINT64; + } + // Cast cbuffer to its result value. if ((SourceInfo.EltKind == AR_OBJECT_CONSTANT_BUFFER || SourceInfo.EltKind == AR_OBJECT_TEXTURE_BUFFER) && @@ -11516,6 +11587,30 @@ static bool CheckBarrierCall(Sema &S, FunctionDecl *FD, CallExpr *CE) { return false; } +static bool CheckVKBufferPointerCast(Sema &S, FunctionDecl *FD, CallExpr *CE, + bool isStatic) { + const Expr *argExpr = CE->getArg(0); + QualType srcType = argExpr->getType(); + QualType destType = CE->getType(); + QualType srcTypeArg = hlsl::GetVKBufferPointerBufferType(srcType); + QualType destTypeArg = hlsl::GetVKBufferPointerBufferType(destType); + + if (isStatic && srcTypeArg != destTypeArg && + !S.IsDerivedFrom(srcTypeArg, destTypeArg)) { + S.Diags.Report(CE->getExprLoc(), + diag::err_hlsl_vk_static_pointer_cast_type); + return true; + } + + if (hlsl::GetVKBufferPointerAlignment(destType) > + hlsl::GetVKBufferPointerAlignment(srcType)) { + S.Diags.Report(CE->getExprLoc(), diag::err_hlsl_vk_pointer_cast_alignment); + return true; + } + + return false; +} + // Check HLSL call constraints, not fatal to creating the AST. void Sema::CheckHLSLFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, const FunctionProtoType *Proto) { @@ -11534,6 +11629,12 @@ void Sema::CheckHLSLFunctionCall(FunctionDecl *FDecl, CallExpr *TheCall, case hlsl::IntrinsicOp::IOP_Barrier: CheckBarrierCall(*this, FDecl, TheCall); break; + case hlsl::IntrinsicOp::IOP_Vkreinterpret_pointer_cast: + CheckVKBufferPointerCast(*this, FDecl, TheCall, false); + break; + case hlsl::IntrinsicOp::IOP_Vkstatic_pointer_cast: + CheckVKBufferPointerCast(*this, FDecl, TheCall, true); + break; default: break; } @@ -13784,6 +13885,10 @@ void hlsl::HandleDeclAttributeForHLSL(Sema &S, Decl *D, const AttributeList &A, A.getRange(), S.Context, A.getAttributeSpellingListIndex()); break; // SPIRV Change Starts + case AttributeList::AT_VKAliasedPointer: { + declAttr = ::new (S.Context) VKAliasedPointerAttr( + A.getRange(), S.Context, A.getAttributeSpellingListIndex()); + } break; case AttributeList::AT_VKDecorateIdExt: { if (A.getNumArgs() == 0 || !A.getArg(0).is()) { Handled = false; diff --git a/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.alias.cs.hlsl b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.alias.cs.hlsl new file mode 100644 index 0000000000..f0f5c54a16 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.alias.cs.hlsl @@ -0,0 +1,28 @@ +// RUN: %dxc -spirv -E main -T cs_6_7 %s | FileCheck %s + +// Bug was causing alignment miss + +struct Content { + int a; +}; + +typedef vk::BufferPointer BufferContent; +typedef vk::BufferPointer BufferBuffer; + +RWStructuredBuffer rwbuf; + +void foo(BufferContent bc) { + bc.Get().a = 1; +} + +[numthreads(1, 1, 1)] +void main() { + foo(rwbuf[0].Get()); +} + +// CHECK: [[L0:%[_0-9A-Za-z]*]] = OpLoad %{{[_0-9A-Za-z]*}} %{{[_0-9A-Za-z]*}} Aligned 8 +// CHECK: [[L1:%[_0-9A-Za-z]*]] = OpLoad %{{[_0-9A-Za-z]*}} [[L0]] Aligned 8 +// CHECK: [[L2:%[_0-9A-Za-z]*]] = OpAccessChain %{{[_0-9A-Za-z]*}} [[L1]] %int_0 +// CHECK: OpStore [[L2]] %int_1 Aligned 4 + + diff --git a/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.alias.hlsl b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.alias.hlsl new file mode 100644 index 0000000000..fc5b9edad0 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.alias.hlsl @@ -0,0 +1,72 @@ +// RUN: %dxc -spirv -Od -T ps_6_0 -E MainPs %s | FileCheck %s + +struct Globals_s +{ + float4 g_vSomeConstantA; + float4 g_vTestFloat4; + float4 g_vSomeConstantB; +}; + +typedef vk::BufferPointer Globals_p; + +struct TestPushConstant_t +{ + Globals_p m_nBufferDeviceAddress; +}; + +[[vk::push_constant]] TestPushConstant_t g_PushConstants; + +cbuffer cbuf { + [[vk::aliased_pointer]] Globals_p bp; +} + +// CHECK: OpDecorate [[BP0:%[_0-9A-Za-z]*]] AliasedPointer +// CHECK: OpDecorate [[BP1:%[_0-9A-Za-z]*]] AliasedPointer +// CHECK: OpDecorate [[BP:%[_0-9A-Za-z]*]] AliasedPointer +// CHECK: [[FLOAT:%[_0-9A-Za-z]*]] = OpTypeFloat 32 +// CHECK-DAG: [[F1:%[_0-9A-Za-z]*]] = OpConstant [[FLOAT]] 1 +// CHECK-DAG: [[F0:%[_0-9A-Za-z]*]] = OpConstant [[FLOAT]] 0 +// CHECK: [[V4FLOAT:%[_0-9A-Za-z]*]] = OpTypeVector [[FLOAT]] 4 +// CHECK: [[V4C:%[_0-9A-Za-z]*]] = OpConstantComposite [[V4FLOAT]] [[F1]] [[F0]] [[F0]] [[F0]] +// CHECK: [[INT:%[_0-9A-Za-z]*]] = OpTypeInt 32 1 +// CHECK-DAG: [[I0:%[_0-9A-Za-z]*]] = OpConstant [[INT]] 0 +// CHECK-DAG: [[I1:%[_0-9A-Za-z]*]] = OpConstant [[INT]] 1 +// CHECK: [[GS:%[_0-9A-Za-z]*]] = OpTypeStruct [[V4FLOAT]] [[V4FLOAT]] [[V4FLOAT]] +// CHECK: [[PGS:%[_0-9A-Za-z]*]] = OpTypePointer PhysicalStorageBuffer [[GS]] +// CHECK: [[TT:%[_0-9A-Za-z]*]] = OpTypeStruct [[PGS]] +// CHECK: [[PTT:%[_0-9A-Za-z]*]] = OpTypePointer PushConstant [[TT]] +// CHECK: [[PFV4FLOAT:%[_0-9A-Za-z]*]] = OpTypePointer Function [[V4FLOAT]] +// CHECK: [[PPGS:%[_0-9A-Za-z]*]] = OpTypePointer PushConstant [[PGS]] +// CHECK: [[PBV4FLOAT:%[_0-9A-Za-z]*]] = OpTypePointer PhysicalStorageBuffer [[V4FLOAT]] + +void f([[vk::aliased_pointer]] Globals_p bp) { +} + +float4 MainPs(void) : SV_Target0 +{ + float4 vTest = float4(1.0,0.0,0.0,0.0); + [[vk::aliased_pointer]] Globals_p bp0 = Globals_p(g_PushConstants.m_nBufferDeviceAddress); + [[vk::aliased_pointer]] Globals_p bp1 = Globals_p(g_PushConstants.m_nBufferDeviceAddress); + bp0.Get().g_vTestFloat4 = vTest; + f(bp0); + return bp1.Get().g_vTestFloat4; // Returns float4(1.0,0.0,0.0,0.0) +} + +// CHECK: [[GP:%[_0-9A-Za-z]*]] = OpVariable [[PTT]] PushConstant +// CHECK: [[VTEST:%[0-9A-Za-z]*]] = OpVariable [[PFV4FLOAT]] Function +// CHECK: OpStore [[VTEST]] [[V4C]] +// CHECK: [[X1:%[_0-9A-Za-z]*]] = OpAccessChain [[PPGS]] [[GP]] [[I0]] +// CHECK: [[X2:%[_0-9A-Za-z]*]] = OpLoad %_ptr_PhysicalStorageBuffer_Globals_s [[X1]] +// CHECK: OpStore [[BP0]] [[X2]] +// CHECK: [[X3:%[_0-9A-Za-z]*]] = OpAccessChain [[PPGS]] [[GP]] [[I0]] +// CHECK: [[X4:%[_0-9A-Za-z]*]] = OpLoad [[PGS]] [[X3]] +// CHECK: OpStore [[BP1]] [[X4]] +// CHECK: [[X5:%[_0-9A-Za-z]*]] = OpLoad [[V4FLOAT]] [[VTEST]] +// CHECK: [[X6:%[_0-9A-Za-z]*]] = OpLoad [[PGS]] [[BP0]] Aligned 16 +// CHECK: [[X7:%[_0-9A-Za-z]*]] = OpAccessChain [[PBV4FLOAT]] [[X6]] [[I1]] +// CHECK: OpStore [[X7]] [[X5]] Aligned 16 +// CHECK: [[X8:%[_0-9A-Za-z]*]] = OpLoad [[PGS]] [[BP1]] Aligned 16 +// CHECK: [[X9:%[_0-9A-Za-z]*]] = OpAccessChain [[PBV4FLOAT]] [[X8]] [[I1]] +// CHECK: [[X10:%[_0-9A-Za-z]*]] = OpLoad [[V4FLOAT]] [[X9]] Aligned 16 +// CHECK: OpReturnValue [[X10]] + diff --git a/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.atomic.hlsl b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.atomic.hlsl new file mode 100644 index 0000000000..992d8b39fd --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.atomic.hlsl @@ -0,0 +1,39 @@ +// RUN: %dxc -spirv -fcgl -T ps_6_0 %s | FileCheck %s + +struct S { + uint u; +}; + +typedef vk::BufferPointer BP; + +struct PC { + BP bp; +}; + +[[vk::push_constant]] PC pc; + +// CHECK: [[UINT:%[_0-9A-Za-z]*]] = OpTypeInt 32 0 +// CHECK: [[U0:%[_0-9A-Za-z]*]] = OpConstant [[UINT]] 0 +// CHECK: [[INT:%[_0-9A-Za-z]*]] = OpTypeInt 32 1 +// CHECK: [[I0:%[_0-9A-Za-z]*]] = OpConstant [[INT]] 0 +// CHECK: [[S:%[_0-9A-Za-z]*]] = OpTypeStruct [[UINT]] +// CHECK: [[PS:%[_0-9A-Za-z]*]] = OpTypePointer PhysicalStorageBuffer [[S]] +// CHECK: [[PU:%[_0-9A-Za-z]*]] = OpTypePointer PhysicalStorageBuffer [[UINT]] +// CHECK: [[U1:%[_0-9A-Za-z]*]] = OpConstant [[UINT]] 1 +// CHECK: [[PC:%[_0-9A-Za-z]*]] = OpVariable %{{[_0-9A-Za-z]*}} PushConstant + +void main() +{ +// CHECK: [[IN:%[_0-9A-Za-z]*]] = OpVariable +// CHECK: [[OUT:%[_0-9A-Za-z]*]] = OpVariable + uint u0, u1; + +// CHECK: [[X1:%[_0-9]+]] = OpAccessChain %{{[_0-9A-Za-z]*}} [[PC]] [[I0]] +// CHECK: [[X2:%[_0-9]+]] = OpLoad [[PS]] [[X1]] Aligned 4 +// CHECK: [[X3:%[_0-9]+]] = OpAccessChain [[PU]] [[X2]] [[I0]] +// CHECK: [[X4:%[_0-9]+]] = OpLoad [[UINT]] [[IN]] +// CHECK: [[X5:%[_0-9]+]] = OpAtomicExchange [[UINT]] [[X3]] [[U1]] [[U0]] [[X4]] +// CHECK: OpStore [[OUT]] [[X5]] + InterlockedExchange(pc.bp.Get().u, u0, u1); +} + diff --git a/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error1.hlsl b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error1.hlsl new file mode 100644 index 0000000000..86cf48c41e --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error1.hlsl @@ -0,0 +1,19 @@ +// RUN: not %dxc -spirv -E main -T cs_6_7 %s 2>&1 | FileCheck %s + +struct Content { + float a; +}; + +typedef vk::BufferPointer BufferContent; + +[[vk::push_constant]] +BufferContent buffer; + +[numthreads(1, 1, 1)] +void main() { + float tmp = buffer.Get().a; + buffer.Get().a = tmp; +} + +// CHECK: vk::push_constant attribute cannot be used on declarations with vk::BufferPointer type + diff --git a/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error2.hlsl b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error2.hlsl new file mode 100644 index 0000000000..09585a7664 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error2.hlsl @@ -0,0 +1,19 @@ +// RUN: not %dxc -spirv -E main -T cs_6_7 %s 2>&1 | FileCheck %s + +struct Globals_s { + float4 a; +}; + +typedef vk::BufferPointer Globals_p; +typedef vk::BufferPointer Globals_pp; + +[[vk::push_constant]] +Globals_pp bda; + +[numthreads(1, 1, 1)] +void main() { + float4 r = bda.Get().Get().a; +} + +// CHECK: vk::push_constant attribute cannot be used on declarations with vk::BufferPointer type + diff --git a/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error3.hlsl b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error3.hlsl new file mode 100644 index 0000000000..e803b5b754 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error3.hlsl @@ -0,0 +1,19 @@ +// RUN: not %dxc -spirv -E main -T cs_6_7 %s 2>&1 | FileCheck %s + +struct Content { + uint a; +}; + +typedef vk::BufferPointer BufferContent; + +[[vk::push_constant]] +BufferContent buffer; + +[numthreads(1, 1, 1)] +void main() { + uint data = buffer.Get(); + buffer.Get() = data; +} + +// CHECK: vk::push_constant attribute cannot be used on declarations with vk::BufferPointer type + diff --git a/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error4.hlsl b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error4.hlsl new file mode 100644 index 0000000000..1029aa7f2e --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error4.hlsl @@ -0,0 +1,18 @@ +// RUN: not %dxc -spirv -E main -T cs_6_7 %s 2>&1 | FileCheck %s + +struct Content { + uint a; +}; + +typedef vk::BufferPointer BufferContent; + +[[vk::push_constant]] +BufferContent buffer; + +[numthreads(1, 1, 1)] +void main() { + buffer.Get() = 1; +} + +// CHECK: vk::push_constant attribute cannot be used on declarations with vk::BufferPointer type + diff --git a/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error5.hlsl b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error5.hlsl new file mode 100644 index 0000000000..62bdb7f3cb --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error5.hlsl @@ -0,0 +1,26 @@ +// RUN: not %dxc -spirv -E main -T cs_6_7 %s 2>&1 | FileCheck %s + +struct Content { + int a; +}; + +typedef vk::BufferPointer BufferContent; +typedef vk::BufferPointer BufferBuffer; + +//[[vk::push_constant]] +//BufferContent buffer; + +RWStructuredBuffer rwbuf; + +// Wrong type in the parameter. +void foo(BufferContent bc) { + bc.Get().a = 1; +} + +[numthreads(1, 1, 1)] +void main() { + foo(rwbuf[0]); +} + +// CHECK: no matching function for call to 'foo' + diff --git a/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error6.hlsl b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error6.hlsl new file mode 100644 index 0000000000..a89b286edf --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.error6.hlsl @@ -0,0 +1,23 @@ +// RUN: not %dxc -spirv -E main -T cs_6_7 %s 2>&1 | FileCheck %s + +struct Content { + int a; +}; + +typedef vk::BufferPointer BufferContent; +typedef vk::BufferPointer BufferBuffer; + +RWStructuredBuffer buf; + +void foo(const BufferContent bc) { + bc.Get().a = 1; +} + +[numthreads(1, 1, 1)] +void main() { + static BufferContent bcs = buf[0]; + static BufferBuffer bbs = (BufferContent)bcs; +} + +// CHECK: cannot initialize a variable of type 'BufferPointer' with an lvalue of type 'BufferPointer' + diff --git a/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.linked-list.hlsl b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.linked-list.hlsl new file mode 100644 index 0000000000..71fee1a795 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.linked-list.hlsl @@ -0,0 +1,101 @@ +// RUN: %dxc -spirv -Od -T ps_6_0 -E MainPs %s | FileCheck %s + +// CHECK: OpCapability PhysicalStorageBufferAddresses +// CHECK: OpExtension "SPV_KHR_physical_storage_buffer" +// CHECK: OpMemoryModel PhysicalStorageBuffer64 GLSL450 +// CHECK: OpEntryPoint Fragment [[MAIN:%[_0-9A-Za-z]*]] "MainPs" [[OUT:%[_0-9A-Za-z]*]] + +// Forward declaration +typedef struct block_s block_t; +typedef vk::BufferPointer block_p; + +struct block_s +{ + float4 x; + block_p next; +}; + +struct TestPushConstant_t +{ + block_p root; +}; + +[[vk::push_constant]] TestPushConstant_t g_PushConstants; + +// CHECK: OpDecorate [[GP:%[_0-9A-Za-z]*]] AliasedPointer +// CHECK: OpDecorate [[COPY1:%[_0-9A-Za-z]*]] RestrictPointer +// CHECK: OpDecorate [[COPY2:%[_0-9A-Za-z]*]] RestrictPointer +// CHECK: OpMemberDecorate [[BLOCK:%[_0-9A-Za-z]*]] 1 Offset 16 +// CHECK: OpTypeForwardPointer [[PBLOCK:%[_0-9A-Za-z]*]] PhysicalStorageBuffer +// CHECK: [[SINT:%[_0-9A-Za-z]*]] = OpTypeInt 32 1 +// CHECK-DAG: [[S0:%[_0-9A-Za-z]*]] = OpConstant [[SINT]] 0 +// CHECK-DAG: [[S1:%[_0-9A-Za-z]*]] = OpConstant [[SINT]] 1 +// CHECK: [[ULONG:%[_0-9A-Za-z]*]] = OpTypeInt 64 0 +// CHECK: [[UL0:%[_0-9A-Za-z]*]] = OpConstant [[ULONG]] 0 +// CHECK: [[FLOAT:%[_0-9A-Za-z]*]] = OpTypeFloat 32 +// CHECK: [[F0:%[_0-9A-Za-z]*]] = OpConstant [[FLOAT]] 0 +// CHECK: [[V4FLOAT:%[_0-9A-Za-z]*]] = OpTypeVector [[FLOAT]] 4 +// CHECK: [[CV4FLOAT:%[_0-9A-Za-z]*]] = OpConstantComposite [[V4FLOAT]] [[F0]] [[F0]] [[F0]] [[F0]] +// CHECK: [[BLOCK]] = OpTypeStruct [[V4FLOAT]] [[PBLOCK]] +// CHECK: [[PBLOCK]] = OpTypePointer PhysicalStorageBuffer [[BLOCK]] +// CHECK: [[PC:%[_0-9A-Za-z]*]] = OpTypeStruct [[PBLOCK]] +// CHECK: [[PPC:%[_0-9A-Za-z]*]] = OpTypePointer PushConstant [[PC]] +// CHECK: [[PV4FLOAT1:%[_0-9A-Za-z]*]] = OpTypePointer Output [[V4FLOAT]] +// CHECK: [[PPBLOCK0:%[_0-9A-Za-z]*]] = OpTypePointer Function %_ptr_PhysicalStorageBuffer_block_s +// CHECK: [[PPBLOCK1:%[_0-9A-Za-z]*]] = OpTypePointer PushConstant [[PBLOCK]] +// CHECK: [[PPBLOCK2:%[_0-9A-Za-z]*]] = OpTypePointer PhysicalStorageBuffer [[PBLOCK]] +// CHECK: [[BOOL:%[_0-9A-Za-z]*]] = OpTypeBool +// CHECK: [[PV4FLOAT2:%[_0-9A-Za-z]*]] = OpTypePointer PhysicalStorageBuffer [[V4FLOAT]] +// CHECK: [[GPC:%[_0-9A-Za-z]*]] = OpVariable [[PPC]] PushConstant +// CHECK: [[OUT]] = OpVariable [[PV4FLOAT1]] Output + +[numthreads(1,1,1)] +float4 MainPs(void) : SV_Target0 +{ + if (__has_feature(hlsl_vk_buffer_pointer)) { + [[vk::aliased_pointer]] block_p g_p = + vk::static_pointer_cast(g_PushConstants.root); + g_p = g_p.Get().next; + uint64_t addr = (uint64_t)g_p; + block_p copy1 = block_p(addr); + block_p copy2 = block_p(copy1); + if (addr == 0) // Null pointer test + return float4(0.0,0.0,0.0,0.0); + return g_p.Get().x; + } + return float4(0.0,0.0,0.0,0.0); +} + +// CHECK: [[MAIN]] = OpFunction +// CHECK-NEXT: OpLabel +// CHECK-NEXT: [[RESULT:%[_0-9A-Za-z]*]] = OpFunctionCall [[V4FLOAT]] [[FUN:%[_0-9A-Za-z]*]] +// CHECK: OpStore [[OUT]] [[RESULT]] +// CHECK: OpFunctionEnd +// CHECK: [[FUN]] = OpFunction [[V4FLOAT]] +// CHECK: [[GP]] = OpVariable [[PPBLOCK0]] Function +// CHECK: [[X1:%[_0-9A-Za-z]*]] = OpAccessChain [[PPBLOCK1]] [[GPC]] [[S0]] +// CHECK: [[X2:%[_0-9A-Za-z]*]] = OpLoad [[PBLOCK]] [[X1]] +// CHECK: OpStore [[GP]] [[X2]] +// CHECK: [[X3:%[_0-9A-Za-z]*]] = OpLoad [[PBLOCK]] [[GP]] Aligned 32 +// CHECK: [[X4:%[_0-9A-Za-z]*]] = OpAccessChain [[PPBLOCK2]] [[X3]] [[S1]] +// CHECK: [[X5:%[_0-9A-Za-z]*]] = OpLoad [[PBLOCK]] [[X4]] Aligned 8 +// CHECK: OpStore [[GP]] [[X5]] +// CHECK: [[X6:%[_0-9A-Za-z]*]] = OpLoad [[PBLOCK]] [[GP]] +// CHECK: [[X7:%[_0-9A-Za-z]*]] = OpConvertPtrToU [[ULONG]] [[X6]] +// CHECK: OpStore [[ADDR:%[_0-9A-Za-z]*]] [[X7]] +// CHECK: [[X8:%[_0-9A-Za-z]*]] = OpLoad [[ULONG]] [[ADDR]] +// CHECK: [[X9:%[_0-9A-Za-z]*]] = OpConvertUToPtr [[PBLOCK]] [[X8]] +// CHECK: OpStore [[COPY1]] [[X9]] +// CHECK: [[X10:%[_0-9A-Za-z]*]] = OpLoad [[PBLOCK]] [[COPY1]] +// CHECK: OpStore [[COPY2]] [[X10]] +// CHECK: [[X11:%[_0-9A-Za-z]*]] = OpLoad [[ULONG]] [[ADDR]] +// CHECK: [[X12:%[_0-9A-Za-z]*]] = OpIEqual %bool [[X11]] [[UL0]] +// CHECK: OpBranchConditional [[X12]] [[IF_TRUE:%[_0-9A-Za-z]*]] [[IF_MERGE:%[_0-9A-Za-z]*]] +// CHECK: [[IF_TRUE]] = OpLabel +// CHECK: OpReturnValue [[CV4FLOAT]] +// CHECK: [[IF_MERGE]] = OpLabel +// CHECK: [[X13:%[_0-9A-Za-z]*]] = OpLoad [[PBLOCK]] [[GP]] Aligned 32 +// CHECK: [[X14:%[_0-9A-Za-z]*]] = OpAccessChain [[PV4FLOAT2]] [[X13]] [[S0]] +// CHECK: [[X15:%[_0-9A-Za-z]*]] = OpLoad [[V4FLOAT]] [[X14]] Aligned 16 +// CHECK: OpReturnValue [[X15]] +// CHECK: OpFunctionEnd diff --git a/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.read.hlsl b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.read.hlsl new file mode 100644 index 0000000000..c7d6f0ed2b --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.read.hlsl @@ -0,0 +1,48 @@ +// RUN: %dxc -spirv -T ps_6_0 -E MainPs %s | FileCheck %s + +// CHECK: OpEntryPoint Fragment [[FUN:%[_0-9A-Za-z]*]] "MainPs" [[OUT:%[_0-9A-Za-z]*]] + +struct Globals_s +{ + float4 g_vSomeConstantA; + float4 g_vTestFloat4; + float4 g_vSomeConstantB; +}; + +typedef vk::BufferPointer Globals_p; + +struct TestPushConstant_t +{ + Globals_p m_nBufferDeviceAddress; +}; + +[[vk::push_constant]] TestPushConstant_t g_PushConstants; + +// CHECK: [[SINT:%[_0-9A-Za-z]*]] = OpTypeInt 32 1 +// CHECK-DAG: [[S0:%[_0-9A-Za-z]*]] = OpConstant [[SINT]] 0 +// CHECK-DAG: [[S1:%[_0-9A-Za-z]*]] = OpConstant [[SINT]] 1 +// CHECK: [[FLOAT:%[_0-9A-Za-z]*]] = OpTypeFloat 32 +// CHECK: [[V4FLOAT:%[_0-9A-Za-z]*]] = OpTypeVector [[FLOAT]] 4 +// CHECK: [[GLOBALS:%[_0-9A-Za-z]*]] = OpTypeStruct [[V4FLOAT]] [[V4FLOAT]] [[V4FLOAT]] +// CHECK: [[PGLOBALS:%[_0-9A-Za-z]*]] = OpTypePointer PhysicalStorageBuffer [[GLOBALS]] +// CHECK: [[PC:%[_0-9A-Za-z]*]] = OpTypeStruct [[PGLOBALS]] +// CHECK: [[PPC:%[_0-9A-Za-z]*]] = OpTypePointer PushConstant [[PC]] +// CHECK: [[PV4FLOAT1:%[_0-9A-Za-z]*]] = OpTypePointer Output [[V4FLOAT]] +// CHECK: [[PPGLOBALS:%[_0-9A-Za-z]*]] = OpTypePointer PushConstant [[PGLOBALS]] +// CHECK: [[PV4FLOAT2:%[_0-9A-Za-z]*]] = OpTypePointer PhysicalStorageBuffer [[V4FLOAT]] +// CHECK: [[GPC:%[_0-9A-Za-z]*]] = OpVariable [[PPC]] PushConstant +// CHECK-DAG: [[OUT]] = OpVariable [[PV4FLOAT1]] Output + +float4 MainPs(void) : SV_Target0 +{ + float4 vTest = g_PushConstants.m_nBufferDeviceAddress.Get().g_vTestFloat4; + return vTest; +} + +// CHECK: [[FUN]] = OpFunction +// CHECK: [[X1:%[_0-9A-Za-z]*]] = OpAccessChain [[PPGLOBALS]] [[GPC]] [[S0]] +// CHECK: [[X2:%[_0-9A-Za-z]*]] = OpLoad [[PGLOBALS]] [[X1]] +// CHECK: [[X3:%[_0-9A-Za-z]*]] = OpAccessChain [[PV4FLOAT2]] [[X2]] [[S1]] +// CHECK: [[X4:%[_0-9A-Za-z]*]] = OpLoad [[V4FLOAT]] [[X3]] Aligned 16 +// CHECK: OpStore [[OUT]] [[X4]] +// CHECK: OpFunctionEnd diff --git a/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.write.hlsl b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.write.hlsl new file mode 100644 index 0000000000..b2efd02cbd --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/vk.buffer-pointer.write.hlsl @@ -0,0 +1,52 @@ +// RUN: %dxc -spirv -T ps_6_0 -E MainPs %s | FileCheck %s + +// CHECK: OpEntryPoint Fragment [[FUN:%[_0-9A-Za-z]*]] "MainPs" [[OUT:%[_0-9A-Za-z]*]] + +struct Globals_s +{ + float4 g_vSomeConstantA; + float4 g_vTestFloat4; + float4 g_vSomeConstantB; +}; + +typedef vk::BufferPointer Globals_p; + +struct TestPushConstant_t +{ + Globals_p m_nBufferDeviceAddress; +}; + +[[vk::push_constant]] TestPushConstant_t g_PushConstants; + +// CHECK: [[FLOAT:%[_0-9A-Za-z]*]] = OpTypeFloat 32 +// CHECK-DAG: [[F0:%[_0-9A-Za-z]*]] = OpConstant [[FLOAT]] 0 +// CHECK-DAG: [[F1:%[_0-9A-Za-z]*]] = OpConstant [[FLOAT]] 1 +// CHECK: [[V4FLOAT:%[_0-9A-Za-z]*]] = OpTypeVector [[FLOAT]] 4 +// CHECK-DAG: [[CV4FLOAT:%[_0-9A-Za-z]*]] = OpConstantComposite [[V4FLOAT]] [[F1]] [[F0]] [[F0]] [[F0]] +// CHECK: [[SINT:%[_0-9A-Za-z]*]] = OpTypeInt 32 1 +// CHECK-DAG: [[S0:%[_0-9A-Za-z]*]] = OpConstant [[SINT]] 0 +// CHECK-DAG: [[S1:%[_0-9A-Za-z]*]] = OpConstant [[SINT]] 1 +// CHECK: [[GLOBALS:%[_0-9A-Za-z]*]] = OpTypeStruct [[V4FLOAT]] [[V4FLOAT]] [[V4FLOAT]] +// CHECK: [[PGLOBALS:%[_0-9A-Za-z]*]] = OpTypePointer PhysicalStorageBuffer [[GLOBALS]] +// CHECK: [[PC:%[_0-9A-Za-z]*]] = OpTypeStruct [[PGLOBALS]] +// CHECK: [[PPC:%[_0-9A-Za-z]*]] = OpTypePointer PushConstant [[PC]] +// CHECK: [[PV4FLOAT1:%[_0-9A-Za-z]*]] = OpTypePointer Output [[V4FLOAT]] +// CHECK: [[PPGLOBALS:%[_0-9A-Za-z]*]] = OpTypePointer PushConstant [[PGLOBALS]] +// CHECK: [[PV4FLOAT2:%[_0-9A-Za-z]*]] = OpTypePointer PhysicalStorageBuffer [[V4FLOAT]] +// CHECK: [[GPC:%[_0-9A-Za-z]*]] = OpVariable [[PPC]] PushConstant +// CHECK-DAG: [[OUT]] = OpVariable [[PV4FLOAT1]] Output + +float4 MainPs(void) : SV_Target0 +{ + float4 vTest = float4(1.0,0.0,0.0,0.0); + g_PushConstants.m_nBufferDeviceAddress.Get().g_vTestFloat4 = vTest; + return vTest; +} + +// CHECK: [[FUN]] = OpFunction +// CHECK: [[X1:%[_0-9A-Za-z]*]] = OpAccessChain [[PPGLOBALS]] [[GPC]] [[S0]] +// CHECK: [[X2:%[_0-9A-Za-z]*]] = OpLoad [[PGLOBALS]] [[X1]] +// CHECK: [[X3:%[_0-9A-Za-z]*]] = OpAccessChain [[PV4FLOAT2]] [[X2]] [[S1]] +// CHECK: OpStore [[X3]] [[CV4FLOAT]] Aligned 16 +// CHECK: OpStore [[OUT]] [[CV4FLOAT]] +// CHECK: OpFunctionEnd diff --git a/utils/hct/gen_intrin_main.txt b/utils/hct/gen_intrin_main.txt index 0ca5b0716b..55c3643d95 100644 --- a/utils/hct/gen_intrin_main.txt +++ b/utils/hct/gen_intrin_main.txt @@ -1,6 +1,9 @@ // Copyright (C) Microsoft Corporation. All rights reserved. // This file is distributed under the University of Illinois Open Source License. See LICENSE.TXT for details. // +// Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +// All rights reserved. +// // See hctdb.py for the implementation of intrinsic file processing. // // Intrinsic declarations are grouped into namespaces that @@ -393,7 +396,13 @@ void [[]] RawBufferStore(in u64 addr, in $funcT value); void [[]] RawBufferStore(in u64 addr, in $funcT value, in uint alignment); void [[]] ext_execution_mode(in uint mode, ...); void [[]] ext_execution_mode_id(in uint mode, ...); +$funcT2 [[]] static_pointer_cast(in VkBufferPointer ptr); +$funcT2 [[]] reinterpret_pointer_cast(in VkBufferPointer ptr); + +} namespace +namespace BufferPointerMethods { +$classT [[ro]] GetBufferContents(); } namespace // SPIRV Change Ends @@ -1147,4 +1156,3 @@ $classT [[]] SubpassLoad(in int sample) : subpassinputms_load; } namespace // SPIRV Change Ends - diff --git a/utils/hct/hctdb.py b/utils/hct/hctdb.py index e32ab1915a..c979ec6d0a 100644 --- a/utils/hct/hctdb.py +++ b/utils/hct/hctdb.py @@ -1,5 +1,7 @@ # Copyright (C) Microsoft Corporation. All rights reserved. # This file is distributed under the University of Illinois Open Source License. See LICENSE.TXT for details. +# Modifications Copyright(C) 2025 Advanced Micro Devices, Inc. +# All rights reserved. ############################################################################### # DXIL information. # ############################################################################### @@ -8450,6 +8452,7 @@ def __init__(self, intrinsic_defs, opcode_data): "GroupNodeOutputRecords": "LICOMPTYPE_GROUP_NODE_OUTPUT_RECORDS", "ThreadNodeOutputRecords": "LICOMPTYPE_THREAD_NODE_OUTPUT_RECORDS", "DxHitObject": "LICOMPTYPE_HIT_OBJECT", + "VkBufferPointer": "LICOMPTYPE_VK_BUFFER_POINTER", } self.trans_rowcol = {"r": "IA_R", "c": "IA_C", "r2": "IA_R2", "c2": "IA_C2"} @@ -8511,7 +8514,8 @@ def load_intrinsics(self, intrinsic_defs): (?:RW)?(?:Texture\w*|ByteAddressBuffer) | acceleration_struct | ray_desc | RayQuery | DxHitObject | Node\w* | RWNode\w* | EmptyNode\w* | - AnyNodeOutput\w* | NodeOutputRecord\w* | GroupShared\w* + AnyNodeOutput\w* | NodeOutputRecord\w* | GroupShared\w* | + VkBufferPointer $)""", flags=re.VERBOSE, ) @@ -8563,6 +8567,10 @@ def process_arg(desc, idx, done_args, intrinsic_name): template_id = "-3" component_id = "0" type_name = "void" + elif type_name == "$funcT2": + template_id = "-4" + component_id = "0" + type_name = "void" elif type_name == "...": assert idx != 0, "'...' can only be used in the parameter list" template_id = "-2" @@ -8691,6 +8699,8 @@ def do_object(m): template_id = "INTRIN_TEMPLATE_VARARGS" elif template_id == "-3": template_id = "INTRIN_TEMPLATE_FROM_FUNCTION" + elif template_id == "-4": + template_id = "INTRIN_TEMPLATE_FROM_FUNCTION_2" if component_id == "-1": component_id = "INTRIN_COMPTYPE_FROM_TYPE_ELT0" if component_id == "-2": diff --git a/utils/hct/hlsl_intrinsic_opcodes.json b/utils/hct/hlsl_intrinsic_opcodes.json index 4c85069488..c4527277cd 100644 --- a/utils/hct/hlsl_intrinsic_opcodes.json +++ b/utils/hct/hlsl_intrinsic_opcodes.json @@ -1,6 +1,6 @@ { "IntrinsicOpCodes": { - "Num_Intrinsics": 360, + "Num_Intrinsics": 363, "IOP_AcceptHitAndEndSearch": 0, "IOP_AddUint64": 1, "IOP_AllMemoryBarrier": 2, @@ -360,6 +360,9 @@ "MOP_InterlockedUMax": 356, "MOP_InterlockedUMin": 357, "MOP_DxHitObject_MakeNop": 358, - "IOP_DxMaybeReorderThread": 359 + "IOP_DxMaybeReorderThread": 359, + "IOP_Vkreinterpret_pointer_cast": 360, + "IOP_Vkstatic_pointer_cast": 361, + "MOP_GetBufferContents": 362 } }