@@ -8,10 +8,10 @@ with some restrictions. See this [document](https://github.com/intel/llvm/blob/s
8
8
- must work with separate compilation and linking
9
9
- must support AOT compilation
10
10
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
15
15
1 ) needs to propagate symbolic =\> numeric ID correspondence to the runtime
16
16
2 ) can assign numeric IDs only when linking due to the separate compilation
17
17
@@ -66,7 +66,7 @@ recognized by a special LLVM pass later.
66
66
67
67
Compilation and subsequent linkage of device code results in a number of
68
68
` __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
70
70
module undergoes processing by the sycl-post-link tool which can run LLVMIR
71
71
passes before passing the module onto the llvm-spirv translator.
72
72
@@ -108,7 +108,7 @@ the `__sycl_getSpecConstantValue` calls with constants - default values of
108
108
the spec constant's type. No maps are generated, and SYCL program can't change
109
109
the value of a spec constant.
110
110
111
- #### LLVMIR- SPIR-V translator
111
+ #### LLVM -> SPIR-V translation
112
112
113
113
Given the ` __spirv_SpecConstant ` intrinsic calls produced by the
114
114
` SpecConstants ` pass:
@@ -175,7 +175,7 @@ struct A {
175
175
176
176
struct POD {
177
177
A a[2];
178
- int b;
178
+ cl::sycl::vec< int, 2> b;
179
179
};
180
180
```
181
181
@@ -187,105 +187,129 @@ and the user says
187
187
{ goldi, goldf },
188
188
{ goldi + 1, goldf + 1 },
189
189
},
190
- goldi
190
+ { goldi, goldi }
191
191
};
192
192
193
193
cl::sycl::ONEAPI::experimental::spec_constant<POD, MyConst> sc = program4.set_spec_constant<MyConst>(gold);
194
194
```
195
195
196
196
#### Compiler
197
197
198
- ##### The SpecConstant pass changes
198
+ ##### The SpecConstants pass
199
199
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):
201
202
202
203
```
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")
204
208
```
205
209
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).
211
215
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 ` .
216
221
217
222
```
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
+
223
241
```
224
242
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.
226
253
254
+ ##### LLVM -> SPIR-V translation
255
+
256
+ Given the ` __spirv_SpecConstantComposite ` intrinsic calls produced by the
257
+ ` SpecConstants ` pass:
227
258
```
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
+ }
229
271
```
230
272
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
+ ```
236
289
237
290
##### The post-link tool changes
238
291
239
292
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),
241
294
where each tuple describes a leaf field, and store it together with the
242
295
existing meta-information associated with the specialization constants and
243
296
passed to the runtime. Also, for a composite specialization constant there is
244
297
no ID map entry within the meta information, and the composite constant is
245
298
referenced by its symbolic ID. For example:
246
299
247
300
```
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]
277
302
```
278
303
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
289
313
290
314
#### SYCL runtime
291
315
0 commit comments