llvmorg-github-actions[bot] wrote:

<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Helena Kotas (hekota)

<details>
<summary>Changes</summary>

Structs in constant buffers are in the `hlsl_constant` address space and in a 
specific constant buffer layout. In order to invoke a method on such struct, it 
first needs to be copied out into temporary variable in a standard layout. This 
change adds `Lvalue_to_Rvalue` cast to the method `this` argument which takes 
care of that.

Fixes #<!-- -->190299

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


5 Files Affected:

- (modified) clang/lib/Sema/SemaOverload.cpp (+8) 
- (modified) clang/test/AST/HLSL/ConstantBuffers-AST.hlsl (+14-1) 
- (added) clang/test/AST/HLSL/resources-in-structs-method-call.hlsl (+36) 
- (added) 
clang/test/CodeGenHLSL/resources/resources-in-structs-method-call.hlsl (+85) 
- (modified) clang/test/SemaHLSL/BuiltIns/ConstantBuffer-member-funcs.hlsl 
(+1-5) 


``````````diff
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index c663765573612..c8b05a9f94bd0 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -6271,6 +6271,14 @@ ExprResult 
Sema::PerformImplicitObjectArgumentInitialization(
   QualType FromRecordType, DestType;
   QualType ImplicitParamRecordType = Method->getFunctionObjectParameterType();
 
+  if (getLangOpts().HLSL &&
+      From->getType().getAddressSpace() == LangAS::hlsl_constant) {
+    QualType CastType = From->getType().getLocalUnqualifiedType().withConst();
+    From = ImplicitCastExpr::Create(Context, CastType, CK_LValueToRValue, From,
+                                    /*BasePath=*/nullptr, VK_PRValue,
+                                    FPOptionsOverride());
+  }
+
   Expr::Classification FromClassification;
   if (const PointerType *PT = From->getType()->getAs<PointerType>()) {
     FromRecordType = PT->getPointeeType();
diff --git a/clang/test/AST/HLSL/ConstantBuffers-AST.hlsl 
b/clang/test/AST/HLSL/ConstantBuffers-AST.hlsl
index 6a880c437db8f..327d39a225b0b 100644
--- a/clang/test/AST/HLSL/ConstantBuffers-AST.hlsl
+++ b/clang/test/AST/HLSL/ConstantBuffers-AST.hlsl
@@ -71,6 +71,7 @@ ConstantBuffer<S> cb;
 struct Nested {
   S s;
   float b;
+  float getA() const { return s.a; }
 };
 ConstantBuffer<Nested> cb_nested;
 
@@ -116,5 +117,17 @@ float main() {
   // CHECK-NEXT: HLSLOutArgExpr {{.*}} 
'ConstantBuffer<S>':'hlsl::ConstantBuffer<S>' lvalue inout
   takes_inout_cb(cb);
 
-  return f1 + f2 + f3;
+  // CHECK: VarDecl {{.*}} a 'float'
+  // CHECK-NEXT: ExprWithCleanups {{.*}} 'float'
+  // CHECK-NEXT: CXXMemberCallExpr {{.*}} 'float'
+  // CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .getA
+  // CHECK-NEXT: MaterializeTemporaryExpr {{.*}} 'const Nested' lvalue
+  // CHECK-NEXT: ImplicitCastExpr {{.*}} 'const Nested' <LValueToRValue>
+  // 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 a = cb_nested.getA();
+   
+  return f1 + f2 + f3 + a;
 }
diff --git a/clang/test/AST/HLSL/resources-in-structs-method-call.hlsl 
b/clang/test/AST/HLSL/resources-in-structs-method-call.hlsl
new file mode 100644
index 0000000000000..ef8be74671533
--- /dev/null
+++ b/clang/test/AST/HLSL/resources-in-structs-method-call.hlsl
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -ast-dump %s | 
FileCheck %s
+
+struct MyStruct {
+  float f;
+  RWBuffer<float> Buf;
+
+  void Store() const {
+    Buf[0] = f;
+  }
+};
+
+cbuffer CB {
+  MyStruct one;
+}
+
+MyStruct two;
+
+// CHECK: FunctionDecl {{.*}} main 'void ()'
+[numthreads(1, 1, 1)]
+void main() {
+// CHECK: ExprWithCleanups {{.*}} 'void'
+// CHECK-NEXT: CXXMemberCallExpr {{.*}} 'void'
+// CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .Store
+// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} 'const MyStruct' lvalue
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'const MyStruct' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'hlsl_constant MyStruct' lvalue Var {{.*}} 
'one' 'hlsl_constant MyStruct'
+  one.Store();
+
+// CHECK: ExprWithCleanups {{.*}} 'void'
+// CHECK-NEXT: CXXMemberCallExpr {{.*}} 'void'
+// CHECK-NEXT: MemberExpr {{.*}} '<bound member function type>' .Store {{.*}}
+// CHECK-NEXT: MaterializeTemporaryExpr {{.*}} 'const MyStruct' lvalue
+// CHECK-NEXT: ImplicitCastExpr {{.*}} 'const MyStruct' <LValueToRValue>
+// CHECK-NEXT: DeclRefExpr {{.*}} 'hlsl_constant MyStruct' lvalue Var {{.*}} 
'two' 'hlsl_constant MyStruct'
+  two.Store();
+}
diff --git 
a/clang/test/CodeGenHLSL/resources/resources-in-structs-method-call.hlsl 
b/clang/test/CodeGenHLSL/resources/resources-in-structs-method-call.hlsl
new file mode 100644
index 0000000000000..29019dd6f99c3
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/resources-in-structs-method-call.hlsl
@@ -0,0 +1,85 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -emit-llvm 
-disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | 
FileCheck %s
+
+struct MyStruct {
+  float f;
+  RWBuffer<float> Buf;
+
+  void Store() const {
+    Buf[0] = f;
+  }
+};
+
+// CHECK-DAG: %"class.hlsl::RWBuffer" = type { target("dx.TypedBuffer", float, 
1, 0, 0) }
+// CHECK-DAG: %__cblayout_CB = type <{ %__cblayout_MyStruct }>
+// CHECK-DAG: %__cblayout_MyStruct = type <{ float }>
+// CHECK-DAG: %struct.MyStruct = type { float, %"class.hlsl::RWBuffer" }
+cbuffer CB {
+  MyStruct one;
+}
+
+// CHECK-DAG: @one.Buf = internal global %"class.hlsl::RWBuffer" poison, align 
4
+// CHECK-DAG: @CB.cb = internal global target("dx.CBuffer", %__cblayout_CB) 
poison
+// CHECK-DAG: @one = external hidden addrspace(2) global %__cblayout_MyStruct, 
align 4
+
+// $Globals constant buffer
+// CHECK-DAG: @"$Globals.cb" = internal global target("dx.CBuffer", 
%"__cblayout_$Globals") poison
+// CHECK-DAG: %"__cblayout_$Globals" = type <{ %__cblayout_MyStruct }>
+// CHECK-DAG: @two.Buf = internal global %"class.hlsl::RWBuffer" poison, align 
4
+// CHECK-DAG: @two = external hidden addrspace(2) global %struct.MyStruct, 
align 4
+MyStruct two;
+
+// CHECK-DAG: @Constants = internal global %"class.hlsl::ConstantBuffer" 
poison, align 4
+// CHECK-DAG: %MyConstants = type <{ <4 x float>, <3 x i32> }>
+// CHECK-DAG: %struct.MyConstants = type { <4 x float>, <3 x i32> }
+
+struct MyConstants {
+  float4 vec;
+  int3 pos;
+  int3 getPosition() const { return pos; }
+};
+
+ConstantBuffer<MyConstants> Constants;
+
+// CHECK-LABEL: main
+[numthreads(4,1,1)]
+void main() {
+// CHECK: [[TMP1:%.*]] = alloca %struct.MyStruct, align 4
+// CHECK-NEXT: [[TMP2:%.*]] = alloca %struct.MyStruct, align 4
+// CHECK-NEXT: %pos = alloca <3 x i32>, align 4
+// CHECK-NEXT: [[TMP3:%.*]] = alloca %struct.MyConstants, align 1
+
+// Make sure we copy the struct from the constant buffer element by element to 
a temporary
+// variable and then call the method on that.
+
+// CHECK-NEXT: [[TMP1_F_PTR:%.*]] = getelementptr inbounds %struct.MyStruct, 
ptr [[TMP1]], i32 0, i32 0
+// CHECK-NEXT: [[CBUFLOAD0:%.*]] = load float, ptr addrspace(2) @one, align 4
+// CHECK-NEXT: store float [[CBUFLOAD0]], ptr [[TMP1_F_PTR]], align 4
+// CHECK-NEXT: [[TMP1_BUF_PTR:%.*]] = getelementptr inbounds %struct.MyStruct, 
ptr [[TMP1]], i32 0, i32 1
+// CHECK-NEXT: store ptr @one.Buf, ptr [[TMP1_BUF_PTR]], align 4
+// CHECK-NEXT: call void @MyStruct::Store() const(ptr {{.*}} [[TMP1]])
+  one.Store();
+
+// CHECK-NEXT: [[TMP2_F_PTR:%.*]] = getelementptr inbounds %struct.MyStruct, 
ptr [[TMP2]], i32 0, i32 0
+// CHECK-NEXT: [[CBUFLOAD2:%.*]] = load float, ptr addrspace(2) @two, align 4
+// CHECK-NEXT: store float [[CBUFLOAD2]], ptr [[TMP2_F_PTR]], align 4
+// CHECK-NEXT: [[TMP2_BUF_PTR:%.*]] = getelementptr inbounds %struct.MyStruct, 
ptr [[TMP2]], i32 0, i32 1
+// CHECK-NEXT: store ptr @two.Buf, ptr [[TMP2_BUF_PTR]], align 4
+// CHECK-NEXT: call void @MyStruct::Store() const(ptr {{.*}} [[TMP2]])
+  two.Store();
+  
+// CHECK-NEXT: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace(2) 
@hlsl::ConstantBuffer<MyConstants>::operator MyConstants const AS2&() const(ptr 
{{.*}} @Constants)
+
+// CHECK-NEXT: [[CB_PTR_VEC:%.*]] = getelementptr inbounds %MyConstants, ptr 
addrspace(2) [[CB_PTR]], i32 0, i32 0
+// CHECK-NEXT: [[TMP3_VEC_PTR:%.*]] = getelementptr inbounds 
%struct.MyConstants, ptr [[TMP3]], i32 0, i32 0
+// CHECK-NEXT: [[CBUFLOAD3:%.*]] = load <4 x float>, ptr addrspace(2) 
[[CB_PTR_VEC]], align 4
+// CHECK-NEXT: store <4 x float> [[CBUFLOAD3]], ptr [[TMP3_VEC_PTR]], align 4
+
+// CHECK-NEXT: [[CB_POS_PTR:%.*]] = getelementptr inbounds %MyConstants, ptr 
addrspace(2) [[CB_PTR]], i32 0, i32 1
+// CHECK-NEXT: [[TMP3_POS_PTR:%.*]] = getelementptr inbounds 
%struct.MyConstants, ptr [[TMP3]], i32 0, i32 1
+// CHECK-NEXT: [[CBUFLOAD4:%.*]] = load <3 x i32>, ptr addrspace(2) 
[[CB_POS_PTR]], align 4
+// CHECK-NEXT: store <3 x i32> [[CBUFLOAD4]], ptr [[TMP3_POS_PTR]], align 4
+
+// CHECK-NEXT: [[POS:%.*]] = call noundef <3 x i32> 
@MyConstants::getPosition() const(ptr {{.*}} [[TMP3]])
+// CHECK-NEXT: store <3 x i32> [[POS]], ptr %pos, align 4
+  int3 pos = Constants.getPosition();
+}
diff --git a/clang/test/SemaHLSL/BuiltIns/ConstantBuffer-member-funcs.hlsl 
b/clang/test/SemaHLSL/BuiltIns/ConstantBuffer-member-funcs.hlsl
index 179a4e1fec101..a5d8862294cf6 100644
--- a/clang/test/SemaHLSL/BuiltIns/ConstantBuffer-member-funcs.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/ConstantBuffer-member-funcs.hlsl
@@ -16,13 +16,9 @@ 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}}
+    // expected-error@+1 {{'this' argument to member function 'bar' has type 
'const S', but function is not marked const}}
     CB.bar();
 }

``````````

</details>


https://github.com/llvm/llvm-project/pull/206596
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to