[clang] [HLSL][Matrix] Add Matrix Layout Keywords (PR #192284)
Farzon Lotfi via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 15 09:33:30 PDT 2026
https://github.com/farzonl created https://github.com/llvm/llvm-project/pull/192284
fixes #192263
HLSL allows per-declaration matrix layout overrides via the row_major and column_major keywords. Prior to this commit, matrix layout was only controlled globally via the -fmatrix-memory-layout command-line flag (LangOptions::DefaultMatrixMemoryLayout). This commit adds the parsing and semantic analysis infrastructure needed to support per-declaration layout, matching DXC behavior.
Assisted-by: Claude Opus 4.6
>From 44d2b31964ae51de8f8ec5597cb860270b9e4ee8 Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzonlotfi at microsoft.com>
Date: Tue, 14 Apr 2026 22:46:10 -0400
Subject: [PATCH] [HLSL][Matrix] Add Matrix Layout Keywords
fixes #192263
HLSL allows per-declaration matrix layout overrides via the row_major and column_major keywords. Prior to this commit, matrix layout was only controlled globally via the -fmatrix-memory-layout command-line flag (LangOptions::DefaultMatrixMemoryLayout). This commit adds the parsing and semantic analysis infrastructure needed to support per-declaration layout, matching DXC behavior.
---
clang/include/clang/Basic/Attr.td | 7 ++
clang/include/clang/Basic/AttrDocs.td | 16 +++++
.../clang/Basic/DiagnosticSemaKinds.td | 4 ++
clang/include/clang/Basic/TokenKinds.def | 2 +
clang/include/clang/Sema/SemaHLSL.h | 1 +
clang/lib/Parse/ParseDecl.cpp | 9 ++-
clang/lib/Sema/SemaDeclAttr.cpp | 3 +
clang/lib/Sema/SemaHLSL.cpp | 30 +++++++++
clang/test/AST/HLSL/matrix_layout_attr.hlsl | 24 +++++++
.../test/Sema/hlsl_matrix_layout_non_hlsl.cpp | 13 ++++
clang/test/SemaHLSL/matrix_layout_attr.hlsl | 65 +++++++++++++++++++
11 files changed, 171 insertions(+), 3 deletions(-)
create mode 100644 clang/test/AST/HLSL/matrix_layout_attr.hlsl
create mode 100644 clang/test/Sema/hlsl_matrix_layout_non_hlsl.cpp
create mode 100644 clang/test/SemaHLSL/matrix_layout_attr.hlsl
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 328e70b3ed900..7c77f36a0c91e 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5269,6 +5269,13 @@ def HLSLVkLocation : HLSLAnnotationAttr {
let Documentation = [HLSLVkLocationDocs];
}
+def HLSLMatrixLayout : InheritableAttr {
+ let Spellings = [CustomKeyword<"row_major">, CustomKeyword<"column_major">];
+ let Subjects = SubjectList<[Var, TypedefName, Field]>;
+ let LangOpts = [HLSL];
+ let Documentation = [HLSLMatrixLayoutDocs];
+}
+
def RandomizeLayout : InheritableAttr {
let Spellings = [GCC<"randomize_layout">];
let Subjects = SubjectList<[Record]>;
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 2ffbecfc2366b..c06431dae5224 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -9228,6 +9228,22 @@ This attribute maps to the 'Location' SPIR-V decoration.
}];
}
+def HLSLMatrixLayoutDocs : Documentation {
+ let Category = DocCatVariable;
+ let Content = [{
+The ``row_major`` and ``column_major`` keywords specify the memory layout
+of an HLSL matrix type.
+
+* ``row_major``: Matrices are stored in memory row-by-row.
+* ``column_major``: Matrices are stored in memory column-by-column (default).
+
+Example:
+.. code-block:: hlsl
+
+ row_major float2x2 myMatrix;
+ }];
+}
+
def WebAssemblyFuncrefDocs : Documentation {
let Category = DocCatType;
let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 6d2fae551566f..4bd48c6209752 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13557,6 +13557,10 @@ def err_hlsl_attr_invalid_ast_node : Error<
"attribute %0 only applies to %1">;
def err_hlsl_attr_incompatible
: Error<"%0 attribute is not compatible with %1 attribute">;
+def err_hlsl_matrix_layout_non_matrix
+ : Error<"%0 attribute can only be applied to a matrix type">;
+def err_hlsl_matrix_layout_conflict
+ : Error<"%0 and %1 attributes are not compatible">;
def err_hlsl_entry_shader_attr_mismatch : Error<
"%0 attribute on entry function does not match the target profile">;
diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def
index 8b9f613037718..3ea29b6d0935b 100644
--- a/clang/include/clang/Basic/TokenKinds.def
+++ b/clang/include/clang/Basic/TokenKinds.def
@@ -680,6 +680,8 @@ KEYWORD(groupshared , KEYHLSL)
KEYWORD(in , KEYHLSL)
KEYWORD(inout , KEYHLSL)
KEYWORD(out , KEYHLSL)
+KEYWORD(row_major , KEYHLSL)
+KEYWORD(column_major , KEYHLSL)
// HLSL Intangible Types
#define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) KEYWORD(Name, KEYHLSL)
#include "clang/Basic/HLSLIntangibleTypes.def"
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index edb7a8202a80b..980d8bd224685 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -176,6 +176,7 @@ 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 handleResourceTypeAttr(QualType T, const ParsedAttr &AL);
template <typename T>
diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp
index e2ac86bc5e064..4fdcd8bb7c129 100644
--- a/clang/lib/Parse/ParseDecl.cpp
+++ b/clang/lib/Parse/ParseDecl.cpp
@@ -1067,7 +1067,8 @@ void Parser::ParseOpenCLQualifiers(ParsedAttributes &Attrs) {
}
bool Parser::isHLSLQualifier(const Token &Tok) const {
- return Tok.is(tok::kw_groupshared);
+ return Tok.is(tok::kw_groupshared) || Tok.is(tok::kw_row_major) ||
+ Tok.is(tok::kw_column_major);
}
void Parser::ParseHLSLQualifiers(ParsedAttributes &Attrs) {
@@ -4631,7 +4632,8 @@ void Parser::ParseDeclarationSpecifiers(
case tok::kw___read_write:
ParseOpenCLQualifiers(DS.getAttributes());
break;
-
+ case tok::kw_row_major:
+ case tok::kw_column_major:
case tok::kw_groupshared:
case tok::kw_in:
case tok::kw_inout:
@@ -4639,7 +4641,6 @@ void Parser::ParseDeclarationSpecifiers(
// NOTE: ParseHLSLQualifiers will consume the qualifier token.
ParseHLSLQualifiers(DS.getAttributes());
continue;
-
#define HLSL_INTANGIBLE_TYPE(Name, Id, SingletonId) \
case tok::kw_##Name: \
isInvalid = DS.SetTypeSpecType(DeclSpec::TST_##Name, Loc, PrevSpec, \
@@ -5748,6 +5749,8 @@ bool Parser::isTypeSpecifierQualifier(const Token &Tok) {
case tok::kw_in:
case tok::kw_inout:
case tok::kw_out:
+ case tok::kw_row_major:
+ case tok::kw_column_major:
return getLangOpts().HLSL;
}
}
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 8a856215a9627..ec90e736eb039 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -8052,6 +8052,9 @@ 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 a943303149931..dac04a1d87c1e 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -2698,6 +2698,36 @@ void SemaHLSL::handleParamModifierAttr(Decl *D, const ParsedAttr &AL) {
D->addAttr(NewAttr);
}
+void SemaHLSL::handleMatrixLayoutAttr(Decl *D, const ParsedAttr &AL) {
+ // row_major and column_major are only valid on matrix types.
+ 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() && !Ty->isConstantMatrixType()) {
+ Diag(AL.getLoc(), diag::err_hlsl_matrix_layout_non_matrix)
+ << 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 {
+ Diag(AL.getLoc(), diag::warn_duplicate_attribute) << AL.getAttrName();
+ Diag(Existing->getLoc(), diag::note_previous_attribute);
+ }
+ return;
+ }
+
+ D->addAttr(::new (getASTContext()) HLSLMatrixLayoutAttr(getASTContext(), AL));
+}
+
namespace {
/// This class implements HLSL availability diagnostics for default
diff --git a/clang/test/AST/HLSL/matrix_layout_attr.hlsl b/clang/test/AST/HLSL/matrix_layout_attr.hlsl
new file mode 100644
index 0000000000000..f1c24a3b5a954
--- /dev/null
+++ b/clang/test/AST/HLSL/matrix_layout_attr.hlsl
@@ -0,0 +1,24 @@
+// 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
+row_major float3x3 rm_mat;
+
+// CHECK: VarDecl {{.*}} cm_mat 'hlsl_constant float4x4'
+// CHECK-NEXT: HLSLMatrixLayoutAttr {{.*}} column_major
+column_major float4x4 cm_mat;
+
+// CHECK: CXXRecordDecl {{.*}} struct S definition
+struct S {
+ // CHECK: FieldDecl {{.*}} m1 'float2x2'
+ // CHECK-NEXT: HLSLMatrixLayoutAttr {{.*}} row_major
+ row_major float2x2 m1;
+ // CHECK: FieldDecl {{.*}} m2 'float3x3'
+ // CHECK-NEXT: HLSLMatrixLayoutAttr {{.*}} column_major
+ column_major float3x3 m2;
+};
+
+// CHECK: TypedefDecl {{.*}} RM44 'float4x4'
+// CHECK: HLSLMatrixLayoutAttr {{.*}} row_major
+typedef row_major float4x4 RM44;
diff --git a/clang/test/Sema/hlsl_matrix_layout_non_hlsl.cpp b/clang/test/Sema/hlsl_matrix_layout_non_hlsl.cpp
new file mode 100644
index 0000000000000..b7510c5812137
--- /dev/null
+++ b/clang/test/Sema/hlsl_matrix_layout_non_hlsl.cpp
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -triple x86_64-pc-linux -std=c++17 -fsyntax-only -verify -x c++ %s
+// expected-no-diagnostics
+
+// row_major and column_major are HLSL-only keywords.
+// In non-HLSL modes, they should not be treated as keywords
+// and can be used as regular identifiers.
+int row_major = 1;
+int column_major = 2;
+
+void foo() {
+ int row_major = 10;
+ int column_major = 20;
+}
diff --git a/clang/test/SemaHLSL/matrix_layout_attr.hlsl b/clang/test/SemaHLSL/matrix_layout_attr.hlsl
new file mode 100644
index 0000000000000..816d465a50d52
--- /dev/null
+++ b/clang/test/SemaHLSL/matrix_layout_attr.hlsl
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -std=hlsl202x -verify %s
+
+// Valid uses: row_major and column_major on matrix types.
+row_major float3x3 rm_mat;
+column_major float4x4 cm_mat;
+
+row_major int2x3 rm_int_mat;
+column_major bool2x2 cm_bool_mat;
+
+// Valid: on struct fields with matrix type.
+struct S {
+ row_major float2x2 mat1;
+ column_major float3x3 mat2;
+};
+
+// Valid: typedef of a matrix type.
+typedef row_major float4x4 RowMajorFloat4x4;
+typedef column_major float4x4 ColMajorFloat4x4;
+
+// Invalid: scalar types.
+// expected-error at +1 {{'row_major' attribute can only be applied to a matrix type}}
+row_major float f;
+// expected-error at +1 {{'column_major' attribute can only be applied to a matrix type}}
+column_major int i;
+
+// Invalid: vector types.
+// expected-error at +1 {{'row_major' attribute can only be applied to a matrix type}}
+row_major float3 v;
+// expected-error at +1 {{'column_major' attribute can only be applied to a matrix type}}
+column_major int4 iv;
+
+// Invalid: array types.
+// expected-error at +1 {{'row_major' attribute can only be applied to a matrix type}}
+row_major float arr[10];
+
+// Invalid: struct types.
+// expected-error at +1 {{'column_major' attribute can only be applied to a matrix type}}
+column_major S s;
+
+// Invalid: struct field with non-matrix type.
+struct S2 {
+ // expected-error at +1 {{'row_major' attribute can only be applied to a matrix type}}
+ row_major float x;
+ // expected-error at +1 {{'column_major' attribute can only be applied to a matrix type}}
+ column_major int y;
+};
+
+// Invalid: typedef of non-matrix type.
+// expected-error at +1 {{'row_major' attribute can only be applied to a matrix type}}
+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