https://github.com/s-perron updated 
https://github.com/llvm/llvm-project/pull/195153

>From c3ea7c0d18932788c0b092c71564923218b0c10c Mon Sep 17 00:00:00 2001
From: Steven Perron <[email protected]>
Date: Wed, 29 Apr 2026 17:00:12 -0400
Subject: [PATCH] [HLSL] Add ConstantBuffer<T>

The ConstantBuffer<T> is a standard resource type in HLSL. This commit
is following the design in wg-hlsl proposal 
[0046](https://github.com/llvm/wg-hlsl/blob/main/proposals/0046-constantbuffer-t.md).

The type constraints will be left to a follow up pr.

Assisted-by: Gemini
---
 clang/include/clang/Sema/SemaHLSL.h           |  11 ++
 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp |  30 ++++-
 clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h   |   3 +-
 clang/lib/Sema/HLSLExternalSemaSource.cpp     |  11 ++
 clang/lib/Sema/SemaExprMember.cpp             |  14 ++
 clang/lib/Sema/SemaHLSL.cpp                   |  47 +++++++
 .../AST/HLSL/ConstantBuffers-AST-error.hlsl   |  24 ++++
 clang/test/AST/HLSL/ConstantBuffers-AST.hlsl  | 120 ++++++++++++++++++
 .../builtins/ConstantBuffer-layout.hlsl       |  68 ++++++++++
 .../CodeGenHLSL/builtins/ConstantBuffer.hlsl  |  65 ++++++++++
 .../test/CodeGenHLSL/cbuffer_copy_layout.hlsl |  24 ++++
 .../BuiltIns/ConstantBuffer-member-funcs.hlsl |  28 ++++
 .../SemaHLSL/BuiltIns/ConstantBuffers.hlsl    |  35 +++++
 13 files changed, 477 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/AST/HLSL/ConstantBuffers-AST-error.hlsl
 create mode 100644 clang/test/AST/HLSL/ConstantBuffers-AST.hlsl
 create mode 100644 clang/test/CodeGenHLSL/builtins/ConstantBuffer-layout.hlsl
 create mode 100644 clang/test/CodeGenHLSL/builtins/ConstantBuffer.hlsl
 create mode 100644 clang/test/CodeGenHLSL/cbuffer_copy_layout.hlsl
 create mode 100644 
clang/test/SemaHLSL/BuiltIns/ConstantBuffer-member-funcs.hlsl
 create mode 100644 clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl

diff --git a/clang/include/clang/Sema/SemaHLSL.h 
b/clang/include/clang/Sema/SemaHLSL.h
index 1ba9bfed9918d..7a8fb5492f8df 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -143,6 +143,17 @@ class SemaHLSL : public SemaBase {
                                        bool IsCompAssign);
   void emitLogicalOperatorFixIt(Expr *LHS, Expr *RHS, BinaryOperatorKind Opc);
 
+  // Returns the result of converting ConstantBuffer<T> to
+  // `const hlsl_constant T&`. If `BaseExpr`'s type is not ConstantBuffer<T>
+  // then the return value is `std::nullopt`.
+  std::optional<ExprResult>
+  performConstantBufferConversion(ExprResult &BaseExpr);
+
+  // Returns the conversion operator to convert `RD` to `const hlsl_constant
+  // Type&`. Returns `nullptr` if it could not be found.
+  NamedDecl *getConstantBufferConversionFunction(QualType Type,
+                                                 CXXRecordDecl *RD);
+
   /// Computes the unique Root Signature identifier from the given signature,
   /// then lookup if there is a previousy created Root Signature decl.
   ///
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp 
b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index ba8e63f01527a..a4c1c70ebf31b 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -554,6 +554,11 @@ void BuiltinTypeMethodBuilder::createDecl() {
         AST, DeclBuilder.Record, SourceLocation(), NameInfo, FuncTy, TSInfo,
         ExplicitSpecifier(), false, /*IsInline=*/true, false,
         ConstexprSpecKind::Unspecified);
+  else if (Name.getNameKind() == DeclarationName::CXXConversionFunctionName)
+    Method = CXXConversionDecl::Create(
+        AST, DeclBuilder.Record, SourceLocation(), NameInfo, FuncTy, TSInfo,
+        false, /*isInline=*/true, ExplicitSpecifier(),
+        ConstexprSpecKind::Unspecified, SourceLocation());
   else
     Method = CXXMethodDecl::Create(
         AST, DeclBuilder.Record, SourceLocation(), NameInfo, FuncTy, TSInfo, 
SC,
@@ -879,7 +884,7 @@ BuiltinTypeMethodBuilder 
&BuiltinTypeMethodBuilder::returnValue(T ReturnValue) {
   ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
 
   QualType Ty = ReturnValueExpr->getType();
-  if (Ty->isRecordType()) {
+  if (Ty->isRecordType() && !Method->getReturnType()->isReferenceType()) {
     // For record types, create a call to copy constructor to ensure proper 
copy
     // semantics.
     auto *ICE =
@@ -1055,6 +1060,27 @@ BuiltinTypeDeclBuilder 
&BuiltinTypeDeclBuilder::addSamplerHandle() {
   return *this;
 }
 
+BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addConversionToType() {
+  assert(!Record->isCompleteDefinition() && "record is already complete");
+  ASTContext &AST = SemaRef.getASTContext();
+  using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+
+  QualType ElemTy = getHandleElementType();
+  QualType AddrSpaceElemTy = AST.getCanonicalType(
+      AST.getAddrSpaceQualType(ElemTy.withConst(), LangAS::hlsl_constant));
+  QualType ReturnTy =
+      AST.getCanonicalType(AST.getLValueReferenceType(AddrSpaceElemTy));
+
+  DeclarationName Name = AST.DeclarationNames.getCXXConversionFunctionName(
+      AST.getCanonicalType(ReturnTy));
+
+  return BuiltinTypeMethodBuilder(*this, Name, ReturnTy, /*IsConst=*/true)
+      .callBuiltin("__builtin_hlsl_resource_getpointer",
+                   AST.getPointerType(AddrSpaceElemTy), PH::Handle)
+      .dereference(PH::LastStmt)
+      .finalize();
+}
+
 BuiltinTypeDeclBuilder &
 BuiltinTypeDeclBuilder::addFriend(CXXRecordDecl *Friend) {
   assert(!Record->isCompleteDefinition() && "record is already complete");
@@ -2159,7 +2185,7 @@ Expr 
*BuiltinTypeDeclBuilder::getConstantUnsignedIntExpr(unsigned value) {
 
 BuiltinTypeDeclBuilder &
 BuiltinTypeDeclBuilder::addSimpleTemplateParams(ArrayRef<StringRef> Names,
-                                                ConceptDecl *CD = nullptr) {
+                                                ConceptDecl *CD) {
   return addSimpleTemplateParams(Names, {}, CD);
 }
 
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h 
b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
index e69afd67b2618..72e7bed2b991d 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
@@ -64,7 +64,7 @@ class BuiltinTypeDeclBuilder {
   ~BuiltinTypeDeclBuilder();
 
   BuiltinTypeDeclBuilder &addSimpleTemplateParams(ArrayRef<StringRef> Names,
-                                                  ConceptDecl *CD);
+                                                  ConceptDecl *CD = nullptr);
   BuiltinTypeDeclBuilder &
   addSimpleTemplateParams(ArrayRef<StringRef> Names,
                           ArrayRef<QualType> DefaultTypes, ConceptDecl *CD);
@@ -83,6 +83,7 @@ class BuiltinTypeDeclBuilder {
   addTextureHandle(ResourceClass RC, bool IsROV, ResourceDimension RD,
                    AccessSpecifier Access = AccessSpecifier::AS_private);
   BuiltinTypeDeclBuilder &addSamplerHandle();
+  BuiltinTypeDeclBuilder &addConversionToType();
   BuiltinTypeDeclBuilder &addArraySubscriptOperators(
       ResourceDimension Dim = ResourceDimension::Unknown);
 
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp 
b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index 235ede8eb0bf0..10ffa7d6ab370 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -472,6 +472,17 @@ void 
HLSLExternalSemaSource::defineHLSLTypesWithForwardDeclarations() {
   ConceptDecl *StructuredBufferConcept = constructBufferConceptDecl(
       *SemaPtr, HLSLNamespace, /*isTypedBuffer*/ false);
 
+  Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "ConstantBuffer")
+             .addSimpleTemplateParams({"element_type"})
+             .finalizeForwardDeclaration();
+
+  onCompletion(Decl, [this](CXXRecordDecl *Decl) {
+    setupBufferType(Decl, *SemaPtr, ResourceClass::CBuffer, /*IsROV=*/false,
+                    /*RawBuffer=*/false, /*HasCounter=*/false)
+        .addConversionToType()
+        .completeDefinition();
+  });
+
   Decl = BuiltinTypeDeclBuilder(*SemaPtr, HLSLNamespace, "Buffer")
              .addSimpleTemplateParams({"element_type"}, TypedBufferConcept)
              .finalizeForwardDeclaration();
diff --git a/clang/lib/Sema/SemaExprMember.cpp 
b/clang/lib/Sema/SemaExprMember.cpp
index a4504410cae28..1e8ed5f678259 100644
--- a/clang/lib/Sema/SemaExprMember.cpp
+++ b/clang/lib/Sema/SemaExprMember.cpp
@@ -1292,6 +1292,20 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult 
&R,
         BaseExpr.get()->getValueKind(), FPOptionsOverride());
   }
 
+  // In HLSL, the member access on a ConstantBuffer<T> access the members of
+  // through the handle in the ConstantBuffer<T>. If BaseType is a
+  // ConstantBuffer, the conversion function to type T is called before trying
+  // to access the member.
+  if (S.getLangOpts().HLSL) {
+    if (std::optional<ExprResult> ConvBase =
+            S.HLSL().performConstantBufferConversion(BaseExpr)) {
+      assert(!ConvBase->isInvalid());
+      BaseExpr = *ConvBase;
+      BaseType = BaseExpr.get()->getType();
+      IsArrow = false;
+    }
+  }
+
   // Handle field access to simple records.
   if (BaseType->getAsRecordDecl()) {
     if (LookupMemberExprInRecord(S, R, BaseExpr.get(), BaseType, OpLoc, 
IsArrow,
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 7788d777edf1c..3280ea2338bb6 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -3175,6 +3175,53 @@ bool SemaHLSL::ActOnResourceMemberAccessExpr(MemberExpr 
*ME) {
   return true;
 }
 
+NamedDecl *SemaHLSL::getConstantBufferConversionFunction(QualType Type,
+                                                         CXXRecordDecl *RD) {
+  QualType AddrSpaceType =
+      SemaRef.Context.getCanonicalType(SemaRef.Context.getAddrSpaceQualType(
+          Type.withConst(), LangAS::hlsl_constant));
+  QualType ReturnTy = SemaRef.Context.getCanonicalType(
+      SemaRef.Context.getLValueReferenceType(AddrSpaceType));
+
+  DeclarationName ConvName =
+      SemaRef.Context.DeclarationNames.getCXXConversionFunctionName(
+          CanQualType::CreateUnsafe(ReturnTy));
+  LookupResult ConvR(SemaRef, ConvName, SourceLocation(),
+                     Sema::LookupOrdinaryName);
+  bool LookupSucceeded = SemaRef.LookupQualifiedName(ConvR, RD);
+  assert(LookupSucceeded);
+
+  for (NamedDecl *D : ConvR) {
+    if (isa<CXXConversionDecl>(D->getUnderlyingDecl()))
+      return D;
+  }
+  return nullptr;
+}
+
+std::optional<ExprResult>
+SemaHLSL::performConstantBufferConversion(ExprResult &BaseExpr) {
+  QualType BaseType = BaseExpr.get()->getType();
+  const HLSLAttributedResourceType *ResTy =
+      HLSLAttributedResourceType::findHandleTypeOnResource(
+          BaseType.getTypePtr());
+  if (!ResTy ||
+      ResTy->getAttrs().ResourceClass != llvm::dxil::ResourceClass::CBuffer)
+    return std::nullopt;
+
+  QualType TemplateType = ResTy->getContainedType();
+
+  NamedDecl *NamedConversionDecl = getConstantBufferConversionFunction(
+      TemplateType, BaseType->getAsCXXRecordDecl());
+  assert(NamedConversionDecl &&
+         "Could not find conversion function for ConstantBuffer.");
+  auto *ConversionDecl =
+      cast<CXXConversionDecl>(NamedConversionDecl->getUnderlyingDecl());
+
+  return SemaRef.BuildCXXMemberCallExpr(BaseExpr.get(), NamedConversionDecl,
+                                        ConversionDecl,
+                                        /*HadMultipleCandidates=*/false);
+}
+
 void SemaHLSL::diagnoseAvailabilityViolations(TranslationUnitDecl *TU) {
   // Skip running the diagnostics scan if the diagnostic mode is
   // strict (-fhlsl-strict-availability) and the target shader stage is known
diff --git a/clang/test/AST/HLSL/ConstantBuffers-AST-error.hlsl 
b/clang/test/AST/HLSL/ConstantBuffers-AST-error.hlsl
new file mode 100644
index 0000000000000..3e2d8075a6569
--- /dev/null
+++ b/clang/test/AST/HLSL/ConstantBuffers-AST-error.hlsl
@@ -0,0 +1,24 @@
+// RUN: not %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl 
-ast-dump -finclude-default-header -o - %s 2>&1 | FileCheck %s
+
+// Unimplemented: https://github.com/llvm/llvm-project/issues/195093
+// Once fixed, these tests should work and we should check the AST.
+
+struct S {
+  float a;
+};
+ConstantBuffer<S> cb;
+
+void takes_s(S s) {}
+
+void main() {
+  S s;
+
+  // CHECK: error: no viable constructor copying parameter of type 'const 
hlsl_constant S'
+  takes_s(cb);
+
+  // CHECK: error: no viable constructor copying variable of type 'const 
hlsl_constant S'
+  S s2 = cb;
+
+  // CHECK: error: no viable conversion from 'ConstantBuffer<S>' to 'const S'
+  s = cb;
+}
diff --git a/clang/test/AST/HLSL/ConstantBuffers-AST.hlsl 
b/clang/test/AST/HLSL/ConstantBuffers-AST.hlsl
new file mode 100644
index 0000000000000..6a880c437db8f
--- /dev/null
+++ b/clang/test/AST/HLSL/ConstantBuffers-AST.hlsl
@@ -0,0 +1,120 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -ast-dump 
-disable-llvm-passes -finclude-default-header -o - %s | FileCheck %s
+
+// CHECK: ClassTemplateDecl {{.*}} ConstantBuffer
+// CHECK: TemplateTypeParmDecl {{.*}} element_type
+// CHECK: CXXRecordDecl {{.*}} ConstantBuffer definition
+// CHECK: FinalAttr {{.*}} Implicit final
+// CHECK-NEXT: FieldDecl {{.*}} implicit __handle '__hlsl_resource_t
+// CHECK-SAME{LITERAL}: [[hlsl::resource_class(CBuffer)]]
+// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]
+
+// CHECK: CXXConstructorDecl {{.*}} ConstantBuffer<element_type> 'void ()' 
inline
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: BinaryOperator {{.*}} '__hlsl_resource_t
+// CHECK-SAME{LITERAL}: [[hlsl::resource_class(CBuffer)]]
+// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]
+// CHECK-SAME: ' '='
+// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t
+// CHECK-SAME{LITERAL}: [[hlsl::resource_class(CBuffer)]]
+// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]
+// CHECK-SAME: ' lvalue .__handle
+// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::ConstantBuffer<element_type>' lvalue 
implicit this
+// CHECK-NEXT: CStyleCastExpr {{.*}} '__hlsl_resource_t
+// CHECK-SAME{LITERAL}: [[hlsl::resource_class(CBuffer)]]
+// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]
+// CHECK-SAME: ' <Dependent>
+// CHECK-NEXT: CallExpr {{.*}} '<dependent type>'
+
+// CHECK: CXXConstructorDecl {{.*}} ConstantBuffer<element_type> 'void (const 
hlsl::ConstantBuffer<element_type> &)' inline
+// CHECK-NEXT: ParmVarDecl {{.*}} other 'const 
hlsl::ConstantBuffer<element_type> &'
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: BinaryOperator {{.*}} '__hlsl_resource_t
+// CHECK-SAME{LITERAL}: [[hlsl::resource_class(CBuffer)]]
+// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]
+// CHECK-SAME: ' '='
+// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t
+// CHECK-SAME{LITERAL}: [[hlsl::resource_class(CBuffer)]]
+// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]
+// CHECK-SAME: ' lvalue .__handle
+// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::ConstantBuffer<element_type>' lvalue 
implicit this
+// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t
+// CHECK-SAME{LITERAL}: [[hlsl::resource_class(CBuffer)]]
+// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]
+// CHECK-SAME: ' lvalue .__handle
+// CHECK-NEXT: DeclRefExpr {{.*}} 'const hlsl::ConstantBuffer<element_type>' 
lvalue ParmVar {{.*}} 'other' 'const hlsl::ConstantBuffer<element_type> &'
+
+// CHECK: CXXMethodDecl {{.*}} operator= 'hlsl::ConstantBuffer<element_type> 
&(const hlsl::ConstantBuffer<element_type> &)' inline
+// CHECK-NEXT: ParmVarDecl {{.*}} other 'const 
hlsl::ConstantBuffer<element_type> &'
+// CHECK-NEXT: CompoundStmt
+// CHECK-NEXT: BinaryOperator {{.*}} '__hlsl_resource_t
+// CHECK-SAME{LITERAL}: [[hlsl::resource_class(CBuffer)]]
+// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]
+// CHECK-SAME: ' '='
+// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t
+// CHECK-SAME{LITERAL}: [[hlsl::resource_class(CBuffer)]]
+// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]
+// CHECK-SAME: ' lvalue .__handle
+// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::ConstantBuffer<element_type>' lvalue 
implicit this
+// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t
+// CHECK-SAME{LITERAL}: [[hlsl::resource_class(CBuffer)]]
+// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]]
+// CHECK-SAME: ' lvalue .__handle
+// CHECK-NEXT: DeclRefExpr {{.*}} 'const hlsl::ConstantBuffer<element_type>' 
lvalue ParmVar {{.*}} 'other' 'const hlsl::ConstantBuffer<element_type> &'
+// CHECK-NEXT: ReturnStmt
+// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::ConstantBuffer<element_type>' lvalue 
implicit this
+
+struct S {
+  float a;
+};
+ConstantBuffer<S> cb;
+
+struct Nested {
+  S s;
+  float b;
+};
+ConstantBuffer<Nested> cb_nested;
+
+void takes_s(S s) {}
+void takes_cb(ConstantBuffer<S> c) {}
+void takes_inout_cb(inout ConstantBuffer<S> c) {}
+
+float main() {
+  // CHECK: FunctionDecl {{.*}} main
+  // CHECK: MemberExpr {{.*}} 'const hlsl_constant float' lvalue .a
+  // CHECK-NEXT: CXXMemberCallExpr {{.*}} 'const hlsl_constant S' lvalue
+  // CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .operator 
const hlsl_constant S &
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'const hlsl::ConstantBuffer<S>' 
lvalue <NoOp>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 
'ConstantBuffer<S>':'hlsl::ConstantBuffer<S>' lvalue Var {{.*}} 'cb' 
'ConstantBuffer<S>':'hlsl::ConstantBuffer<S>'
+  float f1 = cb.a;
+
+  // CHECK: MemberExpr {{.*}} 'const hlsl_constant float' lvalue .b
+  // CHECK-NEXT: CXXMemberCallExpr {{.*}} 'const hlsl_constant Nested' lvalue
+  // CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .operator 
const hlsl_constant Nested &
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'const hlsl::ConstantBuffer<Nested>' 
lvalue <NoOp>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 
'ConstantBuffer<Nested>':'hlsl::ConstantBuffer<Nested>' lvalue Var {{.*}} 
'cb_nested' 'ConstantBuffer<Nested>':'hlsl::ConstantBuffer<Nested>'
+  float f2 = cb_nested.b;
+
+  // CHECK: MemberExpr {{.*}} 'const hlsl_constant float' lvalue .a
+  // CHECK-NEXT: MemberExpr {{.*}} 'const hlsl_constant S' lvalue .s
+  // CHECK-NEXT: CXXMemberCallExpr {{.*}} 'const hlsl_constant Nested' lvalue
+  // CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .operator 
const hlsl_constant Nested &
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'const hlsl::ConstantBuffer<Nested>' 
lvalue <NoOp>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 
'ConstantBuffer<Nested>':'hlsl::ConstantBuffer<Nested>' lvalue Var {{.*}} 
'cb_nested' 'ConstantBuffer<Nested>':'hlsl::ConstantBuffer<Nested>'
+  float f3 = cb_nested.s.a;
+
+  // CHECK: CallExpr {{.*}} 'void'
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(ConstantBuffer<S>)' 
<FunctionToPointerDecay>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 'void (ConstantBuffer<S>)' lvalue Function 
{{.*}} 'takes_cb' 'void (ConstantBuffer<S>)'
+  // CHECK-NEXT: CXXConstructExpr {{.*}} 
'ConstantBuffer<S>':'hlsl::ConstantBuffer<S>' 'void (const 
hlsl::ConstantBuffer<S> &)'
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'const hlsl::ConstantBuffer<S>' 
lvalue <NoOp>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 
'ConstantBuffer<S>':'hlsl::ConstantBuffer<S>' lvalue Var {{.*}} 'cb' 
'ConstantBuffer<S>':'hlsl::ConstantBuffer<S>'
+  takes_cb(cb);
+
+  // CHECK: CallExpr {{.*}} 'void'
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(inout ConstantBuffer<S>)' 
<FunctionToPointerDecay>
+  // CHECK-NEXT: DeclRefExpr {{.*}} 'void (inout ConstantBuffer<S>)' lvalue 
Function {{.*}} 'takes_inout_cb' 'void (inout ConstantBuffer<S>)'
+  // CHECK-NEXT: HLSLOutArgExpr {{.*}} 
'ConstantBuffer<S>':'hlsl::ConstantBuffer<S>' lvalue inout
+  takes_inout_cb(cb);
+
+  return f1 + f2 + f3;
+}
diff --git a/clang/test/CodeGenHLSL/builtins/ConstantBuffer-layout.hlsl 
b/clang/test/CodeGenHLSL/builtins/ConstantBuffer-layout.hlsl
new file mode 100644
index 0000000000000..1cf65b83ab501
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/ConstantBuffer-layout.hlsl
@@ -0,0 +1,68 @@
+// RUN: %clang_cc1 -finclude-default-header -triple 
dxil-pc-shadermodel6.3-library %s \
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s 
--check-prefixes=CHECK,CHECK-DXIL
+// RUN: %clang_cc1 -finclude-default-header -triple spirv-vulkan-library %s \
+// RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s 
--check-prefixes=CHECK,CHECK-SPIRV
+
+// Scenario 1: Basic Padding (No row crossing).
+struct Basic {
+  float3 a;
+  float b;
+};
+// CHECK-DAG: %Basic = type <{ <3 x float>, float }>
+// CHECK-DXIL-DAG: %"class.hlsl::ConstantBuffer" = type { target("dx.CBuffer", 
%Basic) }
+// CHECK-SPIRV-DAG: %"class.hlsl::ConstantBuffer" = type { 
target("spirv.VulkanBuffer", %Basic, 2, 0) }
+ConstantBuffer<Basic> cb_basic;
+
+// Scenario 2: Row Boundary Crossing.
+struct RowCrossing {
+  float2 a;
+  float3 b;
+};
+// CHECK-DXIL-DAG: %RowCrossing = type <{ <2 x float>, target("dx.Padding", 
8), <3 x float> }>
+// CHECK-SPIRV-DAG: %RowCrossing = type <{ <2 x float>, 
target("spirv.Padding", 8), <3 x float> }>
+ConstantBuffer<RowCrossing> cb_row_crossing;
+
+// Scenario 3: Arrays.
+struct ArrayPadding {
+  float a[2];
+  float b;
+};
+// CHECK-DXIL-DAG: %ArrayPadding = type <{ <{ [1 x <{ float, 
target("dx.Padding", 12) }>], float }>, float }>
+// CHECK-SPIRV-DAG: %ArrayPadding = type <{ <{ [1 x <{ float, 
target("spirv.Padding", 12) }>], float }>, float }>
+ConstantBuffer<ArrayPadding> cb_array;
+
+// Scenario 4: Nested Structs.
+struct Inner {
+  float a;
+};
+struct Outer {
+  Inner i;
+  float3 b;
+};
+// CHECK-DAG: %Inner = type <{ float }>
+// CHECK-DAG: %Outer = type <{ %Inner, <3 x float> }>
+ConstantBuffer<Outer> cb_nested;
+
+[numthreads(1,1,1)]
+void main() {
+  // Scenario 1
+  // CHECK-LABEL: define {{.*}} void @_Z4mainv()
+  // CHECK: %[[CB_BASIC:.*]] = call {{.*}} ptr addrspace({{.*}}) 
@_ZNK4hlsl14ConstantBufferI5BasicEcvRU{{.*}}S1_Ev
+  // CHECK: getelementptr inbounds nuw %Basic, ptr addrspace({{.*}}) 
%[[CB_BASIC]], i32 0, i32 1
+  float f1 = cb_basic.b;
+
+  // Scenario 2
+  // CHECK: %[[CB_ROW:.*]] = call {{.*}} ptr addrspace({{.*}}) 
@_ZNK4hlsl14ConstantBufferI11RowCrossingEcvRU{{.*}}S1_Ev
+  // CHECK: getelementptr inbounds nuw %RowCrossing, ptr addrspace({{.*}}) 
%[[CB_ROW]], i32 0, i32 2
+  float3 f2 = cb_row_crossing.b;
+
+  // Scenario 3
+  // CHECK: %[[CB_ARRAY:.*]] = call {{.*}} ptr addrspace({{.*}}) 
@_ZNK4hlsl14ConstantBufferI12ArrayPaddingEcvRU{{.*}}S1_Ev
+  // CHECK: getelementptr inbounds nuw %ArrayPadding, ptr addrspace({{.*}}) 
%[[CB_ARRAY]], i32 0, i32 1
+  float f3 = cb_array.b;
+
+  // Scenario 4
+  // CHECK: %[[CB_NESTED:.*]] = call {{.*}} ptr addrspace({{.*}}) 
@_ZNK4hlsl14ConstantBufferI5OuterEcvRU{{.*}}S1_Ev
+  // CHECK: getelementptr inbounds nuw %Outer, ptr addrspace({{.*}}) 
%[[CB_NESTED]], i32 0, i32 1
+  float3 f4 = cb_nested.b;
+}
diff --git a/clang/test/CodeGenHLSL/builtins/ConstantBuffer.hlsl 
b/clang/test/CodeGenHLSL/builtins/ConstantBuffer.hlsl
new file mode 100644
index 0000000000000..cb853694a9d0d
--- /dev/null
+++ b/clang/test/CodeGenHLSL/builtins/ConstantBuffer.hlsl
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -finclude-default-header -triple 
dxil-pc-shadermodel6.0-compute -emit-llvm -disable-llvm-passes -o - %s | 
FileCheck %s --check-prefixes=CHECK,CHECK-DXIL
+// RUN: %clang_cc1 -finclude-default-header -triple spirv-vulkan-library 
-emit-llvm -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefixes=CHECK,CHECK-SPIRV
+
+struct S {
+  float a;
+  int b;
+};
+
+// CHECK-DXIL: %"class.hlsl::ConstantBuffer" = type { target("dx.CBuffer", %S) 
}
+// CHECK-SPIRV: %"class.hlsl::ConstantBuffer" = type { 
target("spirv.VulkanBuffer", %S, 2, 0) }
+ConstantBuffer<S> cb;
+
+// CHECK-LABEL: define {{.*}} void @_Z4mainv()
+// CHECK-DXIL: [[CB_CONV:%.*]] = call noundef {{.*}} ptr addrspace(2) 
@_ZNK4hlsl14ConstantBufferI1SEcvRU3AS2KS1_Ev(ptr noundef nonnull align 4 
dereferenceable(4) @_ZL2cb)
+// CHECK-DXIL: [[GEP_A:%.*]] = getelementptr inbounds nuw %S, ptr addrspace(2) 
[[CB_CONV]], i32 0, i32 0
+// CHECK-DXIL: [[LOAD_A:%.*]] = load float, ptr addrspace(2) [[GEP_A]], align 4
+
+// CHECK-SPIRV: [[CB_CONV:%.*]] = call noundef {{.*}} ptr addrspace(12) 
@_ZNK4hlsl14ConstantBufferI1SEcvRU4AS12KS1_Ev(ptr noundef nonnull align 8 
dereferenceable(8) @_ZL2cb)
+// CHECK-SPIRV: [[GEP_A:%.*]] = getelementptr inbounds nuw %S, ptr 
addrspace(12) [[CB_CONV]], i32 0, i32 0
+// CHECK-SPIRV: [[LOAD_A:%.*]] = load float, ptr addrspace(12) [[GEP_A]], 
align 4
+
+// CHECK: store float [[LOAD_A]], ptr %f, align 4
+[numthreads(1,1,1)]
+void main() {
+  float f = cb.a;
+}
+
+struct Nested {
+  S s;
+  float c;
+};
+
+ConstantBuffer<Nested> cb_nested[2];
+
+[numthreads(1,1,1)]
+void foo() {
+  // CHECK-LABEL: define {{.*}} void @_Z3foov()
+  // CHECK-DXIL: [[TMP_CB:%.*]] = alloca %"class.hlsl::ConstantBuffer.0", 
align 4
+  // CHECK-DXIL: call void 
@_ZN4hlsl14ConstantBufferI6NestedE27__createFromImplicitBindingEjjijPKc(ptr 
dead_on_unwind writable sret(%"class.hlsl::ConstantBuffer.0") align 4 
[[TMP_CB]], i32 noundef 1, i32 noundef 0, i32 noundef 2, i32 noundef 1, ptr 
noundef @cb_nested.str)
+  // CHECK-DXIL: [[CB_CONV:%.*]] = call noundef {{.*}} ptr addrspace(2) 
@_ZNK4hlsl14ConstantBufferI6NestedEcvRU3AS2KS1_Ev(ptr noundef nonnull align 4 
dereferenceable(4) [[TMP_CB]])
+  // CHECK-DXIL: [[GEP_S:%.*]] = getelementptr inbounds nuw %Nested, ptr 
addrspace(2) [[CB_CONV]], i32 0, i32 0
+  // CHECK-DXIL: [[GEP_A2:%.*]] = getelementptr inbounds nuw %S, ptr 
addrspace(2) [[GEP_S]], i32 0, i32 0
+  // CHECK-DXIL: [[LOAD_A2:%.*]] = load float, ptr addrspace(2) [[GEP_A2]], 
align 4
+
+  // CHECK-SPIRV: [[TMP_CB:%.*]] = alloca %"class.hlsl::ConstantBuffer.0", 
align 8
+  // CHECK-SPIRV: call void 
@_ZN4hlsl14ConstantBufferI6NestedE27__createFromImplicitBindingEjjijPKc(ptr 
dead_on_unwind writable sret(%"class.hlsl::ConstantBuffer.0") align 8 
[[TMP_CB]], i32 noundef 1, i32 noundef 0, i32 noundef 2, i32 noundef 1, ptr 
noundef @cb_nested.str)
+  // CHECK-SPIRV: [[CB_CONV:%.*]] = call noundef {{.*}} ptr addrspace(12) 
@_ZNK4hlsl14ConstantBufferI6NestedEcvRU4AS12KS1_Ev(ptr noundef nonnull align 8 
dereferenceable(8) [[TMP_CB]])
+  // CHECK-SPIRV: [[GEP_S:%.*]] = getelementptr inbounds nuw %Nested, ptr 
addrspace(12) [[CB_CONV]], i32 0, i32 0
+  // CHECK-SPIRV: [[GEP_A2:%.*]] = getelementptr inbounds nuw %S, ptr 
addrspace(12) [[GEP_S]], i32 0, i32 0
+  // CHECK-SPIRV: [[LOAD_A2:%.*]] = load float, ptr addrspace(12) [[GEP_A2]], 
align 4
+
+  // CHECK: store float [[LOAD_A2]], ptr %f2, align 4
+  float f2 = cb_nested[1].s.a;
+}
+
+void takes_cb(ConstantBuffer<S> c) {}
+
+[numthreads(1,1,1)]
+void test_params() {
+  // CHECK-LABEL: define {{.*}} void @_Z11test_paramsv()
+  // CHECK: call void @_ZN4hlsl14ConstantBufferI1SEC1ERKS2_(ptr noundef 
nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) %agg.tmp, ptr noundef 
nonnull align {{[0-9]+}} dereferenceable({{[0-9]+}}) @_ZL2cb)
+  // CHECK-DXIL: call void @_Z8takes_cbN4hlsl14ConstantBufferI1SEE(ptr noundef 
dead_on_return %agg.tmp)
+  // CHECK-SPIRV: call {{.*}} void @_Z8takes_cbN4hlsl14ConstantBufferI1SEE(ptr 
noundef dead_on_return %agg.tmp)
+  takes_cb(cb);
+}
diff --git a/clang/test/CodeGenHLSL/cbuffer_copy_layout.hlsl 
b/clang/test/CodeGenHLSL/cbuffer_copy_layout.hlsl
new file mode 100644
index 0000000000000..ed1fe3ac0014e
--- /dev/null
+++ b/clang/test/CodeGenHLSL/cbuffer_copy_layout.hlsl
@@ -0,0 +1,24 @@
+// RUN: not %clang_cc1 -finclude-default-header -triple 
dxil-pc-shadermodel6.3-library %s 2>&1 | FileCheck %s
+
+// Unimplemented: https://github.com/llvm/llvm-project/issues/195093
+// These cases should work. When fixed we should add proper CHECKs.
+
+struct S {
+  float3 a;
+  float2 b;
+};
+
+cbuffer CB {
+  S s_cb;
+}
+
+ConstantBuffer<S> cb;
+
+[numthreads(1,1,1)]
+void main() {
+  // CHECK: error: no matching constructor for initialization of 'S'
+  S l1 = s_cb;
+
+  // CHECK: error: no viable constructor copying variable of type 'const 
hlsl_constant S'
+  S l2 = cb;
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/ConstantBuffer-member-funcs.hlsl 
b/clang/test/SemaHLSL/BuiltIns/ConstantBuffer-member-funcs.hlsl
new file mode 100644
index 0000000000000..179a4e1fec101
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/ConstantBuffer-member-funcs.hlsl
@@ -0,0 +1,28 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-compute -x hlsl 
-finclude-default-header -fsyntax-only -verify %s
+
+struct S {
+    float a;
+    
+    float foo() const {
+        return a;
+    };
+
+    void bar() { // expected-note {{'bar' declared here}}
+        a = 1.0;
+    }
+};
+
+ConstantBuffer<S> CB;
+
+[numthreads(4,1,1)]
+void main() {
+    // Bug: https://github.com/llvm/llvm-project/issues/153055
+    // Calling non-const member function is allowed, but not  implemented yet.
+    // We should remove the expected error when done.
+    // expected-error@+1 {{cannot initialize object parameter of type 'const 
S' with an expression of type 'const hlsl_constant S'}}
+    float tmp = CB.foo();
+
+    // Calling non-const member function is not allowed.
+    // expected-error@+1 {{'this' argument to member function 'bar' has type 
'const hlsl_constant S', but function is not marked const}}
+    CB.bar();
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl 
b/clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl
new file mode 100644
index 0000000000000..10c65031b79f2
--- /dev/null
+++ b/clang/test/SemaHLSL/BuiltIns/ConstantBuffers.hlsl
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl 
-finclude-default-header -fsyntax-only -verify %s
+
+struct S { // expected-note 3 {{candidate constructor}}
+  float a;
+  int b;
+};
+
+struct Empty {};
+
+struct ContainsResource {
+  Texture2D tex;
+};
+
+union U {
+  float a;
+  int b;
+};
+
+// Valid
+ConstantBuffer<S> cb;
+ConstantBuffer<Empty> cb_empty;
+
+void takes_inout_s(inout S s) {}
+
+void foo() {
+  // This case should fail because we cannot writeback to `cb` after the call.
+  // expected-error@+1 {{no viable constructor copying parameter of type 
'const hlsl_constant S'}}
+  takes_inout_s(cb);
+}
+
+void test_direct_assignment() {
+  // expected-error@+2 {{cannot assign to return value because function 
'operator const hlsl_constant S &' returns a const value}}
+  // expected-note@* {{function 'operator const hlsl_constant S &' which 
returns const-qualified type 'const hlsl_constant S &' declared here}}
+  cb.a = 5.0;
+}

_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits

Reply via email to