Skip to content

Conversation

@bogner
Copy link
Contributor

@bogner bogner commented Nov 26, 2024

Introduce BuiltinTypeMethodBuilder::PlaceHolder values and use them in the callBuiltin method in order to specify how we want to forward arguments and pass the resource handle to builtins.

Introduce BuiltinTypeMethodBuilder::PlaceHolder values and use them in
the callBuiltin method in order to specify how we want to forward
arguments and pass the resource handle to builtins.
@bogner bogner requested review from hekota and tex3d November 26, 2024 20:57
@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 Nov 26, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 26, 2024

@llvm/pr-subscribers-clang

Author: Justin Bogner (bogner)

Changes

Introduce BuiltinTypeMethodBuilder::PlaceHolder values and use them in the callBuiltin method in order to specify how we want to forward arguments and pass the resource handle to builtins.


Full diff: https://github.com/llvm/llvm-project/pull/117789.diff

1 Files Affected:

  • (modified) clang/lib/Sema/HLSLExternalSemaSource.cpp (+38-22)
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 886a4c098580ae..fae43d187e0532 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -455,7 +455,7 @@ struct TemplateParameterListBuilder {
 //
 //   BuiltinTypeMethodBuilder(Sema, RecordBuilder, "MethodName", ReturnType)
 //       .addParam("param_name", Type, InOutModifier)
-//       .callBuiltin("buildin_name", { BuiltinParams })
+//       .callBuiltin("buildin_name", BuiltinParams... )
 //       .finalizeMethod();
 //
 // The builder needs to have all of the method parameters before it can create
@@ -465,9 +465,9 @@ struct TemplateParameterListBuilder {
 // 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.
+// 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
@@ -489,6 +489,24 @@ struct BuiltinTypeMethodBuilder {
   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 additionally `Handle` which
+  // refers to the resource handle.
+  enum class PlaceHolder { _0, _1, _2, _3, Handle = 127 };
+
+  Expr *convertPlaceholder(PlaceHolder PH) {
+    if (PH == PlaceHolder::Handle)
+      return getResourceHandleExpr();
+
+    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);
+  }
+  Expr *convertPlaceholder(Expr *E) { return E; }
+
 public:
   BuiltinTypeMethodBuilder(Sema &S, BuiltinTypeDeclBuilder &DB, StringRef Name,
                            QualType ReturnTy)
@@ -502,7 +520,10 @@ struct BuiltinTypeMethodBuilder {
                                      HLSLParamModifierAttr::Spelling Modifier =
                                          HLSLParamModifierAttr::Keyword_in) {
     assert(Method == nullptr && "Cannot add param, method already created");
-    llvm_unreachable("not yet implemented");
+    const IdentifierInfo &II = DeclBuilder.SemaRef.getASTContext().Idents.get(
+        Name, tok::TokenKind::identifier);
+    Params.emplace_back(II, Ty, Modifier);
+    return *this;
   }
 
 private:
@@ -564,9 +585,9 @@ struct BuiltinTypeMethodBuilder {
                                       OK_Ordinary);
   }
 
-  BuiltinTypeMethodBuilder &
-  callBuiltin(StringRef BuiltinName, ArrayRef<Expr *> CallParms,
-              bool AddResourceHandleAsFirstArg = true) {
+  template <typename... Ts>
+  BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName, Ts... ArgSpecs) {
+    SmallVector<Expr *> Args{convertPlaceholder(std::forward<Ts>(ArgSpecs))...};
 
     // The first statement added to a method or access to 'this` creates the
     // declaration.
@@ -579,16 +600,9 @@ struct BuiltinTypeMethodBuilder {
         AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false,
         FD->getNameInfo(), FD->getType(), VK_PRValue);
 
-    SmallVector<Expr *> NewCallParms;
-    if (AddResourceHandleAsFirstArg) {
-      NewCallParms.push_back(getResourceHandleExpr());
-      for (auto *P : CallParms)
-        NewCallParms.push_back(P);
-    }
-
-    Expr *Call = CallExpr::Create(
-        AST, DRE, AddResourceHandleAsFirstArg ? NewCallParms : CallParms,
-        FD->getReturnType(), VK_PRValue, SourceLocation(), FPOptionsOverride());
+    Expr *Call =
+        CallExpr::Create(AST, DRE, Args, FD->getReturnType(), VK_PRValue,
+                         SourceLocation(), FPOptionsOverride());
     StmtsList.push_back(Call);
     return *this;
   }
@@ -656,18 +670,20 @@ BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
 }
 
 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() {
+  using PH = BuiltinTypeMethodBuilder::PlaceHolder;
   return BuiltinTypeMethodBuilder(SemaRef, *this, "IncrementCounter",
                                   SemaRef.getASTContext().UnsignedIntTy)
-      .callBuiltin("__builtin_hlsl_buffer_update_counter",
-                   {getConstantIntExpr(1)})
+      .callBuiltin("__builtin_hlsl_buffer_update_counter", PH::Handle,
+                   getConstantIntExpr(1))
       .finalizeMethod();
 }
 
 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
