https://github.com/farzonl updated 
https://github.com/llvm/llvm-project/pull/214001

>From 695dfeb131f4e8d84e757ffae526dad639ff1443 Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <[email protected]>
Date: Mon, 3 Aug 2026 14:43:54 -0400
Subject: [PATCH 1/2] [HLSL] Modify the Matrix orientation to be AST based
 instead of Attribute

fixes https://github.com/llvm/llvm-project/issues/213996
fixes https://github.com/llvm/llvm-project/issues/211977
fixes https://godbolt.org/z/rhTYx1KGf

Store explicit matrix orientation in ConstantMatrixType so layout
survives desugaring, arrays, resources, serialization, and template deduction.

Assisted by Copilot with GPT-5.6-Sol
---
 clang/include/clang/AST/ASTContext.h          |  7 +-
 clang/include/clang/AST/MatrixUtils.h         | 26 ++------
 clang/include/clang/AST/PropertiesBase.td     |  7 ++
 clang/include/clang/AST/TypeBase.h            | 25 ++++++--
 clang/include/clang/AST/TypeProperties.td     |  5 +-
 clang/lib/AST/ASTContext.cpp                  | 44 +++++++++++--
 clang/lib/AST/ASTImporter.cpp                 |  2 +-
 clang/lib/AST/Type.cpp                        | 14 ++--
 clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp |  3 +-
 clang/lib/Sema/SemaHLSL.cpp                   | 12 +---
 clang/lib/Sema/SemaTemplateDeduction.cpp      | 11 ++++
 clang/lib/Sema/SemaType.cpp                   | 11 +++-
 clang/lib/Sema/TreeTransform.h                | 11 ++++
 .../test/CodeGenHLSL/matrix-array-layout.hlsl | 64 +++++++++++++++++++
 14 files changed, 188 insertions(+), 54 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/matrix-array-layout.hlsl

