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 <[email protected]> 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@+1 {{'row_major' attribute can only be applied to a matrix type}} +row_major float f; +// expected-error@+1 {{'column_major' attribute can only be applied to a matrix type}} +column_major int i; + +// Invalid: vector types. +// expected-error@+1 {{'row_major' attribute can only be applied to a matrix type}} +row_major float3 v; +// expected-error@+1 {{'column_major' attribute can only be applied to a matrix type}} +column_major int4 iv; + +// Invalid: array types. +// expected-error@+1 {{'row_major' attribute can only be applied to a matrix type}} +row_major float arr[10]; + +// Invalid: struct types. +// expected-error@+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@+1 {{'row_major' attribute can only be applied to a matrix type}} + row_major float x; + // expected-error@+1 {{'column_major' attribute can only be applied to a matrix type}} + column_major int y; +}; + +// Invalid: typedef of non-matrix type. +// expected-error@+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@+2 {{conflicting attribute is here}} +// expected-error@+1 {{'column_major' and 'row_major' attributes are not compatible}} +row_major column_major float3x3 conflict_mat; + +// Invalid: duplicate row_major. +// expected-note@+2 {{previous attribute is here}} +// expected-warning@+1 {{attribute 'row_major' is already applied}} +row_major row_major float3x3 dup_rm_mat; + +// Invalid: duplicate column_major. +// expected-note@+2 {{previous attribute is here}} +// expected-warning@+1 {{attribute 'column_major' is already applied}} +column_major column_major float4x4 dup_cm_mat; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
