Author: Steven Perron Date: 2026-03-12T07:08:26-04:00 New Revision: f3752dcb60fc89d1af7533b9faaa4c53eb1d2118
URL: https://github.com/llvm/llvm-project/commit/f3752dcb60fc89d1af7533b9faaa4c53eb1d2118 DIFF: https://github.com/llvm/llvm-project/commit/f3752dcb60fc89d1af7533b9faaa4c53eb1d2118.diff LOG: [HLSL] Implement Texture2D::Load methods and builtin (#185708) Implements the Textur2D::Load methods. A new HLSL buildin is added to implement the method. The HLSL builtin is lowered to the resource_load_level intrinsic. We chose to have have a single operand hold both the coordinate and the level in the builtin, as is done in the Load method itself. This was to make the external sema source easier. It is easier to split the vector during codegen than in sema. Assisted-by: Gemini Added: clang/test/CodeGenHLSL/resources/Texture2D-Load.hlsl clang/test/SemaHLSL/Texture2D-Load-errors.hlsl Modified: clang/include/clang/Basic/Builtins.td clang/lib/CodeGen/CGHLSLBuiltins.cpp clang/lib/CodeGen/CGHLSLRuntime.h clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h clang/lib/Sema/HLSLExternalSemaSource.cpp clang/lib/Sema/SemaHLSL.cpp clang/test/AST/HLSL/StructuredBuffers-AST.hlsl clang/test/AST/HLSL/Texture2D-scalar-AST.hlsl clang/test/AST/HLSL/Texture2D-vector-AST.hlsl clang/test/AST/HLSL/TypedBuffers-AST.hlsl Removed: ################################################################################ diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index dd5bd689c08d2..4805c93eef2bf 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -5036,6 +5036,12 @@ def HLSLResourceLoadWithStatusTyped : LangBuiltin<"HLSL_LANG"> { let Prototype = "void(...)"; } +def HLSLResourceLoadLevel : LangBuiltin<"HLSL_LANG"> { + let Spellings = ["__builtin_hlsl_resource_load_level"]; + let Attributes = [NoThrow]; + let Prototype = "void(...)"; +} + def HLSLResourceSample : LangBuiltin<"HLSL_LANG"> { let Spellings = ["__builtin_hlsl_resource_sample"]; let Attributes = [NoThrow]; diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp index a5db9d8562662..677f33eb938a7 100644 --- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp +++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp @@ -449,16 +449,18 @@ static std::string getSpecConstantFunctionName(clang::QualType SpecConstantType, return Name; } +static llvm::Type *getOffsetType(CodeGenModule &CGM, llvm::Type *CoordTy) { + llvm::Type *Int32Ty = CGM.Int32Ty; + if (auto *VT = dyn_cast<llvm::FixedVectorType>(CoordTy)) + return llvm::FixedVectorType::get(Int32Ty, VT->getNumElements()); + return Int32Ty; +} + static Value *emitHlslOffset(CodeGenFunction &CGF, const CallExpr *E, - unsigned OffsetArgIndex) { + unsigned OffsetArgIndex, llvm::Type *OffsetTy) { if (E->getNumArgs() > OffsetArgIndex) return CGF.EmitScalarExpr(E->getArg(OffsetArgIndex)); - llvm::Type *CoordTy = CGF.ConvertType(E->getArg(2)->getType()); - llvm::Type *Int32Ty = CGF.Int32Ty; - llvm::Type *OffsetTy = Int32Ty; - if (auto *VT = dyn_cast<llvm::FixedVectorType>(CoordTy)) - OffsetTy = llvm::FixedVectorType::get(Int32Ty, VT->getNumElements()); return llvm::Constant::getNullValue(OffsetTy); } @@ -554,7 +556,8 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Args.push_back(HandleOp); Args.push_back(SamplerOp); Args.push_back(CoordOp); - Args.push_back(emitHlslOffset(*this, E, 3)); + Args.push_back( + emitHlslOffset(*this, E, 3, getOffsetType(CGM, CoordOp->getType()))); llvm::Type *RetTy = ConvertType(E->getType()); if (E->getNumArgs() <= 4) { @@ -579,7 +582,8 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Args.push_back(SamplerOp); Args.push_back(CoordOp); Args.push_back(BiasOp); - Args.push_back(emitHlslOffset(*this, E, 4)); + Args.push_back( + emitHlslOffset(*this, E, 4, getOffsetType(CGM, CoordOp->getType()))); llvm::Type *RetTy = ConvertType(E->getType()); if (E->getNumArgs() <= 5) @@ -603,7 +607,8 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Args.push_back(CoordOp); Args.push_back(DDXOp); Args.push_back(DDYOp); - Args.push_back(emitHlslOffset(*this, E, 5)); + Args.push_back( + emitHlslOffset(*this, E, 5, getOffsetType(CGM, CoordOp->getType()))); llvm::Type *RetTy = ConvertType(E->getType()); @@ -629,12 +634,42 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Args.push_back(SamplerOp); Args.push_back(CoordOp); Args.push_back(LODOp); - Args.push_back(emitHlslOffset(*this, E, 4)); + Args.push_back( + emitHlslOffset(*this, E, 4, getOffsetType(CGM, CoordOp->getType()))); llvm::Type *RetTy = ConvertType(E->getType()); return Builder.CreateIntrinsic( RetTy, CGM.getHLSLRuntime().getSampleLevelIntrinsic(), Args); } + case Builtin::BI__builtin_hlsl_resource_load_level: { + Value *HandleOp = EmitScalarExpr(E->getArg(0)); + Value *CoordLODOp = EmitScalarExpr(E->getArg(1)); + + auto *CoordLODVecTy = cast<llvm::FixedVectorType>(CoordLODOp->getType()); + unsigned NumElts = CoordLODVecTy->getNumElements(); + assert(NumElts >= 2 && "CoordLOD must have at least 2 elements"); + + // Split CoordLOD into Coord and LOD + SmallVector<int, 4> Mask; + for (unsigned I = 0; I < NumElts - 1; ++I) + Mask.push_back(I); + + Value *CoordOp = + Builder.CreateShuffleVector(CoordLODOp, Mask, "hlsl.load.coord"); + Value *LODOp = + Builder.CreateExtractElement(CoordLODOp, NumElts - 1, "hlsl.load.lod"); + + SmallVector<Value *, 4> Args; + Args.push_back(HandleOp); + Args.push_back(CoordOp); + Args.push_back(LODOp); + Args.push_back( + emitHlslOffset(*this, E, 2, getOffsetType(CGM, CoordOp->getType()))); + + llvm::Type *RetTy = ConvertType(E->getType()); + return Builder.CreateIntrinsic( + RetTy, CGM.getHLSLRuntime().getLoadLevelIntrinsic(), Args); + } case Builtin::BI__builtin_hlsl_resource_sample_cmp: { Value *HandleOp = EmitScalarExpr(E->getArg(0)); Value *SamplerOp = EmitScalarExpr(E->getArg(1)); @@ -648,7 +683,8 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Args.push_back(SamplerOp); Args.push_back(CoordOp); Args.push_back(CmpOp); - Args.push_back(emitHlslOffset(*this, E, 4)); + Args.push_back( + emitHlslOffset(*this, E, 4, getOffsetType(CGM, CoordOp->getType()))); llvm::Type *RetTy = ConvertType(E->getType()); if (E->getNumArgs() <= 5) { @@ -674,7 +710,8 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Args.push_back(CoordOp); Args.push_back(CmpOp); - Args.push_back(emitHlslOffset(*this, E, 4)); + Args.push_back( + emitHlslOffset(*this, E, 4, getOffsetType(CGM, CoordOp->getType()))); llvm::Type *RetTy = ConvertType(E->getType()); return Builder.CreateIntrinsic( @@ -694,7 +731,8 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Args.push_back(SamplerOp); Args.push_back(CoordOp); Args.push_back(ComponentOp); - Args.push_back(emitHlslOffset(*this, E, 4)); + Args.push_back( + emitHlslOffset(*this, E, 4, getOffsetType(CGM, CoordOp->getType()))); llvm::Type *RetTy = ConvertType(E->getType()); return Builder.CreateIntrinsic( @@ -722,7 +760,8 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, Args.push_back(ComponentOp); } - Args.push_back(emitHlslOffset(*this, E, 5)); + Args.push_back( + emitHlslOffset(*this, E, 5, getOffsetType(CGM, CoordOp->getType()))); llvm::Type *RetTy = ConvertType(E->getType()); return Builder.CreateIntrinsic( diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index 4d3e089ca7140..10b72f450dd2c 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -188,6 +188,7 @@ class CGHLSLRuntime { GENERATE_HLSL_INTRINSIC_FUNCTION(GroupMemoryBarrierWithGroupSync, group_memory_barrier_with_group_sync) GENERATE_HLSL_INTRINSIC_FUNCTION(GetDimensionsX, resource_getdimensions_x) + GENERATE_HLSL_INTRINSIC_FUNCTION(LoadLevel, resource_load_level) GENERATE_HLSL_INTRINSIC_FUNCTION(DdxCoarse, ddx_coarse) GENERATE_HLSL_INTRINSIC_FUNCTION(DdyCoarse, ddy_coarse) GENERATE_HLSL_INTRINSIC_FUNCTION(DdxFine, ddx_fine) diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp index f9f57824bb48c..708b06bc1d03a 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp @@ -1256,6 +1256,34 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addLoadMethods() { return *this; } +BuiltinTypeDeclBuilder & +BuiltinTypeDeclBuilder::addTextureLoadMethods(ResourceDimension Dim) { + assert(!Record->isCompleteDefinition() && "record is already complete"); + ASTContext &AST = Record->getASTContext(); + uint32_t VecSize = getResourceDimensions(Dim); + QualType IntTy = AST.IntTy; + QualType OffsetTy = AST.getExtVectorType(IntTy, VecSize); + QualType LocationTy = AST.getExtVectorType(IntTy, VecSize + 1); + QualType ReturnType = getHandleElementType(); + + using PH = BuiltinTypeMethodBuilder::PlaceHolder; + + // T Load(int3 location) + BuiltinTypeMethodBuilder(*this, "Load", ReturnType) + .addParam("Location", LocationTy) + .callBuiltin("__builtin_hlsl_resource_load_level", ReturnType, PH::Handle, + PH::_0) + .finalize(); + + // T Load(int3 location, int2 offset) + return BuiltinTypeMethodBuilder(*this, "Load", ReturnType) + .addParam("Location", LocationTy) + .addParam("Offset", OffsetTy) + .callBuiltin("__builtin_hlsl_resource_load_level", ReturnType, PH::Handle, + PH::_0, PH::_1) + .finalize(); +} + BuiltinTypeDeclBuilder & BuiltinTypeDeclBuilder::addByteAddressBufferLoadMethods() { assert(!Record->isCompleteDefinition() && "record is already complete"); diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h index 6e68c1f33d723..5a0b9a02defdb 100644 --- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h +++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h @@ -95,6 +95,7 @@ class BuiltinTypeDeclBuilder { // Builtin types methods BuiltinTypeDeclBuilder &addLoadMethods(); + BuiltinTypeDeclBuilder &addTextureLoadMethods(ResourceDimension Dim); BuiltinTypeDeclBuilder &addByteAddressBufferLoadMethods(); BuiltinTypeDeclBuilder &addByteAddressBufferStoreMethods(); BuiltinTypeDeclBuilder &addSampleMethods(ResourceDimension Dim); diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp index bb86ef4e063e8..e2267ac503b73 100644 --- a/clang/lib/Sema/HLSLExternalSemaSource.cpp +++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp @@ -258,6 +258,7 @@ static BuiltinTypeDeclBuilder setupTextureType(CXXRecordDecl *Decl, Sema &S, ResourceDimension Dim) { return BuiltinTypeDeclBuilder(S, Decl) .addTextureHandle(RC, IsROV, Dim) + .addTextureLoadMethods(Dim) .addDefaultHandleConstructor() .addCopyConstructor() .addCopyAssignmentOperator() diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 413c602febf51..e200c595ab2c2 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -3507,6 +3507,50 @@ static bool CheckGatherBuiltin(Sema &S, CallExpr *TheCall, bool IsCmp) { return false; } +static bool CheckLoadLevelBuiltin(Sema &S, CallExpr *TheCall) { + if (S.checkArgCountRange(TheCall, 2, 3)) + return true; + + // Check the texture handle. + if (CheckResourceHandle(&S, TheCall, 0, + [](const HLSLAttributedResourceType *ResType) { + return ResType->getAttrs().ResourceDimension == + llvm::dxil::ResourceDimension::Unknown; + })) + return true; + + auto *ResourceTy = + TheCall->getArg(0)->getType()->castAs<HLSLAttributedResourceType>(); + + // Check the location + lod (int3 for Texture2D). + unsigned ExpectedDim = + getResourceDimensions(ResourceTy->getAttrs().ResourceDimension); + QualType CoordLODTy = TheCall->getArg(1)->getType(); + if (CheckVectorElementCount(&S, CoordLODTy, S.Context.IntTy, ExpectedDim + 1, + TheCall->getArg(1)->getBeginLoc())) + return true; + + QualType EltTy = CoordLODTy; + if (const auto *VTy = EltTy->getAs<VectorType>()) + EltTy = VTy->getElementType(); + if (!EltTy->isIntegerType()) { + S.Diag(TheCall->getArg(1)->getBeginLoc(), diag::err_typecheck_expect_int) + << CoordLODTy; + return true; + } + + // Check the offset operand. + if (TheCall->getNumArgs() > 2) { + if (CheckVectorElementCount(&S, TheCall->getArg(2)->getType(), + S.Context.IntTy, ExpectedDim, + TheCall->getArg(2)->getBeginLoc())) + return true; + } + + TheCall->setType(ResourceTy->getContainedType()); + return false; +} + static bool CheckSamplingBuiltin(Sema &S, CallExpr *TheCall, SampleKind Kind) { unsigned MinArgs, MaxArgs; if (Kind == SampleKind::Sample) { @@ -3727,6 +3771,8 @@ bool SemaHLSL::CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { break; } + case Builtin::BI__builtin_hlsl_resource_load_level: + return CheckLoadLevelBuiltin(SemaRef, TheCall); case Builtin::BI__builtin_hlsl_resource_sample: return CheckSamplingBuiltin(SemaRef, TheCall, SampleKind::Sample); case Builtin::BI__builtin_hlsl_resource_sample_bias: diff --git a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl index c6411ccf77075..7ab03bcf8fac1 100644 --- a/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl +++ b/clang/test/AST/HLSL/StructuredBuffers-AST.hlsl @@ -288,7 +288,7 @@ RESOURCE<float> Buffer; // CHECK-SUBSCRIPT-NEXT: DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'Index' 'unsigned int' // CHECK-SUBSCRIPT-NEXT: AlwaysInlineAttr {{.*}} Implicit always_inline -// CHECK-SUBSCRIPT-UAV-NEXT: CXXMethodDecl {{.*}} operator[] 'hlsl_device element_type &(unsigned int)' +// CHECK-SUBSCRIPT-UAV: CXXMethodDecl {{.*}} operator[] 'hlsl_device element_type &(unsigned int)' // CHECK-SUBSCRIPT-UAV-NEXT: ParmVarDecl {{.*}} Index 'unsigned int' // CHECK-SUBSCRIPT-UAV-NEXT: CompoundStmt // CHECK-SUBSCRIPT-UAV-NEXT: ReturnStmt @@ -364,7 +364,7 @@ RESOURCE<float> Buffer; // DecrementCounter method -// CHECK-COUNTER-NEXT: CXXMethodDecl {{.*}} DecrementCounter 'unsigned int ()' +// CHECK-COUNTER: CXXMethodDecl {{.*}} DecrementCounter 'unsigned int ()' // CHECK-COUNTER-NEXT: CompoundStmt // CHECK-COUNTER-NEXT: ReturnStmt // CHECK-COUNTER-NEXT: CStyleCastExpr {{.*}} 'unsigned int' diff --git a/clang/test/AST/HLSL/Texture2D-scalar-AST.hlsl b/clang/test/AST/HLSL/Texture2D-scalar-AST.hlsl index 921c92da5cb2b..a1d0341681df5 100644 --- a/clang/test/AST/HLSL/Texture2D-scalar-AST.hlsl +++ b/clang/test/AST/HLSL/Texture2D-scalar-AST.hlsl @@ -19,6 +19,40 @@ // CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] // CHECK-SAME{LITERAL}: [[hlsl::resource_dimension(2D)]] +// CHECK: CXXMethodDecl {{.*}} Load 'element_type (vector<int, 3>)' +// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<int, 3>' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: ReturnStmt +// CHECK-NEXT: CStyleCastExpr {{.*}} 'element_type' <Dependent> +// CHECK-NEXT: CallExpr {{.*}} '<dependent type>' +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_load_level' 'void (...) noexcept' +// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] +// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] +// CHECK-SAME{LITERAL}: [[hlsl::resource_dimension(2D)]] +// CHECK-SAME: ' lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::Texture2D<element_type>' lvalue implicit this +// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 3>' lvalue ParmVar {{.*}} 'Location' 'vector<int, 3>' +// CHECK-NEXT: AlwaysInlineAttr + +// CHECK: CXXMethodDecl {{.*}} Load 'element_type (vector<int, 3>, vector<int, 2>)' +// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<int, 3>' +// CHECK-NEXT: ParmVarDecl {{.*}} Offset 'vector<int, 2>' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: ReturnStmt +// CHECK-NEXT: CStyleCastExpr {{.*}} 'element_type' <Dependent> +// CHECK-NEXT: CallExpr {{.*}} '<dependent type>' +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_load_level' 'void (...) noexcept' +// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] +// CHECK-SAME{LITERAL}: [[hlsl::contained_type(element_type)]] +// CHECK-SAME{LITERAL}: [[hlsl::resource_dimension(2D)]] +// CHECK-SAME: ' lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::Texture2D<element_type>' lvalue implicit this +// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 3>' lvalue ParmVar {{.*}} 'Location' 'vector<int, 3>' +// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 2>' lvalue ParmVar {{.*}} 'Offset' 'vector<int, 2>' +// CHECK-NEXT: AlwaysInlineAttr + // CHECK: CXXMethodDecl {{.*}} Sample 'element_type (hlsl::SamplerState, vector<float, 2>)' // CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerState' // CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>' diff --git a/clang/test/AST/HLSL/Texture2D-vector-AST.hlsl b/clang/test/AST/HLSL/Texture2D-vector-AST.hlsl index 0e056009c1a38..097232a50ceb9 100644 --- a/clang/test/AST/HLSL/Texture2D-vector-AST.hlsl +++ b/clang/test/AST/HLSL/Texture2D-vector-AST.hlsl @@ -23,6 +23,40 @@ // CHECK-SAME{LITERAL}: [[hlsl::contained_type(vector<element_type, element_count>)]] // CHECK-SAME{LITERAL}: [[hlsl::resource_dimension(2D)]] +// CHECK: CXXMethodDecl {{.*}} Load 'vector<element_type, element_count> (vector<int, 3>)' +// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<int, 3>' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: ReturnStmt +// CHECK-NEXT: CStyleCastExpr {{.*}} 'vector<element_type, element_count>' <Dependent> +// CHECK-NEXT: CallExpr {{.*}} '<dependent type>' +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_load_level' 'void (...) noexcept' +// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] +// CHECK-SAME{LITERAL}: [[hlsl::contained_type(vector<element_type, element_count>)]] +// CHECK-SAME{LITERAL}: [[hlsl::resource_dimension(2D)]] +// CHECK-SAME: ' lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::Texture2D<vector<element_type, element_count>>' lvalue implicit this +// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 3>' lvalue ParmVar {{.*}} 'Location' 'vector<int, 3>' +// CHECK-NEXT: AlwaysInlineAttr + +// CHECK: CXXMethodDecl {{.*}} Load 'vector<element_type, element_count> (vector<int, 3>, vector<int, 2>)' +// CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<int, 3>' +// CHECK-NEXT: ParmVarDecl {{.*}} Offset 'vector<int, 2>' +// CHECK-NEXT: CompoundStmt +// CHECK-NEXT: ReturnStmt +// CHECK-NEXT: CStyleCastExpr {{.*}} 'vector<element_type, element_count>' <Dependent> +// CHECK-NEXT: CallExpr {{.*}} '<dependent type>' +// CHECK-NEXT: DeclRefExpr {{.*}} '<builtin fn type>' Function {{.*}} '__builtin_hlsl_resource_load_level' 'void (...) noexcept' +// CHECK-NEXT: MemberExpr {{.*}} '__hlsl_resource_t +// CHECK-SAME{LITERAL}: [[hlsl::resource_class(SRV)]] +// CHECK-SAME{LITERAL}: [[hlsl::contained_type(vector<element_type, element_count>)]] +// CHECK-SAME{LITERAL}: [[hlsl::resource_dimension(2D)]] +// CHECK-SAME: ' lvalue .__handle +// CHECK-NEXT: CXXThisExpr {{.*}} 'hlsl::Texture2D<vector<element_type, element_count>>' lvalue implicit this +// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 3>' lvalue ParmVar {{.*}} 'Location' 'vector<int, 3>' +// CHECK-NEXT: DeclRefExpr {{.*}} 'vector<int, 2>' lvalue ParmVar {{.*}} 'Offset' 'vector<int, 2>' +// CHECK-NEXT: AlwaysInlineAttr + // CHECK: CXXMethodDecl {{.*}} Sample 'vector<element_type, element_count> (hlsl::SamplerState, vector<float, 2>)' // CHECK-NEXT: ParmVarDecl {{.*}} Sampler 'hlsl::SamplerState' // CHECK-NEXT: ParmVarDecl {{.*}} Location 'vector<float, 2>' diff --git a/clang/test/AST/HLSL/TypedBuffers-AST.hlsl b/clang/test/AST/HLSL/TypedBuffers-AST.hlsl index c2144d230887b..035c4e59844da 100644 --- a/clang/test/AST/HLSL/TypedBuffers-AST.hlsl +++ b/clang/test/AST/HLSL/TypedBuffers-AST.hlsl @@ -179,7 +179,7 @@ RESOURCE<float> Buffer; // CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int' lvalue ParmVar {{.*}} 'Index' 'unsigned int' // CHECK-NEXT: AlwaysInlineAttr {{.*}} Implicit always_inline -// CHECK-UAV-NEXT: CXXMethodDecl {{.*}} operator[] 'hlsl_device element_type &(unsigned int)' +// CHECK-UAV: CXXMethodDecl {{.*}} operator[] 'hlsl_device element_type &(unsigned int)' // CHECK-UAV-NEXT: ParmVarDecl {{.*}} Index 'unsigned int' // CHECK-UAV-NEXT: CompoundStmt // CHECK-UAV-NEXT: ReturnStmt @@ -197,7 +197,7 @@ RESOURCE<float> Buffer; // Load method -// CHECK-NEXT: CXXMethodDecl {{.*}} Load 'element_type (unsigned int)' +// CHECK: CXXMethodDecl {{.*}} Load 'element_type (unsigned int)' // CHECK-NEXT: ParmVarDecl {{.*}} Index 'unsigned int' // CHECK-NEXT: CompoundStmt // CHECK-NEXT: ReturnStmt @@ -235,7 +235,7 @@ RESOURCE<float> Buffer; // GetDimensions method -// CHECK-NEXT: CXXMethodDecl {{.*}} GetDimensions 'void (out unsigned int)' +// CHECK: CXXMethodDecl {{.*}} GetDimensions 'void (out unsigned int)' // CHECK-NEXT: ParmVarDecl {{.*}} dim 'unsigned int &__restrict' // CHECK-NEXT: HLSLParamModifierAttr {{.*}} out // CHECK-NEXT: CompoundStmt diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-Load.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-Load.hlsl new file mode 100644 index 0000000000000..7c30a211149e7 --- /dev/null +++ b/clang/test/CodeGenHLSL/resources/Texture2D-Load.hlsl @@ -0,0 +1,238 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | FileCheck %s --check-prefixes=CHECK,DXIL +// RUN: %clang_cc1 -triple spirv-vulkan-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | FileCheck %s --check-prefixes=CHECK,SPIRV + +Texture2D<float4> t; + +// CHECK: define hidden {{.*}} <4 x float> @test_load(int vector[2]) +// CHECK: %[[COORD:.*]] = insertelement <3 x i32> {{.*}}, i32 0, i32 2 +// CHECK: %[[CALL:.*]] = call {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::Load(int vector[3])(ptr {{.*}} @t, <3 x i32> noundef %[[COORD]]) +// CHECK: ret <4 x float> %[[CALL]] + +float4 test_load(int2 loc : LOC) : SV_Target { + return t.Load(int3(loc, 0)); +} + +// CHECK: define linkonce_odr hidden {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::Load(int vector[3])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr +// CHECK: %[[LOCATION_ADDR:.*]] = alloca <3 x i32> +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]] +// CHECK: store <3 x i32> %[[LOCATION]], ptr %[[LOCATION_ADDR]] +// CHECK: %[[THIS_VAL:.*]] = load ptr, ptr %[[THIS_ADDR]] +// CHECK: %[[HANDLE_GEP:.*]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL]], i32 0, i32 0 +// CHECK: %[[HANDLE:.*]] = load target("{{(dx.Texture|spirv.Image)}}", {{.*}}), ptr %[[HANDLE_GEP]] +// CHECK: %[[LOCATION_VAL:.*]] = load <3 x i32>, ptr %[[LOCATION_ADDR]] +// CHECK: %[[COORD:.*]] = shufflevector <3 x i32> %[[LOCATION_VAL]], <3 x i32> poison, <2 x i32> <i32 0, i32 1> +// CHECK: %[[LOD:.*]] = extractelement <3 x i32> %[[LOCATION_VAL]], i64 2 +// DXIL: %[[RES:.*]] = call {{.*}} <4 x float> @llvm.dx.resource.load.level.v4f32.tdx.Texture_v4f32_0_0_0_2t.v2i32.i32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE]], <2 x i32> %[[COORD]], i32 %[[LOD]], <2 x i32> zeroinitializer) +// SPIRV: %[[RES:.*]] = call {{.*}} <4 x float> @llvm.spv.resource.load.level.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE]], <2 x i32> %[[COORD]], i32 %[[LOD]], <2 x i32> zeroinitializer) +// CHECK: ret <4 x float> %[[RES]] + +// CHECK: define hidden {{.*}} <4 x float> @test_load_offset(int vector[2]) +// CHECK: %[[COORD:.*]] = insertelement <3 x i32> {{.*}}, i32 0, i32 2 +// CHECK: %[[CALL:.*]] = call {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::Load(int vector[3], int vector[2])(ptr {{.*}} @t, <3 x i32> noundef %[[COORD]], <2 x i32> noundef splat (i32 1)) +// CHECK: ret <4 x float> %[[CALL]] + +float4 test_load_offset(int2 loc : LOC) : SV_Target { + return t.Load(int3(loc, 0), int2(1, 1)); +} + +// CHECK: define linkonce_odr hidden {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::Load(int vector[3], int vector[2])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]], <2 x i32> {{.*}} %[[OFFSET:.*]]) +// CHECK: %[[THIS_ADDR:.*]] = alloca ptr +// CHECK: %[[LOCATION_ADDR:.*]] = alloca <3 x i32> +// CHECK: %[[OFFSET_ADDR:.*]] = alloca <2 x i32> +// CHECK: store ptr %[[THIS]], ptr %[[THIS_ADDR]] +// CHECK: store <3 x i32> %[[LOCATION]], ptr %[[LOCATION_ADDR]] +// CHECK: store <2 x i32> %[[OFFSET]], ptr %[[OFFSET_ADDR]] +// CHECK: %[[THIS_VAL:.*]] = load ptr, ptr %[[THIS_ADDR]] +// CHECK: %[[HANDLE_GEP:.*]] = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %[[THIS_VAL]], i32 0, i32 0 +// CHECK: %[[HANDLE:.*]] = load target("{{(dx.Texture|spirv.Image)}}", {{.*}}), ptr %[[HANDLE_GEP]] +// CHECK: %[[LOCATION_VAL:.*]] = load <3 x i32>, ptr %[[LOCATION_ADDR]] +// CHECK: %[[COORD:.*]] = shufflevector <3 x i32> %[[LOCATION_VAL]], <3 x i32> poison, <2 x i32> <i32 0, i32 1> +// CHECK: %[[LOD:.*]] = extractelement <3 x i32> %[[LOCATION_VAL]], i64 2 +// CHECK: %[[OFFSET_VAL:.*]] = load <2 x i32>, ptr %[[OFFSET_ADDR]] +// DXIL: %[[RES:.*]] = call {{.*}} <4 x float> @llvm.dx.resource.load.level.v4f32.tdx.Texture_v4f32_0_0_0_2t.v2i32.i32.v2i32(target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE]], <2 x i32> %[[COORD]], i32 %[[LOD]], <2 x i32> %[[OFFSET_VAL]]) +// SPIRV: %[[RES:.*]] = call {{.*}} <4 x float> @llvm.spv.resource.load.level.v4f32.tspirv.Image_f32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %[[HANDLE]], <2 x i32> %[[COORD]], i32 %[[LOD]], <2 x i32> %[[OFFSET_VAL]]) +// CHECK: ret <4 x float> %[[RES]] + + +// For the rest of the types, we just check that the call to the member +// function has the correct return type. + +Texture2D<float> t_float; + +// CHECK: define hidden {{.*}} float @test_load_float(int vector[2]) +// CHECK: define linkonce_odr hidden {{.*}} float @hlsl::Texture2D<float>::Load(int vector[3])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]]) +// DXIL: %[[RES:.*]] = call {{.*}} float @llvm.dx.resource.load.level.f32.tdx.Texture_f32_0_0_0_2t.v2i32.i32.v2i32(target("dx.Texture", float, 0, 0, 0, 2) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// SPIRV: %[[RES:.*]] = call {{.*}} float @llvm.spv.resource.load.level.f32.tspirv.Image_f32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// CHECK: ret float %[[RES]] +float test_load_float(int2 loc : LOC) { + return t_float.Load(int3(loc, 0)); +} + +// CHECK: define hidden {{.*}} float @test_load_offset_float(int vector[2]) +// CHECK: %[[CALL:.*]] = call {{.*}} float @hlsl::Texture2D<float>::Load(int vector[3], int vector[2])(ptr {{.*}} @t_float, <3 x i32> noundef %{{.*}}, <2 x i32> noundef splat (i32 1)) +// CHECK: ret float %[[CALL]] +float test_load_offset_float(int2 loc : LOC) { + return t_float.Load(int3(loc, 0), int2(1, 1)); +} + +// CHECK: define linkonce_odr hidden {{.*}} float @hlsl::Texture2D<float>::Load(int vector[3], int vector[2])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]], <2 x i32> {{.*}} %[[OFFSET:.*]]) +// DXIL: %[[RES:.*]] = call {{.*}} float @llvm.dx.resource.load.level.f32.tdx.Texture_f32_0_0_0_2t.v2i32.i32.v2i32(target("dx.Texture", float, 0, 0, 0, 2) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +// SPIRV: %[[RES:.*]] = call {{.*}} float @llvm.spv.resource.load.level.f32.tspirv.Image_f32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +// CHECK: ret float %[[RES]] + +Texture2D<float2> t_float2; + +// CHECK: define hidden {{.*}} <2 x float> @test_load_float2(int vector[2]) +// CHECK: %[[CALL:.*]] = call {{.*}} <2 x float> @hlsl::Texture2D<float vector[2]>::Load(int vector[3])(ptr {{.*}} @t_float2, <3 x i32> noundef %{{.*}}) +// CHECK: ret <2 x float> %[[CALL]] +float2 test_load_float2(int2 loc : LOC) { + return t_float2.Load(int3(loc, 0)); +} + +// CHECK: define linkonce_odr hidden {{.*}} <2 x float> @hlsl::Texture2D<float vector[2]>::Load(int vector[3])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]]) +// DXIL: %[[RES:.*]] = call {{.*}} <2 x float> @llvm.dx.resource.load.level.v2f32.tdx.Texture_v2f32_0_0_0_2t.v2i32.i32.v2i32(target("dx.Texture", <2 x float>, 0, 0, 0, 2) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// SPIRV: %[[RES:.*]] = call {{.*}} <2 x float> @llvm.spv.resource.load.level.v2f32.tspirv.Image_f32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// CHECK: ret <2 x float> %[[RES]] + +// CHECK: define hidden {{.*}} <2 x float> @test_load_offset_float2(int vector[2]) +// CHECK: %[[CALL:.*]] = call {{.*}} <2 x float> @hlsl::Texture2D<float vector[2]>::Load(int vector[3], int vector[2])(ptr {{.*}} @t_float2, <3 x i32> noundef %{{.*}}, <2 x i32> noundef splat (i32 1)) +// CHECK: ret <2 x float> %[[CALL]] +float2 test_load_offset_float2(int2 loc : LOC) { + return t_float2.Load(int3(loc, 0), int2(1, 1)); +} + +// CHECK: define linkonce_odr hidden {{.*}} <2 x float> @hlsl::Texture2D<float vector[2]>::Load(int vector[3], int vector[2])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]], <2 x i32> {{.*}} %[[OFFSET:.*]]) +// DXIL: %[[RES:.*]] = call {{.*}} <2 x float> @llvm.dx.resource.load.level.v2f32.tdx.Texture_v2f32_0_0_0_2t.v2i32.i32.v2i32(target("dx.Texture", <2 x float>, 0, 0, 0, 2) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +// SPIRV: %[[RES:.*]] = call {{.*}} <2 x float> @llvm.spv.resource.load.level.v2f32.tspirv.Image_f32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +// CHECK: ret <2 x float> %[[RES]] + +Texture2D<float3> t_float3; + +// CHECK: define hidden {{.*}} <3 x float> @test_load_float3(int vector[2]) +// CHECK: %[[CALL:.*]] = call {{.*}} <3 x float> @hlsl::Texture2D<float vector[3]>::Load(int vector[3])(ptr {{.*}} @t_float3, <3 x i32> noundef %{{.*}}) +// CHECK: ret <3 x float> %[[CALL]] +float3 test_load_float3(int2 loc : LOC) { + return t_float3.Load(int3(loc, 0)); +} + +// CHECK: define linkonce_odr hidden {{.*}} <3 x float> @hlsl::Texture2D<float vector[3]>::Load(int vector[3])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]]) +// DXIL: %[[RES:.*]] = call {{.*}} <3 x float> @llvm.dx.resource.load.level.v3f32.tdx.Texture_v3f32_0_0_0_2t.v2i32.i32.v2i32(target("dx.Texture", <3 x float>, 0, 0, 0, 2) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// SPIRV: %[[RES:.*]] = call {{.*}} <3 x float> @llvm.spv.resource.load.level.v3f32.tspirv.Image_f32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// CHECK: ret <3 x float> %[[RES]] + +// CHECK: define hidden {{.*}} <3 x float> @test_load_offset_float3(int vector[2]) +// CHECK: %[[CALL:.*]] = call {{.*}} <3 x float> @hlsl::Texture2D<float vector[3]>::Load(int vector[3], int vector[2])(ptr {{.*}} @t_float3, <3 x i32> noundef %{{.*}}, <2 x i32> noundef splat (i32 1)) +// CHECK: ret <3 x float> %[[CALL]] +float3 test_load_offset_float3(int2 loc : LOC) { + return t_float3.Load(int3(loc, 0), int2(1, 1)); +} + +// CHECK: define linkonce_odr hidden {{.*}} <3 x float> @hlsl::Texture2D<float vector[3]>::Load(int vector[3], int vector[2])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]], <2 x i32> {{.*}} %[[OFFSET:.*]]) +// DXIL: %[[RES:.*]] = call {{.*}} <3 x float> @llvm.dx.resource.load.level.v3f32.tdx.Texture_v3f32_0_0_0_2t.v2i32.i32.v2i32(target("dx.Texture", <3 x float>, 0, 0, 0, 2) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +// SPIRV: %[[RES:.*]] = call {{.*}} <3 x float> @llvm.spv.resource.load.level.v3f32.tspirv.Image_f32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.Image", float, 1, 2, 0, 0, 1, 0) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +// CHECK: ret <3 x float> %[[RES]] + +Texture2D<int> t_int; + +// CHECK: define hidden {{.*}} i32 @test_load_int(int vector[2]) +// CHECK: %[[CALL:.*]] = call {{.*}} i32 @hlsl::Texture2D<int>::Load(int vector[3])(ptr {{.*}} @t_int, <3 x i32> noundef %{{.*}}) +// CHECK: ret i32 %[[CALL]] +int test_load_int(int2 loc : LOC) { + return t_int.Load(int3(loc, 0)); +} + +// CHECK: define linkonce_odr hidden {{.*}} i32 @hlsl::Texture2D<int>::Load(int vector[3])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]]) +// DXIL: %[[RES:.*]] = call i32 @llvm.dx.resource.load.level.i32.tdx.Texture_i32_0_0_1_2t.v2i32.i32.v2i32(target("dx.Texture", i32, 0, 0, 1, 2) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// SPIRV: %[[RES:.*]] = call i32 @llvm.spv.resource.load.level.i32.tspirv.SignedImage_i32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.SignedImage", i32, 1, 2, 0, 0, 1, 0) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// CHECK: ret i32 %[[RES]] + +// CHECK: define hidden {{.*}} i32 @test_load_offset_int(int vector[2]) +// CHECK: %[[CALL:.*]] = call {{.*}} i32 @hlsl::Texture2D<int>::Load(int vector[3], int vector[2])(ptr {{.*}} @t_int, <3 x i32> noundef %{{.*}}, <2 x i32> noundef splat (i32 1)) +// CHECK: ret i32 %[[CALL]] +int test_load_offset_int(int2 loc : LOC) { + return t_int.Load(int3(loc, 0), int2(1, 1)); +} + +// CHECK: define linkonce_odr hidden {{.*}} i32 @hlsl::Texture2D<int>::Load(int vector[3], int vector[2])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]], <2 x i32> {{.*}} %[[OFFSET:.*]]) +// DXIL: %[[RES:.*]] = call i32 @llvm.dx.resource.load.level.i32.tdx.Texture_i32_0_0_1_2t.v2i32.i32.v2i32(target("dx.Texture", i32, 0, 0, 1, 2) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +// SPIRV: %[[RES:.*]] = call i32 @llvm.spv.resource.load.level.i32.tspirv.SignedImage_i32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.SignedImage", i32, 1, 2, 0, 0, 1, 0) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +// CHECK: ret i32 %[[RES]] + +Texture2D<int2> t_int2; + +// CHECK: define hidden {{.*}} <2 x i32> @test_load_int2(int vector[2]) +// CHECK: %[[CALL:.*]] = call {{.*}} <2 x i32> @hlsl::Texture2D<int vector[2]>::Load(int vector[3])(ptr {{.*}} @t_int2, <3 x i32> noundef %{{.*}}) +// CHECK: ret <2 x i32> %[[CALL]] +int2 test_load_int2(int2 loc : LOC) { + return t_int2.Load(int3(loc, 0)); +} + +// CHECK: define linkonce_odr hidden {{.*}} <2 x i32> @hlsl::Texture2D<int vector[2]>::Load(int vector[3])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]]) +// DXIL: %[[RES:.*]] = call <2 x i32> @llvm.dx.resource.load.level.v2i32.tdx.Texture_v2i32_0_0_1_2t.v2i32.i32.v2i32(target("dx.Texture", <2 x i32>, 0, 0, 1, 2) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// SPIRV: %[[RES:.*]] = call <2 x i32> @llvm.spv.resource.load.level.v2i32.tspirv.SignedImage_i32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.SignedImage", i32, 1, 2, 0, 0, 1, 0) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// CHECK: ret <2 x i32> %[[RES]] + +// CHECK: define hidden {{.*}} <2 x i32> @test_load_offset_int2(int vector[2]) +// CHECK: %[[CALL:.*]] = call {{.*}} <2 x i32> @hlsl::Texture2D<int vector[2]>::Load(int vector[3], int vector[2])(ptr {{.*}} @t_int2, <3 x i32> noundef %{{.*}}, <2 x i32> noundef splat (i32 1)) +// CHECK: ret <2 x i32> %[[CALL]] +int2 test_load_offset_int2(int2 loc : LOC) { + return t_int2.Load(int3(loc, 0), int2(1, 1)); +} + +// CHECK: define linkonce_odr hidden {{.*}} <2 x i32> @hlsl::Texture2D<int vector[2]>::Load(int vector[3], int vector[2])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]], <2 x i32> {{.*}} %[[OFFSET:.*]]) +// DXIL: %[[RES:.*]] = call <2 x i32> @llvm.dx.resource.load.level.v2i32.tdx.Texture_v2i32_0_0_1_2t.v2i32.i32.v2i32(target("dx.Texture", <2 x i32>, 0, 0, 1, 2) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +// SPIRV: %[[RES:.*]] = call <2 x i32> @llvm.spv.resource.load.level.v2i32.tspirv.SignedImage_i32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.SignedImage", i32, 1, 2, 0, 0, 1, 0) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +// CHECK: ret <2 x i32> %[[RES]] + +Texture2D<int3> t_int3; + +// CHECK: define hidden {{.*}} <3 x i32> @test_load_int3(int vector[2]) +// CHECK: %[[CALL:.*]] = call {{.*}} <3 x i32> @hlsl::Texture2D<int vector[3]>::Load(int vector[3])(ptr {{.*}} @t_int3, <3 x i32> noundef %{{.*}}) +// CHECK: ret <3 x i32> %[[CALL]] +int3 test_load_int3(int2 loc : LOC) { + return t_int3.Load(int3(loc, 0)); +} + +// CHECK: define linkonce_odr hidden {{.*}} <3 x i32> @hlsl::Texture2D<int vector[3]>::Load(int vector[3])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]]) +// DXIL: %[[RES:.*]] = call <3 x i32> @llvm.dx.resource.load.level.v3i32.tdx.Texture_v3i32_0_0_1_2t.v2i32.i32.v2i32(target("dx.Texture", <3 x i32>, 0, 0, 1, 2) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// SPIRV: %[[RES:.*]] = call <3 x i32> @llvm.spv.resource.load.level.v3i32.tspirv.SignedImage_i32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.SignedImage", i32, 1, 2, 0, 0, 1, 0) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// CHECK: ret <3 x i32> %[[RES]] + +// CHECK: define hidden {{.*}} <3 x i32> @test_load_offset_int3(int vector[2]) +// CHECK: %[[CALL:.*]] = call {{.*}} <3 x i32> @hlsl::Texture2D<int vector[3]>::Load(int vector[3], int vector[2])(ptr {{.*}} @t_int3, <3 x i32> noundef %{{.*}}, <2 x i32> noundef splat (i32 1)) +// CHECK: ret <3 x i32> %[[CALL]] +int3 test_load_offset_int3(int2 loc : LOC) { + return t_int3.Load(int3(loc, 0), int2(1, 1)); +} + +// CHECK: define linkonce_odr hidden {{.*}} <3 x i32> @hlsl::Texture2D<int vector[3]>::Load(int vector[3], int vector[2])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]], <2 x i32> {{.*}} %[[OFFSET:.*]]) +// DXIL: %[[RES:.*]] = call <3 x i32> @llvm.dx.resource.load.level.v3i32.tdx.Texture_v3i32_0_0_1_2t.v2i32.i32.v2i32(target("dx.Texture", <3 x i32>, 0, 0, 1, 2) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +// SPIRV: %[[RES:.*]] = call <3 x i32> @llvm.spv.resource.load.level.v3i32.tspirv.SignedImage_i32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.SignedImage", i32, 1, 2, 0, 0, 1, 0) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +// CHECK: ret <3 x i32> %[[RES]] + +Texture2D<int4> t_int4; + +// CHECK: define hidden {{.*}} <4 x i32> @test_load_int4(int vector[2]) +// CHECK: %[[CALL:.*]] = call {{.*}} <4 x i32> @hlsl::Texture2D<int vector[4]>::Load(int vector[3])(ptr {{.*}} @t_int4, <3 x i32> noundef %{{.*}}) +// CHECK: ret <4 x i32> %[[CALL]] +int4 test_load_int4(int2 loc : LOC) { + return t_int4.Load(int3(loc, 0)); +} + +// CHECK: define linkonce_odr hidden {{.*}} <4 x i32> @hlsl::Texture2D<int vector[4]>::Load(int vector[3])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]]) +// DXIL: %[[RES:.*]] = call <4 x i32> @llvm.dx.resource.load.level.v4i32.tdx.Texture_v4i32_0_0_1_2t.v2i32.i32.v2i32(target("dx.Texture", <4 x i32>, 0, 0, 1, 2) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// SPIRV: %[[RES:.*]] = call <4 x i32> @llvm.spv.resource.load.level.v4i32.tspirv.SignedImage_i32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.SignedImage", i32, 1, 2, 0, 0, 1, 0) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> zeroinitializer) +// CHECK: ret <4 x i32> %[[RES]] + +// CHECK: define hidden {{.*}} <4 x i32> @test_load_offset_int4(int vector[2]) +// CHECK: %[[CALL:.*]] = call {{.*}} <4 x i32> @hlsl::Texture2D<int vector[4]>::Load(int vector[3], int vector[2])(ptr {{.*}} @t_int4, <3 x i32> noundef %{{.*}}, <2 x i32> noundef splat (i32 1)) +// CHECK: ret <4 x i32> %[[CALL]] +int4 test_load_offset_int4(int2 loc : LOC) { + return t_int4.Load(int3(loc, 0), int2(1, 1)); +} + +// CHECK: define linkonce_odr hidden {{.*}} <4 x i32> @hlsl::Texture2D<int vector[4]>::Load(int vector[3], int vector[2])(ptr {{.*}} %[[THIS:.*]], <3 x i32> {{.*}} %[[LOCATION:.*]], <2 x i32> {{.*}} %[[OFFSET:.*]]) +// DXIL: %[[RES:.*]] = call <4 x i32> @llvm.dx.resource.load.level.v4i32.tdx.Texture_v4i32_0_0_1_2t.v2i32.i32.v2i32(target("dx.Texture", <4 x i32>, 0, 0, 1, 2) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +// SPIRV: %[[RES:.*]] = call <4 x i32> @llvm.spv.resource.load.level.v4i32.tspirv.SignedImage_i32_1_2_0_0_1_0t.v2i32.i32.v2i32(target("spirv.SignedImage", i32, 1, 2, 0, 0, 1, 0) %{{.*}}, <2 x i32> %{{.*}}, i32 %{{.*}}, <2 x i32> %{{.*}}) +// CHECK: ret <4 x i32> %[[RES]] diff --git a/clang/test/SemaHLSL/Texture2D-Load-errors.hlsl b/clang/test/SemaHLSL/Texture2D-Load-errors.hlsl new file mode 100644 index 0000000000000..9027c2ea144b8 --- /dev/null +++ b/clang/test/SemaHLSL/Texture2D-Load-errors.hlsl @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -finclude-default-header -verify %s + +Texture2D<float4> t; + +float4 test_too_few_args() { + return t.Load(); // expected-error {{no matching member function for call to 'Load'}} + // expected-note@*:* {{candidate function not viable: requires single argument 'Location', but no arguments were provided}} + // expected-note@*:* {{candidate function not viable: requires 2 arguments, but 0 were provided}} +} + +float4 test_too_many_args(int2 loc) { + return t.Load(int3(loc, 0), int2(1, 1), 1); // expected-error {{no matching member function for call to 'Load'}} + // expected-note@*:* {{candidate function not viable: requires 2 arguments, but 3 were provided}} + // expected-note@*:* {{candidate function not viable: requires single argument 'Location', but 3 arguments were provided}} +} + +float4 test_invalid_coord_type(float2 loc) { + return t.Load(float3(loc, 0)); // expected-warning {{implicit conversion turns floating-point number into integer: 'float3' (aka 'vector<float, 3>') to 'vector<int, 3>'}} +} + +float4 test_invalid_offset_type(int2 loc, float2 offset) { + return t.Load(int3(loc, 0), offset); // expected-warning {{implicit conversion turns floating-point number into integer: 'float2' (aka 'vector<float, 2>') to 'vector<int, 2>'}} +} + +float4 test_invalid_location_count(int2 loc) { + return t.Load(loc); // expected-error {{no matching member function for call to 'Load'}} + // expected-note@*:* {{candidate function not viable: no known conversion from 'int2' (aka 'vector<int, 2>') to 'vector<int, 3>' (vector of 3 'int' values) for 1st argument}} + // expected-note@*:* {{candidate function not viable: requires 2 arguments, but 1 was provided}} +} + +float4 test_truncated_location_count(int4 loc) { + return t.Load(loc); // expected-warning {{implicit conversion truncates vector: 'int4' (aka 'vector<int, 4>') to 'vector<int, 3>' (vector of 3 'int' values)}} +} + +float4 test_splatted_offset_count(int3 loc, int offset) { + // No errors expected. The vector will be generated by splatting `offset`. + return t.Load(loc, offset); +} + + +float4 test_truncated_offset_count(int2 loc, int3 offset) { + return t.Load(int3(loc, 0), offset); // expected-warning {{implicit conversion truncates vector: 'int3' (aka 'vector<int, 3>') to 'vector<int, 2>' (vector of 2 'int' values)}} +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
