[llvm-branch-commits] [clang] [HLSL] Implement Texture2D::mips[][] (PR #186143)
Steven Perron via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Thu Mar 12 08:20:46 PDT 2026
https://github.com/s-perron created https://github.com/llvm/llvm-project/pull/186143
<!-- branch-stack-start -->
-------------------------
- main
- https://github.com/llvm/llvm-project/pull/186110
- users/s-perron/texture2d-mips :point_left:
<sup>[Stack](https://www.git-town.com/how-to/proposal-breadcrumb.html) generated by [Git Town](https://github.com/git-town/git-town)</sup>
<!-- branch-stack-end -->
>From 8c5bd3dbfbdebb2af0a640c8fcb44cf68d9ef120 Mon Sep 17 00:00:00 2001
From: Steven Perron <stevenperron at google.com>
Date: Tue, 10 Mar 2026 13:39:50 -0400
Subject: [PATCH] [HLSL] Implement Texture2D::mips[][]
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
---
.../clang/Basic/DiagnosticSemaKinds.td | 2 +
clang/include/clang/Sema/SemaHLSL.h | 1 +
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp | 319 ++++++++++++++++--
clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h | 18 +-
clang/lib/Sema/HLSLExternalSemaSource.cpp | 1 +
clang/lib/Sema/SemaHLSL.cpp | 43 +++
.../resources/Texture2D-Gather.hlsl | 4 +-
.../CodeGenHLSL/resources/Texture2D-Mips.hlsl | 65 ++++
.../resources/Texture2D-Sample.hlsl | 4 +-
.../resources/Texture2D-SampleBias.hlsl | 4 +-
.../resources/Texture2D-SampleCmp.hlsl | 4 +-
.../resources/Texture2D-SampleGrad.hlsl | 4 +-
.../resources/Texture2D-SampleLevel.hlsl | 4 +-
.../Texture2D-default-explicit-binding.hlsl | 4 +-
.../resources/Texture2D-default.hlsl | 4 +-
.../Texture2D-shorthand-contexts.hlsl | 2 +-
.../test/SemaHLSL/Texture2D-mips-errors.hlsl | 19 ++
clang/test/SemaHLSL/Texture2D-mips-errors.ll | 235 +++++++++++++
18 files changed, 679 insertions(+), 58 deletions(-)
create mode 100644 clang/test/CodeGenHLSL/resources/Texture2D-Mips.hlsl
create mode 100644 clang/test/SemaHLSL/Texture2D-mips-errors.hlsl
create mode 100644 clang/test/SemaHLSL/Texture2D-mips-errors.ll
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index e5da6155f4472..1f7a0d8a4e78f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13665,6 +13665,8 @@ def warn_hlsl_assigning_local_resource_is_not_unique
: Warning<"assignment of %0 to local resource %1 is not to the same "
"unique global resource">,
InGroup<HLSLExplicitBinding>;
+def err_hlsl_intermediate_type_variable : Error<
+ "intermediate helper type %0 cannot be used as a variable">;
def err_hlsl_push_constant_unique
: Error<"cannot have more than one push constant block">;
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 6341d3cd1cc00..3e0c56f72bb24 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -219,6 +219,7 @@ class SemaHLSL : public SemaBase {
bool transformInitList(const InitializedEntity &Entity, InitListExpr *Init);
bool handleInitialization(VarDecl *VDecl, Expr *&Init);
+ bool CheckForMipsIntermediateType(QualType T, SourceLocation Loc);
void deduceAddressSpace(VarDecl *Decl);
QualType checkMatrixComponent(Sema &S, QualType baseType, ExprValueKind &VK,
SourceLocation OpLoc,
diff --git a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
index a99b9446fd65f..a95fc5c6dbfc1 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.cpp
@@ -187,6 +187,7 @@ struct BuiltinTypeMethodBuilder {
_5,
Handle = 128,
CounterHandle,
+ This,
LastStmt
};
@@ -226,12 +227,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);
@@ -253,11 +264,6 @@ struct BuiltinTypeMethodBuilder {
if (!Method)
createDecl();
}
-
- template <typename ResourceT, typename ValueT>
- BuiltinTypeMethodBuilder &setFieldOnResource(ResourceT ResourceRecord,
- ValueT HandleValue,
- FieldDecl *HandleField);
};
TemplateParameterListBuilder::~TemplateParameterListBuilder() {
@@ -414,6 +420,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 +621,44 @@ BuiltinTypeMethodBuilder::declareLocalVar(LocalVar &Var) {
return *this;
}
+template <typename V, typename S>
+BuiltinTypeMethodBuilder &BuiltinTypeMethodBuilder::concat(V Vec, S Scalar,
+ QualType ResultTy) {
+ 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 +755,69 @@ 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->getAs<RecordType>();
+ assert(RT && "mips field must be a record type");
+ 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 *HandleResExpr = convertPlaceholder(ResourceRecord);
+ MemberExpr *HandleMemberExpr = MemberExpr::CreateImplicit(
+ AST, HandleResExpr, false, HandleField, HandleField->getType(), VK_LValue,
+ OK_Ordinary);
+
+ Expr *ResExpr = convertPlaceholder(ResourceRecord);
+ 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>
@@ -819,8 +926,10 @@ BuiltinTypeDeclBuilder &BuiltinTypeMethodBuilder::finalize() {
SourceLocation(), SourceLocation()));
Method->setLexicalDeclContext(DeclBuilder.Record);
Method->setAccess(AS_public);
+ 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 +1030,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 +1042,59 @@ 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::addPrivateNestedRecord(StringRef Name,
+ CXXRecordDecl *&NestedRecord) {
+ assert(!Record->isCompleteDefinition() && "record is already complete");
+ ASTContext &AST = SemaRef.getASTContext();
+ IdentifierInfo &II = AST.Idents.get(Name, tok::TokenKind::identifier);
+ 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 *this;
+}
+
+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();
@@ -1197,13 +1328,12 @@ 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();
}
@@ -1220,13 +1350,12 @@ 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();
}
@@ -1269,6 +1398,126 @@ 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 Int3Ty = 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 *MipSliceRecord = nullptr;
+ addPrivateNestedRecord("mips_slice_type", MipSliceRecord);
+ BuiltinTypeDeclBuilder MipSliceBuilder(SemaRef, MipSliceRecord);
+ MipSliceBuilder.addHandleMember(getResourceAttrs().ResourceClass, Dim,
+ getResourceAttrs().IsROV, false, ReturnType,
+ AccessSpecifier::AS_public);
+ MipSliceBuilder.addMemberVariable("__level", IntTy, {},
+ AccessSpecifier::AS_public);
+
+ MipSliceBuilder.addDefaultHandleConstructor()
+ .addCopyConstructor()
+ .addCopyAssignmentOperator();
+
+ FieldDecl *LevelField = MipSliceBuilder.Fields["__level"];
+ assert(LevelField && "Could not find the level field.\n");
+
+ DeclarationName SubscriptName =
+ AST.DeclarationNames.getCXXOperatorName(OO_Subscript);
+
+ // operator[](intN coord) on mips_slice_type
+ BuiltinTypeMethodBuilder(MipSliceBuilder, SubscriptName, ReturnType,
+ /*IsConst=*/true)
+ .addParam("Coord", IndexTy)
+ .accessFieldOnResource(PH::This, LevelField)
+ .concat(PH::_0, PH::LastStmt, Int3Ty)
+ .callBuiltin("__builtin_hlsl_resource_load_level", ReturnType, PH::Handle,
+ PH::LastStmt)
+ .finalize();
+
+ MipSliceBuilder.completeDefinition();
+ return MipSliceRecord;
+}
+
+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 *MipSliceRecord = 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 *MipRecord = nullptr;
+ addPrivateNestedRecord("mips_type", MipRecord);
+ BuiltinTypeDeclBuilder MipBuilder(SemaRef, MipRecord);
+ MipBuilder.addHandleMember(getResourceAttrs().ResourceClass, Dim,
+ getResourceAttrs().IsROV, false, ReturnType,
+ AccessSpecifier::AS_public);
+
+ MipBuilder.addDefaultHandleConstructor()
+ .addCopyConstructor()
+ .addCopyAssignmentOperator();
+
+ QualType MipSliceTy = AST.getTagType(
+ ElaboratedTypeKeyword::None,
+ NestedNameSpecifierLoc().getNestedNameSpecifier(), MipSliceRecord, false);
+
+ DeclarationName SubscriptName =
+ AST.DeclarationNames.getCXXOperatorName(OO_Subscript);
+
+ // Locate the fields in the slice type so we can initialize them.
+ FieldDecl *MipSliceHandleField = nullptr;
+ FieldDecl *LevelField = nullptr;
+ for (auto *F : MipSliceRecord->fields()) {
+ if (F->getName() == "__handle")
+ MipSliceHandleField = F;
+ else if (F->getName() == "__level")
+ LevelField = F;
+ }
+ assert(MipSliceHandleField && LevelField &&
+ "Could not find fields on mips_slice_type");
+
+ // operator[](int level) on mips_type
+ BuiltinTypeMethodBuilder::LocalVar MipSliceVar("slice", MipSliceTy);
+ BuiltinTypeMethodBuilder(MipBuilder, SubscriptName, MipSliceTy,
+ /*IsConst=*/true)
+ .addParam("Level", IntTy)
+ .declareLocalVar(MipSliceVar)
+ .accessHandleFieldOnResource(PH::This)
+ .setFieldOnResource(MipSliceVar, PH::LastStmt, MipSliceHandleField)
+ .setFieldOnResource(MipSliceVar, PH::_0, LevelField)
+ .returnValue(MipSliceVar)
+ .finalize();
+
+ MipBuilder.completeDefinition();
+ return MipRecord;
+}
+
+BuiltinTypeDeclBuilder &
+BuiltinTypeDeclBuilder::addMipsMember(ResourceDimension Dim) {
+ assert(!Record->isCompleteDefinition() && "record is already complete");
+ ASTContext &AST = Record->getASTContext();
+ QualType ReturnType = getHandleElementType();
+
+ CXXRecordDecl *MipRecord = addMipsType(Dim, ReturnType);
+
+ // Add the mips field to the texture
+ QualType MipTy = AST.getTagType(
+ ElaboratedTypeKeyword::None,
+ NestedNameSpecifierLoc().getNestedNameSpecifier(), MipRecord, false);
+ addMemberVariable("mips", MipTy, {}, 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 d4694f72dd289..1002b8c1afa1a 100644
--- a/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
+++ b/clang/lib/Sema/HLSLBuiltinTypeDeclBuilder.h
@@ -121,23 +121,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 &addPrivateNestedRecord(StringRef Name,
+ CXXRecordDecl *&NestedRecord);
+ 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 c93bf77e7a138..70b35de8517a1 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/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 2b225d1adc7fb..9c121a45875a7 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -4814,7 +4814,45 @@ void SemaHLSL::deduceAddressSpace(VarDecl *Decl) {
Decl->setType(Type);
}
+bool SemaHLSL::CheckForMipsIntermediateType(QualType T, SourceLocation Loc) {
+ const auto *RT = T->getAs<RecordType>();
+ if (!RT)
+ return false;
+
+ const CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(RT->getDecl());
+ if (!RD || !RD->isImplicit())
+ return false;
+
+ const DeclContext *DC = RD->getDeclContext();
+ const auto *ParentRD = dyn_cast<CXXRecordDecl>(DC);
+ if (!ParentRD)
+ return false;
+
+ const DeclContext *ParentDC = ParentRD->getDeclContext();
+ QualType ParentTy =
+ SemaRef.getASTContext().getTypeDeclType(cast<TypeDecl>(ParentRD));
+ if (!ParentTy->isHLSLResourceRecord()) {
+ return false;
+ }
+
+ if (!ParentDC->isNamespace() &&
+ cast<NamespaceDecl>(ParentDC)->getName() != "hlsl")
+ return false;
+
+ if (RD->getIdentifier() &&
+ (RD->getName() == "mips_type" || RD->getName() == "mips_slice_type")) {
+ SemaRef.Diag(Loc, diag::err_hlsl_intermediate_type_variable) << T;
+ return true;
+ }
+ return false;
+}
+
void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
+ if (CheckForMipsIntermediateType(VD->getType(), VD->getLocation())) {
+ VD->setInvalidDecl();
+ return;
+ }
+
if (VD->hasGlobalStorage()) {
// make sure the declaration has a complete type
if (SemaRef.RequireCompleteType(
@@ -5762,6 +5800,11 @@ bool SemaHLSL::handleInitialization(VarDecl *VDecl, Expr *&Init) {
if (VDecl->getType()->isHLSLResourceRecord() && !VDecl->hasGlobalStorage())
trackLocalResource(VDecl, Init);
+ if (CheckForMipsIntermediateType(VDecl->getType(), VDecl->getLocation())) {
+ VDecl->setInvalidDecl();
+ return false;
+ }
+
const HLSLVkConstantIdAttr *ConstIdAttr =
VDecl->getAttr<HLSLVkConstantIdAttr>();
if (!ConstIdAttr)
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..e934dcef5727e
--- /dev/null
+++ b/clang/test/CodeGenHLSL/resources/Texture2D-Mips.hlsl
@@ -0,0 +1,65 @@
+// 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>, align 8
+// CHECK: %[[REF_TMP:.*]] = alloca %"struct.hlsl::Texture2D<>::mips_slice_type", align 4
+// CHECK: store <2 x float> %loc, ptr %[[LOC_ADDR]], align 8
+// 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]], align 8
+// 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)];
+}
+
+// TODO: Some of the checks use %{{.*}}. We should be trying to match the ids correctly to make sure we have the right connections.
+
+// 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, align 4
+// CHECK: %[[THIS_ADDR:.*]] = alloca ptr, align 4
+// CHECK: %[[LEVEL_ADDR:.*]] = alloca i32, align 4
+// CHECK: %[[SLICE:.*]] = alloca %"struct.hlsl::Texture2D<>::mips_slice_type", align 4
+// CHECK: store ptr %agg.result, ptr %{{.*}}, align 4
+// CHECK: store ptr %this, ptr %[[THIS_ADDR]], align 4
+// CHECK: store i32 %Level, ptr %[[LEVEL_ADDR]], align 4
+// CHECK: %[[THIS1:.*]] = load ptr, ptr %[[THIS_ADDR]], align 4
+// 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]], align 4
+// 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]], align 4
+// CHECK: %[[L_VAL:.*]] = load i32, ptr %[[LEVEL_ADDR]], align 4
+// CHECK: %[[LEVEL_GEP:.*]] = getelementptr {{.*}} %"struct.hlsl::Texture2D<>::mips_slice_type", ptr %[[SLICE]], i32 0, i32 1
+// CHECK: store i32 %[[L_VAL]], ptr %[[LEVEL_GEP]], align 4
+// 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>, align 8
+// CHECK: %[[VEC_TMP:.*]] = alloca <2 x i32>, align 8
+// CHECK: store <2 x i32> %[[COORD]], ptr %[[COORD_ADDR]], align 8
+// CHECK: %[[THIS1:.*]] = load ptr, ptr %{{.*}}, align 4
+// CHECK: %[[COORD_PARAM:.*]] = load <2 x i32>, ptr %[[COORD_ADDR]], align 8
+// CHECK: store <2 x i32> %[[COORD_PARAM]], ptr %[[VEC_TMP]], align 8
+// 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]], align 4
+// CHECK: %[[COORD_VAL:.*]] = load <2 x i32>, ptr %[[VEC_TMP]], align 8
+// 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]], align 8
+// 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]], align 4
+// 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..12b0d17c921c6
--- /dev/null
+++ b/clang/test/SemaHLSL/Texture2D-mips-errors.hlsl
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -emit-llvm -disable-llvm-passes -finclude-default-header -verify %s
+
+Texture2D<float4> t;
+
+void main() {
+ // expected-error at +3 {{'mips_type' is a private member of 'hlsl::Texture2D<>'}}
+ // expected-note@*:* {{implicitly declared private here}}
+ // expected-error at +1 {{intermediate helper type 'Texture2D<float4>::mips_type' cannot be used as a variable}}
+ Texture2D<float4>::mips_type a;
+
+ // expected-error at +3 {{'mips_slice_type' is a private member of 'hlsl::Texture2D<>'}}
+ // expected-note@*:* {{implicitly declared private here}}
+ // expected-error at +1 {{intermediate helper type 'Texture2D<float4>::mips_slice_type' cannot be used as a variable}}
+ Texture2D<float4>::mips_slice_type b;
+
+ // expected-warning at +2 {{'auto' type specifier is a HLSL 202y extension}}
+ // expected-error at +1 {{intermediate helper type 'mips_type' cannot be used as a variable}}
+ auto c = t.mips;
+}
diff --git a/clang/test/SemaHLSL/Texture2D-mips-errors.ll b/clang/test/SemaHLSL/Texture2D-mips-errors.ll
new file mode 100644
index 0000000000000..cf4f3a7f7207e
--- /dev/null
+++ b/clang/test/SemaHLSL/Texture2D-mips-errors.ll
@@ -0,0 +1,235 @@
+; ModuleID = '/usr/local/google/home/stevenperron/spirv/llvm/texture-2d-implementation/clang/test/SemaHLSL/Texture2D-mips-errors.hlsl'
+source_filename = "/usr/local/google/home/stevenperron/spirv/llvm/texture-2d-implementation/clang/test/SemaHLSL/Texture2D-mips-errors.hlsl"
+target datalayout = "e-m:e-ve-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64"
+target triple = "dxilv1.0-pc-shadermodel6.0-library"
+
+%"class.hlsl::Texture2D" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2), %"struct.hlsl::Texture2D<>::mips_type" }
+%"struct.hlsl::Texture2D<>::mips_type" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2) }
+%"struct.hlsl::Texture2D<>::mips_slice_type" = type { target("dx.Texture", <4 x float>, 0, 0, 0, 2), i32 }
+
+ at _ZL1t = internal global %"class.hlsl::Texture2D" poison, align 4
+ at .str = private unnamed_addr constant [2 x i8] c"t\00", align 1
+ at llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 65535, ptr @_GLOBAL__sub_I_Texture2D_mips_errors.hlsl, ptr null }]
+
+; Function Attrs: alwaysinline convergent nounwind
+define internal void @__cxx_global_var_init() #0 {
+entry:
+ call void @_ZN4hlsl9Texture2DIDv4_fE27__createFromImplicitBindingEjjijPKc(ptr dead_on_unwind writable sret(%"class.hlsl::Texture2D") align 4 @_ZL1t, i32 noundef 0, i32 noundef 0, i32 noundef 1, i32 noundef 0, ptr noundef @.str) #3
+ ret void
+}
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define linkonce_odr hidden void @_ZN4hlsl9Texture2DIDv4_fE27__createFromImplicitBindingEjjijPKc(ptr dead_on_unwind noalias writable sret(%"class.hlsl::Texture2D") align 4 %agg.result, i32 noundef %orderId, i32 noundef %spaceNo, i32 noundef %range, i32 noundef %index, ptr noundef %name) #1 align 2 {
+entry:
+ %result.ptr = alloca ptr, align 4
+ %orderId.addr = alloca i32, align 4
+ %spaceNo.addr = alloca i32, align 4
+ %range.addr = alloca i32, align 4
+ %index.addr = alloca i32, align 4
+ %name.addr = alloca ptr, align 4
+ %tmp = alloca %"class.hlsl::Texture2D", align 4
+ store ptr %agg.result, ptr %result.ptr, align 4
+ store i32 %orderId, ptr %orderId.addr, align 4
+ store i32 %spaceNo, ptr %spaceNo.addr, align 4
+ store i32 %range, ptr %range.addr, align 4
+ store i32 %index, ptr %index.addr, align 4
+ store ptr %name, ptr %name.addr, align 4
+ call void @_ZN4hlsl9Texture2DIDv4_fEC1Ev(ptr noundef nonnull align 4 dereferenceable(8) %tmp) #3
+ %0 = load i32, ptr %orderId.addr, align 4
+ %1 = load i32, ptr %spaceNo.addr, align 4
+ %2 = load i32, ptr %range.addr, align 4
+ %3 = load i32, ptr %index.addr, align 4
+ %4 = load ptr, ptr %name.addr, align 4
+ %5 = call target("dx.Texture", <4 x float>, 0, 0, 0, 2) @llvm.dx.resource.handlefromimplicitbinding.tdx.Texture_v4f32_0_0_0_2t(i32 %0, i32 %1, i32 %2, i32 %3, ptr %4)
+ %__handle = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %tmp, i32 0, i32 0
+ store target("dx.Texture", <4 x float>, 0, 0, 0, 2) %5, ptr %__handle, align 4
+ %__handle1 = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %tmp, i32 0, i32 0
+ %6 = load target("dx.Texture", <4 x float>, 0, 0, 0, 2), ptr %__handle1, align 4
+ %mips = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %tmp, i32 0, i32 1
+ %__handle2 = getelementptr inbounds nuw %"struct.hlsl::Texture2D<>::mips_type", ptr %mips, i32 0, i32 0
+ store target("dx.Texture", <4 x float>, 0, 0, 0, 2) %6, ptr %__handle2, align 4
+ call void @_ZN4hlsl9Texture2DIDv4_fEC1ERKS2_(ptr noundef nonnull align 4 dereferenceable(8) %agg.result, ptr noundef nonnull align 4 dereferenceable(8) %tmp) #3
+ ret void
+}
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define hidden void @_Z4mainv() #1 {
+entry:
+ %a = alloca %"struct.hlsl::Texture2D<>::mips_type", align 4
+ %b = alloca %"struct.hlsl::Texture2D<>::mips_slice_type", align 4
+ %c = alloca %"struct.hlsl::Texture2D<>::mips_type", align 4
+ call void @_ZN4hlsl9Texture2DIDv4_fE9mips_typeC1Ev(ptr noundef nonnull align 4 dereferenceable(4) %a) #3
+ call void @_ZN4hlsl9Texture2DIDv4_fE15mips_slice_typeC1Ev(ptr noundef nonnull align 4 dereferenceable(8) %b) #3
+ call void @_ZN4hlsl9Texture2DIDv4_fE9mips_typeC1ERKS3_(ptr noundef nonnull align 4 dereferenceable(4) %c, ptr noundef nonnull align 4 dereferenceable(4) getelementptr inbounds nuw (i8, ptr @_ZL1t, i32 4)) #3
+ ret void
+}
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define linkonce_odr hidden void @_ZN4hlsl9Texture2DIDv4_fE9mips_typeC1Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) unnamed_addr #1 align 2 {
+entry:
+ %this.addr = alloca ptr, align 4
+ store ptr %this, ptr %this.addr, align 4
+ %this1 = load ptr, ptr %this.addr, align 4
+ call void @_ZN4hlsl9Texture2DIDv4_fE9mips_typeC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this1) #3
+ ret void
+}
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define linkonce_odr hidden void @_ZN4hlsl9Texture2DIDv4_fE15mips_slice_typeC1Ev(ptr noundef nonnull align 4 dereferenceable(8) %this) unnamed_addr #1 align 2 {
+entry:
+ %this.addr = alloca ptr, align 4
+ store ptr %this, ptr %this.addr, align 4
+ %this1 = load ptr, ptr %this.addr, align 4
+ call void @_ZN4hlsl9Texture2DIDv4_fE15mips_slice_typeC2Ev(ptr noundef nonnull align 4 dereferenceable(8) %this1) #3
+ ret void
+}
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define linkonce_odr hidden void @_ZN4hlsl9Texture2DIDv4_fE9mips_typeC1ERKS3_(ptr noundef nonnull align 4 dereferenceable(4) %this, ptr noundef nonnull align 4 dereferenceable(4) %other) unnamed_addr #1 align 2 {
+entry:
+ %this.addr = alloca ptr, align 4
+ %other.addr = alloca ptr, align 4
+ store ptr %this, ptr %this.addr, align 4
+ store ptr %other, ptr %other.addr, align 4
+ %this1 = load ptr, ptr %this.addr, align 4
+ %0 = load ptr, ptr %other.addr, align 4
+ call void @_ZN4hlsl9Texture2DIDv4_fE9mips_typeC2ERKS3_(ptr noundef nonnull align 4 dereferenceable(4) %this1, ptr noundef nonnull align 4 dereferenceable(4) %0) #3
+ ret void
+}
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define linkonce_odr hidden void @_ZN4hlsl9Texture2DIDv4_fEC1Ev(ptr noundef nonnull align 4 dereferenceable(8) %this) unnamed_addr #1 align 2 {
+entry:
+ %this.addr = alloca ptr, align 4
+ store ptr %this, ptr %this.addr, align 4
+ %this1 = load ptr, ptr %this.addr, align 4
+ call void @_ZN4hlsl9Texture2DIDv4_fEC2Ev(ptr noundef nonnull align 4 dereferenceable(8) %this1) #3
+ ret void
+}
+
+; Function Attrs: nocallback nofree nosync nounwind willreturn memory(none)
+declare target("dx.Texture", <4 x float>, 0, 0, 0, 2) @llvm.dx.resource.handlefromimplicitbinding.tdx.Texture_v4f32_0_0_0_2t(i32, i32, i32, i32, ptr) #2
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define linkonce_odr hidden void @_ZN4hlsl9Texture2DIDv4_fEC1ERKS2_(ptr noundef nonnull align 4 dereferenceable(8) %this, ptr noundef nonnull align 4 dereferenceable(8) %other) unnamed_addr #1 align 2 {
+entry:
+ %this.addr = alloca ptr, align 4
+ %other.addr = alloca ptr, align 4
+ store ptr %this, ptr %this.addr, align 4
+ store ptr %other, ptr %other.addr, align 4
+ %this1 = load ptr, ptr %this.addr, align 4
+ %0 = load ptr, ptr %other.addr, align 4
+ call void @_ZN4hlsl9Texture2DIDv4_fEC2ERKS2_(ptr noundef nonnull align 4 dereferenceable(8) %this1, ptr noundef nonnull align 4 dereferenceable(8) %0) #3
+ ret void
+}
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define linkonce_odr hidden void @_ZN4hlsl9Texture2DIDv4_fEC2Ev(ptr noundef nonnull align 4 dereferenceable(8) %this) unnamed_addr #1 align 2 {
+entry:
+ %this.addr = alloca ptr, align 4
+ store ptr %this, ptr %this.addr, align 4
+ %this1 = load ptr, ptr %this.addr, align 4
+ %mips = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %this1, i32 0, i32 1
+ call void @_ZN4hlsl9Texture2DIDv4_fE9mips_typeC1Ev(ptr noundef nonnull align 4 dereferenceable(4) %mips) #3
+ %__handle = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %this1, i32 0, i32 0
+ store target("dx.Texture", <4 x float>, 0, 0, 0, 2) poison, ptr %__handle, align 4
+ ret void
+}
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define linkonce_odr hidden void @_ZN4hlsl9Texture2DIDv4_fEC2ERKS2_(ptr noundef nonnull align 4 dereferenceable(8) %this, ptr noundef nonnull align 4 dereferenceable(8) %other) unnamed_addr #1 align 2 {
+entry:
+ %this.addr = alloca ptr, align 4
+ %other.addr = alloca ptr, align 4
+ store ptr %this, ptr %this.addr, align 4
+ store ptr %other, ptr %other.addr, align 4
+ %this1 = load ptr, ptr %this.addr, align 4
+ %mips = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %this1, i32 0, i32 1
+ call void @_ZN4hlsl9Texture2DIDv4_fE9mips_typeC1Ev(ptr noundef nonnull align 4 dereferenceable(4) %mips) #3
+ %0 = load ptr, ptr %other.addr, align 4, !nonnull !3, !align !4
+ %__handle = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %0, i32 0, i32 0
+ %1 = load target("dx.Texture", <4 x float>, 0, 0, 0, 2), ptr %__handle, align 4
+ %__handle2 = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %this1, i32 0, i32 0
+ store target("dx.Texture", <4 x float>, 0, 0, 0, 2) %1, ptr %__handle2, align 4
+ %2 = load ptr, ptr %other.addr, align 4, !nonnull !3, !align !4
+ %mips3 = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %2, i32 0, i32 1
+ %mips4 = getelementptr inbounds nuw %"class.hlsl::Texture2D", ptr %this1, i32 0, i32 1
+ %call = call noundef nonnull align 4 dereferenceable(4) ptr @_ZN4hlsl9Texture2DIDv4_fE9mips_typeaSERKS3_(ptr noundef nonnull align 4 dereferenceable(4) %mips4, ptr noundef nonnull align 4 dereferenceable(4) %mips3) #3
+ ret void
+}
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define linkonce_odr hidden noundef nonnull align 4 dereferenceable(4) ptr @_ZN4hlsl9Texture2DIDv4_fE9mips_typeaSERKS3_(ptr noundef nonnull align 4 dereferenceable(4) %this, ptr noundef nonnull align 4 dereferenceable(4) %other) #1 align 2 {
+entry:
+ %this.addr = alloca ptr, align 4
+ %other.addr = alloca ptr, align 4
+ store ptr %this, ptr %this.addr, align 4
+ store ptr %other, ptr %other.addr, align 4
+ %this1 = load ptr, ptr %this.addr, align 4
+ %0 = load ptr, ptr %other.addr, align 4, !nonnull !3, !align !4
+ %__handle = getelementptr inbounds nuw %"struct.hlsl::Texture2D<>::mips_type", ptr %0, i32 0, i32 0
+ %1 = load target("dx.Texture", <4 x float>, 0, 0, 0, 2), ptr %__handle, align 4
+ %__handle2 = getelementptr inbounds nuw %"struct.hlsl::Texture2D<>::mips_type", ptr %this1, i32 0, i32 0
+ store target("dx.Texture", <4 x float>, 0, 0, 0, 2) %1, ptr %__handle2, align 4
+ ret ptr %this1
+}
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define linkonce_odr hidden void @_ZN4hlsl9Texture2DIDv4_fE9mips_typeC2Ev(ptr noundef nonnull align 4 dereferenceable(4) %this) unnamed_addr #1 align 2 {
+entry:
+ %this.addr = alloca ptr, align 4
+ store ptr %this, ptr %this.addr, align 4
+ %this1 = load ptr, ptr %this.addr, align 4
+ %__handle = getelementptr inbounds nuw %"struct.hlsl::Texture2D<>::mips_type", ptr %this1, i32 0, i32 0
+ store target("dx.Texture", <4 x float>, 0, 0, 0, 2) poison, ptr %__handle, align 4
+ ret void
+}
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define linkonce_odr hidden void @_ZN4hlsl9Texture2DIDv4_fE15mips_slice_typeC2Ev(ptr noundef nonnull align 4 dereferenceable(8) %this) unnamed_addr #1 align 2 {
+entry:
+ %this.addr = alloca ptr, align 4
+ store ptr %this, ptr %this.addr, align 4
+ %this1 = load ptr, ptr %this.addr, align 4
+ %__handle = getelementptr inbounds nuw %"struct.hlsl::Texture2D<>::mips_slice_type", ptr %this1, i32 0, i32 0
+ store target("dx.Texture", <4 x float>, 0, 0, 0, 2) poison, ptr %__handle, align 4
+ ret void
+}
+
+; Function Attrs: alwaysinline convergent mustprogress norecurse nounwind
+define linkonce_odr hidden void @_ZN4hlsl9Texture2DIDv4_fE9mips_typeC2ERKS3_(ptr noundef nonnull align 4 dereferenceable(4) %this, ptr noundef nonnull align 4 dereferenceable(4) %other) unnamed_addr #1 align 2 {
+entry:
+ %this.addr = alloca ptr, align 4
+ %other.addr = alloca ptr, align 4
+ store ptr %this, ptr %this.addr, align 4
+ store ptr %other, ptr %other.addr, align 4
+ %this1 = load ptr, ptr %this.addr, align 4
+ %0 = load ptr, ptr %other.addr, align 4, !nonnull !3, !align !4
+ %__handle = getelementptr inbounds nuw %"struct.hlsl::Texture2D<>::mips_type", ptr %0, i32 0, i32 0
+ %1 = load target("dx.Texture", <4 x float>, 0, 0, 0, 2), ptr %__handle, align 4
+ %__handle2 = getelementptr inbounds nuw %"struct.hlsl::Texture2D<>::mips_type", ptr %this1, i32 0, i32 0
+ store target("dx.Texture", <4 x float>, 0, 0, 0, 2) %1, ptr %__handle2, align 4
+ ret void
+}
+
+; Function Attrs: alwaysinline convergent nounwind
+define internal void @_GLOBAL__sub_I_Texture2D_mips_errors.hlsl() #0 {
+entry:
+ call void @__cxx_global_var_init()
+ ret void
+}
+
+attributes #0 = { alwaysinline convergent nounwind "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #1 = { alwaysinline convergent mustprogress norecurse nounwind "no-nans-fp-math"="true" "no-signed-zeros-fp-math"="true" "no-trapping-math"="true" "stack-protector-buffer-size"="8" }
+attributes #2 = { nocallback nofree nosync nounwind willreturn memory(none) }
+attributes #3 = { convergent }
+
+!dx.valver = !{!0}
+!llvm.module.flags = !{!1}
+!llvm.ident = !{!2}
+
+!0 = !{i32 1, i32 8}
+!1 = !{i32 4, !"dx.disable_optimizations", i32 1}
+!2 = !{!"clang version 23.0.0git (git at github.com:llvm/llvm-project.git 61ec7e51067976def87b0fd8e88f084182349d51)"}
+!3 = !{}
+!4 = !{i64 4}
More information about the llvm-branch-commits
mailing list