Skip to content

Commit fae15b8

Browse files
authored
Add HLSL Matrix expression (#357)
fixes #354 ## Purpose * Add a dedicated matrix swizzle expression type to Clang’s AST that can represent HLSL matrix component access/sizzle constructs. * Ensure the new expression type preserves exact token spelling and correct per-component source location information. * Provide a working plan for how this expression type will be integrated into parsing, AST representation, and later compilation stages. ## Goals * Support HLSL matrix swizzle accessors of the form Base._mij_mkl_… where each suffix identifies one or more matrix elements (row, column). * Integrate cleanly into Clang with minimal HLSL specific changes needed for Sema (though obviously some matrix-specific logic is required hopefully this can be useful for the existing clang matrix type so we don’t pollute `ExtVectorElementExpr` HLSL LangOpts). ## Non-Goals * Change the semantics of vector swizzles for ExtVectorElementExpr. * Support arbitrary new syntax beyond HLSL matrix-swizzle style (i.e., no attempt to introduce fully generalized arbitrary multi-dimensional swizzle syntax). * Modify existing vector swizzle syntax or semantics.
1 parent 2d56746 commit fae15b8

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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

Comments
 (0)