diff --git a/clang/include/clang/AST/ASTContext.h 
b/clang/include/clang/AST/ASTContext.h
index 763039e690dec..4b8adcceed285 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1850,14 +1850,17 @@ class ASTContext : public RefCountedBase<ASTContext> {
   ///
   /// \pre \p ElementType must be a valid matrix element type (see
   /// MatrixType::isValidElementType).
-  QualType getConstantMatrixType(QualType ElementType, unsigned NumRows,
-                                 unsigned NumColumns) const;
+  QualType getConstantMatrixType(
+      QualType ElementType, unsigned NumRows, unsigned NumColumns,
+      std::optional<MatrixType::LayoutKind> Layout = std::nullopt) const;
 
   /// Return the unique reference to the matrix type of the specified element
   /// type and size
   QualType getDependentSizedMatrixType(QualType ElementType, Expr *RowExpr,
                                        Expr *ColumnExpr,
                                        SourceLocation AttrLoc) const;
+  QualType getMatrixTypeWithLayout(QualType T,
+                                   MatrixType::LayoutKind Layout) const;
 
   QualType getDependentAddressSpaceType(QualType PointeeType,
                                         Expr *AddrSpaceExpr,
diff --git a/clang/include/clang/AST/MatrixUtils.h 
b/clang/include/clang/AST/MatrixUtils.h
index ef6cbba6ba7c0..1057bbd67a526 100644
--- a/clang/include/clang/AST/MatrixUtils.h
+++ b/clang/include/clang/AST/MatrixUtils.h
@@ -15,32 +15,18 @@
 #define LLVM_CLANG_AST_MATRIXUTILS_H
 
 #include "clang/AST/Type.h"
-#include "clang/Basic/AttrKinds.h"
 #include "clang/Basic/LangOptions.h"
 
 namespace clang {
 /// Returns true if matrices of \p T should be laid out in row-major order.
 ///
-/// In HLSL mode, an `HLSLRowMajor` / `HLSLColumnMajor` AttributedType anywhere
-/// in the sugar chain of \p T (imprinted by Sema when a source decl carries
-/// `[[hlsl::row_major]]` / `[[hlsl::column_major]]`) takes precedence over the
-/// `-fmatrix-memory-layout=` default carried in \p LangOpts. Otherwise the
-/// LangOptions default is used.
+/// An explicit layout stored on the matrix type takes precedence over the
+/// `-fmatrix-memory-layout=` default carried in \p LangOpts.
 inline bool isMatrixRowMajor(const LangOptions &LangOpts, QualType T) {
-  if (LangOpts.HLSL && !T.isNull()) {
-    QualType Cur = T;
-    while (const auto *AT = Cur->getAs<AttributedType>()) {
-      switch (AT->getAttrKind()) {
-      case attr::HLSLRowMajor:
-        return true;
-      case attr::HLSLColumnMajor:
-        return false;
-      default:
-        break;
-      }
-      Cur = AT->getModifiedType();
-    }
-  }
+  if (LangOpts.HLSL && !T.isNull())
+    if (const auto *MT = T->getAs<ConstantMatrixType>())
+      if (auto Layout = MT->getLayout())
+        return *Layout == MatrixType::LayoutKind::RowMajor;
   return LangOpts.getDefaultMatrixMemoryLayout() ==
          LangOptions::MatrixMemoryLayout::MatrixRowMajor;
 }
diff --git a/clang/include/clang/AST/PropertiesBase.td 
b/clang/include/clang/AST/PropertiesBase.td
index 25ef4c26a9aa1..bab9787d0dc13 100644
--- a/clang/include/clang/AST/PropertiesBase.td
+++ b/clang/include/clang/AST/PropertiesBase.td
@@ -130,6 +130,13 @@ def LValuePathSerializationHelper :
     PropertyType<"APValue::LValuePathSerializationHelper"> {
   let BufferElementTypes = [ LValuePathEntry ];
 }
+def MatrixLayoutKind : EnumPropertyType<"MatrixType::LayoutKind"> {
+  let PackOptional =
+    "value.value_or(static_cast<MatrixType::LayoutKind>(2))";
+  let UnpackOptional =
+    "value == static_cast<MatrixType::LayoutKind>(2) ? std::nullopt : "
+    "std::optional<MatrixType::LayoutKind>(value)";
+}
 def NestedNameSpecifier : PropertyType<"NestedNameSpecifier">;
 def NestedNameSpecifierKind : EnumPropertyType<"NestedNameSpecifier::Kind">;
 def OverloadedOperatorKind : EnumPropertyType;
diff --git a/clang/include/clang/AST/TypeBase.h 
b/clang/include/clang/AST/TypeBase.h
index 4851c4e5185dd..b6129b1814d70 100644
--- a/clang/include/clang/AST/TypeBase.h
+++ b/clang/include/clang/AST/TypeBase.h
@@ -4443,9 +4443,16 @@ class MatrixType : public Type, public 
llvm::FoldingSetNode {
 protected:
   friend class ASTContext;
 
+public:
+  /// A matrix's explicit `row_major`/`column_major` orientation, if any was
+  /// spelled in source; absent means no explicit orientation was given.
+  enum class LayoutKind : uint8_t { RowMajor, ColumnMajor };
+
+private:
   /// The element type of the matrix.
   QualType ElementType;
 
+protected:
   MatrixType(QualType ElementTy, QualType CanonElementTy);
 
   MatrixType(TypeClass TypeClass, QualType ElementTy, QualType CanonElementTy,
@@ -4497,11 +4504,18 @@ class ConstantMatrixType final : public MatrixType {
   unsigned NumRows;
   unsigned NumColumns;
 
+  /// Only ConstantMatrixType has a layout; it isn't meaningful until the
+  /// matrix dimensions are concrete. Absent if no explicit orientation was
+  /// spelled in source.
+  std::optional<LayoutKind> Layout;
+
   ConstantMatrixType(QualType MatrixElementType, unsigned NRows,
-                     unsigned NColumns, QualType CanonElementType);
+                     unsigned NColumns, QualType CanonElementType,
+                     std::optional<LayoutKind> Layout);
 
   ConstantMatrixType(TypeClass typeClass, QualType MatrixType, unsigned NRows,
-                     unsigned NColumns, QualType CanonElementType);
+                     unsigned NColumns, QualType CanonElementType,
+                     std::optional<LayoutKind> Layout);
 
 public:
   /// Returns the number of rows in the matrix.
@@ -4510,6 +4524,8 @@ class ConstantMatrixType final : public MatrixType {
   /// Returns the number of columns in the matrix.
   unsigned getNumColumns() const { return NumColumns; }
 
+  std::optional<LayoutKind> getLayout() const { return Layout; }
+
   /// Returns the number of elements required to embed the matrix into a 
vector.
   unsigned getNumElementsFlattened() const {
     return getNumRows() * getNumColumns();
@@ -4555,16 +4571,17 @@ class ConstantMatrixType final : public MatrixType {
   }
 
   void Profile(llvm::FoldingSetNodeID &ID) {
-    Profile(ID, getElementType(), getNumRows(), getNumColumns(),
+    Profile(ID, getElementType(), getNumRows(), getNumColumns(), getLayout(),
             getTypeClass());
   }
 
   static void Profile(llvm::FoldingSetNodeID &ID, QualType ElementType,
                       unsigned NumRows, unsigned NumColumns,
-                      TypeClass TypeClass) {
+                      std::optional<LayoutKind> Layout, TypeClass TypeClass) {
     ID.AddPointer(ElementType.getAsOpaquePtr());
     ID.AddInteger(NumRows);
     ID.AddInteger(NumColumns);
+    ID.AddInteger(Layout ? llvm::to_underlying(*Layout) + 1 : 0);
     ID.AddInteger(TypeClass);
   }
 
diff --git a/clang/include/clang/AST/TypeProperties.td 
b/clang/include/clang/AST/TypeProperties.td
index 1185a3b1dc670..f9066d07b0f9c 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -254,9 +254,12 @@ let Class = ConstantMatrixType in {
   def : Property<"numColumns", UInt32> {
     let Read = [{ node->getNumColumns() }];
   }
+  def : Property<"layout", Optional<MatrixLayoutKind>> {
+    let Read = [{ node->getLayout() }];
+  }
 
   def : Creator<[{
-    return ctx.getConstantMatrixType(elementType, numRows, numColumns);
+    return ctx.getConstantMatrixType(elementType, numRows, numColumns, layout);
   }]>;
 }
 
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 5f1e5b30ee50c..c21ad7b7e01b2 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -4855,10 +4855,11 @@ ASTContext::getDependentSizedExtVectorType(QualType 
vecType,
   return QualType(New, 0);
 }
 
-QualType ASTContext::getConstantMatrixType(QualType ElementTy, unsigned 
NumRows,
-                                           unsigned NumColumns) const {
+QualType ASTContext::getConstantMatrixType(
+    QualType ElementTy, unsigned NumRows, unsigned NumColumns,
+    std::optional<MatrixType::LayoutKind> Layout) const {
   llvm::FoldingSetNodeID ID;
-  ConstantMatrixType::Profile(ID, ElementTy, NumRows, NumColumns,
+  ConstantMatrixType::Profile(ID, ElementTy, NumRows, NumColumns, Layout,
                               Type::ConstantMatrix);
 
   assert(MatrixType::isValidElementType(ElementTy, getLangOpts()) &&
@@ -4872,8 +4873,8 @@ QualType ASTContext::getConstantMatrixType(QualType 
ElementTy, unsigned NumRows,
 
   QualType Canonical;
   if (!ElementTy.isCanonical()) {
-    Canonical =
-        getConstantMatrixType(getCanonicalType(ElementTy), NumRows, 
NumColumns);
+    Canonical = getConstantMatrixType(getCanonicalType(ElementTy), NumRows,
+                                      NumColumns, Layout);
 
     ConstantMatrixType *NewIP = MatrixTypes.FindNodeOrInsertPos(ID, InsertPos);
     assert(!NewIP && "Matrix type shouldn't already exist in the map");
@@ -4881,7 +4882,7 @@ QualType ASTContext::getConstantMatrixType(QualType 
ElementTy, unsigned NumRows,
   }
 
   auto *New = new (*this, alignof(ConstantMatrixType))
-      ConstantMatrixType(ElementTy, NumRows, NumColumns, Canonical);
+      ConstantMatrixType(ElementTy, NumRows, NumColumns, Canonical, Layout);
   MatrixTypes.InsertNode(New, InsertPos);
   Types.push_back(New);
   return QualType(New, 0);
@@ -4928,6 +4929,37 @@ QualType 
ASTContext::getDependentSizedMatrixType(QualType ElementTy,
   return QualType(New, 0);
 }
 
+QualType
+ASTContext::getMatrixTypeWithLayout(QualType T,
+                                    MatrixType::LayoutKind Layout) const {
+  Qualifiers Quals = T.getQualifiers();
+  const Type *Ty = T->getUnqualifiedDesugaredType();
+
+  if (const auto *MT = dyn_cast<ConstantMatrixType>(Ty))
+    return getQualifiedType(getConstantMatrixType(MT->getElementType(),
+                                                  MT->getNumRows(),
+                                                  MT->getNumColumns(), Layout),
+                            Quals);
+
+  // `row_major`/`column_major` are HLSL-only and only ever applied to a
+  // non-dependent type, so a ConstantArrayType (or its HLSL parameter-decayed
+  // ArrayParameterType subclass) is the only array kind that can wrap a
+  // matrix here: HLSL has no VLAs or incomplete data arrays, and dependent
+  // array bounds imply a dependent type, which is rejected before this point.
+  const auto *CAT = dyn_cast<ConstantArrayType>(Ty);
+  if (!CAT)
+    return T;
+
+  QualType Result = getConstantArrayType(
+      getMatrixTypeWithLayout(CAT->getElementType(), Layout), CAT->getSize(),
+      CAT->getSizeExpr(), CAT->getSizeModifier(),
+      CAT->getIndexTypeCVRQualifiers());
+  if (isa<ArrayParameterType>(CAT))
+    Result = getArrayParameterType(Result);
+
+  return getQualifiedType(Result, Quals);
+}
+
 QualType ASTContext::getDependentAddressSpaceType(QualType PointeeType,
                                                   Expr *AddrSpaceExpr,
                                                   SourceLocation AttrLoc) 
const {
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index 3ad71a223903c..f3345cedb6517 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -2095,7 +2095,7 @@ ExpectedType 
clang::ASTNodeImporter::VisitConstantMatrixType(
     return ToElementTypeOrErr.takeError();
 
   return Importer.getToContext().getConstantMatrixType(
-      *ToElementTypeOrErr, T->getNumRows(), T->getNumColumns());
+      *ToElementTypeOrErr, T->getNumRows(), T->getNumColumns(), 
T->getLayout());
 }
 
 ExpectedType clang::ASTNodeImporter::VisitDependentAddressSpaceType(
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index e51e7de9f176a..3dae2684236b8 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -413,15 +413,17 @@ MatrixType::MatrixType(TypeClass tc, QualType matrixType, 
QualType canonType,
       ElementType(matrixType) {}
 
 ConstantMatrixType::ConstantMatrixType(QualType matrixType, unsigned nRows,
-                                       unsigned nColumns, QualType canonType)
-    : ConstantMatrixType(ConstantMatrix, matrixType, nRows, nColumns,
-                         canonType) {}
+                                       unsigned nColumns, QualType canonType,
+                                       std::optional<LayoutKind> Layout)
+    : ConstantMatrixType(ConstantMatrix, matrixType, nRows, nColumns, 
canonType,
+                         Layout) {}
 
 ConstantMatrixType::ConstantMatrixType(TypeClass tc, QualType matrixType,
                                        unsigned nRows, unsigned nColumns,
-                                       QualType canonType)
+                                       QualType canonType,
+                                       std::optional<LayoutKind> Layout)
     : MatrixType(tc, matrixType, canonType), NumRows(nRows),
-      NumColumns(nColumns) {}
+      NumColumns(nColumns), Layout(Layout) {}
 
 DependentSizedMatrixType::DependentSizedMatrixType(QualType ElementType,
                                                    QualType CanonicalType,
@@ -1187,7 +1189,7 @@ struct SimpleTransformVisitor : public 
TypeVisitor<Derived, QualType> {
       return QualType(T, 0);
 
     return Ctx.getConstantMatrixType(elementType, T->getNumRows(),
-                                     T->getNumColumns());
+                                     T->getNumColumns(), T->getLayout());
   }
 
   QualType VisitOverflowBehaviorType(const OverflowBehaviorType *T) {
diff --git a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp 
b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
index b09fc4c91df2a..075eaa8d82c13 100644
--- a/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
+++ b/clang/lib/CodeGen/HLSLBufferLayoutBuilder.cpp
@@ -136,8 +136,7 @@ llvm::Type *HLSLBufferLayoutBuilder::layOutArray(const 
ConstantArrayType *AT) {
 llvm::Type *HLSLBufferLayoutBuilder::layOutMatrix(QualType Ty) {
   // ConvertTypeForMem already handles row/column-major layout and bool
   // promotion, producing [Count x <VecLen x EltTy>]. We just need to add
-  // cbuffer padding between the array elements. Pass the sugared QualType so
-  // that the `row_major`/`column_major` orientation attribute is preserved.
+  // cbuffer padding between the array elements.
   llvm::ArrayType *MemTy =
       cast<llvm::ArrayType>(CGM.getTypes().ConvertTypeForMem(Ty));
   return padArrayElements(MemTy->getElementType(), MemTy->getNumElements());
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index c353c3fec3f62..05ae000b6f18f 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -525,15 +525,8 @@ static const Type *createHostLayoutType(Sema &S, const 
Type *Ty) {
   return Ty;
 }
 
-// Returns the type to use for a host layout struct field. For most types this
-// is the unqualified desugared type. Matrix types, however, retain their sugar
-// so that the row_major/column_major orientation (carried as an 
AttributedType)
-// is preserved; the orientation determines the in-memory cbuffer layout.
 static const Type *getHostLayoutFieldType(QualType QT) {
-  const Type *Desugared = QT->getUnqualifiedDesugaredType();
-  if (Desugared->isConstantMatrixType())
-    return QT.getTypePtr();
-  return Desugared;
+  return QT->getUnqualifiedDesugaredType();
 }
 
 // Creates a field declaration of given name and type for HLSL buffer layout
@@ -2845,8 +2838,7 @@ void SemaHLSL::propagateContextualMatrixLayout(Expr *E, 
QualType DestType) {
   if (!CallMat || CallMat->getNumRows() != DestMat->getNumRows() ||
       CallMat->getNumColumns() != DestMat->getNumColumns())
     return;
-  // Re-type the call with the destination sugar so CodeGen lowers into that
-  // layout, not the TU default.
+  // Re-type the call with the destination layout.
   Call->setType(DestType.getUnqualifiedType());
 }
 
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp 
b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 3c45806c47a6e..f0db5218ffe1e 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -3747,6 +3747,17 @@ CheckOriginalCallArgDeduction(Sema &S, 
TemplateDeductionInfo &Info,
                           DeducedA.getAtomicUnqualifiedType()))
     return TemplateDeductionResult::Success;
 
+  if (S.getLangOpts().HLSL) {
+    const auto *AMatrix = A->getAs<ConstantMatrixType>();
+    const auto *DeducedMatrix = DeducedA->getAs<ConstantMatrixType>();
+    if (AMatrix && DeducedMatrix &&
+        AMatrix->getNumRows() == DeducedMatrix->getNumRows() &&
+        AMatrix->getNumColumns() == DeducedMatrix->getNumColumns() &&
+        Context.hasSameType(AMatrix->getElementType(),
+                            DeducedMatrix->getElementType()))
+      return TemplateDeductionResult::Success;
+  }
+
   // Strip off references on the argument types; they aren't needed for
   // the following checks.
   if (const ReferenceType *DeducedARef = DeducedA->getAs<ReferenceType>())
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index dc3564c8b17fd..c1a0eec829885 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -9136,8 +9136,15 @@ static void processTypeAttrs(TypeProcessingState &state, 
QualType &type,
     case ParsedAttr::AT_HLSLRowMajor:
     case ParsedAttr::AT_HLSLColumnMajor:
       if (Attr *A =
-              state.getSema().HLSL().buildMatrixLayoutTypeAttr(type, attr))
-        type = state.getAttributedType(A, type, type);
+              state.getSema().HLSL().buildMatrixLayoutTypeAttr(type, attr)) {
+        MatrixType::LayoutKind Layout =
+            attr.getKind() == ParsedAttr::AT_HLSLRowMajor
+                ? MatrixType::LayoutKind::RowMajor
+                : MatrixType::LayoutKind::ColumnMajor;
+        QualType Equivalent =
+            state.getSema().Context.getMatrixTypeWithLayout(type, Layout);
+        type = state.getAttributedType(A, type, Equivalent);
+      }
       attr.setUsedAsTypeAttr();
       break;
     OBJC_POINTER_TYPE_ATTRS_CASELIST:
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 2083fcd372e81..0b96b943e925f 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -7721,6 +7721,17 @@ QualType 
TreeTransform<Derived>::TransformAttributedType(TypeLocBuilder &TLB,
         return QualType();
     }
 
+    if (SemaRef.getLangOpts().HLSL &&
+        (oldType->getAttrKind() == attr::HLSLRowMajor ||
+         oldType->getAttrKind() == attr::HLSLColumnMajor)) {
+      MatrixType::LayoutKind Layout =
+          oldType->getAttrKind() == attr::HLSLRowMajor
+              ? MatrixType::LayoutKind::RowMajor
+              : MatrixType::LayoutKind::ColumnMajor;
+      equivalentType =
+          SemaRef.Context.getMatrixTypeWithLayout(equivalentType, Layout);
+    }
+
     // Check whether we can add nullability; it is only represented as
     // type sugar, and therefore cannot be diagnosed in any other way.
     if (auto nullability = oldType->getImmediateNullability()) {
diff --git a/clang/test/CodeGenHLSL/matrix-array-layout.hlsl 
b/clang/test/CodeGenHLSL/matrix-array-layout.hlsl
new file mode 100644
index 0000000000000..b2faba5483e52
--- /dev/null
+++ b/clang/test/CodeGenHLSL/matrix-array-layout.hlsl
@@ -0,0 +1,64 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.4-compute -x hlsl -emit-llvm 
-finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s
+
+// StructuredBuffer is read-only and stores one handle per resource.
+// CHECK: type { target("dx.RawBuffer", [3 x <2 x float>], 0, 0) } 
+
+// CHECK: type { target("dx.RawBuffer", [2 x <3 x float>], 1, 0), 
target("dx.RawBuffer", [2 x <3 x float>], 1, 0) }
+// CHECK: type { target("dx.RawBuffer", [3 x <2 x float>], 1, 1), 
target("dx.RawBuffer", [3 x <2 x float>], 1, 1) }
+
+// The array element layout matches the bare matrix layout for each 
orientation.
+// CHECK: %rm_arr = alloca [2 x [2 x <3 x float>]], align 4
+// CHECK: %cm_arr = alloca [2 x [3 x <2 x float>]], align 4
+// CHECK: %rm_bare = alloca [2 x <3 x float>], align 4
+// CHECK: %cm_bare = alloca [3 x <2 x float>], align 4
+// CHECK: %[[RM_VALUE:.*]] = load <6 x float>, ptr %rm_bare, align 4
+// CHECK: %[[RM_ELEMENT:.*]] = getelementptr inbounds [2 x [2 x <3 x float>]], 
ptr %rm_arr, i32 0, i32 0
+// CHECK: store <6 x float> %[[RM_VALUE]], ptr %[[RM_ELEMENT]], align 4
+// CHECK: %[[CM_VALUE:.*]] = load <6 x float>, ptr %cm_bare, align 4
+// CHECK: %[[CM_ELEMENT:.*]] = getelementptr inbounds [2 x [3 x <2 x float>]], 
ptr %cm_arr, i32 0, i32 0
+// CHECK: store <6 x float> %[[CM_VALUE]], ptr %[[CM_ELEMENT]], align 4
+
+export void f() {
+  row_major    float2x3 rm_arr[2];
+  column_major float2x3 cm_arr[2];
+  row_major    float2x3 rm_bare;
+  column_major float2x3 cm_bare;
+  rm_arr[0] = rm_bare;
+  cm_arr[0] = cm_bare;
+}
+
+StructuredBuffer<column_major float2x3> ColumnSource : register(t0);
+RWStructuredBuffer<row_major float2x3> RowDestination : register(u0);
+RasterizerOrderedStructuredBuffer<column_major float2x3> ColumnDestination
+  : register(u1);
+
+[numthreads(1,1,1)]
+void main() {
+  RowDestination[0] = ColumnSource[0];
+  ColumnDestination[0] = RowDestination[0];
+}
+
+// CHECK-LABEL: define internal void @_Z4mainv()
+// CHECK: %[[CM_PTR:.*]] = call {{.*}} ptr 
{{.*}}StructuredBuffer{{.*}}ColumnSource
+// CHECK: %[[CM_LOAD:.*]] = load <6 x float>, ptr %[[CM_PTR]], align 4
+// CHECK: %[[RM_PTR:.*]] = call {{.*}} ptr 
{{.*}}RWStructuredBuffer{{.*}}RowDestination
+// CHECK: store <6 x float> %[[CM_LOAD]], ptr %[[RM_PTR]], align 4
+// CHECK: %[[RM_SRC_PTR:.*]] = call {{.*}} ptr 
{{.*}}RWStructuredBuffer{{.*}}RowDestination
+// CHECK: %[[RM_LOAD:.*]] = load <6 x float>, ptr %[[RM_SRC_PTR]], align 4
+// CHECK: %[[CM_DST_PTR:.*]] = call {{.*}} ptr 
{{.*}}RasterizerOrderedStructuredBuffer{{.*}}ColumnDestination
+// CHECK: store <6 x float> %[[RM_LOAD]], ptr %[[CM_DST_PTR]], align 4
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} ptr 
@_ZNK4hlsl16StructuredBuffer
+// CHECK: %[[CM_HANDLE_PTR:.*]] = getelementptr 
{{.*}}%"class.hlsl::StructuredBuffer", ptr {{.*}}, i32 0, i32 0
+// CHECK: %[[CM_HANDLE:.*]] = load target("dx.RawBuffer", [3 x <2 x float>], 
0, 0), ptr %[[CM_HANDLE_PTR]], align 4
+// CHECK: call ptr @llvm.dx.resource.getpointer{{.*}}(target("dx.RawBuffer", 
[3 x <2 x float>], 0, 0) %[[CM_HANDLE]], i32 {{.*}})
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} ptr 
@_ZNK4hlsl18RWStructuredBuffer
+// CHECK: %[[RM_HANDLE_PTR:.*]] = getelementptr 
{{.*}}%"class.hlsl::RWStructuredBuffer", ptr {{.*}}, i32 0, i32 0
+// CHECK: %[[RM_HANDLE:.*]] = load target("dx.RawBuffer", [2 x <3 x float>], 
1, 0), ptr %[[RM_HANDLE_PTR]], align 4
+// CHECK: call ptr @llvm.dx.resource.getpointer{{.*}}(target("dx.RawBuffer", 
[2 x <3 x float>], 1, 0) %[[RM_HANDLE]], i32 {{.*}})
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} ptr 
@_ZNK4hlsl33RasterizerOrderedStructuredBuffer
+// CHECK: %[[CM_DST_HANDLE_PTR:.*]] = getelementptr 
{{.*}}%"class.hlsl::RasterizerOrderedStructuredBuffer", ptr {{.*}}, i32 0, i32 0
+// CHECK: %[[CM_DST_HANDLE:.*]] = load target("dx.RawBuffer", [3 x <2 x 
float>], 1, 1), ptr %[[CM_DST_HANDLE_PTR]], align 4
+// CHECK: call ptr @llvm.dx.resource.getpointer{{.*}}(target("dx.RawBuffer", 
[3 x <2 x float>], 1, 1) %[[CM_DST_HANDLE]], i32 {{.*}})

