[clang] 5841935 - [HLSL] Implement Texture2D::mips[][] (#186143)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Mar 30 07:53:18 PDT 2026
Author: Steven Perron
Date: 2026-03-30T14:53:09Z
New Revision: 58419354a8b3a3f981507d13c3324511eb4833ca
URL: https://github.com/llvm/llvm-project/commit/58419354a8b3a3f981507d13c3324511eb4833ca
DIFF: https://github.com/llvm/llvm-project/commit/58419354a8b3a3f981507d13c3324511eb4833ca.diff
LOG: [HLSL] Implement Texture2D::mips[][] (#186143)
We implement the Textur2D::mips[][] method. We follow the design in DXC.
There is a new member called `mips` with type mips_type. The member will
contain a copy of the handle for the texture.
The type `mips_type` will have a member function `operator[]` that takes
a level, and returns a `mips_slice_type`. The slice will contain the
handle and the level. It also has an operator[] member function that
take a coordinate. It will do a load from the handle with the level and
coordinate, and return that value.
Assisted-by: Gemini
Added:
clang/test/CodeGenHLSL/resources/Texture2D-Mips.hlsl
clang/test/SemaHLSL/Texture2D-mips-errors.hlsl
Modified:
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
clang/lib/Sema/HLSLExternalSemaSource.cpp
clang/test/CodeGenHLSL/resources/Texture2D-Gather.hlsl
clang/test/CodeGenHLSL/resources/Texture2D-Sample.hlsl
clang/test/CodeGenHLSL/resources/Texture2D-SampleBias.hlsl
clang/test/CodeGenHLSL/resources/Texture2D-SampleCmp.hlsl
clang/test/CodeGenHLSL/resources/Texture2D-SampleGrad.hlsl
clang/test/CodeGenHLSL/resources/Texture2D-SampleLevel.hlsl
clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl
clang/test/CodeGenHLSL/resources/Texture2D-default.hlsl
clang/test/CodeGenHLSL/resources/Texture2D-shorthand-contexts.hlsl
Removed:
################################################################################
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index a99b9446fd65f..3c84cd28732c9 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -16,6 +16,7 @@
#include "clang/AST/Attr.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclFriend.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/HLSLResource.h"
@@ -187,6 +188,7 @@ struct BuiltinTypeMethodBuilder {
_5,
Handle = 128,
CounterHandle,
+ This,
LastStmt
};
@@ -226,12 +228,22 @@ struct BuiltinTypeMethodBuilder {
template <typename TLHS, typename TRHS>
BuiltinTypeMethodBuilder &assign(TLHS LHS, TRHS RHS);
template <typename T> BuiltinTypeMethodBuilder &dereference(T Ptr);
+ template <typename V, typename S>
+ BuiltinTypeMethodBuilder &concat(V Vec, S Scalar, QualType ResultTy);
template <typename T>
BuiltinTypeMethodBuilder &accessHandleFieldOnResource(T ResourceRecord);
- template <typename ResourceT, typename ValueT>
- BuiltinTypeMethodBuilder &setHandleFieldOnResource(ResourceT ResourceRecord,
+ template <typename T>
+ BuiltinTypeMethodBuilder &accessFieldOnResource(T ResourceRecord,
+ FieldDecl *Field);
+ template <typename ValueT>
+ BuiltinTypeMethodBuilder &setHandleFieldOnResource(LocalVar &ResourceRecord,
ValueT HandleValue);
+ template <typename ResourceT, typename ValueT>
+ BuiltinTypeMethodBuilder &setFieldOnResource(ResourceT ResourceRecord,
+ ValueT HandleValue,
+ FieldDecl *HandleField);
+ void setMipsHandleField(LocalVar &ResourceRecord);
template <typename T>
BuiltinTypeMethodBuilder &
accessCounterHandleFieldOnResource(T ResourceRecord);
@@ -240,7 +252,8 @@ struct BuiltinTypeMethodBuilder {
setCounterHandleFieldOnResource(ResourceT ResourceRecord, ValueT HandleValue);
template <typename T> BuiltinTypeMethodBuilder &returnValue(T ReturnValue);
BuiltinTypeMethodBuilder &returnThis();
- BuiltinTypeDeclBuilder &finalize();
+ BuiltinTypeDeclBuilder &
+ finalize(AccessSpecifier Access = AccessSpecifier::AS_public);
Expr *getResourceHandleExpr();
Expr *getResourceCounterHandleExpr();
@@ -253,11 +266,6 @@ struct BuiltinTypeMethodBuilder {
if (!Method)
createDecl();
}
-
- template <typename ResourceT, typename ValueT>
- BuiltinTypeMethodBuilder &setFieldOnResource(ResourceT ResourceRecord,
- ValueT HandleValue,
- FieldDecl *HandleField);
};
TemplateParameterListBuilder::~TemplateParameterListBuilder() {
@@ -414,6 +422,12 @@ Expr *BuiltinTypeMethodBuilder::convertPlaceholder(PlaceHolder PH) {
return getResourceHandleExpr();
if (PH == PlaceHolder::CounterHandle)
return getResourceCounterHandleExpr();
+ if (PH == PlaceHolder::This) {
+ ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+ return CXXThisExpr::Create(AST, SourceLocation(),
+ Method->getFunctionObjectParameterType(),
+ /*IsImplicit=*/true);
+ }
if (PH == PlaceHolder::LastStmt) {
assert(!StmtsList.empty() && "no statements in the list");
@@ -609,6 +623,45 @@ BuiltinTypeMethodBuilder::declareLocalVar(LocalVar &Var) {
return *this;
}
+template <typename V, typename S>
+BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::concat(V Vec, S Scalar,
+ QualType ResultTy) {
+ assert(ResultTy->isVectorType() && "The result type must be a vector type.");
+ ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+ Expr *VecExpr = convertPlaceholder(Vec);
+ auto *VecTy = VecExpr->getType()->castAs<VectorType>();
+ Expr *ScalarExpr = convertPlaceholder(Scalar);
+
+ // Save the vector to a local variable to avoid evaluating the placeholder
+ // multiple times or sharing the AST node.
+ LocalVar VecVar("vec_tmp", VecTy->desugar());
+ declareLocalVar(VecVar);
+ assign(VecVar, VecExpr);
+
+ QualType EltTy = VecTy->getElementType();
+ unsigned NumElts = VecTy->getNumElements();
+
+ SmallVector<Expr *, 4> Elts;
+ for (unsigned I = 0; I < NumElts; ++I) {
+ Elts.push_back(new (AST) ArraySubscriptExpr(
+ convertPlaceholder(VecVar), DeclBuilder.getConstantIntExpr(I), EltTy,
+ VK_PRValue, OK_Ordinary, SourceLocation()));
+ }
+ Elts.push_back(ScalarExpr);
+
+ auto *InitList =
+ new (AST) InitListExpr(AST, SourceLocation(), Elts, SourceLocation());
+ InitList->setType(ResultTy);
+
+ ExprResult Cast = DeclBuilder.SemaRef.BuildCStyleCastExpr(
+ SourceLocation(), AST.getTrivialTypeSourceInfo(ResultTy),
+ SourceLocation(), InitList);
+ assert(!Cast.isInvalid() && "Cast cannot fail!");
+ StmtsList.push_back(Cast.get());
+
+ return *this;
+}
+
BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::returnThis() {
ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
CXXThisExpr *ThisExpr = CXXThisExpr::Create(
@@ -705,12 +758,67 @@ BuiltinTypeMethodBuilder::accessHandleFieldOnResource(T ResourceRecord) {
return *this;
}
-template <typename ResourceT, typename ValueT>
+template <typename T>
+BuiltinTypeMethodBuilder &
+BuiltinTypeMethodBuilder::accessFieldOnResource(T ResourceRecord,
+ FieldDecl *Field) {
+ ensureCompleteDecl();
+ Expr *Base = convertPlaceholder(ResourceRecord);
+
+ ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+ auto *Member =
+ MemberExpr::CreateImplicit(AST, Base, /*IsArrow=*/false, Field,
+ Field->getType(), VK_LValue, OK_Ordinary);
+ StmtsList.push_back(Member);
+ return *this;
+}
+
+void BuiltinTypeMethodBuilder::setMipsHandleField(LocalVar &ResourceRecord) {
+ FieldDecl *MipsField = DeclBuilder.Fields.lookup("mips");
+ if (!MipsField)
+ return;
+
+ ASTContext &AST = DeclBuilder.SemaRef.getASTContext();
+ QualType MipsTy = MipsField->getType();
+ const auto *RT = MipsTy->castAs<RecordType>();
+ CXXRecordDecl *MipsRecord = cast<CXXRecordDecl>(RT->getDecl());
+
+ // The mips record should have a single field that is the handle.
+ assert(MipsRecord->field_begin() != MipsRecord->field_end() &&
+ "mips_type must have at least one field");
+ assert(std::next(MipsRecord->field_begin()) == MipsRecord->field_end() &&
+ "mips_type must have exactly one field");
+ FieldDecl *MipsHandleField = *MipsRecord->field_begin();
+
+ FieldDecl *HandleField = DeclBuilder.getResourceHandleField();
+ Expr *ResExpr = convertPlaceholder(ResourceRecord);
+ MemberExpr *HandleMemberExpr = MemberExpr::CreateImplicit(
+ AST, ResExpr, false, HandleField, HandleField->getType(), VK_LValue,
+ OK_Ordinary);
+
+ MemberExpr *MipsMemberExpr =
+ MemberExpr::CreateImplicit(AST, ResExpr, false, MipsField,
+ MipsField->getType(), VK_LValue, OK_Ordinary);
+ MemberExpr *MipsHandleMemberExpr = MemberExpr::CreateImplicit(
+ AST, MipsMemberExpr, false, MipsHandleField, MipsHandleField->getType(),
+ VK_LValue, OK_Ordinary);
+
+ Stmt *AssignStmt = BinaryOperator::Create(
+ AST, MipsHandleMemberExpr, HandleMemberExpr, BO_Assign,
+ MipsHandleMemberExpr->getType(), ExprValueKind::VK_LValue,
+ ExprObjectKind::OK_Ordinary, SourceLocation(), FPOptionsOverride());
+
+ StmtsList.push_back(AssignStmt);
+}
+
+template <typename ValueT>
BuiltinTypeMethodBuilder &
-BuiltinTypeMethodBuilder::setHandleFieldOnResource(ResourceT ResourceRecord,
+BuiltinTypeMethodBuilder::setHandleFieldOnResource(LocalVar &ResourceRecord,
ValueT HandleValue) {
- return setFieldOnResource(ResourceRecord, HandleValue,
- DeclBuilder.getResourceHandleField());
+ setFieldOnResource(ResourceRecord, HandleValue,
+ DeclBuilder.getResourceHandleField());
+ setMipsHandleField(ResourceRecord);
+ return *this;
}
template <typename ResourceT, typename ValueT>
@@ -791,7 +899,8 @@ BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::returnValue(T ReturnValue) {
return *this;
}
-BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalize() {
+BuiltinTypeDeclBuilder &
+BuiltinTypeMethodBuilder::finalize(AccessSpecifier Access) {
assert(!DeclBuilder.Record->isCompleteDefinition() &&
"record is already complete");
@@ -818,9 +927,11 @@ BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalize() {
Method->setBody(CompoundStmt::Create(AST, StmtsList, FPOptionsOverride(),
SourceLocation(), SourceLocation()));
Method->setLexicalDeclContext(DeclBuilder.Record);
- Method->setAccess(AS_public);
+ Method->setAccess(Access);
+ Method->setImplicitlyInline();
Method->addAttr(AlwaysInlineAttr::CreateImplicit(
AST, SourceRange(), AlwaysInlineAttr::CXX11_clang_always_inline));
+ Method->addAttr(ConvergentAttr::CreateImplicit(AST));
if (!TemplateParamDecls.empty()) {
TemplateParams = TemplateParameterList::Create(
AST, SourceLocation(), SourceLocation(), TemplateParamDecls,
@@ -921,9 +1032,11 @@ BuiltinTypeDeclBuilder &
BuiltinTypeDeclBuilder::addBufferHandles(ResourceClass RC, bool IsROV,
bool RawBuffer, bool HasCounter,
AccessSpecifier Access) {
- addHandleMember(RC, ResourceDimension::Unknown, IsROV, RawBuffer, Access);
+ QualType ElementTy = getHandleElementType();
+ addHandleMember(RC, ResourceDimension::Unknown, IsROV, RawBuffer, ElementTy,
+ Access);
if (HasCounter)
- addCounterHandleMember(RC, IsROV, RawBuffer, Access);
+ addCounterHandleMember(RC, IsROV, RawBuffer, ElementTy, Access);
return *this;
}
@@ -931,39 +1044,71 @@ BuiltinTypeDeclBuilder &
BuiltinTypeDeclBuilder::addTextureHandle(ResourceClass RC, bool IsROV,
ResourceDimension RD,
AccessSpecifier Access) {
- addHandleMember(RC, RD, IsROV, /*RawBuffer=*/false, Access);
+ addHandleMember(RC, RD, IsROV, /*RawBuffer=*/false, getHandleElementType(),
+ Access);
return *this;
}
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addSamplerHandle() {
addHandleMember(ResourceClass::Sampler, ResourceDimension::Unknown,
- /*IsROV=*/false, /*RawBuffer=*/false);
+ /*IsROV=*/false, /*RawBuffer=*/false, getHandleElementType());
return *this;
}
BuiltinTypeDeclBuilder &
-BuiltinTypeDeclBuilder::addHandleMember(ResourceClass RC, ResourceDimension RD,
- bool IsROV, bool RawBuffer,
- AccessSpecifier Access) {
+BuiltinTypeDeclBuilder::addFriend(CXXRecordDecl *Friend) {
+ assert(!Record->isCompleteDefinition() && "record is already complete");
+ ASTContext &AST = SemaRef.getASTContext();
+ QualType FriendTy = AST.getCanonicalTagType(Friend);
+ TypeSourceInfo *TSI = AST.getTrivialTypeSourceInfo(FriendTy);
+ FriendDecl *FD =
+ FriendDecl::Create(AST, Record, SourceLocation(), TSI, SourceLocation());
+ FD->setAccess(AS_public);
+ Record->addDecl(FD);
+ return *this;
+}
+
+CXXRecordDecl *BuiltinTypeDeclBuilder::addPrivateNestedRecord(StringRef Name) {
+ assert(!Record->isCompleteDefinition() && "record is already complete");
+ ASTContext &AST = SemaRef.getASTContext();
+ IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
+ CXXRecordDecl *NestedRecord =
+ CXXRecordDecl::Create(AST, TagDecl::TagKind::Struct, Record,
+ SourceLocation(), SourceLocation(), &II);
+ NestedRecord->setImplicit(true);
+ NestedRecord->setAccess(AccessSpecifier::AS_private);
+ NestedRecord->setLexicalDeclContext(Record);
+ Record->addDecl(NestedRecord);
+ return NestedRecord;
+}
+
+BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addHandleMember(
+ ResourceClass RC, ResourceDimension RD, bool IsROV, bool RawBuffer,
+ QualType ElementTy, AccessSpecifier Access) {
return addResourceMember("__handle", RC, RD, IsROV, RawBuffer,
- /*IsCounter=*/false, Access);
+ /*IsCounter=*/false, ElementTy, Access);
}
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCounterHandleMember(
- ResourceClass RC, bool IsROV, bool RawBuffer, AccessSpecifier Access) {
+ ResourceClass RC, bool IsROV, bool RawBuffer, QualType ElementTy,
+ AccessSpecifier Access) {
return addResourceMember("__counter_handle", RC, ResourceDimension::Unknown,
IsROV, RawBuffer,
- /*IsCounter=*/true, Access);
+ /*IsCounter=*/true, ElementTy, Access);
}
BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addResourceMember(
StringRef MemberName, ResourceClass RC, ResourceDimension RD, bool IsROV,
- bool RawBuffer, bool IsCounter, AccessSpecifier Access) {
+ bool RawBuffer, bool IsCounter, QualType ElementTy,
+ AccessSpecifier Access) {
assert(!Record->isCompleteDefinition() && "record is already complete");
ASTContext &Ctx = SemaRef.getASTContext();
+
+ assert(!ElementTy.isNull() &&
+ "The caller should always pass in the type for the handle.");
TypeSourceInfo *ElementTypeInfo =
- Ctx.getTrivialTypeSourceInfo(getHandleElementType(), SourceLocation());
+ Ctx.getTrivialTypeSourceInfo(ElementTy, SourceLocation());
// add handle member with resource type attributes
QualType AttributedResTy = QualType();
@@ -988,7 +1133,8 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addResourceMember(
// Adds default constructor to the resource class:
// Resource::Resource()
-BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDefaultHandleConstructor() {
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addDefaultHandleConstructor(AccessSpecifier Access) {
assert(!Record->isCompleteDefinition() && "record is already complete");
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
@@ -998,7 +1144,7 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addDefaultHandleConstructor() {
.callBuiltin("__builtin_hlsl_resource_uninitializedhandle", HandleType,
PH::Handle)
.assign(PH::Handle, PH::LastStmt)
- .finalize();
+ .finalize(Access);
}
BuiltinTypeDeclBuilder &
@@ -1185,7 +1331,8 @@ BuiltinTypeDeclBuilder::addCreateFromImplicitBindingWithImplicitCounter() {
.finalize();
}
-BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyConstructor() {
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addCopyConstructor(AccessSpecifier Access) {
assert(!Record->isCompleteDefinition() && "record is already complete");
ASTContext &AST = SemaRef.getASTContext();
@@ -1197,18 +1344,18 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyConstructor() {
BuiltinTypeMethodBuilder MMB(*this, /*Name=*/"", AST.VoidTy,
/*IsConst=*/false, /*IsCtor=*/true);
- MMB.addParam("other", ConstRecordRefType)
- .accessHandleFieldOnResource(PH::_0)
- .assign(PH::Handle, PH::LastStmt);
+ MMB.addParam("other", ConstRecordRefType);
- if (getResourceCounterHandleField())
- MMB.accessCounterHandleFieldOnResource(PH::_0).assign(PH::CounterHandle,
- PH::LastStmt);
+ for (auto *Field : Record->fields()) {
+ MMB.accessFieldOnResource(PH::_0, Field)
+ .setFieldOnResource(PH::This, PH::LastStmt, Field);
+ }
- return MMB.finalize();
+ return MMB.finalize(Access);
}
-BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyAssignmentOperator() {
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addCopyAssignmentOperator(AccessSpecifier Access) {
assert(!Record->isCompleteDefinition() && "record is already complete");
ASTContext &AST = SemaRef.getASTContext();
@@ -1220,15 +1367,14 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addCopyAssignmentOperator() {
using PH = BuiltinTypeMethodBuilder::PlaceHolder;
DeclarationName Name = AST.DeclarationNames.getCXXOperatorName(OO_Equal);
BuiltinTypeMethodBuilder MMB(*this, Name, RecordRefType);
- MMB.addParam("other", ConstRecordRefType)
- .accessHandleFieldOnResource(PH::_0)
- .assign(PH::Handle, PH::LastStmt);
+ MMB.addParam("other", ConstRecordRefType);
- if (getResourceCounterHandleField())
- MMB.accessCounterHandleFieldOnResource(PH::_0).assign(PH::CounterHandle,
- PH::LastStmt);
+ for (auto *Field : Record->fields()) {
+ MMB.accessFieldOnResource(PH::_0, Field)
+ .setFieldOnResource(PH::This, PH::LastStmt, Field);
+ }
- return MMB.returnThis().finalize();
+ return MMB.returnThis().finalize(Access);
}
BuiltinTypeDeclBuilder &
@@ -1269,6 +1415,115 @@ BuiltinTypeDeclBuilder &BuiltinTypeDeclBuilder::addLoadMethods() {
return *this;
}
+CXXRecordDecl *BuiltinTypeDeclBuilder::addMipsSliceType(ResourceDimension Dim,
+ QualType ReturnType) {
+ ASTContext &AST = Record->getASTContext();
+ uint32_t VecSize = getResourceDimensions(Dim);
+ QualType IntTy = AST.IntTy;
+ QualType IndexTy = VecSize > 1 ? AST.getExtVectorType(IntTy, VecSize) : IntTy;
+ QualType CoordLevelTy = AST.getExtVectorType(IntTy, VecSize + 1);
+ using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+
+ // Define the mips_slice_type which is returned by mips_type::operator[].
+ // It holds the resource handle and the mip level. It has an operator[]
+ // that takes the coordinate and performs the actual resource load.
+ CXXRecordDecl *MipsSliceRecord = addPrivateNestedRecord("mips_slice_type");
+ BuiltinTypeDeclBuilder MipsSliceBuilder(SemaRef, MipsSliceRecord);
+ MipsSliceBuilder.addFriend(Record)
+ .addHandleMember(getResourceAttrs().ResourceClass, Dim,
+ getResourceAttrs().IsROV, false, ReturnType,
+ AccessSpecifier::AS_public)
+ .addMemberVariable("__level", IntTy, {}, AccessSpecifier::AS_public)
+ .addDefaultHandleConstructor(AccessSpecifier::AS_protected)
+ .addCopyConstructor(AccessSpecifier::AS_protected)
+ .addCopyAssignmentOperator(AccessSpecifier::AS_protected);
+
+ FieldDecl *LevelField = MipsSliceBuilder.Fields["__level"];
+ assert(LevelField && "Could not find the level field.");
+
+ DeclarationName SubscriptName =
+ AST.DeclarationNames.getCXXOperatorName(OO_Subscript);
+
+ // operator[](intN coord) on mips_slice_type
+ BuiltinTypeMethodBuilder(MipsSliceBuilder, SubscriptName, ReturnType,
+ /*IsConst=*/true)
+ .addParam("Coord", IndexTy)
+ .accessFieldOnResource(PH::This, LevelField)
+ .concat(PH::_0, PH::LastStmt, CoordLevelTy)
+ .callBuiltin("__builtin_hlsl_resource_load_level", ReturnType, PH::Handle,
+ PH::LastStmt)
+ .finalize();
+
+ MipsSliceBuilder.completeDefinition();
+ return MipsSliceRecord;
+}
+
+CXXRecordDecl *BuiltinTypeDeclBuilder::addMipsType(ResourceDimension Dim,
+ QualType ReturnType) {
+ ASTContext &AST = Record->getASTContext();
+ QualType IntTy = AST.IntTy;
+ using PH = BuiltinTypeMethodBuilder::PlaceHolder;
+
+ // First, define the mips_slice_type that will be returned by our operator[].
+ CXXRecordDecl *MipsSliceRecord = addMipsSliceType(Dim, ReturnType);
+
+ // Define the mips_type, which provides the syntax `Resource.mips[level]`.
+ // It only holds the handle, and its operator[] returns a mips_slice_type
+ // initialized with the handle and the requested mip level.
+ CXXRecordDecl *MipsRecord = addPrivateNestedRecord("mips_type");
+ BuiltinTypeDeclBuilder MipsBuilder(SemaRef, MipsRecord);
+ MipsBuilder.addFriend(Record)
+ .addHandleMember(getResourceAttrs().ResourceClass, Dim,
+ getResourceAttrs().IsROV, false, ReturnType,
+ AccessSpecifier::AS_public)
+ .addDefaultHandleConstructor(AccessSpecifier::AS_protected)
+ .addCopyConstructor(AccessSpecifier::AS_protected)
+ .addCopyAssignmentOperator(AccessSpecifier::AS_protected);
+
+ QualType MipsSliceTy = AST.getCanonicalTagType(MipsSliceRecord);
+
+ DeclarationName SubscriptName =
+ AST.DeclarationNames.getCXXOperatorName(OO_Subscript);
+
+ // Locate the fields in the slice type so we can initialize them.
+ auto FieldIt = MipsSliceRecord->field_begin();
+ FieldDecl *MipsSliceHandleField = *FieldIt;
+ FieldDecl *LevelField = *++FieldIt;
+ assert(MipsSliceHandleField->getName() == "__handle" &&
+ LevelField->getName() == "__level" &&
+ "Could not find fields on mips_slice_type");
+
+ // operator[](int level) on mips_type
+ BuiltinTypeMethodBuilder::LocalVar MipsSliceVar("slice", MipsSliceTy);
+ BuiltinTypeMethodBuilder(MipsBuilder, SubscriptName, MipsSliceTy,
+ /*IsConst=*/true)
+ .addParam("Level", IntTy)
+ .declareLocalVar(MipsSliceVar)
+ .accessHandleFieldOnResource(PH::This)
+ .setFieldOnResource(MipsSliceVar, PH::LastStmt, MipsSliceHandleField)
+ .setFieldOnResource(MipsSliceVar, PH::_0, LevelField)
+ .returnValue(MipsSliceVar)
+ .finalize();
+
+ MipsBuilder.completeDefinition();
+ return MipsRecord;
+}
+
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addMipsMember(ResourceDimension Dim) {
+ assert(!Record->isCompleteDefinition() && "record is already complete");
+ ASTContext &AST = Record->getASTContext();
+ QualType ReturnType = getHandleElementType();
+
+ CXXRecordDecl *MipsRecord = addMipsType(Dim, ReturnType);
+
+ // Add the mips field to the texture
+ QualType MipsTy = AST.getCanonicalTagType(MipsRecord);
+ addMemberVariable("mips", MipsTy, {}, AccessSpecifier::AS_public);
+
+ return *this;
+}
+
BuiltinTypeDeclBuilder &
BuiltinTypeDeclBuilder::addTextureLoadMethods(ResourceDimension Dim) {
assert(!Record->isCompleteDefinition() && "record is already complete");
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
index 4fe6acabfac62..430dc00630676 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
@@ -87,9 +87,12 @@ class BuiltinTypeDeclBuilder {
ResourceDimension Dim = ResourceDimension::Unknown);
// Builtin types constructors
- BuiltinTypeDeclBuilder &addDefaultHandleConstructor();
- BuiltinTypeDeclBuilder &addCopyConstructor();
- BuiltinTypeDeclBuilder &addCopyAssignmentOperator();
+ BuiltinTypeDeclBuilder &addDefaultHandleConstructor(
+ AccessSpecifier Access = AccessSpecifier::AS_public);
+ BuiltinTypeDeclBuilder &
+ addCopyConstructor(AccessSpecifier Access = AccessSpecifier::AS_public);
+ BuiltinTypeDeclBuilder &addCopyAssignmentOperator(
+ AccessSpecifier Access = AccessSpecifier::AS_public);
// Static create methods
BuiltinTypeDeclBuilder &addStaticInitializationFunctions(bool HasCounter);
@@ -122,23 +125,29 @@ class BuiltinTypeDeclBuilder {
BuiltinTypeDeclBuilder &addConsumeMethod();
BuiltinTypeDeclBuilder &addGetDimensionsMethodForBuffer();
+ BuiltinTypeDeclBuilder &addMipsMember(ResourceDimension Dim);
private:
BuiltinTypeDeclBuilder &addCreateFromBinding();
BuiltinTypeDeclBuilder &addCreateFromImplicitBinding();
BuiltinTypeDeclBuilder &addCreateFromBindingWithImplicitCounter();
BuiltinTypeDeclBuilder &addCreateFromImplicitBindingWithImplicitCounter();
- BuiltinTypeDeclBuilder &addResourceMember(StringRef MemberName,
- ResourceClass RC,
- ResourceDimension RD, bool IsROV,
- bool RawBuffer, bool IsCounter,
- AccessSpecifier Access);
+ BuiltinTypeDeclBuilder &
+ addResourceMember(StringRef MemberName, ResourceClass RC,
+ ResourceDimension RD, bool IsROV, bool RawBuffer,
+ bool IsCounter, QualType ElementTy,
+ AccessSpecifier Access = AccessSpecifier::AS_private);
+ BuiltinTypeDeclBuilder &addFriend(CXXRecordDecl *Friend);
+ CXXRecordDecl *addPrivateNestedRecord(StringRef Name);
+ CXXRecordDecl *addMipsSliceType(ResourceDimension Dim, QualType ReturnType);
+ CXXRecordDecl *addMipsType(ResourceDimension Dim, QualType ReturnType);
BuiltinTypeDeclBuilder &
addHandleMember(ResourceClass RC, ResourceDimension RD, bool IsROV,
- bool RawBuffer,
+ bool RawBuffer, QualType ElementTy,
AccessSpecifier Access = AccessSpecifier::AS_private);
BuiltinTypeDeclBuilder &
addCounterHandleMember(ResourceClass RC, bool IsROV, bool RawBuffer,
+ QualType ElementTy,
AccessSpecifier Access = AccessSpecifier::AS_private);
QualType getGatherReturnType();
FieldDecl *getResourceHandleField() const;
diff --git a/clang/lib/Sema/HLSLExternalSemaSource.cpp b/clang/lib/Sema/HLSLExternalSemaSource.cpp
index fb40ccc674349..8f99acb4dd442 100644
--- a/clang/lib/Sema/HLSLExternalSemaSource.cpp
+++ b/clang/lib/Sema/HLSLExternalSemaSource.cpp
@@ -260,6 +260,7 @@ static BuiltinTypeDeclBuilder setupTextureType(CXXRecordDecl *Decl, Sema &S,
.addTextureHandle(RC, IsROV, Dim)
.addTextureLoadMethods(Dim)
.addArraySubscriptOperators(Dim)
+ .addMipsMember(Dim)
.addDefaultHandleConstructor()
.addCopyConstructor()
.addCopyAssignmentOperator()
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-Gather.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-Gather.hlsl
index 54d428285d88c..2f4cb1d5b98ed 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-Gather.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-Gather.hlsl
@@ -1,11 +1,11 @@
// 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
-// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2) }
+// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
// DXIL: %"class.hlsl::SamplerState" = type { target("dx.Sampler", 0) }
// DXIL: %"class.hlsl::SamplerComparisonState" = type { target("dx.Sampler", 0) }
-// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0) }
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0), %"struct.hlsl::Texture2D<>::mips_type" }
// SPIRV: %"class.hlsl::SamplerState" = type { target("spirv.Sampler") }
// SPIRV: %"class.hlsl::SamplerComparisonState" = type { target("spirv.Sampler") }
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-Mips.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-Mips.hlsl
new file mode 100644
index 0000000000000..b84df2b184283
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-Mips.hlsl
@@ -0,0 +1,63 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-pixel -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -o - %s | llvm-cxxfilt | FileCheck %s --check-prefixes=CHECK,DXIL
+
+Texture2D<float4> t;
+
+// CHECK: define internal {{.*}} <4 x float> @test_mips(float vector[2])(<2 x float> {{.*}} %loc) #1 {
+// CHECK: entry:
+// CHECK: %[[LOC_ADDR:.*]] = alloca <2 x float>
+// CHECK: %[[REF_TMP:.*]] = alloca %"struct.hlsl::Texture2D<>::mips_slice_type"
+// CHECK: store <2 x float> %loc, ptr %[[LOC_ADDR]]
+// CHECK: call void @hlsl::Texture2D<float vector[4]>::mips_type::operator[](int) const(ptr {{.*}} %[[REF_TMP]], ptr {{.*}} getelementptr {{.*}} (i8, ptr @t, i32 4), i32 noundef 0)
+// CHECK: %[[V0:.*]] = load <2 x float>, ptr %[[LOC_ADDR]]
+// CHECK: %[[CONV:.*]] = fptosi <2 x float> %[[V0]] to <2 x i32>
+// CHECK: %[[CALL:.*]] = call {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::mips_slice_type::operator[](int vector[2]) const(ptr {{.*}} %[[REF_TMP]], <2 x i32> {{.*}} %[[CONV]])
+// CHECK: ret <4 x float> %[[CALL]]
+
+[shader("pixel")]
+float4 test_mips(float2 loc : LOC) : SV_Target {
+ return t.mips[0][int2(loc)];
+}
+
+// CHECK: define linkonce_odr hidden void @hlsl::Texture2D<float vector[4]>::mips_type::operator[](int) const(ptr {{.*}} %agg.result, ptr {{.*}} %this, i32 {{.*}} %Level)
+// CHECK: entry:
+// CHECK: %{{.*}} = alloca ptr
+// CHECK: %[[THIS_ADDR:.*]] = alloca ptr
+// CHECK: %[[LEVEL_ADDR:.*]] = alloca i32
+// CHECK: %[[SLICE:.*]] = alloca %"struct.hlsl::Texture2D<>::mips_slice_type"
+// CHECK: store ptr %agg.result, ptr %{{.*}}
+// CHECK: store ptr %this, ptr %[[THIS_ADDR]]
+// CHECK: store i32 %Level, ptr %[[LEVEL_ADDR]]
+// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]]
+// CHECK: call void @hlsl::Texture2D<float vector[4]>::mips_slice_type::mips_slice_type()(ptr {{.*}} %[[SLICE]])
+// CHECK: %[[HANDLE_GEP:.*]] = getelementptr {{.*}} %"struct.hlsl::Texture2D<>::mips_type", ptr %[[THIS1]], i32 0, i32 0
+// CHECK: %[[HANDLE:.*]] = load target("dx.Texture", <4 x float>, 0, 0, 0, 2), ptr %[[HANDLE_GEP]]
+// CHECK: %[[HANDLE_GEP2:.*]] = getelementptr {{.*}} %"struct.hlsl::Texture2D<>::mips_slice_type", ptr %[[SLICE]], i32 0, i32 0
+// CHECK: store target("dx.Texture", <4 x float>, 0, 0, 0, 2) %[[HANDLE]], ptr %[[HANDLE_GEP2]]
+// CHECK: %[[L_VAL:.*]] = load i32, ptr %[[LEVEL_ADDR]]
+// CHECK: %[[LEVEL_GEP:.*]] = getelementptr {{.*}} %"struct.hlsl::Texture2D<>::mips_slice_type", ptr %[[SLICE]], i32 0, i32 1
+// CHECK: store i32 %[[L_VAL]], ptr %[[LEVEL_GEP]]
+// CHECK: call void @hlsl::Texture2D<float vector[4]>::mips_slice_type::mips_slice_type(hlsl::Texture2D<float vector[4]>::mips_slice_type const&)(ptr noundef nonnull align 4 dereferenceable(8) %agg.result, ptr noundef nonnull align 4 dereferenceable(8) %[[SLICE]])
+
+// CHECK: define linkonce_odr hidden {{.*}} <4 x float> @hlsl::Texture2D<float vector[4]>::mips_slice_type::operator[](int vector[2]) const(ptr {{.*}} %[[THIS:.*]], <2 x i32> noundef %[[COORD:.*]])
+// CHECK: entry:
+// CHECK: %[[COORD_ADDR:.*]] = alloca <2 x i32>
+// CHECK: %[[VEC_TMP:.*]] = alloca <2 x i32>
+// CHECK: store <2 x i32> %[[COORD]], ptr %[[COORD_ADDR]]
+// CHECK: %[[THIS1:.*]] = load ptr, ptr %{{.*}}
+// CHECK: %[[COORD_PARAM:.*]] = load <2 x i32>, ptr %[[COORD_ADDR]]
+// CHECK: store <2 x i32> %[[COORD_PARAM]], ptr %[[VEC_TMP]]
+// CHECK: %[[HANDLE_PTR:.*]] = getelementptr {{.*}} %"struct.hlsl::Texture2D<>::mips_slice_type", ptr %[[THIS1]], i32 0, i32 0
+// CHECK: %[[HANDLE:.*]] = load target("dx.Texture", <4 x float>, 0, 0, 0, 2), ptr %[[HANDLE_PTR]]
+// CHECK: %[[COORD_VAL:.*]] = load <2 x i32>, ptr %[[VEC_TMP]]
+// CHECK: %[[VECEXT:.*]] = extractelement <2 x i32> %[[COORD_VAL]], i32 0
+// CHECK: %[[VECINIT:.*]] = insertelement <3 x i32> poison, i32 %[[VECEXT]], i32 0
+// CHECK: %[[COORD_VAL2:.*]] = load <2 x i32>, ptr %[[VEC_TMP]]
+// CHECK: %[[VECEXT2:.*]] = extractelement <2 x i32> %[[COORD_VAL2]], i32 1
+// CHECK: %[[VECINIT3:.*]] = insertelement <3 x i32> %[[VECINIT]], i32 %[[VECEXT2]], i32 1
+// CHECK: %[[LEVEL_PTR:.*]] = getelementptr {{.*}} %"struct.hlsl::Texture2D<>::mips_slice_type", ptr %[[THIS1]], i32 0, i32 1
+// CHECK: %[[LEVEL_VAL:.*]] = load i32, ptr %[[LEVEL_PTR]]
+// CHECK: %[[VECINIT4:.*]] = insertelement <3 x i32> %[[VECINIT3]], i32 %[[LEVEL_VAL]], i32 2
+// CHECK: %[[COORD_X:.*]] = shufflevector <3 x i32> %[[VECINIT4]], <3 x i32> poison, <2 x i32> <i32 0, i32 1>
+// CHECK: %[[LOD:.*]] = extractelement <3 x i32> %[[VECINIT4]], 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_X]], i32 %[[LOD]], <2 x i32> zeroinitializer)
+// CHECK: ret <4 x float> %[[RES]]
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-Sample.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-Sample.hlsl
index 93bed2f2b7c27..e37e182be6b44 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-Sample.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-Sample.hlsl
@@ -1,10 +1,10 @@
// 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
-// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2) }
+// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
// DXIL: %"class.hlsl::SamplerState" = type { target("dx.Sampler", 0) }
-// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0) }
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0), %"struct.hlsl::Texture2D<>::mips_type" }
// SPIRV: %"class.hlsl::SamplerState" = type { target("spirv.Sampler") }
Texture2D<float4> t;
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-SampleBias.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-SampleBias.hlsl
index c138e7f0a6c8b..481a66c7a59a9 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-SampleBias.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-SampleBias.hlsl
@@ -1,10 +1,10 @@
// 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
-// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2) }
+// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
// DXIL: %"class.hlsl::SamplerState" = type { target("dx.Sampler", 0) }
-// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0) }
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0), %"struct.hlsl::Texture2D<>::mips_type" }
// SPIRV: %"class.hlsl::SamplerState" = type { target("spirv.Sampler") }
Texture2D<float4> t;
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-SampleCmp.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-SampleCmp.hlsl
index b76b02177abbc..5262aed2816ef 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-SampleCmp.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-SampleCmp.hlsl
@@ -1,10 +1,10 @@
// 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
-// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2) }
+// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
// DXIL: %"class.hlsl::SamplerComparisonState" = type { target("dx.Sampler", 0) }
-// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0) }
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0), %"struct.hlsl::Texture2D<>::mips_type" }
// SPIRV: %"class.hlsl::SamplerComparisonState" = type { target("spirv.Sampler") }
Texture2D<float4> t;
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-SampleGrad.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-SampleGrad.hlsl
index 279521c0bb988..1a2cd93a34168 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-SampleGrad.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-SampleGrad.hlsl
@@ -1,10 +1,10 @@
// 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
-// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2) }
+// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
// DXIL: %"class.hlsl::SamplerState" = type { target("dx.Sampler", 0) }
-// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0) }
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0), %"struct.hlsl::Texture2D<>::mips_type" }
// SPIRV: %"class.hlsl::SamplerState" = type { target("spirv.Sampler") }
Texture2D<float4> t;
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-SampleLevel.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-SampleLevel.hlsl
index bcd025c164ac0..85fae6ea278f2 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-SampleLevel.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-SampleLevel.hlsl
@@ -1,10 +1,10 @@
// 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
-// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2) }
+// DXIL: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
// DXIL: %"class.hlsl::SamplerState" = type { target("dx.Sampler", 0) }
-// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0) }
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0), %"struct.hlsl::Texture2D<>::mips_type" }
// SPIRV: %"class.hlsl::SamplerState" = type { target("spirv.Sampler") }
Texture2D<float4> t;
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl
index 06b4cb7ec9900..9a7819364ee83 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-default-explicit-binding.hlsl
@@ -5,8 +5,8 @@ SamplerState g_s : register(s0);
Texture2D<> default_template : register(t1, space2);
Texture2D implicit_template : register(t0, space1);
-// CHECK: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2) }
-// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0) }
+// CHECK: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
+// SPIRV: %"class.hlsl::Texture2D" = type { target("spirv.Image", float, 1, 2, 0, 0, 1, 0), %"struct.hlsl::Texture2D<>::mips_type" }
// CHECK: @{{.*}}default_template = internal global %"class.hlsl::Texture2D" poison, align {{[0-9]+}}
// CHECK: @{{.*}}implicit_template = internal global %"class.hlsl::Texture2D" poison, align {{[0-9]+}}
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-default.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-default.hlsl
index 5b5bb737a7958..8ff981e4fa512 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-default.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-default.hlsl
@@ -1,7 +1,7 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -std=hlsl202x -emit-llvm -disable-llvm-passes -finclude-default-header -o - %s | FileCheck %s
-// CHECK: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2) }
-// CHECK: %"class.hlsl::Texture2D.0" = type { target("dx.Texture", float, 0, 0, 0, 2) }
+// CHECK: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
+// CHECK: %"class.hlsl::Texture2D.0" = type { target("dx.Texture", float, 0, 0, 0, 2), %"struct.hlsl::Texture2D<float>::mips_type" }
// CHECK: @{{.*}}t1 = internal global %"class.hlsl::Texture2D" poison, align 4
Texture2D<> t1;
diff --git a/clang/test/CodeGenHLSL/resources/Texture2D-shorthand-contexts.hlsl b/clang/test/CodeGenHLSL/resources/Texture2D-shorthand-contexts.hlsl
index 71ce46232d088..0367e2360242b 100644
--- a/clang/test/CodeGenHLSL/resources/Texture2D-shorthand-contexts.hlsl
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-shorthand-contexts.hlsl
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -finclude-default-header -emit-llvm -disable-llvm-passes -o - %s | llvm-cxxfilt | FileCheck %s
-// CHECK: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2) }
+// CHECK: %"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
SamplerState g_s : register(s0);
diff --git a/clang/test/SemaHLSL/Texture2D-mips-errors.hlsl b/clang/test/SemaHLSL/Texture2D-mips-errors.hlsl
new file mode 100644
index 0000000000000..0c0fc074cae0b
--- /dev/null
+++ b/clang/test/SemaHLSL/Texture2D-mips-errors.hlsl
@@ -0,0 +1,35 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -verify %s
+
+Texture2D<float4> t;
+
+template<class T>
+float4 foo(T t) {
+ return t[int2(0, 0)];
+}
+
+[shader("pixel")]
+float4 test_mips() : SV_Target {
+ // expected-error at +4 {{'mips_type' is a private member of 'hlsl::Texture2D<>'}}
+ // expected-note@*:* {{implicitly declared private here}}
+ // expected-error at +2 {{calling a protected constructor of class 'hlsl::Texture2D<>::mips_type'}}
+ // expected-note@*:* {{implicitly declared protected here}}
+ Texture2D<float4>::mips_type a;
+
+ // expected-error at +4 {{'mips_slice_type' is a private member of 'hlsl::Texture2D<>'}}
+ // expected-note@*:* {{implicitly declared private here}}
+ // expected-error at +2 {{calling a protected constructor of class 'hlsl::Texture2D<>::mips_slice_type'}}
+ // expected-note@*:* {{implicitly declared protected here}}
+ Texture2D<float4>::mips_slice_type b;
+
+ // expected-warning at +3 {{'auto' type specifier is a HLSL 202y extension}}
+ // expected-error at +2 {{calling a protected constructor of class 'hlsl::Texture2D<>::mips_type'}}
+ // expected-note@*:* {{implicitly declared protected here}}
+ auto c = t.mips;
+
+ // Note: t.mips[0] correctly returns a mips_slice_type prvalue.
+ // Passing it to a template function like 'foo(t.mips[0])' currently crashes
+ // the compiler due to an unrelated bug in HLSL template instantiation.
+ // See: https://github.com/llvm/llvm-project/issues/188556
+ // return t.mips[0][int2(0, 0)] + foo(t.mips[0]);
+ return t.mips[0][int2(0, 0)];
+}
More information about the cfe-commits
mailing list