https://github.com/hekota created https://github.com/llvm/llvm-project/pull/205996
None >From 8e8d54219ed36bc352fd3a0b57919722e4fe2851 Mon Sep 17 00:00:00 2001 From: Helena Kotas <[email protected]> Date: Fri, 26 Jun 2026 00:05:44 -0700 Subject: [PATCH] [HLSL] Enable conversion of ConstantBuffer<T> to T --- clang/include/clang/Sema/Initialization.h | 6 +- clang/include/clang/Sema/SemaHLSL.h | 3 +- clang/lib/Sema/SemaExpr.cpp | 18 +- clang/lib/Sema/SemaExprMember.cpp | 2 +- clang/lib/Sema/SemaHLSL.cpp | 8 +- clang/lib/Sema/SemaInit.cpp | 30 ++- .../AST/HLSL/ConstantBuffers-AST-error.hlsl | 24 -- clang/test/AST/HLSL/ConstantBuffers-AST.hlsl | 31 +++ .../test/CodeGenHLSL/cbuffer_copy_layout.hlsl | 23 -- .../ConstantBufferT-struct-passing.hlsl | 255 ++++++++++++++++++ .../SemaHLSL/Resources/ConstantBuffers.hlsl | 3 +- 11 files changed, 342 insertions(+), 61 deletions(-) delete mode 100644 clang/test/AST/HLSL/ConstantBuffers-AST-error.hlsl delete mode 100644 clang/test/CodeGenHLSL/cbuffer_copy_layout.hlsl create mode 100644 clang/test/CodeGenHLSL/resources/ConstantBufferT-struct-passing.hlsl diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h index 96ea4fae4437c..2c2df1f03747b 100644 --- a/clang/include/clang/Sema/Initialization.h +++ b/clang/include/clang/Sema/Initialization.h @@ -979,7 +979,9 @@ class InitializationSequence { /// Initialize an aggreagate with parenthesized list of values. /// This is a C++20 feature. - SK_ParenthesizedListInit + SK_ParenthesizedListInit, + + SK_HLSLBufferConversion }; /// A single step in the initialization sequence. @@ -1434,6 +1436,8 @@ class InitializationSequence { /// single element and rewrap it at the end. void RewrapReferenceInitList(QualType T, InitListExpr *Syntactic); + void AddHLSLBufferConversionStep(QualType T); + /// Note that this initialization sequence failed. void SetFailed(FailureKind Failure) { SequenceKind = FailedSequence; diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 4a95cb8b2d181..8928524e49783 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -148,8 +148,7 @@ class SemaHLSL : public SemaBase { // 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> - tryPerformConstantBufferConversion(ExprResult &BaseExpr); + std::optional<ExprResult> tryPerformConstantBufferConversion(Expr *BaseExpr); // Returns the conversion operator to convert `RD` to `const hlsl_constant // Type&`. Returns `nullptr` if it could not be found. diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 7c868d176e803..1fa42a0093fe2 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -16135,10 +16135,20 @@ ExprResult Sema::BuildBinOp(Scope *S, SourceLocation OpLoc, RHSExpr = resolvedRHS.get(); } - if (getLangOpts().HLSL && (LHSExpr->getType()->isHLSLResourceRecord() || - LHSExpr->getType()->isHLSLResourceRecordArray())) { - if (!HLSL().CheckResourceBinOp(Opc, LHSExpr, RHSExpr, OpLoc)) - return ExprError(); + if (getLangOpts().HLSL) { + if (LHSExpr->getType()->isHLSLResourceRecord() || + LHSExpr->getType()->isHLSLResourceRecordArray()) { + if (!HLSL().CheckResourceBinOp(Opc, LHSExpr, RHSExpr, OpLoc)) + return ExprError(); + } else if (RHSExpr->getType()->isHLSLResourceRecord()) { + std::optional<ExprResult> ConvRHS = + HLSL().tryPerformConstantBufferConversion(RHSExpr); + if (ConvRHS && Context.hasSameUnqualifiedType( + LHSExpr->getType(), ConvRHS->get()->getType())) { + assert(!ConvRHS->isInvalid()); + RHSExpr = ConvRHS->get(); + } + } } if (getLangOpts().CPlusPlus) { diff --git a/clang/lib/Sema/SemaExprMember.cpp b/clang/lib/Sema/SemaExprMember.cpp index 851d58c49f7b9..62ecc52c0374b 100644 --- a/clang/lib/Sema/SemaExprMember.cpp +++ b/clang/lib/Sema/SemaExprMember.cpp @@ -1298,7 +1298,7 @@ static ExprResult LookupMemberExpr(Sema &S, LookupResult &R, // to access the member. if (S.getLangOpts().HLSL && BaseType->isHLSLResourceRecord()) { if (std::optional<ExprResult> ConvBase = - S.HLSL().tryPerformConstantBufferConversion(BaseExpr)) { + S.HLSL().tryPerformConstantBufferConversion(BaseExpr.get())) { assert(!ConvBase->isInvalid()); BaseExpr = *ConvBase; BaseType = BaseExpr.get()->getType(); diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 075dc97b0aef2..1d4deedcbbf7d 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -3279,8 +3279,8 @@ NamedDecl *SemaHLSL::getConstantBufferConversionFunction(QualType Type, } std::optional<ExprResult> -SemaHLSL::tryPerformConstantBufferConversion(ExprResult &BaseExpr) { - QualType BaseType = BaseExpr.get()->getType(); +SemaHLSL::tryPerformConstantBufferConversion(Expr *BaseExpr) { + QualType BaseType = BaseExpr->getType(); const HLSLAttributedResourceType *ResTy = HLSLAttributedResourceType::findHandleTypeOnResource( BaseType.getTypePtr()); @@ -3297,7 +3297,7 @@ SemaHLSL::tryPerformConstantBufferConversion(ExprResult &BaseExpr) { auto *ConversionDecl = cast<CXXConversionDecl>(NamedConversionDecl->getUnderlyingDecl()); - return SemaRef.BuildCXXMemberCallExpr(BaseExpr.get(), NamedConversionDecl, + return SemaRef.BuildCXXMemberCallExpr(BaseExpr, NamedConversionDecl, ConversionDecl, /*HadMultipleCandidates=*/false); } @@ -5146,7 +5146,7 @@ ExprResult SemaHLSL::ActOnOutParamExpr(ParmVarDecl *Param, Expr *Arg) { // Writebacks are performed with `=` binary operator, which allows for // overload resolution on writeback result expressions. - Res = SemaRef.ActOnBinOp(SemaRef.getCurScope(), Param->getBeginLoc(), + Res = SemaRef.ActOnBinOp(SemaRef.getCurScope(), Arg->getBeginLoc(), tok::equal, ArgOpV, OpV); if (Res.isInvalid()) diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 8f685feac4beb..4f549f3eca108 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -3977,6 +3977,7 @@ void InitializationSequence::Step::Destroy() { case SK_OCLSamplerInit: case SK_OCLZeroOpaqueType: case SK_ParenthesizedListInit: + case SK_HLSLBufferConversion: break; case SK_ConversionSequence: @@ -4306,6 +4307,13 @@ void InitializationSequence::RewrapReferenceInitList(QualType T, Steps.push_back(S); } +void InitializationSequence::AddHLSLBufferConversionStep(QualType T) { + Step S; + S.Kind = SK_HLSLBufferConversion; + S.Type = T; + Steps.push_back(S); +} + void InitializationSequence::SetOverloadFailure(FailureKind Failure, OverloadingResult Result) { setSequenceKind(FailedSequence); @@ -6349,6 +6357,13 @@ static void TryUserDefinedConversion(Sema &S, HadMultipleCandidates); if (ConvType->isRecordType()) { + if (S.getLangOpts().HLSL && + ConvType.getAddressSpace() == LangAS::hlsl_constant && + S.Context.hasSameUnqualifiedType(ConvType, DestType)) { + Sequence.AddHLSLBufferConversionStep(ConvType); + return; + } + // The call is used to direct-initialize [...] the object that is the // destination of the copy-initialization. // @@ -8072,7 +8087,8 @@ ExprResult InitializationSequence::Perform(Sema &S, case SK_ProduceObjCObject: case SK_StdInitializerList: case SK_OCLSamplerInit: - case SK_OCLZeroOpaqueType: { + case SK_OCLZeroOpaqueType: + case SK_HLSLBufferConversion: { assert(Args.size() == 1 || IsHLSLVectorOrMatrixInit); CurInit = Args[0]; if (!CurInit.get()) return ExprError(); @@ -8859,6 +8875,13 @@ ExprResult InitializationSequence::Perform(Sema &S, CurInit = S.MaybeBindToTemporary(CurInit.get()); break; } + case SK_HLSLBufferConversion: { + CurInit = ImplicitCastExpr::Create( + S.Context, Step->Type.getLocalUnqualifiedType(), CK_LValueToRValue, + CurInit.get(), + /*BasePath=*/nullptr, VK_PRValue, FPOptionsOverride()); + break; + } } } @@ -9866,9 +9889,14 @@ void InitializationSequence::dump(raw_ostream &OS) const { case SK_OCLZeroOpaqueType: OS << "OpenCL opaque type from zero"; break; + case SK_ParenthesizedListInit: OS << "initialization from a parenthesized list of values"; break; + + case SK_HLSLBufferConversion: + OS << "HLSL buffer conversion"; + break; } OS << " [" << S->Type << ']'; diff --git a/clang/test/AST/HLSL/ConstantBuffers-AST-error.hlsl b/clang/test/AST/HLSL/ConstantBuffers-AST-error.hlsl deleted file mode 100644 index 4f9d60c741f90..0000000000000 --- a/clang/test/AST/HLSL/ConstantBuffers-AST-error.hlsl +++ /dev/null @@ -1,24 +0,0 @@ -// 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: assigning to 'S' from incompatible type 'ConstantBuffer<S>' - s = cb; -} diff --git a/clang/test/AST/HLSL/ConstantBuffers-AST.hlsl b/clang/test/AST/HLSL/ConstantBuffers-AST.hlsl index 6a880c437db8f..0b51c0fc57543 100644 --- a/clang/test/AST/HLSL/ConstantBuffers-AST.hlsl +++ b/clang/test/AST/HLSL/ConstantBuffers-AST.hlsl @@ -116,5 +116,36 @@ float main() { // CHECK-NEXT: HLSLOutArgExpr {{.*}} 'ConstantBuffer<S>':'hlsl::ConstantBuffer<S>' lvalue inout takes_inout_cb(cb); + // CHECK: DeclStmt + // CHECK-NEXT: VarDecl {{.*}} s2 'S' cinit + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'S' <LValueToRValue> + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'const hlsl_constant S' lvalue <UserDefinedConversion> + // 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>' + S s2 = cb; + + // CHECK: BinaryOperator {{.*}} 'S' lvalue '=' + // CHECK-NEXT: DeclRefExpr {{.*}} 'S' lvalue Var {{.*}} 's' 'S' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'S' <LValueToRValue> + // 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>' + S s; + s = cb; + + // CHECK: CallExpr {{.*}} 'void' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(S)' <FunctionToPointerDecay> + // CHECK-NEXT: DeclRefExpr {{.*}} 'void (S)' lvalue Function {{.*}} 'takes_s' 'void (S)' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'S' <LValueToRValue> + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'const hlsl_constant S' lvalue <UserDefinedConversion> + // 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>' + takes_s(cb); + return f1 + f2 + f3; } diff --git a/clang/test/CodeGenHLSL/cbuffer_copy_layout.hlsl b/clang/test/CodeGenHLSL/cbuffer_copy_layout.hlsl deleted file mode 100644 index 022844284f4ba..0000000000000 --- a/clang/test/CodeGenHLSL/cbuffer_copy_layout.hlsl +++ /dev/null @@ -1,23 +0,0 @@ -// 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() { - 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/CodeGenHLSL/resources/ConstantBufferT-struct-passing.hlsl b/clang/test/CodeGenHLSL/resources/ConstantBufferT-struct-passing.hlsl new file mode 100644 index 0000000000000..a8107894757d2 --- /dev/null +++ b/clang/test/CodeGenHLSL/resources/ConstantBufferT-struct-passing.hlsl @@ -0,0 +1,255 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -emit-llvm -disable-llvm-passes -o - %s | \ +// RUN: llvm-cxxfilt | FileCheck %s -DCONST_ADDR_SPACE=2 -DPADDING_TYPE="dx.Padding" -check-prefixes=CHECK,CHECK-DXIL + +// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-library -finclude-default-header -emit-llvm -disable-llvm-passes -o - %s | \ +// RUN: llvm-cxxfilt | FileCheck %s -DCONST_ADDR_SPACE=12 -DPADDING_TYPE="spirv.Padding" -check-prefixes=CHECK,CHECK-SPIRV + +struct P { + float a; +}; + +struct S : P { + double2 b; +}; + +struct T { + P p; + int arr[2]; +}; + +ConstantBuffer<S> CBS; +ConstantBuffer<T> CBT; + +// CHECK-DXIL: %"class.hlsl::ConstantBuffer" = type { target("dx.CBuffer", %S) } +// CHECK-SPIRV: %"class.hlsl::ConstantBuffer" = type { target("spirv.VulkanBuffer", %S, 2, 0) } +// CHECK: %S = type <{ float, target("[[PADDING_TYPE]]", 12), <2 x double> }> + +// CHECK-DXIL: %"class.hlsl::ConstantBuffer.0" = type { target("dx.CBuffer", %T) } +// CHECK-SPIRV: %"class.hlsl::ConstantBuffer.0" = type { target("spirv.VulkanBuffer", %T, 2, 0) } +// CHECK: %T = type <{ %P, target("[[PADDING_TYPE]]", 12), <{ [1 x <{ i32, target("[[PADDING_TYPE]]", 12) }>], i32 }> }> +// CHECK: %P = type <{ float }> + +// CHECK: %struct.S = type <{ %struct.P, <2 x double> }> +// CHECK: %struct.P = type { float } +// CHECK: %struct.T = type { %struct.P, [2 x i32] } + +// CHECK: @CBS = internal global %"class.hlsl::ConstantBuffer" poison, align {{(4|8)}} +// CHECK: @CBT = internal global %"class.hlsl::ConstantBuffer.0" poison, align {{(4|8)}} + +void useS(S s) {} +void useP(P p) {} +void useT(T t) {} + +// CHECK-LABEL: case1 +void case1() { +// CHECK: %s = alloca %struct.S, align 1 +// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) @hlsl::ConstantBuffer<S>::operator S const AS[[CONST_ADDR_SPACE]]&() const(ptr {{.*}} @CBS) + +// s.a +// CHECK-NEXT: [[CB_A_PTR:%.*]] = getelementptr inbounds %S, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[S_A_PTR:%.*]] = getelementptr inbounds %struct.S, ptr %s, i32 0, i32 0 +// CHECK-NEXT: [[CBUFLOAD:%.*]] = load float, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_A_PTR]], align 4 +// CHECK-NEXT: store float [[CBUFLOAD]], ptr [[S_A_PTR]], align 4 + +// s.b +// CHECK-NEXT: [[CB_B_PTR:%.*]] = getelementptr inbounds %S, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 2 +// CHECK-NEXT: [[S_B_PTR:%.*]] = getelementptr inbounds %struct.S, ptr %s, i32 0, i32 1 +// CHECK-NEXT: [[CBUFLOAD2:%.*]] = load <2 x double>, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_B_PTR]], align 8 +// CHECK-NEXT: store <2 x double> [[CBUFLOAD2]], ptr [[S_B_PTR]], align 8 + S s = CBS; +} + +// CHECK-LABEL: case2 +void case2() { +// CHECK: %s = alloca %struct.S, align 1 +// CHECK: [[TMP:%.*]] = alloca %struct.S, align 1 +// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) @hlsl::ConstantBuffer<S>::operator S const AS[[CONST_ADDR_SPACE]]&() const(ptr {{.*}} @CBS) + +// s.a +// CHECK-NEXT: [[CB_A_PTR:%.*]] = getelementptr inbounds %S, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[S_A_PTR:%.*]] = getelementptr inbounds %struct.S, ptr %s, i32 0, i32 0 +// CHECK-NEXT: [[CBUFLOAD:%.*]] = load float, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_A_PTR]], align 4 +// CHECK-NEXT: store float [[CBUFLOAD]], ptr [[S_A_PTR]], align 4 + +// s.b +// CHECK-NEXT: [[CB_B_PTR:%.*]] = getelementptr inbounds %S, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 2 +// CHECK-NEXT: [[S_B_PTR:%.*]] = getelementptr inbounds %struct.S, ptr %s, i32 0, i32 1 +// CHECK-NEXT: [[CBUFLOAD2:%.*]] = load <2 x double>, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_B_PTR]], align 8 +// CHECK-NEXT: store <2 x double> [[CBUFLOAD2]], ptr [[S_B_PTR]], align 8 + +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i{{(32|64)}}(ptr align 1 [[TMP]], ptr align 1 %s, i{{(32|64)}} 20, i1 false) + S s; + s = CBS; +} + +// CHECK-LABEL: case3 +void case3() { +// CHECK: [[TMP:%.*]] = alloca %struct.S, align 1 +// CHECK-NEXT: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) @hlsl::ConstantBuffer<S>::operator S const AS[[CONST_ADDR_SPACE]]&() const(ptr {{.*}} @CBS) + +// tmp.a +// CHECK-NEXT: [[CB_A_PTR:%.*]] = getelementptr inbounds %S, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[TMP_A_PTR:%.*]] = getelementptr inbounds %struct.S, ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[CBUFLOAD:%.*]] = load float, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_A_PTR]], align 4 +// CHECK-NEXT: store float [[CBUFLOAD]], ptr [[S_A_PTR]], align 4 + +// tmp.b +// CHECK-NEXT: [[CB_B_PTR:%.*]] = getelementptr inbounds %S, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 2 +// CHECK-NEXT: [[TMP_B_PTR:%.*]] = getelementptr inbounds %struct.S, ptr [[TMP]], i32 0, i32 1 +// CHECK-NEXT: [[CBUFLOAD2:%.*]] = load <2 x double>, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_B_PTR]], align 8 +// CHECK-NEXT: store <2 x double> [[CBUFLOAD2]], ptr [[S_B_PTR]], align 8 + +// CHECK-NEXT: call {{.*}}void @useS(S)(ptr noundef dead_on_return [[TMP]]) + useS(CBS); +} + +// CHECK-LABEL: case4 +void case4() { + +// CHECK: %t = alloca %struct.T, align 1 +// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) @hlsl::ConstantBuffer<T>::operator T const AS[[CONST_ADDR_SPACE]]&() const(ptr {{.*}} @CBT) + +// t.p +// CHECK-NEXT: [[CB_P_PTR:%.*]] = getelementptr inbounds %T, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[T_P_PTR:%.*]] = getelementptr inbounds %struct.T, ptr %t, i32 0, i32 0 + +// t.p.a +// CHECK-NEXT: [[CB_P_A_PTR:%.*]] = getelementptr inbounds %P, ptr addrspace([[CONST_ADDR_SPACE]]) %1, i32 0, i32 0 +// CHECK-NEXT: [[T_P_A_PTR:%.*]] = getelementptr inbounds %struct.P, ptr %2, i32 0, i32 0 +// CHECK-NEXT: [[CBUF_LOAD1:%.*]] = load float, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_P_A_PTR]], align 4 +// CHECK-NEXT: store float [[CBUF_LOAD1]], ptr [[T_P_A_PTR]], align 4 + +// t.arr +// CHECK-NEXT: [[CB_ARR_PTR:%.*]] = getelementptr inbounds %T, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 2 +// CHECK-NEXT: [[T_ARR_PTR:%.*]] = getelementptr inbounds %struct.T, ptr %t, i32 0, i32 1 + +// t.arr[0] +// CHECK-NEXT: [[CB_ARR_0_PTR:%.*]] = getelementptr inbounds <{ [1 x <{ i32, target("[[PADDING_TYPE]]", 12) }>], i32 }>, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_PTR]], i32 0, i32 0, i32 0, i32 0 +// CHECK-NEXT: [[T_ARR_0_PTR:%.*]] = getelementptr inbounds [2 x i32], ptr [[T_ARR_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[CBUF_LOAD2:%.*]] = load i32, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_0_PTR]], align 4 +// CHECK-NEXT: store i32 [[CBUF_LOAD2]], ptr [[T_ARR_0_PTR]], align 4 + +// t.arr[1] +// CHECK-NEXT: [[CB_ARR_1_PTR:%.*]] = getelementptr inbounds <{ [1 x <{ i32, target("[[PADDING_TYPE]]", 12) }>], i32 }>, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_PTR]], i32 0, i32 1 +// CHECK-NEXT: [[T_ARR_1_PTR:%.*]] = getelementptr inbounds [2 x i32], ptr [[T_ARR_PTR]], i32 0, i32 1 +// CHECK-NEXT: [[CBUF_LOAD3:%.*]] = load i32, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_1_PTR]], align 4 +// CHECK-NEXT: store i32 [[CBUF_LOAD3]], ptr [[T_ARR_1_PTR]], align 4 + + T t = CBT; +} + +// CHECK-LABEL: case5 +void case5() { +// CHECK: %t = alloca %struct.T, align 1 +// CHECK: [[TMP:%.*]] = alloca %struct.T, align 1 +// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) @hlsl::ConstantBuffer<T>::operator T const AS[[CONST_ADDR_SPACE]]&() const(ptr {{.*}} @CBT) + +// t.p +// CHECK-NEXT: [[CB_P_PTR:%.*]] = getelementptr inbounds %T, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[T_P_PTR:%.*]] = getelementptr inbounds %struct.T, ptr %t, i32 0, i32 0 + +// t.p.a +// CHECK-NEXT: [[CB_P_A_PTR:%.*]] = getelementptr inbounds %P, ptr addrspace([[CONST_ADDR_SPACE]]) %1, i32 0, i32 0 +// CHECK-NEXT: [[T_P_A_PTR:%.*]] = getelementptr inbounds %struct.P, ptr %2, i32 0, i32 0 +// CHECK-NEXT: [[CBUF_LOAD1:%.*]] = load float, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_P_A_PTR]], align 4 +// CHECK-NEXT: store float [[CBUF_LOAD1]], ptr [[T_P_A_PTR]], align 4 + +// t.arr +// CHECK-NEXT: [[CB_ARR_PTR:%.*]] = getelementptr inbounds %T, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 2 +// CHECK-NEXT: [[T_ARR_PTR:%.*]] = getelementptr inbounds %struct.T, ptr %t, i32 0, i32 1 + +// t.arr[0] +// CHECK-NEXT: [[CB_ARR_0_PTR:%.*]] = getelementptr inbounds <{ [1 x <{ i32, target("[[PADDING_TYPE]]", 12) }>], i32 }>, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_PTR]], i32 0, i32 0, i32 0, i32 0 +// CHECK-NEXT: [[T_ARR_0_PTR:%.*]] = getelementptr inbounds [2 x i32], ptr [[T_ARR_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[CBUF_LOAD2:%.*]] = load i32, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_0_PTR]], align 4 +// CHECK-NEXT: store i32 [[CBUF_LOAD2]], ptr [[T_ARR_0_PTR]], align 4 + +// t.arr[1] +// CHECK-NEXT: [[CB_ARR_1_PTR:%.*]] = getelementptr inbounds <{ [1 x <{ i32, target("[[PADDING_TYPE]]", 12) }>], i32 }>, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_PTR]], i32 0, i32 1 +// CHECK-NEXT: [[T_ARR_1_PTR:%.*]] = getelementptr inbounds [2 x i32], ptr [[T_ARR_PTR]], i32 0, i32 1 +// CHECK-NEXT: [[CBUF_LOAD3:%.*]] = load i32, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_1_PTR]], align 4 +// CHECK-NEXT: store i32 [[CBUF_LOAD3]], ptr [[T_ARR_1_PTR]], align 4 + +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i{{(32|64)}}(ptr align 1 [[TMP]], ptr align 1 %t, i{{(32|64)}} 12, i1 false) + + T t; + t = CBT; +} + +// CHECK-LABEL: case6 +void case6() { +// CHECK: [[TMP:%.*]] = alloca %struct.T, align 1 +// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) @hlsl::ConstantBuffer<T>::operator T const AS[[CONST_ADDR_SPACE]]&() const(ptr {{.*}} @CBT) + +// t.p +// CHECK-NEXT: [[CB_P_PTR:%.*]] = getelementptr inbounds %T, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[T_P_PTR:%.*]] = getelementptr inbounds %struct.T, ptr [[TMP]], i32 0, i32 0 + +// t.p.a +// CHECK-NEXT: [[CB_P_A_PTR:%.*]] = getelementptr inbounds %P, ptr addrspace([[CONST_ADDR_SPACE]]) %1, i32 0, i32 0 +// CHECK-NEXT: [[T_P_A_PTR:%.*]] = getelementptr inbounds %struct.P, ptr %2, i32 0, i32 0 +// CHECK-NEXT: [[CBUF_LOAD1:%.*]] = load float, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_P_A_PTR]], align 4 +// CHECK-NEXT: store float [[CBUF_LOAD1]], ptr [[T_P_A_PTR]], align 4 + +// t.arr +// CHECK-NEXT: [[CB_ARR_PTR:%.*]] = getelementptr inbounds %T, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 2 +// CHECK-NEXT: [[T_ARR_PTR:%.*]] = getelementptr inbounds %struct.T, ptr [[TMP]], i32 0, i32 1 + +// t.arr[0] +// CHECK-NEXT: [[CB_ARR_0_PTR:%.*]] = getelementptr inbounds <{ [1 x <{ i32, target("[[PADDING_TYPE]]", 12) }>], i32 }>, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_PTR]], i32 0, i32 0, i32 0, i32 0 +// CHECK-NEXT: [[T_ARR_0_PTR:%.*]] = getelementptr inbounds [2 x i32], ptr [[T_ARR_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[CBUF_LOAD2:%.*]] = load i32, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_0_PTR]], align 4 +// CHECK-NEXT: store i32 [[CBUF_LOAD2]], ptr [[T_ARR_0_PTR]], align 4 + +// t.arr[1] +// CHECK-NEXT: [[CB_ARR_1_PTR:%.*]] = getelementptr inbounds <{ [1 x <{ i32, target("[[PADDING_TYPE]]", 12) }>], i32 }>, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_PTR]], i32 0, i32 1 +// CHECK-NEXT: [[T_ARR_1_PTR:%.*]] = getelementptr inbounds [2 x i32], ptr [[T_ARR_PTR]], i32 0, i32 1 +// CHECK-NEXT: [[CBUF_LOAD3:%.*]] = load i32, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_ARR_1_PTR]], align 4 +// CHECK-NEXT: store i32 [[CBUF_LOAD3]], ptr [[T_ARR_1_PTR]], align 4 + useT(CBT); +} + +// CHECK-LABEL: case7 +void case7() { +// CHECK: %p = alloca %struct.P, align 1 +// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) @hlsl::ConstantBuffer<T>::operator T const AS[[CONST_ADDR_SPACE]]&() const(ptr {{.*}} @CBT) + +// CHECK-NEXT: [[CB_P_PTR:%.*]] = getelementptr inbounds nuw %T, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[CB_P_A_PTR:%.*]] = getelementptr inbounds %P, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_P_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[P_A_PTR:%.*]] = getelementptr inbounds %struct.P, ptr %p, i32 0, i32 0 +// CHECK-NEXT: [[CBUF_LOAD:%.*]] = load float, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_P_A_PTR]], align 4 +// CHECK-NEXT: store float [[CBUF_LOAD]], ptr [[P_A_PTR]], align 4 + P p = CBT.p; +} + +// CHECK-LABEL: case8 +void case8() { +// CHECK: %p = alloca %struct.P, align 1 +// CHECK: [[TMP:%.*]] = alloca %struct.P, align 1 +// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) @hlsl::ConstantBuffer<T>::operator T const AS[[CONST_ADDR_SPACE]]&() const(ptr {{.*}} @CBT) + +// CHECK-NEXT: [[CB_P_PTR:%.*]] = getelementptr inbounds nuw %T, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[CB_P_A_PTR:%.*]] = getelementptr inbounds %P, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_P_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[P_A_PTR:%.*]] = getelementptr inbounds %struct.P, ptr %p, i32 0, i32 0 +// CHECK-NEXT: [[CBUF_LOAD:%.*]] = load float, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_P_A_PTR]], align 4 +// CHECK-NEXT: store float [[CBUF_LOAD]], ptr [[P_A_PTR]], align 4 + +// CHECK-NEXT: call void @llvm.memcpy.p0.p0.i{{(32|64)}}(ptr align 1 [[TMP]], ptr align 1 %p, i{{(32|64)}} 4, i1 false) + P p; + p = CBT.p; +} + +// CHECK-LABEL: case9 +void case9() { +// CHECK: [[TMP:%.*]] = alloca %struct.P, align 1 +// CHECK: [[CB_PTR:%.*]] = call {{.*}} ptr addrspace([[CONST_ADDR_SPACE]]) @hlsl::ConstantBuffer<T>::operator T const AS[[CONST_ADDR_SPACE]]&() const(ptr {{.*}} @CBT) + +// CHECK-NEXT: [[CB_P_PTR:%.*]] = getelementptr inbounds nuw %T, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[CB_P_A_PTR:%.*]] = getelementptr inbounds %P, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_P_PTR]], i32 0, i32 0 +// CHECK-NEXT: [[P_A_PTR:%.*]] = getelementptr inbounds %struct.P, ptr [[TMP]], i32 0, i32 0 +// CHECK-NEXT: [[CBUF_LOAD:%.*]] = load float, ptr addrspace([[CONST_ADDR_SPACE]]) [[CB_P_A_PTR]], align 4 +// CHECK-NEXT: store float [[CBUF_LOAD]], ptr [[P_A_PTR]], align 4 + +// CHECK-NEXT: call {{.*}}void @useP(P)(ptr noundef dead_on_return [[TMP]]) + useP(CBT.p); +} diff --git a/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl b/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl index 6dc566a58b5e4..540fecd6f6c42 100644 --- a/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl +++ b/clang/test/SemaHLSL/Resources/ConstantBuffers.hlsl @@ -63,7 +63,8 @@ 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'}} + // expected-error@+2 {{no viable overloaded '='}} + // expected-note@*:* {{candidate function not viable: no known conversion from 'S' to 'const hlsl::ConstantBuffer<S>' for 1st argument}} takes_inout_s(cb); } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