>From eb9b2fbbdb8e4423f177db9be7cea3d9c0271971 Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <[email protected]>
Date: Tue, 11 Aug 2026 14:52:17 -0400
Subject: [PATCH 2/2] Add ICK_HLSL_Matrix_Layout typr conversions and type name
 mangling for layout

---
 clang/include/clang/Sema/Overload.h           |  3 +
 clang/lib/AST/ASTContext.cpp                  |  4 +-
 clang/lib/AST/ItaniumMangle.cpp               |  5 ++
 clang/lib/AST/MicrosoftMangle.cpp             |  3 +
 clang/lib/Sema/SemaExpr.cpp                   | 15 ++++
 clang/lib/Sema/SemaExprCXX.cpp                | 20 +++++
 clang/lib/Sema/SemaOverload.cpp               | 12 ++-
 .../test/CodeGenHLSL/matrix-array-layout.hlsl | 26 +++++-
 .../matrix-layout-attr-overrides-default.hlsl | 86 ++++++++++++-------
 clang/test/SemaHLSL/matrix_layout_attr.hlsl   | 27 ++++++
 10 files changed, 161 insertions(+), 40 deletions(-)

diff --git a/clang/include/clang/Sema/Overload.h 
b/clang/include/clang/Sema/Overload.h
index 1e412ff6fc9e2..e4a108404eed6 100644
--- a/clang/include/clang/Sema/Overload.h
+++ b/clang/include/clang/Sema/Overload.h
@@ -210,6 +210,9 @@ class Sema;
     /// HLSL matrix splat from scalar or boolean type.
     ICK_HLSL_Matrix_Splat,
 
