[clang] [llvm] [HLSL] Add `Increment`/`DecrementCounter` methods to structured buffers (PR #114148)

Helena Kotas via cfe-commits cfe-commits at lists.llvm.org
Thu Nov 14 00:29:16 PST 2024


https://github.com/hekota updated https://github.com/llvm/llvm-project/pull/114148

>From 8c76f28c8a0ba3d087361141366968071fa3af6e Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Tue, 29 Oct 2024 16:02:26 -0700
Subject: [PATCH 1/8] [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 90475a361bb8f8..72bc2d5e7df23e 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 8e4718008ece72..2aea6bb657578a 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 e2d03eff8ab4a0..71273de3400b17 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<Value *>{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<Value *>{ResHandle, Offset}, nullptr);
+  }
   }
   return nullptr;
 }
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index ff7df41b5c62e7..aac93dfc373ed4 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 ce8564429b3802..24c3954b134c5f 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 <functional>
 
 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<TemplateTypeParmDecl>(
-              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<ValueDecl>(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<TemplateTypeParmDecl>(
-              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<TemplateTypeParmDecl>(
+              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<StringRef> 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<MethodParam> Params;
+  llvm::SmallVector<Stmt *> 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<QualType> 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<ParmVarDecl *> ParmDecls;
+    auto FnProtoLoc =
+        Method->getTypeSourceInfo()->getTypeLoc().getAs<FunctionProtoTypeLoc>();
+    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<Expr *> &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<Expr *> CallParms,
+              bool AddResourceHandleAsFirstArg = true) {
+    if (!Method)
+      createMethodDecl();
+
+    ASTContext &AST = S.getASTContext();
+    DeclRefExpr *Fn = lookupBuiltinFunction(S, BuiltinName);
+    Expr *Call = nullptr;
+
+    if (AddResourceHandleAsFirstArg) {
+      SmallVector<Expr *> 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<Expr>(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<ValueDecl>(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 ff6616901016ab..770bd4a81633e1 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 1f6c5b8d4561bc..1b7f0456a3e82a 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<llvm::APSInt> 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 00000000000000..c8ff5d3cd905fb
--- /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<float> RWSB1 : register(u0);
+RWStructuredBuffer<float> 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 00000000000000..fe9e9cfdcb8736
--- /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<float> RWSB1, RWSB2;
+RasterizerOrderedStructuredBuffer<float> 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 00000000000000..11b8cebc1aeb42
--- /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 at +1 {{too few arguments to function call, expected 2, have 1}}
+  __builtin_hlsl_buffer_update_counter(x);
+
+  // expected-error at +1 {{too many arguments to function call, expected 2, have 3}}
+  __builtin_hlsl_buffer_update_counter(x, x, x);
+
+  // expected-error at +1 {{used type 'int' where __hlsl_resource_t is required}}
+  __builtin_hlsl_buffer_update_counter(x, x);
+
+  // expected-error at +1 {{argument 1 must be constant integer 1 or -1}}
+  __builtin_hlsl_buffer_update_counter(res, x);
+
+  // expected-error at +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 e30d37f69f781e..75e70657084bd0 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 6df2eb156a0774..8978877feb69d6 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 <hekotas at microsoft.com>
Date: Thu, 31 Oct 2024 20:49:45 -0700
Subject: [PATCH 2/8] 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 24c3954b134c5f..10814f10cfeb6e 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<FieldDecl *> 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<TemplateTypeParmDecl>(
-              Template->getTemplateParameters()->getParam(0))) {
-        return QualType(TTD->getTypeForDecl(), 0);
-      }
+    assert(Template && "record it not a template");
+    if (const auto *TTD = dyn_cast<TemplateTypeParmDecl>(
+            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<StringRef> Names);
+  TemplateParameterListBuilder addTemplateArgumentList();
+  BuiltinTypeDeclBuilder &addSimpleTemplateParams(ArrayRef<StringRef> Names);
 
   // Builtin types methods
-  BuiltinTypeDeclBuilder &addIncrementCounterMethod(Sema &S);
-  BuiltinTypeDeclBuilder &addDecrementCounterMethod(Sema &S);
+  BuiltinTypeDeclBuilder &addIncrementCounterMethod();
+  BuiltinTypeDeclBuilder &addDecrementCounterMethod();
 };
 
 struct TemplateParameterListBuilder {
   BuiltinTypeDeclBuilder &Builder;
-  Sema &S;
   llvm::SmallVector<NamedDecl *> 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<unsigned>(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<QualType> ParamTypes;
     for (auto &MP : Params)
       ParamTypes.emplace_back(MP.Ty);
@@ -451,7 +450,7 @@ struct BuiltinTypeMethodBuilder {
   }
 
   void addResourceHandleToParms(SmallVector<Expr *> &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<Expr *> NewCallParms;
     if (AddResourceHandleAsFirstArg) {
-      SmallVector<Expr *> 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<Expr>(StmtsList.back())) {
+          assert(AST.hasSameUnqualifiedType(
+                     isa<CallExpr>(LastExpr)
+                         ? cast<CallExpr>(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<StringRef> Names) {
-  TemplateParameterListBuilder Builder = this->addTemplateArgumentList(S);
+BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> 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<ValueDecl>(R.getFoundDecl());
-  QualType Ty = VD->getType();
-  return DeclRefExpr::Create(S.getASTContext(), NestedNameSpecifierLoc(),
-                             SourceLocation(), VD, false, NameInfo, Ty,
-                             VK_PRValue);
+  assert(isa<FunctionDecl>(R.getFoundDecl()));
+  return cast<FunctionDecl>(R.getFoundDecl());
 }
diff --git a/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl b/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl
index f95d74b30acded..fd283b1af0226f 100644
--- a/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl
+++ b/clang/test/AST/HLSL/RWStructuredBuffer-AST.hlsl
@@ -52,6 +52,32 @@ RWStructuredBuffer<int> Buffer;
 // CHECK-NEXT: CXXThisExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'RWStructuredBuffer<element_type>' lvalue implicit this
 // CHECK-NEXT: AlwaysInlineAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit always_inline
 
+// CHECK-NEXT: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> IncrementCounter 'unsigned int ()'
+// CHECK-NEXT: CompoundStmt 0x{{[0-9A-Fa-f]+}} <<invalid sloc>>
+// CHECK-NEXT: ReturnStmt 0x{{[0-9A-Fa-f]+}} <<invalid sloc>>
+// CHECK-NEXT: CallExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'unsigned int'
+// CHECK-NEXT: DeclRefExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> '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]+}} <<invalid sloc>> '__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]+}} <<invalid sloc>> 'RWStructuredBuffer<element_type>' lvalue implicit this
+// CHECK-NEXT: IntegerLiteral 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'int' 1
+// CHECK-NEXT: AlwaysInlineAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit always_inline
+
+// CHECK-NEXT: CXXMethodDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> DecrementCounter 'unsigned int ()'
+// CHECK-NEXT: CompoundStmt 0x{{[0-9A-Fa-f]+}} <<invalid sloc>>
+// CHECK-NEXT: ReturnStmt 0x{{[0-9A-Fa-f]+}} <<invalid sloc>>
+// CHECK-NEXT: CallExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'unsigned int'
+// CHECK-NEXT: DeclRefExpr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> '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]+}} <<invalid sloc>> '__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]+}} <<invalid sloc>> 'RWStructuredBuffer<element_type>' lvalue implicit this
+// CHECK-NEXT: IntegerLiteral 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> 'int' -1
+// CHECK-NEXT: AlwaysInlineAttr 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> Implicit always_inline
+
 // CHECK: ClassTemplateSpecializationDecl 0x{{[0-9A-Fa-f]+}} <<invalid sloc>> <invalid sloc> class RWStructuredBuffer definition
 
 // CHECK: TemplateArgument type 'int'

>From 0f4c61f3ab51c8e614e921e43827f0c28be74685 Mon Sep 17 00:00:00 2001
From: Helena Kotas <hekotas at microsoft.com>
Date: Mon, 4 Nov 2024 16:13:03 -0800
Subject: [PATCH 3/8] 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 10814f10cfeb6e..52f1ec3bd39e3c 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<ClassTemplateDecl>(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<Attr *> 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<unsigned>(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<StringRef> 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 <hekotas at microsoft.com>
Date: Mon, 4 Nov 2024 16:17:23 -0800
Subject: [PATCH 4/8] 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 52f1ec3bd39e3c..b64efb481146d4 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<unsigned>(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<StringRef> 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 <hekotas at microsoft.com>
Date: Tue, 5 Nov 2024 11:59:44 -0800
Subject: [PATCH 5/8] 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 b64efb481146d4..45302f86d4d724 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -459,6 +459,8 @@ struct BuiltinTypeMethodBuilder {
   BuiltinTypeMethodBuilder &
   callBuiltin(StringRef BuiltinName, ArrayRef<Expr *> 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 <hekotas at microsoft.com>
Date: Wed, 6 Nov 2024 16:13:01 -0800
Subject: [PATCH 6/8] 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 00000000000000..b64efb481146d4
--- /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 <functional>
+
+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<FieldDecl *> 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<ClassTemplateDecl>(Found)) {
+        PrevDecl = TD->getTemplatedDecl();
+        PrevTemplate = TD;
+      } else
+        PrevDecl = dyn_cast<CXXRecordDecl>(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<Attr *> 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<const Attr *> 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<uint8_t>(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<FunctionProtoTypeLoc>();
+    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<TemplateTypeParmDecl>(
+            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<StringRef> Names);
+
+  // Builtin types methods
+  BuiltinTypeDeclBuilder &addIncrementCounterMethod();
+  BuiltinTypeDeclBuilder &addDecrementCounterMethod();
+};
+
+struct TemplateParameterListBuilder {
+  BuiltinTypeDeclBuilder &Builder;
+  llvm::SmallVector<NamedDecl *> 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<unsigned>(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<MethodParam> Params;
+  llvm::SmallVector<Stmt *> 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<QualType> 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<ParmVarDecl *> ParmDecls;
+    auto FnProtoLoc =
+        Method->getTypeSourceInfo()->getTypeLoc().getAs<FunctionProtoTypeLoc>();
+    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<Expr *> &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<Expr *> 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<Expr *> 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<Expr>(StmtsList.back())) {
+          assert(AST.hasSameUnqualifiedType(
+                     isa<CallExpr>(LastExpr)
+                         ? cast<CallExpr>(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<StringRef> 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<NamespaceDecl>();
+  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<NamedDecl *> 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<CXXRecordDecl>(Tag))
+    return;
+  auto Record = cast<CXXRecordDecl>(Tag);
+
+  // If this is a specialization, we need to get the underlying templated
+  // declaration and complete that.
+  if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(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<FunctionDecl>(R.getFoundDecl()));
+  return cast<FunctionDecl>(R.getFoundDecl());
+}
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index ad1c6b2bcf953b..4df69d54141ae6 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -534,9 +534,9 @@ BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> 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 c8ff5d3cd905fb..6cfa133f6f2895 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 fe9e9cfdcb8736..b193f17484c1c3 100644
--- a/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl
+++ b/clang/test/CodeGenHLSL/builtins/StructuredBuffers-methods-ps.hlsl
@@ -9,21 +9,20 @@ RasterizerOrderedStructuredBuffer<float> 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 <hekotas at microsoft.com>
Date: Thu, 7 Nov 2024 14:24:13 -0800
Subject: [PATCH 7/8] 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 b64efb481146d4..00000000000000
--- 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 <functional>
-
-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<FieldDecl *> 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<ClassTemplateDecl>(Found)) {
-        PrevDecl = TD->getTemplatedDecl();
-        PrevTemplate = TD;
-      } else
-        PrevDecl = dyn_cast<CXXRecordDecl>(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<Attr *> 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<const Attr *> 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<uint8_t>(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<FunctionProtoTypeLoc>();
-    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<TemplateTypeParmDecl>(
-            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<StringRef> Names);
-
-  // Builtin types methods
-  BuiltinTypeDeclBuilder &addIncrementCounterMethod();
-  BuiltinTypeDeclBuilder &addDecrementCounterMethod();
-};
-
-struct TemplateParameterListBuilder {
-  BuiltinTypeDeclBuilder &Builder;
-  llvm::SmallVector<NamedDecl *> 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<unsigned>(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<MethodParam> Params;
-  llvm::SmallVector<Stmt *> 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<QualType> 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<ParmVarDecl *> ParmDecls;
-    auto FnProtoLoc =
-        Method->getTypeSourceInfo()->getTypeLoc().getAs<FunctionProtoTypeLoc>();
-    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<Expr *> &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<Expr *> 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<Expr *> 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<Expr>(StmtsList.back())) {
-          assert(AST.hasSameUnqualifiedType(
-                     isa<CallExpr>(LastExpr)
-                         ? cast<CallExpr>(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<StringRef> 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<NamespaceDecl>();
-  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<NamedDecl *> 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<CXXRecordDecl>(Tag))
-    return;
-  auto Record = cast<CXXRecordDecl>(Tag);
-
-  // If this is a specialization, we need to get the underlying templated
-  // declaration and complete that.
-  if (auto TDecl = dyn_cast<ClassTemplateSpecializationDecl>(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<FunctionDecl>(R.getFoundDecl()));
-  return cast<FunctionDecl>(R.getFoundDecl());
-}
diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td
index 0f0069153c32eb..236a6b7a87ea38 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 <hekotas at microsoft.com>
Date: Thu, 14 Nov 2024 00:28:40 -0800
Subject: [PATCH 8/8] 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 4df69d54141ae6..a6ba89a0dbb8bd 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<StringRef> 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<Expr *> &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<Expr *> 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<Expr *> NewCallParms;
     if (AddResourceHandleAsFirstArg) {
-      addResourceHandleToParms(NewCallParms);
+      NewCallParms.push_back(getResourceHandleExpr());
       for (auto *P : CallParms)
         NewCallParms.push_back(P);
     }
@@ -533,24 +554,18 @@ BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> 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();
 }
 



More information about the cfe-commits mailing list