+  using PH = BuiltinTypeMethodBuilder::PlaceHolder;
   return BuiltinTypeMethodBuilder(SemaRef, *this, "DecrementCounter",
                                   SemaRef.getASTContext().UnsignedIntTy)
-      .callBuiltin("__builtin_hlsl_buffer_update_counter",
-                   {getConstantIntExpr(-1)})
+      .callBuiltin("__builtin_hlsl_buffer_update_counter", PH::Handle,
+                   getConstantIntExpr(-1))
       .finalizeMethod();
 }
 

@llvmbot
Copy link
Member

llvmbot commented Nov 26, 2024

@llvm/pr-subscribers-hlsl

Author: Justin Bogner (bogner)

Changes

Introduce BuiltinTypeMethodBuilder::PlaceHolder values and use them in the callBuiltin method in order to specify how we want to forward arguments and pass the resource handle to builtins.


Full diff: https://github.com/llvm/llvm-project/pull/117789.diff

1 Files Affected:

  • (modified) clang/lib/Sema/HLSLExternalSemaSource.cpp (+38-22)
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 886a4c098580ae..fae43d187e0532 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -455,7 +455,7 @@ struct TemplateParameterListBuilder {
 //
 //   BuiltinTypeMethodBuilder(Sema, RecordBuilder, "MethodName", ReturnType)
 //       .addParam("param_name", Type, InOutModifier)
-//       .callBuiltin("buildin_name", { BuiltinParams })
+//       .callBuiltin("buildin_name", BuiltinParams... )
 //       .finalizeMethod();
 //
 // The builder needs to have all of the method parameters before it can create
@@ -465,9 +465,9 @@ struct TemplateParameterListBuilder {
 // 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.
+// 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
@@ -489,6 +489,24 @@ struct BuiltinTypeMethodBuilder {
   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 additionally `Handle` which
+  // refers to the resource handle.
+  enum class PlaceHolder { _0, _1, _2, _3, Handle = 127 };
+
+  Expr *convertPlaceholder(PlaceHolder PH) {
+    if (PH == PlaceHolder::Handle)
+      return getResourceHandleExpr();
+
+    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);
+  }
+  Expr *convertPlaceholder(Expr *E) { return E; }
+
 public:
   BuiltinTypeMethodBuilder(Sema &S, BuiltinTypeDeclBuilder &DB, StringRef Name,
                            QualType ReturnTy)
@@ -502,7 +520,10 @@ struct BuiltinTypeMethodBuilder {
                                      HLSLParamModifierAttr::Spelling Modifier =
                                          HLSLParamModifierAttr::Keyword_in) {
     assert(Method == nullptr && "Cannot add param, method already created");
-    llvm_unreachable("not yet implemented");
+    const IdentifierInfo &II = DeclBuilder.SemaRef.getASTContext().Idents.get(
+        Name, tok::TokenKind::identifier);
+    Params.emplace_back(II, Ty, Modifier);
+    return *this;
   }
 
 private:
@@ -564,9 +585,9 @@ struct BuiltinTypeMethodBuilder {
                                       OK_Ordinary);
   }
 
-  BuiltinTypeMethodBuilder &
-  callBuiltin(StringRef BuiltinName, ArrayRef<Expr *> CallParms,
-              bool AddResourceHandleAsFirstArg = true) {
+  template <typename... Ts>
+  BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName, Ts... ArgSpecs) {
+    SmallVector<Expr *> Args{convertPlaceholder(std::forward<Ts>(ArgSpecs))...};
 
     // The first statement added to a method or access to 'this` creates the
     // declaration.
@@ -579,16 +600,9 @@ struct BuiltinTypeMethodBuilder {
         AST, NestedNameSpecifierLoc(), SourceLocation(), FD, false,
         FD->getNameInfo(), FD->getType(), VK_PRValue);
 
-    SmallVector<Expr *> NewCallParms;
-    if (AddResourceHandleAsFirstArg) {
-      NewCallParms.push_back(getResourceHandleExpr());
-      for (auto *P : CallParms)
-        NewCallParms.push_back(P);
-    }
-
-    Expr *Call = CallExpr::Create(
-        AST, DRE, AddResourceHandleAsFirstArg ? NewCallParms : CallParms,
-        FD->getReturnType(), VK_PRValue, SourceLocation(), FPOptionsOverride());
+    Expr *Call =
+        CallExpr::Create(AST, DRE, Args, FD->getReturnType(), VK_PRValue,
+                         SourceLocation(), FPOptionsOverride());
     StmtsList.push_back(Call);
     return *this;
   }
@@ -656,18 +670,20 @@ BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
 }
 
 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addIncrementCounterMethod() {
+  using PH = BuiltinTypeMethodBuilder::PlaceHolder;
   return BuiltinTypeMethodBuilder(SemaRef, *this, "IncrementCounter",
                                   SemaRef.getASTContext().UnsignedIntTy)
-      .callBuiltin("__builtin_hlsl_buffer_update_counter",
-                   {getConstantIntExpr(1)})
+      .callBuiltin("__builtin_hlsl_buffer_update_counter", PH::Handle,
+                   getConstantIntExpr(1))
       .finalizeMethod();
 }
 
 BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDecrementCounterMethod() {
+  using PH = BuiltinTypeMethodBuilder::PlaceHolder;
   return BuiltinTypeMethodBuilder(SemaRef, *this, "DecrementCounter",
                                   SemaRef.getASTContext().UnsignedIntTy)
-      .callBuiltin("__builtin_hlsl_buffer_update_counter",
-                   {getConstantIntExpr(-1)})
+      .callBuiltin("__builtin_hlsl_buffer_update_counter", PH::Handle,
+                   getConstantIntExpr(-1))
       .finalizeMethod();
 }
 

