[clang] [HLSL][Matrix] Add Matrix Layout Keywords (PR #192284)

Farzon Lotfi via cfe-commits cfe-commits at lists.llvm.org
Fri Apr 17 11:52:42 PDT 2026


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

>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 1/2] [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;

>From d13d87426fdd7cab095dc43a17c192a6aa6a8fdb Mon Sep 17 00:00:00 2001
From: Farzon Lotfi <farzonlotfi at microsoft.com>
Date: Fri, 17 Apr 2026 14:52:26 -0400
Subject: [PATCH 2/2] fix documentation issue

---
 clang/include/clang/Basic/AttrDocs.td | 1 +
 1 file changed, 1 insertion(+)

diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index c06431dae5224..608854fe38736 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -9230,6 +9230,7 @@ This attribute maps to the 'Location' SPIR-V decoration.
 
 def HLSLMatrixLayoutDocs : Documentation {
   let Category = DocCatVariable;
+  let Heading = "row_major, column_major";
   let Content = [{
 The ``row_major`` and ``column_major`` keywords specify the memory layout 
 of an HLSL matrix type. 



More information about the cfe-commits mailing list