Skip to content

Commit 81963d1

Browse files
[SYCL][Doc] POD Specialization constants design update (#2669)
Besides some tiny markup & typo fixes this PR changes interfaces for defining specialization constants between `sycl-post-link` and SPIRV-LLVM-Translator, in particular: Renamed `__spirvCompositeSpecConstant` to `__spirv_SpecConstantComposite` to better match SPIR-V friendly IR representaiton, which is aligned with opcode names from SPIR-V spec. Changed logic of generating `__spirv_SpecConstantComposite`: instead of getting list of `SpecID` for each composite member it now accepts vector of actual members, which might be another composits, undefs or regular constants. This change was done to align this intrinsic with corresponding opcode prototype to make its handling more universal and useful for the upstream translator project.
1 parent 4326b95 commit 81963d1

File tree

2 files changed

+99
-73
lines changed

2 files changed

+99
-73
lines changed

sycl/doc/CompilerAndRuntimeDesign.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,9 @@ unit)
498498
TBD
499499

500500
##### Specialization constants lowering
501-
TBD
501+
502+
See [corresponding documentation](SpecializationConstants.md)
503+
502504

503505
#### CUDA support
504506

sycl/doc/SpecializationConstants.md

Lines changed: 96 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ with some restrictions. See this [document](https://github.com/intel/llvm/blob/s
88
- must work with separate compilation and linking
99
- must support AOT compilation
1010

11-
Implementaion is based on SPIR-V specialization constants. But there is one
12-
important difference between SYCL and SPIR-V: in SYCL speciazation constants are
13-
identified by a type ID which is mapped to a symbolic name, in SPIR-V - by an
14-
ordinal number. This complicates the design, as the compiler
11+
Implementation is based on SPIR-V specialization constants. But there is one
12+
important difference between SYCL and SPIR-V: in SYCL specialization constants
13+
are identified by a type ID which is mapped to a symbolic name, in SPIR-V - by
14+
an ordinal number. This complicates the design, as the compiler
1515
1) needs to propagate symbolic =\> numeric ID correspondence to the runtime
1616
2) can assign numeric IDs only when linking due to the separate compilation
1717

@@ -66,7 +66,7 @@ recognized by a special LLVM pass later.
6666

6767
Compilation and subsequent linkage of device code results in a number of
6868
`__sycl_getSpecConstantValue` calls whose arguments are symbolic spec constant
69-
IDs. Before generating the a device binary, each linked device code LLVMIR
69+
IDs. Before generating a device binary, each linked device code LLVMIR
7070
module undergoes processing by the sycl-post-link tool which can run LLVMIR
7171
passes before passing the module onto the llvm-spirv translator.
7272

@@ -108,7 +108,7 @@ the `__sycl_getSpecConstantValue` calls with constants - default values of
108108
the spec constant's type. No maps are generated, and SYCL program can't change
109109
the value of a spec constant.
110110

111-
#### LLVMIR-SPIR-V translator
111+
#### LLVM -> SPIR-V translation
112112

113113
Given the `__spirv_SpecConstant` intrinsic calls produced by the
114114
`SpecConstants` pass:
@@ -175,7 +175,7 @@ struct A {
175175
176176
struct POD {
177177
A a[2];
178-
int b;
178+
cl::sycl::vec<int, 2> b;
179179
};
180180
```
181181

@@ -187,105 +187,129 @@ and the user says
187187
{ goldi, goldf },
188188
{ goldi + 1, goldf + 1 },
189189
},
190-
goldi
190+
{ goldi, goldi }
191191
};
192192
193193
cl::sycl::ONEAPI::experimental::spec_constant<POD, MyConst> sc = program4.set_spec_constant<MyConst>(gold);
194194
```
195195

196196
#### Compiler
197197

198-
##### The SpecConstant pass changes
198+
##### The SpecConstants pass
199199

200-
- The SpecConstants pass in the post-link will have the following IR as input (`sret` conversion is omitted for clarity):
200+
The SpecConstants pass in the post-link will have the following IR as input
201+
(`sret` conversion is omitted for clarity):
201202

202203
```
203-
%spec_const = call %struct.POD __sycl_getCompositeSpecConstantValue<POD type mangling> ("MyConst_mangled")
204+
%struct.POD = type { [2 x %struct.A], <2 x i32> }
205+
%struct.A = type { i32, float }
206+
207+
%spec_const = call %struct.POD __sycl_getCompositeSpecConstantValue<POD type mangling> ("MyConst_mangled")
204208
```
205209

206-
where `__sycl_getCompositeSpecConstantValue` is a new "intrinsic"
207-
(in addition to `__sycl_getSpecConstantValue`) recognized by SpecConstants pass,
208-
which creates a value of a composite (of non-primitive type) specialization constant.
209-
It does not need a default value, because its default value consists of default
210-
valued of its leaf specialization constants (see below).
210+
`__sycl_getCompositeSpecConstantValue` is a new "intrinsic" (in addition to
211+
`__sycl_getSpecConstantValue`) recognized by the `SpecConstants` pass, which
212+
creates a value of a composite (of non-primitive type) specialization constant.
213+
It does not need a default value, because its default value consists of default
214+
values of its leaf specialization constants (see below).
211215

212-
- after spec constant enumeration (symbolic -\> int ID translation), the SpecConstants pass
213-
will handle the `__sycl_getCompositeSpecConstantValue`. Given the knowledge of the composite
214-
specialization constant's type (`%struct.POD`), the pass will traverse its leaf
215-
fields and generate 5 "primitive" spec constants using already existing SPIR-V intrinsic:
216+
`__sycl_getCompositeSpecConstantValue` will be replaced with a set of
217+
`__spirv_SpecConstant` calls for each member of its return type plus one
218+
`__spirv_SpecConstantComposite` to gather members back into a single composite.
219+
If any composite member is another composite, then it will be also represented
220+
by number of `__spirv_SpecConstant` plus one `__spirv_SpecConstantComposite`.
216221

217222
```
218-
%gold_POD_a0x = call i32 __spirv_SpecConstant(i32 10, i32 0)
219-
%gold_POD_a0y = call float __spirv_SpecConstant(i32 11, float 0)
220-
%gold_POD_a1x = call i32 __spirv_SpecConstant(i32 12, i32 0)
221-
%gold_POD_a1y = call float __spirv_SpecConstant(i32 13, float 0)
222-
%gold_POD_b = call i32 __spirv_SpecConstant(i32 14, i32 0)
223+
%gold_POD_A0_x = call i32 __spirv_SpecConstant(i32 10, i32 0)
224+
%gold_POD_A0_y = call float __spirv_SpecConstant(i32 11, float 0)
225+
226+
%gold_POD_A0 = call %struct.A __spirv_SpecConstantComposite(i32 %gold_POD_A0_x, float %gold_POD_A0_y)
227+
228+
%gold_POD_A1_x = call i32 __spirv_SpecConstant(i32 12, i32 0)
229+
%gold_POD_A1_y = call float __spirv_SpecConstant(i32 13, float 0)
230+
231+
%gold_POD_A1 = call %struct.A __spirv_SpecConstantComposite(i32 %gold_POD_A1_x, float %gold_POD_A1_y)
232+
233+
%gold_POD_A = call [2 x %struct.A] __spirv_SpecConstantComposite(%struct.A %gold_POD_A0, %struct.A %gold_POD_A1)
234+
235+
%gold_POD_b0 = call i32 __spirv_SpecConstant(i32 14, i32 0)
236+
%gold_POD_b1 = call i32 __spirv_SpecConstant(i32 15, i32 0)
237+
%gold_POD_b = call <2 x i32> __spirv_SpecConstant(i32 %gold_POD_b0, i32 %gold_POD_b1)
238+
239+
%gold = call %struct.POD __spirv_SpecConstantComposite([2 x %struct.A] %gold_POD_A, <2 x i32> %gold_POD_b)
240+
223241
```
224242

225-
And 1 "composite"
243+
Spec ID for the composite spec constant is not needed, as runtime will never use
244+
it - it will use IDs of the leaves instead, which are being assigned by the
245+
`SpecConstants` pass during replacement of SYCL intrinsics with SPIR-V
246+
intrinsics.
247+
Besides, the SPIR-V specification does not allow `SpecID` decoration for
248+
composite spec constants, because its defined by its members instead.
249+
250+
`__spirv_SpecConstantComposite` is a new SPIR-V intrinsic, which represents
251+
composite specialization constant. Its arguments are LLVM IR values
252+
corresponding to elements of the composite constant.
226253

254+
##### LLVM -> SPIR-V translation
255+
256+
Given the `__spirv_SpecConstantComposite` intrinsic calls produced by the
257+
`SpecConstants` pass:
227258
```
228-
%gold_POD = call %struct.POD __spirvCompositeSpecConstant<POD type mangling>(i32 10, i32 11, i32 12, i32 13, i32 14)
259+
260+
%struct.A = type { i32, float }
261+
262+
; Function Attrs: alwaysinline
263+
define dso_local spir_func void @get(%struct.A* sret %ret.ptr) local_unnamed_addr #0 {
264+
; args are "ID" and "default value":
265+
%1 = tail call spir_func i32 @_Z20__spirv_SpecConstantii(i32 42, i32 0)
266+
%2 = tail call spir_func float @_Z20__spirv_SpecConstantif(i32 43, float 0.000000e+00)
267+
%ret = tail call spir_func %struct.A @_Z29__spirv_SpecConstantCompositeif(%1, %2)
268+
store %struct.A %ret, %struct.A* %ret.ptr
269+
ret void
270+
}
229271
```
230272

231-
where `__spirvCompositeSpecConstant<POD type mangling>` is a new SPIR-V intrinsic which
232-
represents creation of a composite specialization constant. Its arguments are spec
233-
constant IDs corresponding to the leaf fields of the POD type of the constant.
234-
Spec ID for the composite spec constant is not needed, as runtime will never use it - it will use IDs of the leaves instead.
235-
Yet, the SPIR-V specification does not allow `SpecID` decoration for composite spec constants.
273+
the translator will generate `OpSpecConstant` and `OpSpecConstantComposite`
274+
SPIR-V instructions with proper `SpecId` decorations:
275+
276+
```
277+
OpDecorate %i32 SpecId 42 ; ID of the 1st member
278+
OpDecorate %float SpecId 43 ; ID of the 2nd member
279+
%i32 = OpSpecConstant %int.type 0 ; 1st member with default value
280+
%float = OpSpecConstant %float.type 0.0 ; 2nd member with default value
281+
%struct = OpSpecConstantComposite %struct.type %i32 %float ; Composite doens't need IDs or default value
282+
%1 = OpTypeFunction %struct.type
283+
284+
%get = OpFunction %struct.type None %1
285+
%2 = OpLabel
286+
OpReturnValue %struct
287+
OpFunctionEnd
288+
```
236289

237290
##### The post-link tool changes
238291

239292
For composite specialization constants the post link tool will additionally
240-
generate linearized list of \<leaf spec ID,type,offset,size\> tuples (descriptors),
293+
generate linearized list of \<leaf spec ID,offset,size\> tuples (descriptors),
241294
where each tuple describes a leaf field, and store it together with the
242295
existing meta-information associated with the specialization constants and
243296
passed to the runtime. Also, for a composite specialization constant there is
244297
no ID map entry within the meta information, and the composite constant is
245298
referenced by its symbolic ID. For example:
246299

247300
```
248-
MyConst_mangled [10,int,0,4],[11,float,4,4],[12,int,8,4],[13,float,12,4],[14,int,16,4]
249-
```
250-
251-
#### LLVMIR-\>SPIR-V translator
252-
253-
The translator aims to create the following code (pseudo-code)
254-
255-
```
256-
%gold_POD_a0x = OpSpecConstant(0) [SpecId = 10]
257-
%gold_POD_a0y = OpSpecConstant(0.0f) [SpecId = 11]
258-
%gold_POD_a1x = OpSpecConstant(0) [SpecId = 12]
259-
%gold_POD_a1y = OpSpecConstant(0.0f) [SpecId = 13]
260-
%gold_POD_b = OpSpecConstant(0) [SpecId = 14]
261-
262-
%gold_POD_a0 = OpSpecConstantComposite(
263-
%gold_POD_a0x // gold.a[0].x
264-
%gold_POD_a0y // gold.a[0].y
265-
)
266-
267-
%gold_POD_a1 = OpSpecConstantComposite(
268-
%gold_POD_a1x // gold.a[1].x
269-
%gold_POD_a1y // gold.a[1].y
270-
)
271-
272-
%gold_POD = OpSpecConstantComposite(
273-
%gold_POD_a0,
274-
%gold_POD_a1,
275-
%gold_POD_b // gold.b
276-
}
301+
MyConst_mangled [10,0,4],[11,4,4],[12,8,4],[13,12,4],[14,16,4]
277302
```
278303

279-
- First, `OpSpecConstant` instructions are created using already existing mechanism for
280-
primitive spec constants.
281-
- Then the translator will handle `__spirvCompositeSpecConstant*` intrinsic.
282-
It will recursively traverse the spec constant type structure in parallel with
283-
the argument list - which is a list of primitive spec constant SpecIds.
284-
When traversing, it will create all the intermediate OpSpecConstantComposite
285-
instructions as well as the root one (`%gold_POD`) using simple depth-first tree
286-
traversal with stack. This requires mapping from SpecId decoration number to
287-
\<id\> of the corresponding OpSpecConstant instruction, but this should be pretty
288-
straightforward.
304+
This tuple is needed, because at SYCL runtime level, composite constants are set
305+
by user as a byte array and we have to break it down to the leaf members of the
306+
composite and set a value for each leaf as for a separate scalar specialization
307+
constant. Each tuple contains the following data:
308+
- ID of composite constant leaf, i.e. ID of a scalar specialization constant
309+
- Offset from the beginning of composite, which points to the location of a
310+
scalar value within the composite, i.e. the position where scalar
311+
specialization constant resides within the byte array supplied by the user
312+
- Size of the scalar specialization constant
289313

290314
#### SYCL runtime
291315

0 commit comments

Comments
 (0)