+    /// HLSL conversion between matrix memory layouts.
+    ICK_HLSL_Matrix_Layout,
+
     /// The number of conversion kinds
     ICK_Num_Conversion_Kinds,
   };
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index c21ad7b7e01b2..6dec8becedafe 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -14617,8 +14617,10 @@ static QualType getCommonNonSugarTypeNode(const 
ASTContext &Ctx, const Type *X,
                *MY = cast<ConstantMatrixType>(Y);
     assert(MX->getNumRows() == MY->getNumRows());
     assert(MX->getNumColumns() == MY->getNumColumns());
+    assert(MX->getLayout() == MY->getLayout());
     return Ctx.getConstantMatrixType(getCommonElementType(Ctx, MX, MY),
-                                     MX->getNumRows(), MX->getNumColumns());
+                                     MX->getNumRows(), MX->getNumColumns(),
+                                     MX->getLayout());
   }
   case Type::DependentSizedMatrix: {
     const auto *MX = cast<DependentSizedMatrixType>(X),
diff --git a/clang/lib/AST/ItaniumMangle.cpp b/clang/lib/AST/ItaniumMangle.cpp
index f8e6b898be250..49dc7043849ee 100644
--- a/clang/lib/AST/ItaniumMangle.cpp
+++ b/clang/lib/AST/ItaniumMangle.cpp
@@ -4406,6 +4406,11 @@ void CXXNameMangler::mangleType(const ConstantMatrixType 
*T) {
   llvm::APSInt Columns(BitWidth);
   Columns = T->getNumColumns();
   mangleIntegerLiteral(ASTCtx.getSizeType(), Columns);
+  if (std::optional<MatrixType::LayoutKind> Layout = T->getLayout()) {
+    llvm::APSInt LayoutValue(BitWidth);
+    LayoutValue = static_cast<unsigned>(*Layout) + 1;
+    mangleIntegerLiteral(ASTCtx.getSizeType(), LayoutValue);
+  }
   mangleType(T->getElementType());
   Out << "E";
 }
diff --git a/clang/lib/AST/MicrosoftMangle.cpp 
b/clang/lib/AST/MicrosoftMangle.cpp
index a1f2b671d6216..4ad552b9e7445 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -3766,6 +3766,9 @@ void MicrosoftCXXNameMangler::mangleType(const 
ConstantMatrixType *T,
 
   Extra.mangleIntegerLiteral(llvm::APSInt::getUnsigned(T->getNumRows()));
   Extra.mangleIntegerLiteral(llvm::APSInt::getUnsigned(T->getNumColumns()));
+  if (std::optional<MatrixType::LayoutKind> Layout = T->getLayout())
+    Extra.mangleIntegerLiteral(
+        llvm::APSInt::getUnsigned(static_cast<unsigned>(*Layout) + 1));
 
   mangleArtificialTagType(TagTypeKind::Struct, TemplateMangling, {"__clang"});
 }
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 59b8c9b60663c..73134751814d0 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -13778,6 +13778,21 @@ QualType 
Sema::CheckMatrixElementwiseOperands(ExprResult &LHS, ExprResult &RHS,
   if (Context.hasSameType(LHSType, RHSType))
     return Context.getCommonSugaredType(LHSType, RHSType);
 
+  if (const auto *LHSConstantMat = dyn_cast_or_null<ConstantMatrixType>(
+          LHSMatType)) {
+    const auto *RHSConstantMat =
+        dyn_cast_or_null<ConstantMatrixType>(RHSMatType);
+    if (RHSConstantMat &&
+        LHSConstantMat->getNumRows() == RHSConstantMat->getNumRows() &&
+        LHSConstantMat->getNumColumns() == RHSConstantMat->getNumColumns() &&
+        Context.hasSameUnqualifiedType(LHSConstantMat->getElementType(),
+                                       RHSConstantMat->getElementType())) {
+      RHS = tryConvertExprToType(RHS.get(), LHSType);
+      if (!RHS.isInvalid())
+        return LHSType;
+    }
+  }
+
   // Type conversion may change LHS/RHS. Keep copies to the original results, 
in
   // case we have to return InvalidOperands.
   ExprResult OriginalLHS = LHS;
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 538604aa2e64b..f570010fcc325 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5306,6 +5306,7 @@ Sema::PerformImplicitConversion(Expr *From, QualType 
ToType,
   case ICK_HLSL_Matrix_Truncation:
   case ICK_HLSL_Vector_Splat:
   case ICK_HLSL_Matrix_Splat:
+  case ICK_HLSL_Matrix_Layout:
     llvm_unreachable("Improper second standard conversion");
   }
 
@@ -5363,6 +5364,11 @@ Sema::PerformImplicitConversion(Expr *From, QualType 
ToType,
                  .get();
       break;
     }
+    case ICK_HLSL_Matrix_Layout:
+      From = ImpCastExprToType(From, ToType, CK_HLSLMatrixTruncation,
+                               From->getValueKind())
+                 .get();
+      break;
     case ICK_Identity:
     default:
       llvm_unreachable("Improper element standard conversion");
@@ -6011,6 +6017,20 @@ QualType Sema::CXXCheckConditionalOperands(ExprResult 
&Cond, ExprResult &LHS,
     return QualType();
   }
 
+  if (getLangOpts().HLSL) {
+    const auto *LMat = LTy->getAs<ConstantMatrixType>();
+    const auto *RMat = RTy->getAs<ConstantMatrixType>();
+    if (LMat && RMat && LMat->getNumRows() == RMat->getNumRows() &&
+        LMat->getNumColumns() == RMat->getNumColumns() &&
+        Context.hasSameUnqualifiedType(LMat->getElementType(),
+                                       RMat->getElementType())) {
+      RHS = tryConvertExprToType(RHS.get(), LTy);
+      if (RHS.isInvalid())
+        return QualType();
+      RTy = RHS.get()->getType();
+    }
+  }
+
   // C++11 [expr.cond]p3
   //   Otherwise, if the second and third operand have different types, and
   //   either has (cv) class type [...] an attempt is made to convert each of
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index d66dea0d918fa..740cb974da1f6 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -173,6 +173,7 @@ ImplicitConversionRank 
clang::GetConversionRank(ImplicitConversionKind Kind) {
       ICR_Conversion,
       ICR_HLSL_Scalar_Widening,
       ICR_HLSL_Scalar_Widening,
+        ICR_Exact_Match,
   };
   static_assert(std::size(Rank) == (int)ICK_Num_Conversion_Kinds);
   return Rank[(int)Kind];
@@ -237,6 +238,7 @@ static const char 
*GetImplicitConversionName(ImplicitConversionKind Kind) {
       "Non-decaying array conversion",
       "HLSL vector splat",
       "HLSL matrix splat",
+        "HLSL matrix layout conversion",
   };
   static_assert(std::size(Name) == (int)ICK_Num_Conversion_Kinds);
   return Name[Kind];
@@ -2165,10 +2167,13 @@ static bool IsMatrixConversion(Sema &S, QualType 
FromType, QualType ToType,
     if (FromRows < ToRows)
       return false;
 
-    if (FromRows == ToRows && FromCols == ToCols)
-      ElConv = ICK_Identity;
-    else
+    if (FromRows == ToRows && FromCols == ToCols) {
+      ElConv = FromMatrixType->getLayout() == ToMatrixType->getLayout()
+                   ? ICK_Identity
+                   : ICK_HLSL_Matrix_Layout;
+    } else {
       ElConv = ICK_HLSL_Matrix_Truncation;
+    }
 
     QualType FromElTy = FromMatrixType->getElementType();
     QualType ToElTy = ToMatrixType->getElementType();
@@ -6453,6 +6458,7 @@ static bool CheckConvertedConstantConversions(Sema &S,
   case ICK_RVV_Vector_Conversion:
   case ICK_HLSL_Vector_Splat:
   case ICK_HLSL_Matrix_Splat:
+  case ICK_HLSL_Matrix_Layout:
   case ICK_Vector_Splat:
   case ICK_Complex_Real:
   case ICK_Block_Pointer_Conversion:
diff --git a/clang/test/CodeGenHLSL/matrix-array-layout.hlsl 
b/clang/test/CodeGenHLSL/matrix-array-layout.hlsl
index b2faba5483e52..65ce6789798c0 100644
--- a/clang/test/CodeGenHLSL/matrix-array-layout.hlsl
+++ b/clang/test/CodeGenHLSL/matrix-array-layout.hlsl
@@ -2,6 +2,7 @@
 
 // StructuredBuffer is read-only and stores one handle per resource.
 // CHECK: type { target("dx.RawBuffer", [3 x <2 x float>], 0, 0) } 
+// CHECK: type { target("dx.RawBuffer", [2 x <3 x float>], 0, 0) }
 
 // CHECK: type { target("dx.RawBuffer", [2 x <3 x float>], 1, 0), 
target("dx.RawBuffer", [2 x <3 x float>], 1, 0) }
 // CHECK: type { target("dx.RawBuffer", [3 x <2 x float>], 1, 1), 
target("dx.RawBuffer", [3 x <2 x float>], 1, 1) }
@@ -27,7 +28,18 @@ export void f() {
   cm_arr[0] = cm_bare;
 }
 
+float use_default_layout(float2x3 M) { return M[0][0]; }
+
+export float call_default_layout(row_major float2x3 M) {
+  return use_default_layout(M);
+}
+
+// CHECK-LABEL: define {{.*}} float 
@_Z19call_default_layoutu11matrix_typeILm2ELm3ELm1EfE
+// CHECK: %[[CALL_LAYOUT:.*]] = shufflevector <6 x float> %{{.*}}, <6 x float> 
poison, <6 x i32> <i32 0, i32 3, i32 1, i32 4, i32 2, i32 5>
+// CHECK: call {{.*}} float 
@_Z18use_default_layoutu11matrix_typeILm2ELm3EfE(<6 x float> 
{{.*}}%[[CALL_LAYOUT]])
+
 StructuredBuffer<column_major float2x3> ColumnSource : register(t0);
+StructuredBuffer<row_major float2x3> RowSource : register(t1);
 RWStructuredBuffer<row_major float2x3> RowDestination : register(u0);
 RasterizerOrderedStructuredBuffer<column_major float2x3> ColumnDestination
   : register(u1);
@@ -36,19 +48,22 @@ RasterizerOrderedStructuredBuffer<column_major float2x3> 
ColumnDestination
 void main() {
   RowDestination[0] = ColumnSource[0];
   ColumnDestination[0] = RowDestination[0];
+  RowDestination[1] = RowSource[0];
 }
 
 // CHECK-LABEL: define internal void @_Z4mainv()
 // CHECK: %[[CM_PTR:.*]] = call {{.*}} ptr 
{{.*}}StructuredBuffer{{.*}}ColumnSource
 // CHECK: %[[CM_LOAD:.*]] = load <6 x float>, ptr %[[CM_PTR]], align 4
+// CHECK: %[[CM_TO_RM:.*]] = shufflevector <6 x float> %[[CM_LOAD]], <6 x 
float> poison, <6 x i32> <i32 0, i32 2, i32 4, i32 1, i32 3, i32 5>
 // CHECK: %[[RM_PTR:.*]] = call {{.*}} ptr 
{{.*}}RWStructuredBuffer{{.*}}RowDestination
-// CHECK: store <6 x float> %[[CM_LOAD]], ptr %[[RM_PTR]], align 4
+// CHECK: store <6 x float> %[[CM_TO_RM]], ptr %[[RM_PTR]], align 4
 // CHECK: %[[RM_SRC_PTR:.*]] = call {{.*}} ptr 
{{.*}}RWStructuredBuffer{{.*}}RowDestination
 // CHECK: %[[RM_LOAD:.*]] = load <6 x float>, ptr %[[RM_SRC_PTR]], align 4
+// CHECK: %[[RM_TO_CM:.*]] = shufflevector <6 x float> %[[RM_LOAD]], <6 x 
float> poison, <6 x i32> <i32 0, i32 3, i32 1, i32 4, i32 2, i32 5>
 // CHECK: %[[CM_DST_PTR:.*]] = call {{.*}} ptr 
{{.*}}RasterizerOrderedStructuredBuffer{{.*}}ColumnDestination
-// CHECK: store <6 x float> %[[RM_LOAD]], ptr %[[CM_DST_PTR]], align 4
+// CHECK: store <6 x float> %[[RM_TO_CM]], ptr %[[CM_DST_PTR]], align 4
 
-// CHECK-LABEL: define linkonce_odr hidden {{.*}} ptr 
@_ZNK4hlsl16StructuredBuffer
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} ptr 
@_ZNK4hlsl16StructuredBufferIu11matrix_typeILm2ELm3ELm2EfEEixEj
 // CHECK: %[[CM_HANDLE_PTR:.*]] = getelementptr 
{{.*}}%"class.hlsl::StructuredBuffer", ptr {{.*}}, i32 0, i32 0
 // CHECK: %[[CM_HANDLE:.*]] = load target("dx.RawBuffer", [3 x <2 x float>], 
0, 0), ptr %[[CM_HANDLE_PTR]], align 4
 // CHECK: call ptr @llvm.dx.resource.getpointer{{.*}}(target("dx.RawBuffer", 