Copy link
Contributor

@damyanp damyanp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM - a nit and an idea in the comments.

bool AddResourceHandleAsFirstArg = true) {
template <typename... Ts>
BuiltinTypeMethodBuilder &callBuiltin(StringRef BuiltinName, Ts... ArgSpecs) {
SmallVector<Expr *> Args{convertPlaceholder(std::forward<Ts>(ArgSpecs))...};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is SmallVector smart enough for this to evaporate into being a stack allocated array, or is there a chance that it might make a heap allocation to store the args?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, SmallVector will have stack allocated storage. Its size is derived from the initialization, which is dependent on the number of ArgSpecs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This raises a good point though - I'll change this to just use std::array here since we know the exact size at compile time.

.callBuiltin("__builtin_hlsl_buffer_update_counter",
{getConstantIntExpr(1)})
.callBuiltin("__builtin_hlsl_buffer_update_counter", PH::Handle,
getConstantIntExpr(1))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How evil would it be to add a Expr *convertPlaceholder(int) that does the getConstantIntExpr for you, so this could be written as: .callBuiltin("__builtin_hlsl_buffer_update_conter", PH::Handle, 1)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels too magical to me (and also it introduces an annoying ambiguity with a literal zero - is it an int or a null Expr *?)

Copy link
Contributor

@tex3d tex3d left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good.

@bogner bogner merged commit 9fde1a4 into llvm:main Nov 26, 2024
5 of 7 checks passed
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

Archived in project

Development

Successfully merging this pull request may close these issues.

5 participants