|
| 1 | +--- |
| 2 | +title: 0036 - HLSL Matrix Element Expression |
| 3 | +params: |
| 4 | + authors: |
| 5 | + farzonl: Farzon Lotfi |
| 6 | +status: Accepted |
| 7 | +--- |
| 8 | + |
| 9 | +## Introduction |
| 10 | + |
| 11 | +HLSL supports matrix “element accessor” syntax that can name individual elements |
| 12 | +or swizzles of elements, for example: |
| 13 | + |
| 14 | +- `M._m00` (zero-based, one element) |
| 15 | +- `M._11` (one-based, one element) |
| 16 | +- `M._m00_m11` (zero-based swizzle) |
| 17 | +- `M._11_22_33` (one-based swizzle) |
| 18 | + |
| 19 | +To represent these in Clang’s AST we introduce `MatrixElementExpr`, an |
| 20 | +expression node that is structurally similar to `ExtVectorElementExpr`, but |
| 21 | +specialized for matrices. |
| 22 | + |
| 23 | +`MatrixElementExpr`: |
| 24 | + |
| 25 | +- Represents both scalar matrix element access and matrix swizzles. |
| 26 | +- Preserves the accessor spelling (e.g. `_m00_m11`) as a single identifier. |
| 27 | +- Implements the HLSL rules for 0-based (`_mRC`) vs 1-based (`_RC`) accessors. |
| 28 | +- Enforces l-value semantics (no duplicate components on assignment) analogous |
| 29 | + to vector swizzles. |
| 30 | + |
| 31 | +Unlike the earlier proposal for a dedicated `MatrixSwizzleExpr` with an |
| 32 | +explicit per-component list and per-component source locations, the final |
| 33 | +design: |
| 34 | + |
| 35 | +- Reuses the existing `ExtVectorElementExpr` machinery via a shared base class. |
| 36 | + - Per [Aaron Ballman's Feedback](https://discourse.llvm.org/t/rfc-extend-extvectorelementexpr-for-hlsl-matrix-accessors/88802/4) |
| 37 | + - ExtVectorElementExpr is migrated to use this base. |
| 38 | + - MatrixElementExpr is introduced as a sibling. |
| 39 | +- Keeps the AST node compact, storing only the base expression, the accessor |
| 40 | + identifier, and the accessor source location. |
| 41 | +- Computes row/column indices and duplicate information on demand. |
| 42 | + |
| 43 | +## Requirements for a MatrixElementExpr AST Node |
| 44 | + |
| 45 | +We want to represent access such as `M._m00_m01_m10` that can produce a vector |
| 46 | +of the matrix element type, as well as single-element cases like `M._m00`. |
| 47 | + |
| 48 | +### General |
| 49 | + |
| 50 | +- The node must be usable for: |
| 51 | + - Scalar element access: `float r = A._m00;` |
| 52 | + - Swizzle access for 1 to 4 elements: `float3 v = A._11_22_33;` |
| 53 | +- The accessor must follow the HLSL matrix rules: |
| 54 | + - Zero-based form: `_mRC` where `R` and `C` are decimal digits. |
| 55 | + - One-based form: `_RC` where `R` and `C` are decimal digits. |
| 56 | + - A swizzle is a `_`-separated sequence of those forms: |
| 57 | + - `_m00_m11`, `_11_22_33`, etc. |
| 58 | +- Swizzle length must be between 1 and 4 (inclusive). Invalid lengths should |
| 59 | + be rejected during semantic analysis. |
| 60 | + |
| 61 | +### L-value vs R-value semantics |
| 62 | + |
| 63 | +For l-value (assignment) cases: |
| 64 | + |
| 65 | +- The base must be a modifiable l-value matrix. |
| 66 | +- The swizzle must not contain duplicate element references (same `(row, col)` |
| 67 | + pair repeated) when used as a store destination. |
| 68 | + - Example (not allowed): `A._m00_m00 = float2(1, 2);` |
| 69 | +- Assignments with duplicate components should be rejected with an error akin |
| 70 | + to “matrix is not assignable (contains duplicate components)”. |
| 71 | + |
| 72 | +For r-value cases: |
| 73 | + |
| 74 | +- Reads are allowed even with duplicate components, analogous to vector |
| 75 | + swizzles. |
| 76 | + - Example (allowed): `float2 v = A._m00_m00;` |
| 77 | +- The number of components in the accessor determines the result type: |
| 78 | + - 1 component → scalar element type. |
| 79 | + - N components (2–4) → `vector<N, element-type>`. |
| 80 | + |
| 81 | +### Bounds and accessor validation |
| 82 | + |
| 83 | +Sema must validate that: |
| 84 | + |
| 85 | +- The accessor uses one of the supported forms (`_mRC` or `_RC`). |
| 86 | +- The indices are within bounds for the matrix type: |
| 87 | + - For zero-based accessors, `R` and `C` must be in `[0, rows-1]` and |
| 88 | + `[0, cols-1]`. |
| 89 | + - For one-based accessors, `R` and `C` must be in `[1, rows]` and |
| 90 | + `[1, cols]`. |
| 91 | +- Clear diagnostics are produced for: |
| 92 | + - Accessors that are lexically malformed (bad characters, wrong length, |
| 93 | + wrong prefix, mixed forms in a single accessor string). |
| 94 | + - Accessors that are syntactically correct but out-of-bounds for the |
| 95 | + given matrix type. |
| 96 | + - Swizzle lengths that are not in `[1, 4]`. |
| 97 | + |
| 98 | +## Implementation |
| 99 | + |
| 100 | +### AST Implementation |
| 101 | + |
| 102 | +We introduce a small CRTP base for element-like access expressions that are |
| 103 | +spelled via a single identifier and applied to a base expression: |
| 104 | + |
| 105 | +```cpp |
| 106 | +template <typename Derived> |
| 107 | +class ElementAccessExprBase : public Expr { |
| 108 | + Stmt *Base; |
| 109 | + IdentifierInfo *Accessor; |
| 110 | + SourceLocation AccessorLoc; |
| 111 | + |
| 112 | +protected: |
| 113 | + ElementAccessExprBase(StmtClass SC, QualType Ty, ExprValueKind VK, |
| 114 | + Expr *Base, IdentifierInfo &Accessor, |
| 115 | + SourceLocation Loc, ExprObjectKind OK) |
| 116 | + : Expr(SC, Ty, VK, OK), |
| 117 | + Base(Base), |
| 118 | + Accessor(&Accessor), |
| 119 | + AccessorLoc(Loc) { |
| 120 | + setDependence(computeDependence(static_cast<Derived *>(this))); |
| 121 | + } |
| 122 | + |
| 123 | + explicit ElementAccessExprBase(StmtClass SC, EmptyShell Empty) |
| 124 | + : Expr(SC, Empty) {} |
| 125 | + |
| 126 | +public: |
| 127 | + const Expr *getBase() const { return cast<Expr>(Base); } |
| 128 | + Expr *getBase() { return cast<Expr>(Base); } |
| 129 | + void setBase(Expr *E) { Base = E; } |
| 130 | + |
| 131 | + IdentifierInfo *getAccessor() const { return Accessor; } |
| 132 | + void setAccessor(IdentifierInfo *II) { Accessor = II; } |
| 133 | + |
| 134 | + SourceLocation getAccessorLoc() const { return AccessorLoc; } |
| 135 | + void setAccessorLoc(SourceLocation L) { AccessorLoc = L; } |
| 136 | + |
| 137 | + SourceLocation getBeginLoc() const { return getBase()->getBeginLoc(); } |
| 138 | + SourceLocation getEndLoc() const { return AccessorLoc; } |
| 139 | + |
| 140 | + /// Helpers implemented by the derived class: |
| 141 | + /// |
| 142 | + /// - unsigned getNumElements() const; |
| 143 | + /// - bool containsDuplicateElements() const; |
| 144 | + /// - void getEncodedElementAccess(SmallVectorImpl<uint32_t> &Elts) const; |
| 145 | +}; |
| 146 | +``` |
0 commit comments