Skip to content
20 changes: 18 additions & 2 deletions clang/lib/CodeGen/CGHLSLRuntime.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -828,11 +828,27 @@ llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {

class OpaqueValueVisitor : public RecursiveASTVisitor<OpaqueValueVisitor> {
public:
llvm::SmallPtrSet<OpaqueValueExpr *, 8> OVEs;
llvm::SmallVector<OpaqueValueExpr *, 8> OVEs;
llvm::SmallPtrSet<OpaqueValueExpr *, 8> Visited;
OpaqueValueVisitor() {}

bool VisitHLSLOutArgExpr(HLSLOutArgExpr *) {
// These need to be bound in CodeGenFunction::EmitHLSLOutArgLValues
// or CodeGenFunction::EmitHLSLOutArgExpr. If they are part of this
// traversal, the temporary containing the copy out will not have
// been created yet.
return false;
}

bool VisitOpaqueValueExpr(OpaqueValueExpr *E) {
OVEs.insert(E);
// Traverse the source expression first.
if (E->getSourceExpr())
TraverseStmt(E->getSourceExpr());

// Then add this OVE if we haven't seen it before.
if (Visited.insert(E).second)
OVEs.push_back(E);

return true;
}
};
Expand Down
69 changes: 69 additions & 0 deletions clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -676,6 +676,75 @@ BuiltinTypeDeclBuilder::addHandleConstructorFromImplicitBinding() {
.finalize();
}

BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyConstructor() {
if (Record->isCompleteDefinition())
return *this;

ASTContext &AST = SemaRef.getASTContext();
QualType RecordType = AST.getCanonicalTagType(Record);
QualType ConstRecordType = RecordType.withConst();
QualType ConstRecordRefType = AST.getLValueReferenceType(ConstRecordType);

using PH = BuiltinTypeMethodBuilder::PlaceHolder;

BuiltinTypeMethodBuilder Builder(*this, "", AST.VoidTy, false, true);
Builder.addParam("other", ConstRecordRefType);
Builder.ensureCompleteDecl();

ParmVarDecl *OtherParam = Builder.Method->getParamDecl(0);

Expr *OtherDeclRef = DeclRefExpr::Create(
AST, NestedNameSpecifierLoc(), SourceLocation(), OtherParam, false,
DeclarationNameInfo(OtherParam->getDeclName(), SourceLocation()),
ConstRecordType, VK_LValue);

FieldDecl *HandleField = getResourceHandleField();
Expr *OtherHandleMemberExpr = MemberExpr::CreateImplicit(
AST, OtherDeclRef, false, HandleField, HandleField->getType(), VK_LValue,
OK_Ordinary);

return Builder.assign(PH::Handle, OtherHandleMemberExpr).finalize();
}

BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyAssignmentOperator() {
if (Record->isCompleteDefinition())
return *this;

ASTContext &AST = SemaRef.getASTContext();
QualType RecordType = AST.getCanonicalTagType(Record);
QualType ConstRecordType = RecordType.withConst();
QualType ConstRecordRefType = AST.getLValueReferenceType(ConstRecordType);
QualType RecordRefType = AST.getLValueReferenceType(RecordType);

using PH = BuiltinTypeMethodBuilder::PlaceHolder;

DeclarationName Name = AST.DeclarationNames.getCXXOperatorName(OO_Equal);
BuiltinTypeMethodBuilder Builder(*this, Name, RecordRefType, false, false);
Builder.addParam("other", ConstRecordRefType);
Builder.ensureCompleteDecl();

ParmVarDecl *OtherParam = Builder.Method->getParamDecl(0);
Expr *OtherDeclRef = DeclRefExpr::Create(
AST, NestedNameSpecifierLoc(), SourceLocation(), OtherParam, false,
DeclarationNameInfo(OtherParam->getDeclName(), SourceLocation()),
ConstRecordType, VK_LValue);

FieldDecl *HandleField = getResourceHandleField();
Expr *OtherHandleMemberExpr = MemberExpr::CreateImplicit(
AST, OtherDeclRef, false, HandleField, HandleField->getType(), VK_LValue,
OK_Ordinary);

Builder.assign(PH::Handle, OtherHandleMemberExpr);

// return *this;
CXXThisExpr *This = CXXThisExpr::Create(
AST, SourceLocation(), Builder.Method->getFunctionObjectParameterType(),
true);
Builder.StmtsList.push_back(This);

return Builder.finalize();
}

BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addArraySubscriptOperators() {
ASTContext &AST = Record->getASTContext();
DeclarationName Subscript =
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ class BuiltinTypeDeclBuilder {
BuiltinTypeDeclBuilder &addDefaultHandleConstructor();
BuiltinTypeDeclBuilder &addHandleConstructorFromBinding();
BuiltinTypeDeclBuilder &addHandleConstructorFromImplicitBinding();
BuiltinTypeDeclBuilder &addCopyConstructor();
BuiltinTypeDeclBuilder &addCopyAssignmentOperator();

// Builtin types methods
BuiltinTypeDeclBuilder &addLoadMethods();
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/HLSLExternalSemaSource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ static BuiltinTypeDeclBuilder setupBufferType(CXXRecordDecl *Decl, Sema &S,
return BuiltinTypeDeclBuilder(S, Decl)
.addHandleMember(RC, IsROV, RawBuffer)
.addDefaultHandleConstructor()
.addCopyConstructor()
.addCopyAssignmentOperator()
.addHandleConstructorFromBinding()
.addHandleConstructorFromImplicitBinding();
}
Expand Down
12 changes: 6 additions & 6 deletions clang/test/CodeGenHLSL/builtins/hlsl_resource_t.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ void fb(CustomResource a) {
CustomResource b = a;
}

// CHECK: define hidden void @_Z2fcN4hlsl8RWBufferIDv4_fEE(ptr noundef byval(%"class.hlsl::RWBuffer") align 4 %a)
// CHECK: call void @_Z4foo2N4hlsl8RWBufferIDv4_fEE(ptr noundef byval(%"class.hlsl::RWBuffer") align 4 %agg.tmp)
// CHECK: declare hidden void @_Z4foo2N4hlsl8RWBufferIDv4_fEE(ptr noundef byval(%"class.hlsl::RWBuffer") align 4)
// CHECK: define hidden void @_Z2fcN4hlsl8RWBufferIDv4_fEE(ptr dead_on_return noundef %a)
// CHECK: call void @_Z4foo2N4hlsl8RWBufferIDv4_fEE(ptr dead_on_return noundef %{{.*}})
// CHECK: declare hidden void @_Z4foo2N4hlsl8RWBufferIDv4_fEE(ptr dead_on_return noundef)
void foo2(RWBuffer<float4> buf);

void fc(RWBuffer<float4> a) {
Expand All @@ -44,9 +44,9 @@ struct MyStruct {
int2 i;
};

// CHECK: define hidden void @_Z2feN4hlsl16StructuredBufferI8MyStructEE(ptr noundef byval(%"class.hlsl::StructuredBuffer") align 4 %a)
// CHECK: call void @_Z4foo3N4hlsl16StructuredBufferI8MyStructEE(ptr noundef byval(%"class.hlsl::StructuredBuffer") align 4 %agg.tmp)
// CHECK: declare hidden void @_Z4foo3N4hlsl16StructuredBufferI8MyStructEE(ptr noundef byval(%"class.hlsl::StructuredBuffer") align 4)
// CHECK: define hidden void @_Z2feN4hlsl16StructuredBufferI8MyStructEE(ptr dead_on_return noundef %a)
// CHECK: call void @_Z4foo3N4hlsl16StructuredBufferI8MyStructEE(ptr dead_on_return noundef %{{.*}})
// CHECK: declare hidden void @_Z4foo3N4hlsl16StructuredBufferI8MyStructEE(ptr dead_on_return noundef)
void foo3(StructuredBuffer<MyStruct> buf);

void fe(StructuredBuffer<MyStruct> a) {
Expand Down
4 changes: 2 additions & 2 deletions clang/test/CodeGenHLSL/debug/rwbuffer_debug_info.hlsl
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -x hlsl -emit-llvm -disable-llvm-passes -o - -hlsl-entry main %s -debug-info-kind=standalone -dwarf-version=4 | FileCheck %s


// CHECK: [[DWTag:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "RWBuffer<float>",
// CHECK: [[DWTag:![0-9]+]] = distinct !DICompositeType(tag: DW_TAG_class_type, name: "RWBuffer<float>",
// CHECK: [[thisType:![0-9]+]] = !DIDerivedType(tag: DW_TAG_reference_type, baseType: [[DWTag]], size: 32)
// CHECK: [[RWBuffer:![0-9]+]] = distinct !DISubprogram(name: "RWBuffer",
// CHECK-SAME: scope: [[DWTag]]
// CHECK: [[FirstThis:![0-9]+]] = !DILocalVariable(name: "this", arg: 1, scope: [[RWBuffer]], type: [[thisType:![0-9]+]]
// CHECK: [[thisType]] = !DIDerivedType(tag: DW_TAG_reference_type, baseType: [[DWTag]], size: 32)
RWBuffer<float> Out : register(u7, space4);

[numthreads(8,1,1)]
Expand Down
2 changes: 1 addition & 1 deletion clang/test/CodeGenHLSL/implicit-norecurse-attrib.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ uint Find(Node SortedTree[MAX], uint key) {
}

// CHECK: Function Attrs:{{.*}}norecurse
// CHECK: define noundef i1 @_Z8InitTreeA100_4NodeN4hlsl8RWBufferIDv4_jEEj(ptr noundef byval([100 x %struct.Node]) align 1 %tree, ptr noundef byval(%"class.hlsl::RWBuffer") align 4 %encodedTree, i32 noundef %maxDepth) [[Attr:\#[0-9]+]]
// CHECK: define noundef i1 @_Z8InitTreeA100_4NodeN4hlsl8RWBufferIDv4_jEEj(ptr noundef byval([100 x %struct.Node]) align 1 %tree, ptr dead_on_return noundef %encodedTree, i32 noundef %maxDepth) [[Attr:\#[0-9]+]]
// CHECK: ret i1
// Initialize tree with given buffer
// Imagine the inout works
Expand Down
114 changes: 65 additions & 49 deletions clang/test/CodeGenHLSL/resources/res-array-local-multi-dim.hlsl
Original file line number Diff line number Diff line change
@@ -1,49 +1,65 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -finclude-default-header \
// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s

// This test verifies handling of multi-dimensional local arrays of resources
// when used as a function argument and local variable.

// CHECK: @_ZL1A = internal global %"class.hlsl::RWBuffer" poison, align 4
// CHECK: @_ZL1B = internal global %"class.hlsl::RWBuffer" poison, align 4

RWBuffer<float> A : register(u10);
RWBuffer<float> B : register(u20);
RWStructuredBuffer<float> Out;

// NOTE: _ZN4hlsl8RWBufferIfEixEj is the subscript operator for RWBuffer<float> and
// _ZN4hlsl18RWStructuredBufferIfEixEj is the subscript operator for RWStructuredBuffer<float>

// CHECK: define {{.*}} float @_Z3fooA2_A2_N4hlsl8RWBufferIfEE(ptr noundef byval([2 x [2 x %"class.hlsl::RWBuffer"]]) align 4 %Arr)
// CHECK-NEXT: entry:
float foo(RWBuffer<float> Arr[2][2]) {
// CHECK-NEXT: %[[Arr_1_Ptr:.*]] = getelementptr inbounds [2 x [2 x %"class.hlsl::RWBuffer"]], ptr %Arr, i32 0, i32 1
// CHECK-NEXT: %[[Arr_1_1_Ptr:.*]] = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %[[Arr_1_Ptr]], i32 0, i32 1
// CHECK-NEXT: %[[BufPtr:.*]] = call {{.*}} ptr @_ZN4hlsl8RWBufferIfEixEj(ptr {{.*}} %[[Arr_1_1_Ptr]], i32 noundef 0)
// CHECK-NEXT: %[[Value:.*]] = load float, ptr %[[BufPtr]], align 4
// CHECK-NEXT: ret float %[[Value]]
return Arr[1][1][0];
}

// CHECK: define internal void @_Z4mainv()
// CHECK-NEXT: entry:
[numthreads(4,1,1)]
void main() {
// CHECK-NEXT: %L = alloca [2 x [2 x %"class.hlsl::RWBuffer"]], align 4
// CHECK-NEXT: %[[Tmp:.*]] = alloca [2 x [2 x %"class.hlsl::RWBuffer"]], align 4
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %L, ptr align 4 @_ZL1A, i32 4, i1 false)
// CHECK-NEXT: %[[Ptr1:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %L, i32 1
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Ptr1]], ptr align 4 @_ZL1B, i32 4, i1 false)
// CHECK-NEXT: %[[Ptr2:.*]] = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %L, i32 1
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Ptr2]], ptr align 4 @_ZL1A, i32 4, i1 false)
// CHECK-NEXT: %[[Ptr3:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %[[Ptr2]], i32 1
// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Ptr3]], ptr align 4 @_ZL1B, i32 4, i1 false)
RWBuffer<float> L[2][2] = { { A, B }, { A, B } };

// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[Tmp]], ptr align 4 %L, i32 16, i1 false)
// CHECK-NEXT: %[[ReturnedValue:.*]] = call {{.*}}float @_Z3fooA2_A2_N4hlsl8RWBufferIfEE(ptr noundef byval([2 x [2 x %"class.hlsl::RWBuffer"]]) align 4 %[[Tmp]])
// CHECK-NEXT: %[[OutBufPtr:.*]] = call {{.*}} ptr @_ZN4hlsl18RWStructuredBufferIfEixEj(ptr {{.*}} @_ZL3Out, i32 noundef 0)
// CHECK-NEXT: store float %[[ReturnedValue]], ptr %[[OutBufPtr]], align 4
// CHECK-NEXT: ret void
Out[0] = foo(L);
}
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-compute -finclude-default-header \
// RUN: -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s

// This test verifies handling of multi-dimensional local arrays of resources
// when used as a function argument and local variable.

// CHECK: @_ZL1A = internal global %"class.hlsl::RWBuffer" poison, align 4
// CHECK: @_ZL1B = internal global %"class.hlsl::RWBuffer" poison, align 4

RWBuffer<float> A : register(u10);
RWBuffer<float> B : register(u20);
RWStructuredBuffer<float> Out;

// NOTE: _ZN4hlsl8RWBufferIfEixEj is the subscript operator for RWBuffer<float> and
// _ZN4hlsl18RWStructuredBufferIfEixEj is the subscript operator for RWStructuredBuffer<float>

