Skip to content

[HLSL][NFC] Refactor HLSLExternalSemaSource #131032

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 24, 2025

Conversation

hekota
Copy link
Member

@hekota hekota commented Mar 12, 2025

Moving builder classes into separate files HLSLBuiltinTypeDeclBuilder.cpp/.h, changing a some HLSLExternalSemaSource methods to private and removing unused methods.

This is a prep work before we start adding more builtin types and methods, like textures, resource constructors or matrices. For example constructors could make use of the BuiltinTypeMethodBuilder, but this helper class was defined in HLSLExternalSemaSource.cpp after the method that creates a constructor. Rather than reshuffling the code one big source file I am moving the builders into a separate cpp & header file and placing the helper classes declarations up top.

Currently the new header only exposes BuiltinTypeDeclBuilder to HLSLExternalSemaSource. In the future but we might decide to expose more helper classes as needed.

Moving builder classes into separate files `HLSLBuiltinTypeDeclBuilder.cpp`/`.h` and changing a some `HLSLExternalSemaSource` methods to private.

This is a prep work before we start adding more builtin types and methods, like textures or resource constructors. For example constructors could make use of the `BuiltinTypeMethodBuilder`, but this helper class was defined in `HLSLExternalSemaSource.cpp` after the method that creates a constructor. Rather than reshuffling the code one big source file I am moving the builders into a separate cpp & header file and placing the helper classes declarations up top.

Currently the new header only exposes `BuiltinTypeDeclBuilder` to `HLSLExternalSemaSource`. In the future but we might decide to expose more helper classes as needed.
Copy link

github-actions bot commented Mar 12, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@hekota hekota changed the title [HLSL][NFC] Refactoring HLSLExternalSemaSource [HLSL][NFC] Refactor HLSLExternalSemaSource Mar 12, 2025
@hekota hekota marked this pull request as ready for review March 14, 2025 02:15
@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" HLSL HLSL Language Support labels Mar 14, 2025
@llvmbot
Copy link
Member

llvmbot commented Mar 14, 2025

@llvm/pr-subscribers-clang

Author: Helena Kotas (hekota)

Changes

Moving builder classes into separate files HLSLBuiltinTypeDeclBuilder.cpp/.h, changing a some HLSLExternalSemaSource methods to private and removing unused methods.

This is a prep work before we start adding more builtin types and methods, like textures, resource constructors or matrices. For example constructors could make use of the BuiltinTypeMethodBuilder, but this helper class was defined in HLSLExternalSemaSource.cpp after the method that creates a constructor. Rather than reshuffling the code one big source file I am moving the builders into a separate cpp & header file and placing the helper classes declarations up top.

Currently the new header only exposes BuiltinTypeDeclBuilder to HLSLExternalSemaSource. In the future but we might decide to expose more helper classes as needed.


Patch is 67.86 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/131032.diff

5 Files Affected:

  • (modified) clang/include/clang/Sema/HLSLExternalSemaSource.h (+8-7)
  • (modified) clang/lib/Sema/CMakeLists.txt (+1)
  • (added) clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp (+782)
  • (added) clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h (+96)
  • (modified) clang/lib/Sema/HLSLExternalSemaSource.cpp (+2-748)
