Skip to content

Commit e42b2c5

Browse files
authored
Update proposals for implicit binding and resource constructors to match implementation (#282)
1 parent b46fa42 commit e42b2c5

File tree

2 files changed

+93
-45
lines changed

2 files changed

+93
-45
lines changed

proposals/0024-implicit-resource-binding.md

Lines changed: 70 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -448,26 +448,25 @@ particular entry point is linked to a final shader.
448448
449449
### Clang Frontend
450450
451-
Unbound resources will be initialized by a resource class constructor that takes
452-
3 arguments - the `space`, `range` and `index`. This constructor will be
453-
declared in `HLSLExternalSemaSource` as part of resource class setup.
451+
Unbound resources will be initialized by a resource class constructor for
452+
implicit binding which is described [here](0025-resource-constructors.md#constructor-for-resources-with-implicit-binding).
454453
455-
The arguments specify the virtual register space (defaulting to 0),
456-
required descriptor range and an index of the resource in the range.
454+
The `order_id` number will be generated by in `SemaHLSL` class and it will
455+
be used to uniquely identify the unbound resource, and it will also reflect the
456+
order in which the resource has been declared. Implicit binding assignments
457+
depend on the order the resources were declared, so this will help the compiler
458+
to preserve the order. Additionally, it will also help the compiler distinquish
459+
between individual resources with the same type and binding range.
457460
458461
The constructor will call Clang builtin function
459-
`__builtin_hlsl_create_handle_from_implicit_binding` and will pass along the
460-
`space`, `range` and `index` values, and it will also include the uninitialized
461-
resource handle. The type of the handle argument will be used to infer the
462-
specific resource handle type returned by the buildin function. This will happen
463-
in `SemaHLSL::CheckBuiltinFunctionCall` the same way we infer return types for
464-
HLSL intrinsic builtins based on the builtin arguments.
462+
`__builtin_hlsl_create_handle_from_implicit_binding` and will pass along all the
463+
arguments provided to the constructor plus the resource handle.
465464
466465
```c++
467466
template <typename T> class RWBuffer<T> {
468-
RWBuffer(unsigned space, int range, unsigned index) {
469-
__handle = __builtin_hlsl_create_handle_from_implicit_binding(__handle, space, range, index);
470-
}
467+
RWBuffer(unsigned spaceNo, int range, unsigned index, unsigned orderId, const char *name) {
468+
__handle = __builtin_hlsl_resource_createhandlefromimplicitbinding(__handle, spaceNo, range, index, orderId, name);
469+
}
471470
}
472471
```
473472

@@ -485,36 +484,72 @@ The signature of the `@llvm.dx.resource.handlefromimplicitbinding` intrinsic wil
485484
| %space | i32 | Virtual register space |
486485
| %range | i32 | Range size of the binding |
487486
| %index | i32 | Index from the beginning of the binding |
487+
| %name | ptr | Pointer to a global constant string that is the name of the resource |
488488

489489
_Note: We might need to add a uniformity bit here, unless we can derive it from uniformity analysis._
490490

491-
The `order_id` number will be generated by Clang Codegen in `GCHLSLRuntime`. It
492-
will be used to uniquely identify the unbound resource, and it will also reflect
493-
the order in which the resource has been declared. Implicit binding assignments
494-
depend on the order the resources were declared, so this will help the compiler
495-
to preserve the order. Additionally, it will also help the compiler distinquish
496-
between individual resources with the same type and binding range.
497-
498491
### LLVM Binding Assignment Pass
499492

500-
The implicit binding assignment for DirectX will happen in an LLVM pass
501-
`DXILResourceImplicitBindings`. The pass will scan the module for all instances of
502-
`@llvm.dx.resource.handlefrombinding` and create a map of available register
503-
slots. Then it will gather all instances of
504-
`@llvm.dx.resource.handlefromimplicitbinding` calls and sort them by
505-
`%order_id`. Then for each group of calls operating on the same unique resource
506-
it will:
493+
The implicit binding assignment will happen in two phases - `DXILResourceBindingAnalysis` and `DXILResourceImplicitBindings`.
494+
495+
#### 1. Analysis of available register spaces
496+
497+
A new `DXILResourceBindingAnalysis` will scan the module for all instances of
498+
`@llvm.dx.resource.handlefrombinding` calls and create a map of available
499+
register slots and spaces. The results of the analysis will be stored in
500+
`DXILResourceBindingInfo`.
501+
502+
While the map is being created the analysis can easily detect whether any of the
503+
register bindings overlap, which is something the compiler needs to diagnose.
504+
However, in order to issue a useful error message for this, the analysis would
505+
have to track which register ranges are occupied by which resource. That is
506+
beyond the need of the common case scenario where all bindings are correct and
507+
the analysis just need to figure out the available register slot ranges.
508+
509+
For this reason if the analysis finds that some register bindings overlap, it
510+
will prioritize performance for the common case and just set the
511+
`hasOverlappingBinding` flag on `DXILResourceBindingInfo` to true. The detailed
512+
error message calculation for the uncommon failure code path will happen later
513+
in `DXILPostOptimizationValidation` pass which can analyze the resource bindings
514+
in more detail and report exactly which resources are overlapping and where.
515+
516+
This is the same principle that we use when detecting invalid counter usage for
517+
structured buffers as described
518+
[here](0022-resource-instance-analysis.md).
519+
520+
While scanning for the `@llvm.dx.resource.handlefrombinding` calls the analysis
521+
can also take note on whether there are any
522+
`@llvm.dx.resource.handlefromimplicitbinding` calls in the module. If there are,
523+
it will set the `hasImplicitBinding` flag on `DXILResourceBindingInfo` to true.
524+
This will enable the follow-up `DXILResourceImplicitBindings` pass to exit early
525+
if there are no implicit bindings in the module.
526+
527+
_Note: In general diagnostics should not be raised in LLVM analyses or
528+
passes. Analyses may be invalidated and re-ran several times, increasing
529+
performance impact and raising repeated diagnostics. Diagnostics raised after
530+
transformations passes also lose source context resulting in less useful error
531+
messages. However the shader compiler requires certain validations to be done
532+
after code optimizations which requires the diagnostic to be raised from a pass.
533+
Impact is minimized by raising the diagnostic only from a limited set of passes
534+
and minimizing computation in the common case._
535+
536+
#### 2. Assign bindings to implictily bound resource
537+
538+
The implicit binding assignment will happen in an LLVM pass
539+
`DXILResourceImplicitBindings`. The pass will use the map of available register
540+
spaces `DXILResourceBindingInfo` created by `DXILResourceBindingAnalysis`. It
541+
will scan the module for all instances of
542+
`@llvm.dx.resource.handlefromimplicitbinding` calls, sort them by `order_id`,
543+
and then for each group of calls operating on the same unique resource it will:
507544
- find an available space to bind the resource based on the resource class,
508-
required range and space
545+
required range and space
509546
- replace the `@llvm.dx.resource.handlefromimplicitbinding` calls and all of their
510-
uses with `@llvm.dx.resource.handlefrombinding` using the new binding
547+
uses with `@llvm.dx.resource.handlefrombinding` with the assigned binding
511548

512549
The `DXILResourceImplicitBindings` pass needs to run after all IR optimizations
513-
passes but before any pass or analysis that relies on
514-
`@llvm.dx.resource.handlefrombinding` calls to exist for all non-dynamically
515-
bound resources used by the shader. For example, it needs to run before any pass
516-
that requires `DXILResourceAnalysis`. The pass invalidate any existing
517-
`DXILResourceAnalysis` if it assigns any new bindings.
550+
passes but before any pass that depends on `DXILResourceAnalysis` which relies
551+
on `@llvm.dx.resource.handlefrombinding` calls to exist for all non-dynamically
552+
bound resources used by the shader.
518553

519554
## Alternatives considered (Optional)
520555

proposals/0025-resource-constructors.md

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ template <typename T> struct RWBuffer {
9090
...
9191
private:
9292
// For resources with explicit binding
93-
RWBuffer(unsigned register, unsigned space, int range, unsigned index) {
94-
__handle = __builtin_hlsl_resource_createhandlefrombinding(__handle, register, space, range, index);
93+
RWBuffer(unsigned registerNo, unsigned spaceNo, int range, unsigned index, const char *name) {
94+
__handle = __builtin_hlsl_resource_createhandlefrombinding(__handle, registerNo, spaceNo, range, index, name);
9595
}
9696
...
9797
};
@@ -102,6 +102,10 @@ The `__handle` argument passed into the
102102
to infer the return type of the that function (the same way as for the default
103103
construtor).
104104
105+
The `name` argument will be used to generate the DXIL resource metadata and also
106+
for resource diagnostics that need to happen after optimizations later in the
107+
compiler pipeline.
108+
105109
A call to this constructor will be created by Sema as part of uninitialized
106110
variable declaration processing (`Sema::ActOnUninitializedDecl`). It will
107111
work as if it would replace:
@@ -110,7 +114,7 @@ work as if it would replace:
110114
111115
with
112116
113-
`RWBuffer<float> A(3,0,1,0);`.
117+
`RWBuffer<float> A(3,0,1,0,"A");`.
114118
115119
### Constructor for resources with implicit binding
116120
@@ -125,17 +129,26 @@ template <typename T> struct RWBuffer {
125129
...
126130
private:
127131
// For resources with implicit binding
128-
RWBuffer(unsigned space, int range, unsigned index) {
129-
__handle = __builtin_hlsl_resource_createhandlefromimplicitbinding(__handle, space, range, index);
132+
RWBuffer(unsigned spaceNo, int range, unsigned index, unsigned orderId, const char *name) {
133+
__handle = __builtin_hlsl_resource_createhandlefromimplicitbinding(__handle, spaceNo, range, index, orderId, name);
130134
}
131135
...
132136
};
133137
```
134138

135139
The `__handle` argument passed into the
136-
`__builtin_hlsl_resource_createhandlefromimplicitbinding` Clang builtin function will
137-
be used to infer the return type of the that function (the same way as for the
138-
default construtor).
140+
`__builtin_hlsl_resource_createhandlefromimplicitbinding` Clang builtin function
141+
will be used to infer the return type of the that function (the same way as for
142+
the default construtor).
143+
144+
The `orderId` number will be generated by in `SemaHLSL` class and it will be
145+
used to uniquely identify the unbound resource, and it will also reflect the
146+
order in which the resource has been declared. It will be used later on in the
147+
compiler to assign implicit bindings to resources in the right order.
148+
149+
The `name` argument will be used to generate the DXIL resource metadata and also
150+
for resource diagnostics that need to happen after optimizations later in the
151+
compiler pipeline.
139152

140153
A call to this constructor will be created by Sema as part of uninitialized
141154
variable declaration processing (`Sema::ActOnUninitializedDecl`). It will
@@ -145,7 +158,7 @@ work as if it would replace:
145158

146159
with
147160

148-
`RWBuffer<float> A(0,1,0);`.
161+
`RWBuffer<float> A(0,1,0,0,"A");`.
149162

150163
Or if the resource has a space-only binding annotation, it will work as if it
151164
would replace:
@@ -154,7 +167,7 @@ would replace:
154167

155168
with
156169

157-
`RWBuffer<float> A(13,1,0);`.
170+
`RWBuffer<float> A(13,1,0,0,"A");`.
158171

159172
### Copy constructor and assignment operator
160173

0 commit comments

Comments
 (0)