Skip to content

Commit 9a4d97e

Browse files
authored
[0011] Resource element type validation (#69)
This spec describes how the compiler will validate resource element types. This spec is needed before the implementation, which will eventually solve llvm/llvm-project#75676
1 parent a18f368 commit 9a4d97e

1 file changed

Lines changed: 215 additions & 0 deletions

File tree

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
* Proposal: [0011](0011-resource-element-type-validation.md)
2+
* Author(s): [Joshua Batista](https://github.com/bob80905)
3+
* Sponsor: Joshua Batista
4+
* Status: **Under Consideration**
5+
* Impacted Project(s): (LLVM)
6+
* Issues: [#75676](https://github.com/llvm/llvm-project/issues/75676)
7+
8+
## Introduction
9+
Resources are often used in HLSL, with various resource element types.
10+
11+
For example:
12+
```
13+
RWBuffer<float> rwbuf: register(u0);
14+
```
15+
In this code, the element type is `float`, and the resource type is `RWBuffer`.
16+
`RWBuffer`, along with some other buffers and textures, fall under the "typed buffer"
17+
category. Other buffer resources are categorized as "raw buffers".
18+
Below is a description of resources that are considered "typed buffers" vs "raw buffers".
19+
* typed buffers
20+
* [RW|RasterizerOrdered]Buffer
21+
* [Feedback]Texture*
22+
* raw buffers
23+
* [Append|Consume|RW|RasterizerOrdered]StructuredBuffer
24+
* [RW]ByteAddressBuffer
25+
26+
There is a distinct set of rules that define valid element types for typed buffer resources
27+
and valid element types for `*StructuredBuffer` resources and `*ByteAddressBuffer` Load/Store operations.
28+
29+
Element types for typed buffer resources:
30+
* Are not intangible (e.g., isn't a resource type)
31+
* Must be vectors or scalars of arithmetic types, not bools nor enums nor arrays
32+
* Should be a scalar or homogenous vector of a floating-point or integer type, with a maximum of 4 components after translating 64-bit components into pairs of uint32_t components
33+
Element types for raw buffer resources:
34+
* Are not intangible (e.g., isn't a resource type)
35+
36+
If someone writes `RWBuffer<MyCustomType>` and MyCustomType is not a valid element type,
37+
there should be infrastructure to reject this element type and emit a message explaining
38+
why it was rejected as an element type.
39+
40+
## Motivation
41+
Currently, there is an allow list of valid element types where element types not on the list
42+
are rejected. It must be modified with respect to this spec. The allow list isn't
43+
broad enough, because user-defined types aren't allowed for raw buffer resources.
44+
Ideally, a user should be able to determine exactly how any user-defined type is invalid
45+
as an element type. Some system should be in place to more completely enforce the rules for
46+
valid and invalid element types, as well as provide useful information on why they are invalid.
47+
48+
For example, `RWBuffer<double4> b : register(u4);` will emit an error in DXC, but will not in
49+
clang-dxc, despite the fact that `double4` is an invalid element type for typed buffers.
50+
51+
## Proposed solution
52+
53+
The proposed solution is to modify the declaration of each resource declared in
54+
`clang\lib\Sema\HLSLExternalSemaSource.cpp` and insert into each representative
55+
AST node a concept. The AST node will be created as if the C++20 `concept` keyword
56+
was parsed and applied to the declaration. The concept will be used to validate the
57+
given element type, and will emit errors when the given element type is invalid.
58+
Although concepts are not currently supported in HLSL, we expect support to be
59+
added at some point in the future. Meanwhile, because LLVM does support concepts,
60+
we can make use of them when constructing the AST in Sema.
61+
62+
Two builtins will be used to validate typed buffer element types. Any resource
63+
element type may not be intangible, so the negation of `__builtin_hlsl_is_intangible`
64+
will be used for both typed and raw buffer resources.
65+
A new built-in, `__builtin_hlsl_typed_resource_element_compatible`, will be added in order
66+
to fully express the typed buffer constraint. This builtin will be placed within a
67+
concept constraint expression that is added to each AST node representing a typed
68+
buffer resource. The builtin is described below. Standard clang diagnostics for
69+
unsatisfied constraints will be used to report any invalid element types. Until
70+
concepts are formally supported by HLSL, the concepts and constraints will be
71+
expressed only in the AST via the HLSL external sema source.
72+
73+
## Detailed design
74+
75+
In `clang\lib\Sema\HLSLExternalSemaSource.cpp`, `RWBuffer` is defined, along with
76+
`RasterizerOrderedBuffer` and `StructuredBuffer`. It is at this point that the
77+
concept would be incorporated into these resource declarations. A concept representing
78+
the relevant constraints will be applied to each resource declaration. If a concept
79+
is not true for the given element type, a corresponding error message will be emitted.
80+
81+
The list of builtins to be used as type traits that will be available for
82+
concept definition are described below:
83+
| type trait | Description|
84+
|-|-|
85+
| `!__builtin_hlsl_is_intangible ` | An element type should be an arithmetic type, bool, enum, or a vector or matrix or UDT containing such types. This is equivalent to validating that the element type is not intangible. This will error when given an incomplete type. |
86+
| `__builtin_hlsl_typed_resource_element_compatible ` | A typed buffer element type should never have two different subelement types. Compatible typed buffer element types require at most 4 elements, and a total size of at most 16 bytes. The builtin will also disallow the element type if any of its constituent types are enums or bools. |
87+
88+
For typed buffers, `__builtin_hlsl_typed_resource_element_compatible` and
89+
`!__builtin_hlsl_is_intangible` needs to be true, while `!__builtin_hlsl_is_intangible` is all
90+
that's needed to validate element types for raw buffers.
91+
92+
### Examples of Element Type validation results:
93+
```
94+
struct oneInt {
95+
int i;
96+
};
97+
98+
struct twoInt {
99+
int aa;
100+
int ab;
101+
};
102+
103+
struct threeInts {
104+
oneInt o;
105+
twoInt t;
106+
};
107+
108+
struct oneFloat {
109+
float f;
110+
};
111+
struct notComplete;
112+
struct depthDiff {
113+
int i;
114+
oneInt o;
115+
oneFloat f;
116+
};
117+
118+
struct notHomogenous{
119+
int i;
120+
float f;
121+
};
122+
123+
struct EightElements {
124+
twoInt x[2];
125+
twoInt y[2];
126+
};
127+
128+
struct EightHalves {
129+
half x[8];
130+
};
131+
132+
struct intVec {
133+
int2 i;
134+
};
135+
136+
struct oneIntWithVec {
137+
int i;
138+
oneInt i2;
139+
int2 i3;
140+
};
141+
142+
struct weirdStruct {
143+
int i;
144+
intVec iv;
145+
};
146+
147+
RWBuffer<double2> r0; // valid - element type fits in 4 32-bit quantities
148+
RWBuffer<int> r1; // valid
149+
RWBuffer<float> r2; // valid
150+
RWBuffer<float4> r3; // valid
151+
RWBuffer<notComplete> r4; // invalid - the element type isn't complete, the definition is missing.
152+
// the type trait that would catch this is the negation of `__builtin_hlsl_is_intangible`
153+
RWBuffer<oneInt> r5; // valid - all leaf types are valid primitive types, and homogenous
154+
RWBuffer<oneFloat> r6; // valid
155+
RWBuffer<twoInt> r7; // valid
156+
RWBuffer<threeInts> r8; // valid
157+
RWBuffer<notHomogenous> r9; // invalid, all template type components must have the same type, DXC fails
158+
StructuredBuffer<notHomogenous> r9Structured; // valid
159+
RWBuffer<depthDiff> r10; // invalid, all template type components must have the same type, DXC fails
160+
RWBuffer<EightElements> r11; // invalid, > 4 elements and > 16 bytes, DXC fails
161+
// This would be caught by __builtin_hlsl_typed_resource_element_compatible
162+
StructuredBuffer<EightElements> r9Structured; // valid
163+
RWBuffer<EightHalves> r12; // invalid, > 4 elements, DXC fails
164+
// This would be caught by __builtin_hlsl_typed_resource_element_compatible
165+
StructuredBuffer<EightHalves> r12Structured; // valid
166+
RWBuffer<oneIntWithVec> r13; // valid
167+
RWBuffer<weirdStruct> r14; // valid
168+
RWBuffer<RWBuffer<int> > r15; // invalid - the element type has a handle with unknown size,
169+
// thus it is an intangible element type. The type trait that would catch this is the negation of `__builtin_hlsl_is_intangible`
170+
```
171+
172+
Below is a sample C++ implementation of the `RWBuffer` resource type.
173+
This code would exist within an hlsl header, but concepts are not implemented in HLSL. Instead, the AST node
174+
associated with RWBuffers is constructed as if this code was read and parsed by the compiler.
175+
```
176+
#include <type_traits>
177+
178+
namespace hlsl {
179+
180+
template<typename T>
181+
concept is_typed_resource_element_compatible =
182+
__builtin_hlsl_typed_resource_element_compatible(T);
183+
184+
template<typename element_type> requires !__builtin_hlsl_is_intangible(element_type) && is_typed_resource_element_compatible<element_type>
185+
struct RWBuffer {
186+
element_type Val;
187+
};
188+
189+
// doesn't need __builtin_hlsl_typed_resource_element_compatible, because this is a raw buffer
190+
// also, raw buffers allow bools and enums as constituent types
191+
template<typename T> requires !__builtin_hlsl_is_intangible(T)
192+
struct StructuredBuffer {
193+
T Val;
194+
};
195+
}
196+
197+
```
198+
199+
## Alternatives considered (Optional)
200+
We could instead implement a diagnostic function that checks each of these conceptual constraints in
201+
one place, either in Sema or CodeGen, but this would prevent us from defining a single header where
202+
all resource information is localized.
203+
204+
Another alternative considered was creating a builtin called `__builtin_hlsl_is_valid_resource_element_type`, to
205+
check all possible valid resource element types, rather than just checking that the element type is not intangible.
206+
This is unneeded because all primitive non-intangible types are valid element types.
207+
208+
## Acknowledgments (Optional)
209+
* Damyan Pepper
210+
* Chris Bieneman
211+
* Greg Roth
212+
* Sarah Spall
213+
* Tex Riddell
214+
* Justin Bogner
215+
<!-- {% endraw %} -->

0 commit comments

Comments
 (0)