diff --git a/clang/include/clang/Sema/HLSLExternalSemaSource.h b/clang/include/clang/Sema/HLSLExternalSemaSource.h
index 3c7495e66055d..9c1b16ba3950c 100644
--- a/clang/include/clang/Sema/HLSLExternalSemaSource.h
+++ b/clang/include/clang/Sema/HLSLExternalSemaSource.h
@@ -21,20 +21,15 @@ class NamespaceDecl;
 class Sema;
 
 class HLSLExternalSemaSource : public ExternalSemaSource {
+private:
   Sema *SemaPtr = nullptr;
   NamespaceDecl *HLSLNamespace = nullptr;
 
   using CompletionFunction = std::function<void(CXXRecordDecl *)>;
   llvm::DenseMap<CXXRecordDecl *, CompletionFunction> Completions;
 
-  void defineHLSLVectorAlias();
-  void defineTrivialHLSLTypes();
-  void defineHLSLTypesWithForwardDeclarations();
-
-  void onCompletion(CXXRecordDecl *Record, CompletionFunction Fn);
-
 public:
-  ~HLSLExternalSemaSource() override;
+  ~HLSLExternalSemaSource() override {}
 
   /// Initialize the semantic source with the Sema instance
   /// being used to perform semantic analysis on the abstract syntax
@@ -47,6 +42,12 @@ class HLSLExternalSemaSource : public ExternalSemaSource {
   using ExternalASTSource::CompleteType;
   /// Complete an incomplete HLSL builtin type
   void CompleteType(TagDecl *Tag) override;
+
+private:
+  void defineTrivialHLSLTypes();
+  void defineHLSLVectorAlias();
+  void defineHLSLTypesWithForwardDeclarations();
+  void onCompletion(CXXRecordDecl *Record, CompletionFunction Fn);
 };
 
 } // namespace clang
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 1a351684d133e..d3fe80f659f69 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -20,6 +20,7 @@ add_clang_library(clangSema
   DeclSpec.cpp
   DelayedDiagnostic.cpp
   HeuristicResolver.cpp
+  HLSLBuiltinTypeDeclBuilder.cpp
   HLSLExternalSemaSource.cpp
   IdentifierResolver.cpp
   JumpDiagnostics.cpp
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
new file mode 100644
index 0000000000000..db0ed3434d837
--- /dev/null
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -0,0 +1,782 @@
+//===--- HLSLBuiltinTypeDeclBuilder.cpp - HLSL Builtin Type Decl Builder --===//
+//
+// 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 "HLSLBuiltinTypeDeclBuilder.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/SourceLocation.h"
+#include "clang/Sema/Lookup.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaHLSL.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace llvm::hlsl;
+
+namespace clang {
+
+namespace hlsl {
+
+namespace {
+
+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!");
+  return cast<FunctionDecl>(R.getFoundDecl());
+}
+} // namespace
+
+// Builder for template arguments of builtin types. Used internally
+// by BuiltinTypeDeclBuilder.
+struct TemplateParameterListBuilder {
+  BuiltinTypeDeclBuilder &Builder;
+  llvm::SmallVector<NamedDecl *> Params;
+
+  TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) : Builder(RB) {}
+  ~TemplateParameterListBuilder();
+
+  TemplateParameterListBuilder &
+  addTypeParameter(StringRef Name, QualType DefaultValue = QualType());
+
+  ConceptSpecializationExpr *
+  constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD);
+
+  BuiltinTypeDeclBuilder &finalizeTemplateArgs(ConceptDecl *CD = nullptr);
+};
+
+// Builder for methods of builtin types. Allows adding methods to builtin types
+// using the builder pattern like this:
+//
+//   BuiltinTypeMethodBuilder(RecordBuilder, "MethodName", ReturnType)
+//       .addParam("param_name", Type, InOutModifier)
+//       .callBuiltin("builtin_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 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 accepts constants via `Expr *` or placeholder
+// value arguments to indicate which function arguments to forward to the
+// builtin.
+//
+// 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 {
+private:
+  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;
+  bool IsConst;
+  llvm::SmallVector<MethodParam> Params;
+  llvm::SmallVector<Stmt *> StmtsList;
+
+  // Argument placeholders, inspired by std::placeholder. These are the indices
+  // of arguments to forward to `callBuiltin` and other method builder methods.
+  // Additional special values are:
+  //   Handle   - refers to the resource handle.
+  //   LastStmt - refers to the last statement in the method body; referencing
+  //              LastStmt will remove the statement from the method body since
+  //              it will be linked from the new expression being constructed.
+  enum class PlaceHolder { _0, _1, _2, _3, Handle = 128, LastStmt };
+
+  Expr *convertPlaceholder(PlaceHolder PH);
+  Expr *convertPlaceholder(Expr *E) { return E; }
+
+public:
+  friend BuiltinTypeDeclBuilder;
+
+  BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, DeclarationName &Name,
+                           QualType ReturnTy, bool IsConst = false)
+      : DeclBuilder(DB), NameInfo(DeclarationNameInfo(Name, SourceLocation())),
+        ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) {}
+
+  BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, StringRef Name,
+                           QualType ReturnTy, bool IsConst = false);
+  BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete;
+
+  ~BuiltinTypeMethodBuilder() { finalizeMethod(); }
+
+  BuiltinTypeMethodBuilder &
+  operator=(const BuiltinTypeMethodBuilder &Other) = delete;
+
+  BuiltinTypeMethodBuilder &addParam(StringRef Name, QualType Ty,
+                                     HLSLParamModifierAttr::Spelling Modifier =
+                                         HLSLParamModifierAttr::Keyword_in);
+  template <typename... Ts>
+  BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName,
+                                        QualType ReturnType, Ts... ArgSpecs);
+  template <typename TLHS, typename TRHS>
+  BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS);
+  template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr);
+  BuiltinTypeDeclBuilder &finalizeMethod();
+  Expr *getResourceHandleExpr();
+
+private:
+  void createMethodDecl();
+};
+
+TemplateParameterListBuilder::~TemplateParameterListBuilder() {
+  finalizeTemplateArgs();
+}
+
+TemplateParameterListBuilder &
+TemplateParameterListBuilder::addTypeParameter(StringRef Name,
+                                               QualType DefaultValue) {
+  assert(!Builder.Record->isCompleteDefinition() &&
+         "record is already complete");
+  ASTContext &AST = Builder.SemaRef.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 */ true,
+      /* ParameterPack */ false,
+      /* HasTypeConstraint*/ false);
+  if (!DefaultValue.isNull())
+    Decl->setDefaultArgument(AST,
+                             Builder.SemaRef.getTrivialTemplateArgumentLoc(
+                                 DefaultValue, QualType(), SourceLocation()));
+
+  Params.emplace_back(Decl);
+  return *this;
+}
+
+// The concept specialization expression (CSE) constructed in
+// constructConceptSpecializationExpr is constructed so that it
+// matches the CSE that is constructed when parsing the below C++ code:
+//
+// template<typename T>
+// concept is_typed_resource_element_compatible =
+// __builtin_hlsl_typed_resource_element_compatible<T>
+//
+// template<typename element_type> requires
+// is_typed_resource_element_compatible<element_type>
+// struct RWBuffer {
+//     element_type Val;
+// };
+//
+// int fn() {
+//     RWBuffer<int> Buf;
+// }
+//
+// When dumping the AST and filtering for "RWBuffer", the resulting AST
+// structure is what we're trying to construct below, specifically the
+// CSE portion.
+ConceptSpecializationExpr *
+TemplateParameterListBuilder::constructConceptSpecializationExpr(
+    Sema &S, ConceptDecl *CD) {
+  ASTContext &Context = S.getASTContext();
+  SourceLocation Loc = Builder.Record->getBeginLoc();
+  DeclarationNameInfo DNI(CD->getDeclName(), Loc);
+  NestedNameSpecifierLoc NNSLoc;
+  DeclContext *DC = Builder.Record->getDeclContext();
+  TemplateArgumentListInfo TALI(Loc, Loc);
+
+  // Assume that the concept decl has just one template parameter
+  // This parameter should have been added when CD was constructed
+  // in getTypedBufferConceptDecl
+  assert(CD->getTemplateParameters()->size() == 1 &&
+         "unexpected concept decl parameter count");
+  TemplateTypeParmDecl *ConceptTTPD =
+      dyn_cast<TemplateTypeParmDecl>(CD->getTemplateParameters()->getParam(0));
+
+  // this TemplateTypeParmDecl is the template for the resource, and is
+  // used to construct a template argumentthat will be used
+  // to construct the ImplicitConceptSpecializationDecl
+  TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
+      Context,                          // AST context
+      Builder.Record->getDeclContext(), // DeclContext
+      SourceLocation(), SourceLocation(),
+      /*D=*/0,                    // Depth in the template parameter list
+      /*P=*/0,                    // Position in the template parameter list
+      /*Id=*/nullptr,             // Identifier for 'T'
+      /*Typename=*/true,          // Indicates this is a 'typename' or 'class'
+      /*ParameterPack=*/false,    // Not a parameter pack
+      /*HasTypeConstraint=*/false // Has no type constraint
+  );
+
+  T->setDeclContext(DC);
+
+  QualType ConceptTType = Context.getTypeDeclType(ConceptTTPD);
+
+  // this is the 2nd template argument node, on which
+  // the concept constraint is actually being applied: 'element_type'
+  TemplateArgument ConceptTA = TemplateArgument(ConceptTType);
+
+  QualType CSETType = Context.getTypeDeclType(T);
+
+  // this is the 1st template argument node, which represents
+  // the abstract type that a concept would refer to: 'T'
+  TemplateArgument CSETA = TemplateArgument(CSETType);
+
+  ImplicitConceptSpecializationDecl *ImplicitCSEDecl =
+      ImplicitConceptSpecializationDecl::Create(
+          Context, Builder.Record->getDeclContext(), Loc, {CSETA});
+
+  // Constraint satisfaction is used to construct the
+  // ConceptSpecailizationExpr, and represents the 2nd Template Argument,
+  // located at the bottom of the sample AST above.
+  const ConstraintSatisfaction CS(CD, {ConceptTA});
+  TemplateArgumentLoc TAL =
+      S.getTrivialTemplateArgumentLoc(ConceptTA, QualType(), SourceLocation());
+
+  TALI.addArgument(TAL);
+  const ASTTemplateArgumentListInfo *ATALI =
+      ASTTemplateArgumentListInfo::Create(Context, TALI);
+
+  // In the concept reference, ATALI is what adds the extra
+  // TemplateArgument node underneath CSE
+  ConceptReference *CR =
+      ConceptReference::Create(Context, NNSLoc, Loc, DNI, CD, CD, ATALI);
+
+  ConceptSpecializationExpr *CSE =
+      ConceptSpecializationExpr::Create(Context, CR, ImplicitCSEDecl, &CS);
+
+  return CSE;
+}
+
+BuiltinTypeDeclBuilder &
+TemplateParameterListBuilder::finalizeTemplateArgs(ConceptDecl *CD) {
+  if (Params.empty())
+    return Builder;
+
+  ASTContext &AST = Builder.SemaRef.Context;
+  ConceptSpecializationExpr *CSE =
+      CD ? constructConceptSpecializationExpr(Builder.SemaRef, CD) : nullptr;
+  auto *ParamList = TemplateParameterList::Create(
+      AST, SourceLocation(), SourceLocation(), Params, SourceLocation(), CSE);
+  Builder.Template = ClassTemplateDecl::Create(
+      AST, Builder.Record->getDeclContext(), SourceLocation(),
+      DeclarationName(Builder.Record->getIdentifier()), ParamList,
+      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;
+}
+
+Expr *BuiltinTypeMethodBuilder::convertPlaceholder(PlaceHolder PH) {
+  if (PH == PlaceHolder::Handle)
+    return getResourceHandleExpr();
+
+  if (PH == PlaceHolder::LastStmt) {
+    assert(!StmtsList.empty() && "no statements in the list");
+    Stmt *LastStmt = StmtsList.pop_back_val();
+    assert(isa<ValueStmt>(LastStmt) && "last statement does not have a value");
+    return cast<ValueStmt>(LastStmt)->getExprStmt();
+  }
+
+  ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+  ParmVarDecl *ParamDecl = Method->getParamDecl(static_cast<unsigned>(PH));
+  return DeclRefExpr::Create(
+      AST, NestedNameSpecifierLoc(), SourceLocation(), ParamDecl, false,
+      DeclarationNameInfo(ParamDecl->getDeclName(), SourceLocation()),
+      ParamDecl->getType(), VK_PRValue);
+}
+
+BuiltinTypeMethodBuilder::BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB,
+                                                   StringRef Name,
+                                                   QualType ReturnTy,
+                                                   bool IsConst)
+    : DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) {
+  const IdentifierInfo &II =
+      DB.SemaRef.getASTContext().Idents.get(Name, tok::TokenKind::identifier);
+  NameInfo = DeclarationNameInfo(DeclarationName(&II), SourceLocation());
+}
+
+BuiltinTypeMethodBuilder &
+BuiltinTypeMethodBuilder::addParam(StringRef Name, QualType Ty,
+                                   HLSLParamModifierAttr::Spelling Modifier) {
+  assert(Method == nullptr && "Cannot add param, method already created");
+  const IdentifierInfo &II = DeclBuilder.SemaRef.getASTContext().Idents.get(
+      Name, tok::TokenKind::identifier);
+  Params.emplace_back(II, Ty, Modifier);
+  return *this;
+}
+
+void BuiltinTypeMethodBuilder::createMethodDecl() {
+  assert(Method == nullptr && "Method already created");
+
+  // create method type
+  ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+  SmallVector<QualType> ParamTypes;
+  for (MethodParam &MP : Params)
+    ParamTypes.emplace_back(MP.Ty);
+
+  FunctionProtoType::ExtProtoInfo ExtInfo;
+  if (IsConst)
+    ExtInfo.TypeQuals.addConst();
+
+  QualType MethodTy = AST.getFunctionType(ReturnTy, ParamTypes, ExtInfo);
+
+  // 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>();
+  for (int I = 0, E = Params.size(); I != E; I++) {
+    MethodParam &MP = Params[I];
+    ParmVarDecl *Parm = ParmVarDecl::Create(
+        AST, Method->getDeclContext(), SourceLocation(), SourceLocation(),
+        &MP.NameII, MP.Ty,
+        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});
+}
+
+Expr *BuiltinTypeMethodBuilder::getResourceHandleExpr() {
+  // The first statement added to a method or access to 'this' creates the
+  // declaration.
+  if (!Method)
+    createMethodDecl();
+
+  ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+  CXXThisExpr *This = CXXThisExpr::Create(
+      AST, SourceLocation(), Method->getFunctionObjectParameterType(), true);
+  FieldDecl *HandleField = DeclBuilder.getResourceHandleField();
+  return MemberExpr::CreateImplicit(AST, This, false, HandleField,
+                                    HandleField->getType(), VK_LValue,
+                                    OK_Ordinary);
+}
+
+template <typename... Ts>
+BuiltinTypeMethodBuilder &
+BuiltinTypeMethodBuilder::callBuiltin(StringRef BuiltinName,
+                                      QualType ReturnType, Ts... ArgSpecs) {
+  std::array<Expr *, sizeof...(ArgSpecs)> Args{
+      convertPlaceholder(std::forward<Ts>(ArgSpecs))...};
+
+  // The first statement added to a method or access to 'this` creates the
+  // declaration.
+  if (!Method)
+    createMethodDecl();
+
+  ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+  FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.SemaRef, BuiltinName);
+  DeclRefExpr *DRE = DeclRefExpr::Create(
+      AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false,
+      FD->getNameInfo(), AST.BuiltinFnTy, VK_PRValue);
+
+  if (ReturnType.isNull())
+    ReturnType = FD->getReturnType();
+
+  Expr *Call = CallExpr::Create(AST, DRE, Args, ReturnType, VK_PRValue,
+                                SourceLocation(), FPOptionsOverride());
+  StmtsList.push_back(Call);
+  return *this;
+}
+
+template <typename TLHS, typename TRHS>
+BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::assign(TLHS LHS, TRHS RHS) {
+  Expr *LHSExpr = convertPlaceholder(LHS);
+  Expr *RHSExpr = convertPlaceholder(RHS);
+  Stmt *AssignStmt = BinaryOperator::Create(
+      DeclBuilder.SemaRef.getASTContext(), LHSExpr, RHSExpr, BO_Assign,
+      LHSExpr->getType(), ExprValueKind::VK_PRValue,
+      ExprObjectKind::OK_Ordinary, SourceLocation(), FPOptionsOverride());
+  StmtsList.push_back(AssignStmt);
+  return *this;
+}
+
+template <typename T>
+BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::dereference(T Ptr) {
+  Expr *PtrExpr = convertPlaceholder(Ptr);
+  Expr *Deref =
+      UnaryOperator::Create(DeclBuilder.SemaRef.getASTContext(), PtrExpr,
+                            UO_Deref, PtrExpr->getType()->getPointeeType(),
+                            VK_PRValue, OK_Ordinary, SourceLocation(),
+                            /*CanOverflow=*/false, FPOp...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Mar 14, 2025

@llvm/pr-subscribers-hlsl

Author: Helena Kotas (hekota)

Changes

Moving builder classes into separate files HLSLBuiltinTypeDeclBuilder.cpp/.h, changing a some HLSLExternalSemaSource methods to private and removing unused methods.

This is a prep work before we start adding more builtin types and methods, like textures, resource constructors or matrices. For example constructors could make use of the BuiltinTypeMethodBuilder, but this helper class was defined in HLSLExternalSemaSource.cpp after the method that creates a constructor. Rather than reshuffling the code one big source file I am moving the builders into a separate cpp & header file and placing the helper classes declarations up top.

Currently the new header only exposes BuiltinTypeDeclBuilder to HLSLExternalSemaSource. In the future but we might decide to expose more helper classes as needed.


Patch is 67.86 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/131032.diff

5 Files Affected:

  • (modified) clang/include/clang/Sema/HLSLExternalSemaSource.h (+8-7)
  • (modified) clang/lib/Sema/CMakeLists.txt (+1)
  • (added) clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp (+782)
  • (added) clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h (+96)
  • (modified) clang/lib/Sema/HLSLExternalSemaSource.cpp (+2-748)
diff --git a/clang/include/clang/Sema/HLSLExternalSemaSource.h b/clang/include/clang/Sema/HLSLExternalSemaSource.h
index 3c7495e66055d..9c1b16ba3950c 100644
--- a/clang/include/clang/Sema/HLSLExternalSemaSource.h
+++ b/clang/include/clang/Sema/HLSLExternalSemaSource.h
@@ -21,20 +21,15 @@ class NamespaceDecl;
 class Sema;
 
 class HLSLExternalSemaSource : public ExternalSemaSource {
+private:
   Sema *SemaPtr = nullptr;
   NamespaceDecl *HLSLNamespace = nullptr;
 
   using CompletionFunction = std::function<void(CXXRecordDecl *)>;
   llvm::DenseMap<CXXRecordDecl *, CompletionFunction> Completions;
 
-  void defineHLSLVectorAlias();
-  void defineTrivialHLSLTypes();
-  void defineHLSLTypesWithForwardDeclarations();
-
-  void onCompletion(CXXRecordDecl *Record, CompletionFunction Fn);
-
 public:
-  ~HLSLExternalSemaSource() override;
+  ~HLSLExternalSemaSource() override {}
 
   /// Initialize the semantic source with the Sema instance
   /// being used to perform semantic analysis on the abstract syntax
@@ -47,6 +42,12 @@ class HLSLExternalSemaSource : public ExternalSemaSource {
   using ExternalASTSource::CompleteType;
   /// Complete an incomplete HLSL builtin type
   void CompleteType(TagDecl *Tag) override;
+
+private:
+  void defineTrivialHLSLTypes();
+  void defineHLSLVectorAlias();
+  void defineHLSLTypesWithForwardDeclarations();
+  void onCompletion(CXXRecordDecl *Record, CompletionFunction Fn);
 };
 
 } // namespace clang
diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt
index 1a351684d133e..d3fe80f659f69 100644
--- a/clang/lib/Sema/CMakeLists.txt
+++ b/clang/lib/Sema/CMakeLists.txt
@@ -20,6 +20,7 @@ add_clang_library(clangSema
   DeclSpec.cpp
   DelayedDiagnostic.cpp
   HeuristicResolver.cpp
+  HLSLBuiltinTypeDeclBuilder.cpp
   HLSLExternalSemaSource.cpp
   IdentifierResolver.cpp
   JumpDiagnostics.cpp
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
new file mode 100644
index 0000000000000..db0ed3434d837
--- /dev/null
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -0,0 +1,782 @@
+//===--- HLSLBuiltinTypeDeclBuilder.cpp - HLSL Builtin Type Decl Builder --===//
+//
+// 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 "HLSLBuiltinTypeDeclBuilder.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/SourceLocation.h"
+#include "clang/Sema/Lookup.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaHLSL.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace llvm::hlsl;
+
+namespace clang {
+
+namespace hlsl {
+
+namespace {
+
+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!");
+  return cast<FunctionDecl>(R.getFoundDecl());
+}
+} // namespace
+
+// Builder for template arguments of builtin types. Used internally
+// by BuiltinTypeDeclBuilder.
+struct TemplateParameterListBuilder {
+  BuiltinTypeDeclBuilder &Builder;
+  llvm::SmallVector<NamedDecl *> Params;
+
+  TemplateParameterListBuilder(BuiltinTypeDeclBuilder &RB) : Builder(RB) {}
+  ~TemplateParameterListBuilder();
+
+  TemplateParameterListBuilder &
+  addTypeParameter(StringRef Name, QualType DefaultValue = QualType());
+
+  ConceptSpecializationExpr *
+  constructConceptSpecializationExpr(Sema &S, ConceptDecl *CD);
+
+  BuiltinTypeDeclBuilder &finalizeTemplateArgs(ConceptDecl *CD = nullptr);
+};
+
+// Builder for methods of builtin types. Allows adding methods to builtin types
+// using the builder pattern like this:
+//
+//   BuiltinTypeMethodBuilder(RecordBuilder, "MethodName", ReturnType)
+//       .addParam("param_name", Type, InOutModifier)
+//       .callBuiltin("builtin_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 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 accepts constants via `Expr *` or placeholder
+// value arguments to indicate which function arguments to forward to the
+// builtin.
+//
+// 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 {
+private:
+  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;
+  bool IsConst;
+  llvm::SmallVector<MethodParam> Params;
+  llvm::SmallVector<Stmt *> StmtsList;
+
+  // Argument placeholders, inspired by std::placeholder. These are the indices
+  // of arguments to forward to `callBuiltin` and other method builder methods.
+  // Additional special values are:
+  //   Handle   - refers to the resource handle.
+  //   LastStmt - refers to the last statement in the method body; referencing
+  //              LastStmt will remove the statement from the method body since
+  //              it will be linked from the new expression being constructed.
+  enum class PlaceHolder { _0, _1, _2, _3, Handle = 128, LastStmt };
+
+  Expr *convertPlaceholder(PlaceHolder PH);
+  Expr *convertPlaceholder(Expr *E) { return E; }
+
+public:
+  friend BuiltinTypeDeclBuilder;
+
+  BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, DeclarationName &Name,
+                           QualType ReturnTy, bool IsConst = false)
+      : DeclBuilder(DB), NameInfo(DeclarationNameInfo(Name, SourceLocation())),
+        ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) {}
+
+  BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB, StringRef Name,
+                           QualType ReturnTy, bool IsConst = false);
+  BuiltinTypeMethodBuilder(const BuiltinTypeMethodBuilder &Other) = delete;
+
+  ~BuiltinTypeMethodBuilder() { finalizeMethod(); }
+
+  BuiltinTypeMethodBuilder &
+  operator=(const BuiltinTypeMethodBuilder &Other) = delete;
+
+  BuiltinTypeMethodBuilder &addParam(StringRef Name, QualType Ty,
+                                     HLSLParamModifierAttr::Spelling Modifier =
+                                         HLSLParamModifierAttr::Keyword_in);
+  template <typename... Ts>
+  BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName,
+                                        QualType ReturnType, Ts... ArgSpecs);
+  template <typename TLHS, typename TRHS>
+  BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS);
+  template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr);
+  BuiltinTypeDeclBuilder &finalizeMethod();
+  Expr *getResourceHandleExpr();
+
+private:
+  void createMethodDecl();
+};
+
+TemplateParameterListBuilder::~TemplateParameterListBuilder() {
+  finalizeTemplateArgs();
+}
+
+TemplateParameterListBuilder &
+TemplateParameterListBuilder::addTypeParameter(StringRef Name,
+                                               QualType DefaultValue) {
+  assert(!Builder.Record->isCompleteDefinition() &&
+         "record is already complete");
+  ASTContext &AST = Builder.SemaRef.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 */ true,
+      /* ParameterPack */ false,
+      /* HasTypeConstraint*/ false);
+  if (!DefaultValue.isNull())
+    Decl->setDefaultArgument(AST,
+                             Builder.SemaRef.getTrivialTemplateArgumentLoc(
+                                 DefaultValue, QualType(), SourceLocation()));
+
+  Params.emplace_back(Decl);
+  return *this;
+}
+
+// The concept specialization expression (CSE) constructed in
+// constructConceptSpecializationExpr is constructed so that it
+// matches the CSE that is constructed when parsing the below C++ code:
+//
+// template<typename T>
+// concept is_typed_resource_element_compatible =
+// __builtin_hlsl_typed_resource_element_compatible<T>
+//
+// template<typename element_type> requires
+// is_typed_resource_element_compatible<element_type>
+// struct RWBuffer {
+//     element_type Val;
+// };
+//
+// int fn() {
+//     RWBuffer<int> Buf;
+// }
+//
+// When dumping the AST and filtering for "RWBuffer", the resulting AST
+// structure is what we're trying to construct below, specifically the
+// CSE portion.
+ConceptSpecializationExpr *
+TemplateParameterListBuilder::constructConceptSpecializationExpr(
+    Sema &S, ConceptDecl *CD) {
+  ASTContext &Context = S.getASTContext();
+  SourceLocation Loc = Builder.Record->getBeginLoc();
+  DeclarationNameInfo DNI(CD->getDeclName(), Loc);
+  NestedNameSpecifierLoc NNSLoc;
+  DeclContext *DC = Builder.Record->getDeclContext();
+  TemplateArgumentListInfo TALI(Loc, Loc);
+
+  // Assume that the concept decl has just one template parameter
+  // This parameter should have been added when CD was constructed
+  // in getTypedBufferConceptDecl
+  assert(CD->getTemplateParameters()->size() == 1 &&
+         "unexpected concept decl parameter count");
+  TemplateTypeParmDecl *ConceptTTPD =
+      dyn_cast<TemplateTypeParmDecl>(CD->getTemplateParameters()->getParam(0));
+
+  // this TemplateTypeParmDecl is the template for the resource, and is
+  // used to construct a template argumentthat will be used
+  // to construct the ImplicitConceptSpecializationDecl
+  TemplateTypeParmDecl *T = TemplateTypeParmDecl::Create(
+      Context,                          // AST context
+      Builder.Record->getDeclContext(), // DeclContext
+      SourceLocation(), SourceLocation(),
+      /*D=*/0,                    // Depth in the template parameter list
+      /*P=*/0,                    // Position in the template parameter list
+      /*Id=*/nullptr,             // Identifier for 'T'
+      /*Typename=*/true,          // Indicates this is a 'typename' or 'class'
+      /*ParameterPack=*/false,    // Not a parameter pack
+      /*HasTypeConstraint=*/false // Has no type constraint
+  );
+
+  T->setDeclContext(DC);
+
+  QualType ConceptTType = Context.getTypeDeclType(ConceptTTPD);
+
+  // this is the 2nd template argument node, on which
+  // the concept constraint is actually being applied: 'element_type'
+  TemplateArgument ConceptTA = TemplateArgument(ConceptTType);
+
+  QualType CSETType = Context.getTypeDeclType(T);
+
+  // this is the 1st template argument node, which represents
+  // the abstract type that a concept would refer to: 'T'
+  TemplateArgument CSETA = TemplateArgument(CSETType);
+
+  ImplicitConceptSpecializationDecl *ImplicitCSEDecl =
+      ImplicitConceptSpecializationDecl::Create(
+          Context, Builder.Record->getDeclContext(), Loc, {CSETA});
+
+  // Constraint satisfaction is used to construct the
+  // ConceptSpecailizationExpr, and represents the 2nd Template Argument,
+  // located at the bottom of the sample AST above.
+  const ConstraintSatisfaction CS(CD, {ConceptTA});
+  TemplateArgumentLoc TAL =
+      S.getTrivialTemplateArgumentLoc(ConceptTA, QualType(), SourceLocation());
+
+  TALI.addArgument(TAL);
+  const ASTTemplateArgumentListInfo *ATALI =
+      ASTTemplateArgumentListInfo::Create(Context, TALI);
+
+  // In the concept reference, ATALI is what adds the extra
+  // TemplateArgument node underneath CSE
+  ConceptReference *CR =
+      ConceptReference::Create(Context, NNSLoc, Loc, DNI, CD, CD, ATALI);
+
+  ConceptSpecializationExpr *CSE =
+      ConceptSpecializationExpr::Create(Context, CR, ImplicitCSEDecl, &CS);
+
+  return CSE;
+}
+
+BuiltinTypeDeclBuilder &
+TemplateParameterListBuilder::finalizeTemplateArgs(ConceptDecl *CD) {
+  if (Params.empty())
+    return Builder;
+
+  ASTContext &AST = Builder.SemaRef.Context;
+  ConceptSpecializationExpr *CSE =
+      CD ? constructConceptSpecializationExpr(Builder.SemaRef, CD) : nullptr;
+  auto *ParamList = TemplateParameterList::Create(
+      AST, SourceLocation(), SourceLocation(), Params, SourceLocation(), CSE);
+  Builder.Template = ClassTemplateDecl::Create(
+      AST, Builder.Record->getDeclContext(), SourceLocation(),
+      DeclarationName(Builder.Record->getIdentifier()), ParamList,
+      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;
+}
+
+Expr *BuiltinTypeMethodBuilder::convertPlaceholder(PlaceHolder PH) {
+  if (PH == PlaceHolder::Handle)
+    return getResourceHandleExpr();
+
+  if (PH == PlaceHolder::LastStmt) {
+    assert(!StmtsList.empty() && "no statements in the list");
+    Stmt *LastStmt = StmtsList.pop_back_val();
+    assert(isa<ValueStmt>(LastStmt) && "last statement does not have a value");
+    return cast<ValueStmt>(LastStmt)->getExprStmt();
+  }
+
+  ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+  ParmVarDecl *ParamDecl = Method->getParamDecl(static_cast<unsigned>(PH));
+  return DeclRefExpr::Create(
+      AST, NestedNameSpecifierLoc(), SourceLocation(), ParamDecl, false,
+      DeclarationNameInfo(ParamDecl->getDeclName(), SourceLocation()),
+      ParamDecl->getType(), VK_PRValue);
+}
+
+BuiltinTypeMethodBuilder::BuiltinTypeMethodBuilder(BuiltinTypeDeclBuilder &DB,
+                                                   StringRef Name,
+                                                   QualType ReturnTy,
+                                                   bool IsConst)
+    : DeclBuilder(DB), ReturnTy(ReturnTy), Method(nullptr), IsConst(IsConst) {
+  const IdentifierInfo &II =
+      DB.SemaRef.getASTContext().Idents.get(Name, tok::TokenKind::identifier);
+  NameInfo = DeclarationNameInfo(DeclarationName(&II), SourceLocation());
+}
+
+BuiltinTypeMethodBuilder &
+BuiltinTypeMethodBuilder::addParam(StringRef Name, QualType Ty,
+                                   HLSLParamModifierAttr::Spelling Modifier) {
+  assert(Method == nullptr && "Cannot add param, method already created");
+  const IdentifierInfo &II = DeclBuilder.SemaRef.getASTContext().Idents.get(
+      Name, tok::TokenKind::identifier);
+  Params.emplace_back(II, Ty, Modifier);
+  return *this;
+}
+
+void BuiltinTypeMethodBuilder::createMethodDecl() {
+  assert(Method == nullptr && "Method already created");
+
+  // create method type
+  ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+  SmallVector<QualType> ParamTypes;
+  for (MethodParam &MP : Params)
+    ParamTypes.emplace_back(MP.Ty);
+
+  FunctionProtoType::ExtProtoInfo ExtInfo;
+  if (IsConst)
+    ExtInfo.TypeQuals.addConst();
+
+  QualType MethodTy = AST.getFunctionType(ReturnTy, ParamTypes, ExtInfo);
+
+  // 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>();
+  for (int I = 0, E = Params.size(); I != E; I++) {
+    MethodParam &MP = Params[I];
+    ParmVarDecl *Parm = ParmVarDecl::Create(
+        AST, Method->getDeclContext(), SourceLocation(), SourceLocation(),
+        &MP.NameII, MP.Ty,
+        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});
+}
+
+Expr *BuiltinTypeMethodBuilder::getResourceHandleExpr() {
+  // The first statement added to a method or access to 'this' creates the
+  // declaration.
+  if (!Method)
+    createMethodDecl();
+
+  ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+  CXXThisExpr *This = CXXThisExpr::Create(
+      AST, SourceLocation(), Method->getFunctionObjectParameterType(), true);
+  FieldDecl *HandleField = DeclBuilder.getResourceHandleField();
+  return MemberExpr::CreateImplicit(AST, This, false, HandleField,
+                                    HandleField->getType(), VK_LValue,
+                                    OK_Ordinary);
+}
+
+template <typename... Ts>
+BuiltinTypeMethodBuilder &
+BuiltinTypeMethodBuilder::callBuiltin(StringRef BuiltinName,
+                                      QualType ReturnType, Ts... ArgSpecs) {
+  std::array<Expr *, sizeof...(ArgSpecs)> Args{
+      convertPlaceholder(std::forward<Ts>(ArgSpecs))...};
+
+  // The first statement added to a method or access to 'this` creates the
+  // declaration.
+  if (!Method)
+    createMethodDecl();
+
+  ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+  FunctionDecl *FD = lookupBuiltinFunction(DeclBuilder.SemaRef, BuiltinName);
+  DeclRefExpr *DRE = DeclRefExpr::Create(
+      AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false,
+      FD->getNameInfo(), AST.BuiltinFnTy, VK_PRValue);
+
+  if (ReturnType.isNull())
+    ReturnType = FD->getReturnType();
+
+  Expr *Call = CallExpr::Create(AST, DRE, Args, ReturnType, VK_PRValue,
+                                SourceLocation(), FPOptionsOverride());
+  StmtsList.push_back(Call);
+  return *this;
+}
+
+template <typename TLHS, typename TRHS>
+BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::assign(TLHS LHS, TRHS RHS) {
+  Expr *LHSExpr = convertPlaceholder(LHS);
+  Expr *RHSExpr = convertPlaceholder(RHS);
+  Stmt *AssignStmt = BinaryOperator::Create(
+      DeclBuilder.SemaRef.getASTContext(), LHSExpr, RHSExpr, BO_Assign,
+      LHSExpr->getType(), ExprValueKind::VK_PRValue,
+      ExprObjectKind::OK_Ordinary, SourceLocation(), FPOptionsOverride());
+  StmtsList.push_back(AssignStmt);
+  return *this;
+}
+
+template <typename T>
+BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::dereference(T Ptr) {
+  Expr *PtrExpr = convertPlaceholder(Ptr);
+  Expr *Deref =
+      UnaryOperator::Create(DeclBuilder.SemaRef.getASTContext(), PtrExpr,
+                            UO_Deref, PtrExpr->getType()->getPointeeType(),
+                            VK_PRValue, OK_Ordinary, SourceLocation(),
+                            /*CanOverflow=*/false, FPOp...
[truncated]

@hekota hekota merged commit 9a82f74 into llvm:main Mar 24, 2025
12 checks passed
@llvm-ci
Copy link
Collaborator

llvm-ci commented Mar 24, 2025

LLVM Buildbot has detected a new failure on builder clang-debian-cpp20 running on clang-debian-cpp20 while building clang at step 6 "test-build-unified-tree-check-all".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/108/builds/10826

Here is the relevant piece of the build log for the reference
Step 6 (test-build-unified-tree-check-all) failure: test (failure)
******************** TEST 'LLVM :: tools/llvm-exegesis/RISCV/rvv/filter.test' FAILED ********************
Exit Code: 2

Command Output (stderr):
--
/vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/llvm-exegesis -mtriple=riscv64 -mcpu=sifive-x280 -benchmark-phase=assemble-measured-code --mode=inverse_throughput --opcode-name=PseudoVNCLIPU_WX_M1_MASK     --riscv-filter-config='vtype = {VXRM: rod, AVL: VLMAX, SEW: e(8|16), Policy: ta/mu}' --max-configs-per-opcode=1000 --min-instructions=10 | /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/FileCheck /vol/worker/clang-debian-cpp20/clang-debian-cpp20/llvm-project/llvm/test/tools/llvm-exegesis/RISCV/rvv/filter.test # RUN: at line 1
+ /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/FileCheck /vol/worker/clang-debian-cpp20/clang-debian-cpp20/llvm-project/llvm/test/tools/llvm-exegesis/RISCV/rvv/filter.test
+ /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/llvm-exegesis -mtriple=riscv64 -mcpu=sifive-x280 -benchmark-phase=assemble-measured-code --mode=inverse_throughput --opcode-name=PseudoVNCLIPU_WX_M1_MASK '--riscv-filter-config=vtype = {VXRM: rod, AVL: VLMAX, SEW: e(8|16), Policy: ta/mu}' --max-configs-per-opcode=1000 --min-instructions=10
PseudoVNCLIPU_WX_M1_MASK: Failed to produce any snippet via: instruction has tied variables, avoiding Read-After-Write issue, picking random def and use registers not aliasing each other, for uses, one unique register for each position
FileCheck error: '<stdin>' is empty.
FileCheck command line:  /vol/worker/clang-debian-cpp20/clang-debian-cpp20/build/bin/FileCheck /vol/worker/clang-debian-cpp20/clang-debian-cpp20/llvm-project/llvm/test/tools/llvm-exegesis/RISCV/rvv/filter.test

--

********************


hekota added a commit that referenced this pull request Mar 28, 2025
…#131384)

Updates `BuiltinTypeMethodBuilder` helper class to support creation of
constructors and updates the code that creates default constructor for
resource classes to use it.

This enables us to share code when creating builtin methods and
constructors and will come in handy when we add more constructors in the
future.

Depends on #131032.
@damyanp damyanp moved this to Closed in HLSL Support Apr 25, 2025
@damyanp damyanp removed this from HLSL Support Jun 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category HLSL HLSL Language Support
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants