[clang] [Matrix][HLSL] Add codgen support for Matrix Layout keywords (PR #198887)
Farzon Lotfi via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 2 07:38:08 PDT 2026
https://github.com/farzonl updated https://github.com/llvm/llvm-project/pull/198887
>From 656f637d6da026c6fc0491ef0bcfc2b3c644a5fb Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzonlotfi at microsoft.com>
Date: Mon, 4 May 2026 12:10:08 -0400
Subject: [PATCH 1/8] [Matrix][HLSL] Add codgen support for Matrix Layout
keywords
fixes #192262
- Wrap Matrix Type in HLSLMatrixLayoutArrr
- Add Helper to know which Matrix Layout to apply in codegen or check for in Sema
---
clang/include/clang/AST/MatrixUtils.h | 71 +++++++
clang/include/clang/Basic/Attr.td | 16 ++
clang/lib/AST/TypePrinter.cpp | 20 ++
clang/lib/CodeGen/CGExpr.cpp | 17 +-
clang/lib/CodeGen/CGExprConstant.cpp | 4 +-
clang/lib/CodeGen/CGExprScalar.cpp | 22 ++-
clang/lib/CodeGen/CGHLSLBuiltins.cpp | 9 +-
clang/lib/CodeGen/CodeGenTypes.cpp | 5 +-
clang/lib/Sema/SemaHLSL.cpp | 79 ++++++++
clang/test/AST/HLSL/matrix_layout_attr.hlsl | 11 ++
.../matrix-layout-attr-overrides-default.hlsl | 175 ++++++++++++++++++
11 files changed, 400 insertions(+), 29 deletions(-)
create mode 100644 clang/include/clang/AST/MatrixUtils.h
create mode 100644 clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl
diff --git a/clang/include/clang/AST/MatrixUtils.h b/clang/include/clang/AST/MatrixUtils.h
new file mode 100644
index 0000000000000..d1b351bf4b07b
--- /dev/null
+++ b/clang/include/clang/AST/MatrixUtils.h
@@ -0,0 +1,71 @@
+//===- MatrixUtils.h - Matrix AST utilities ---------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file
+/// Defines AST-level helper utilities for matrix types.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_MATRIXUTILS_H
+#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.
+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();
+ }
+ }
+ return LangOpts.getDefaultMatrixMemoryLayout() ==
+ LangOptions::MatrixMemoryLayout::MatrixRowMajor;
+}
+
+/// Returns true if matrices of \p T should be laid out in column-major order.
+/// Mirrors `isMatrixRowMajor`; per-decl HLSL attributes win over the
+/// `-fmatrix-memory-layout=` default.
+inline bool isMatrixColumnMajor(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::HLSLColumnMajor:
+ return true;
+ case attr::HLSLRowMajor:
+ return false;
+ default:
+ break;
+ }
+ Cur = AT->getModifiedType();
+ }
+ }
+ return LangOpts.getDefaultMatrixMemoryLayout() ==
+ LangOptions::MatrixMemoryLayout::MatrixColMajor;
+}
+} // namespace clang
+
+#endif // LLVM_CLANG_AST_MATRIXUTILS_H
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 32f72e8da960e..accb3ca3c4ce7 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5299,6 +5299,22 @@ def HLSLMatrixLayout : InheritableAttr {
let Documentation = [HLSLMatrixLayoutDocs];
}
+// Internal type-attribute markers attached to matrix QualTypes by Sema when
+// a decl carries `[[hlsl::row_major]]` / `[[hlsl::column_major]]`. They let
+// CodeGen / const-eval recover the layout from any matrix-typed expression
+// without re-walking back to the source decl.
+def HLSLRowMajor : TypeAttr {
+ let Spellings = [];
+ let LangOpts = [HLSL];
+ let Documentation = [InternalOnly];
+}
+
+def HLSLColumnMajor : TypeAttr {
+ let Spellings = [];
+ let LangOpts = [HLSL];
+ let Documentation = [InternalOnly];
+}
+
def RandomizeLayout : InheritableAttr {
let Spellings = [GCC<"randomize_layout">];
let Subjects = SubjectList<[Record]>;
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 80f5b90ba35c4..7a5aac8bd5cd4 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -295,6 +295,13 @@ bool TypePrinter::canPrefixQualifiers(const Type *T,
// We still want to print the address_space before the type if it is an
// address_space attribute.
const auto *AttrTy = cast<AttributedType>(UnderlyingType);
+ // HLSLRowMajor / HLSLColumnMajor are internal markers with no spelling;
+ // the printer skips them entirely, so look through them when deciding
+ // qualifier placement.
+ if (AttrTy->getAttrKind() == attr::HLSLRowMajor ||
+ AttrTy->getAttrKind() == attr::HLSLColumnMajor)
+ return canPrefixQualifiers(AttrTy->getModifiedType().getTypePtr(),
+ NeedARCStrongQualifier);
CanPrefixQualifiers = AttrTy->getAttrKind() == attr::AddressSpace;
break;
}
@@ -1915,6 +1922,15 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
if (T->getAttrKind() == attr::ObjCInertUnsafeUnretained)
return;
+ // HLSLRowMajor / HLSLColumnMajor are internal markers attached to matrix
+ // types by Sema to record an explicit `[[hlsl::row_major]]` /
+ // `[[hlsl::column_major]]` qualifier from the source decl. They have no
+ // user-visible spelling; the user-facing form is the original keyword on
+ // the decl, not on the type.
+ if (T->getAttrKind() == attr::HLSLRowMajor ||
+ T->getAttrKind() == attr::HLSLColumnMajor)
+ return;
+
// Don't print ns_returns_retained unless it had an effect.
if (T->getAttrKind() == attr::NSReturnsRetained &&
!T->getEquivalentType()->castAs<FunctionType>()
@@ -1992,6 +2008,10 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::HLSLResourceDimension:
llvm_unreachable("HLSL resource type attributes handled separately");
+ case attr::HLSLRowMajor:
+ case attr::HLSLColumnMajor:
+ llvm_unreachable("HLSL matrix layout type attributes handled separately");
+
case attr::OpenCLPrivateAddressSpace:
case attr::OpenCLGlobalAddressSpace:
case attr::OpenCLGlobalDeviceAddressSpace:
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 325902f2127bc..52853719a4d00 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -31,6 +31,7 @@
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
#include "clang/AST/InferAlloc.h"
+#include "clang/AST/MatrixUtils.h"
#include "clang/AST/NSAPI.h"
#include "clang/AST/ParentMapContext.h"
#include "clang/AST/StmtVisitor.h"
@@ -2360,8 +2361,7 @@ LValue CodeGenFunction::EmitMatrixElementExpr(const MatrixElementExpr *E) {
// getEncodedElementAccess returns row-major linearized indices
// If the matrix memory layout is column-major, convert indices
// to column-major indices.
- bool IsColMajor = getLangOpts().getDefaultMatrixMemoryLayout() ==
- LangOptions::MatrixMemoryLayout::MatrixColMajor;
+ bool IsColMajor = isMatrixColumnMajor(getLangOpts(), E->getBase()->getType());
if (IsColMajor) {
const auto *MT = E->getBase()->getType()->castAs<ConstantMatrixType>();
unsigned NumCols = MT->getNumColumns();
@@ -2617,8 +2617,7 @@ RValue CodeGenFunction::EmitLoadOfLValue(LValue LV, SourceLocation Loc) {
ColIdx = ColConstsIndices->getAggregateElement(Col);
else
ColIdx = llvm::ConstantInt::get(Row->getType(), Col);
- bool IsMatrixRowMajor = getLangOpts().getDefaultMatrixMemoryLayout() ==
- LangOptions::MatrixMemoryLayout::MatrixRowMajor;
+ bool IsMatrixRowMajor = isMatrixRowMajor(getLangOpts(), MatTy);
llvm::Value *EltIndex =
MB.CreateIndex(Row, ColIdx, NumRows, NumCols, IsMatrixRowMajor);
llvm::Value *Elt = Builder.CreateExtractElement(MatrixVec, EltIndex);
@@ -2936,8 +2935,7 @@ void CodeGenFunction::EmitStoreThroughLValue(RValue Src, LValue Dst,
ColIdx = ColConstsIndices->getAggregateElement(Col);
else
ColIdx = llvm::ConstantInt::get(Row->getType(), Col);
- bool IsMatrixRowMajor = getLangOpts().getDefaultMatrixMemoryLayout() ==
- LangOptions::MatrixMemoryLayout::MatrixRowMajor;
+ bool IsMatrixRowMajor = isMatrixRowMajor(getLangOpts(), Dst.getType());
llvm::Value *EltIndex =
MB.CreateIndex(Row, ColIdx, NumRows, NumCols, IsMatrixRowMajor);
llvm::Value *Lane = llvm::ConstantInt::get(Builder.getInt32Ty(), Col);
@@ -5229,8 +5227,8 @@ LValue CodeGenFunction::EmitMatrixSubscriptExpr(const MatrixSubscriptExpr *E) {
const auto *MatrixTy = E->getBase()->getType()->castAs<ConstantMatrixType>();
unsigned NumCols = MatrixTy->getNumColumns();
unsigned NumRows = MatrixTy->getNumRows();
- bool IsMatrixRowMajor = getLangOpts().getDefaultMatrixMemoryLayout() ==
- LangOptions::MatrixMemoryLayout::MatrixRowMajor;
+ bool IsMatrixRowMajor =
+ isMatrixRowMajor(getLangOpts(), E->getBase()->getType());
llvm::Value *FinalIdx =
MB.CreateIndex(RowIdx, ColIdx, NumRows, NumCols, IsMatrixRowMajor);
@@ -7453,8 +7451,7 @@ void CodeGenFunction::FlattenAccessAndTypeLValue(
Address MatAddr = MaybeConvertMatrixAddress(Base.getAddress(), *this);
unsigned NumRows = MT->getNumRows();
unsigned NumCols = MT->getNumColumns();
- bool IsMatrixRowMajor = getLangOpts().getDefaultMatrixMemoryLayout() ==
- LangOptions::MatrixMemoryLayout::MatrixRowMajor;
+ bool IsMatrixRowMajor = isMatrixRowMajor(getLangOpts(), T);
llvm::MatrixBuilder MB(Builder);
for (unsigned Row = 0; Row < MT->getNumRows(); Row++) {
for (unsigned Col = 0; Col < MT->getNumColumns(); Col++) {
diff --git a/clang/lib/CodeGen/CGExprConstant.cpp b/clang/lib/CodeGen/CGExprConstant.cpp
index 17a10dd40eba2..ffcd3fef9cd52 100644
--- a/clang/lib/CodeGen/CGExprConstant.cpp
+++ b/clang/lib/CodeGen/CGExprConstant.cpp
@@ -21,6 +21,7 @@
#include "clang/AST/APValue.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Attr.h"
+#include "clang/AST/MatrixUtils.h"
#include "clang/AST/NSAPI.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/StmtVisitor.h"
@@ -2645,8 +2646,7 @@ ConstantEmitter::tryEmitPrivate(const APValue &Value, QualType DestType,
unsigned NumElts = NumRows * NumCols;
SmallVector<llvm::Constant *, 16> Inits(NumElts);
- bool IsRowMajor = CGM.getLangOpts().getDefaultMatrixMemoryLayout() ==
- LangOptions::MatrixMemoryLayout::MatrixRowMajor;
+ bool IsRowMajor = isMatrixRowMajor(CGM.getLangOpts(), DestType);
for (unsigned Row = 0; Row != NumRows; ++Row) {
for (unsigned Col = 0; Col != NumCols; ++Col) {
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index c8a8ec7b6d928..ddd28cfe5372c 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -26,6 +26,7 @@
#include "clang/AST/Attr.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/MatrixUtils.h"
#include "clang/AST/ParentMapContext.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/StmtVisitor.h"
@@ -2247,8 +2248,8 @@ Value *ScalarExprEmitter::VisitMatrixSingleSubscriptExpr(
for (unsigned Col = 0; Col != NumColumns; ++Col) {
Value *ColVal = llvm::ConstantInt::get(RowIdx->getType(), Col);
- bool IsMatrixRowMajor = CGF.getLangOpts().getDefaultMatrixMemoryLayout() ==
- LangOptions::MatrixMemoryLayout::MatrixRowMajor;
+ bool IsMatrixRowMajor =
+ isMatrixRowMajor(CGF.getLangOpts(), E->getBase()->getType());
Value *EltIdx = MB.CreateIndex(RowIdx, ColVal, NumRows, NumColumns,
IsMatrixRowMajor, "matrix_row_idx");
Value *Elt =
@@ -2274,8 +2275,8 @@ Value *ScalarExprEmitter::VisitMatrixSubscriptExpr(MatrixSubscriptExpr *E) {
Value *Idx;
unsigned NumCols = MatrixTy->getNumColumns();
unsigned NumRows = MatrixTy->getNumRows();
- bool IsMatrixRowMajor = CGF.getLangOpts().getDefaultMatrixMemoryLayout() ==
- LangOptions::MatrixMemoryLayout::MatrixRowMajor;
+ bool IsMatrixRowMajor =
+ isMatrixRowMajor(CGF.getLangOpts(), E->getBase()->getType());
Idx = MB.CreateIndex(RowIdx, ColumnIdx, NumRows, NumCols, IsMatrixRowMajor);
if (CGF.CGM.getCodeGenOpts().OptimizationLevel > 0)
@@ -2360,8 +2361,7 @@ Value *ScalarExprEmitter::VisitInitListExpr(InitListExpr *E) {
// column-major positions rather than inserting sequentially and shuffling.
const ConstantMatrixType *ColMajorMT = nullptr;
if (const auto *MT = E->getType()->getAs<ConstantMatrixType>();
- MT && CGF.getLangOpts().getDefaultMatrixMemoryLayout() ==
- LangOptions::MatrixMemoryLayout::MatrixColMajor)
+ MT && isMatrixColumnMajor(CGF.getLangOpts(), E->getType()))
ColMajorMT = MT;
// Loop over initializers collecting the Value for each, and remembering
@@ -2600,8 +2600,7 @@ static Value *EmitHLSLElementwiseCast(CodeGenFunction &CGF, LValue SrcVal,
"Flattened type on RHS must have the same number or more elements "
"than vector on LHS.");
- bool IsRowMajor = CGF.getLangOpts().getDefaultMatrixMemoryLayout() ==
- LangOptions::MatrixMemoryLayout::MatrixRowMajor;
+ bool IsRowMajor = isMatrixRowMajor(CGF.getLangOpts(), DestTy);
llvm::Value *V = CGF.Builder.CreateLoad(
CGF.CreateIRTempWithoutCast(DestTy, "flatcast.tmp"));
@@ -3180,8 +3179,11 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
assert(SrcMatTy && "Source type must be a matrix type.");
assert(NumRows <= SrcMatTy->getNumRows());
assert(NumCols <= SrcMatTy->getNumColumns());
- bool IsRowMajor = CGF.getLangOpts().getDefaultMatrixMemoryLayout() ==
- LangOptions::MatrixMemoryLayout::MatrixRowMajor;
+
+ // isMatrixRowMajor needs the full sugared QualType to find matrix layout
+ // attrs. So use Use E->getType() (the source QualType) rather than
+ // SrcMatTy b\c getAs<ConstantMatrixType>() strips the sugar.
+ bool IsRowMajor = isMatrixRowMajor(CGF.getLangOpts(), E->getType());
for (unsigned R = 0; R < NumRows; R++)
for (unsigned C = 0; C < NumCols; C++)
Mask[MatTy->getFlattenedIndex(R, C, IsRowMajor)] =
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index bf599e2d77aa8..ace00a0e4692e 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -13,6 +13,7 @@
#include "CGBuiltin.h"
#include "CGHLSLRuntime.h"
#include "CodeGenFunction.h"
+#include "clang/AST/MatrixUtils.h"
#include "llvm/IR/MatrixBuilder.h"
using namespace clang;
@@ -1206,8 +1207,9 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
// The matrix multiply intrinsic only operates on column-major order
// matrices. Therefore matrix memory layout transforms must be inserted
// before and after matrix multiply intrinsics.
- bool IsRowMajor = getLangOpts().getDefaultMatrixMemoryLayout() ==
- LangOptions::MatrixMemoryLayout::MatrixRowMajor;
+ // Use whichever operand is a matrix to discover its declared layout.
+ QualType MatTy = IsMat0 ? QTy0 : QTy1;
+ bool IsRowMajor = isMatrixRowMajor(getLangOpts(), MatTy);
llvm::MatrixBuilder MB(Builder);
if (IsVec0 && IsMat1) {
@@ -1259,8 +1261,7 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
// For row-major, a row-major RxC matrix is equivalent to a column-major
// CxR matrix, so transposing with swapped dimensions produces the correct
// row-major CxR result directly.
- bool IsRowMajor = getLangOpts().getDefaultMatrixMemoryLayout() ==
- LangOptions::MatrixMemoryLayout::MatrixRowMajor;
+ bool IsRowMajor = isMatrixRowMajor(getLangOpts(), E->getArg(0)->getType());
if (IsRowMajor)
return MB.CreateMatrixTranspose(Op0, Cols, Rows);
return MB.CreateMatrixTranspose(Op0, Rows, Cols);
diff --git a/clang/lib/CodeGen/CodeGenTypes.cpp b/clang/lib/CodeGen/CodeGenTypes.cpp
index e76b1e8608d33..b28a0eb82f302 100644
--- a/clang/lib/CodeGen/CodeGenTypes.cpp
+++ b/clang/lib/CodeGen/CodeGenTypes.cpp
@@ -22,6 +22,7 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/MatrixUtils.h"
#include "clang/AST/RecordLayout.h"
#include "clang/CodeGen/CGFunctionInfo.h"
#include "llvm/IR/DataLayout.h"
@@ -111,9 +112,7 @@ llvm::Type *CodeGenTypes::ConvertTypeForMem(QualType T) {
unsigned NumRows = MT->getNumRows();
unsigned NumCols = MT->getNumColumns();
- bool IsRowMajor =
- CGM.getContext().getLangOpts().getDefaultMatrixMemoryLayout() ==
- LangOptions::MatrixMemoryLayout::MatrixRowMajor;
+ bool IsRowMajor = isMatrixRowMajor(Context.getLangOpts(), T);
unsigned VecLen = IsRowMajor ? NumCols : NumRows;
unsigned ArrayLen = IsRowMajor ? NumRows : NumCols;
llvm::Type *VecTy = llvm::FixedVectorType::get(IRElemTy, VecLen);
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 04148fcd008d5..cc613d48add42 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2704,6 +2704,35 @@ static bool isMatrixOrArrayOfMatrix(const ASTContext &Ctx, QualType QT) {
return Ty->isDependentType() || Ty->isConstantMatrixType();
}
+/// Embeds the row/column-major layout ( \p AttrK ) directly into the
+/// matrix QualType of \p T, preserving any surrounding type sugar.
+static QualType wrapMatrixWithLayoutAttr(ASTContext &C, QualType T,
+ attr::Kind AttrK) {
+ if (T.isNull() || T->isDependentType())
+ return T;
+
+ if (const auto *AT = dyn_cast<ArrayType>(T.getTypePtr())) {
+ QualType Inner = wrapMatrixWithLayoutAttr(C, AT->getElementType(), AttrK);
+ if (Inner == AT->getElementType())
+ return T;
+
+ // fallthrough Inner != AT->getElementType()
+ if (const auto *CAT = dyn_cast<ConstantArrayType>(T.getTypePtr()))
+ return C.getConstantArrayType(Inner, CAT->getSize(), CAT->getSizeExpr(),
+ CAT->getSizeModifier(),
+ CAT->getIndexTypeCVRQualifiers());
+ // Note The IncompleteArrayType case would handle something like row_major
+ // float2x3 arr[] but HLSL doesn't support this syntax. so we can't test it.
+ // consider removing.
+ if (const auto *IAT = dyn_cast<IncompleteArrayType>(T.getTypePtr()))
+ return C.getIncompleteArrayType(Inner, IAT->getSizeModifier(),
+ IAT->getIndexTypeCVRQualifiers());
+ }
+ if (T->isConstantMatrixType())
+ return C.getAttributedType(AttrK, T, T);
+ return T;
+}
+
static bool diagnoseMatrixLayoutOnNonMatrix(Sema &SemaRef, Decl *D,
SourceLocation Loc,
const IdentifierInfo *AttrName) {
@@ -2731,6 +2760,53 @@ static bool diagnoseMatrixLayoutOnNonMatrix(Sema &SemaRef, Decl *D,
return true;
}
+/// Embeds layout attributes into the matrix type so no decl lookups are
+/// needed.
+static void applyHLSLMatrixLayoutTypeAttr(ASTContext &Ctx, Decl *D,
+ const ParsedAttr &AL) {
+ attr::Kind AttrK =
+ AL.getSemanticSpelling() == HLSLMatrixLayoutAttr::Keyword_row_major
+ ? attr::HLSLRowMajor
+ : attr::HLSLColumnMajor;
+
+ auto *TD = dyn_cast<TypedefNameDecl>(D);
+ auto *VD = dyn_cast<ValueDecl>(D);
+
+ if (!TD && !VD)
+ return;
+
+ if (auto *FD = dyn_cast<FunctionDecl>(D)) {
+ const auto *FPT = FD->getType()->getAs<FunctionProtoType>();
+ if (!FPT)
+ return;
+
+ QualType NewRet =
+ wrapMatrixWithLayoutAttr(Ctx, FPT->getReturnType(), AttrK);
+ if (NewRet == FPT->getReturnType())
+ return;
+
+ FD->setType(Ctx.getFunctionType(NewRet, FPT->getParamTypes(),
+ FPT->getExtProtoInfo()));
+ return;
+ }
+
+ if (VD) {
+ QualType OldT = VD->getType();
+ QualType NewT = wrapMatrixWithLayoutAttr(Ctx, OldT, AttrK);
+ if (NewT != OldT)
+ VD->setType(NewT);
+ return;
+ }
+
+ if (TD) {
+ QualType OldT = TD->getUnderlyingType();
+ QualType NewT = wrapMatrixWithLayoutAttr(Ctx, OldT, AttrK);
+ if (NewT != OldT)
+ TD->setModedTypeSourceInfo(TD->getTypeSourceInfo(), NewT);
+ return;
+ }
+}
+
void SemaHLSL::handleMatrixLayoutAttr(Decl *D, const ParsedAttr &AL) {
// row_major and column_major are only valid on matrix types.
if (diagnoseMatrixLayoutOnNonMatrix(SemaRef, D, AL.getLoc(),
@@ -2752,6 +2828,9 @@ void SemaHLSL::handleMatrixLayoutAttr(Decl *D, const ParsedAttr &AL) {
}
D->addAttr(::new (getASTContext()) HLSLMatrixLayoutAttr(getASTContext(), AL));
+
+ ASTContext &Ctx = getASTContext();
+ applyHLSLMatrixLayoutTypeAttr(Ctx, D, AL);
}
bool SemaHLSL::diagnoseInstantiatedMatrixLayoutAttr(
diff --git a/clang/test/AST/HLSL/matrix_layout_attr.hlsl b/clang/test/AST/HLSL/matrix_layout_attr.hlsl
index f1c24a3b5a954..98bcb9976c58c 100644
--- a/clang/test/AST/HLSL/matrix_layout_attr.hlsl
+++ b/clang/test/AST/HLSL/matrix_layout_attr.hlsl
@@ -19,6 +19,17 @@ struct S {
column_major float3x3 m2;
};
+
+// Check to make sure the internal type-sugar marker is invisible in the
+//printed type (it has no spelling), but the `AttributedType` node and
+// the `HLSLMatrixLayoutAttr` child still appear in the AST dump.
+
// CHECK: TypedefDecl {{.*}} RM44 'float4x4'
+// CHECK: AttributedType {{.*}} 'float4x4' sugar
// CHECK: HLSLMatrixLayoutAttr {{.*}} row_major
typedef row_major float4x4 RM44;
+
+// CHECK-LABEL: TypedefDecl {{.*}} CM44 'float4x4'
+// CHECK: AttributedType {{.*}} 'float4x4' sugar
+// CHECK: HLSLMatrixLayoutAttr {{.*}} column_major
+typedef column_major float4x4 CM44;
diff --git a/clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl b/clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl
new file mode 100644
index 0000000000000..8320807e1cd89
--- /dev/null
+++ b/clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl
@@ -0,0 +1,175 @@
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -x hlsl -triple \
+// RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \
+// RUN: -fmatrix-memory-layout=column-major -o - | FileCheck %s
+// RUN: %clang_cc1 -std=hlsl202x -finclude-default-header -x hlsl -triple \
+// RUN: dxil-pc-shadermodel6.3-library %s -emit-llvm -disable-llvm-passes \
+// RUN: -fmatrix-memory-layout=row-major -o - | FileCheck %s
+
+// Verifies that a per-decl `[[hlsl::row_major]]` / `[[hlsl::column_major]]`
+// (spelled `row_major` / `column_major` in HLSL) overrides the
+// `-fmatrix-memory-layout=` default at every CodeGen lowering site:
+//
+// * `MatrixSubscriptExpr` index computation
+// * `MatrixSingleSubscriptExpr` row extraction
+// * `__builtin_hlsl_mul` matrix-multiply transpose insertion
+// * `__builtin_hlsl_transpose` row/col dimension swap
+// * `CK_HLSLMatrixTruncation` shuffle mask
+//
+// The decl-level attribute should win regardless of the TU default.
+
+// -----------------------------------------------------------------------------
+// MatrixSubscriptExpr indexing: row-major attr -> Row*NumCols + Col
+// -----------------------------------------------------------------------------
+export float subscript_rm(int row, int col, row_major float2x3 m) {
+ return m[row][col];
+}
+// CHECK-LABEL: define {{.*}} float @_Z12subscript_rmiiu11matrix_typeILm2ELm3EfE
+// CHECK: [[ROW:%.*]] = load i32, ptr %row.addr
+// CHECK: [[COL:%.*]] = load i32, ptr %col.addr
+// CHECK: [[OFFSET:%.*]] = mul i32 [[ROW]], 3
+// CHECK: [[IDX:%.*]] = add i32 [[OFFSET]], [[COL]]
+// CHECK: extractelement <6 x float> %{{.*}}, i32 [[IDX]]
+
+// -----------------------------------------------------------------------------
+// MatrixSubscriptExpr indexing: column-major attr -> Col*NumRows + Row
+// -----------------------------------------------------------------------------
+export float subscript_cm(int row, int col, column_major float2x3 m) {
+ return m[row][col];
+}
+// CHECK-LABEL: define {{.*}} float @_Z12subscript_cmiiu11matrix_typeILm2ELm3EfE
+// CHECK: [[ROW:%.*]] = load i32, ptr %row.addr
+// CHECK: [[COL:%.*]] = load i32, ptr %col.addr
+// CHECK: [[OFFSET:%.*]] = mul i32 [[COL]], 2
+// CHECK: [[IDX:%.*]] = add i32 [[OFFSET]], [[ROW]]
+// CHECK: extractelement <6 x float> %{{.*}}, i32 [[IDX]]
+
+// -----------------------------------------------------------------------------
+// MatrixSingleSubscriptExpr (row extraction): attribute selects the per-element
+// index formula even when the TU default disagrees.
+// -----------------------------------------------------------------------------
+
+// Row-major: per-column element index is Row*NumCols + Col, materialized as a
+// constant-zero / constant-one / constant-two add to (Row*3).
+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: [[ROW:%.*]] = load i32, ptr %row.addr
+// CHECK: [[ROW_OFFSET0:%.*]] = mul i32 [[ROW]], 3
+// CHECK: add i32 [[ROW_OFFSET0]], 0
+// CHECK: [[ROW_OFFSET1:%.*]] = mul i32 [[ROW]], 3
+// CHECK: add i32 [[ROW_OFFSET1]], 1
+// CHECK: [[ROW_OFFSET2:%.*]] = mul i32 [[ROW]], 3
+// CHECK: add i32 [[ROW_OFFSET2]], 2
+
+// Column-major: per-column element index is Col*NumRows + Row, so we *don't*
+// see the Row*NumCols multiply; instead each column folds the constant
+// Col*NumRows into the GEP, leaving just an add of Row.
+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: [[ROW:%.*]] = load i32, ptr %row.addr
+// CHECK: add i32 0, [[ROW]]
+// CHECK: add i32 2, [[ROW]]
+// CHECK: add i32 4, [[ROW]]
+
+// -----------------------------------------------------------------------------
+// __builtin_hlsl_mul (vector * matrix): row-major operand triggers a transpose
+// 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: 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)
+
+// -----------------------------------------------------------------------------
+// __builtin_hlsl_transpose: row-major operand swaps Rows/Cols passed to the
+// underlying intrinsic.
+// -----------------------------------------------------------------------------
+
+// Row-major float2x3 transposed: passes (Cols=3, Rows=2) to the intrinsic.
+export float3x2 transpose_rm(row_major float2x3 m) { return transpose(m); }
+// CHECK-LABEL: define {{.*}} <6 x float> @_Z12transpose_rmu11matrix_typeILm2ELm3EfE
+// CHECK: call {{.*}} <6 x float> @llvm.matrix.transpose.v6f32(<6 x float> %{{.*}}, i32 3, i32 2)
+
+// Column-major float2x3 transposed: passes (Rows=2, Cols=3) to the intrinsic.
+export float3x2 transpose_cm(column_major float2x3 m) { return transpose(m); }
+// CHECK-LABEL: define {{.*}} <6 x float> @_Z12transpose_cmu11matrix_typeILm2ELm3EfE
+// CHECK: call {{.*}} <6 x float> @llvm.matrix.transpose.v6f32(<6 x float> %{{.*}}, i32 2, i32 3)
+
+// -----------------------------------------------------------------------------
+// CK_HLSLMatrixTruncation: the shuffle mask that picks elements from the
+// source matrix uses the operand's per-decl layout to flatten indices.
+// -----------------------------------------------------------------------------
+
+// Row-major source 3x2 -> row-major dest 2x2: flat row-major mask is {0,1,2,3}.
+export float2x2 truncate_rm(row_major float3x2 m) { return (float2x2)m; }
+// CHECK-LABEL: define {{.*}} <4 x float> @_Z11truncate_rmu11matrix_typeILm3ELm2EfE
+// 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 float2x2 truncate_cm(column_major float3x2 m) { return (float2x2)m; }
+// CHECK-LABEL: define {{.*}} <4 x float> @_Z11truncate_cmu11matrix_typeILm3ELm2EfE
+// CHECK: shufflevector <6 x float> %{{.*}}, <6 x float> poison, <4 x i32> <i32 0, i32 1, i32 3, i32 4>
+
+// -----------------------------------------------------------------------------
+// Array of matrix: the per-decl layout attribute propagates through
+// ConstantArrayType sugar via wrapMatrixWithLayoutAttr, so indexing into an
+// array element still uses the correct layout.
+// -----------------------------------------------------------------------------
+
+// Row-major array element subscript: Row*NumCols + Col
+export float arr_subscript_rm(int row, int col, row_major float2x3 arr[2]) {
+ return arr[1][row][col];
+}
+// CHECK-LABEL: define {{.*}} float @_Z16arr_subscript_rm
+// CHECK: [[ROW:%.*]] = load i32, ptr %row.addr
+// CHECK: [[COL:%.*]] = load i32, ptr %col.addr
+// CHECK: [[OFFSET:%.*]] = mul i32 [[ROW]], 3
+// CHECK: [[IDX:%.*]] = add i32 [[OFFSET]], [[COL]]
+// CHECK: extractelement <6 x float> %{{.*}}, i32 [[IDX]]
+
+// Column-major array element subscript: Col*NumRows + Row
+export float arr_subscript_cm(int row, int col, column_major float2x3 arr[2]) {
+ return arr[1][row][col];
+}
+// CHECK-LABEL: define {{.*}} float @_Z16arr_subscript_cm
+// CHECK: [[ROW:%.*]] = load i32, ptr %row.addr
+// CHECK: [[COL:%.*]] = load i32, ptr %col.addr
+// CHECK: [[OFFSET:%.*]] = mul i32 [[COL]], 2
+// CHECK: [[IDX:%.*]] = add i32 [[OFFSET]], [[ROW]]
+// CHECK: extractelement <6 x float> %{{.*}}, i32 [[IDX]]
+
+// -----------------------------------------------------------------------------
+// Multi-dimensional array of matrix: wrapMatrixWithLayoutAttr recurses
+// through nested ConstantArrayType layers.
+// -----------------------------------------------------------------------------
+
+// Row-major 2D array element subscript: Row*NumCols + Col
+export float arr2d_subscript_rm(int row, int col, row_major float2x3 arr[2][3]) {
+ return arr[0][1][row][col];
+}
+// CHECK-LABEL: define {{.*}} float @_Z18arr2d_subscript_rm
+// CHECK: [[ROW:%.*]] = load i32, ptr %row.addr
+// CHECK: [[COL:%.*]] = load i32, ptr %col.addr
+// CHECK: [[OFFSET:%.*]] = mul i32 [[ROW]], 3
+// CHECK: [[IDX:%.*]] = add i32 [[OFFSET]], [[COL]]
+// CHECK: extractelement <6 x float> %{{.*}}, i32 [[IDX]]
+
+// Column-major 2D array element subscript: Col*NumRows + Row
+export float arr2d_subscript_cm(int row, int col, column_major float2x3 arr[2][3]) {
+ return arr[0][1][row][col];
+}
+// CHECK-LABEL: define {{.*}} float @_Z18arr2d_subscript_cm
+// CHECK: [[ROW:%.*]] = load i32, ptr %row.addr
+// CHECK: [[COL:%.*]] = load i32, ptr %col.addr
+// CHECK: [[OFFSET:%.*]] = mul i32 [[COL]], 2
+// CHECK: [[IDX:%.*]] = add i32 [[OFFSET]], [[ROW]]
+// CHECK: extractelement <6 x float> %{{.*}}, i32 [[IDX]]
>From 124a51dbeb380cc11ba06d5f0a3db864c74bbfe0 Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzonlotfi at microsoft.com>
Date: Mon, 11 May 2026 14:37:57 -0400
Subject: [PATCH 2/8] switch to just ConstantArrayType, HLSL does not support
IncompleteArrayType
---
clang/include/clang/AST/MatrixUtils.h | 2 +-
clang/lib/Sema/SemaHLSL.cpp | 21 ++++++---------------
2 files changed, 7 insertions(+), 16 deletions(-)
diff --git a/clang/include/clang/AST/MatrixUtils.h b/clang/include/clang/AST/MatrixUtils.h
index d1b351bf4b07b..b6385100c3e4d 100644
--- a/clang/include/clang/AST/MatrixUtils.h
+++ b/clang/include/clang/AST/MatrixUtils.h
@@ -1,4 +1,4 @@
-//===- MatrixUtils.h - Matrix AST utilities ---------------------*- C++ -*-===//
+//===- MatrixUtils.h - Matrix AST utilities -------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index cc613d48add42..67bb88bf91581 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2711,22 +2711,13 @@ static QualType wrapMatrixWithLayoutAttr(ASTContext &C, QualType T,
if (T.isNull() || T->isDependentType())
return T;
- if (const auto *AT = dyn_cast<ArrayType>(T.getTypePtr())) {
- QualType Inner = wrapMatrixWithLayoutAttr(C, AT->getElementType(), AttrK);
- if (Inner == AT->getElementType())
+ if (const auto *CAT = dyn_cast<ConstantArrayType>(T.getTypePtr())) {
+ QualType Inner = wrapMatrixWithLayoutAttr(C, CAT->getElementType(), AttrK);
+ if (Inner == CAT->getElementType())
return T;
-
- // fallthrough Inner != AT->getElementType()
- if (const auto *CAT = dyn_cast<ConstantArrayType>(T.getTypePtr()))
- return C.getConstantArrayType(Inner, CAT->getSize(), CAT->getSizeExpr(),
- CAT->getSizeModifier(),
- CAT->getIndexTypeCVRQualifiers());
- // Note The IncompleteArrayType case would handle something like row_major
- // float2x3 arr[] but HLSL doesn't support this syntax. so we can't test it.
- // consider removing.
- if (const auto *IAT = dyn_cast<IncompleteArrayType>(T.getTypePtr()))
- return C.getIncompleteArrayType(Inner, IAT->getSizeModifier(),
- IAT->getIndexTypeCVRQualifiers());
+ return C.getConstantArrayType(Inner, CAT->getSize(), CAT->getSizeExpr(),
+ CAT->getSizeModifier(),
+ CAT->getIndexTypeCVRQualifiers());
}
if (T->isConstantMatrixType())
return C.getAttributedType(AttrK, T, T);
>From 753477b7b5cd36bfc3981e50261ceab21e18bba5 Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzonlotfi at microsoft.com>
Date: Thu, 14 May 2026 16:05:40 -0400
Subject: [PATCH 3/8] Make Matrix multiply check both matrix types for
attribute type sugar of matrix layout
---
clang/lib/CodeGen/CGHLSLBuiltins.cpp | 15 ++++++-------
.../matrix-layout-attr-overrides-default.hlsl | 21 +++++++++++++++++++
2 files changed, 29 insertions(+), 7 deletions(-)
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index ace00a0e4692e..4a8e3a22fb80b 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -1208,8 +1208,8 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
// matrices. Therefore matrix memory layout transforms must be inserted
// before and after matrix multiply intrinsics.
// Use whichever operand is a matrix to discover its declared layout.
- QualType MatTy = IsMat0 ? QTy0 : QTy1;
- bool IsRowMajor = isMatrixRowMajor(getLangOpts(), MatTy);
+ bool IsRowMajorMat0 = IsMat0 && isMatrixRowMajor(getLangOpts(), QTy0);
+ bool IsRowMajorMat1 = IsMat1 && isMatrixRowMajor(getLangOpts(), QTy1);
llvm::MatrixBuilder MB(Builder);
if (IsVec0 && IsMat1) {
@@ -1218,7 +1218,7 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
unsigned Rows = MatTy->getNumRows();
unsigned Cols = MatTy->getNumColumns();
assert(N == Rows && "vector length must match matrix row count");
- if (IsRowMajor)
+ if (IsRowMajorMat1)
Op1 = MB.CreateRowMajorToColumnMajorTransform(Op1, Rows, Cols);
return MB.CreateMatrixMultiply(Op0, Op1, 1, N, Cols, "hlsl.mul");
}
@@ -1228,7 +1228,7 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
unsigned Cols = MatTy->getNumColumns();
assert(QTy1->castAs<VectorType>()->getNumElements() == Cols &&
"vector length must match matrix column count");
- if (IsRowMajor)
+ if (IsRowMajorMat0)
Op0 = MB.CreateRowMajorToColumnMajorTransform(Op0, Rows, Cols);
return MB.CreateMatrixMultiply(Op0, Op1, Rows, Cols, 1, "hlsl.mul");
}
@@ -1241,13 +1241,14 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
unsigned Cols1 = MatTy1->getNumColumns();
assert(Cols0 == Rows1 &&
"inner matrix dimensions must match for multiplication");
- if (IsRowMajor) {
+ if (IsRowMajorMat0)
Op0 = MB.CreateRowMajorToColumnMajorTransform(Op0, Rows0, Cols0);
+ if (IsRowMajorMat1)
Op1 = MB.CreateRowMajorToColumnMajorTransform(Op1, Rows1, Cols1);
- }
+
Value *Result =
MB.CreateMatrixMultiply(Op0, Op1, Rows0, Cols0, Cols1, "hlsl.mul");
- if (IsRowMajor)
+ if (IsRowMajorMat0 || IsRowMajorMat1)
Result = MB.CreateColumnMajorToRowMajorTransform(Result, Rows0, Cols1);
return Result;
}
diff --git a/clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl b/clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl
index 8320807e1cd89..4e8196c885da9 100644
--- a/clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl
+++ b/clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl
@@ -89,6 +89,27 @@ export float3 vec_mat_cm(float2 v, column_major float2x3 m) { return mul(v, m);
// 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)
+// -----------------------------------------------------------------------------
+// __builtin_hlsl_mul (matrix * matrix): mixed per-decl layouts cause a
+// transpose only on the row-major operand.
+// -----------------------------------------------------------------------------
+
+// LHS row-major, RHS column-major: only LHS is transposed.
+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
+// 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)
+
+// 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
+// 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)
+
// -----------------------------------------------------------------------------
// __builtin_hlsl_transpose: row-major operand swaps Rows/Cols passed to the
// underlying intrinsic.
>From a496b17fbbe5b7cdfb13840fb89d80cf0167fc59 Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzonlotfi at microsoft.com>
Date: Wed, 20 May 2026 09:08:51 -0400
Subject: [PATCH 4/8] remove isMatrixColumnMajor
---
clang/include/clang/AST/MatrixUtils.h | 22 ----------------------
clang/lib/CodeGen/CGExpr.cpp | 4 ++--
clang/lib/CodeGen/CGExprScalar.cpp | 2 +-
3 files changed, 3 insertions(+), 25 deletions(-)
diff --git a/clang/include/clang/AST/MatrixUtils.h b/clang/include/clang/AST/MatrixUtils.h
index b6385100c3e4d..ef6cbba6ba7c0 100644
--- a/clang/include/clang/AST/MatrixUtils.h
+++ b/clang/include/clang/AST/MatrixUtils.h
@@ -44,28 +44,6 @@ inline bool isMatrixRowMajor(const LangOptions &LangOpts, QualType T) {
return LangOpts.getDefaultMatrixMemoryLayout() ==
LangOptions::MatrixMemoryLayout::MatrixRowMajor;
}
-
-/// Returns true if matrices of \p T should be laid out in column-major order.
-/// Mirrors `isMatrixRowMajor`; per-decl HLSL attributes win over the
-/// `-fmatrix-memory-layout=` default.
-inline bool isMatrixColumnMajor(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::HLSLColumnMajor:
- return true;
- case attr::HLSLRowMajor:
- return false;
- default:
- break;
- }
- Cur = AT->getModifiedType();
- }
- }
- return LangOpts.getDefaultMatrixMemoryLayout() ==
- LangOptions::MatrixMemoryLayout::MatrixColMajor;
-}
} // namespace clang
#endif // LLVM_CLANG_AST_MATRIXUTILS_H
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 52853719a4d00..6d08473dd24d9 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2361,8 +2361,8 @@ LValue CodeGenFunction::EmitMatrixElementExpr(const MatrixElementExpr *E) {
// getEncodedElementAccess returns row-major linearized indices
// If the matrix memory layout is column-major, convert indices
// to column-major indices.
- bool IsColMajor = isMatrixColumnMajor(getLangOpts(), E->getBase()->getType());
- if (IsColMajor) {
+ bool IsRowlMajor = isMatrixRowMajor(getLangOpts(), E->getBase()->getType());
+ if (!IsRowlMajor) {
const auto *MT = E->getBase()->getType()->castAs<ConstantMatrixType>();
unsigned NumCols = MT->getNumColumns();
for (uint32_t &Idx : Indices) {
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index ddd28cfe5372c..c1d2e8a104fe7 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2361,7 +2361,7 @@ Value *ScalarExprEmitter::VisitInitListExpr(InitListExpr *E) {
// column-major positions rather than inserting sequentially and shuffling.
const ConstantMatrixType *ColMajorMT = nullptr;
if (const auto *MT = E->getType()->getAs<ConstantMatrixType>();
- MT && isMatrixColumnMajor(CGF.getLangOpts(), E->getType()))
+ MT && !isMatrixRowMajor(CGF.getLangOpts(), E->getType()))
ColMajorMT = MT;
// Loop over initializers collecting the Value for each, and remembering
>From b7dd228c6d2ff4bce041925974a537e77bf2fd4b Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzonlotfi at microsoft.com>
Date: Wed, 20 May 2026 16:02:26 -0400
Subject: [PATCH 5/8] remove decl attribute switch entirely to type attribute
Assisted by Claud Opus 4.7
---
clang/include/clang/Basic/Attr.td | 22 +--
clang/include/clang/Sema/SemaHLSL.h | 6 +-
clang/lib/AST/TypePrinter.cpp | 27 +--
clang/lib/Sema/SemaDeclAttr.cpp | 3 -
clang/lib/Sema/SemaHLSL.cpp | 164 ++++++------------
.../lib/Sema/SemaTemplateInstantiateDecl.cpp | 7 -
clang/lib/Sema/SemaType.cpp | 7 +
clang/lib/Sema/TreeTransform.h | 10 ++
clang/test/AST/HLSL/matrix_layout_attr.hlsl | 28 +--
clang/test/SemaHLSL/matrix_layout_attr.hlsl | 3 -
10 files changed, 96 insertions(+), 181 deletions(-)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index accb3ca3c4ce7..782aec69622ff 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5292,28 +5292,20 @@ def HLSLVkLocation : HLSLAnnotationAttr {
let Documentation = [HLSLVkLocationDocs];
}
-def HLSLMatrixLayout : InheritableAttr {
- let Spellings = [CustomKeyword<"row_major">, CustomKeyword<"column_major">];
- let Subjects = SubjectList<[Var, TypedefName, Field, Function]>;
- let LangOpts = [HLSL];
- let Documentation = [HLSLMatrixLayoutDocs];
-}
-
-// Internal type-attribute markers attached to matrix QualTypes by Sema when
-// a decl carries `[[hlsl::row_major]]` / `[[hlsl::column_major]]`. They let
-// CodeGen / const-eval recover the layout from any matrix-typed expression
-// without re-walking back to the source decl.
+// `row_major` / `column_major` are HLSL keywords that select the in-memory
+// layout of a matrix-typed declaration.
def HLSLRowMajor : TypeAttr {
- let Spellings = [];
+ let Spellings = [CustomKeyword<"row_major">];
let LangOpts = [HLSL];
- let Documentation = [InternalOnly];
+ let Documentation = [HLSLMatrixLayoutDocs];
}
def HLSLColumnMajor : TypeAttr {
- let Spellings = [];
+ let Spellings = [CustomKeyword<"column_major">];
let LangOpts = [HLSL];
- let Documentation = [InternalOnly];
+ let Documentation = [HLSLMatrixLayoutDocs];
}
+def : MutualExclusions<[HLSLRowMajor, HLSLColumnMajor]>;
def RandomizeLayout : InheritableAttr {
let Spellings = [GCC<"randomize_layout">];
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index e65de5d4aa4c3..6cdb158e86e61 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -187,9 +187,9 @@ class SemaHLSL : public SemaBase {
void handleShaderAttr(Decl *D, const ParsedAttr &AL);
void handleResourceBindingAttr(Decl *D, const ParsedAttr &AL);
void handleParamModifierAttr(Decl *D, const ParsedAttr &AL);
- void handleMatrixLayoutAttr(Decl *D, const ParsedAttr &AL);
- bool diagnoseInstantiatedMatrixLayoutAttr(Decl *D,
- const HLSLMatrixLayoutAttr *Attr);
+ Attr *buildMatrixLayoutTypeAttr(QualType T, const ParsedAttr &AL);
+ bool diagnoseMatrixLayoutInstantiation(attr::Kind K, QualType T,
+ SourceLocation Loc);
bool handleResourceTypeAttr(QualType T, const ParsedAttr &AL);
template <typename T>
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 7a5aac8bd5cd4..d85ed244f643a 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -295,13 +295,6 @@ bool TypePrinter::canPrefixQualifiers(const Type *T,
// We still want to print the address_space before the type if it is an
// address_space attribute.
const auto *AttrTy = cast<AttributedType>(UnderlyingType);
- // HLSLRowMajor / HLSLColumnMajor are internal markers with no spelling;
- // the printer skips them entirely, so look through them when deciding
- // qualifier placement.
- if (AttrTy->getAttrKind() == attr::HLSLRowMajor ||
- AttrTy->getAttrKind() == attr::HLSLColumnMajor)
- return canPrefixQualifiers(AttrTy->getModifiedType().getTypePtr(),
- NeedARCStrongQualifier);
CanPrefixQualifiers = AttrTy->getAttrKind() == attr::AddressSpace;
break;
}
@@ -1922,15 +1915,6 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
if (T->getAttrKind() == attr::ObjCInertUnsafeUnretained)
return;
- // HLSLRowMajor / HLSLColumnMajor are internal markers attached to matrix
- // types by Sema to record an explicit `[[hlsl::row_major]]` /
- // `[[hlsl::column_major]]` qualifier from the source decl. They have no
- // user-visible spelling; the user-facing form is the original keyword on
- // the decl, not on the type.
- if (T->getAttrKind() == attr::HLSLRowMajor ||
- T->getAttrKind() == attr::HLSLColumnMajor)
- return;
-
// Don't print ns_returns_retained unless it had an effect.
if (T->getAttrKind() == attr::NSReturnsRetained &&
!T->getEquivalentType()->castAs<FunctionType>()
@@ -2008,10 +1992,6 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::HLSLResourceDimension:
llvm_unreachable("HLSL resource type attributes handled separately");
- case attr::HLSLRowMajor:
- case attr::HLSLColumnMajor:
- llvm_unreachable("HLSL matrix layout type attributes handled separately");
-
case attr::OpenCLPrivateAddressSpace:
case attr::OpenCLGlobalAddressSpace:
case attr::OpenCLGlobalDeviceAddressSpace:
@@ -2069,6 +2049,13 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
OS << "ns_returns_retained";
break;
+ case attr::HLSLRowMajor:
+ OS << "row_major";
+ break;
+ case attr::HLSLColumnMajor:
+ OS << "column_major";
+ break;
+
// FIXME: When Sema learns to form this AttributedType, avoid printing the
// attribute again in printFunctionProtoAfter.
case attr::AnyX86NoCfCheck: OS << "nocf_check"; break;
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index ae04d3855f01c..fa93ef90a6505 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -8193,9 +8193,6 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_HLSLParamModifier:
S.HLSL().handleParamModifierAttr(D, AL);
break;
- case ParsedAttr::AT_HLSLMatrixLayout:
- S.HLSL().handleMatrixLayoutAttr(D, AL);
- break;
case ParsedAttr::AT_HLSLUnparsedSemantic:
S.HLSL().handleSemanticAttr(D, AL);
break;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 67bb88bf91581..70e0feab29e8f 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2704,130 +2704,74 @@ static bool isMatrixOrArrayOfMatrix(const ASTContext &Ctx, QualType QT) {
return Ty->isDependentType() || Ty->isConstantMatrixType();
}
-/// Embeds the row/column-major layout ( \p AttrK ) directly into the
-/// matrix QualType of \p T, preserving any surrounding type sugar.
-static QualType wrapMatrixWithLayoutAttr(ASTContext &C, QualType T,
- attr::Kind AttrK) {
- if (T.isNull() || T->isDependentType())
- return T;
-
- if (const auto *CAT = dyn_cast<ConstantArrayType>(T.getTypePtr())) {
- QualType Inner = wrapMatrixWithLayoutAttr(C, CAT->getElementType(), AttrK);
- if (Inner == CAT->getElementType())
- return T;
- return C.getConstantArrayType(Inner, CAT->getSize(), CAT->getSizeExpr(),
- CAT->getSizeModifier(),
- CAT->getIndexTypeCVRQualifiers());
- }
- if (T->isConstantMatrixType())
- return C.getAttributedType(AttrK, T, T);
- return T;
-}
-
-static bool diagnoseMatrixLayoutOnNonMatrix(Sema &SemaRef, Decl *D,
- SourceLocation Loc,
- const IdentifierInfo *AttrName) {
- QualType Ty;
- if (auto *VD = dyn_cast<ValueDecl>(D))
- Ty = VD->getType();
- else if (auto *TD = dyn_cast<TypedefNameDecl>(D))
- Ty = TD->getUnderlyingType();
-
- if (Ty.isNull() || Ty->isDependentType())
- return false;
-
- // For functions, the qualifier can apply to the return type or any parameter.
- if (const auto *FPT = Ty->getAs<FunctionProtoType>()) {
- if (isMatrixOrArrayOfMatrix(SemaRef.getASTContext(), FPT->getReturnType()))
- return false;
- SemaRef.Diag(Loc, diag::err_hlsl_matrix_layout_non_matrix) << AttrName;
- return true;
+/// Walks the existing AttributedType sugar of \p T looking for a previously
+/// applied HLSLRowMajor/HLSLColumnMajor marker. If one is found, populates
+/// \p ExistingKind with its attr::Kind and returns true.
+static bool findExistingMatrixLayoutMarker(QualType T,
+ attr::Kind &ExistingKind) {
+ QualType Cur = T;
+ while (const auto *AT = Cur->getAs<AttributedType>()) {
+ attr::Kind K = AT->getAttrKind();
+ if (K == attr::HLSLRowMajor || K == attr::HLSLColumnMajor) {
+ ExistingKind = K;
+ return true;
+ }
+ Cur = AT->getModifiedType();
}
-
- if (isMatrixOrArrayOfMatrix(SemaRef.getASTContext(), Ty))
- return false;
-
- SemaRef.Diag(Loc, diag::err_hlsl_matrix_layout_non_matrix) << AttrName;
- return true;
+ return false;
}
-/// Embeds layout attributes into the matrix type so no decl lookups are
-/// needed.
-static void applyHLSLMatrixLayoutTypeAttr(ASTContext &Ctx, Decl *D,
- const ParsedAttr &AL) {
- attr::Kind AttrK =
- AL.getSemanticSpelling() == HLSLMatrixLayoutAttr::Keyword_row_major
- ? attr::HLSLRowMajor
- : attr::HLSLColumnMajor;
-
- auto *TD = dyn_cast<TypedefNameDecl>(D);
- auto *VD = dyn_cast<ValueDecl>(D);
-
- if (!TD && !VD)
- return;
-
- if (auto *FD = dyn_cast<FunctionDecl>(D)) {
- const auto *FPT = FD->getType()->getAs<FunctionProtoType>();
- if (!FPT)
- return;
-
- QualType NewRet =
- wrapMatrixWithLayoutAttr(Ctx, FPT->getReturnType(), AttrK);
- if (NewRet == FPT->getReturnType())
- return;
-
- FD->setType(Ctx.getFunctionType(NewRet, FPT->getParamTypes(),
- FPT->getExtProtoInfo()));
- return;
- }
+Attr *SemaHLSL::buildMatrixLayoutTypeAttr(QualType T, const ParsedAttr &AL) {
+ ASTContext &Ctx = getASTContext();
+ attr::Kind AttrK = AL.getKind() == ParsedAttr::AT_HLSLRowMajor
+ ? attr::HLSLRowMajor
+ : attr::HLSLColumnMajor;
- if (VD) {
- QualType OldT = VD->getType();
- QualType NewT = wrapMatrixWithLayoutAttr(Ctx, OldT, AttrK);
- if (NewT != OldT)
- VD->setType(NewT);
- return;
- }
+ if (T.isNull())
+ return nullptr;
- if (TD) {
- QualType OldT = TD->getUnderlyingType();
- QualType NewT = wrapMatrixWithLayoutAttr(Ctx, OldT, AttrK);
- if (NewT != OldT)
- TD->setModedTypeSourceInfo(TD->getTypeSourceInfo(), NewT);
- return;
+ // For non-dependent types, the operand must be a matrix (or array of
+ // matrices).
+ if (!T->isDependentType() && !isMatrixOrArrayOfMatrix(Ctx, T)) {
+ Diag(AL.getLoc(), diag::err_hlsl_matrix_layout_non_matrix)
+ << AL.getAttrName();
+ AL.setInvalid();
+ return nullptr;
}
-}
-
-void SemaHLSL::handleMatrixLayoutAttr(Decl *D, const ParsedAttr &AL) {
- // row_major and column_major are only valid on matrix types.
- if (diagnoseMatrixLayoutOnNonMatrix(SemaRef, D, AL.getLoc(),
- AL.getAttrName()))
- return;
- // Check for conflicting or duplicate matrix layout attributes.
- if (const auto *Existing = D->getAttr<HLSLMatrixLayoutAttr>()) {
- if (Existing->getSemanticSpelling() != AL.getSemanticSpelling()) {
- Diag(AL.getLoc(), diag::err_hlsl_matrix_layout_conflict)
- << AL.getAttrName() << Existing->getAttrName();
- Diag(Existing->getLoc(), diag::note_conflicting_attribute);
- } else {
+ // Conflict / duplicate detection by walking existing sugar.
+ attr::Kind ExistingKind;
+ if (findExistingMatrixLayoutMarker(T, ExistingKind)) {
+ if (ExistingKind == AttrK) {
Diag(AL.getLoc(), diag::warn_duplicate_attribute_exact)
<< AL.getAttrName();
- Diag(Existing->getLoc(), diag::note_previous_attribute);
+ return nullptr;
}
- return;
+ IdentifierInfo *ExistingII = &Ctx.Idents.get(
+ ExistingKind == attr::HLSLRowMajor ? "row_major" : "column_major");
+ Diag(AL.getLoc(), diag::err_hlsl_matrix_layout_conflict)
+ << AL.getAttrName() << ExistingII;
+ AL.setInvalid();
+ return nullptr;
}
- D->addAttr(::new (getASTContext()) HLSLMatrixLayoutAttr(getASTContext(), AL));
-
- ASTContext &Ctx = getASTContext();
- applyHLSLMatrixLayoutTypeAttr(Ctx, D, AL);
+ if (AttrK == attr::HLSLRowMajor)
+ return ::new (Ctx) HLSLRowMajorAttr(Ctx, AL);
+ return ::new (Ctx) HLSLColumnMajorAttr(Ctx, AL);
}
-bool SemaHLSL::diagnoseInstantiatedMatrixLayoutAttr(
- Decl *D, const HLSLMatrixLayoutAttr *Attr) {
- return diagnoseMatrixLayoutOnNonMatrix(SemaRef, D, Attr->getLoc(),
- Attr->getAttrName());
+bool SemaHLSL::diagnoseMatrixLayoutInstantiation(attr::Kind K, QualType T,
+ SourceLocation Loc) {
+ if (K != attr::HLSLRowMajor && K != attr::HLSLColumnMajor)
+ return false;
+ if (T.isNull() || T->isDependentType())
+ return false;
+ if (isMatrixOrArrayOfMatrix(getASTContext(), T))
+ return false;
+ IdentifierInfo *II = &getASTContext().Idents.get(
+ K == attr::HLSLRowMajor ? "row_major" : "column_major");
+ Diag(Loc, diag::err_hlsl_matrix_layout_non_matrix) << II;
+ return true;
}
namespace {
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index c9bc613a7c4ea..324d6bf3857c7 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1044,13 +1044,6 @@ void Sema::InstantiateAttrs(const MultiLevelTemplateArgumentList &TemplateArgs,
continue;
}
- if (auto *A = dyn_cast<HLSLMatrixLayoutAttr>(TmplAttr)) {
- if (!HLSL().diagnoseInstantiatedMatrixLayoutAttr(New, A) &&
- !New->hasAttr<HLSLMatrixLayoutAttr>())
- New->addAttr(A->clone(Context));
- continue;
- }
-
assert(!TmplAttr->isPackExpansion());
if (TmplAttr->isLateParsed() && LateAttrs) {
// Late parsed attributes must be instantiated and attached after the
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 0e6532a6e2178..6283fe4f41f36 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -9129,6 +9129,13 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type,
}
attr.setUsedAsTypeAttr();
break;
+ case ParsedAttr::AT_HLSLRowMajor:
+ case ParsedAttr::AT_HLSLColumnMajor:
+ if (Attr *A =
+ state.getSema().HLSL().buildMatrixLayoutTypeAttr(type, attr))
+ type = state.getAttributedType(A, type, type);
+ attr.setUsedAsTypeAttr();
+ break;
OBJC_POINTER_TYPE_ATTRS_CASELIST:
if (!handleObjCPointerTypeAttr(state, attr, type))
distributeObjCPointerTypeAttr(state, attr, type);
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 70cdc47e42d5d..77c8f17439a1a 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -39,6 +39,7 @@
#include "clang/Sema/ParsedTemplate.h"
#include "clang/Sema/ScopeInfo.h"
#include "clang/Sema/SemaDiagnostic.h"
+#include "clang/Sema/SemaHLSL.h"
#include "clang/Sema/SemaInternal.h"
#include "clang/Sema/SemaObjC.h"
#include "clang/Sema/SemaOpenACC.h"
@@ -7673,6 +7674,15 @@ QualType TreeTransform<Derived>::TransformAttributedType(TypeLocBuilder &TLB,
if (modifiedType.isNull())
return QualType();
+ // HLSL: re-validate matrix-layout markers after substitution. If the
+ // post-substitution type is no longer a matrix, diagnose now.
+ if (SemaRef.getLangOpts().HLSL &&
+ SemaRef.HLSL().diagnoseMatrixLayoutInstantiation(
+ oldType->getAttrKind(), modifiedType,
+ TL.getAttr() ? TL.getAttr()->getLocation()
+ : TL.getModifiedLoc().getBeginLoc()))
+ return QualType();
+
// oldAttr can be null if we started with a QualType rather than a TypeLoc.
const Attr *oldAttr = TL.getAttr();
const Attr *newAttr = oldAttr ? getDerived().TransformAttr(oldAttr) : nullptr;
diff --git a/clang/test/AST/HLSL/matrix_layout_attr.hlsl b/clang/test/AST/HLSL/matrix_layout_attr.hlsl
index 98bcb9976c58c..5e5084feb143f 100644
--- a/clang/test/AST/HLSL/matrix_layout_attr.hlsl
+++ b/clang/test/AST/HLSL/matrix_layout_attr.hlsl
@@ -1,35 +1,23 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header \
// RUN: -std=hlsl202x -ast-dump -x hlsl %s | FileCheck %s
-// CHECK: VarDecl {{.*}} rm_mat 'hlsl_constant float3x3'
-// CHECK-NEXT: HLSLMatrixLayoutAttr {{.*}} row_major
+// CHECK: VarDecl {{.*}} rm_mat 'float3x3 hlsl_constant __attribute__((row_major))':'matrix<float, 3, 3> hlsl_constant'
row_major float3x3 rm_mat;
-
-// CHECK: VarDecl {{.*}} cm_mat 'hlsl_constant float4x4'
-// CHECK-NEXT: HLSLMatrixLayoutAttr {{.*}} column_major
+// CHECK: VarDecl {{.*}} cm_mat 'float4x4 hlsl_constant __attribute__((column_major))':'matrix<float, 4, 4> hlsl_constant'
column_major float4x4 cm_mat;
// CHECK: CXXRecordDecl {{.*}} struct S definition
struct S {
- // CHECK: FieldDecl {{.*}} m1 'float2x2'
- // CHECK-NEXT: HLSLMatrixLayoutAttr {{.*}} row_major
+ // CHECK: FieldDecl {{.*}} m1 'float2x2 __attribute__((row_major))':'matrix<float, 2, 2>'
row_major float2x2 m1;
- // CHECK: FieldDecl {{.*}} m2 'float3x3'
- // CHECK-NEXT: HLSLMatrixLayoutAttr {{.*}} column_major
+ // CHECK: FieldDecl {{.*}} m2 'float3x3 __attribute__((column_major))':'matrix<float, 3, 3>'
column_major float3x3 m2;
};
-
-// Check to make sure the internal type-sugar marker is invisible in the
-//printed type (it has no spelling), but the `AttributedType` node and
-// the `HLSLMatrixLayoutAttr` child still appear in the AST dump.
-
-// CHECK: TypedefDecl {{.*}} RM44 'float4x4'
-// CHECK: AttributedType {{.*}} 'float4x4' sugar
-// CHECK: HLSLMatrixLayoutAttr {{.*}} row_major
+// CHECK-LABEL: TypedefDecl {{.*}} RM44 'float4x4 __attribute__((row_major))':'matrix<float, 4, 4>'
+// CHECK-NEXT: AttributedType {{.*}} 'float4x4 __attribute__((row_major))' sugar
typedef row_major float4x4 RM44;
-// CHECK-LABEL: TypedefDecl {{.*}} CM44 'float4x4'
-// CHECK: AttributedType {{.*}} 'float4x4' sugar
-// CHECK: HLSLMatrixLayoutAttr {{.*}} column_major
+// CHECK-LABEL: TypedefDecl {{.*}} CM44 'float4x4 __attribute__((column_major))':'matrix<float, 4, 4>'
+// CHECK-NEXT: AttributedType {{.*}} 'float4x4 __attribute__((column_major))' sugar
typedef column_major float4x4 CM44;
diff --git a/clang/test/SemaHLSL/matrix_layout_attr.hlsl b/clang/test/SemaHLSL/matrix_layout_attr.hlsl
index 3e953f07557e6..a5c3cc38027b0 100644
--- a/clang/test/SemaHLSL/matrix_layout_attr.hlsl
+++ b/clang/test/SemaHLSL/matrix_layout_attr.hlsl
@@ -102,16 +102,13 @@ struct S2 {
typedef row_major float ScalarRM;
// Invalid: conflicting row_major and column_major on same decl.
-// expected-note at +2 {{conflicting attribute is here}}
// expected-error at +1 {{'column_major' and 'row_major' attributes are not compatible}}
row_major column_major float3x3 conflict_mat;
// Invalid: duplicate row_major.
-// expected-note at +2 {{previous attribute is here}}
// expected-warning at +1 {{attribute 'row_major' is already applied}}
row_major row_major float3x3 dup_rm_mat;
// Invalid: duplicate column_major.
-// expected-note at +2 {{previous attribute is here}}
// expected-warning at +1 {{attribute 'column_major' is already applied}}
column_major column_major float4x4 dup_cm_mat;
>From fb40fc8553c62146f1dfb0061a2aef66d894f9cd Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzonlotfi at microsoft.com>
Date: Wed, 20 May 2026 18:10:36 -0400
Subject: [PATCH 6/8] address obvious PR comments
---
clang/lib/CodeGen/CGExpr.cpp | 4 ++--
clang/lib/CodeGen/CGExprScalar.cpp | 9 +++++----
clang/lib/CodeGen/CGHLSLBuiltins.cpp | 4 +++-
3 files changed, 10 insertions(+), 7 deletions(-)
diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index 6d08473dd24d9..19b5c61395b34 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -2361,8 +2361,8 @@ LValue CodeGenFunction::EmitMatrixElementExpr(const MatrixElementExpr *E) {
// getEncodedElementAccess returns row-major linearized indices
// If the matrix memory layout is column-major, convert indices
// to column-major indices.
- bool IsRowlMajor = isMatrixRowMajor(getLangOpts(), E->getBase()->getType());
- if (!IsRowlMajor) {
+ bool IsRowMajor = isMatrixRowMajor(getLangOpts(), E->getBase()->getType());
+ if (!IsRowMajor) {
const auto *MT = E->getBase()->getType()->castAs<ConstantMatrixType>();
unsigned NumCols = MT->getNumColumns();
for (uint32_t &Idx : Indices) {
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index c1d2e8a104fe7..8170fa41d1ea1 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -2246,10 +2246,11 @@ Value *ScalarExprEmitter::VisitMatrixSingleSubscriptExpr(
auto *ResultTy = llvm::FixedVectorType::get(ElemTy, NumColumns);
Value *RowVec = llvm::PoisonValue::get(ResultTy);
+ bool IsMatrixRowMajor =
+ isMatrixRowMajor(CGF.getLangOpts(), E->getBase()->getType());
+
for (unsigned Col = 0; Col != NumColumns; ++Col) {
Value *ColVal = llvm::ConstantInt::get(RowIdx->getType(), Col);
- bool IsMatrixRowMajor =
- isMatrixRowMajor(CGF.getLangOpts(), E->getBase()->getType());
Value *EltIdx = MB.CreateIndex(RowIdx, ColVal, NumRows, NumColumns,
IsMatrixRowMajor, "matrix_row_idx");
Value *Elt =
@@ -3181,8 +3182,8 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
assert(NumCols <= SrcMatTy->getNumColumns());
// isMatrixRowMajor needs the full sugared QualType to find matrix layout
- // attrs. So use Use E->getType() (the source QualType) rather than
- // SrcMatTy b\c getAs<ConstantMatrixType>() strips the sugar.
+ // attrs. So use E->getType() (the source QualType) rather than
+ // SrcMatTy b/c getAs<ConstantMatrixType>() strips the sugar.
bool IsRowMajor = isMatrixRowMajor(CGF.getLangOpts(), E->getType());
for (unsigned R = 0; R < NumRows; R++)
for (unsigned C = 0; C < NumCols; C++)
diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
index 4a8e3a22fb80b..e9511f5729f15 100644
--- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp
+++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp
@@ -1248,7 +1248,9 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID,
Value *Result =
MB.CreateMatrixMultiply(Op0, Op1, Rows0, Cols0, Cols1, "hlsl.mul");
- if (IsRowMajorMat0 || IsRowMajorMat1)
+
+ bool IsResultRowMajor = isMatrixRowMajor(getLangOpts(), E->getType());
+ if (IsResultRowMajor)
Result = MB.CreateColumnMajorToRowMajorTransform(Result, Rows0, Cols1);
return Result;
}
>From 9788f7e5f80d3d5c4bf52e8834d76be349250c4a Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzonlotfi at microsoft.com>
Date: Wed, 20 May 2026 21:37:30 -0400
Subject: [PATCH 7/8] keep type sugar in matrix truncation so we can lookup
DestTy matrix layout type.
---
clang/lib/CodeGen/CGExprScalar.cpp | 13 +++----
clang/lib/Sema/SemaExprCXX.cpp | 8 +++--
.../matrix-layout-attr-overrides-default.hlsl | 35 +++++++++++++++++--
.../BuiltIns/logical-mat-operator-errors.hlsl | 8 ++---
.../MatrixElementOverloadResolution.hlsl | 26 +++++++-------
.../MatrixImplicitTruncCastWarnings.hlsl | 14 ++++----
6 files changed, 69 insertions(+), 35 deletions(-)
diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp
index 8170fa41d1ea1..3a3dff7bec347 100644
--- a/clang/lib/CodeGen/CGExprScalar.cpp
+++ b/clang/lib/CodeGen/CGExprScalar.cpp
@@ -3181,14 +3181,15 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
assert(NumRows <= SrcMatTy->getNumRows());
assert(NumCols <= SrcMatTy->getNumColumns());
- // isMatrixRowMajor needs the full sugared QualType to find matrix layout
- // attrs. So use E->getType() (the source QualType) rather than
- // SrcMatTy b/c getAs<ConstantMatrixType>() strips the sugar.
- bool IsRowMajor = isMatrixRowMajor(CGF.getLangOpts(), E->getType());
+ // isMatrix[Src|Dst]RowMajor needs the full sugared QualType to find
+ // matrix layout attrs. So use E->getType() & DestTy rather than SrcMatTy
+ // & MatTy b/c getAs<ConstantMatrixType>() strips the sugar.
+ bool IsSrcRowMajor = isMatrixRowMajor(CGF.getLangOpts(), E->getType());
+ bool IsDstRowMajor = isMatrixRowMajor(CGF.getLangOpts(), DestTy);
for (unsigned R = 0; R < NumRows; R++)
for (unsigned C = 0; C < NumCols; C++)
- Mask[MatTy->getFlattenedIndex(R, C, IsRowMajor)] =
- SrcMatTy->getFlattenedIndex(R, C, IsRowMajor);
+ Mask[MatTy->getFlattenedIndex(R, C, IsDstRowMajor)] =
+ SrcMatTy->getFlattenedIndex(R, C, IsSrcRowMajor);
return Builder.CreateShuffleVector(Mat, Mask, "trunc");
}
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index 00c873833c8a7..4e1652462b3ae 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -5344,9 +5344,11 @@ Sema::PerformImplicitConversion(Expr *From, QualType ToType,
case ICK_HLSL_Matrix_Truncation: {
auto *FromMat = From->getType()->castAs<ConstantMatrixType>();
QualType TruncTy = FromMat->getElementType();
- if (auto *ToMat = ToType->getAs<ConstantMatrixType>())
- TruncTy = Context.getConstantMatrixType(TruncTy, ToMat->getNumRows(),
- ToMat->getNumColumns());
+ // Preserve any sugar (e.g. `row_major`/`column_major` HLSL TypeAttrs) on
+ // `ToType` so that downstream CodeGen can query the destination layout
+ // from the cast node itself rather than falling back to the TU default.
+ if (ToType->getAs<ConstantMatrixType>())
+ TruncTy = ToType;
From = ImpCastExprToType(From, TruncTy, CK_HLSLMatrixTruncation,
From->getValueKind())
.get();
diff --git a/clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl b/clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl
index 4e8196c885da9..6536a4341d3e9 100644
--- a/clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl
+++ b/clang/test/CodeGenHLSL/matrix-layout-attr-overrides-default.hlsl
@@ -130,16 +130,47 @@ export float3x2 transpose_cm(column_major float2x3 m) { return transpose(m); }
// source matrix uses the operand's per-decl layout to flatten indices.
// -----------------------------------------------------------------------------
+typedef row_major float2x2 RM22;
+typedef column_major float2x2 CM22;
+typedef row_major float3x3 RM33;
+typedef column_major float3x3 CM33;
+
// Row-major source 3x2 -> row-major dest 2x2: flat row-major mask is {0,1,2,3}.
-export float2x2 truncate_rm(row_major float3x2 m) { return (float2x2)m; }
+export row_major float2x2 truncate_rm(row_major float3x2 m) { return (RM22)m; }
// CHECK-LABEL: define {{.*}} <4 x float> @_Z11truncate_rmu11matrix_typeILm3ELm2EfE
// 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 float2x2 truncate_cm(column_major float3x2 m) { return (float2x2)m; }
+export column_major float2x2 truncate_cm(column_major float3x2 m) { return (CM22)m; }
// CHECK-LABEL: define {{.*}} <4 x float> @_Z11truncate_cmu11matrix_typeILm3ELm2EfE
// CHECK: shufflevector <6 x float> %{{.*}}, <6 x float> poison, <4 x i32> <i32 0, i32 1, i32 3, i32 4>
+// -----------------------------------------------------------------------------
+// CK_HLSLMatrixTruncation cross-layout: when source and destination carry
+// different layout keywords, `IsSrcRowMajor` and `IsDstRowMajor` differ. The
+// source indices flatten using the source layout while the destination
+// positions flatten using the destination layout. This is independent of the
+// `-fmatrix-memory-layout=` default.
+// -----------------------------------------------------------------------------
+
+// Row-major src 3x4 -> column-major dst 3x3.
+// src idx (R,C) = R*4+C; dst slot (R,C) = C*3+R.
+// (0,0)->mask[0]=0 (0,1)->mask[3]=1 (0,2)->mask[6]=2
+// (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: 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.
+// src idx (R,C) = C*3+R; dst slot (R,C) = R*3+C.
+// (0,0)->mask[0]=0 (0,1)->mask[1]=3 (0,2)->mask[2]=6
+// (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: 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>
+
// -----------------------------------------------------------------------------
// Array of matrix: the per-decl layout attribute propagates through
// ConstantArrayType sugar via wrapMatrixWithLayoutAttr, so indexing into an
diff --git a/clang/test/SemaHLSL/BuiltIns/logical-mat-operator-errors.hlsl b/clang/test/SemaHLSL/BuiltIns/logical-mat-operator-errors.hlsl
index bd7f01dbd768f..693c49c497824 100644
--- a/clang/test/SemaHLSL/BuiltIns/logical-mat-operator-errors.hlsl
+++ b/clang/test/SemaHLSL/BuiltIns/logical-mat-operator-errors.hlsl
@@ -4,19 +4,19 @@
bool2x2 test_mismatched_args(bool2x2 a, bool3x3 b)
{
return TEST_FUNC(a, b);
- // expected-warning at -1 {{implicit conversion truncates matrix: 'bool3x3' (aka 'matrix<bool, 3, 3>') to 'matrix<bool, 2, 2>'}}
+ // expected-warning at -1 {{implicit conversion truncates matrix: 'bool3x3' (aka 'matrix<bool, 3, 3>') to 'bool2x2' (aka 'matrix<bool, 2, 2>')}}
}
bool3x3 test_mismatched_args(bool3x3 a, bool2x2 b)
{
return TEST_FUNC(a, b);
- // expected-error at -1 {{cannot initialize return object of type 'bool3x3' (aka 'matrix<bool, 3, 3>') with an rvalue of type 'matrix<bool, 2, 2>'}}
+ // expected-error at -1 {{cannot initialize return object of type 'matrix<[...], 3, 3>' with an rvalue of type 'matrix<[...], 2, 2>'}}
}
bool2x2 test_mismatched_args2(bool3x3 a, bool2x2 b)
{
return TEST_FUNC(a, b);
- // expected-warning at -1 {{implicit conversion truncates matrix: 'bool3x3' (aka 'matrix<bool, 3, 3>') to 'matrix<bool, 2, 2>'}}
+ // expected-warning at -1 {{implicit conversion truncates matrix: 'bool3x3' (aka 'matrix<bool, 3, 3>') to 'bool2x2' (aka 'matrix<bool, 2, 2>')}}
}
bool3x3 test_mismatched_return_larger(bool2x2 a, bool2x2 b)
@@ -28,5 +28,5 @@ bool3x3 test_mismatched_return_larger(bool2x2 a, bool2x2 b)
bool2x2 test_mismatched_return_smaller(bool3x3 a, bool3x3 b)
{
return TEST_FUNC(a, b);
- // expected-warning at -1 {{implicit conversion truncates matrix: 'bool3x3' (aka 'matrix<bool, 3, 3>') to 'matrix<bool, 2, 2>'}}
+ // expected-warning at -1 {{implicit conversion truncates matrix: 'bool3x3' (aka 'matrix<bool, 3, 3>') to 'bool2x2' (aka 'matrix<bool, 2, 2>')}}
}
diff --git a/clang/test/SemaHLSL/MatrixElementOverloadResolution.hlsl b/clang/test/SemaHLSL/MatrixElementOverloadResolution.hlsl
index 011f2a19cb847..e0619c2987fbc 100644
--- a/clang/test/SemaHLSL/MatrixElementOverloadResolution.hlsl
+++ b/clang/test/SemaHLSL/MatrixElementOverloadResolution.hlsl
@@ -238,10 +238,10 @@ void matOrVec3(float4x4 F) {}
export void Case8(float2x3 f23, float4x4 f44, float3x3 f33, float3x2 f32) {
int2x2 i22 = f23;
- // expected-warning at -1{{implicit conversion truncates matrix: 'float2x3' (aka 'matrix<float, 2, 3>') to 'matrix<int, 2, 2>'}}
- // expected-warning at -2{{implicit conversion turns floating-point number into integer: 'float2x3' (aka 'matrix<float, 2, 3>') to 'matrix<int, 2, 2>'}}
+ // expected-warning at -1{{implicit conversion truncates matrix: 'float2x3' (aka 'matrix<float, 2, 3>') to 'int2x2' (aka 'matrix<int, 2, 2>')}}
+ // expected-warning at -2{{implicit conversion turns floating-point number into integer: 'float2x3' (aka 'matrix<float, 2, 3>') to 'int2x2' (aka 'matrix<int, 2, 2>')}}
//CHECK: VarDecl {{.*}} i22 'int2x2':'matrix<int, 2, 2>' cinit
- //CHECK-NEXT: ImplicitCastExpr {{.*}} 'matrix<int, 2, 2>' <HLSLMatrixTruncation>
+ //CHECK-NEXT: ImplicitCastExpr {{.*}} 'int2x2':'matrix<int, 2, 2>' <HLSLMatrixTruncation>
//CHECK-NEXT: ImplicitCastExpr {{.*}} 'matrix<int, 2, 3>' <FloatingToIntegral>
//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float2x3':'matrix<float, 2, 3>' <LValueToRValue>
#ifdef ERROR
@@ -250,9 +250,9 @@ export void Case8(float2x3 f23, float4x4 f44, float3x3 f33, float3x2 f32) {
#endif
fn2x2(f23);
- // expected-warning at -1{{implicit conversion truncates matrix: 'float2x3' (aka 'matrix<float, 2, 3>') to 'matrix<float, 2, 2>'}}
+ // expected-warning at -1{{implicit conversion truncates matrix: 'float2x3' (aka 'matrix<float, 2, 3>') to 'float2x2' (aka 'matrix<float, 2, 2>')}}
//CHECK: DeclRefExpr {{.*}} 'void (float2x2)' lvalue Function {{.*}} 'fn2x2' 'void (float2x2)'
- //CHECK-NEXT: ImplicitCastExpr {{.*}} 'matrix<float, 2, 2>' <HLSLMatrixTruncation>
+ //CHECK-NEXT: ImplicitCastExpr {{.*}} 'float2x2':'matrix<float, 2, 2>' <HLSLMatrixTruncation>
//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float2x3':'matrix<float, 2, 3>' <LValueToRValue>
#ifdef ERROR
@@ -261,15 +261,15 @@ export void Case8(float2x3 f23, float4x4 f44, float3x3 f33, float3x2 f32) {
#endif
matOrVec(f23);
- // expected-warning at -1{{implicit conversion truncates matrix: 'float2x3' (aka 'matrix<float, 2, 3>') to 'matrix<float, 2, 2>'}}
+ // expected-warning at -1{{implicit conversion truncates matrix: 'float2x3' (aka 'matrix<float, 2, 3>') to 'float2x2' (aka 'matrix<float, 2, 2>')}}
//CHECK: DeclRefExpr {{.*}} 'void (float2x2)' lvalue Function {{.*}} 'matOrVec' 'void (float2x2)'
- //CHECK-NEXT: ImplicitCastExpr {{.*}} 'matrix<float, 2, 2>' <HLSLMatrixTruncation>
+ //CHECK-NEXT: ImplicitCastExpr {{.*}} 'float2x2':'matrix<float, 2, 2>' <HLSLMatrixTruncation>
//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float2x3':'matrix<float, 2, 3>' <LValueToRValue>
matOrVec(f44);
- // expected-warning at -1{{implicit conversion truncates matrix: 'float4x4' (aka 'matrix<float, 4, 4>') to 'matrix<float, 2, 2>'}}
+ // expected-warning at -1{{implicit conversion truncates matrix: 'float4x4' (aka 'matrix<float, 4, 4>') to 'float2x2' (aka 'matrix<float, 2, 2>')}}
//CHECK: DeclRefExpr {{.*}} 'void (float2x2)' lvalue Function {{.*}} 'matOrVec' 'void (float2x2)'
- //CHECK-NEXT: ImplicitCastExpr {{.*}} 'matrix<float, 2, 2>' <HLSLMatrixTruncation>
+ //CHECK-NEXT: ImplicitCastExpr {{.*}} 'float2x2':'matrix<float, 2, 2>' <HLSLMatrixTruncation>
//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float4x4':'matrix<float, 4, 4>' <LValueToRValue>
#ifdef ERROR
@@ -284,15 +284,15 @@ export void Case8(float2x3 f23, float4x4 f44, float3x3 f33, float3x2 f32) {
//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float2x3':'matrix<float, 2, 3>' <LValueToRValue>
matOrVec2(f44);
- // expected-warning at -1{{implicit conversion truncates matrix: 'float4x4' (aka 'matrix<float, 4, 4>') to 'matrix<float, 2, 3>'}}
+ // expected-warning at -1{{implicit conversion truncates matrix: 'float4x4' (aka 'matrix<float, 4, 4>') to 'float2x3' (aka 'matrix<float, 2, 3>')}}
//CHECK: DeclRefExpr {{.*}} 'void (float2x3)' lvalue Function {{.*}} 'matOrVec2' 'void (float2x3)'
- //CHECK-NEXT: ImplicitCastExpr {{.*}} 'matrix<float, 2, 3>' <HLSLMatrixTruncation>
+ //CHECK-NEXT: ImplicitCastExpr {{.*}} 'float2x3':'matrix<float, 2, 3>' <HLSLMatrixTruncation>
//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float4x4':'matrix<float, 4, 4>' <LValueToRValue>
matOrVec2(f33);
- // expected-warning at -1{{implicit conversion truncates matrix: 'float3x3' (aka 'matrix<float, 3, 3>') to 'matrix<float, 2, 3>'}}
+ // expected-warning at -1{{implicit conversion truncates matrix: 'float3x3' (aka 'matrix<float, 3, 3>') to 'float2x3' (aka 'matrix<float, 2, 3>')}}
//CHECK: DeclRefExpr {{.*}} 'void (float2x3)' lvalue Function {{.*}} 'matOrVec2' 'void (float2x3)'
- //CHECK-NEXT: ImplicitCastExpr {{.*}} 'matrix<float, 2, 3>' <HLSLMatrixTruncation>
+ //CHECK-NEXT: ImplicitCastExpr {{.*}} 'float2x3':'matrix<float, 2, 3>' <HLSLMatrixTruncation>
//CHECK-NEXT: ImplicitCastExpr {{.*}} 'float3x3':'matrix<float, 3, 3>' <LValueToRValue>
#ifdef ERROR
diff --git a/clang/test/SemaHLSL/Types/BuiltinMatrix/MatrixImplicitTruncCastWarnings.hlsl b/clang/test/SemaHLSL/Types/BuiltinMatrix/MatrixImplicitTruncCastWarnings.hlsl
index 2c50b957578ec..6ac3feeb1ea03 100644
--- a/clang/test/SemaHLSL/Types/BuiltinMatrix/MatrixImplicitTruncCastWarnings.hlsl
+++ b/clang/test/SemaHLSL/Types/BuiltinMatrix/MatrixImplicitTruncCastWarnings.hlsl
@@ -2,43 +2,43 @@
export int3x4 trunc_cast(int4x4 i44) {
int3x4 i34 = i44;
- // expected-warning at -1{{implicit conversion truncates matrix: 'int4x4' (aka 'matrix<int, 4, 4>') to 'matrix<int, 3, 4>'}}
+ // expected-warning at -1{{implicit conversion truncates matrix: 'int4x4' (aka 'matrix<int, 4, 4>') to 'int3x4' (aka 'matrix<int, 3, 4>')}}
return i34;
}
export int4x3 trunc_cast0(int4x4 i44) {
int4x3 i43 = i44;
- // expected-warning at -1{{implicit conversion truncates matrix: 'int4x4' (aka 'matrix<int, 4, 4>') to 'matrix<int, 4, 3>'}}
+ // expected-warning at -1{{implicit conversion truncates matrix: 'int4x4' (aka 'matrix<int, 4, 4>') to 'int4x3' (aka 'matrix<int, 4, 3>')}}
return i43;
}
export int3x3 trunc_cast1(int4x4 i44) {
int3x3 i33 = i44;
- // expected-warning at -1{{implicit conversion truncates matrix: 'int4x4' (aka 'matrix<int, 4, 4>') to 'matrix<int, 3, 3>'}}
+ // expected-warning at -1{{implicit conversion truncates matrix: 'int4x4' (aka 'matrix<int, 4, 4>') to 'int3x3' (aka 'matrix<int, 3, 3>')}}
return i33;
}
export int3x2 trunc_cast2(int4x4 i44) {
int3x2 i32 = i44;
- // expected-warning at -1{{implicit conversion truncates matrix: 'int4x4' (aka 'matrix<int, 4, 4>') to 'matrix<int, 3, 2>'}}
+ // expected-warning at -1{{implicit conversion truncates matrix: 'int4x4' (aka 'matrix<int, 4, 4>') to 'int3x2' (aka 'matrix<int, 3, 2>')}}
return i32;
}
export int2x3 trunc_cast3(int4x4 i44) {
int2x3 i23 = i44;
- // expected-warning at -1{{implicit conversion truncates matrix: 'int4x4' (aka 'matrix<int, 4, 4>') to 'matrix<int, 2, 3>'}}
+ // expected-warning at -1{{implicit conversion truncates matrix: 'int4x4' (aka 'matrix<int, 4, 4>') to 'int2x3' (aka 'matrix<int, 2, 3>')}}
return i23;
}
export int2x2 trunc_cast4(int4x4 i44) {
int2x2 i22 = i44;
- // expected-warning at -1{{implicit conversion truncates matrix: 'int4x4' (aka 'matrix<int, 4, 4>') to 'matrix<int, 2, 2>'}}
+ // expected-warning at -1{{implicit conversion truncates matrix: 'int4x4' (aka 'matrix<int, 4, 4>') to 'int2x2' (aka 'matrix<int, 2, 2>')}}
return i22;
}
export int2x1 trunc_cast5(int4x4 i44) {
int2x1 i21 = i44;
- // expected-warning at -1{{implicit conversion truncates matrix: 'int4x4' (aka 'matrix<int, 4, 4>') to 'matrix<int, 2, 1>'}}
+ // expected-warning at -1{{implicit conversion truncates matrix: 'int4x4' (aka 'matrix<int, 4, 4>') to 'int2x1' (aka 'matrix<int, 2, 1>')}}
return i21;
}
>From a036f2502bb619a9f7fa261168b8f8ef1f676fcb Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzonlotfi at microsoft.com>
Date: Tue, 2 Jun 2026 10:36:51 -0400
Subject: [PATCH 8/8] address pr comments
---
clang/include/clang/Basic/Attr.td | 1 -
clang/lib/Sema/SemaHLSL.cpp | 2 ++
clang/test/SemaHLSL/matrix_layout_attr.hlsl | 3 +++
3 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 782aec69622ff..e53d6dd20f824 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5305,7 +5305,6 @@ def HLSLColumnMajor : TypeAttr {
let LangOpts = [HLSL];
let Documentation = [HLSLMatrixLayoutDocs];
}
-def : MutualExclusions<[HLSLRowMajor, HLSLColumnMajor]>;
def RandomizeLayout : InheritableAttr {
let Spellings = [GCC<"randomize_layout">];
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 70e0feab29e8f..42621ae87da19 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2745,12 +2745,14 @@ Attr *SemaHLSL::buildMatrixLayoutTypeAttr(QualType T, const ParsedAttr &AL) {
if (ExistingKind == AttrK) {
Diag(AL.getLoc(), diag::warn_duplicate_attribute_exact)
<< AL.getAttrName();
+ Diag(AL.getLoc(), diag::note_previous_attribute);
return nullptr;
}
IdentifierInfo *ExistingII = &Ctx.Idents.get(
ExistingKind == attr::HLSLRowMajor ? "row_major" : "column_major");
Diag(AL.getLoc(), diag::err_hlsl_matrix_layout_conflict)
<< AL.getAttrName() << ExistingII;
+ Diag(AL.getLoc(), diag::note_conflicting_attribute);
AL.setInvalid();
return nullptr;
}
diff --git a/clang/test/SemaHLSL/matrix_layout_attr.hlsl b/clang/test/SemaHLSL/matrix_layout_attr.hlsl
index a5c3cc38027b0..3e953f07557e6 100644
--- a/clang/test/SemaHLSL/matrix_layout_attr.hlsl
+++ b/clang/test/SemaHLSL/matrix_layout_attr.hlsl
@@ -102,13 +102,16 @@ struct S2 {
typedef row_major float ScalarRM;
// Invalid: conflicting row_major and column_major on same decl.
+// expected-note at +2 {{conflicting attribute is here}}
// expected-error at +1 {{'column_major' and 'row_major' attributes are not compatible}}
row_major column_major float3x3 conflict_mat;
// Invalid: duplicate row_major.
+// expected-note at +2 {{previous attribute is here}}
// expected-warning at +1 {{attribute 'row_major' is already applied}}
row_major row_major float3x3 dup_rm_mat;
// Invalid: duplicate column_major.
+// expected-note at +2 {{previous attribute is here}}
// expected-warning at +1 {{attribute 'column_major' is already applied}}
column_major column_major float4x4 dup_cm_mat;
More information about the cfe-commits
mailing list