[3 x <2 x float>], 0, 0) %[[CM_HANDLE]], i32 {{.*}})
@@ -62,3 +77,8 @@ void main() {
 // CHECK: %[[CM_DST_HANDLE_PTR:.*]] = getelementptr 
{{.*}}%"class.hlsl::RasterizerOrderedStructuredBuffer", ptr {{.*}}, i32 0, i32 0
 // CHECK: %[[CM_DST_HANDLE:.*]] = load target("dx.RawBuffer", [3 x <2 x 
float>], 1, 1), ptr %[[CM_DST_HANDLE_PTR]], align 4
 // CHECK: call ptr @llvm.dx.resource.getpointer{{.*}}(target("dx.RawBuffer", 
[3 x <2 x float>], 1, 1) %[[CM_DST_HANDLE]], i32 {{.*}})
+
+// CHECK-LABEL: define linkonce_odr hidden {{.*}} ptr 
@_ZNK4hlsl16StructuredBufferIu11matrix_typeILm2ELm3ELm1EfEEixEj
+// CHECK: %[[RM_SOURCE_HANDLE_PTR:.*]] = getelementptr 
{{.*}}%"class.hlsl::StructuredBuffer{{(\.0)?}}", ptr {{.*}}, i32 0, i32 0
+// CHECK: %[[RM_SOURCE_HANDLE:.*]] = load target("dx.RawBuffer", [2 x <3 x 
float>], 0, 0), ptr %[[RM_SOURCE_HANDLE_PTR]], align 4
+// CHECK: call ptr @llvm.dx.resource.getpointer{{.*}}(target("dx.RawBuffer", 
[2 x <3 x float>], 0, 0) %[[RM_SOURCE_HANDLE]], i32 {{.*}})
diff --git a/clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl 
b/clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl
index dfafa2b0b7e61..a259d915657ca 100644
--- a/clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl
+++ b/clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl
@@ -19,7 +19,7 @@
 export float subscript_rm(int row, int col, row_major float2x3 m) {
   return m[row][col];
 }
-// CHECK-LABEL: define {{.*}} float 
@_Z12subscript_rmiiu11matrix_typeILm2ELm3EfE
+// CHECK-LABEL: define {{.*}} float 
@_Z12subscript_rmiiu11matrix_typeILm2ELm3ELm1EfE
 // CHECK: [[ROW:%.*]] = load i32, ptr %row.addr
 // CHECK: [[COL:%.*]] = load i32, ptr %col.addr
 // CHECK: [[OFFSET:%.*]] = mul i32 [[ROW]], 3
@@ -32,7 +32,7 @@ export float subscript_rm(int row, int col, row_major 
float2x3 m) {
 export float subscript_cm(int row, int col, column_major float2x3 m) {
   return m[row][col];
 }
-// CHECK-LABEL: define {{.*}} float 
@_Z12subscript_cmiiu11matrix_typeILm2ELm3EfE
+// CHECK-LABEL: define {{.*}} float 
@_Z12subscript_cmiiu11matrix_typeILm2ELm3ELm2EfE
 // CHECK: [[ROW:%.*]] = load i32, ptr %row.addr
 // CHECK: [[COL:%.*]] = load i32, ptr %col.addr
 // CHECK: [[OFFSET:%.*]] = mul i32 [[COL]], 2
@@ -49,7 +49,7 @@ export float subscript_cm(int row, int col, column_major 
float2x3 m) {
 export float3 row_extract_rm(int row, row_major float2x3 m) {
   return m[row];
 }
-// CHECK-LABEL: define {{.*}} <3 x float> 
@_Z14row_extract_rmiu11matrix_typeILm2ELm3EfE
+// CHECK-LABEL: define {{.*}} <3 x float> 
@_Z14row_extract_rmiu11matrix_typeILm2ELm3ELm1EfE
 // CHECK: [[ROW:%.*]] = load i32, ptr %row.addr
 // CHECK: [[ROW_OFFSET0:%.*]] = mul i32 [[ROW]], 3
 // CHECK: add i32 [[ROW_OFFSET0]], 0
@@ -64,7 +64,7 @@ export float3 row_extract_rm(int row, row_major float2x3 m) {
 export float3 row_extract_cm(int row, column_major float2x3 m) {
   return m[row];
 }
-// CHECK-LABEL: define {{.*}} <3 x float> 
@_Z14row_extract_cmiu11matrix_typeILm2ELm3EfE
+// CHECK-LABEL: define {{.*}} <3 x float> 
@_Z14row_extract_cmiu11matrix_typeILm2ELm3ELm2EfE
 // CHECK: [[ROW:%.*]] = load i32, ptr %row.addr
 // CHECK: add i32 0, [[ROW]]
 // CHECK: add i32 2, [[ROW]]
@@ -75,15 +75,19 @@ export float3 row_extract_cm(int row, column_major float2x3 
m) {
 // before the column-major matrix.multiply intrinsic.
 // 
-----------------------------------------------------------------------------
 export float3 vec_mat_rm(float2 v, row_major float2x3 m) { return mul(v, m); }
-// CHECK-LABEL: define {{.*}} <3 x float> 
@_Z10vec_mat_rmDv2_fu11matrix_typeILm2ELm3EfE
-// CHECK: [[T:%.*]] = call {{.*}} <6 x float> @llvm.matrix.transpose.v6f32(<6 
x float> %{{.*}}, i32 3, i32 2)
+// CHECK-LABEL: define {{.*}} <3 x float> 
@_Z10vec_mat_rmDv2_fu11matrix_typeILm2ELm3ELm1EfE
+// COLMAJOR: [[T:%.*]] = shufflevector <6 x float> %{{.*}}, <6 x float> 
poison, <6 x i32> <i32 0, i32 3, i32 1, i32 4, i32 2, i32 5>
+// ROWMAJOR: [[S:%.*]] = shufflevector <6 x float> %{{.*}}, <6 x float> 
poison, <6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>
+// ROWMAJOR: [[T:%.*]] = call {{.*}} <6 x float> 
@llvm.matrix.transpose.v6f32(<6 x float> [[S]], i32 3, i32 2)
 // CHECK: call {{.*}} <3 x float> @llvm.matrix.multiply.v3f32.v2f32.v6f32(<2 x 
float> %{{.*}}, <6 x float> [[T]], i32 1, i32 2, i32 3)
 
 // Column-major operand: no transpose is inserted before matrix.multiply.
 export float3 vec_mat_cm(float2 v, column_major float2x3 m) { return mul(v, 
m); }
-// CHECK-LABEL: define {{.*}} <3 x float> 
@_Z10vec_mat_cmDv2_fu11matrix_typeILm2ELm3EfE
-// CHECK-NOT: @llvm.matrix.transpose
-// CHECK: call {{.*}} <3 x float> @llvm.matrix.multiply.v3f32.v2f32.v6f32(<2 x 
float> %{{.*}}, <6 x float> %{{.*}}, i32 1, i32 2, i32 3)
+// CHECK-LABEL: define {{.*}} <3 x float> 
@_Z10vec_mat_cmDv2_fu11matrix_typeILm2ELm3ELm2EfE
+// COLMAJOR: [[T:%.*]] = shufflevector <6 x float> %{{.*}}, <6 x float> 
poison, <6 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5>
+// ROWMAJOR: [[S:%.*]] = shufflevector <6 x float> %{{.*}}, <6 x float> 
poison, <6 x i32> <i32 0, i32 2, i32 4, i32 1, i32 3, i32 5>
+// ROWMAJOR: [[T:%.*]] = call {{.*}} <6 x float> 
@llvm.matrix.transpose.v6f32(<6 x float> [[S]], i32 3, i32 2)
+// CHECK: call {{.*}} <3 x float> @llvm.matrix.multiply.v3f32.v2f32.v6f32(<2 x 
float> %{{.*}}, <6 x float> [[T]], i32 1, i32 2, i32 3)
 
 // 
