[RFC] [Clang] Use column-major matrix SSA in every language mode

Context RFC: RFC: Represent HLSL Matrix Layout in the Clang AST

The Problem

#230638 exposed an issue in C\C++ that HLSL recently solved. Clang currently allows the selected memory layout to leak into the representation and interpretation of matrix values. As a result, every operation must determine whether its vector operand is a row-major memory image or a logical matrix value.

That ambiguity is already is a concrete problems in CIR. The discussion in #230600 identified exactly where this becomes painful:

  • cir.matrix.insert and cir.matrix.extract are pure operations on matrix values.
  • CIR loads a matrix from memory into a !cir.matrix value before applying these operations.
  • During LLVM lowering, the operations currently need to know the layout of the memory from which that value originated in order to calculate a flat vector index.
  • That forces a memory-layout concern into otherwise representation-independent SSA operations.
  • If CIR is serialized and lowered separately, we would need to preserve the frontend option or attach layout provenance to the IR.

Proposed Solution

The HLSL solution C\C++ should adopt

Matrix SSA values are always canonical column-major logical values. Row-major and column-major are properties of storage, not properties of matrix values.

Under this model:

  • Whole-matrix loads convert from the selected physical layout into canonical column-major SSA.
  • Whole-matrix stores convert canonical SSA back into the selected physical layout.
  • Matrix arithmetic, transpose, extract, insert, and function-local SSA all operate on one representation.
  • Direct physical element accesses remain layout-aware because they actually index memory.
  • Explicit column-major load/store builtins retain their explicitly requested memory semantics.
  • LLVM matrix intrinsics are always lowered using their documented column-major interpretation.

This directly resolves the concern raised in #230638. cir.matrix.insert no longer needs a module-level layout setting or knowledge of where its operand originated.

CC: @fhahn @erichkeane @andykaylor @anemet @beanz @Icohedron

1 Like

I couldn’t find a case where this is actively a problem in Clang without CIR. That’s a result of LLVM IR not having a first-class matrix type. The problem I reported in #230638 was visible without CIR. When generating LLVM IR, Clang handles writes to a single matrix element by loading the entire matrix (as a vector) without regard to memory layout, using a memory-layout aware calculation to find the index of the desired element within the vector, using insertelement to write the element, and then writing the vector back to memory. The problem, which has since been fixed, was that we weren’t considering the memory layout when calculating the index for corresponding reads. Now that it’s fixed, the value is loaded as a vector and there’s no way to tell from the IR that it is “really” a matrix and so it doesn’t matter what the memory layout of the matrix was, but if anyone wanted to access a different element of the matrix, they’d need to know the memory layout.

The problem becomes more acute for CIR because we separate the load from the insert/extract and want to represent this as a first-class matrix type in between. I think the LLVM IR matrix intrinsics must be doing something when they are expanded to handle the memory layout, so that has the same basic problem that @farzonl described with an SSA value needing to be aware of the memory layout.

The cost, I think, would be having to transpose the data at every load and store for row-major memory layout. That would go away in optimization in some cases, but maybe not all?