https://github.com/inbelic created 
https://github.com/llvm/llvm-project/pull/233495

Updates the emission of semantics with matrix values now that we have updated 
its representation in the AST.

Resolves https://github.com/llvm/llvm-project/issues/143523

Assisted by: ChatGPT Sol 6.1

>From 48b0a0bbf8ed385aad99dd45cfdb3a45671386b8 Mon Sep 17 00:00:00 2001
From: Finn Plummer <[email protected]>
Date: Thu, 15 Oct 2026 21:45:28 +0000
Subject: [PATCH] [CGHLSL] Account for matrices in semantics

---
 clang/lib/CodeGen/CGHLSLRuntime.cpp           |  62 ++++++--
 .../semantics/semantic.matrix.hlsl            | 140 ++++++++++++++++++
 2 files changed, 186 insertions(+), 16 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/semantics/semantic.matrix.hlsl

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 35a31942c3b59..beaab53621203 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -24,6 +24,7 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/HLSLResource.h"
+#include "clang/AST/MatrixUtils.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/DiagnosticDriver.h"
@@ -1240,9 +1241,20 @@ void CGHLSLRuntime::emitSPIRVUserSemanticStore(
 namespace {
 // Describes how a semantic leaf lowers to signature rows
 struct SemanticShape {
-  SmallVector<unsigned> Dimensions; // Empty dims denotes a scalar
+  SmallVector<unsigned> Dimensions; // Arrays followed by matrix major vectors.
   unsigned Cols;
   QualType RowType;
+  bool IsMatrix = false;
+  bool IsRowMajor = false;
+
+  // Direct matrix arguments/results use column-major flattened vectors even
+  // when their signature (and aggregate memory representation) is row-major.
+  unsigned getMatrixElementIndex(unsigned Row, unsigned Col) const {
+    assert(IsMatrix);
+    unsigned MatrixRow = Row % Dimensions.back();
+    return IsRowMajor ? Col * Dimensions.back() + MatrixRow
+                      : MatrixRow * Cols + Col;
+  }
 
   unsigned getNumRows() const {
     unsigned Rows = 1;
@@ -1273,8 +1285,8 @@ static QualType getSemanticLeafType(const 
clang::DeclaratorDecl *Decl) {
   return Decl->getType();
 }
 
-// Walks through the surrounding constant array types of \p Ty, collecting 
their
-// dimensions until reaching a scalar, vector, or matrix leaf.
+// Collects array dimensions and, for a matrix leaf, its major-axis dimension.
+// RowType is the scalar or vector transferred by each signature load/store.
 static SemanticShape getSemanticShape(ASTContext &Ctx, QualType Ty) {
   SemanticShape Shape{{}, 1, Ty};
   while (const ConstantArrayType *CAT =
@@ -1287,10 +1299,12 @@ static SemanticShape getSemanticShape(ASTContext &Ctx, 
QualType Ty) {
     Shape.Cols = VT->getNumElements();
   } else if (const auto *MT =
                  Shape.RowType->getAs<clang::ConstantMatrixType>()) {
-    // FIXME: a matrix leaf lowers to one row per matrix row but if 
column_major
-    // is specified we transpose the num rows and num cols, this depends on
-    // #211977 to resolve
-    Shape.Cols = MT->getNumColumns();
+    Shape.IsMatrix = true;
+    Shape.IsRowMajor = isMatrixRowMajor(Ctx.getLangOpts(), Shape.RowType);
+    Shape.Dimensions.push_back(Shape.IsRowMajor ? MT->getNumRows()
+                                              : MT->getNumColumns());
+    Shape.Cols = Shape.IsRowMajor ? MT->getNumColumns() : MT->getNumRows();
+    Shape.RowType = Ctx.getExtVectorType(MT->getElementType(), Shape.Cols);
   }
 
   return Shape;
@@ -1300,8 +1314,6 @@ static llvm::dxil::ElementType 
getSignatureComponentType(CodeGenModule &CGM,
                                                          QualType Ty) {
   if (const auto *VT = Ty->getAs<clang::VectorType>())
     Ty = VT->getElementType();
-  else if (const auto *MT = Ty->getAs<clang::ConstantMatrixType>())
-    Ty = MT->getElementType();
 
   llvm::Type *IRTy = CGM.getTypes().ConvertTypeForMem(Ty);
   bool IsSigned = Ty->isSignedIntegerOrEnumerationType();
@@ -1351,13 +1363,15 @@ llvm::Value *CGHLSLRuntime::emitDXILUserSemanticLoad(
     OB.emplace_back("convergencectrl", bundleArgs);
   }
 
-  llvm::Type *LeafTy = CGM.getTypes().ConvertType(Shape.RowType);
+  bool IsMatrixValue = Shape.IsMatrix && Type->isVectorTy();
+  llvm::Type *LeafTy = Shape.IsMatrix && !IsMatrixValue
+                           ? RowTy
+                           : CGM.getTypes().ConvertType(Shape.RowType);
   llvm::Value *Result = llvm::PoisonValue::get(Type);
 
   const unsigned NumRows = Shape.getNumRows();
 
   for (unsigned Row = 0; Row < NumRows; ++Row) {
-    SmallVector<unsigned> Indices = Shape.getArrayIndicesForRow(Row);
     std::array<Value *, 4> Args{
         /*SigElementId=*/B.getInt32(SigId),
         /*RowIndex=*/B.getInt32(Row),
@@ -1374,8 +1388,16 @@ llvm::Value *CGHLSLRuntime::emitDXILUserSemanticLoad(
           Value, llvm::Constant::getNullValue(Value->getType()), "loadedv");
     }
 
-    Result =
-        Indices.empty() ? Value : B.CreateInsertValue(Result, Value, Indices);
+    if (IsMatrixValue) {
+      for (unsigned Col = 0; Col < Shape.Cols; ++Col)
+        Result = B.CreateInsertElement(
+            Result, B.CreateExtractElement(Value, Col),
+            Shape.getMatrixElementIndex(Row, Col));
+    } else {
+      SmallVector<unsigned> Indices = Shape.getArrayIndicesForRow(Row);
+      Result =
+          Indices.empty() ? Value : B.CreateInsertValue(Result, Value, 
Indices);
+    }
   }
   return Result;
 }
@@ -1404,11 +1426,19 @@ void 
CGHLSLRuntime::emitDXILUserSemanticStore(llvm::IRBuilder<> &B,
     OB.emplace_back("convergencectrl", bundleArgs);
   }
 
+  bool IsMatrixValue = Shape.IsMatrix && Source->getType()->isVectorTy();
   const unsigned NumRows = Shape.getNumRows();
   for (unsigned Row = 0; Row < NumRows; ++Row) {
-    SmallVector<unsigned> Indices = Shape.getArrayIndicesForRow(Row);
-    llvm::Value *Val =
-        Indices.empty() ? Source : B.CreateExtractValue(Source, Indices);
+    llvm::Value *Val;
+    if (IsMatrixValue) {
+      SmallVector<int, 4> Mask;
+      for (unsigned Col = 0; Col < Shape.Cols; ++Col)
+        Mask.push_back(Shape.getMatrixElementIndex(Row, Col));
+      Val = B.CreateShuffleVector(Source, Mask);
+    } else {
+      SmallVector<unsigned> Indices = Shape.getArrayIndicesForRow(Row);
+      Val = Indices.empty() ? Source : B.CreateExtractValue(Source, Indices);
+    }
 
     // Booleans use their memory representation in DXIL signatures, but direct
     // function results use their value representation.
diff --git a/clang/test/CodeGenHLSL/semantics/semantic.matrix.hlsl 
b/clang/test/CodeGenHLSL/semantics/semantic.matrix.hlsl
new file mode 100644
index 0000000000000..533e77381479f
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic.matrix.hlsl
@@ -0,0 +1,140 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -emit-llvm 
-finclude-default-header -disable-llvm-passes -fmatrix-memory-layout=row-major 
-o - %s | FileCheck %s --check-prefixes=CHECK,ROW
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -emit-llvm 
-finclude-default-header -disable-llvm-passes 
-fmatrix-memory-layout=column-major -o - %s | FileCheck %s 
--check-prefixes=CHECK,COLUMN
+
+// Explicit layout overrides the default. A row-major 2x3 matrix occupies two
+// signature rows, but direct arguments and returns are column-major vectors.
+[shader("vertex")]
+row_major float2x3 rowMatrix(row_major float2x3 m : RIN2) : ROUT4 {
+  return m;
+}
+
+// CHECK-LABEL: define void @rowMatrix()
+// CHECK: %[[R0:.*]] = call <3 x float> @llvm.dx.load.input.v3f32(i32 0, i32 
0, i8 0, i32 poison)
+// CHECK: %[[R00:.*]] = extractelement <3 x float> %[[R0]], i64 0
+// CHECK: %[[R1:.*]] = insertelement <6 x float> poison, float %[[R00]], i64 0
+// CHECK: %[[R01:.*]] = extractelement <3 x float> %[[R0]], i64 1
+// CHECK: %[[R2:.*]] = insertelement <6 x float> %[[R1]], float %[[R01]], i64 2
+// CHECK: %[[R02:.*]] = extractelement <3 x float> %[[R0]], i64 2
+// CHECK: %[[R3:.*]] = insertelement <6 x float> %[[R2]], float %[[R02]], i64 4
+// CHECK: %[[R4:.*]] = call <3 x float> @llvm.dx.load.input.v3f32(i32 0, i32 
1, i8 0, i32 poison)
+// CHECK: %[[R10:.*]] = extractelement <3 x float> %[[R4]], i64 0
+// CHECK: %[[R5:.*]] = insertelement <6 x float> %[[R3]], float %[[R10]], i64 1
+// CHECK: %[[R11:.*]] = extractelement <3 x float> %[[R4]], i64 1
+// CHECK: %[[R6:.*]] = insertelement <6 x float> %[[R5]], float %[[R11]], i64 3
+// CHECK: %[[R12:.*]] = extractelement <3 x float> %[[R4]], i64 2
+// CHECK: %[[R7:.*]] = insertelement <6 x float> %[[R6]], float %[[R12]], i64 5
+// CHECK: %[[RR:.*]] = call <6 x float> @{{.*}}rowMatrix{{.*}}(<6 x float> 
%[[R7]])
+// CHECK: %[[RO0:.*]] = shufflevector <6 x float> %[[RR]], <6 x float> poison, 
<3 x i32> <i32 0, i32 2, i32 4>
+// CHECK: call void @llvm.dx.store.output.v3f32(i32 0, i32 0, i8 0, <3 x 
float> %[[RO0]])
+// CHECK: %[[RO1:.*]] = shufflevector <6 x float> %[[RR]], <6 x float> poison, 
<3 x i32> <i32 1, i32 3, i32 5>
+// CHECK: call void @llvm.dx.store.output.v3f32(i32 0, i32 1, i8 0, <3 x 
float> %[[RO1]])
+
+[shader("vertex")]
+column_major float2x3 columnMatrix(column_major float2x3 m : CIN3) : COUT6 {
+  return m;
+}
+
+// CHECK-LABEL: define void @columnMatrix()
+// CHECK: call <2 x float> @llvm.dx.load.input.v2f32(i32 0, i32 0, i8 0, i32 
poison)
+// CHECK: insertelement <6 x float> poison, float %{{.*}}, i64 0
+// CHECK: insertelement <6 x float> %{{.*}}, float %{{.*}}, i64 1
+// CHECK: call <2 x float> @llvm.dx.load.input.v2f32(i32 0, i32 1, i8 0, i32 
poison)
+// CHECK: insertelement <6 x float> %{{.*}}, float %{{.*}}, i64 2
+// CHECK: insertelement <6 x float> %{{.*}}, float %{{.*}}, i64 3
+// CHECK: call <2 x float> @llvm.dx.load.input.v2f32(i32 0, i32 2, i8 0, i32 
poison)
+// CHECK: insertelement <6 x float> %{{.*}}, float %{{.*}}, i64 4
+// CHECK: insertelement <6 x float> %{{.*}}, float %{{.*}}, i64 5
+// CHECK: %[[CR:.*]] = call <6 x float> @{{.*}}columnMatrix
+// CHECK: %[[CO0:.*]] = shufflevector <6 x float> %[[CR]], <6 x float> poison, 
<2 x i32> <i32 0, i32 1>
+// CHECK: call void @llvm.dx.store.output.v2f32(i32 0, i32 0, i8 0, <2 x 
float> %[[CO0]])
+// CHECK: %[[CO1:.*]] = shufflevector <6 x float> %[[CR]], <6 x float> poison, 
<2 x i32> <i32 2, i32 3>
+// CHECK: call void @llvm.dx.store.output.v2f32(i32 0, i32 1, i8 0, <2 x 
float> %[[CO1]])
+// CHECK: %[[CO2:.*]] = shufflevector <6 x float> %[[CR]], <6 x float> poison, 
<2 x i32> <i32 4, i32 5>
+// CHECK: call void @llvm.dx.store.output.v2f32(i32 0, i32 2, i8 0, <2 x 
float> %[[CO2]])
+
+[shader("vertex")]
+float2x3 defaultMatrix(float2x3 m : DIN) : DOUT {
+  return m;
+}
+
+// CHECK-LABEL: define void @defaultMatrix()
+// ROW: call <3 x float> @llvm.dx.load.input.v3f32(i32 0, i32 1, i8 0, i32 
poison)
+// ROW-NOT: @llvm.dx.load.input
+// ROW: call void @llvm.dx.store.output.v3f32(i32 0, i32 1, i8 0,
+// COLUMN: call <2 x float> @llvm.dx.load.input.v2f32(i32 0, i32 2, i8 0, i32 
poison)
+// COLUMN-NOT: @llvm.dx.load.input
+// COLUMN: call void @llvm.dx.store.output.v2f32(i32 0, i32 2, i8 0,
+
+// Matrix rows extend the surrounding array dimensions. Aggregate matrices use
+// their memory representation; bool components remain i32 in that 
representation.
+struct Matrices {
+  row_major float2x3 r[2][2] : ARRAY3;
+  column_major bool2x3 b : BOOL9;
+  row_major int1x3 singleRow : SINGLE7;
+  column_major float3x1 singleColumn : COL6;
+};
+
+[shader("vertex")]
+Matrices aggregateMatrix(Matrices m) {
+  return m;
+}
+
+// CHECK-LABEL: define void @aggregateMatrix()
+// CHECK: %[[AR0:.*]] = call <3 x float> @llvm.dx.load.input.v3f32(i32 0, i32 
0, i8 0, i32 poison)
+// CHECK: insertvalue [2 x [2 x [2 x <3 x float>]]] poison, <3 x float> 
%[[AR0]], 0, 0, 0
+// CHECK: %[[AR7:.*]] = call <3 x float> @llvm.dx.load.input.v3f32(i32 0, i32 
7, i8 0, i32 poison)
+// CHECK: insertvalue [2 x [2 x [2 x <3 x float>]]] %{{.*}}, <3 x float> 
%[[AR7]], 1, 1, 1
+// CHECK: %[[AB0:.*]] = call <2 x i32> @llvm.dx.load.input.v2i32(i32 1, i32 0, 
i8 0, i32 poison)
+// CHECK: insertvalue [3 x <2 x i32>] poison, <2 x i32> %[[AB0]], 0
+// CHECK: %[[AB2:.*]] = call <2 x i32> @llvm.dx.load.input.v2i32(i32 1, i32 2, 
i8 0, i32 poison)
+// CHECK: insertvalue [3 x <2 x i32>] %{{.*}}, <2 x i32> %[[AB2]], 2
+// CHECK: call <3 x i32> @llvm.dx.load.input.v3i32(i32 2, i32 0, i8 0, i32 
poison)
+// CHECK: call <3 x float> @llvm.dx.load.input.v3f32(i32 3, i32 0, i8 0, i32 
poison)
+// CHECK: %[[AO0:.*]] = extractvalue [2 x [2 x [2 x <3 x float>]]] %{{.*}}, 0, 
0, 0
+// CHECK: call void @llvm.dx.store.output.v3f32(i32 0, i32 0, i8 0, <3 x 
float> %[[AO0]])
+// CHECK: %[[AO7:.*]] = extractvalue [2 x [2 x [2 x <3 x float>]]] %{{.*}}, 1, 
1, 1
+// CHECK: call void @llvm.dx.store.output.v3f32(i32 0, i32 7, i8 0, <3 x 
float> %[[AO7]])
+// CHECK: %[[BO2:.*]] = extractvalue [3 x <2 x i32>] %{{.*}}, 2
+// CHECK: call void @llvm.dx.store.output.v2i32(i32 1, i32 2, i8 0, <2 x i32> 
%[[BO2]])
+
+[shader("vertex")]
+row_major bool2x3 boolMatrix(row_major bool2x3 m : BIN) : BOUT {
+  return m;
+}
+
+// CHECK-LABEL: define void @boolMatrix()
+// CHECK: %[[BI0:.*]] = call <3 x i32> @llvm.dx.load.input.v3i32(i32 0, i32 0, 
i8 0, i32 poison)
+// CHECK: icmp ne <3 x i32> %[[BI0]], zeroinitializer
+// CHECK: %[[BI1:.*]] = call <3 x i32> @llvm.dx.load.input.v3i32(i32 0, i32 1, 
i8 0, i32 poison)
+// CHECK: icmp ne <3 x i32> %[[BI1]], zeroinitializer
+// CHECK: %[[BR:.*]] = call <6 x i1> @{{.*}}boolMatrix
+// CHECK: %[[BS0:.*]] = shufflevector <6 x i1> %[[BR]], <6 x i1> poison, <3 x 
i32> <i32 0, i32 2, i32 4>
+// CHECK: %[[BZ0:.*]] = zext <3 x i1> %[[BS0]] to <3 x i32>
+// CHECK: call void @llvm.dx.store.output.v3i32(i32 0, i32 0, i8 0, <3 x i32> 
%[[BZ0]])
+// CHECK: %[[BS1:.*]] = shufflevector <6 x i1> %[[BR]], <6 x i1> poison, <3 x 
i32> <i32 1, i32 3, i32 5>
+// CHECK: %[[BZ1:.*]] = zext <3 x i1> %[[BS1]] to <3 x i32>
+// CHECK: call void @llvm.dx.store.output.v3i32(i32 0, i32 1, i8 0, <3 x i32> 
%[[BZ1]])
+
+// Check component types, row/column counts, and consecutive semantic indices.
+// CHECK-DAG: !{i32 0, !"RIN", i32 9, i32 0, ![[RI:[0-9]+]], i32 0, i32 2, i8 
3,
+// CHECK-DAG: ![[RI]] = !{i32 2, i32 3}
+// CHECK-DAG: !{i32 0, !"ROUT", i32 9, i32 0, ![[RO:[0-9]+]], i32 2, i32 2, i8 
3,
+// CHECK-DAG: ![[RO]] = !{i32 4, i32 5}
+// CHECK-DAG: !{i32 0, !"CIN", i32 9, i32 0, ![[CI:[0-9]+]], i32 0, i32 3, i8 
2,
+// CHECK-DAG: ![[CI]] = !{i32 3, i32 4, i32 5}
+// CHECK-DAG: !{i32 0, !"COUT", i32 9, i32 0, ![[CO:[0-9]+]], i32 2, i32 3, i8 
2,
+// CHECK-DAG: ![[CO]] = !{i32 6, i32 7, i32 8}
+// ROW-DAG: !{i32 0, !"DIN", i32 9, i32 0, !{{[0-9]+}}, i32 0, i32 2, i8 3,
+// ROW-DAG: !{i32 0, !"DOUT", i32 9, i32 0, !{{[0-9]+}}, i32 2, i32 2, i8 3,
+// COLUMN-DAG: !{i32 0, !"DIN", i32 9, i32 0, !{{[0-9]+}}, i32 0, i32 3, i8 2,
+// COLUMN-DAG: !{i32 0, !"DOUT", i32 9, i32 0, !{{[0-9]+}}, i32 2, i32 3, i8 2,
+// CHECK-DAG: !{i32 0, !"ARRAY", i32 9, i32 0, ![[AI:[0-9]+]], i32 0, i32 8, 
i8 3,
+// CHECK-DAG: !{i32 0, !"ARRAY", i32 9, i32 0, ![[AI]], i32 2, i32 8, i8 3,
+// CHECK-DAG: ![[AI]] = !{i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 
10}
+// CHECK-DAG: !{i32 1, !"BOOL", i32 5, i32 0, ![[BI:[0-9]+]], i32 0, i32 3, i8 
2,
+// CHECK-DAG: !{i32 1, !"BOOL", i32 5, i32 0, ![[BI]], i32 1, i32 3, i8 2,
+// CHECK-DAG: ![[BI]] = !{i32 9, i32 10, i32 11}
+// CHECK-DAG: !{i32 2, !"SINGLE", i32 4, i32 0, !{{[0-9]+}}, i32 0, i32 1, i8 
3,
+// CHECK-DAG: !{i32 2, !"SINGLE", i32 4, i32 0, !{{[0-9]+}}, i32 1, i32 1, i8 
3,
+// CHECK-DAG: !{i32 3, !"COL", i32 9, i32 0, !{{[0-9]+}}, i32 0, i32 1, i8 3,
+// CHECK-DAG: !{i32 3, !"COL", i32 9, i32 0, !{{[0-9]+}}, i32 2, i32 1, i8 3,

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

Reply via email to