-----------------------------------------------------------------------------
 // __builtin_hlsl_mul (matrix * matrix): mixed per-decl layouts cause a
@@ -94,17 +98,29 @@ export float3 vec_mat_cm(float2 v, column_major float2x3 m) 
{ return mul(v, m);
 export float2x2 mat_mat_rm_cm(row_major float2x3 a, column_major float3x2 b) { 
return mul(a, b); }
 // CHECK-LABEL: define {{.*}} <4 x float> @_Z13mat_mat_rm_cm
 // CHECK: [[AMat:%.*]] = load <6 x float>, ptr %a.addr, align 4
+// COLMAJOR: [[A:%.*]] = shufflevector <6 x float> [[AMat]], {{.*}} <i32 0, 
i32 3, i32 1, i32 4, i32 2, i32 5>
+// ROWMAJOR: [[A:%.*]] = shufflevector <6 x float> [[AMat]], {{.*}} <i32 0, 
i32 1, i32 2, i32 3, i32 4, i32 5>
 // CHECK: [[BMat:%.*]] = load <6 x float>, ptr %b.addr, align 4
-// CHECK: [[T:%.*]] = call {{.*}} <6 x float> @llvm.matrix.transpose.v6f32(<6 
x float> [[AMat]], i32 3, i32 2)
-// CHECK: call {{.*}} <4 x float> @llvm.matrix.multiply.v4f32.v6f32.v6f32(<6 x 
float> [[T]], <6 x float> [[BMat]], i32 2, i32 3, i32 2)
+// COLMAJOR: [[B:%.*]] = shufflevector <6 x float> [[BMat]], {{.*}} <i32 0, 
i32 1, i32 2, i32 3, i32 4, i32 5>
+// COLMAJOR: call {{.*}} @llvm.matrix.multiply{{.*}}(<6 x float> [[A]], <6 x 
float> [[B]],
+// ROWMAJOR: [[B:%.*]] = shufflevector <6 x float> [[BMat]], {{.*}} <i32 0, 
i32 3, i32 1, i32 4, i32 2, i32 5>
+// ROWMAJOR: [[AT:%.*]] = call {{.*}} @llvm.matrix.transpose{{.*}}(<6 x float> 
[[A]], i32 3, i32 2)
+// ROWMAJOR: [[BT:%.*]] = call {{.*}} @llvm.matrix.transpose{{.*}}(<6 x float> 
[[B]], i32 2, i32 3)
+// ROWMAJOR: call {{.*}} @llvm.matrix.multiply{{.*}}(<6 x float> [[AT]], <6 x 
float> [[BT]],
 
 // LHS column-major, RHS row-major: only RHS is transposed.
 export float2x2 mat_mat_cm_rm(column_major float2x3 a, row_major float3x2 b) { 
return mul(a, b); }
 // CHECK-LABEL: define {{.*}} <4 x float> @_Z13mat_mat_cm_rm
 // CHECK: [[AMat:%.*]] = load <6 x float>, ptr %a.addr, align 4
+// COLMAJOR: [[A:%.*]] = shufflevector <6 x float> [[AMat]], {{.*}} <i32 0, 
i32 1, i32 2, i32 3, i32 4, i32 5>
+// ROWMAJOR: [[A:%.*]] = shufflevector <6 x float> [[AMat]], {{.*}} <i32 0, 
i32 2, i32 4, i32 1, i32 3, i32 5>
 // CHECK: [[BMat:%.*]] = load <6 x float>, ptr %b.addr, align 4
-// CHECK: [[T:%.*]] = call {{.*}} <6 x float> @llvm.matrix.transpose.v6f32(<6 
x float> [[BMat]], i32 2, i32 3)
-// CHECK: call {{.*}} <4 x float> @llvm.matrix.multiply.v4f32.v6f32.v6f32(<6 x 
float> [[AMat]], <6 x float> [[T]], i32 2, i32 3, i32 2)
+// COLMAJOR: [[B:%.*]] = shufflevector <6 x float> [[BMat]], {{.*}} <i32 0, 
i32 2, i32 4, i32 1, i32 3, i32 5>
+// COLMAJOR: call {{.*}} @llvm.matrix.multiply{{.*}}(<6 x float> [[A]], <6 x 
float> [[B]],
+// ROWMAJOR: [[B:%.*]] = shufflevector <6 x float> [[BMat]], {{.*}} <i32 0, 
i32 1, i32 2, i32 3, i32 4, i32 5>
+// ROWMAJOR: [[AT:%.*]] = call {{.*}} @llvm.matrix.transpose{{.*}}(<6 x float> 
[[A]], i32 3, i32 2)
+// ROWMAJOR: [[BT:%.*]] = call {{.*}} @llvm.matrix.transpose{{.*}}(<6 x float> 
[[B]], i32 2, i32 3)
+// ROWMAJOR: call {{.*}} @llvm.matrix.multiply{{.*}}(<6 x float> [[AT]], <6 x 
float> [[BT]],
 
 // Destination layout: the result is column-major, so no transpose is needed.
 export column_major float2x2 mat_mat_dst_cm(column_major float2x3 a, 
column_major float3x2 b) { return mul(a, b); }
@@ -121,42 +137,46 @@ export row_major float2x2 mat_mat_dst_rm(column_major 
float2x3 a, column_major f
 
 // Row-major source -> column-major destination: bits already transposed, 
no-op.
 export column_major float3x2 transpose_rm_to_cm(row_major float2x3 m) { return 
transpose(m); }
-// CHECK-LABEL: define {{.*}} <6 x float> 
@_Z18transpose_rm_to_cmu11matrix_typeILm2ELm3EfE
-// CHECK-NOT: @llvm.matrix.transpose
-// CHECK: ret <6 x float>
+// CHECK-LABEL: define {{.*}} <6 x float> 
@_Z18transpose_rm_to_cmu11matrix_typeILm2ELm3ELm1EfE
+// COLMAJOR: [[S:%.*]] = shufflevector <6 x float> %{{.*}}, {{.*}} <i32 0, i32 
3, i32 1, i32 4, i32 2, i32 5>
+// COLMAJOR: call {{.*}} @llvm.matrix.transpose{{.*}}(<6 x float> [[S]], i32 
2, i32 3)
+// ROWMAJOR-NOT: @llvm.matrix.transpose
+// ROWMAJOR: ret <6 x float>
 
 // Column-major source -> row-major destination: bits already transposed, 
no-op.
 export row_major float3x2 transpose_cm_to_rm(column_major float2x3 m) { return 
transpose(m); }
-// CHECK-LABEL: define {{.*}} <6 x float> 
@_Z18transpose_cm_to_rmu11matrix_typeILm2ELm3EfE
-// CHECK-NOT: @llvm.matrix.transpose
-// CHECK: ret <6 x float>
+// CHECK-LABEL: define {{.*}} <6 x float> 
@_Z18transpose_cm_to_rmu11matrix_typeILm2ELm3ELm2EfE
+// COLMAJOR-NOT: @llvm.matrix.transpose
+// COLMAJOR: ret <6 x float>
+// ROWMAJOR: [[S:%.*]] = shufflevector <6 x float> %{{.*}}, {{.*}} <i32 0, i32 
2, i32 4, i32 1, i32 3, i32 5>
+// ROWMAJOR: call {{.*}} @llvm.matrix.transpose{{.*}}(<6 x float> [[S]], i32 
3, i32 2)
 
 // Row-major source -> row-major destination: real transpose, dims swapped.
 export row_major float3x2 transpose_rm_to_rm(row_major float2x3 m) { return 
transpose(m); }
-// CHECK-LABEL: define {{.*}} <6 x float> 
@_Z18transpose_rm_to_rmu11matrix_typeILm2ELm3EfE
-// CHECK: call {{.*}} <6 x float> @llvm.matrix.transpose.v6f32(<6 x float> 
%{{.*}}, i32 3, i32 2)
+// CHECK-LABEL: define {{.*}} <6 x float> 
@_Z18transpose_rm_to_rmu11matrix_typeILm2ELm3ELm1EfE
+// COLMAJOR-NOT: @llvm.matrix.transpose
+// ROWMAJOR: call {{.*}} <6 x float> @llvm.matrix.transpose.v6f32(<6 x float> 
%{{.*}}, i32 3, i32 2)
 
 // Column-major source -> column-major destination: real transpose, natural 
dims.
 export column_major float3x2 transpose_cm_to_cm(column_major float2x3 m) { 
return transpose(m); }
-// CHECK-LABEL: define {{.*}} <6 x float> 
@_Z18transpose_cm_to_cmu11matrix_typeILm2ELm3EfE
-// CHECK: call {{.*}} <6 x float> @llvm.matrix.transpose.v6f32(<6 x float> 
%{{.*}}, i32 2, i32 3)
+// CHECK-LABEL: define {{.*}} <6 x float> 
@_Z18transpose_cm_to_cmu11matrix_typeILm2ELm3ELm2EfE
+// COLMAJOR: call {{.*}} <6 x float> @llvm.matrix.transpose.v6f32(<6 x float> 
%{{.*}}, i32 2, i32 3)
+// ROWMAJOR-NOT: @llvm.matrix.transpose
 
 // Default-layout return type: the TU `-fmatrix-memory-layout=` default 
 // flips between a real transpose and a no-op depending on the default.
 export float3x2 transpose_rm(row_major float2x3 m) { return transpose(m); }
-// CHECK-LABEL: define {{.*}} <6 x float> 
@_Z12transpose_rmu11matrix_typeILm2ELm3EfE
-// COLMAJOR-NOT: @llvm.matrix.transpose
-// COLMAJOR: ret <6 x float>
+// CHECK-LABEL: define {{.*}} <6 x float> 
@_Z12transpose_rmu11matrix_typeILm2ELm3ELm1EfE
+// COLMAJOR: call {{.*}} <6 x float> @llvm.matrix.transpose.v6f32(<6 x float> 
%{{.*}}, i32 2, i32 3)
 // ROWMAJOR: call {{.*}} <6 x float> @llvm.matrix.transpose.v6f32(<6 x float> 
%{{.*}}, i32 3, i32 2)
 
 
 // column-major default: src/dst match -> real transpose, natural dims.
 // row-major default: src/dst differ -> bits already transposed, no-op.
 export float3x2 transpose_cm(column_major float2x3 m) { return transpose(m); }
-// CHECK-LABEL: define {{.*}} <6 x float> 
@_Z12transpose_cmu11matrix_typeILm2ELm3EfE
+// CHECK-LABEL: define {{.*}} <6 x float> 
@_Z12transpose_cmu11matrix_typeILm2ELm3ELm2EfE
 // COLMAJOR: call {{.*}} <6 x float> @llvm.matrix.transpose.v6f32(<6 x float> 
%{{.*}}, i32 2, i32 3)
-// ROWMAJOR-NOT: @llvm.matrix.transpose
-// ROWMAJOR: ret <6 x float>
+// ROWMAJOR: call {{.*}} <6 x float> @llvm.matrix.transpose.v6f32(<6 x float> 
%{{.*}}, i32 3, i32 2)
 
 // 
-----------------------------------------------------------------------------
 // CK_HLSLMatrixTruncation: the shuffle mask that picks elements from the
@@ -170,12 +190,12 @@ typedef column_major float3x3 CM33;
 
 // Row-major source 3x2 -> row-major dest 2x2: flat row-major mask is 
{0,1,2,3}.
 export row_major float2x2 truncate_rm(row_major float3x2 m) { return (RM22)m; }
-// CHECK-LABEL: define {{.*}} <4 x float> 
@_Z11truncate_rmu11matrix_typeILm3ELm2EfE
+// CHECK-LABEL: define {{.*}} <4 x float> 
@_Z11truncate_rmu11matrix_typeILm3ELm2ELm1EfE
 // CHECK: shufflevector <6 x float> %{{.*}}, <6 x float> poison, <4 x i32> 
<i32 0, i32 1, i32 2, i32 3>
 
 // Column-major source 3x2 -> column-major dest 2x2: flat column-major mask is 
{0,1,3,4}.
 export column_major float2x2 truncate_cm(column_major float3x2 m) { return 
(CM22)m; }
-// CHECK-LABEL: define {{.*}} <4 x float> 
@_Z11truncate_cmu11matrix_typeILm3ELm2EfE
+// CHECK-LABEL: define {{.*}} <4 x float> 
@_Z11truncate_cmu11matrix_typeILm3ELm2ELm2EfE
 // CHECK: shufflevector <6 x float> %{{.*}}, <6 x float> poison, <4 x i32> 
<i32 0, i32 1, i32 3, i32 4>
 
 // 
-----------------------------------------------------------------------------
@@ -192,7 +212,7 @@ export column_major float2x2 truncate_cm(column_major 
float3x2 m) { return (CM22
 //   (1,0)->mask[1]=4  (1,1)->mask[4]=5  (1,2)->mask[7]=6
 //   (2,0)->mask[2]=8  (2,1)->mask[5]=9  (2,2)->mask[8]=10
 export column_major float3x3 truncate_rm_to_cm(row_major float3x4 m) { return 
(CM33)m; }
-// CHECK-LABEL: define {{.*}} <9 x float> 
@_Z17truncate_rm_to_cmu11matrix_typeILm3ELm4EfE
+// CHECK-LABEL: define {{.*}} <9 x float> 
@_Z17truncate_rm_to_cmu11matrix_typeILm3ELm4ELm1EfE
 // CHECK: shufflevector <12 x float> %{{.*}}, <12 x float> poison, <9 x i32> 
<i32 0, i32 4, i32 8, i32 1, i32 5, i32 9, i32 2, i32 6, i32 10>
 
 // Column-major src 3x4 -> row-major dst 3x3.
@@ -201,7 +221,7 @@ export column_major float3x3 truncate_rm_to_cm(row_major 
float3x4 m) { return (C
 //   (1,0)->mask[3]=1  (1,1)->mask[4]=4  (1,2)->mask[5]=7
 //   (2,0)->mask[6]=2  (2,1)->mask[7]=5  (2,2)->mask[8]=8
 export row_major float3x3 truncate_cm_to_rm(column_major float3x4 m) { return 
(RM33)m; }
-// CHECK-LABEL: define {{.*}} <9 x float> 
@_Z17truncate_cm_to_rmu11matrix_typeILm3ELm4EfE
+// CHECK-LABEL: define {{.*}} <9 x float> 
@_Z17truncate_cm_to_rmu11matrix_typeILm3ELm4ELm2EfE
 // CHECK: shufflevector <12 x float> %{{.*}}, <12 x float> poison, <9 x i32> 
<i32 0, i32 3, i32 6, i32 1, i32 4, i32 7, i32 2, i32 5, i32 8>
 
 // 
-----------------------------------------------------------------------------
diff --git a/clang/test/SemaHLSL/matrix_layout_attr.hlsl 
b/clang/test/SemaHLSL/matrix_layout_attr.hlsl
index 3e953f07557e6..a1a2176bc1dbf 100644
--- a/clang/test/SemaHLSL/matrix_layout_attr.hlsl
+++ b/clang/test/SemaHLSL/matrix_layout_attr.hlsl
@@ -44,6 +44,33 @@ column_major float4x4 Col2Row(row_major float4x4 M) {
 
 void bar(row_major float4x4 M, column_major float4x4 M2) {}
 
+void takes_default_layout(float2x2 M) {}
+
+float2x2 layout_conversions(row_major float2x2 RM,
+                            column_major float2x2 CM, bool SelectRM) {
+  takes_default_layout(RM);
+  takes_default_layout(CM);
+
+  float2x2 Result = RM;
+  Result = CM;
+  Result += RM;
+  Result -= CM;
+  Result *= RM;
+
+  float2x2 Sum = RM + CM;
+  float2x2 Difference = RM - CM;
+  float2x2 Product = RM * CM;
+  float2x2 MulProduct = mul(RM, CM);
+  return SelectRM ? RM : CM;
+}
+
+typedef float FLOAT;
+
+float2x2 common_matrix_element_sugar(row_major matrix<FLOAT, 2, 2> A,
+                                     row_major matrix<float, 2, 2> B) {
+  return A + B;
+}
+
 //Invalid: 
 // expected-error@+1 {{'row_major' attribute can only be applied to a matrix 
type}}
 void foo(column_major float4x4 mat, row_major int i) {}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to