// CHECK: define {{.*}} float @_Z3fooA2_A2_N4hlsl8RWBufferIfEE(ptr noundef byval([2 x [2 x %"class.hlsl::RWBuffer"]]) align 4 %Arr)
// CHECK-NEXT: entry:
float foo(RWBuffer<float> Arr[2][2]) {
// CHECK-NEXT: %[[Arr_1_Ptr:.*]] = getelementptr inbounds [2 x [2 x %"class.hlsl::RWBuffer"]], ptr %Arr, i32 0, i32 1
// CHECK-NEXT: %[[Arr_1_1_Ptr:.*]] = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %[[Arr_1_Ptr]], i32 0, i32 1
// CHECK-NEXT: %[[BufPtr:.*]] = call {{.*}} ptr @_ZN4hlsl8RWBufferIfEixEj(ptr {{.*}} %[[Arr_1_1_Ptr]], i32 noundef 0)
// CHECK-NEXT: %[[Value:.*]] = load float, ptr %[[BufPtr]], align 4
// CHECK-NEXT: ret float %[[Value]]
return Arr[1][1][0];
}

// CHECK: define internal void @_Z4mainv()
// CHECK-NEXT: entry:
[numthreads(4,1,1)]
void main() {
// CHECK: %L = alloca [2 x [2 x %"class.hlsl::RWBuffer"]], align 4
// CHECK: %[[ref_tmp:.*]] = alloca %"class.hlsl::RWBuffer", align 4
// CHECK: %[[ref_tmp1:.*]] = alloca %"class.hlsl::RWBuffer", align 4
// CHECK: %[[ref_tmp2:.*]] = alloca %"class.hlsl::RWBuffer", align 4
// CHECK: %[[ref_tmp3:.*]] = alloca %"class.hlsl::RWBuffer", align 4
// CHECK: %[[ref_tmp4:.*]] = alloca %"class.hlsl::RWBuffer", align 4
// CHECK: %[[ref_tmp5:.*]] = alloca %"class.hlsl::RWBuffer", align 4
// CHECK: %[[ref_tmp6:.*]] = alloca %"class.hlsl::RWBuffer", align 4
// CHECK: %[[ref_tmp7:.*]] = alloca %"class.hlsl::RWBuffer", align 4
// CHECK: %[[agg_tmp:.*]] = alloca [2 x [2 x %"class.hlsl::RWBuffer"]], align 4
// CHECK: call void @_ZN4hlsl8RWBufferIfEC1ERKS1_(ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp]], ptr noundef nonnull align 4 dereferenceable(4) @_ZL1A)
// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1ERKS1_(ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp1]], ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp]])
// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1ERKS1_(ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp2]], ptr noundef nonnull align 4 dereferenceable(4) @_ZL1B)
// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1ERKS1_(ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp3]], ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp2]])
// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1ERKS1_(ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp4]], ptr noundef nonnull align 4 dereferenceable(4) @_ZL1A)
// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1ERKS1_(ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp5]], ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp4]])
// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1ERKS1_(ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp6]], ptr noundef nonnull align 4 dereferenceable(4) @_ZL1B)
// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1ERKS1_(ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp7]], ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp6]])
// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1ERKS1_(ptr noundef nonnull align 4 dereferenceable(4) %L, ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp1]])
// CHECK-NEXT: %[[arrayinit_element:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %L, i32 1
// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1ERKS1_(ptr noundef nonnull align 4 dereferenceable(4) %[[arrayinit_element]], ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp3]])
// CHECK-NEXT: %[[arrayinit_element8:.*]] = getelementptr inbounds [2 x %"class.hlsl::RWBuffer"], ptr %L, i32 1
// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1ERKS1_(ptr noundef nonnull align 4 dereferenceable(4) %[[arrayinit_element8]], ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp5]])
// CHECK-NEXT: %[[arrayinit_element9:.*]] = getelementptr inbounds %"class.hlsl::RWBuffer", ptr %[[arrayinit_element8]], i32 1
// CHECK-NEXT: call void @_ZN4hlsl8RWBufferIfEC1ERKS1_(ptr noundef nonnull align 4 dereferenceable(4) %[[arrayinit_element9]], ptr noundef nonnull align 4 dereferenceable(4) %[[ref_tmp7]])
RWBuffer<float> L[2][2] = { { A, B }, { A, B } };

// CHECK: call void @llvm.memcpy.p0.p0.i32(ptr align 4 %[[agg_tmp]], ptr align 4 %L, i32 16, i1 false)
// CHECK-NEXT: %[[ReturnedValue:.*]] = call {{.*}}float @_Z3fooA2_A2_N4hlsl8RWBufferIfEE(ptr noundef byval([2 x [2 x %"class.hlsl::RWBuffer"]]) align 4 %[[agg_tmp]])
// CHECK-NEXT: %[[OutBufPtr:.*]] = call {{.*}} ptr @_ZN4hlsl18RWStructuredBufferIfEixEj(ptr {{.*}} @_ZL3Out, i32 noundef 0)
// CHECK-NEXT: store float %[[ReturnedValue]], ptr %[[OutBufPtr]], align 4
// CHECK-NEXT: ret void
Out[0] = foo(L);
}
Loading
Loading