[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