From 8c76f28c8a0ba3d087361141366968071fa3af6e Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 29 Oct 2024 16:02:26 -0700 Subject: [PATCH 01/15] [HLSL] Add Increment/DecrementCounter methods to structured buffers Introduces `__builtin_hlsl_buffer_update_counter` clang buildin that is used to implement IncrementCounter and DecrementCounter methods on RWStructuredBuffer and RasterizerOrderedStructuredBuffer. The builtin is translated to LLVM intrisics llvm.dx.bufferUpdateCounter/llvm.spv.bufferUpdateCounter. Introduces `BuiltinTypeMethodBuilder` helper in `HLSLExternalSemaSource` that allows adding methods to builtin types using the builder pattern like this: BuiltinTypeMethodBuilder(Sema, RecordBuilder, "MethodName", ReturnType) .addParam("param_name", Type, InOutModifier) .callBuiltin("buildin_name", { BuiltinParams }) .finalizeMethod(); Fixes #113513 --- clang/include/clang/Basic/Builtins.td | 7 +- .../clang/Basic/DiagnosticSemaKinds.td | 4 + clang/lib/CodeGen/CGBuiltin.cpp | 8 + clang/lib/CodeGen/CGHLSLRuntime.h | 1 + clang/lib/Sema/HLSLExternalSemaSource.cpp | 278 ++++++++++++++++-- clang/lib/Sema/SemaExpr.cpp | 4 + clang/lib/Sema/SemaHLSL.cpp | 41 +++ .../StructuredBuffers-methods-lib.hlsl | 25 ++ .../StructuredBuffers-methods-ps.hlsl | 29 ++ .../buffer_update_counter-errors.hlsl | 22 ++ llvm/include/llvm/IR/IntrinsicsDirectX.td | 3 + llvm/include/llvm/IR/IntrinsicsSPIRV.td | 3 + 12 files changed, 393 insertions(+), 32 deletions(-) create mode 100644 clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl create mode 100644 clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl create mode 100644 clang/test/SemaHLSL/BuiltIns/buffer_update_counter-errors.hlsl diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 90475a361bb8f..72bc2d5e7df23 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -4846,7 +4846,6 @@ def HLSLSaturate : LangBuiltin<"HLSL_LANG"> { let Prototype = "void(...)"; } - def HLSLSelect : LangBuiltin<"HLSL_LANG"> { let Spellings = ["__builtin_hlsl_select"]; let Attributes = [NoThrow, Const]; @@ -4871,6 +4870,12 @@ def HLSLRadians : LangBuiltin<"HLSL_LANG"> { let Prototype = "void(...)"; } +def HLSLBufferUpdateCounter : LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_buffer_update_counter"]; + let Attributes = [NoThrow, Const]; + let Prototype = "uint32_t(...)"; +} + // Builtins for XRay. def XRayCustomEvent : Builtin { let Spellings = ["__xray_customevent"]; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 8e4718008ece7..2aea6bb657578 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7256,6 +7256,8 @@ def err_typecheck_illegal_increment_decrement : Error< "cannot %select{decrement|increment}1 value of type %0">; def err_typecheck_expect_int : Error< "used type %0 where integer is required">; +def err_typecheck_expect_hlsl_resource : Error< + "used type %0 where __hlsl_resource_t is required">; def err_typecheck_arithmetic_incomplete_or_sizeless_type : Error< "arithmetic on a pointer to %select{an incomplete|sizeless}0 type %1">; def err_typecheck_pointer_arith_function_type : Error< @@ -12485,6 +12487,8 @@ def warn_attr_min_eq_max: Warning< def err_hlsl_attribute_number_arguments_insufficient_shader_model: Error< "attribute %0 with %1 arguments requires shader model %2 or greater">; +def err_hlsl_expect_arg_const_int_one_or_neg_one: Error< + "argument %0 must be constant integer 1 or -1">; // Layout randomization diagnostics. def err_non_designated_init_used : Error< diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index e2d03eff8ab4a..71273de3400b1 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -18959,6 +18959,14 @@ case Builtin::BI__builtin_hlsl_elementwise_isinf: { CGM.getHLSLRuntime().getRadiansIntrinsic(), ArrayRef{Op0}, nullptr, "hlsl.radians"); } + case Builtin::BI__builtin_hlsl_buffer_update_counter: { + Value *ResHandle = EmitScalarExpr(E->getArg(0)); + Value *Offset = EmitScalarExpr(E->getArg(1)); + return Builder.CreateIntrinsic( + /*ReturnType=*/Offset->getType(), + CGM.getHLSLRuntime().getBufferUpdateCounterIntrinsic(), + ArrayRef{ResHandle, Offset}, nullptr); + } } return nullptr; } diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index ff7df41b5c62e..aac93dfc373ed 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -93,6 +93,7 @@ class CGHLSLRuntime { GENERATE_HLSL_INTRINSIC_FUNCTION(WaveReadLaneAt, wave_readlane) GENERATE_HLSL_INTRINSIC_FUNCTION(CreateHandleFromBinding, handle_fromBinding) + GENERATE_HLSL_INTRINSIC_FUNCTION(BufferUpdateCounter, bufferUpdateCounter) //===----------------------------------------------------------------------===// // End of reserved area for HLSL intrinsic getters. diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index ce8564429b380..24c3954b134c5 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -22,12 +22,15 @@ #include "clang/Sema/SemaHLSL.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Frontend/HLSL/HLSLResource.h" +#include "llvm/Support/ErrorHandling.h" #include using namespace clang; using namespace llvm::hlsl; +static DeclRefExpr *lookupBuiltinFunction(Sema &S, StringRef Name); + namespace { struct TemplateParameterListBuilder; @@ -121,12 +124,8 @@ struct BuiltinTypeDeclBuilder { TypeSourceInfo *ElementTypeInfo = nullptr; QualType ElemTy = Ctx.Char8Ty; - if (Template) { - if (const auto *TTD = dyn_cast( - Template->getTemplateParameters()->getParam(0))) { - ElemTy = QualType(TTD->getTypeForDecl(), 0); - } - } + if (Template) + ElemTy = getFirstTemplateTypeParam(); ElementTypeInfo = Ctx.getTrivialTypeSourceInfo(ElemTy, SourceLocation()); // add handle member with resource type attributes @@ -145,25 +144,6 @@ struct BuiltinTypeDeclBuilder { return *this; } - static DeclRefExpr *lookupBuiltinFunction(ASTContext &AST, Sema &S, - StringRef Name) { - IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier); - DeclarationNameInfo NameInfo = - DeclarationNameInfo(DeclarationName(&II), SourceLocation()); - LookupResult R(S, NameInfo, Sema::LookupOrdinaryName); - // AllowBuiltinCreation is false but LookupDirect will create - // the builtin when searching the global scope anyways... - S.LookupName(R, S.getCurScope()); - // FIXME: If the builtin function was user-declared in global scope, - // this assert *will* fail. Should this call LookupBuiltin instead? - assert(R.isSingleResult() && - "Since this is a builtin it should always resolve!"); - auto *VD = cast(R.getFoundDecl()); - QualType Ty = VD->getType(); - return DeclRefExpr::Create(AST, NestedNameSpecifierLoc(), SourceLocation(), - VD, false, NameInfo, Ty, VK_PRValue); - } - static Expr *emitResourceClassExpr(ASTContext &AST, ResourceClass RC) { return IntegerLiteral::Create( AST, @@ -211,12 +191,8 @@ struct BuiltinTypeDeclBuilder { ASTContext &AST = Record->getASTContext(); QualType ElemTy = AST.Char8Ty; - if (Template) { - if (const auto *TTD = dyn_cast( - Template->getTemplateParameters()->getParam(0))) { - ElemTy = QualType(TTD->getTypeForDecl(), 0); - } - } + if (Template) + ElemTy = getFirstTemplateTypeParam(); QualType ReturnTy = ElemTy; FunctionProtoType::ExtProtoInfo ExtInfo; @@ -282,6 +258,23 @@ struct BuiltinTypeDeclBuilder { return *this; } + FieldDecl *getResourceHandleField() { + FieldDecl *FD = Fields["h"]; + if (FD && FD->getType()->isHLSLAttributedResourceType()) + return FD; + return nullptr; + } + + QualType getFirstTemplateTypeParam() { + if (Template) { + if (const auto *TTD = dyn_cast( + Template->getTemplateParameters()->getParam(0))) { + return QualType(TTD->getTypeForDecl(), 0); + } + } + return QualType(); + } + BuiltinTypeDeclBuilder &startDefinition() { if (Record->isCompleteDefinition()) return *this; @@ -302,6 +295,10 @@ struct BuiltinTypeDeclBuilder { TemplateParameterListBuilder addTemplateArgumentList(Sema &S); BuiltinTypeDeclBuilder &addSimpleTemplateParams(Sema &S, ArrayRef Names); + + // Builtin types methods + BuiltinTypeDeclBuilder &addIncrementCounterMethod(Sema &S); + BuiltinTypeDeclBuilder &addDecrementCounterMethod(Sema &S); }; struct TemplateParameterListBuilder { @@ -359,6 +356,176 @@ struct TemplateParameterListBuilder { return Builder; } }; + +// Builder for methods of builtin types. Allows adding methods to builtin types +// using the builder pattern like this: +// +// BuiltinTypeMethodBuilder(Sema, RecordBuilder, "MethodName", ReturnType) +// .addParam("param_name", Type, InOutModifier) +// .callBuiltin("buildin_name", { BuiltinParams }) +// .finalizeMethod(); +// +// The builder needs to have all of the method parameters before it can create +// a CXXMethodDecl. It collects them in addParam calls and when a first +// method that builds the body is called it creates the CXXMethodDecl and +// ParmVarDecls instances. These can then be referenced from the body building +// methods. Destructor or an explicit call to finalizeMethod() will complete +// the method definition. +struct BuiltinTypeMethodBuilder { + struct MethodParam { + const IdentifierInfo &NameII; + QualType Ty; + HLSLParamModifierAttr::Spelling Modifier; + MethodParam(const IdentifierInfo &NameII, QualType Ty, + HLSLParamModifierAttr::Spelling Modifier) + : NameII(NameII), Ty(Ty), Modifier(Modifier) {} + }; + + BuiltinTypeDeclBuilder &DeclBuilder; + Sema &S; + DeclarationNameInfo NameInfo; + QualType ReturnTy; + CXXMethodDecl *Method; + llvm::SmallVector Params; + llvm::SmallVector StmtsList; + +public: + BuiltinTypeMethodBuilder(Sema &S, BuiltinTypeDeclBuilder &DB, StringRef Name, + QualType ReturnTy) + : DeclBuilder(DB), S(S), ReturnTy(ReturnTy), Method(nullptr) { + const IdentifierInfo &II = + S.getASTContext().Idents.get(Name, tok::TokenKind::identifier); + NameInfo = DeclarationNameInfo(DeclarationName(&II), SourceLocation()); + } + + BuiltinTypeMethodBuilder &addParam(StringRef Name, QualType Ty, + HLSLParamModifierAttr::Spelling Modifier = + HLSLParamModifierAttr::Keyword_in) { + assert(Method == nullptr && "Cannot add param, method already created"); + + const IdentifierInfo &II = + S.getASTContext().Idents.get(Name, tok::TokenKind::identifier); + Params.emplace_back(II, Ty, Modifier); + return *this; + } + +private: + void createMethodDecl() { + assert(Method == nullptr && "Method already created"); + + // create method type + ASTContext &AST = S.getASTContext(); + SmallVector ParamTypes; + for (auto &MP : Params) + ParamTypes.emplace_back(MP.Ty); + QualType MethodTy = AST.getFunctionType(ReturnTy, ParamTypes, + FunctionProtoType::ExtProtoInfo()); + + // create method decl + auto *TSInfo = AST.getTrivialTypeSourceInfo(MethodTy, SourceLocation()); + Method = + CXXMethodDecl::Create(AST, DeclBuilder.Record, SourceLocation(), + NameInfo, MethodTy, TSInfo, SC_None, false, false, + ConstexprSpecKind::Unspecified, SourceLocation()); + + // create params & set them to the function prototype + SmallVector ParmDecls; + auto FnProtoLoc = + Method->getTypeSourceInfo()->getTypeLoc().getAs(); + unsigned i = 0; + for (auto &MP : Params) { + ParmVarDecl *Parm = ParmVarDecl::Create( + AST, Method->getDeclContext(), SourceLocation(), SourceLocation(), + &MP.NameII, MP.Ty, + AST.getTrivialTypeSourceInfo(MP.Ty, SourceLocation()), SC_None, + nullptr); + if (MP.Modifier != HLSLParamModifierAttr::Keyword_in) { + auto *Mod = + HLSLParamModifierAttr::Create(AST, SourceRange(), MP.Modifier); + Parm->addAttr(Mod); + } + ParmDecls.push_back(Parm); + FnProtoLoc.setParam(i++, Parm); + } + Method->setParams({ParmDecls}); + } + + void addResourceHandleToParms(SmallVector &Parms) { + ASTContext &AST = S.getASTContext(); + FieldDecl *HandleField = DeclBuilder.getResourceHandleField(); + auto *This = CXXThisExpr::Create( + AST, SourceLocation(), Method->getFunctionObjectParameterType(), true); + Parms.push_back(MemberExpr::CreateImplicit(AST, This, false, HandleField, + HandleField->getType(), + VK_LValue, OK_Ordinary)); + } + +public: + ~BuiltinTypeMethodBuilder() { finalizeMethod(); } + + BuiltinTypeMethodBuilder & + callBuiltin(StringRef BuiltinName, ArrayRef CallParms, + bool AddResourceHandleAsFirstArg = true) { + if (!Method) + createMethodDecl(); + + ASTContext &AST = S.getASTContext(); + DeclRefExpr *Fn = lookupBuiltinFunction(S, BuiltinName); + Expr *Call = nullptr; + + if (AddResourceHandleAsFirstArg) { + SmallVector NewCallParms; + addResourceHandleToParms(NewCallParms); + for (auto *P : CallParms) + NewCallParms.push_back(P); + + Call = CallExpr::Create(AST, Fn, NewCallParms, AST.VoidPtrTy, VK_PRValue, + SourceLocation(), FPOptionsOverride()); + } else { + Call = CallExpr::Create(AST, Fn, CallParms, AST.VoidPtrTy, VK_PRValue, + SourceLocation(), FPOptionsOverride()); + } + StmtsList.push_back(Call); + return *this; + } + + BuiltinTypeMethodBuilder & + callBuiltinForwardArgs(StringRef BuiltinName, + bool AddResourceHandleAsFirstArg = true) { + // FIXME: Call the buildin with all of the method parameters + // plus optional resource handle as the first arg. + llvm_unreachable("not yet implemented"); + } + + BuiltinTypeDeclBuilder &finalizeMethod() { + if (DeclBuilder.Record->isCompleteDefinition()) + return DeclBuilder; + + if (!Method) + createMethodDecl(); + + if (!Method->hasBody()) { + ASTContext &AST = S.getASTContext(); + if (ReturnTy != AST.VoidTy && !StmtsList.empty()) { + if (Expr *LastExpr = dyn_cast(StmtsList.back())) { + StmtsList.pop_back(); + StmtsList.push_back( + ReturnStmt::Create(AST, SourceLocation(), LastExpr, nullptr)); + } + } + + Method->setBody(CompoundStmt::Create(AST, StmtsList, FPOptionsOverride(), + SourceLocation(), SourceLocation())); + Method->setLexicalDeclContext(DeclBuilder.Record); + Method->setAccess(AccessSpecifier::AS_public); + Method->addAttr(AlwaysInlineAttr::CreateImplicit( + AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline)); + DeclBuilder.Record->addDecl(Method); + } + return DeclBuilder; + } +}; + } // namespace TemplateParameterListBuilder @@ -375,6 +542,30 @@ BuiltinTypeDeclBuilder::addSimpleTemplateParams(Sema &S, return Builder.finalizeTemplateArgs(); } +BuiltinTypeDeclBuilder & +BuiltinTypeDeclBuilder::addIncrementCounterMethod(Sema &S) { + ASTContext &AST = S.getASTContext(); + Expr *One = + IntegerLiteral::Create(AST, llvm::APInt(AST.getTypeSize(AST.IntTy), 1), + AST.IntTy, SourceLocation()); + return BuiltinTypeMethodBuilder(S, *this, "IncrementCounter", + AST.UnsignedIntTy) + .callBuiltin("__builtin_hlsl_buffer_update_counter", {One}) + .finalizeMethod(); +} + +BuiltinTypeDeclBuilder & +BuiltinTypeDeclBuilder::addDecrementCounterMethod(Sema &S) { + ASTContext &AST = S.getASTContext(); + Expr *NegOne = + IntegerLiteral::Create(AST, llvm::APInt(AST.getTypeSize(AST.IntTy), -1), + AST.IntTy, SourceLocation()); + return BuiltinTypeMethodBuilder(S, *this, "DecrementCounter", + AST.UnsignedIntTy) + .callBuiltin("__builtin_hlsl_buffer_update_counter", {NegOne}) + .finalizeMethod(); +} + HLSLExternalSemaSource::~HLSLExternalSemaSource() {} void HLSLExternalSemaSource::InitializeSema(Sema &S) { @@ -528,8 +719,13 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { ResourceKind::TypedBuffer, /*IsROV=*/false, /*RawBuffer=*/true) .addArraySubscriptOperators() + .addIncrementCounterMethod(*SemaPtr) + .addDecrementCounterMethod(*SemaPtr) .completeDefinition(); }); + + // FIXME: Also add Increment/DecrementCounter to + // RasterizerOrderedStructuredBuffer when llvm/llvm-project/#113648 is merged. } void HLSLExternalSemaSource::onCompletion(CXXRecordDecl *Record, @@ -552,3 +748,23 @@ void HLSLExternalSemaSource::CompleteType(TagDecl *Tag) { return; It->second(Record); } + +static DeclRefExpr *lookupBuiltinFunction(Sema &S, StringRef Name) { + IdentifierInfo &II = + S.getASTContext().Idents.get(Name, tok::TokenKind::identifier); + DeclarationNameInfo NameInfo = + DeclarationNameInfo(DeclarationName(&II), SourceLocation()); + LookupResult R(S, NameInfo, Sema::LookupOrdinaryName); + // AllowBuiltinCreation is false but LookupDirect will create + // the builtin when searching the global scope anyways... + S.LookupName(R, S.getCurScope()); + // FIXME: If the builtin function was user-declared in global scope, + // this assert *will* fail. Should this call LookupBuiltin instead? + assert(R.isSingleResult() && + "Since this is a builtin it should always resolve!"); + auto *VD = cast(R.getFoundDecl()); + QualType Ty = VD->getType(); + return DeclRefExpr::Create(S.getASTContext(), NestedNameSpecifierLoc(), + SourceLocation(), VD, false, NameInfo, Ty, + VK_PRValue); +} diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index ff6616901016a..770bd4a81633e 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -986,6 +986,10 @@ Sema::VarArgKind Sema::isValidVarArgType(const QualType &Ty) { if (getLangOpts().MSVCCompat) return VAK_MSVCUndefined; + if (getLangOpts().HLSL && + Ty->getUnqualifiedDesugaredType()->isHLSLAttributedResourceType()) + return VAK_Valid; + // FIXME: In C++11, these cases are conditionally-supported, meaning we're // permitted to reject them. We should consider doing so. return VAK_Undefined; diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 1f6c5b8d4561b..1b7f0456a3e82 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -1860,6 +1860,31 @@ static bool CheckVectorSelect(Sema *S, CallExpr *TheCall) { return false; } +static bool CheckResourceHandle(Sema *S, CallExpr *TheCall, unsigned ArgIndex) { + assert(TheCall->getNumArgs() >= ArgIndex); + QualType ArgType = TheCall->getArg(ArgIndex)->getType(); + if (!ArgType.getTypePtr() + ->getUnqualifiedDesugaredType() + ->isHLSLAttributedResourceType()) { + S->Diag(TheCall->getArg(0)->getBeginLoc(), + diag::err_typecheck_expect_hlsl_resource) + << ArgType; + return true; + } + return false; +} + +static bool CheckInt(Sema *S, CallExpr *TheCall, unsigned ArgIndex) { + assert(TheCall->getNumArgs() >= ArgIndex); + QualType ArgType = TheCall->getArg(ArgIndex)->getType(); + if (!ArgType->isIntegerType()) { + S->Diag(TheCall->getArg(0)->getBeginLoc(), diag::err_typecheck_expect_int) + << ArgType; + return true; + } + return false; +} + // Note: returning true in this case results in CheckBuiltinFunctionCall // returning an ExprError bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { @@ -2100,6 +2125,22 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { return true; break; } + case Builtin::BI__builtin_hlsl_buffer_update_counter: { + if (SemaRef.checkArgCount(TheCall, 2) || + CheckResourceHandle(&SemaRef, TheCall, 0) || + CheckInt(&SemaRef, TheCall, 1)) + return true; + Expr *OffsetExpr = TheCall->getArg(1); + std::optional Offset = + OffsetExpr->getIntegerConstantExpr(SemaRef.getASTContext()); + if (!Offset.has_value() || abs(Offset->getExtValue()) != 1) { + SemaRef.Diag(TheCall->getArg(1)->getBeginLoc(), + diag::err_hlsl_expect_arg_const_int_one_or_neg_one) + << 1; + return true; + } + break; + } } return false; } diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl new file mode 100644 index 0000000000000..c8ff5d3cd905f --- /dev/null +++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl @@ -0,0 +1,25 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL +// RUN-DISABLED: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV + +// NOTE: SPIRV codegen for resource methods is not yet implemented + +RWStructuredBuffer RWSB1 : register(u0); +RWStructuredBuffer RWSB2 : register(u1); + +// CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0), float } + +export void TestIncrementCounter() { + RWSB1.IncrementCounter(); +} + +// CHECK: define void @_Z20TestIncrementCounterv() +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 1) + +export void TestDecrementCounter() { + RWSB2.DecrementCounter(); +} + +// CHECK: define void @_Z20TestDecrementCounterv() +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 -1) + +// CHECK: declare i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i32) \ No newline at end of file diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl new file mode 100644 index 0000000000000..fe9e9cfdcb873 --- /dev/null +++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl @@ -0,0 +1,29 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL +// RUN-DISABLED: %clang_cc1 -triple spirv-vulkan-pixel -x hlsl -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV + +// NOTE: SPIRV codegen for resource methods is not yet implemented + +RWStructuredBuffer RWSB1, RWSB2; +RasterizerOrderedStructuredBuffer ROSB1, ROSB2; + +// CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0), float } + +export void TestIncrementCounter() { + RWSB1.IncrementCounter(); + ROSB1.IncrementCounter(); +} + +// CHECK: define void @_Z20TestIncrementCounterv() +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 1) +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 1) %{{[0-9]+}}, i32 1) + +export void TestDecrementCounter() { + RWSB2.DecrementCounter(); + ROSB2.DecrementCounter(); +} + +// CHECK: define void @_Z20TestDecrementCounterv() +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 -1) +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 1) %{{[0-9]+}}, i32 -1) + +// CHECK: declare i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i32) \ No newline at end of file diff --git a/clang/test/SemaHLSL/BuiltIns/buffer_update_counter-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/buffer_update_counter-errors.hlsl new file mode 100644 index 0000000000000..11b8cebc1aeb4 --- /dev/null +++ b/clang/test/SemaHLSL/BuiltIns/buffer_update_counter-errors.hlsl @@ -0,0 +1,22 @@ +// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -emit-llvm-only -disable-llvm-passes -verify + +using handle_t = __hlsl_resource_t [[hlsl::resource_class(UAV)]] [[hlsl::contained_type(int)]]; + +void test_args(int x, bool b) { + handle_t res; + + // expected-error@+1 {{too few arguments to function call, expected 2, have 1}} + __builtin_hlsl_buffer_update_counter(x); + + // expected-error@+1 {{too many arguments to function call, expected 2, have 3}} + __builtin_hlsl_buffer_update_counter(x, x, x); + + // expected-error@+1 {{used type 'int' where __hlsl_resource_t is required}} + __builtin_hlsl_buffer_update_counter(x, x); + + // expected-error@+1 {{argument 1 must be constant integer 1 or -1}} + __builtin_hlsl_buffer_update_counter(res, x); + + // expected-error@+1 {{argument 1 must be constant integer 1 or -1}} + __builtin_hlsl_buffer_update_counter(res, 10); +} diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td index e30d37f69f781..75e70657084bd 100644 --- a/llvm/include/llvm/IR/IntrinsicsDirectX.td +++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td @@ -35,6 +35,9 @@ def int_dx_typedBufferLoad_checkbit def int_dx_typedBufferStore : DefaultAttrsIntrinsic<[], [llvm_any_ty, llvm_i32_ty, llvm_anyvector_ty]>; +def int_dx_bufferUpdateCounter + : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_i32_ty], [IntrWriteMem]>; + // Cast between target extension handle types and dxil-style opaque handles def int_dx_cast_handle : Intrinsic<[llvm_any_ty], [llvm_any_ty]>; diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td index 6df2eb156a077..8978877feb69d 100644 --- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td +++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td @@ -97,4 +97,7 @@ let TargetPrefix = "spv" in { [llvm_any_ty], [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i1_ty], [IntrNoMem]>; + + def int_spv_bufferUpdateCounter + : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_i32_ty], [IntrWriteMem]>; } From a94c90110d042c5170e8b8d469765688be657d2c Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Thu, 31 Oct 2024 20:49:45 -0700 Subject: [PATCH 02/15] Code review feedback - add test & asserts, add Sema to main decl builder, fix generated CallExpr type --- clang/lib/Sema/HLSLExternalSemaSource.cpp | 147 +++++++++--------- .../test/AST/HLSL/RWStructuredBuffer-AST.hlsl | 26 ++++ 2 files changed, 99 insertions(+), 74 deletions(-) diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 24c3954b134c5..10814f10cfeb6 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -12,7 +12,9 @@ #include "clang/Sema/HLSLExternalSemaSource.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/Expr.h" #include "clang/AST/Type.h" #include "clang/Basic/AttrKinds.h" #include "clang/Basic/HLSLRuntime.h" @@ -29,26 +31,27 @@ using namespace clang; using namespace llvm::hlsl; -static DeclRefExpr *lookupBuiltinFunction(Sema &S, StringRef Name); +static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name); namespace { struct TemplateParameterListBuilder; struct BuiltinTypeDeclBuilder { + Sema &S; CXXRecordDecl *Record = nullptr; ClassTemplateDecl *Template = nullptr; ClassTemplateDecl *PrevTemplate = nullptr; NamespaceDecl *HLSLNamespace = nullptr; llvm::StringMap Fields; - BuiltinTypeDeclBuilder(CXXRecordDecl *R) : Record(R) { + BuiltinTypeDeclBuilder(Sema &S, CXXRecordDecl *R) : S(S), Record(R) { Record->startDefinition(); Template = Record->getDescribedClassTemplate(); } BuiltinTypeDeclBuilder(Sema &S, NamespaceDecl *Namespace, StringRef Name) - : HLSLNamespace(Namespace) { + : S(S), HLSLNamespace(Namespace) { ASTContext &AST = S.getASTContext(); IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier); @@ -114,8 +117,7 @@ struct BuiltinTypeDeclBuilder { } BuiltinTypeDeclBuilder & - addHandleMember(Sema &S, ResourceClass RC, ResourceKind RK, bool IsROV, - bool RawBuffer, + addHandleMember(ResourceClass RC, ResourceKind RK, bool IsROV, bool RawBuffer, AccessSpecifier Access = AccessSpecifier::AS_private) { if (Record->isCompleteDefinition()) return *this; @@ -152,8 +154,7 @@ struct BuiltinTypeDeclBuilder { AST.UnsignedCharTy, SourceLocation()); } - BuiltinTypeDeclBuilder &addDefaultHandleConstructor(Sema &S, - ResourceClass RC) { + BuiltinTypeDeclBuilder &addDefaultHandleConstructor(ResourceClass RC) { if (Record->isCompleteDefinition()) return *this; ASTContext &AST = Record->getASTContext(); @@ -260,22 +261,22 @@ struct BuiltinTypeDeclBuilder { FieldDecl *getResourceHandleField() { FieldDecl *FD = Fields["h"]; - if (FD && FD->getType()->isHLSLAttributedResourceType()) - return FD; - return nullptr; + assert(FD && FD->getType()->isHLSLAttributedResourceType() && + "record does not have resource handle"); + return FD; } QualType getFirstTemplateTypeParam() { - if (Template) { - if (const auto *TTD = dyn_cast( - Template->getTemplateParameters()->getParam(0))) { - return QualType(TTD->getTypeForDecl(), 0); - } + assert(Template && "record it not a template"); + if (const auto *TTD = dyn_cast( + Template->getTemplateParameters()->getParam(0))) { + return QualType(TTD->getTypeForDecl(), 0); } return QualType(); } BuiltinTypeDeclBuilder &startDefinition() { + // we might already have complete definition from a precompiled header if (Record->isCompleteDefinition()) return *this; Record->startDefinition(); @@ -292,22 +293,19 @@ struct BuiltinTypeDeclBuilder { return *this; } - TemplateParameterListBuilder addTemplateArgumentList(Sema &S); - BuiltinTypeDeclBuilder &addSimpleTemplateParams(Sema &S, - ArrayRef Names); + TemplateParameterListBuilder addTemplateArgumentList(); + BuiltinTypeDeclBuilder &addSimpleTemplateParams(ArrayRef Names); // Builtin types methods - BuiltinTypeDeclBuilder &addIncrementCounterMethod(Sema &S); - BuiltinTypeDeclBuilder &addDecrementCounterMethod(Sema &S); + BuiltinTypeDeclBuilder &addIncrementCounterMethod(); + BuiltinTypeDeclBuilder &addDecrementCounterMethod(); }; struct TemplateParameterListBuilder { BuiltinTypeDeclBuilder &Builder; - Sema &S; llvm::SmallVector Params; - TemplateParameterListBuilder(Sema &S, BuiltinTypeDeclBuilder &RB) - : Builder(RB), S(S) {} + TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) : Builder(RB) {} ~TemplateParameterListBuilder() { finalizeTemplateArgs(); } @@ -315,17 +313,18 @@ struct TemplateParameterListBuilder { addTypeParameter(StringRef Name, QualType DefaultValue = QualType()) { if (Builder.Record->isCompleteDefinition()) return *this; + ASTContext &AST = Builder.S.getASTContext(); unsigned Position = static_cast(Params.size()); auto *Decl = TemplateTypeParmDecl::Create( - S.Context, Builder.Record->getDeclContext(), SourceLocation(), + AST, Builder.Record->getDeclContext(), SourceLocation(), SourceLocation(), /* TemplateDepth */ 0, Position, - &S.Context.Idents.get(Name, tok::TokenKind::identifier), + &AST.Idents.get(Name, tok::TokenKind::identifier), /* Typename */ false, /* ParameterPack */ false); if (!DefaultValue.isNull()) Decl->setDefaultArgument( - S.Context, S.getTrivialTemplateArgumentLoc(DefaultValue, QualType(), - SourceLocation())); + AST, Builder.S.getTrivialTemplateArgumentLoc(DefaultValue, QualType(), + SourceLocation())); Params.emplace_back(Decl); return *this; @@ -334,11 +333,12 @@ struct TemplateParameterListBuilder { BuiltinTypeDeclBuilder &finalizeTemplateArgs() { if (Params.empty()) return Builder; - auto *ParamList = TemplateParameterList::Create(S.Context, SourceLocation(), - SourceLocation(), Params, - SourceLocation(), nullptr); + ASTContext &AST = Builder.S.Context; + auto *ParamList = + TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(), + Params, SourceLocation(), nullptr); Builder.Template = ClassTemplateDecl::Create( - S.Context, Builder.Record->getDeclContext(), SourceLocation(), + AST, Builder.Record->getDeclContext(), SourceLocation(), DeclarationName(Builder.Record->getIdentifier()), ParamList, Builder.Record); Builder.Record->setDescribedClassTemplate(Builder.Template); @@ -351,7 +351,7 @@ struct TemplateParameterListBuilder { Params.clear(); QualType T = Builder.Template->getInjectedClassNameSpecialization(); - T = S.Context.getInjectedClassNameType(Builder.Record, T); + T = AST.getInjectedClassNameType(Builder.Record, T); return Builder; } @@ -382,7 +382,6 @@ struct BuiltinTypeMethodBuilder { }; BuiltinTypeDeclBuilder &DeclBuilder; - Sema &S; DeclarationNameInfo NameInfo; QualType ReturnTy; CXXMethodDecl *Method; @@ -392,7 +391,7 @@ struct BuiltinTypeMethodBuilder { public: BuiltinTypeMethodBuilder(Sema &S, BuiltinTypeDeclBuilder &DB, StringRef Name, QualType ReturnTy) - : DeclBuilder(DB), S(S), ReturnTy(ReturnTy), Method(nullptr) { + : DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr) { const IdentifierInfo &II = S.getASTContext().Idents.get(Name, tok::TokenKind::identifier); NameInfo = DeclarationNameInfo(DeclarationName(&II), SourceLocation()); @@ -403,8 +402,8 @@ struct BuiltinTypeMethodBuilder { HLSLParamModifierAttr::Keyword_in) { assert(Method == nullptr && "Cannot add param, method already created"); - const IdentifierInfo &II = - S.getASTContext().Idents.get(Name, tok::TokenKind::identifier); + const IdentifierInfo &II = DeclBuilder.S.getASTContext().Idents.get( + Name, tok::TokenKind::identifier); Params.emplace_back(II, Ty, Modifier); return *this; } @@ -414,7 +413,7 @@ struct BuiltinTypeMethodBuilder { assert(Method == nullptr && "Method already created"); // create method type - ASTContext &AST = S.getASTContext(); + ASTContext &AST = DeclBuilder.S.getASTContext(); SmallVector ParamTypes; for (auto &MP : Params) ParamTypes.emplace_back(MP.Ty); @@ -451,7 +450,7 @@ struct BuiltinTypeMethodBuilder { } void addResourceHandleToParms(SmallVector &Parms) { - ASTContext &AST = S.getASTContext(); + ASTContext &AST = DeclBuilder.S.getASTContext(); FieldDecl *HandleField = DeclBuilder.getResourceHandleField(); auto *This = CXXThisExpr::Create( AST, SourceLocation(), Method->getFunctionObjectParameterType(), true); @@ -469,22 +468,22 @@ struct BuiltinTypeMethodBuilder { if (!Method) createMethodDecl(); - ASTContext &AST = S.getASTContext(); - DeclRefExpr *Fn = lookupBuiltinFunction(S, BuiltinName); - Expr *Call = nullptr; + ASTContext &AST = DeclBuilder.S.getASTContext(); + FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.S, BuiltinName); + DeclRefExpr *DRE = DeclRefExpr::Create( + AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false, + FD->getNameInfo(), FD->getType(), VK_PRValue); + SmallVector NewCallParms; if (AddResourceHandleAsFirstArg) { - SmallVector NewCallParms; addResourceHandleToParms(NewCallParms); for (auto *P : CallParms) NewCallParms.push_back(P); - - Call = CallExpr::Create(AST, Fn, NewCallParms, AST.VoidPtrTy, VK_PRValue, - SourceLocation(), FPOptionsOverride()); - } else { - Call = CallExpr::Create(AST, Fn, CallParms, AST.VoidPtrTy, VK_PRValue, - SourceLocation(), FPOptionsOverride()); } + + Expr *Call = CallExpr::Create( + AST, DRE, AddResourceHandleAsFirstArg ? NewCallParms : CallParms, + FD->getReturnType(), VK_PRValue, SourceLocation(), FPOptionsOverride()); StmtsList.push_back(Call); return *this; } @@ -505,9 +504,16 @@ struct BuiltinTypeMethodBuilder { createMethodDecl(); if (!Method->hasBody()) { - ASTContext &AST = S.getASTContext(); + ASTContext &AST = DeclBuilder.S.getASTContext(); if (ReturnTy != AST.VoidTy && !StmtsList.empty()) { if (Expr *LastExpr = dyn_cast(StmtsList.back())) { + assert(AST.hasSameUnqualifiedType( + isa(LastExpr) + ? cast(LastExpr)->getCallReturnType(AST) + : LastExpr->getType(), + ReturnTy) && + "Return type of the last statement must match the return type " + "of the method"); StmtsList.pop_back(); StmtsList.push_back( ReturnStmt::Create(AST, SourceLocation(), LastExpr, nullptr)); @@ -528,22 +534,19 @@ struct BuiltinTypeMethodBuilder { } // namespace -TemplateParameterListBuilder -BuiltinTypeDeclBuilder::addTemplateArgumentList(Sema &S) { - return TemplateParameterListBuilder(S, *this); +TemplateParameterListBuilder BuiltinTypeDeclBuilder::addTemplateArgumentList() { + return TemplateParameterListBuilder(*this); } BuiltinTypeDeclBuilder & -BuiltinTypeDeclBuilder::addSimpleTemplateParams(Sema &S, - ArrayRef Names) { - TemplateParameterListBuilder Builder = this->addTemplateArgumentList(S); +BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef Names) { + TemplateParameterListBuilder Builder = this->addTemplateArgumentList(); for (StringRef Name : Names) Builder.addTypeParameter(Name); return Builder.finalizeTemplateArgs(); } -BuiltinTypeDeclBuilder & -BuiltinTypeDeclBuilder::addIncrementCounterMethod(Sema &S) { +BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() { ASTContext &AST = S.getASTContext(); Expr *One = IntegerLiteral::Create(AST, llvm::APInt(AST.getTypeSize(AST.IntTy), 1), @@ -554,8 +557,7 @@ BuiltinTypeDeclBuilder::addIncrementCounterMethod(Sema &S) { .finalizeMethod(); } -BuiltinTypeDeclBuilder & -BuiltinTypeDeclBuilder::addDecrementCounterMethod(Sema &S) { +BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() { ASTContext &AST = S.getASTContext(); Expr *NegOne = IntegerLiteral::Create(AST, llvm::APInt(AST.getTypeSize(AST.IntTy), -1), @@ -669,15 +671,15 @@ void HLSLExternalSemaSource::defineTrivialHLSLTypes() { static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S, ResourceClass RC, ResourceKind RK, bool IsROV, bool RawBuffer) { - return BuiltinTypeDeclBuilder(Decl) - .addHandleMember(S, RC, RK, IsROV, RawBuffer) - .addDefaultHandleConstructor(S, RC); + return BuiltinTypeDeclBuilder(S, Decl) + .addHandleMember(RC, RK, IsROV, RawBuffer) + .addDefaultHandleConstructor(RC); } void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { CXXRecordDecl *Decl; Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer") - .addSimpleTemplateParams(*SemaPtr, {"element_type"}) + .addSimpleTemplateParams({"element_type"}) .Record; onCompletion(Decl, [this](CXXRecordDecl *Decl) { @@ -690,7 +692,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer") - .addSimpleTemplateParams(*SemaPtr, {"element_type"}) + .addSimpleTemplateParams({"element_type"}) .Record; onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, @@ -701,7 +703,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { }); Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "StructuredBuffer") - .addSimpleTemplateParams(*SemaPtr, {"element_type"}) + .addSimpleTemplateParams({"element_type"}) .Record; onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, @@ -712,15 +714,15 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { }); Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWStructuredBuffer") - .addSimpleTemplateParams(*SemaPtr, {"element_type"}) + .addSimpleTemplateParams({"element_type"}) .Record; onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::TypedBuffer, /*IsROV=*/false, /*RawBuffer=*/true) .addArraySubscriptOperators() - .addIncrementCounterMethod(*SemaPtr) - .addDecrementCounterMethod(*SemaPtr) + .addIncrementCounterMethod() + .addDecrementCounterMethod() .completeDefinition(); }); @@ -749,7 +751,7 @@ void HLSLExternalSemaSource::CompleteType(TagDecl *Tag) { It->second(Record); } -static DeclRefExpr *lookupBuiltinFunction(Sema &S, StringRef Name) { +static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name) { IdentifierInfo &II = S.getASTContext().Idents.get(Name, tok::TokenKind::identifier); DeclarationNameInfo NameInfo = @@ -762,9 +764,6 @@ static DeclRefExpr *lookupBuiltinFunction(Sema &S, StringRef Name) { // this assert *will* fail. Should this call LookupBuiltin instead? assert(R.isSingleResult() && "Since this is a builtin it should always resolve!"); - auto *VD = cast(R.getFoundDecl()); - QualType Ty = VD->getType(); - return DeclRefExpr::Create(S.getASTContext(), NestedNameSpecifierLoc(), - SourceLocation(), VD, false, NameInfo, Ty, - VK_PRValue); + assert(isa(R.getFoundDecl())); + return cast(R.getFoundDecl()); } diff --git a/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl b/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl index f95d74b30acde..fd283b1af0226 100644 --- a/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl +++ b/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl @@ -52,6 +52,32 @@ RWStructuredBuffer Buffer; // CHECK-NEXT: CXXThisExpr 0x{{[0-9A-Fa-f]+}} <> 'RWStructuredBuffer' lvalue implicit this // CHECK-NEXT: AlwaysInlineAttr 0x{{[0-9A-Fa-f]+}} <> Implicit always_inline +// CHECK-NEXT: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> IncrementCounter 'unsigned int ()' +// CHECK-NEXT: CompoundStmt 0x{{[0-9A-Fa-f]+}} <> +// CHECK-NEXT: ReturnStmt 0x{{[0-9A-Fa-f]+}} <> +// CHECK-NEXT: CallExpr 0x{{[0-9A-Fa-f]+}} <> 'unsigned int' +// CHECK-NEXT: DeclRefExpr 0x{{[0-9A-Fa-f]+}} <> 'unsigned int (...) noexcept' Function 0x{{[0-9A-Fa-f]+}} '__builtin_hlsl_buffer_update_counter' 'unsigned int (...) noexcept' +// CHECK-NEXT: MemberExpr 0x{{[0-9A-Fa-f]+}} <> '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] +// CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] +// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]' lvalue .h +// CHECK-NEXT: CXXThisExpr 0x{{[0-9A-Fa-f]+}} <> 'RWStructuredBuffer' lvalue implicit this +// CHECK-NEXT: IntegerLiteral 0x{{[0-9A-Fa-f]+}} <> 'int' 1 +// CHECK-NEXT: AlwaysInlineAttr 0x{{[0-9A-Fa-f]+}} <> Implicit always_inline + +// CHECK-NEXT: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <> DecrementCounter 'unsigned int ()' +// CHECK-NEXT: CompoundStmt 0x{{[0-9A-Fa-f]+}} <> +// CHECK-NEXT: ReturnStmt 0x{{[0-9A-Fa-f]+}} <> +// CHECK-NEXT: CallExpr 0x{{[0-9A-Fa-f]+}} <> 'unsigned int' +// CHECK-NEXT: DeclRefExpr 0x{{[0-9A-Fa-f]+}} <> 'unsigned int (...) noexcept' Function 0x{{[0-9A-Fa-f]+}} '__builtin_hlsl_buffer_update_counter' 'unsigned int (...) noexcept' +// CHECK-NEXT: MemberExpr 0x{{[0-9A-Fa-f]+}} <> '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] +// CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] +// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]' lvalue .h +// CHECK-NEXT: CXXThisExpr 0x{{[0-9A-Fa-f]+}} <> 'RWStructuredBuffer' lvalue implicit this +// CHECK-NEXT: IntegerLiteral 0x{{[0-9A-Fa-f]+}} <> 'int' -1 +// CHECK-NEXT: AlwaysInlineAttr 0x{{[0-9A-Fa-f]+}} <> Implicit always_inline + // CHECK: ClassTemplateSpecializationDecl 0x{{[0-9A-Fa-f]+}} <> class RWStructuredBuffer definition // CHECK: TemplateArgument type 'int' From 0f4c61f3ab51c8e614e921e43827f0c28be74685 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Mon, 4 Nov 2024 16:13:03 -0800 Subject: [PATCH 03/15] Code review feedback - assert of completed definition or no method body Do not complete definition if we already got one from precompiled headers. --- clang/lib/Sema/HLSLExternalSemaSource.cpp | 41 +++++++++++------------ 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 10814f10cfeb6..52f1ec3bd39e3 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -58,6 +58,7 @@ struct BuiltinTypeDeclBuilder { LookupResult Result(S, &II, SourceLocation(), Sema::LookupTagName); CXXRecordDecl *PrevDecl = nullptr; if (S.LookupQualifiedName(Result, HLSLNamespace)) { + // Declaration already exists (from precompiled headers) NamedDecl *Found = Result.getFoundDecl(); if (auto *TD = dyn_cast(Found)) { PrevDecl = TD->getTemplatedDecl(); @@ -69,6 +70,7 @@ struct BuiltinTypeDeclBuilder { if (PrevDecl && PrevDecl->isCompleteDefinition()) { Record = PrevDecl; + Template = PrevTemplate; return; } @@ -92,8 +94,7 @@ struct BuiltinTypeDeclBuilder { BuiltinTypeDeclBuilder & addMemberVariable(StringRef Name, QualType Type, llvm::ArrayRef Attrs, AccessSpecifier Access = AccessSpecifier::AS_private) { - if (Record->isCompleteDefinition()) - return *this; + assert(!Record->isCompleteDefinition() && "record is already complete"); assert(Record->isBeingDefined() && "Definition must be started before adding members!"); ASTContext &AST = Record->getASTContext(); @@ -119,8 +120,7 @@ struct BuiltinTypeDeclBuilder { BuiltinTypeDeclBuilder & addHandleMember(ResourceClass RC, ResourceKind RK, bool IsROV, bool RawBuffer, AccessSpecifier Access = AccessSpecifier::AS_private) { - if (Record->isCompleteDefinition()) - return *this; + assert(!Record->isCompleteDefinition() && "record is already complete"); ASTContext &Ctx = S.getASTContext(); TypeSourceInfo *ElementTypeInfo = nullptr; @@ -155,8 +155,7 @@ struct BuiltinTypeDeclBuilder { } BuiltinTypeDeclBuilder &addDefaultHandleConstructor(ResourceClass RC) { - if (Record->isCompleteDefinition()) - return *this; + assert(!Record->isCompleteDefinition() && "record is already complete"); ASTContext &AST = Record->getASTContext(); QualType ConstructorType = @@ -179,16 +178,13 @@ struct BuiltinTypeDeclBuilder { } BuiltinTypeDeclBuilder &addArraySubscriptOperators() { - if (Record->isCompleteDefinition()) - return *this; addArraySubscriptOperator(true); addArraySubscriptOperator(false); return *this; } BuiltinTypeDeclBuilder &addArraySubscriptOperator(bool IsConst) { - if (Record->isCompleteDefinition()) - return *this; + assert(!Record->isCompleteDefinition() && "record is already complete"); ASTContext &AST = Record->getASTContext(); QualType ElemTy = AST.Char8Ty; @@ -277,15 +273,13 @@ struct BuiltinTypeDeclBuilder { BuiltinTypeDeclBuilder &startDefinition() { // we might already have complete definition from a precompiled header - if (Record->isCompleteDefinition()) - return *this; + assert(!Record->isCompleteDefinition() && "record is already complete"); Record->startDefinition(); return *this; } BuiltinTypeDeclBuilder &completeDefinition() { - if (Record->isCompleteDefinition()) - return *this; + assert(!Record->isCompleteDefinition() && "record is already complete"); assert(Record->isBeingDefined() && "Definition must be started before completing it."); @@ -311,8 +305,7 @@ struct TemplateParameterListBuilder { TemplateParameterListBuilder & addTypeParameter(StringRef Name, QualType DefaultValue = QualType()) { - if (Builder.Record->isCompleteDefinition()) - return *this; + assert(!Builder.Record->isCompleteDefinition() && "record is already complete"); ASTContext &AST = Builder.S.getASTContext(); unsigned Position = static_cast(Params.size()); auto *Decl = TemplateTypeParmDecl::Create( @@ -497,11 +490,8 @@ struct BuiltinTypeMethodBuilder { } BuiltinTypeDeclBuilder &finalizeMethod() { - if (DeclBuilder.Record->isCompleteDefinition()) - return DeclBuilder; - - if (!Method) - createMethodDecl(); + assert(!DeclBuilder.Record->isCompleteDefinition() && "record is already complete"); + assert(Method != nullptr && "method decl not created; are you missing a call to build the body?"); if (!Method->hasBody()) { ASTContext &AST = DeclBuilder.S.getASTContext(); @@ -540,6 +530,12 @@ TemplateParameterListBuilder BuiltinTypeDeclBuilder::addTemplateArgumentList() { BuiltinTypeDeclBuilder & BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef Names) { + if (Record->isCompleteDefinition()) { + assert(Template && "existing record it not a template"); + assert(Template->getTemplateParameters()->size() == Names.size() && "template param count mismatch"); + return *this; + } + TemplateParameterListBuilder Builder = this->addTemplateArgumentList(); for (StringRef Name : Names) Builder.addTypeParameter(Name); @@ -732,7 +728,8 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { void HLSLExternalSemaSource::onCompletion(CXXRecordDecl *Record, CompletionFunction Fn) { - Completions.insert(std::make_pair(Record->getCanonicalDecl(), Fn)); + if (!Record->isCompleteDefinition()) + Completions.insert(std::make_pair(Record->getCanonicalDecl(), Fn)); } void HLSLExternalSemaSource::CompleteType(TagDecl *Tag) { From f180391871e898851a221b7e120c137464b45b85 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Mon, 4 Nov 2024 16:17:23 -0800 Subject: [PATCH 04/15] clang-format --- clang/lib/Sema/HLSLExternalSemaSource.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 52f1ec3bd39e3..b64efb481146d 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -305,7 +305,8 @@ struct TemplateParameterListBuilder { TemplateParameterListBuilder & addTypeParameter(StringRef Name, QualType DefaultValue = QualType()) { - assert(!Builder.Record->isCompleteDefinition() && "record is already complete"); + assert(!Builder.Record->isCompleteDefinition() && + "record is already complete"); ASTContext &AST = Builder.S.getASTContext(); unsigned Position = static_cast(Params.size()); auto *Decl = TemplateTypeParmDecl::Create( @@ -490,8 +491,11 @@ struct BuiltinTypeMethodBuilder { } BuiltinTypeDeclBuilder &finalizeMethod() { - assert(!DeclBuilder.Record->isCompleteDefinition() && "record is already complete"); - assert(Method != nullptr && "method decl not created; are you missing a call to build the body?"); + assert(!DeclBuilder.Record->isCompleteDefinition() && + "record is already complete"); + assert( + Method != nullptr && + "method decl not created; are you missing a call to build the body?"); if (!Method->hasBody()) { ASTContext &AST = DeclBuilder.S.getASTContext(); @@ -532,7 +536,8 @@ BuiltinTypeDeclBuilder & BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef Names) { if (Record->isCompleteDefinition()) { assert(Template && "existing record it not a template"); - assert(Template->getTemplateParameters()->size() == Names.size() && "template param count mismatch"); + assert(Template->getTemplateParameters()->size() == Names.size() && + "template param count mismatch"); return *this; } From 942af76a3d008d23e78c38d1a6bff82a865a0d90 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Tue, 5 Nov 2024 11:59:44 -0800 Subject: [PATCH 05/15] Code review feedback - remove unused method, add comment --- clang/lib/Sema/HLSLExternalSemaSource.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index b64efb481146d..45302f86d4d72 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -459,6 +459,8 @@ struct BuiltinTypeMethodBuilder { BuiltinTypeMethodBuilder & callBuiltin(StringRef BuiltinName, ArrayRef CallParms, bool AddResourceHandleAsFirstArg = true) { + + // The first statement added to a method creates the declaration. if (!Method) createMethodDecl(); @@ -482,14 +484,6 @@ struct BuiltinTypeMethodBuilder { return *this; } - BuiltinTypeMethodBuilder & - callBuiltinForwardArgs(StringRef BuiltinName, - bool AddResourceHandleAsFirstArg = true) { - // FIXME: Call the buildin with all of the method parameters - // plus optional resource handle as the first arg. - llvm_unreachable("not yet implemented"); - } - BuiltinTypeDeclBuilder &finalizeMethod() { assert(!DeclBuilder.Record->isCompleteDefinition() && "record is already complete"); From ed6d0f955a80c7f620e7d96ff76d206a3a430907 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Wed, 6 Nov 2024 16:13:01 -0800 Subject: [PATCH 06/15] Add Increment/DecrementCounter to RasterizerOrderedStructuredBuffer; update tests --- HLSLExternalSemaSource.cpp | 771 ++++++++++++++++++ clang/lib/Sema/HLSLExternalSemaSource.cpp | 14 +- .../StructuredBuffers-methods-lib.hlsl | 2 +- .../StructuredBuffers-methods-ps.hlsl | 17 +- 4 files changed, 788 insertions(+), 16 deletions(-) create mode 100644 HLSLExternalSemaSource.cpp diff --git a/HLSLExternalSemaSource.cpp b/HLSLExternalSemaSource.cpp new file mode 100644 index 0000000000000..b64efb481146d --- /dev/null +++ b/HLSLExternalSemaSource.cpp @@ -0,0 +1,771 @@ +//===--- HLSLExternalSemaSource.cpp - HLSL Sema Source --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// +//===----------------------------------------------------------------------===// + +#include "clang/Sema/HLSLExternalSemaSource.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/Basic/AttrKinds.h" +#include "clang/Basic/HLSLRuntime.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Sema/Lookup.h" +#include "clang/Sema/Sema.h" +#include "clang/Sema/SemaHLSL.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Frontend/HLSL/HLSLResource.h" +#include "llvm/Support/ErrorHandling.h" + +#include + +using namespace clang; +using namespace llvm::hlsl; + +static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name); + +namespace { + +struct TemplateParameterListBuilder; + +struct BuiltinTypeDeclBuilder { + Sema &S; + CXXRecordDecl *Record = nullptr; + ClassTemplateDecl *Template = nullptr; + ClassTemplateDecl *PrevTemplate = nullptr; + NamespaceDecl *HLSLNamespace = nullptr; + llvm::StringMap Fields; + + BuiltinTypeDeclBuilder(Sema &S, CXXRecordDecl *R) : S(S), Record(R) { + Record->startDefinition(); + Template = Record->getDescribedClassTemplate(); + } + + BuiltinTypeDeclBuilder(Sema &S, NamespaceDecl *Namespace, StringRef Name) + : S(S), HLSLNamespace(Namespace) { + ASTContext &AST = S.getASTContext(); + IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier); + + LookupResult Result(S, &II, SourceLocation(), Sema::LookupTagName); + CXXRecordDecl *PrevDecl = nullptr; + if (S.LookupQualifiedName(Result, HLSLNamespace)) { + // Declaration already exists (from precompiled headers) + NamedDecl *Found = Result.getFoundDecl(); + if (auto *TD = dyn_cast(Found)) { + PrevDecl = TD->getTemplatedDecl(); + PrevTemplate = TD; + } else + PrevDecl = dyn_cast(Found); + assert(PrevDecl && "Unexpected lookup result type."); + } + + if (PrevDecl && PrevDecl->isCompleteDefinition()) { + Record = PrevDecl; + Template = PrevTemplate; + return; + } + + Record = CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, HLSLNamespace, + SourceLocation(), SourceLocation(), &II, + PrevDecl, true); + Record->setImplicit(true); + Record->setLexicalDeclContext(HLSLNamespace); + Record->setHasExternalLexicalStorage(); + + // Don't let anyone derive from built-in types. + Record->addAttr(FinalAttr::CreateImplicit(AST, SourceRange(), + FinalAttr::Keyword_final)); + } + + ~BuiltinTypeDeclBuilder() { + if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace) + HLSLNamespace->addDecl(Record); + } + + BuiltinTypeDeclBuilder & + addMemberVariable(StringRef Name, QualType Type, llvm::ArrayRef Attrs, + AccessSpecifier Access = AccessSpecifier::AS_private) { + assert(!Record->isCompleteDefinition() && "record is already complete"); + assert(Record->isBeingDefined() && + "Definition must be started before adding members!"); + ASTContext &AST = Record->getASTContext(); + + IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier); + TypeSourceInfo *MemTySource = + AST.getTrivialTypeSourceInfo(Type, SourceLocation()); + auto *Field = FieldDecl::Create( + AST, Record, SourceLocation(), SourceLocation(), &II, Type, MemTySource, + nullptr, false, InClassInitStyle::ICIS_NoInit); + Field->setAccess(Access); + Field->setImplicit(true); + for (Attr *A : Attrs) { + if (A) + Field->addAttr(A); + } + + Record->addDecl(Field); + Fields[Name] = Field; + return *this; + } + + BuiltinTypeDeclBuilder & + addHandleMember(ResourceClass RC, ResourceKind RK, bool IsROV, bool RawBuffer, + AccessSpecifier Access = AccessSpecifier::AS_private) { + assert(!Record->isCompleteDefinition() && "record is already complete"); + + ASTContext &Ctx = S.getASTContext(); + TypeSourceInfo *ElementTypeInfo = nullptr; + + QualType ElemTy = Ctx.Char8Ty; + if (Template) + ElemTy = getFirstTemplateTypeParam(); + ElementTypeInfo = Ctx.getTrivialTypeSourceInfo(ElemTy, SourceLocation()); + + // add handle member with resource type attributes + QualType AttributedResTy = QualType(); + SmallVector Attrs = { + HLSLResourceClassAttr::CreateImplicit(Ctx, RC), + IsROV ? HLSLROVAttr::CreateImplicit(Ctx) : nullptr, + RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx) : nullptr, + ElementTypeInfo + ? HLSLContainedTypeAttr::CreateImplicit(Ctx, ElementTypeInfo) + : nullptr}; + Attr *ResourceAttr = HLSLResourceAttr::CreateImplicit(Ctx, RK); + if (CreateHLSLAttributedResourceType(S, Ctx.HLSLResourceTy, Attrs, + AttributedResTy)) + addMemberVariable("h", AttributedResTy, {ResourceAttr}, Access); + return *this; + } + + static Expr *emitResourceClassExpr(ASTContext &AST, ResourceClass RC) { + return IntegerLiteral::Create( + AST, + llvm::APInt(AST.getIntWidth(AST.UnsignedCharTy), + static_cast(RC)), + AST.UnsignedCharTy, SourceLocation()); + } + + BuiltinTypeDeclBuilder &addDefaultHandleConstructor(ResourceClass RC) { + assert(!Record->isCompleteDefinition() && "record is already complete"); + ASTContext &AST = Record->getASTContext(); + + QualType ConstructorType = + AST.getFunctionType(AST.VoidTy, {}, FunctionProtoType::ExtProtoInfo()); + + CanQualType CanTy = Record->getTypeForDecl()->getCanonicalTypeUnqualified(); + DeclarationName Name = AST.DeclarationNames.getCXXConstructorName(CanTy); + CXXConstructorDecl *Constructor = CXXConstructorDecl::Create( + AST, Record, SourceLocation(), + DeclarationNameInfo(Name, SourceLocation()), ConstructorType, + AST.getTrivialTypeSourceInfo(ConstructorType, SourceLocation()), + ExplicitSpecifier(), false, true, false, + ConstexprSpecKind::Unspecified); + + Constructor->setBody(CompoundStmt::Create( + AST, {}, FPOptionsOverride(), SourceLocation(), SourceLocation())); + Constructor->setAccess(AccessSpecifier::AS_public); + Record->addDecl(Constructor); + return *this; + } + + BuiltinTypeDeclBuilder &addArraySubscriptOperators() { + addArraySubscriptOperator(true); + addArraySubscriptOperator(false); + return *this; + } + + BuiltinTypeDeclBuilder &addArraySubscriptOperator(bool IsConst) { + assert(!Record->isCompleteDefinition() && "record is already complete"); + + ASTContext &AST = Record->getASTContext(); + QualType ElemTy = AST.Char8Ty; + if (Template) + ElemTy = getFirstTemplateTypeParam(); + QualType ReturnTy = ElemTy; + + FunctionProtoType::ExtProtoInfo ExtInfo; + + // Subscript operators return references to elements, const makes the + // reference and method const so that the underlying data is not mutable. + ReturnTy = AST.getLValueReferenceType(ReturnTy); + if (IsConst) { + ExtInfo.TypeQuals.addConst(); + ReturnTy.addConst(); + } + + QualType MethodTy = + AST.getFunctionType(ReturnTy, {AST.UnsignedIntTy}, ExtInfo); + auto *TSInfo = AST.getTrivialTypeSourceInfo(MethodTy, SourceLocation()); + auto *MethodDecl = CXXMethodDecl::Create( + AST, Record, SourceLocation(), + DeclarationNameInfo( + AST.DeclarationNames.getCXXOperatorName(OO_Subscript), + SourceLocation()), + MethodTy, TSInfo, SC_None, false, false, ConstexprSpecKind::Unspecified, + SourceLocation()); + + IdentifierInfo &II = AST.Idents.get("Idx", tok::TokenKind::identifier); + auto *IdxParam = ParmVarDecl::Create( + AST, MethodDecl->getDeclContext(), SourceLocation(), SourceLocation(), + &II, AST.UnsignedIntTy, + AST.getTrivialTypeSourceInfo(AST.UnsignedIntTy, SourceLocation()), + SC_None, nullptr); + MethodDecl->setParams({IdxParam}); + + // Also add the parameter to the function prototype. + auto FnProtoLoc = TSInfo->getTypeLoc().getAs(); + FnProtoLoc.setParam(0, IdxParam); + + // FIXME: Placeholder to make sure we return the correct type - create + // field of element_type and return reference to it. This field will go + // away once indexing into resources is properly implemented in + // llvm/llvm-project#95956. + if (Fields.count("e") == 0) { + addMemberVariable("e", ElemTy, {}); + } + FieldDecl *ElemFieldDecl = Fields["e"]; + + auto *This = + CXXThisExpr::Create(AST, SourceLocation(), + MethodDecl->getFunctionObjectParameterType(), true); + Expr *ElemField = MemberExpr::CreateImplicit( + AST, This, false, ElemFieldDecl, ElemFieldDecl->getType(), VK_LValue, + OK_Ordinary); + auto *Return = + ReturnStmt::Create(AST, SourceLocation(), ElemField, nullptr); + + MethodDecl->setBody(CompoundStmt::Create(AST, {Return}, FPOptionsOverride(), + SourceLocation(), + SourceLocation())); + MethodDecl->setLexicalDeclContext(Record); + MethodDecl->setAccess(AccessSpecifier::AS_public); + MethodDecl->addAttr(AlwaysInlineAttr::CreateImplicit( + AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline)); + Record->addDecl(MethodDecl); + + return *this; + } + + FieldDecl *getResourceHandleField() { + FieldDecl *FD = Fields["h"]; + assert(FD && FD->getType()->isHLSLAttributedResourceType() && + "record does not have resource handle"); + return FD; + } + + QualType getFirstTemplateTypeParam() { + assert(Template && "record it not a template"); + if (const auto *TTD = dyn_cast( + Template->getTemplateParameters()->getParam(0))) { + return QualType(TTD->getTypeForDecl(), 0); + } + return QualType(); + } + + BuiltinTypeDeclBuilder &startDefinition() { + // we might already have complete definition from a precompiled header + assert(!Record->isCompleteDefinition() && "record is already complete"); + Record->startDefinition(); + return *this; + } + + BuiltinTypeDeclBuilder &completeDefinition() { + assert(!Record->isCompleteDefinition() && "record is already complete"); + assert(Record->isBeingDefined() && + "Definition must be started before completing it."); + + Record->completeDefinition(); + return *this; + } + + TemplateParameterListBuilder addTemplateArgumentList(); + BuiltinTypeDeclBuilder &addSimpleTemplateParams(ArrayRef Names); + + // Builtin types methods + BuiltinTypeDeclBuilder &addIncrementCounterMethod(); + BuiltinTypeDeclBuilder &addDecrementCounterMethod(); +}; + +struct TemplateParameterListBuilder { + BuiltinTypeDeclBuilder &Builder; + llvm::SmallVector Params; + + TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) : Builder(RB) {} + + ~TemplateParameterListBuilder() { finalizeTemplateArgs(); } + + TemplateParameterListBuilder & + addTypeParameter(StringRef Name, QualType DefaultValue = QualType()) { + assert(!Builder.Record->isCompleteDefinition() && + "record is already complete"); + ASTContext &AST = Builder.S.getASTContext(); + unsigned Position = static_cast(Params.size()); + auto *Decl = TemplateTypeParmDecl::Create( + AST, Builder.Record->getDeclContext(), SourceLocation(), + SourceLocation(), /* TemplateDepth */ 0, Position, + &AST.Idents.get(Name, tok::TokenKind::identifier), + /* Typename */ false, + /* ParameterPack */ false); + if (!DefaultValue.isNull()) + Decl->setDefaultArgument( + AST, Builder.S.getTrivialTemplateArgumentLoc(DefaultValue, QualType(), + SourceLocation())); + + Params.emplace_back(Decl); + return *this; + } + + BuiltinTypeDeclBuilder &finalizeTemplateArgs() { + if (Params.empty()) + return Builder; + ASTContext &AST = Builder.S.Context; + auto *ParamList = + TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(), + Params, SourceLocation(), nullptr); + Builder.Template = ClassTemplateDecl::Create( + AST, Builder.Record->getDeclContext(), SourceLocation(), + DeclarationName(Builder.Record->getIdentifier()), ParamList, + Builder.Record); + Builder.Record->setDescribedClassTemplate(Builder.Template); + Builder.Template->setImplicit(true); + Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext()); + // NOTE: setPreviousDecl before addDecl so new decl replace old decl when + // make visible. + Builder.Template->setPreviousDecl(Builder.PrevTemplate); + Builder.Record->getDeclContext()->addDecl(Builder.Template); + Params.clear(); + + QualType T = Builder.Template->getInjectedClassNameSpecialization(); + T = AST.getInjectedClassNameType(Builder.Record, T); + + return Builder; + } +}; + +// Builder for methods of builtin types. Allows adding methods to builtin types +// using the builder pattern like this: +// +// BuiltinTypeMethodBuilder(Sema, RecordBuilder, "MethodName", ReturnType) +// .addParam("param_name", Type, InOutModifier) +// .callBuiltin("buildin_name", { BuiltinParams }) +// .finalizeMethod(); +// +// The builder needs to have all of the method parameters before it can create +// a CXXMethodDecl. It collects them in addParam calls and when a first +// method that builds the body is called it creates the CXXMethodDecl and +// ParmVarDecls instances. These can then be referenced from the body building +// methods. Destructor or an explicit call to finalizeMethod() will complete +// the method definition. +struct BuiltinTypeMethodBuilder { + struct MethodParam { + const IdentifierInfo &NameII; + QualType Ty; + HLSLParamModifierAttr::Spelling Modifier; + MethodParam(const IdentifierInfo &NameII, QualType Ty, + HLSLParamModifierAttr::Spelling Modifier) + : NameII(NameII), Ty(Ty), Modifier(Modifier) {} + }; + + BuiltinTypeDeclBuilder &DeclBuilder; + DeclarationNameInfo NameInfo; + QualType ReturnTy; + CXXMethodDecl *Method; + llvm::SmallVector Params; + llvm::SmallVector StmtsList; + +public: + BuiltinTypeMethodBuilder(Sema &S, BuiltinTypeDeclBuilder &DB, StringRef Name, + QualType ReturnTy) + : DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr) { + const IdentifierInfo &II = + S.getASTContext().Idents.get(Name, tok::TokenKind::identifier); + NameInfo = DeclarationNameInfo(DeclarationName(&II), SourceLocation()); + } + + BuiltinTypeMethodBuilder &addParam(StringRef Name, QualType Ty, + HLSLParamModifierAttr::Spelling Modifier = + HLSLParamModifierAttr::Keyword_in) { + assert(Method == nullptr && "Cannot add param, method already created"); + + const IdentifierInfo &II = DeclBuilder.S.getASTContext().Idents.get( + Name, tok::TokenKind::identifier); + Params.emplace_back(II, Ty, Modifier); + return *this; + } + +private: + void createMethodDecl() { + assert(Method == nullptr && "Method already created"); + + // create method type + ASTContext &AST = DeclBuilder.S.getASTContext(); + SmallVector ParamTypes; + for (auto &MP : Params) + ParamTypes.emplace_back(MP.Ty); + QualType MethodTy = AST.getFunctionType(ReturnTy, ParamTypes, + FunctionProtoType::ExtProtoInfo()); + + // create method decl + auto *TSInfo = AST.getTrivialTypeSourceInfo(MethodTy, SourceLocation()); + Method = + CXXMethodDecl::Create(AST, DeclBuilder.Record, SourceLocation(), + NameInfo, MethodTy, TSInfo, SC_None, false, false, + ConstexprSpecKind::Unspecified, SourceLocation()); + + // create params & set them to the function prototype + SmallVector ParmDecls; + auto FnProtoLoc = + Method->getTypeSourceInfo()->getTypeLoc().getAs(); + unsigned i = 0; + for (auto &MP : Params) { + ParmVarDecl *Parm = ParmVarDecl::Create( + AST, Method->getDeclContext(), SourceLocation(), SourceLocation(), + &MP.NameII, MP.Ty, + AST.getTrivialTypeSourceInfo(MP.Ty, SourceLocation()), SC_None, + nullptr); + if (MP.Modifier != HLSLParamModifierAttr::Keyword_in) { + auto *Mod = + HLSLParamModifierAttr::Create(AST, SourceRange(), MP.Modifier); + Parm->addAttr(Mod); + } + ParmDecls.push_back(Parm); + FnProtoLoc.setParam(i++, Parm); + } + Method->setParams({ParmDecls}); + } + + void addResourceHandleToParms(SmallVector &Parms) { + ASTContext &AST = DeclBuilder.S.getASTContext(); + FieldDecl *HandleField = DeclBuilder.getResourceHandleField(); + auto *This = CXXThisExpr::Create( + AST, SourceLocation(), Method->getFunctionObjectParameterType(), true); + Parms.push_back(MemberExpr::CreateImplicit(AST, This, false, HandleField, + HandleField->getType(), + VK_LValue, OK_Ordinary)); + } + +public: + ~BuiltinTypeMethodBuilder() { finalizeMethod(); } + + BuiltinTypeMethodBuilder & + callBuiltin(StringRef BuiltinName, ArrayRef CallParms, + bool AddResourceHandleAsFirstArg = true) { + if (!Method) + createMethodDecl(); + + ASTContext &AST = DeclBuilder.S.getASTContext(); + FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.S, BuiltinName); + DeclRefExpr *DRE = DeclRefExpr::Create( + AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false, + FD->getNameInfo(), FD->getType(), VK_PRValue); + + SmallVector NewCallParms; + if (AddResourceHandleAsFirstArg) { + addResourceHandleToParms(NewCallParms); + for (auto *P : CallParms) + NewCallParms.push_back(P); + } + + Expr *Call = CallExpr::Create( + AST, DRE, AddResourceHandleAsFirstArg ? NewCallParms : CallParms, + FD->getReturnType(), VK_PRValue, SourceLocation(), FPOptionsOverride()); + StmtsList.push_back(Call); + return *this; + } + + BuiltinTypeMethodBuilder & + callBuiltinForwardArgs(StringRef BuiltinName, + bool AddResourceHandleAsFirstArg = true) { + // FIXME: Call the buildin with all of the method parameters + // plus optional resource handle as the first arg. + llvm_unreachable("not yet implemented"); + } + + BuiltinTypeDeclBuilder &finalizeMethod() { + assert(!DeclBuilder.Record->isCompleteDefinition() && + "record is already complete"); + assert( + Method != nullptr && + "method decl not created; are you missing a call to build the body?"); + + if (!Method->hasBody()) { + ASTContext &AST = DeclBuilder.S.getASTContext(); + if (ReturnTy != AST.VoidTy && !StmtsList.empty()) { + if (Expr *LastExpr = dyn_cast(StmtsList.back())) { + assert(AST.hasSameUnqualifiedType( + isa(LastExpr) + ? cast(LastExpr)->getCallReturnType(AST) + : LastExpr->getType(), + ReturnTy) && + "Return type of the last statement must match the return type " + "of the method"); + StmtsList.pop_back(); + StmtsList.push_back( + ReturnStmt::Create(AST, SourceLocation(), LastExpr, nullptr)); + } + } + + Method->setBody(CompoundStmt::Create(AST, StmtsList, FPOptionsOverride(), + SourceLocation(), SourceLocation())); + Method->setLexicalDeclContext(DeclBuilder.Record); + Method->setAccess(AccessSpecifier::AS_public); + Method->addAttr(AlwaysInlineAttr::CreateImplicit( + AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline)); + DeclBuilder.Record->addDecl(Method); + } + return DeclBuilder; + } +}; + +} // namespace + +TemplateParameterListBuilder BuiltinTypeDeclBuilder::addTemplateArgumentList() { + return TemplateParameterListBuilder(*this); +} + +BuiltinTypeDeclBuilder & +BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef Names) { + if (Record->isCompleteDefinition()) { + assert(Template && "existing record it not a template"); + assert(Template->getTemplateParameters()->size() == Names.size() && + "template param count mismatch"); + return *this; + } + + TemplateParameterListBuilder Builder = this->addTemplateArgumentList(); + for (StringRef Name : Names) + Builder.addTypeParameter(Name); + return Builder.finalizeTemplateArgs(); +} + +BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() { + ASTContext &AST = S.getASTContext(); + Expr *One = + IntegerLiteral::Create(AST, llvm::APInt(AST.getTypeSize(AST.IntTy), 1), + AST.IntTy, SourceLocation()); + return BuiltinTypeMethodBuilder(S, *this, "IncrementCounter", + AST.UnsignedIntTy) + .callBuiltin("__builtin_hlsl_buffer_update_counter", {One}) + .finalizeMethod(); +} + +BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() { + ASTContext &AST = S.getASTContext(); + Expr *NegOne = + IntegerLiteral::Create(AST, llvm::APInt(AST.getTypeSize(AST.IntTy), -1), + AST.IntTy, SourceLocation()); + return BuiltinTypeMethodBuilder(S, *this, "DecrementCounter", + AST.UnsignedIntTy) + .callBuiltin("__builtin_hlsl_buffer_update_counter", {NegOne}) + .finalizeMethod(); +} + +HLSLExternalSemaSource::~HLSLExternalSemaSource() {} + +void HLSLExternalSemaSource::InitializeSema(Sema &S) { + SemaPtr = &S; + ASTContext &AST = SemaPtr->getASTContext(); + // If the translation unit has external storage force external decls to load. + if (AST.getTranslationUnitDecl()->hasExternalLexicalStorage()) + (void)AST.getTranslationUnitDecl()->decls_begin(); + + IdentifierInfo &HLSL = AST.Idents.get("hlsl", tok::TokenKind::identifier); + LookupResult Result(S, &HLSL, SourceLocation(), Sema::LookupNamespaceName); + NamespaceDecl *PrevDecl = nullptr; + if (S.LookupQualifiedName(Result, AST.getTranslationUnitDecl())) + PrevDecl = Result.getAsSingle(); + HLSLNamespace = NamespaceDecl::Create( + AST, AST.getTranslationUnitDecl(), /*Inline=*/false, SourceLocation(), + SourceLocation(), &HLSL, PrevDecl, /*Nested=*/false); + HLSLNamespace->setImplicit(true); + HLSLNamespace->setHasExternalLexicalStorage(); + AST.getTranslationUnitDecl()->addDecl(HLSLNamespace); + + // Force external decls in the HLSL namespace to load from the PCH. + (void)HLSLNamespace->getCanonicalDecl()->decls_begin(); + defineTrivialHLSLTypes(); + defineHLSLTypesWithForwardDeclarations(); + + // This adds a `using namespace hlsl` directive. In DXC, we don't put HLSL's + // built in types inside a namespace, but we are planning to change that in + // the near future. In order to be source compatible older versions of HLSL + // will need to implicitly use the hlsl namespace. For now in clang everything + // will get added to the namespace, and we can remove the using directive for + // future language versions to match HLSL's evolution. + auto *UsingDecl = UsingDirectiveDecl::Create( + AST, AST.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), + NestedNameSpecifierLoc(), SourceLocation(), HLSLNamespace, + AST.getTranslationUnitDecl()); + + AST.getTranslationUnitDecl()->addDecl(UsingDecl); +} + +void HLSLExternalSemaSource::defineHLSLVectorAlias() { + ASTContext &AST = SemaPtr->getASTContext(); + + llvm::SmallVector TemplateParams; + + auto *TypeParam = TemplateTypeParmDecl::Create( + AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 0, + &AST.Idents.get("element", tok::TokenKind::identifier), false, false); + TypeParam->setDefaultArgument( + AST, SemaPtr->getTrivialTemplateArgumentLoc( + TemplateArgument(AST.FloatTy), QualType(), SourceLocation())); + + TemplateParams.emplace_back(TypeParam); + + auto *SizeParam = NonTypeTemplateParmDecl::Create( + AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 1, + &AST.Idents.get("element_count", tok::TokenKind::identifier), AST.IntTy, + false, AST.getTrivialTypeSourceInfo(AST.IntTy)); + llvm::APInt Val(AST.getIntWidth(AST.IntTy), 4); + TemplateArgument Default(AST, llvm::APSInt(std::move(Val)), AST.IntTy, + /*IsDefaulted=*/true); + SizeParam->setDefaultArgument( + AST, SemaPtr->getTrivialTemplateArgumentLoc(Default, AST.IntTy, + SourceLocation(), SizeParam)); + TemplateParams.emplace_back(SizeParam); + + auto *ParamList = + TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(), + TemplateParams, SourceLocation(), nullptr); + + IdentifierInfo &II = AST.Idents.get("vector", tok::TokenKind::identifier); + + QualType AliasType = AST.getDependentSizedExtVectorType( + AST.getTemplateTypeParmType(0, 0, false, TypeParam), + DeclRefExpr::Create( + AST, NestedNameSpecifierLoc(), SourceLocation(), SizeParam, false, + DeclarationNameInfo(SizeParam->getDeclName(), SourceLocation()), + AST.IntTy, VK_LValue), + SourceLocation()); + + auto *Record = TypeAliasDecl::Create(AST, HLSLNamespace, SourceLocation(), + SourceLocation(), &II, + AST.getTrivialTypeSourceInfo(AliasType)); + Record->setImplicit(true); + + auto *Template = + TypeAliasTemplateDecl::Create(AST, HLSLNamespace, SourceLocation(), + Record->getIdentifier(), ParamList, Record); + + Record->setDescribedAliasTemplate(Template); + Template->setImplicit(true); + Template->setLexicalDeclContext(Record->getDeclContext()); + HLSLNamespace->addDecl(Template); +} + +void HLSLExternalSemaSource::defineTrivialHLSLTypes() { + defineHLSLVectorAlias(); +} + +/// Set up common members and attributes for buffer types +static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S, + ResourceClass RC, ResourceKind RK, + bool IsROV, bool RawBuffer) { + return BuiltinTypeDeclBuilder(S, Decl) + .addHandleMember(RC, RK, IsROV, RawBuffer) + .addDefaultHandleConstructor(RC); +} + +void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { + CXXRecordDecl *Decl; + Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer") + .addSimpleTemplateParams({"element_type"}) + .Record; + + onCompletion(Decl, [this](CXXRecordDecl *Decl) { + setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, + ResourceKind::TypedBuffer, + /*IsROV=*/false, /*RawBuffer=*/false) + .addArraySubscriptOperators() + .completeDefinition(); + }); + + Decl = + BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer") + .addSimpleTemplateParams({"element_type"}) + .Record; + onCompletion(Decl, [this](CXXRecordDecl *Decl) { + setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, + ResourceKind::TypedBuffer, /*IsROV=*/true, + /*RawBuffer=*/false) + .addArraySubscriptOperators() + .completeDefinition(); + }); + + Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "StructuredBuffer") + .addSimpleTemplateParams({"element_type"}) + .Record; + onCompletion(Decl, [this](CXXRecordDecl *Decl) { + setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, + ResourceKind::TypedBuffer, /*IsROV=*/false, + /*RawBuffer=*/true) + .addArraySubscriptOperators() + .completeDefinition(); + }); + + Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWStructuredBuffer") + .addSimpleTemplateParams({"element_type"}) + .Record; + onCompletion(Decl, [this](CXXRecordDecl *Decl) { + setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, + ResourceKind::TypedBuffer, /*IsROV=*/false, + /*RawBuffer=*/true) + .addArraySubscriptOperators() + .addIncrementCounterMethod() + .addDecrementCounterMethod() + .completeDefinition(); + }); + + // FIXME: Also add Increment/DecrementCounter to + // RasterizerOrderedStructuredBuffer when llvm/llvm-project/#113648 is merged. +} + +void HLSLExternalSemaSource::onCompletion(CXXRecordDecl *Record, + CompletionFunction Fn) { + if (!Record->isCompleteDefinition()) + Completions.insert(std::make_pair(Record->getCanonicalDecl(), Fn)); +} + +void HLSLExternalSemaSource::CompleteType(TagDecl *Tag) { + if (!isa(Tag)) + return; + auto Record = cast(Tag); + + // If this is a specialization, we need to get the underlying templated + // declaration and complete that. + if (auto TDecl = dyn_cast(Record)) + Record = TDecl->getSpecializedTemplate()->getTemplatedDecl(); + Record = Record->getCanonicalDecl(); + auto It = Completions.find(Record); + if (It == Completions.end()) + return; + It->second(Record); +} + +static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name) { + IdentifierInfo &II = + S.getASTContext().Idents.get(Name, tok::TokenKind::identifier); + DeclarationNameInfo NameInfo = + DeclarationNameInfo(DeclarationName(&II), SourceLocation()); + LookupResult R(S, NameInfo, Sema::LookupOrdinaryName); + // AllowBuiltinCreation is false but LookupDirect will create + // the builtin when searching the global scope anyways... + S.LookupName(R, S.getCurScope()); + // FIXME: If the builtin function was user-declared in global scope, + // this assert *will* fail. Should this call LookupBuiltin instead? + assert(R.isSingleResult() && + "Since this is a builtin it should always resolve!"); + assert(isa(R.getFoundDecl())); + return cast(R.getFoundDecl()); +} diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index ad1c6b2bcf953..4df69d54141ae 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -534,9 +534,9 @@ BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef Names) { BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() { ASTContext &AST = S.getASTContext(); - Expr *One = - IntegerLiteral::Create(AST, llvm::APInt(AST.getTypeSize(AST.IntTy), 1), - AST.IntTy, SourceLocation()); + Expr *One = IntegerLiteral::Create( + AST, llvm::APInt(AST.getTypeSize(AST.IntTy), 1, true), AST.IntTy, + SourceLocation()); return BuiltinTypeMethodBuilder(S, *this, "IncrementCounter", AST.UnsignedIntTy) .callBuiltin("__builtin_hlsl_buffer_update_counter", {One}) @@ -545,9 +545,9 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() { BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() { ASTContext &AST = S.getASTContext(); - Expr *NegOne = - IntegerLiteral::Create(AST, llvm::APInt(AST.getTypeSize(AST.IntTy), -1), - AST.IntTy, SourceLocation()); + Expr *NegOne = IntegerLiteral::Create( + AST, llvm::APInt(AST.getTypeSize(AST.IntTy), -1, true), AST.IntTy, + SourceLocation()); return BuiltinTypeMethodBuilder(S, *this, "DecrementCounter", AST.UnsignedIntTy) .callBuiltin("__builtin_hlsl_buffer_update_counter", {NegOne}) @@ -721,6 +721,8 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { ResourceKind::TypedBuffer, /*IsROV=*/true, /*RawBuffer=*/true) .addArraySubscriptOperators() + .addIncrementCounterMethod() + .addDecrementCounterMethod() .completeDefinition(); }); } diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl index c8ff5d3cd905f..6cfa133f6f289 100644 --- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl +++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl @@ -22,4 +22,4 @@ export void TestDecrementCounter() { // CHECK: define void @_Z20TestDecrementCounterv() // CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 -1) -// CHECK: declare i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i32) \ No newline at end of file +// CHECK: declare i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i32) diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl index fe9e9cfdcb873..b193f17484c1c 100644 --- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl +++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl @@ -9,21 +9,20 @@ RasterizerOrderedStructuredBuffer ROSB1, ROSB2; // CHECK: %"class.hlsl::RWStructuredBuffer" = type { target("dx.RawBuffer", float, 1, 0), float } export void TestIncrementCounter() { +// CHECK: define void @_Z20TestIncrementCounterv() +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 1) +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1) %{{[0-9]+}}, i32 1) RWSB1.IncrementCounter(); ROSB1.IncrementCounter(); } -// CHECK: define void @_Z20TestIncrementCounterv() -// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 1) -// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 1) %{{[0-9]+}}, i32 1) - export void TestDecrementCounter() { +// CHECK: define void @_Z20TestDecrementCounterv() +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 -1) +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1) %{{[0-9]+}}, i32 -1) RWSB2.DecrementCounter(); ROSB2.DecrementCounter(); } -// CHECK: define void @_Z20TestDecrementCounterv() -// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 -1) -// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 1) %{{[0-9]+}}, i32 -1) - -// CHECK: declare i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i32) \ No newline at end of file +// CHECK: declare i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i32) +// CHECK: declare i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i32) From 7fe5623c036669e56e0405e2f8f5ae8b07de995a Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Thu, 7 Nov 2024 14:24:13 -0800 Subject: [PATCH 07/15] Remove accidentally added file; update builtin to not be Const --- HLSLExternalSemaSource.cpp | 771 -------------------------- clang/include/clang/Basic/Builtins.td | 2 +- 2 files changed, 1 insertion(+), 772 deletions(-) delete mode 100644 HLSLExternalSemaSource.cpp diff --git a/HLSLExternalSemaSource.cpp b/HLSLExternalSemaSource.cpp deleted file mode 100644 index b64efb481146d..0000000000000 --- a/HLSLExternalSemaSource.cpp +++ /dev/null @@ -1,771 +0,0 @@ -//===--- HLSLExternalSemaSource.cpp - HLSL Sema Source --------------------===// -// -// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. -// See https://llvm.org/LICENSE.txt for license information. -// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -// -//===----------------------------------------------------------------------===// -// -// -//===----------------------------------------------------------------------===// - -#include "clang/Sema/HLSLExternalSemaSource.h" -#include "clang/AST/ASTContext.h" -#include "clang/AST/Attr.h" -#include "clang/AST/Decl.h" -#include "clang/AST/DeclCXX.h" -#include "clang/AST/Expr.h" -#include "clang/AST/Type.h" -#include "clang/Basic/AttrKinds.h" -#include "clang/Basic/HLSLRuntime.h" -#include "clang/Basic/SourceLocation.h" -#include "clang/Sema/Lookup.h" -#include "clang/Sema/Sema.h" -#include "clang/Sema/SemaHLSL.h" -#include "llvm/ADT/SmallVector.h" -#include "llvm/Frontend/HLSL/HLSLResource.h" -#include "llvm/Support/ErrorHandling.h" - -#include - -using namespace clang; -using namespace llvm::hlsl; - -static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name); - -namespace { - -struct TemplateParameterListBuilder; - -struct BuiltinTypeDeclBuilder { - Sema &S; - CXXRecordDecl *Record = nullptr; - ClassTemplateDecl *Template = nullptr; - ClassTemplateDecl *PrevTemplate = nullptr; - NamespaceDecl *HLSLNamespace = nullptr; - llvm::StringMap Fields; - - BuiltinTypeDeclBuilder(Sema &S, CXXRecordDecl *R) : S(S), Record(R) { - Record->startDefinition(); - Template = Record->getDescribedClassTemplate(); - } - - BuiltinTypeDeclBuilder(Sema &S, NamespaceDecl *Namespace, StringRef Name) - : S(S), HLSLNamespace(Namespace) { - ASTContext &AST = S.getASTContext(); - IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier); - - LookupResult Result(S, &II, SourceLocation(), Sema::LookupTagName); - CXXRecordDecl *PrevDecl = nullptr; - if (S.LookupQualifiedName(Result, HLSLNamespace)) { - // Declaration already exists (from precompiled headers) - NamedDecl *Found = Result.getFoundDecl(); - if (auto *TD = dyn_cast(Found)) { - PrevDecl = TD->getTemplatedDecl(); - PrevTemplate = TD; - } else - PrevDecl = dyn_cast(Found); - assert(PrevDecl && "Unexpected lookup result type."); - } - - if (PrevDecl && PrevDecl->isCompleteDefinition()) { - Record = PrevDecl; - Template = PrevTemplate; - return; - } - - Record = CXXRecordDecl::Create(AST, TagDecl::TagKind::Class, HLSLNamespace, - SourceLocation(), SourceLocation(), &II, - PrevDecl, true); - Record->setImplicit(true); - Record->setLexicalDeclContext(HLSLNamespace); - Record->setHasExternalLexicalStorage(); - - // Don't let anyone derive from built-in types. - Record->addAttr(FinalAttr::CreateImplicit(AST, SourceRange(), - FinalAttr::Keyword_final)); - } - - ~BuiltinTypeDeclBuilder() { - if (HLSLNamespace && !Template && Record->getDeclContext() == HLSLNamespace) - HLSLNamespace->addDecl(Record); - } - - BuiltinTypeDeclBuilder & - addMemberVariable(StringRef Name, QualType Type, llvm::ArrayRef Attrs, - AccessSpecifier Access = AccessSpecifier::AS_private) { - assert(!Record->isCompleteDefinition() && "record is already complete"); - assert(Record->isBeingDefined() && - "Definition must be started before adding members!"); - ASTContext &AST = Record->getASTContext(); - - IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier); - TypeSourceInfo *MemTySource = - AST.getTrivialTypeSourceInfo(Type, SourceLocation()); - auto *Field = FieldDecl::Create( - AST, Record, SourceLocation(), SourceLocation(), &II, Type, MemTySource, - nullptr, false, InClassInitStyle::ICIS_NoInit); - Field->setAccess(Access); - Field->setImplicit(true); - for (Attr *A : Attrs) { - if (A) - Field->addAttr(A); - } - - Record->addDecl(Field); - Fields[Name] = Field; - return *this; - } - - BuiltinTypeDeclBuilder & - addHandleMember(ResourceClass RC, ResourceKind RK, bool IsROV, bool RawBuffer, - AccessSpecifier Access = AccessSpecifier::AS_private) { - assert(!Record->isCompleteDefinition() && "record is already complete"); - - ASTContext &Ctx = S.getASTContext(); - TypeSourceInfo *ElementTypeInfo = nullptr; - - QualType ElemTy = Ctx.Char8Ty; - if (Template) - ElemTy = getFirstTemplateTypeParam(); - ElementTypeInfo = Ctx.getTrivialTypeSourceInfo(ElemTy, SourceLocation()); - - // add handle member with resource type attributes - QualType AttributedResTy = QualType(); - SmallVector Attrs = { - HLSLResourceClassAttr::CreateImplicit(Ctx, RC), - IsROV ? HLSLROVAttr::CreateImplicit(Ctx) : nullptr, - RawBuffer ? HLSLRawBufferAttr::CreateImplicit(Ctx) : nullptr, - ElementTypeInfo - ? HLSLContainedTypeAttr::CreateImplicit(Ctx, ElementTypeInfo) - : nullptr}; - Attr *ResourceAttr = HLSLResourceAttr::CreateImplicit(Ctx, RK); - if (CreateHLSLAttributedResourceType(S, Ctx.HLSLResourceTy, Attrs, - AttributedResTy)) - addMemberVariable("h", AttributedResTy, {ResourceAttr}, Access); - return *this; - } - - static Expr *emitResourceClassExpr(ASTContext &AST, ResourceClass RC) { - return IntegerLiteral::Create( - AST, - llvm::APInt(AST.getIntWidth(AST.UnsignedCharTy), - static_cast(RC)), - AST.UnsignedCharTy, SourceLocation()); - } - - BuiltinTypeDeclBuilder &addDefaultHandleConstructor(ResourceClass RC) { - assert(!Record->isCompleteDefinition() && "record is already complete"); - ASTContext &AST = Record->getASTContext(); - - QualType ConstructorType = - AST.getFunctionType(AST.VoidTy, {}, FunctionProtoType::ExtProtoInfo()); - - CanQualType CanTy = Record->getTypeForDecl()->getCanonicalTypeUnqualified(); - DeclarationName Name = AST.DeclarationNames.getCXXConstructorName(CanTy); - CXXConstructorDecl *Constructor = CXXConstructorDecl::Create( - AST, Record, SourceLocation(), - DeclarationNameInfo(Name, SourceLocation()), ConstructorType, - AST.getTrivialTypeSourceInfo(ConstructorType, SourceLocation()), - ExplicitSpecifier(), false, true, false, - ConstexprSpecKind::Unspecified); - - Constructor->setBody(CompoundStmt::Create( - AST, {}, FPOptionsOverride(), SourceLocation(), SourceLocation())); - Constructor->setAccess(AccessSpecifier::AS_public); - Record->addDecl(Constructor); - return *this; - } - - BuiltinTypeDeclBuilder &addArraySubscriptOperators() { - addArraySubscriptOperator(true); - addArraySubscriptOperator(false); - return *this; - } - - BuiltinTypeDeclBuilder &addArraySubscriptOperator(bool IsConst) { - assert(!Record->isCompleteDefinition() && "record is already complete"); - - ASTContext &AST = Record->getASTContext(); - QualType ElemTy = AST.Char8Ty; - if (Template) - ElemTy = getFirstTemplateTypeParam(); - QualType ReturnTy = ElemTy; - - FunctionProtoType::ExtProtoInfo ExtInfo; - - // Subscript operators return references to elements, const makes the - // reference and method const so that the underlying data is not mutable. - ReturnTy = AST.getLValueReferenceType(ReturnTy); - if (IsConst) { - ExtInfo.TypeQuals.addConst(); - ReturnTy.addConst(); - } - - QualType MethodTy = - AST.getFunctionType(ReturnTy, {AST.UnsignedIntTy}, ExtInfo); - auto *TSInfo = AST.getTrivialTypeSourceInfo(MethodTy, SourceLocation()); - auto *MethodDecl = CXXMethodDecl::Create( - AST, Record, SourceLocation(), - DeclarationNameInfo( - AST.DeclarationNames.getCXXOperatorName(OO_Subscript), - SourceLocation()), - MethodTy, TSInfo, SC_None, false, false, ConstexprSpecKind::Unspecified, - SourceLocation()); - - IdentifierInfo &II = AST.Idents.get("Idx", tok::TokenKind::identifier); - auto *IdxParam = ParmVarDecl::Create( - AST, MethodDecl->getDeclContext(), SourceLocation(), SourceLocation(), - &II, AST.UnsignedIntTy, - AST.getTrivialTypeSourceInfo(AST.UnsignedIntTy, SourceLocation()), - SC_None, nullptr); - MethodDecl->setParams({IdxParam}); - - // Also add the parameter to the function prototype. - auto FnProtoLoc = TSInfo->getTypeLoc().getAs(); - FnProtoLoc.setParam(0, IdxParam); - - // FIXME: Placeholder to make sure we return the correct type - create - // field of element_type and return reference to it. This field will go - // away once indexing into resources is properly implemented in - // llvm/llvm-project#95956. - if (Fields.count("e") == 0) { - addMemberVariable("e", ElemTy, {}); - } - FieldDecl *ElemFieldDecl = Fields["e"]; - - auto *This = - CXXThisExpr::Create(AST, SourceLocation(), - MethodDecl->getFunctionObjectParameterType(), true); - Expr *ElemField = MemberExpr::CreateImplicit( - AST, This, false, ElemFieldDecl, ElemFieldDecl->getType(), VK_LValue, - OK_Ordinary); - auto *Return = - ReturnStmt::Create(AST, SourceLocation(), ElemField, nullptr); - - MethodDecl->setBody(CompoundStmt::Create(AST, {Return}, FPOptionsOverride(), - SourceLocation(), - SourceLocation())); - MethodDecl->setLexicalDeclContext(Record); - MethodDecl->setAccess(AccessSpecifier::AS_public); - MethodDecl->addAttr(AlwaysInlineAttr::CreateImplicit( - AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline)); - Record->addDecl(MethodDecl); - - return *this; - } - - FieldDecl *getResourceHandleField() { - FieldDecl *FD = Fields["h"]; - assert(FD && FD->getType()->isHLSLAttributedResourceType() && - "record does not have resource handle"); - return FD; - } - - QualType getFirstTemplateTypeParam() { - assert(Template && "record it not a template"); - if (const auto *TTD = dyn_cast( - Template->getTemplateParameters()->getParam(0))) { - return QualType(TTD->getTypeForDecl(), 0); - } - return QualType(); - } - - BuiltinTypeDeclBuilder &startDefinition() { - // we might already have complete definition from a precompiled header - assert(!Record->isCompleteDefinition() && "record is already complete"); - Record->startDefinition(); - return *this; - } - - BuiltinTypeDeclBuilder &completeDefinition() { - assert(!Record->isCompleteDefinition() && "record is already complete"); - assert(Record->isBeingDefined() && - "Definition must be started before completing it."); - - Record->completeDefinition(); - return *this; - } - - TemplateParameterListBuilder addTemplateArgumentList(); - BuiltinTypeDeclBuilder &addSimpleTemplateParams(ArrayRef Names); - - // Builtin types methods - BuiltinTypeDeclBuilder &addIncrementCounterMethod(); - BuiltinTypeDeclBuilder &addDecrementCounterMethod(); -}; - -struct TemplateParameterListBuilder { - BuiltinTypeDeclBuilder &Builder; - llvm::SmallVector Params; - - TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) : Builder(RB) {} - - ~TemplateParameterListBuilder() { finalizeTemplateArgs(); } - - TemplateParameterListBuilder & - addTypeParameter(StringRef Name, QualType DefaultValue = QualType()) { - assert(!Builder.Record->isCompleteDefinition() && - "record is already complete"); - ASTContext &AST = Builder.S.getASTContext(); - unsigned Position = static_cast(Params.size()); - auto *Decl = TemplateTypeParmDecl::Create( - AST, Builder.Record->getDeclContext(), SourceLocation(), - SourceLocation(), /* TemplateDepth */ 0, Position, - &AST.Idents.get(Name, tok::TokenKind::identifier), - /* Typename */ false, - /* ParameterPack */ false); - if (!DefaultValue.isNull()) - Decl->setDefaultArgument( - AST, Builder.S.getTrivialTemplateArgumentLoc(DefaultValue, QualType(), - SourceLocation())); - - Params.emplace_back(Decl); - return *this; - } - - BuiltinTypeDeclBuilder &finalizeTemplateArgs() { - if (Params.empty()) - return Builder; - ASTContext &AST = Builder.S.Context; - auto *ParamList = - TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(), - Params, SourceLocation(), nullptr); - Builder.Template = ClassTemplateDecl::Create( - AST, Builder.Record->getDeclContext(), SourceLocation(), - DeclarationName(Builder.Record->getIdentifier()), ParamList, - Builder.Record); - Builder.Record->setDescribedClassTemplate(Builder.Template); - Builder.Template->setImplicit(true); - Builder.Template->setLexicalDeclContext(Builder.Record->getDeclContext()); - // NOTE: setPreviousDecl before addDecl so new decl replace old decl when - // make visible. - Builder.Template->setPreviousDecl(Builder.PrevTemplate); - Builder.Record->getDeclContext()->addDecl(Builder.Template); - Params.clear(); - - QualType T = Builder.Template->getInjectedClassNameSpecialization(); - T = AST.getInjectedClassNameType(Builder.Record, T); - - return Builder; - } -}; - -// Builder for methods of builtin types. Allows adding methods to builtin types -// using the builder pattern like this: -// -// BuiltinTypeMethodBuilder(Sema, RecordBuilder, "MethodName", ReturnType) -// .addParam("param_name", Type, InOutModifier) -// .callBuiltin("buildin_name", { BuiltinParams }) -// .finalizeMethod(); -// -// The builder needs to have all of the method parameters before it can create -// a CXXMethodDecl. It collects them in addParam calls and when a first -// method that builds the body is called it creates the CXXMethodDecl and -// ParmVarDecls instances. These can then be referenced from the body building -// methods. Destructor or an explicit call to finalizeMethod() will complete -// the method definition. -struct BuiltinTypeMethodBuilder { - struct MethodParam { - const IdentifierInfo &NameII; - QualType Ty; - HLSLParamModifierAttr::Spelling Modifier; - MethodParam(const IdentifierInfo &NameII, QualType Ty, - HLSLParamModifierAttr::Spelling Modifier) - : NameII(NameII), Ty(Ty), Modifier(Modifier) {} - }; - - BuiltinTypeDeclBuilder &DeclBuilder; - DeclarationNameInfo NameInfo; - QualType ReturnTy; - CXXMethodDecl *Method; - llvm::SmallVector Params; - llvm::SmallVector StmtsList; - -public: - BuiltinTypeMethodBuilder(Sema &S, BuiltinTypeDeclBuilder &DB, StringRef Name, - QualType ReturnTy) - : DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr) { - const IdentifierInfo &II = - S.getASTContext().Idents.get(Name, tok::TokenKind::identifier); - NameInfo = DeclarationNameInfo(DeclarationName(&II), SourceLocation()); - } - - BuiltinTypeMethodBuilder &addParam(StringRef Name, QualType Ty, - HLSLParamModifierAttr::Spelling Modifier = - HLSLParamModifierAttr::Keyword_in) { - assert(Method == nullptr && "Cannot add param, method already created"); - - const IdentifierInfo &II = DeclBuilder.S.getASTContext().Idents.get( - Name, tok::TokenKind::identifier); - Params.emplace_back(II, Ty, Modifier); - return *this; - } - -private: - void createMethodDecl() { - assert(Method == nullptr && "Method already created"); - - // create method type - ASTContext &AST = DeclBuilder.S.getASTContext(); - SmallVector ParamTypes; - for (auto &MP : Params) - ParamTypes.emplace_back(MP.Ty); - QualType MethodTy = AST.getFunctionType(ReturnTy, ParamTypes, - FunctionProtoType::ExtProtoInfo()); - - // create method decl - auto *TSInfo = AST.getTrivialTypeSourceInfo(MethodTy, SourceLocation()); - Method = - CXXMethodDecl::Create(AST, DeclBuilder.Record, SourceLocation(), - NameInfo, MethodTy, TSInfo, SC_None, false, false, - ConstexprSpecKind::Unspecified, SourceLocation()); - - // create params & set them to the function prototype - SmallVector ParmDecls; - auto FnProtoLoc = - Method->getTypeSourceInfo()->getTypeLoc().getAs(); - unsigned i = 0; - for (auto &MP : Params) { - ParmVarDecl *Parm = ParmVarDecl::Create( - AST, Method->getDeclContext(), SourceLocation(), SourceLocation(), - &MP.NameII, MP.Ty, - AST.getTrivialTypeSourceInfo(MP.Ty, SourceLocation()), SC_None, - nullptr); - if (MP.Modifier != HLSLParamModifierAttr::Keyword_in) { - auto *Mod = - HLSLParamModifierAttr::Create(AST, SourceRange(), MP.Modifier); - Parm->addAttr(Mod); - } - ParmDecls.push_back(Parm); - FnProtoLoc.setParam(i++, Parm); - } - Method->setParams({ParmDecls}); - } - - void addResourceHandleToParms(SmallVector &Parms) { - ASTContext &AST = DeclBuilder.S.getASTContext(); - FieldDecl *HandleField = DeclBuilder.getResourceHandleField(); - auto *This = CXXThisExpr::Create( - AST, SourceLocation(), Method->getFunctionObjectParameterType(), true); - Parms.push_back(MemberExpr::CreateImplicit(AST, This, false, HandleField, - HandleField->getType(), - VK_LValue, OK_Ordinary)); - } - -public: - ~BuiltinTypeMethodBuilder() { finalizeMethod(); } - - BuiltinTypeMethodBuilder & - callBuiltin(StringRef BuiltinName, ArrayRef CallParms, - bool AddResourceHandleAsFirstArg = true) { - if (!Method) - createMethodDecl(); - - ASTContext &AST = DeclBuilder.S.getASTContext(); - FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.S, BuiltinName); - DeclRefExpr *DRE = DeclRefExpr::Create( - AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false, - FD->getNameInfo(), FD->getType(), VK_PRValue); - - SmallVector NewCallParms; - if (AddResourceHandleAsFirstArg) { - addResourceHandleToParms(NewCallParms); - for (auto *P : CallParms) - NewCallParms.push_back(P); - } - - Expr *Call = CallExpr::Create( - AST, DRE, AddResourceHandleAsFirstArg ? NewCallParms : CallParms, - FD->getReturnType(), VK_PRValue, SourceLocation(), FPOptionsOverride()); - StmtsList.push_back(Call); - return *this; - } - - BuiltinTypeMethodBuilder & - callBuiltinForwardArgs(StringRef BuiltinName, - bool AddResourceHandleAsFirstArg = true) { - // FIXME: Call the buildin with all of the method parameters - // plus optional resource handle as the first arg. - llvm_unreachable("not yet implemented"); - } - - BuiltinTypeDeclBuilder &finalizeMethod() { - assert(!DeclBuilder.Record->isCompleteDefinition() && - "record is already complete"); - assert( - Method != nullptr && - "method decl not created; are you missing a call to build the body?"); - - if (!Method->hasBody()) { - ASTContext &AST = DeclBuilder.S.getASTContext(); - if (ReturnTy != AST.VoidTy && !StmtsList.empty()) { - if (Expr *LastExpr = dyn_cast(StmtsList.back())) { - assert(AST.hasSameUnqualifiedType( - isa(LastExpr) - ? cast(LastExpr)->getCallReturnType(AST) - : LastExpr->getType(), - ReturnTy) && - "Return type of the last statement must match the return type " - "of the method"); - StmtsList.pop_back(); - StmtsList.push_back( - ReturnStmt::Create(AST, SourceLocation(), LastExpr, nullptr)); - } - } - - Method->setBody(CompoundStmt::Create(AST, StmtsList, FPOptionsOverride(), - SourceLocation(), SourceLocation())); - Method->setLexicalDeclContext(DeclBuilder.Record); - Method->setAccess(AccessSpecifier::AS_public); - Method->addAttr(AlwaysInlineAttr::CreateImplicit( - AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline)); - DeclBuilder.Record->addDecl(Method); - } - return DeclBuilder; - } -}; - -} // namespace - -TemplateParameterListBuilder BuiltinTypeDeclBuilder::addTemplateArgumentList() { - return TemplateParameterListBuilder(*this); -} - -BuiltinTypeDeclBuilder & -BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef Names) { - if (Record->isCompleteDefinition()) { - assert(Template && "existing record it not a template"); - assert(Template->getTemplateParameters()->size() == Names.size() && - "template param count mismatch"); - return *this; - } - - TemplateParameterListBuilder Builder = this->addTemplateArgumentList(); - for (StringRef Name : Names) - Builder.addTypeParameter(Name); - return Builder.finalizeTemplateArgs(); -} - -BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() { - ASTContext &AST = S.getASTContext(); - Expr *One = - IntegerLiteral::Create(AST, llvm::APInt(AST.getTypeSize(AST.IntTy), 1), - AST.IntTy, SourceLocation()); - return BuiltinTypeMethodBuilder(S, *this, "IncrementCounter", - AST.UnsignedIntTy) - .callBuiltin("__builtin_hlsl_buffer_update_counter", {One}) - .finalizeMethod(); -} - -BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() { - ASTContext &AST = S.getASTContext(); - Expr *NegOne = - IntegerLiteral::Create(AST, llvm::APInt(AST.getTypeSize(AST.IntTy), -1), - AST.IntTy, SourceLocation()); - return BuiltinTypeMethodBuilder(S, *this, "DecrementCounter", - AST.UnsignedIntTy) - .callBuiltin("__builtin_hlsl_buffer_update_counter", {NegOne}) - .finalizeMethod(); -} - -HLSLExternalSemaSource::~HLSLExternalSemaSource() {} - -void HLSLExternalSemaSource::InitializeSema(Sema &S) { - SemaPtr = &S; - ASTContext &AST = SemaPtr->getASTContext(); - // If the translation unit has external storage force external decls to load. - if (AST.getTranslationUnitDecl()->hasExternalLexicalStorage()) - (void)AST.getTranslationUnitDecl()->decls_begin(); - - IdentifierInfo &HLSL = AST.Idents.get("hlsl", tok::TokenKind::identifier); - LookupResult Result(S, &HLSL, SourceLocation(), Sema::LookupNamespaceName); - NamespaceDecl *PrevDecl = nullptr; - if (S.LookupQualifiedName(Result, AST.getTranslationUnitDecl())) - PrevDecl = Result.getAsSingle(); - HLSLNamespace = NamespaceDecl::Create( - AST, AST.getTranslationUnitDecl(), /*Inline=*/false, SourceLocation(), - SourceLocation(), &HLSL, PrevDecl, /*Nested=*/false); - HLSLNamespace->setImplicit(true); - HLSLNamespace->setHasExternalLexicalStorage(); - AST.getTranslationUnitDecl()->addDecl(HLSLNamespace); - - // Force external decls in the HLSL namespace to load from the PCH. - (void)HLSLNamespace->getCanonicalDecl()->decls_begin(); - defineTrivialHLSLTypes(); - defineHLSLTypesWithForwardDeclarations(); - - // This adds a `using namespace hlsl` directive. In DXC, we don't put HLSL's - // built in types inside a namespace, but we are planning to change that in - // the near future. In order to be source compatible older versions of HLSL - // will need to implicitly use the hlsl namespace. For now in clang everything - // will get added to the namespace, and we can remove the using directive for - // future language versions to match HLSL's evolution. - auto *UsingDecl = UsingDirectiveDecl::Create( - AST, AST.getTranslationUnitDecl(), SourceLocation(), SourceLocation(), - NestedNameSpecifierLoc(), SourceLocation(), HLSLNamespace, - AST.getTranslationUnitDecl()); - - AST.getTranslationUnitDecl()->addDecl(UsingDecl); -} - -void HLSLExternalSemaSource::defineHLSLVectorAlias() { - ASTContext &AST = SemaPtr->getASTContext(); - - llvm::SmallVector TemplateParams; - - auto *TypeParam = TemplateTypeParmDecl::Create( - AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 0, - &AST.Idents.get("element", tok::TokenKind::identifier), false, false); - TypeParam->setDefaultArgument( - AST, SemaPtr->getTrivialTemplateArgumentLoc( - TemplateArgument(AST.FloatTy), QualType(), SourceLocation())); - - TemplateParams.emplace_back(TypeParam); - - auto *SizeParam = NonTypeTemplateParmDecl::Create( - AST, HLSLNamespace, SourceLocation(), SourceLocation(), 0, 1, - &AST.Idents.get("element_count", tok::TokenKind::identifier), AST.IntTy, - false, AST.getTrivialTypeSourceInfo(AST.IntTy)); - llvm::APInt Val(AST.getIntWidth(AST.IntTy), 4); - TemplateArgument Default(AST, llvm::APSInt(std::move(Val)), AST.IntTy, - /*IsDefaulted=*/true); - SizeParam->setDefaultArgument( - AST, SemaPtr->getTrivialTemplateArgumentLoc(Default, AST.IntTy, - SourceLocation(), SizeParam)); - TemplateParams.emplace_back(SizeParam); - - auto *ParamList = - TemplateParameterList::Create(AST, SourceLocation(), SourceLocation(), - TemplateParams, SourceLocation(), nullptr); - - IdentifierInfo &II = AST.Idents.get("vector", tok::TokenKind::identifier); - - QualType AliasType = AST.getDependentSizedExtVectorType( - AST.getTemplateTypeParmType(0, 0, false, TypeParam), - DeclRefExpr::Create( - AST, NestedNameSpecifierLoc(), SourceLocation(), SizeParam, false, - DeclarationNameInfo(SizeParam->getDeclName(), SourceLocation()), - AST.IntTy, VK_LValue), - SourceLocation()); - - auto *Record = TypeAliasDecl::Create(AST, HLSLNamespace, SourceLocation(), - SourceLocation(), &II, - AST.getTrivialTypeSourceInfo(AliasType)); - Record->setImplicit(true); - - auto *Template = - TypeAliasTemplateDecl::Create(AST, HLSLNamespace, SourceLocation(), - Record->getIdentifier(), ParamList, Record); - - Record->setDescribedAliasTemplate(Template); - Template->setImplicit(true); - Template->setLexicalDeclContext(Record->getDeclContext()); - HLSLNamespace->addDecl(Template); -} - -void HLSLExternalSemaSource::defineTrivialHLSLTypes() { - defineHLSLVectorAlias(); -} - -/// Set up common members and attributes for buffer types -static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S, - ResourceClass RC, ResourceKind RK, - bool IsROV, bool RawBuffer) { - return BuiltinTypeDeclBuilder(S, Decl) - .addHandleMember(RC, RK, IsROV, RawBuffer) - .addDefaultHandleConstructor(RC); -} - -void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { - CXXRecordDecl *Decl; - Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer") - .addSimpleTemplateParams({"element_type"}) - .Record; - - onCompletion(Decl, [this](CXXRecordDecl *Decl) { - setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, - ResourceKind::TypedBuffer, - /*IsROV=*/false, /*RawBuffer=*/false) - .addArraySubscriptOperators() - .completeDefinition(); - }); - - Decl = - BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RasterizerOrderedBuffer") - .addSimpleTemplateParams({"element_type"}) - .Record; - onCompletion(Decl, [this](CXXRecordDecl *Decl) { - setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, - ResourceKind::TypedBuffer, /*IsROV=*/true, - /*RawBuffer=*/false) - .addArraySubscriptOperators() - .completeDefinition(); - }); - - Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "StructuredBuffer") - .addSimpleTemplateParams({"element_type"}) - .Record; - onCompletion(Decl, [this](CXXRecordDecl *Decl) { - setupBufferType(Decl, *SemaPtr, ResourceClass::SRV, - ResourceKind::TypedBuffer, /*IsROV=*/false, - /*RawBuffer=*/true) - .addArraySubscriptOperators() - .completeDefinition(); - }); - - Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWStructuredBuffer") - .addSimpleTemplateParams({"element_type"}) - .Record; - onCompletion(Decl, [this](CXXRecordDecl *Decl) { - setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, - ResourceKind::TypedBuffer, /*IsROV=*/false, - /*RawBuffer=*/true) - .addArraySubscriptOperators() - .addIncrementCounterMethod() - .addDecrementCounterMethod() - .completeDefinition(); - }); - - // FIXME: Also add Increment/DecrementCounter to - // RasterizerOrderedStructuredBuffer when llvm/llvm-project/#113648 is merged. -} - -void HLSLExternalSemaSource::onCompletion(CXXRecordDecl *Record, - CompletionFunction Fn) { - if (!Record->isCompleteDefinition()) - Completions.insert(std::make_pair(Record->getCanonicalDecl(), Fn)); -} - -void HLSLExternalSemaSource::CompleteType(TagDecl *Tag) { - if (!isa(Tag)) - return; - auto Record = cast(Tag); - - // If this is a specialization, we need to get the underlying templated - // declaration and complete that. - if (auto TDecl = dyn_cast(Record)) - Record = TDecl->getSpecializedTemplate()->getTemplatedDecl(); - Record = Record->getCanonicalDecl(); - auto It = Completions.find(Record); - if (It == Completions.end()) - return; - It->second(Record); -} - -static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name) { - IdentifierInfo &II = - S.getASTContext().Idents.get(Name, tok::TokenKind::identifier); - DeclarationNameInfo NameInfo = - DeclarationNameInfo(DeclarationName(&II), SourceLocation()); - LookupResult R(S, NameInfo, Sema::LookupOrdinaryName); - // AllowBuiltinCreation is false but LookupDirect will create - // the builtin when searching the global scope anyways... - S.LookupName(R, S.getCurScope()); - // FIXME: If the builtin function was user-declared in global scope, - // this assert *will* fail. Should this call LookupBuiltin instead? - assert(R.isSingleResult() && - "Since this is a builtin it should always resolve!"); - assert(isa(R.getFoundDecl())); - return cast(R.getFoundDecl()); -} diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index 0f0069153c32e..236a6b7a87ea3 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -4884,7 +4884,7 @@ def HLSLRadians : LangBuiltin<"HLSL_LANG"> { def HLSLBufferUpdateCounter : LangBuiltin<"HLSL_LANG"> { let Spellings = ["__builtin_hlsl_buffer_update_counter"]; - let Attributes = [NoThrow, Const]; + let Attributes = [NoThrow]; let Prototype = "uint32_t(...)"; } From d04755b520a96a718d49943a5da73bbac7158357 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Thu, 14 Nov 2024 00:28:40 -0800 Subject: [PATCH 08/15] Add comments, make getResourceHandleExpr public, add const int helper to simplify method building code --- clang/lib/Sema/HLSLExternalSemaSource.cpp | 65 ++++++++++++++--------- 1 file changed, 40 insertions(+), 25 deletions(-) diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 4df69d54141ae..a6ba89a0dbb8b 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -278,6 +278,13 @@ struct BuiltinTypeDeclBuilder { return *this; } + Expr *getConstantIntExpr(int value) { + ASTContext &AST = S.getASTContext(); + return IntegerLiteral::Create( + AST, llvm::APInt(AST.getTypeSize(AST.IntTy), value, true), AST.IntTy, + SourceLocation()); + } + TemplateParameterListBuilder addTemplateArgumentList(); BuiltinTypeDeclBuilder &addSimpleTemplateParams(ArrayRef Names); @@ -352,10 +359,18 @@ struct TemplateParameterListBuilder { // // The builder needs to have all of the method parameters before it can create // a CXXMethodDecl. It collects them in addParam calls and when a first -// method that builds the body is called it creates the CXXMethodDecl and -// ParmVarDecls instances. These can then be referenced from the body building -// methods. Destructor or an explicit call to finalizeMethod() will complete -// the method definition. +// method that builds the body is called or when access to 'this` is needed it +// creates the CXXMethodDecl and ParmVarDecls instances. These can then be +// referenced from the body building methods. Destructor or an explicit call to +// finalizeMethod() will complete the method definition. +// +// The callBuiltin helper method passes in the resource handle as the first +// argument of the builtin call. If this is not desired it takes a bool flag to +// disable this. +// +// If the method that is being built has a non-void return type the +// finalizeMethod will create a return statent with the value of the last +// statement (unless the last statement is already a ReturnStmt). struct BuiltinTypeMethodBuilder { struct MethodParam { const IdentifierInfo &NameII; @@ -434,24 +449,30 @@ struct BuiltinTypeMethodBuilder { Method->setParams({ParmDecls}); } - void addResourceHandleToParms(SmallVector &Parms) { +public: + ~BuiltinTypeMethodBuilder() { finalizeMethod(); } + + Expr *getResourceHandleExpr() { + // The first statement added to a method or access to 'this' creates the + // declaration. + if (!Method) + createMethodDecl(); + ASTContext &AST = DeclBuilder.S.getASTContext(); FieldDecl *HandleField = DeclBuilder.getResourceHandleField(); auto *This = CXXThisExpr::Create( AST, SourceLocation(), Method->getFunctionObjectParameterType(), true); - Parms.push_back(MemberExpr::CreateImplicit(AST, This, false, HandleField, - HandleField->getType(), - VK_LValue, OK_Ordinary)); + return MemberExpr::CreateImplicit(AST, This, false, HandleField, + HandleField->getType(), VK_LValue, + OK_Ordinary); } -public: - ~BuiltinTypeMethodBuilder() { finalizeMethod(); } - BuiltinTypeMethodBuilder & callBuiltin(StringRef BuiltinName, ArrayRef CallParms, bool AddResourceHandleAsFirstArg = true) { - // The first statement added to a method creates the declaration. + // The first statement added to a method or access to 'this` creates the + // declaration. if (!Method) createMethodDecl(); @@ -463,7 +484,7 @@ struct BuiltinTypeMethodBuilder { SmallVector NewCallParms; if (AddResourceHandleAsFirstArg) { - addResourceHandleToParms(NewCallParms); + NewCallParms.push_back(getResourceHandleExpr()); for (auto *P : CallParms) NewCallParms.push_back(P); } @@ -533,24 +554,18 @@ BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef Names) { } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() { - ASTContext &AST = S.getASTContext(); - Expr *One = IntegerLiteral::Create( - AST, llvm::APInt(AST.getTypeSize(AST.IntTy), 1, true), AST.IntTy, - SourceLocation()); return BuiltinTypeMethodBuilder(S, *this, "IncrementCounter", - AST.UnsignedIntTy) - .callBuiltin("__builtin_hlsl_buffer_update_counter", {One}) + S.getASTContext().UnsignedIntTy) + .callBuiltin("__builtin_hlsl_buffer_update_counter", + {getConstantIntExpr(1)}) .finalizeMethod(); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() { - ASTContext &AST = S.getASTContext(); - Expr *NegOne = IntegerLiteral::Create( - AST, llvm::APInt(AST.getTypeSize(AST.IntTy), -1, true), AST.IntTy, - SourceLocation()); return BuiltinTypeMethodBuilder(S, *this, "DecrementCounter", - AST.UnsignedIntTy) - .callBuiltin("__builtin_hlsl_buffer_update_counter", {NegOne}) + S.getASTContext().UnsignedIntTy) + .callBuiltin("__builtin_hlsl_buffer_update_counter", + {getConstantIntExpr(-1)}) .finalizeMethod(); } From bc336cbf2738bf20ecc2910ee07be3ae47b72ace Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Thu, 14 Nov 2024 00:59:08 -0800 Subject: [PATCH 09/15] code review feedback --- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/lib/Sema/HLSLExternalSemaSource.cpp | 32 +++++++------- clang/lib/Sema/SemaHLSL.cpp | 42 ++++++++++++------- .../buffer_update_counter-errors.hlsl | 32 ++++++++++++-- llvm/include/llvm/IR/IntrinsicsDirectX.td | 2 +- llvm/include/llvm/IR/IntrinsicsSPIRV.td | 2 +- 6 files changed, 77 insertions(+), 35 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 8dc8dcb1737e1..84991b82c2a28 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12498,6 +12498,8 @@ def err_hlsl_attribute_number_arguments_insufficient_shader_model: Error< "attribute %0 with %1 arguments requires shader model %2 or greater">; def err_hlsl_expect_arg_const_int_one_or_neg_one: Error< "argument %0 must be constant integer 1 or -1">; +def err_invalid_hlsl_resource_type: Error< + "invalid __hlsl_resource_t type attributes">; // Layout randomization diagnostics. def err_non_designated_init_used : Error< diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index a6ba89a0dbb8b..a1491d8c8c37d 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -247,10 +247,11 @@ struct BuiltinTypeDeclBuilder { } FieldDecl *getResourceHandleField() { - FieldDecl *FD = Fields["h"]; - assert(FD && FD->getType()->isHLSLAttributedResourceType() && - "record does not have resource handle"); - return FD; + auto I = Fields.find("h"); + assert(I != Fields.end() && + I->second->getType()->isHLSLAttributedResourceType() && + "record does not have resource handle field"); + return I->second; } QualType getFirstTemplateTypeParam() { @@ -263,7 +264,6 @@ struct BuiltinTypeDeclBuilder { } BuiltinTypeDeclBuilder &startDefinition() { - // we might already have complete definition from a precompiled header assert(!Record->isCompleteDefinition() && "record is already complete"); Record->startDefinition(); return *this; @@ -401,11 +401,7 @@ struct BuiltinTypeMethodBuilder { HLSLParamModifierAttr::Spelling Modifier = HLSLParamModifierAttr::Keyword_in) { assert(Method == nullptr && "Cannot add param, method already created"); - - const IdentifierInfo &II = DeclBuilder.S.getASTContext().Idents.get( - Name, tok::TokenKind::identifier); - Params.emplace_back(II, Ty, Modifier); - return *this; + llvm_unreachable("not yet implemented"); } private: @@ -459,9 +455,9 @@ struct BuiltinTypeMethodBuilder { createMethodDecl(); ASTContext &AST = DeclBuilder.S.getASTContext(); - FieldDecl *HandleField = DeclBuilder.getResourceHandleField(); - auto *This = CXXThisExpr::Create( + CXXThisExpr *This = CXXThisExpr::Create( AST, SourceLocation(), Method->getFunctionObjectParameterType(), true); + FieldDecl *HandleField = DeclBuilder.getResourceHandleField(); return MemberExpr::CreateImplicit(AST, This, false, HandleField, HandleField->getType(), VK_LValue, OK_Ordinary); @@ -505,7 +501,9 @@ struct BuiltinTypeMethodBuilder { if (!Method->hasBody()) { ASTContext &AST = DeclBuilder.S.getASTContext(); - if (ReturnTy != AST.VoidTy && !StmtsList.empty()) { + assert((ReturnTy == AST.VoidTy || !StmtsList.empty()) && + "nothing to return from non-void method"); + if (ReturnTy != AST.VoidTy) { if (Expr *LastExpr = dyn_cast(StmtsList.back())) { assert(AST.hasSameUnqualifiedType( isa(LastExpr) @@ -514,9 +512,11 @@ struct BuiltinTypeMethodBuilder { ReturnTy) && "Return type of the last statement must match the return type " "of the method"); - StmtsList.pop_back(); - StmtsList.push_back( - ReturnStmt::Create(AST, SourceLocation(), LastExpr, nullptr)); + if (!isa(LastExpr)) { + StmtsList.pop_back(); + StmtsList.push_back( + ReturnStmt::Create(AST, SourceLocation(), LastExpr, nullptr)); + } } } diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 80d9d581c5e74..9db3bd50fc2b9 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -1698,6 +1698,16 @@ static bool CheckVectorElementCallArgs(Sema *S, CallExpr *TheCall) { return true; } +bool CheckArgTypeIsCorrect(Sema *S, Expr *Arg, QualType ExpectedType) { + QualType ArgType = Arg->getType(); + if (!S->getASTContext().hasSameUnqualifiedType(ArgType, ExpectedType)) { + S->Diag(Arg->getBeginLoc(), diag::err_typecheck_convert_incompatible) + << ArgType << ExpectedType << 1 << 0 << 0; + return true; + } + return false; +} + bool CheckArgTypeIsCorrect( Sema *S, Expr *Arg, QualType ExpectedType, llvm::function_ref Check) { @@ -1880,25 +1890,24 @@ static bool CheckVectorSelect(Sema *S, CallExpr *TheCall) { return false; } -static bool CheckResourceHandle(Sema *S, CallExpr *TheCall, unsigned ArgIndex) { +static bool CheckResourceHandle( + Sema *S, CallExpr *TheCall, unsigned ArgIndex, + llvm::function_ref Check = + nullptr) { assert(TheCall->getNumArgs() >= ArgIndex); QualType ArgType = TheCall->getArg(ArgIndex)->getType(); - if (!ArgType.getTypePtr() - ->getUnqualifiedDesugaredType() - ->isHLSLAttributedResourceType()) { + const HLSLAttributedResourceType *ResTy = + dyn_cast( + ArgType.getTypePtr()->getUnqualifiedDesugaredType()); + if (!ResTy) { S->Diag(TheCall->getArg(0)->getBeginLoc(), diag::err_typecheck_expect_hlsl_resource) << ArgType; return true; } - return false; -} - -static bool CheckInt(Sema *S, CallExpr *TheCall, unsigned ArgIndex) { - assert(TheCall->getNumArgs() >= ArgIndex); - QualType ArgType = TheCall->getArg(ArgIndex)->getType(); - if (!ArgType->isIntegerType()) { - S->Diag(TheCall->getArg(0)->getBeginLoc(), diag::err_typecheck_expect_int) + if (Check && Check(ResTy)) { + S->Diag(TheCall->getArg(ArgIndex)->getExprLoc(), + diag::err_invalid_hlsl_resource_type) << ArgType; return true; } @@ -2187,9 +2196,14 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { break; } case Builtin::BI__builtin_hlsl_buffer_update_counter: { + auto checkResTy = [](const HLSLAttributedResourceType *ResTy) -> bool { + return !(ResTy->getAttrs().ResourceClass == ResourceClass::UAV && + ResTy->getAttrs().RawBuffer && ResTy->hasContainedType()); + }; if (SemaRef.checkArgCount(TheCall, 2) || - CheckResourceHandle(&SemaRef, TheCall, 0) || - CheckInt(&SemaRef, TheCall, 1)) + CheckResourceHandle(&SemaRef, TheCall, 0, checkResTy) || + CheckArgTypeIsCorrect(&SemaRef, TheCall->getArg(1), + SemaRef.getASTContext().IntTy)) return true; Expr *OffsetExpr = TheCall->getArg(1); std::optional Offset = diff --git a/clang/test/SemaHLSL/BuiltIns/buffer_update_counter-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/buffer_update_counter-errors.hlsl index 11b8cebc1aeb4..4aa3ac183d3b1 100644 --- a/clang/test/SemaHLSL/BuiltIns/buffer_update_counter-errors.hlsl +++ b/clang/test/SemaHLSL/BuiltIns/buffer_update_counter-errors.hlsl @@ -1,10 +1,15 @@ // RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.6-library %s -emit-llvm-only -disable-llvm-passes -verify -using handle_t = __hlsl_resource_t [[hlsl::resource_class(UAV)]] [[hlsl::contained_type(int)]]; +// RWStructuredBuffer +using handle_t = __hlsl_resource_t [[hlsl::resource_class(UAV)]] [[hlsl::contained_type(int)]] [[hlsl::raw_buffer]]; +// RWBuffer +using bad_handle_not_raw_t = __hlsl_resource_t [[hlsl::resource_class(UAV)]] [[hlsl::contained_type(int)]]; +// RWByteAddressBuffer +using bad_handle_no_type_t = __hlsl_resource_t [[hlsl::resource_class(UAV)]] [[hlsl::raw_buffer]]; +// StructuredBuffer +using bad_handle_not_uav_t = __hlsl_resource_t [[hlsl::resource_class(SRV)]] [[hlsl::contained_type(int)]] [[hlsl::raw_buffer]]; void test_args(int x, bool b) { - handle_t res; - // expected-error@+1 {{too few arguments to function call, expected 2, have 1}} __builtin_hlsl_buffer_update_counter(x); @@ -14,9 +19,30 @@ void test_args(int x, bool b) { // expected-error@+1 {{used type 'int' where __hlsl_resource_t is required}} __builtin_hlsl_buffer_update_counter(x, x); + bad_handle_not_raw_t bad1; + bad_handle_no_type_t bad2; + bad_handle_not_uav_t bad3; + + // expected-error@+1 {{invalid __hlsl_resource_t type attributes}} + __builtin_hlsl_buffer_update_counter(bad1, 1); + + // expected-error@+1 {{invalid __hlsl_resource_t type attributes}} + __builtin_hlsl_buffer_update_counter(bad2, 1); + + // expected-error@+1 {{invalid __hlsl_resource_t type attributes}} + __builtin_hlsl_buffer_update_counter(bad3, 1); + + handle_t res; + // expected-error@+1 {{argument 1 must be constant integer 1 or -1}} __builtin_hlsl_buffer_update_counter(res, x); + // expected-error@+1 {{passing 'const char *' to parameter of incompatible type 'int'}} + __builtin_hlsl_buffer_update_counter(res, "1"); + // expected-error@+1 {{argument 1 must be constant integer 1 or -1}} __builtin_hlsl_buffer_update_counter(res, 10); + + // no error + __builtin_hlsl_buffer_update_counter(res, 1); } diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td index fc4c5a5243fe6..4e1a4c0ab72a8 100644 --- a/llvm/include/llvm/IR/IntrinsicsDirectX.td +++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td @@ -36,7 +36,7 @@ def int_dx_typedBufferStore : DefaultAttrsIntrinsic<[], [llvm_any_ty, llvm_i32_ty, llvm_anyvector_ty]>; def int_dx_bufferUpdateCounter - : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_i32_ty], [IntrWriteMem]>; + : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_i32_ty], [IntrInaccessibleMemOrArgMemOnly]>; // Cast between target extension handle types and dxil-style opaque handles def int_dx_cast_handle : Intrinsic<[llvm_any_ty], [llvm_any_ty]>; diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td index 58be18d41cd3c..d8191298aab9c 100644 --- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td +++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td @@ -104,5 +104,5 @@ let TargetPrefix = "spv" in { def int_spv_firstbitshigh : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>; def int_spv_bufferUpdateCounter - : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_i32_ty], [IntrWriteMem]>; + : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_i32_ty], [IntrInaccessibleMemOrArgMemOnly]>; } From 170e390bccc20dd952bd06a8a97863add5396901 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Thu, 14 Nov 2024 02:02:57 -0800 Subject: [PATCH 10/15] fix merge --- clang/lib/Sema/HLSLExternalSemaSource.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index d714f6120f143..677976b0f077c 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -729,7 +729,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "AppendStructuredBuffer") - .addSimpleTemplateParams(*SemaPtr, {"element_type"}) + .addSimpleTemplateParams({"element_type"}) .Record; onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer, @@ -740,7 +740,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ConsumeStructuredBuffer") - .addSimpleTemplateParams(*SemaPtr, {"element_type"}) + .addSimpleTemplateParams({"element_type"}) .Record; onCompletion(Decl, [this](CXXRecordDecl *Decl) { setupBufferType(Decl, *SemaPtr, ResourceClass::UAV, ResourceKind::RawBuffer, From d8f3d9c009da2ae25117225c63dbf3e1e59832a6 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Thu, 21 Nov 2024 10:24:18 -0800 Subject: [PATCH 11/15] Update intrinsics name and arguments to match DXIL and SPIRV. --- llvm/include/llvm/IR/IntrinsicsDirectX.td | 2 +- llvm/include/llvm/IR/IntrinsicsSPIRV.td | 3 ++- llvm/lib/Target/DirectX/DXILOpLowering.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td b/llvm/include/llvm/IR/IntrinsicsDirectX.td index 48a9595f844f0..fad29380586db 100644 --- a/llvm/include/llvm/IR/IntrinsicsDirectX.td +++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td @@ -37,7 +37,7 @@ def int_dx_typedBufferStore : DefaultAttrsIntrinsic<[], [llvm_any_ty, llvm_i32_ty, llvm_anyvector_ty], [IntrWriteMem]>; -def int_dx_updateCounter +def int_dx_bufferUpdateCounter : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_i8_ty], [IntrInaccessibleMemOrArgMemOnly]>; diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td index 1d5be7bc98b97..ae3daf4a7476f 100644 --- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td +++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td @@ -110,7 +110,8 @@ let TargetPrefix = "spv" in { def int_spv_firstbitshigh : DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, llvm_i32_ty>], [llvm_anyint_ty], [IntrNoMem]>; def int_spv_bufferUpdateCounter - : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_i32_ty], [IntrInaccessibleMemOrArgMemOnly]>; + : DefaultAttrsIntrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_i8_ty], + [IntrInaccessibleMemOrArgMemOnly]>; // Read a value from the image buffer. It does not translate directly to a // single OpImageRead because the result type is not necessarily a 4 element diff --git a/llvm/lib/Target/DirectX/DXILOpLowering.cpp b/llvm/lib/Target/DirectX/DXILOpLowering.cpp index 9f124394363a3..7f50fc509a862 100644 --- a/llvm/lib/Target/DirectX/DXILOpLowering.cpp +++ b/llvm/lib/Target/DirectX/DXILOpLowering.cpp @@ -647,7 +647,7 @@ class OpLowerer { case Intrinsic::dx_typedBufferStore: HasErrors |= lowerTypedBufferStore(F); break; - case Intrinsic::dx_updateCounter: + case Intrinsic::dx_bufferUpdateCounter: HasErrors |= lowerUpdateCounter(F); break; // TODO: this can be removed when From 62714331e1b13ce0a5d362af40913a42a3cb1580 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Thu, 21 Nov 2024 21:00:30 -0800 Subject: [PATCH 12/15] Add bitcast from builtin i32 param to intrinsic's i8 Few changes to minimize conflicts with another PR in progress. Update handle field name and tests. --- clang/lib/CodeGen/CGBuiltin.cpp | 3 ++- clang/lib/Sema/HLSLExternalSemaSource.cpp | 2 +- clang/lib/Sema/SemaExpr.cpp | 3 +-- clang/lib/Sema/SemaHLSL.cpp | 3 +-- clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl | 4 ++-- .../builtins/StructuredBuffers-methods-lib.hlsl | 6 +++--- .../builtins/StructuredBuffers-methods-ps.hlsl | 12 ++++++------ 7 files changed, 16 insertions(+), 17 deletions(-) diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 9c56f73dda363..abc8b4cab6acb 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -19374,10 +19374,11 @@ case Builtin::BI__builtin_hlsl_elementwise_isinf: { case Builtin::BI__builtin_hlsl_buffer_update_counter: { Value *ResHandle = EmitScalarExpr(E->getArg(0)); Value *Offset = EmitScalarExpr(E->getArg(1)); + Value *Offset_i8 = Builder.CreateIntCast(Offset, Int8Ty, true); return Builder.CreateIntrinsic( /*ReturnType=*/Offset->getType(), CGM.getHLSLRuntime().getBufferUpdateCounterIntrinsic(), - ArrayRef{ResHandle, Offset}, nullptr); + ArrayRef{ResHandle, Offset_i8}, nullptr); } case Builtin::BI__builtin_hlsl_elementwise_splitdouble: { diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index c0d1aab78e807..c58bd0032fb6c 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -247,7 +247,7 @@ struct BuiltinTypeDeclBuilder { } FieldDecl *getResourceHandleField() { - auto I = Fields.find("h"); + auto I = Fields.find("__handle"); assert(I != Fields.end() && I->second->getType()->isHLSLAttributedResourceType() && "record does not have resource handle field"); diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index d0c9cecc1807b..c9d7444d5865a 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -983,8 +983,7 @@ Sema::VarArgKind Sema::isValidVarArgType(const QualType &Ty) { if (getLangOpts().MSVCCompat) return VAK_MSVCUndefined; - if (getLangOpts().HLSL && - Ty->getUnqualifiedDesugaredType()->isHLSLAttributedResourceType()) + if (getLangOpts().HLSL && Ty->getAs()) return VAK_Valid; // FIXME: In C++11, these cases are conditionally-supported, meaning we're diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index cab0178df5d2f..94fde09dbb066 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -1895,8 +1895,7 @@ static bool CheckResourceHandle( assert(TheCall->getNumArgs() >= ArgIndex); QualType ArgType = TheCall->getArg(ArgIndex)->getType(); const HLSLAttributedResourceType *ResTy = - dyn_cast( - ArgType.getTypePtr()->getUnqualifiedDesugaredType()); + ArgType.getTypePtr()->getAs(); if (!ResTy) { S->Diag(TheCall->getArg(0)->getBeginLoc(), diag::err_typecheck_expect_hlsl_resource) diff --git a/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl b/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl index d73956190479e..1439b1eca2835 100644 --- a/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl +++ b/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl @@ -60,7 +60,7 @@ RWStructuredBuffer Buffer; // CHECK-NEXT: MemberExpr 0x{{[0-9A-Fa-f]+}} <> '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] -// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]' lvalue .h +// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]' lvalue .__handle // CHECK-NEXT: CXXThisExpr 0x{{[0-9A-Fa-f]+}} <> 'RWStructuredBuffer' lvalue implicit this // CHECK-NEXT: IntegerLiteral 0x{{[0-9A-Fa-f]+}} <> 'int' 1 // CHECK-NEXT: AlwaysInlineAttr 0x{{[0-9A-Fa-f]+}} <> Implicit always_inline @@ -73,7 +73,7 @@ RWStructuredBuffer Buffer; // CHECK-NEXT: MemberExpr 0x{{[0-9A-Fa-f]+}} <> '__hlsl_resource_t // CHECK-SAME{LITERAL}: [[hlsl::resource_class(UAV)]] // CHECK-SAME{LITERAL}: [[hlsl::raw_buffer]] -// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]' lvalue .h +// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]' lvalue .__handle // CHECK-NEXT: CXXThisExpr 0x{{[0-9A-Fa-f]+}} <> 'RWStructuredBuffer' lvalue implicit this // CHECK-NEXT: IntegerLiteral 0x{{[0-9A-Fa-f]+}} <> 'int' -1 // CHECK-NEXT: AlwaysInlineAttr 0x{{[0-9A-Fa-f]+}} <> Implicit always_inline diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl index 6cfa133f6f289..128fff9b90a22 100644 --- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl +++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-lib.hlsl @@ -13,13 +13,13 @@ export void TestIncrementCounter() { } // CHECK: define void @_Z20TestIncrementCounterv() -// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 1) +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i8 1) export void TestDecrementCounter() { RWSB2.DecrementCounter(); } // CHECK: define void @_Z20TestDecrementCounterv() -// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 -1) +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i8 -1) -// CHECK: declare i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i32) +// CHECK: declare i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i8) diff --git a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl index b193f17484c1c..e895d30b54007 100644 --- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl +++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl @@ -10,19 +10,19 @@ RasterizerOrderedStructuredBuffer ROSB1, ROSB2; export void TestIncrementCounter() { // CHECK: define void @_Z20TestIncrementCounterv() -// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 1) -// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1) %{{[0-9]+}}, i32 1) +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i8 1) +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1) %{{[0-9]+}}, i8 1) RWSB1.IncrementCounter(); ROSB1.IncrementCounter(); } export void TestDecrementCounter() { // CHECK: define void @_Z20TestDecrementCounterv() -// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i32 -1) -// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1) %{{[0-9]+}}, i32 -1) +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0) %{{[0-9]+}}, i8 -1) +// CHECK-DXIL: call i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1) %{{[0-9]+}}, i8 -1) RWSB2.DecrementCounter(); ROSB2.DecrementCounter(); } -// CHECK: declare i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i32) -// CHECK: declare i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i32) +// CHECK: declare i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_0t(target("dx.RawBuffer", float, 1, 0), i8) +// CHECK: declare i32 @llvm.dx.bufferUpdateCounter.tdx.RawBuffer_f32_1_1t(target("dx.RawBuffer", float, 1, 1), i8) From 4b70b9914579891ff034c27abbc4e5f43df21871 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Fri, 22 Nov 2024 17:12:14 -0800 Subject: [PATCH 13/15] code review feedback, fix merge, remove unused addConceptSpecializationExpr declaration --- clang/lib/CodeGen/CGBuiltin.cpp | 4 ++-- clang/lib/Sema/HLSLExternalSemaSource.cpp | 26 +++++++++++------------ 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 85dd93d372090..0ad669264050c 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -19412,11 +19412,11 @@ case Builtin::BI__builtin_hlsl_elementwise_isinf: { case Builtin::BI__builtin_hlsl_buffer_update_counter: { Value *ResHandle = EmitScalarExpr(E->getArg(0)); Value *Offset = EmitScalarExpr(E->getArg(1)); - Value *Offset_i8 = Builder.CreateIntCast(Offset, Int8Ty, true); + Value *OffsetI8 = Builder.CreateIntCast(Offset, Int8Ty, true); return Builder.CreateIntrinsic( /*ReturnType=*/Offset->getType(), CGM.getHLSLRuntime().getBufferUpdateCounterIntrinsic(), - ArrayRef{ResHandle, Offset_i8}, nullptr); + ArrayRef{ResHandle, OffsetI8}, nullptr); } case Builtin::BI__builtin_hlsl_elementwise_splitdouble: { diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 95a9149454785..6a06b60f3b2ab 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -285,10 +285,9 @@ struct BuiltinTypeDeclBuilder { SourceLocation()); } - TemplateParameterListBuilder addTemplateArgumentList(Sema &S); - BuiltinTypeDeclBuilder & - addSimpleTemplateParams(Sema &S, ArrayRef Names, ConceptDecl *CD); - BuiltinTypeDeclBuilder &addConceptSpecializationExpr(Sema &S); + TemplateParameterListBuilder addTemplateArgumentList(); + BuiltinTypeDeclBuilder &addSimpleTemplateParams(ArrayRef Names, + ConceptDecl *CD); // Builtin types methods BuiltinTypeDeclBuilder &addIncrementCounterMethod(); @@ -512,7 +511,7 @@ struct BuiltinTypeMethodBuilder { // create method type ASTContext &AST = DeclBuilder.S.getASTContext(); SmallVector ParamTypes; - for (auto &MP : Params) + for (MethodParam &MP : Params) ParamTypes.emplace_back(MP.Ty); QualType MethodTy = AST.getFunctionType(ReturnTy, ParamTypes, FunctionProtoType::ExtProtoInfo()); @@ -528,8 +527,8 @@ struct BuiltinTypeMethodBuilder { SmallVector ParmDecls; auto FnProtoLoc = Method->getTypeSourceInfo()->getTypeLoc().getAs(); - unsigned i = 0; - for (auto &MP : Params) { + for (int I = 0, E = Params.size(); I != E; I++) { + MethodParam &MP = Params[I]; ParmVarDecl *Parm = ParmVarDecl::Create( AST, Method->getDeclContext(), SourceLocation(), SourceLocation(), &MP.NameII, MP.Ty, @@ -541,7 +540,7 @@ struct BuiltinTypeMethodBuilder { Parm->addAttr(Mod); } ParmDecls.push_back(Parm); - FnProtoLoc.setParam(i++, Parm); + FnProtoLoc.setParam(I, Parm); } Method->setParams({ParmDecls}); } @@ -639,8 +638,9 @@ TemplateParameterListBuilder BuiltinTypeDeclBuilder::addTemplateArgumentList() { return TemplateParameterListBuilder(*this); } -BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addSimpleTemplateParams( - Sema &S, ArrayRef Names, ConceptDecl *CD = nullptr) { +BuiltinTypeDeclBuilder & +BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef Names, + ConceptDecl *CD = nullptr) { if (Record->isCompleteDefinition()) { assert(Template && "existing record it not a template"); assert(Template->getTemplateParameters()->size() == Names.size() && @@ -648,7 +648,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addSimpleTemplateParams( return *this; } - TemplateParameterListBuilder Builder = this->addTemplateArgumentList(S); + TemplateParameterListBuilder Builder = this->addTemplateArgumentList(); for (StringRef Name : Names) Builder.addTypeParameter(Name); return Builder.finalizeTemplateArgs(CD); @@ -843,8 +843,7 @@ void HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() { ConceptDecl *TypedBufferConcept = constructTypedBufferConceptDecl(*SemaPtr, HLSLNamespace); Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "RWBuffer") - .addSimpleTemplateParams(*SemaPtr, {"element_type"}, - TypedBufferConcept) + .addSimpleTemplateParams({"element_type"}, TypedBufferConcept) .Record; onCompletion(Decl, [this](CXXRecordDecl *Decl) { @@ -958,6 +957,5 @@ static FunctionDecl *lookupBuiltinFunction(Sema &S, StringRef Name) { // this assert *will* fail. Should this call LookupBuiltin instead? assert(R.isSingleResult() && "Since this is a builtin it should always resolve!"); - assert(isa(R.getFoundDecl())); return cast(R.getFoundDecl()); } From 00ad0324176e6ac5bc0dd05e5a86c703183dcbda Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Fri, 22 Nov 2024 22:51:36 -0800 Subject: [PATCH 14/15] Rename S to SemaRef & clang-format --- clang/lib/Sema/HLSLExternalSemaSource.cpp | 57 ++++++++++++----------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index 6a06b60f3b2ab..fcc74a2e8e71b 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -36,26 +36,28 @@ namespace { struct TemplateParameterListBuilder; struct BuiltinTypeDeclBuilder { - Sema &S; + Sema &SemaRef; CXXRecordDecl *Record = nullptr; ClassTemplateDecl *Template = nullptr; ClassTemplateDecl *PrevTemplate = nullptr; NamespaceDecl *HLSLNamespace = nullptr; llvm::StringMap Fields; - BuiltinTypeDeclBuilder(Sema &S, CXXRecordDecl *R) : S(S), Record(R) { + BuiltinTypeDeclBuilder(Sema &SemaRef, CXXRecordDecl *R) + : SemaRef(SemaRef), Record(R) { Record->startDefinition(); Template = Record->getDescribedClassTemplate(); } - BuiltinTypeDeclBuilder(Sema &S, NamespaceDecl *Namespace, StringRef Name) - : S(S), HLSLNamespace(Namespace) { - ASTContext &AST = S.getASTContext(); + BuiltinTypeDeclBuilder(Sema &SemaRef, NamespaceDecl *Namespace, + StringRef Name) + : SemaRef(SemaRef), HLSLNamespace(Namespace) { + ASTContext &AST = SemaRef.getASTContext(); IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier); - LookupResult Result(S, &II, SourceLocation(), Sema::LookupTagName); + LookupResult Result(SemaRef, &II, SourceLocation(), Sema::LookupTagName); CXXRecordDecl *PrevDecl = nullptr; - if (S.LookupQualifiedName(Result, HLSLNamespace)) { + if (SemaRef.LookupQualifiedName(Result, HLSLNamespace)) { // Declaration already exists (from precompiled headers) NamedDecl *Found = Result.getFoundDecl(); if (auto *TD = dyn_cast(Found)) { @@ -120,7 +122,7 @@ struct BuiltinTypeDeclBuilder { AccessSpecifier Access = AccessSpecifier::AS_private) { assert(!Record->isCompleteDefinition() && "record is already complete"); - ASTContext &Ctx = S.getASTContext(); + ASTContext &Ctx = SemaRef.getASTContext(); TypeSourceInfo *ElementTypeInfo = nullptr; QualType ElemTy = Ctx.Char8Ty; @@ -138,7 +140,7 @@ struct BuiltinTypeDeclBuilder { ? HLSLContainedTypeAttr::CreateImplicit(Ctx, ElementTypeInfo) : nullptr}; Attr *ResourceAttr = HLSLResourceAttr::CreateImplicit(Ctx, RK); - if (CreateHLSLAttributedResourceType(S, Ctx.HLSLResourceTy, Attrs, + if (CreateHLSLAttributedResourceType(SemaRef, Ctx.HLSLResourceTy, Attrs, AttributedResTy)) addMemberVariable("__handle", AttributedResTy, {ResourceAttr}, Access); return *this; @@ -279,7 +281,7 @@ struct BuiltinTypeDeclBuilder { } Expr *getConstantIntExpr(int value) { - ASTContext &AST = S.getASTContext(); + ASTContext &AST = SemaRef.getASTContext(); return IntegerLiteral::Create( AST, llvm::APInt(AST.getTypeSize(AST.IntTy), value, true), AST.IntTy, SourceLocation()); @@ -306,7 +308,7 @@ struct TemplateParameterListBuilder { addTypeParameter(StringRef Name, QualType DefaultValue = QualType()) { assert(!Builder.Record->isCompleteDefinition() && "record is already complete"); - ASTContext &AST = Builder.S.getASTContext(); + ASTContext &AST = Builder.SemaRef.getASTContext(); unsigned Position = static_cast(Params.size()); auto *Decl = TemplateTypeParmDecl::Create( AST, Builder.Record->getDeclContext(), SourceLocation(), @@ -316,9 +318,9 @@ struct TemplateParameterListBuilder { /* ParameterPack */ false, /* HasTypeConstraint*/ false); if (!DefaultValue.isNull()) - Decl->setDefaultArgument( - AST, Builder.S.getTrivialTemplateArgumentLoc(DefaultValue, QualType(), - SourceLocation())); + Decl->setDefaultArgument(AST, + Builder.SemaRef.getTrivialTemplateArgumentLoc( + DefaultValue, QualType(), SourceLocation())); Params.emplace_back(Decl); return *this; @@ -421,12 +423,11 @@ struct TemplateParameterListBuilder { if (Params.empty()) return Builder; - ASTContext &AST = Builder.S.Context; + ASTContext &AST = Builder.SemaRef.Context; ConceptSpecializationExpr *CSE = - CD ? constructConceptSpecializationExpr(Builder.S, CD) : nullptr; - auto *ParamList = TemplateParameterList::Create(AST, SourceLocation(), - SourceLocation(), Params, - SourceLocation(), CSE); + CD ? constructConceptSpecializationExpr(Builder.SemaRef, CD) : nullptr; + auto *ParamList = TemplateParameterList::Create( + AST, SourceLocation(), SourceLocation(), Params, SourceLocation(), CSE); Builder.Template = ClassTemplateDecl::Create( AST, Builder.Record->getDeclContext(), SourceLocation(), DeclarationName(Builder.Record->getIdentifier()), ParamList, @@ -509,7 +510,7 @@ struct BuiltinTypeMethodBuilder { assert(Method == nullptr && "Method already created"); // create method type - ASTContext &AST = DeclBuilder.S.getASTContext(); + ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); SmallVector ParamTypes; for (MethodParam &MP : Params) ParamTypes.emplace_back(MP.Ty); @@ -554,7 +555,7 @@ struct BuiltinTypeMethodBuilder { if (!Method) createMethodDecl(); - ASTContext &AST = DeclBuilder.S.getASTContext(); + ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); CXXThisExpr *This = CXXThisExpr::Create( AST, SourceLocation(), Method->getFunctionObjectParameterType(), true); FieldDecl *HandleField = DeclBuilder.getResourceHandleField(); @@ -572,8 +573,8 @@ struct BuiltinTypeMethodBuilder { if (!Method) createMethodDecl(); - ASTContext &AST = DeclBuilder.S.getASTContext(); - FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.S, BuiltinName); + ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); + FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.SemaRef, BuiltinName); DeclRefExpr *DRE = DeclRefExpr::Create( AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false, FD->getNameInfo(), FD->getType(), VK_PRValue); @@ -600,7 +601,7 @@ struct BuiltinTypeMethodBuilder { "method decl not created; are you missing a call to build the body?"); if (!Method->hasBody()) { - ASTContext &AST = DeclBuilder.S.getASTContext(); + ASTContext &AST = DeclBuilder.SemaRef.getASTContext(); assert((ReturnTy == AST.VoidTy || !StmtsList.empty()) && "nothing to return from non-void method"); if (ReturnTy != AST.VoidTy) { @@ -655,16 +656,16 @@ BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef Names, } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() { - return BuiltinTypeMethodBuilder(S, *this, "IncrementCounter", - S.getASTContext().UnsignedIntTy) + return BuiltinTypeMethodBuilder(SemaRef, *this, "IncrementCounter", + SemaRef.getASTContext().UnsignedIntTy) .callBuiltin("__builtin_hlsl_buffer_update_counter", {getConstantIntExpr(1)}) .finalizeMethod(); } BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() { - return BuiltinTypeMethodBuilder(S, *this, "DecrementCounter", - S.getASTContext().UnsignedIntTy) + return BuiltinTypeMethodBuilder(SemaRef, *this, "DecrementCounter", + SemaRef.getASTContext().UnsignedIntTy) .callBuiltin("__builtin_hlsl_buffer_update_counter", {getConstantIntExpr(-1)}) .finalizeMethod(); From 898769726cad7ab7dc77c4d4e45e915476c625e4 Mon Sep 17 00:00:00 2001 From: Helena Kotas Date: Mon, 25 Nov 2024 10:49:45 -0800 Subject: [PATCH 15/15] Use std::abs and update test --- clang/lib/Sema/SemaHLSL.cpp | 2 +- .../DirectX/{updateCounter.ll => bufferUpdateCounter.ll} | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename llvm/test/CodeGen/DirectX/{updateCounter.ll => bufferUpdateCounter.ll} (86%) diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 289d9dc0f1130..8bdacd6ded165 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -2222,7 +2222,7 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { Expr *OffsetExpr = TheCall->getArg(1); std::optional Offset = OffsetExpr->getIntegerConstantExpr(SemaRef.getASTContext()); - if (!Offset.has_value() || abs(Offset->getExtValue()) != 1) { + if (!Offset.has_value() || std::abs(Offset->getExtValue()) != 1) { SemaRef.Diag(TheCall->getArg(1)->getBeginLoc(), diag::err_hlsl_expect_arg_const_int_one_or_neg_one) << 1; diff --git a/llvm/test/CodeGen/DirectX/updateCounter.ll b/llvm/test/CodeGen/DirectX/bufferUpdateCounter.ll similarity index 86% rename from llvm/test/CodeGen/DirectX/updateCounter.ll rename to llvm/test/CodeGen/DirectX/bufferUpdateCounter.ll index 6bfb4d8670f55..3f2610649cba1 100644 --- a/llvm/test/CodeGen/DirectX/updateCounter.ll +++ b/llvm/test/CodeGen/DirectX/bufferUpdateCounter.ll @@ -12,7 +12,7 @@ define void @update_counter_decrement_vector() { ; CHECK-NEXT: [[BUFFANOT:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BIND]] ; CHECK-NEXT: [[REG:%.*]] = call i32 @dx.op.bufferUpdateCounter(i32 70, %dx.types.Handle [[BUFFANOT]], i8 -1) - %1 = call i32 @llvm.dx.updateCounter(target("dx.TypedBuffer", <4 x float>, 0, 0, 0) %buffer, i8 -1) + %1 = call i32 @llvm.dx.bufferUpdateCounter(target("dx.TypedBuffer", <4 x float>, 0, 0, 0) %buffer, i8 -1) ret void } @@ -24,7 +24,7 @@ define void @update_counter_increment_vector() { i32 0, i32 0, i32 1, i32 0, i1 false) ; CHECK-NEXT: [[BUFFANOT:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BIND]] ; CHECK-NEXT: [[REG:%.*]] = call i32 @dx.op.bufferUpdateCounter(i32 70, %dx.types.Handle [[BUFFANOT]], i8 1) - %1 = call i32 @llvm.dx.updateCounter(target("dx.TypedBuffer", <4 x float>, 0, 0, 0) %buffer, i8 1) + %1 = call i32 @llvm.dx.bufferUpdateCounter(target("dx.TypedBuffer", <4 x float>, 0, 0, 0) %buffer, i8 1) ret void } @@ -36,6 +36,6 @@ define void @update_counter_decrement_scalar() { i32 1, i32 8, i32 1, i32 0, i1 false) ; CHECK-NEXT: [[BUFFANOT:%.*]] = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle [[BIND]] ; CHECK-NEXT: [[REG:%.*]] = call i32 @dx.op.bufferUpdateCounter(i32 70, %dx.types.Handle [[BUFFANOT]], i8 -1) - %1 = call i32 @llvm.dx.updateCounter(target("dx.RawBuffer", i8, 0, 0) %buffer, i8 -1) + %1 = call i32 @llvm.dx.bufferUpdateCounter(target("dx.RawBuffer", i8, 0, 0) %buffer, i8 -1) ret void }