Fix GCC build problem with 288f05f related to SmallVector. (#116958)
[llvm-project.git] / mlir / docs / SPIRVToLLVMDialectConversion.md
blob872690bb52646e8eb9454b99f949c315e109cf84
1 # SPIR-V Dialect to LLVM Dialect conversion manual
3 This manual describes the conversion from [SPIR-V Dialect](Dialects/SPIR-V.md)
4 to [LLVM Dialect](Dialects/LLVM.md). It assumes familiarity with both, and
5 describes the design choices behind the modelling of SPIR-V concepts in LLVM
6 Dialect. The conversion is an ongoing work, and is expected to grow as more
7 features are implemented.
9 Conversion can be performed by invoking an appropriate conversion pass:
11 ```shell
12 mlir-opt -convert-spirv-to-llvm <filename.mlir>
13 ```
15 This pass performs type and operation conversions for SPIR-V operations as
16 described in this document.
18 [TOC]
20 ## Type Conversion
22 This section describes how SPIR-V Dialect types are mapped to LLVM Dialect.
24 ### Scalar types
26 SPIR-V Dialect | LLVM Dialect
27 :------------: | :-----------------:
28 `i<bitwidth>`  | `!llvm.i<bitwidth>`
29 `si<bitwidth>` | `!llvm.i<bitwidth>`
30 `ui<bitwidth>` | `!llvm.i<bitwidth>`
31 `f16`          | `f16`
32 `f32`          | `f32`
33 `f64`          | `f64`
35 ### Vector types
37 SPIR-V Dialect                    | LLVM Dialect
38 :-------------------------------: | :-------------------------------:
39 `vector<<count> x <scalar-type>>` | `vector<<count> x <scalar-type>>`
41 ### Pointer types
43 A SPIR-V pointer also takes a Storage Class. At the moment, conversion does
44 **not** take it into account.
46 SPIR-V Dialect                                | LLVM Dialect
47 :-------------------------------------------: | :-------------------------:
48 `!spirv.ptr< <element-type>, <storage-class> >` | `!llvm.ptr`
50 ### Array types
52 SPIR-V distinguishes between array type and run-time array type, the length of
53 which is not known at compile time. In LLVM, it is possible to index beyond the
54 end of the array. Therefore, runtime array can be implemented as a zero length
55 array type.
57 Moreover, SPIR-V supports the notion of array stride. Currently only natural
58 strides (based on [`VulkanLayoutUtils`][VulkanLayoutUtils]) are supported. They
59 are also mapped to LLVM array.
61 SPIR-V Dialect                         | LLVM Dialect
62 :------------------------------------: | :-------------------------------------:
63 `!spirv.array<<count> x <element-type>>` | `!llvm.array<<count> x <element-type>>`
64 `!spirv.rtarray< <element-type> >`       | `!llvm.array<0 x <element-type>>`
66 ### Struct types
68 Members of SPIR-V struct types may have decorations and offset information.
69 Currently, there is **no** support of member decorations conversion for structs.
70 For more information see section on [Decorations](#Decorations-conversion).
72 Usually we expect that each struct member has a natural size and alignment.
73 However, there are cases (*e.g.* in graphics) where one would place struct
74 members explicitly at particular offsets. This case is **not** supported at the
75 moment. Hence, we adhere to the following mapping:
77 *   Structs with no offset are modelled as LLVM packed structures.
79 *   Structs with natural offset (*i.e.* offset that equals to cumulative size of
80     the previous struct elements or is a natural alignment) are mapped to
81     naturally padded structs.
83 *   Structs with unnatural offset (*i.e.* offset that is not equal to cumulative
84     size of the previous struct elements) are **not** supported. In this case,
85     offsets can be emulated with padding fields (*e.g.* integers). However, such
86     a design would require index recalculation in the conversion of ops that
87     involve memory addressing.
89 Examples of SPIR-V struct conversion are: ```mlir !spirv.struct<i8, i32> =>
90 !llvm.struct<packed (i8, i32)> !spirv.struct<i8 [0], i32 [4]> => !llvm.struct<(i8,
91 i32)>
93 // error !spirv.struct<i8 [0], i32 [8]> ```
95 ### Not implemented types
97 The rest of the types not mentioned explicitly above are not supported by the
98 conversion. This includes `ImageType` and `MatrixType`.
100 ## Operation Conversion
102 This section describes how SPIR-V Dialect operations are converted to LLVM
103 Dialect. It lists already working conversion patterns, as well as those that are
104 an ongoing work.
106 There are also multiple ops for which there is no clear mapping in LLVM.
107 Conversion for those have to be discussed within the community on the
108 case-by-case basis.
110 ### Arithmetic ops
112 SPIR-V arithmetic ops mostly have a direct equivalent in LLVM Dialect. Such
113 exceptions as `spirv.SMod` and `spirv.FMod` are rare.
115 SPIR-V Dialect op | LLVM Dialect op
116 :---------------: | :-------------:
117 `spirv.FAdd`        | `llvm.fadd`
118 `spirv.FDiv`        | `llvm.fdiv`
119 `spirv.FNegate`     | `llvm.fneg`
120 `spirv.FMul`        | `llvm.fmul`
121 `spirv.FRem`        | `llvm.frem`
122 `spirv.FSub`        | `llvm.fsub`
123 `spirv.IAdd`        | `llvm.add`
124 `spirv.IMul`        | `llvm.mul`
125 `spirv.ISub`        | `llvm.sub`
126 `spirv.SDiv`        | `llvm.sdiv`
127 `spirv.SRem`        | `llvm.srem`
128 `spirv.UDiv`        | `llvm.udiv`
129 `spirv.UMod`        | `llvm.urem`
131 ### Bitwise ops
133 SPIR-V has a range of bit ops that are mapped to LLVM dialect ops, intrinsics or
134 may have a specific conversion pattern.
136 #### Direct conversion
138 As with arithmetic ops, most of bitwise ops have a semantically equivalent op in
139 LLVM:
141 SPIR-V Dialect op | LLVM Dialect op
142 :---------------: | :-------------:
143 `spirv.BitwiseAnd`  | `llvm.and`
144 `spirv.BitwiseOr`   | `llvm.or`
145 `spirv.BitwiseXor`  | `llvm.xor`
147 Also, some of bitwise ops can be modelled with LLVM intrinsics:
149 SPIR-V Dialect op | LLVM Dialect intrinsic
150 :---------------: | :--------------------:
151 `spirv.BitCount`    | `llvm.intr.ctpop`
152 `spirv.BitReverse`  | `llvm.intr.bitreverse`
154 #### `spirv.Not`
156 `spirv.Not` is modelled with a `xor` operation with a mask with all bits set.
158 ```mlir
159                             %mask = llvm.mlir.constant(-1 : i32) : i32
160 %0 = spirv.Not %op : i32  =>  %0  = llvm.xor %op, %mask : i32
163 #### Bitfield ops
165 SPIR-V dialect has three bitfield ops: `spirv.BitFieldInsert`,
166 `spirv.BitFieldSExtract` and `spirv.BitFieldUExtract`. This section will first
167 outline the general design of conversion patterns for this ops, and then
168 describe each of them.
170 All of these ops take `base`, `offset` and `count` (`insert` for
171 `spirv.BitFieldInsert`) as arguments. There are two important things to note:
173 *   `offset` and `count` are always scalar. This means that we can have the
174     following case:
176     ```mlir
177     %0 = spirv.BitFieldSExtract %base, %offset, %count : vector<2xi32>, i8, i8
178     ```
180     To be able to proceed with conversion algorithms described below, all
181     operands have to be of the same type and bitwidth. This requires
182     broadcasting of `offset` and `count` to vectors, for example for the case
183     above it gives:
185     ```mlir
186     // Broadcasting offset
187     %offset0 = llvm.mlir.undef : vector<2xi8>
188     %zero = llvm.mlir.constant(0 : i32) : i32
189     %offset1 = llvm.insertelement %offset, %offset0[%zero : i32] : vector<2xi8>
190     %one = llvm.mlir.constant(1 : i32) : i32
191     %vec_offset = llvm.insertelement  %offset, %offset1[%one : i32] : vector<2xi8>
193     // Broadcasting count
194     // ...
195     ```
197 *   `offset` and `count` may have different bitwidths from `base`. In this case,
198     both of these operands have to be zero extended (since they are treated as
199     unsigned by the specification) or truncated. For the above example it would
200     be:
202     ```mlir
203     // Zero extending offset after broadcasting
204     %res_offset = llvm.zext %vec_offset: vector<2xi8> to vector<2xi32>
205     ```
207     Also, note that if the bitwidth of `offset` or `count` is greater than the
208     bitwidth of `base`, truncation is still permitted. This is because the ops
209     have a defined behaviour with `offset` and `count` being less than the size
210     of `base`. It creates a natural upper bound on what values `offset` and
211     `count` can take, which is 64. This can be expressed in less than 8 bits.
213 Now, having these two cases in mind, we can proceed with conversion for the ops
214 and their operands.
216 ##### `spirv.BitFieldInsert`
218 This operation is implemented as a series of LLVM Dialect operations. First step
219 would be to create a mask with bits set outside [`offset`, `offset` + `count` -
220 1]. Then, unchanged bits are extracted from `base` that are outside of
221 [`offset`, `offset` + `count` - 1]. The result is `or`ed with shifted `insert`.
223 ```mlir
224 // Create mask
225 // %minus_one = llvm.mlir.constant(-1 : i32) : i32
226 // %t0        = llvm.shl %minus_one, %count : i32
227 // %t1        = llvm.xor %t0, %minus_one : i32
228 // %t2        = llvm.shl %t1, %offset : i32
229 // %mask      = llvm.xor %t2, %minus_one : i32
231 // Extract unchanged bits from the Base
232 // %new_base  = llvm.and %base, %mask : i32
234 // Insert new bits
235 // %sh_insert = llvm.shl %insert, %offset : i32
236 // %res       = llvm.or %new_base, %sh_insert : i32
237 %res = spirv.BitFieldInsert %base, %insert, %offset, %count : i32, i32, i32
240 ##### `spirv.BitFieldSExtract`
242 To implement `spirv.BitFieldSExtract`, `base` is shifted left by [sizeof(`base`) -
243 (`count` + `offset`)], so that the bit at `offset` + `count` - 1 is the most
244 significant bit. After, the result is shifted right, filling the bits with the
245 sign bit.
247 ```mlir
248 // Calculate the amount to shift left.
249 // %size    = llvm.mlir.constant(32 : i32) : i32
250 // %t0      = llvm.add %count, %offset : i32
251 // %t1      = llvm.sub %size, %t0 : i32
253 // Shift left and then right to extract the bits
254 // %sh_left = llvm.shl %base, %t1 : i32
255 // %t2      = llvm.add %offset, %t1 : i32
256 // %res     = llvm.ashr %sh_left, %t2 : i32
257 %res = spirv.BitFieldSExtract %base, %offset, %count : i32, i32, i32
260 ##### `spirv.BitFieldUExtract`
262 For this op a similar pattern as for `spirv.BitFieldInsert` is used. First, a mask
263 with bits set at [0, `count` - 1] is created. Then `base` is shifted by `offset`
264 and the mask is applied.
266 ```mlir
267 // Create a mask
268 // %minus_one = llvm.mlir.constant(-1 : i32) : i32
269 // %t0        = llvm.shl %minus_one, %count : i32
270 // mask       = llvm.xor  %t0, %minus_one : i32
272 // Shift Base and apply mask
273 // %sh_base   = llvm.lshr %base, %offset : i32
274 // %res       = llvm.and %sh_base, %mask : i32
275 %res = spirv.BitFieldUExtract %base, %offset, %count : i32, i32, i32
278 ### Cast ops
280 #### Direct conversions
282 SPIR-V Dialect op | LLVM Dialect op
283 :---------------: | :-------------:
284 `spirv.ConvertFToS` | `llvm.fptosi`
285 `spirv.ConvertFToU` | `llvm.fptoui`
286 `spirv.ConvertSToF` | `llvm.sitofp`
287 `spirv.ConvertUToF` | `llvm.uitofp`
289 #### spirv.Bitcast
291 This operation has a direct counterpart in LLVM: `llvm.bitcast`. It is treated
292 separately since it also supports pointer to pointer bit pattern-preserving type
293 conversion, apart from regular scalar or vector of numerical type.
295 #### Special cases
297 Special cases include `spirv.FConvert`, `spirv.SConvert` and `spirv.UConvert`. These
298 operations are either a truncate or extend. Let's denote the operand component
299 width as A, and result component width as R. Then, the following mappings are
300 used:
302 ##### `spirv.FConvert`
304 Case  | LLVM Dialect op
305 :---: | :-------------:
306 A < R | `llvm.fpext`
307 A > R | `llvm.fptrunc`
309 ##### `spirv.SConvert`
311 Case  | LLVM Dialect op
312 :---: | :-------------:
313 A < R | `llvm.sext`
314 A > R | `llvm.trunc`
316 ##### `spirv.UConvert`
318 Case  | LLVM Dialect op
319 :---: | :-------------:
320 A < R | `llvm.zext`
321 A > R | `llvm.trunc`
323 The case when A = R is not possible, based on SPIR-V Dialect specification:
325 > The component width cannot equal the component width in Result Type.
327 ### Comparison ops
329 SPIR-V comparison ops are mapped to LLVM `icmp` and `fcmp` operations.
331 SPIR-V Dialect op            | LLVM Dialect op
332 :--------------------------: | :---------------:
333 `spirv.IEqual`                 | `llvm.icmp "eq"`
334 `spirv.INotEqual`              | `llvm.icmp "ne"`
335 `spirv.FOrdEqual`              | `llvm.fcmp "oeq"`
336 `spirv.FOrdGreaterThan`        | `llvm.fcmp "ogt"`
337 `spirv.FOrdGreaterThanEqual`   | `llvm.fcmp "oge"`
338 `spirv.FOrdLessThan`           | `llvm.fcmp "olt"`
339 `spirv.FOrdLessThanEqual`      | `llvm.fcmp "ole"`
340 `spirv.FOrdNotEqual`           | `llvm.fcmp "one"`
341 `spirv.FUnordEqual`            | `llvm.fcmp "ueq"`
342 `spirv.FUnordGreaterThan`      | `llvm.fcmp "ugt"`
343 `spirv.FUnordGreaterThanEqual` | `llvm.fcmp "uge"`
344 `spirv.FUnordLessThan`         | `llvm.fcmp "ult"`
345 `spirv.FUnordLessThanEqual`    | `llvm.fcmp "ule"`
346 `spirv.FUnordNotEqual`         | `llvm.fcmp "une"`
347 `spirv.SGreaterThan`           | `llvm.icmp "sgt"`
348 `spirv.SGreaterThanEqual`      | `llvm.icmp "sge"`
349 `spirv.SLessThan`              | `llvm.icmp "slt"`
350 `spirv.SLessThanEqual`         | `llvm.icmp "sle"`
351 `spirv.UGreaterThan`           | `llvm.icmp "ugt"`
352 `spirv.UGreaterThanEqual`      | `llvm.icmp "uge"`
353 `spirv.ULessThan`              | `llvm.icmp "ult"`
354 `spirv.ULessThanEqual`         | `llvm.icmp "ule"`
356 ### Composite ops
358 Currently, conversion supports rewrite patterns for `spirv.CompositeExtract` and
359 `spirv.CompositeInsert`. We distinguish two cases for these operations: when the
360 composite object is a vector, and when the composite object is of a non-vector
361 type (*i.e.* struct, array or runtime array).
363 Composite type | SPIR-V Dialect op      | LLVM Dialect op
364 :------------: | :--------------------: | :-------------------:
365 vector         | `spirv.CompositeExtract` | `llvm.extractelement`
366 vector         | `spirv.CompositeInsert`  | `llvm.insertelement`
367 non-vector     | `spirv.CompositeExtract` | `llvm.extractvalue`
368 non-vector     | `spirv.CompositeInsert`  | `llvm.insertvalue`
370 ### `spirv.EntryPoint` and `spirv.ExecutionMode`
372 First of all, it is important to note that there is no direct representation of
373 entry points in LLVM. At the moment, we use the following approach:
375 *   `spirv.EntryPoint` is simply removed.
377 *   In contrast, `spirv.ExecutionMode` may contain important information about the
378     entry point. For example, `LocalSize` provides information about the
379     work-group size that can be reused.
381     In order to preserve this information, `spirv.ExecutionMode` is converted to a
382     struct global variable that stores the execution mode id and any variables
383     associated with it. In C, the struct has the structure shown below.
385     ```c
386     // No values are associated      // There are values that are associated
387     // with this entry point.        // with this entry point.
388     struct {                         struct {
389       int32_t executionMode;             int32_t executionMode;
390     };                                   int32_t values[];
391                                      };
392     ```
394     ```mlir
395     // spirv.ExecutionMode @empty "ContractionOff"
396     llvm.mlir.global external constant @{{.*}}() : !llvm.struct<(i32)> {
397       %0   = llvm.mlir.undef : !llvm.struct<(i32)>
398       %1   = llvm.mlir.constant(31 : i32) : i32
399       %ret = llvm.insertvalue %1, %0[0] : !llvm.struct<(i32)>
400       llvm.return %ret : !llvm.struct<(i32)>
401     }
402     ```
404 ### Logical ops
406 Logical ops follow a similar pattern as bitwise ops, with the difference that
407 they operate on `i1` or vector of `i1` values. The following mapping is used to
408 emulate SPIR-V ops behaviour:
410 SPIR-V Dialect op     | LLVM Dialect op
411 :-------------------: | :--------------:
412 `spirv.LogicalAnd`      | `llvm.and`
413 `spirv.LogicalOr`       | `llvm.or`
414 `spirv.LogicalEqual`    | `llvm.icmp "eq"`
415 `spirv.LogicalNotEqual` | `llvm.icmp "ne"`
417 `spirv.LogicalNot` has the same conversion pattern as bitwise `spirv.Not`. It is
418 modelled with `xor` operation with a mask with all bits set.
420 ```mlir
421                                   %mask = llvm.mlir.constant(-1 : i1) : i1
422 %0 = spirv.LogicalNot %op : i1  =>  %0    = llvm.xor %op, %mask : i1
425 ### Memory ops
427 This section describes the conversion patterns for SPIR-V dialect operations
428 that concern memory.
430 #### `spirv.AccessChain`
432 `spirv.AccessChain` is mapped to `llvm.getelementptr` op. In order to create a
433 valid LLVM op, we also add a 0 index to the `spirv.AccessChain`'s indices list in
434 order to go through the pointer.
436 ```mlir
437 // Access the 1st element of the array
438 %i   = spirv.Constant 1: i32
439 %var = spirv.Variable : !spirv.ptr<!spirv.struct<f32, !spirv.array<4xf32>>, Function>
440 %el  = spirv.AccessChain %var[%i, %i] : !spirv.ptr<!spirv.struct<f32, !spirv.array<4xf32>>, Function>, i32, i32
442 // Corresponding LLVM dialect code
443 %i   = ...
444 %var = ...
445 %0   = llvm.mlir.constant(0 : i32) : i32
446 %el  = llvm.getelementptr %var[%0, %i, %i] : (!llvm.ptr, i32, i32, i32), !llvm.struct<packed (f32, array<4 x f32>)>
449 #### `spirv.Load` and `spirv.Store`
451 These ops are converted to their LLVM counterparts: `llvm.load` and
452 `llvm.store`. If the op has a memory access attribute, then there are the
453 following cases, based on the value of the attribute:
455 *   **Aligned**: alignment is passed on to LLVM op builder, for example: `mlir
456     // llvm.store %ptr, %val {alignment = 4 : i64} : !llvm.ptr spirv.Store
457     "Function" %ptr, %val ["Aligned", 4] : f32`
458 *   **None**: same case as if there is no memory access attribute.
460 *   **Nontemporal**: set `nontemporal` flag, for example: `mlir // %res =
461     llvm.load %ptr {nontemporal} : !llvm.ptr %res = spirv.Load "Function"
462     %ptr ["Nontemporal"] : f32`
464 *   **Volatile**: mark the op as `volatile`, for example: `mlir // %res =
465     llvm.load volatile %ptr : !llvm.ptr f32> %res = spirv.Load "Function" %ptr
466     ["Volatile"] : f32` Otherwise the conversion fails as other cases
467     (`MakePointerAvailable`, `MakePointerVisible`, `NonPrivatePointer`) are not
468     supported yet.
470 #### `spirv.GlobalVariable` and `spirv.mlir.addressof`
472 `spirv.GlobalVariable` is modelled with `llvm.mlir.global` op. However, there is a
473 difference that has to be pointed out.
475 In SPIR-V dialect, the global variable returns a pointer, whereas in LLVM
476 dialect the global holds an actual value. This difference is handled by
477 `spirv.mlir.addressof` and `llvm.mlir.addressof` ops that both return a pointer
478 and are used to reference the global.
480 ```mlir
481 // Original SPIR-V module
482 spirv.module Logical GLSL450 {
483   spirv.GlobalVariable @struct : !spirv.ptr<!spirv.struct<f32, !spirv.array<10xf32>>, Private>
484   spirv.func @func() -> () "None" {
485     %0 = spirv.mlir.addressof @struct : !spirv.ptr<!spirv.struct<f32, !spirv.array<10xf32>>, Private>
486     spirv.Return
487   }
490 // Converted result
491 module {
492   llvm.mlir.global private @struct() : !llvm.struct<packed (f32, [10 x f32])>
493   llvm.func @func() {
494     %0 = llvm.mlir.addressof @struct : !llvm.ptr
495     llvm.return
496   }
500 The SPIR-V to LLVM conversion does not involve modelling of workgroups. Hence,
501 we say that only current invocation is in conversion's scope. This means that
502 global variables with pointers of `Input`, `Output`, and `Private` storage
503 classes are supported. Also, `StorageBuffer` storage class is allowed for
504 executing [SPIR-V CPU Runner tests](#spir-v-cpu-runner-tests).
506 Moreover, `bind` that specifies the descriptor set and the binding number and
507 `built_in` that specifies SPIR-V `BuiltIn` decoration have no conversion into
508 LLVM dialect.
510 Currently `llvm.mlir.global`s are created with `private` linkage for `Private`
511 storage class and `External` for other storage classes, based on SPIR-V spec:
513 > By default, functions and global variables are private to a module and cannot
514 > be accessed by other modules. However, a module may be written to export or
515 > import functions and global (module scope) variables.
517 If the global variable's pointer has `Input` storage class, then a `constant`
518 flag is added to LLVM op:
520 ```mlir
521 spirv.GlobalVariable @var : !spirv.ptr<f32, Input>    =>    llvm.mlir.global external constant @var() : f32
524 #### `spirv.Variable`
526 Per SPIR-V dialect spec, `spirv.Variable` allocates an object in memory, resulting
527 in a pointer to it, which can be used with `spirv.Load` and `spirv.Store`. It is
528 also a function-level variable.
530 `spirv.Variable` is modelled as `llvm.alloca` op. If initialized, an additional
531 store instruction is used. Note that there is no initialization for arrays and
532 structs since constants of these types are not supported in LLVM dialect (TODO).
533 Also, at the moment initialization is only possible via `spirv.Constant`.
535 ```mlir
536 // Conversion of VariableOp without initialization
537                                                                %size = llvm.mlir.constant(1 : i32) : i32
538 %res = spirv.Variable : !spirv.ptr<vector<3xf32>, Function>   =>   %res  = llvm.alloca  %size x vector<3xf32> : (i32) -> !llvm.ptr
540 // Conversion of VariableOp with initialization
541                                                                %c    = llvm.mlir.constant(0 : i64) : i64
542 %c   = spirv.Constant 0 : i64                                    %size = llvm.mlir.constant(1 : i32) : i32
543 %res = spirv.Variable init(%c) : !spirv.ptr<i64, Function>    =>   %res  = llvm.alloca %[[SIZE]] x i64 : (i32) -> !llvm.ptr
544                                                                llvm.store %c, %res : i64, !llvm.ptr
547 Note that simple conversion to `alloca` may not be sufficient if the code has
548 some scoping. For example, if converting ops executed in a loop into `alloca`s,
549 a stack overflow may occur. For this case, `stacksave`/`stackrestore` pair can
550 be used (TODO).
552 ### Miscellaneous ops with direct conversions
554 There are multiple SPIR-V ops that do not fit in a particular group but can be
555 converted directly to LLVM dialect. Their conversion is addressed in this
556 section.
558 SPIR-V Dialect op | LLVM Dialect op
559 :---------------: | :---------------:
560 `spirv.Select`      | `llvm.select`
561 `spirv.Undef`       | `llvm.mlir.undef`
563 ### Shift ops
565 Shift operates on two operands: `shift` and `base`.
567 In SPIR-V dialect, `shift` and `base` may have different bit width. On the
568 contrary, in LLVM Dialect both `base` and `shift` have to be of the same
569 bitwidth. This leads to the following conversions:
571 *   if `base` has the same bitwidth as `shift`, the conversion is
572     straightforward.
574 *   if `base` has a greater bit width than `shift`, shift is sign or zero
575     extended first. Then the extended value is passed to the shift.
577 *   otherwise, the conversion is considered to be illegal.
579 ```mlir
580 // Shift without extension
581 %res0 = spirv.ShiftRightArithmetic %0, %2 : i32, i32  =>  %res0 = llvm.ashr %0, %2 : i32
583 // Shift with extension
584                                                         %ext  = llvm.sext %1 : i16 to i32
585 %res1 = spirv.ShiftRightArithmetic %0, %1 : i32, i16  =>  %res1 = llvm.ashr %0, %ext: i32
588 ### `spirv.Constant`
590 At the moment `spirv.Constant` conversion supports scalar and vector constants
591 **only**.
593 #### Mapping
595 `spirv.Constant` is mapped to `llvm.mlir.constant`. This is a straightforward
596 conversion pattern with a special case when the argument is signed or unsigned.
598 #### Special case
600 SPIR-V constant can be a signed or unsigned integer. Since LLVM Dialect does not
601 have signedness semantics, this case should be handled separately.
603 The conversion casts constant value attribute to a signless integer or a vector
604 of signless integers. This is correct because in SPIR-V, like in LLVM, how to
605 interpret an integer number is also dictated by the opcode. However, in reality
606 hardware implementation might show unexpected behavior. Therefore, it is better
607 to handle it case-by-case, given that the purpose of the conversion is not to
608 cover all possible corner cases.
610 ```mlir
611 // %0 = llvm.mlir.constant(0 : i8) : i8
612 %0 = spirv.Constant  0 : i8
614 // %1 = llvm.mlir.constant(dense<[2, 3, 4]> : vector<3xi32>) : vector<3xi32>
615 %1 = spirv.Constant dense<[2, 3, 4]> : vector<3xui32>
618 ### Not implemented ops
620 There is no support of the following ops:
622 *   All atomic ops
623 *   All group ops
624 *   All matrix ops
625 *   All CL ops
627 As well as:
629 *   spirv.CompositeConstruct
630 *   spirv.ControlBarrier
631 *   spirv.CopyMemory
632 *   spirv.FMod
633 *   spirv.GL.Acos
634 *   spirv.GL.Asin
635 *   spirv.GL.Atan
636 *   spirv.GL.Cosh
637 *   spirv.GL.FSign
638 *   spirv.GL.SAbs
639 *   spirv.GL.Sinh
640 *   spirv.GL.SSign
641 *   spirv.MemoryBarrier
642 *   spirv.mlir.referenceof
643 *   spirv.SMod
644 *   spirv.SpecConstant
645 *   spirv.Unreachable
646 *   spirv.VectorExtractDynamic
648 ## Control flow conversion
650 ### Branch ops
652 `spirv.Branch` and `spirv.BranchConditional` are mapped to `llvm.br` and
653 `llvm.cond_br`. Branch weights for `spirv.BranchConditional` are mapped to
654 corresponding `branch_weights` attribute of `llvm.cond_br`. When translated to
655 proper LLVM, `branch_weights` are converted into LLVM metadata associated with
656 the conditional branch.
658 ### `spirv.FunctionCall`
660 `spirv.FunctionCall` maps to `llvm.call`. For example:
662 ```mlir
663 %0 = spirv.FunctionCall @foo() : () -> i32    =>    %0 = llvm.call @foo() : () -> f32
664 spirv.FunctionCall @bar(%0) : (i32) -> ()     =>    llvm.call @bar(%0) : (f32) -> ()
667 ### `spirv.mlir.selection` and `spirv.mlir.loop`
669 Control flow within `spirv.mlir.selection` and `spirv.mlir.loop` is lowered directly
670 to LLVM via branch ops. The conversion can only be applied to selection or loop
671 with all blocks being reachable. Moreover, selection and loop control attributes
672 (such as `Flatten` or `Unroll`) are not supported at the moment.
674 ```mlir
675 // Conversion of selection
676 %cond = spirv.Constant true                               %cond = llvm.mlir.constant(true) : i1
677 spirv.mlir.selection {
678   spirv.BranchConditional %cond, ^true, ^false            llvm.cond_br %cond, ^true, ^false
680 ^true:                                                                                              ^true:
681   // True block code                                    // True block code
682   spirv.Branch ^merge                             =>      llvm.br ^merge
684 ^false:                                               ^false:
685   // False block code                                   // False block code
686   spirv.Branch ^merge                                     llvm.br ^merge
688 ^merge:                                               ^merge:
689   spirv.mlir.merge                                            llvm.br ^continue
691 // Remaining code                                                                           ^continue:
692                                                         // Remaining code
695 ```mlir
696 // Conversion of loop
697 %cond = spirv.Constant true                               %cond = llvm.mlir.constant(true) : i1
698 spirv.mlir.loop {
699   spirv.Branch ^header                                    llvm.br ^header
701 ^header:                                              ^header:
702   // Header code                                        // Header code
703   spirv.BranchConditional %cond, ^body, ^merge    =>      llvm.cond_br %cond, ^body, ^merge
705 ^body:                                                ^body:
706   // Body code                                          // Body code
707   spirv.Branch ^continue                                  llvm.br ^continue
709 ^continue:                                            ^continue:
710   // Continue code                                      // Continue code
711   spirv.Branch ^header                                    llvm.br ^header
713 ^merge:                                               ^merge:
714   spirv.mlir.merge                                            llvm.br ^remaining
716 // Remaining code                                     ^remaining:
717                                                         // Remaining code
720 ## Decorations conversion
722 **Note: these conversions have not been implemented yet**
724 ## GLSL extended instruction set
726 This section describes how SPIR-V ops from GLSL extended instructions set are
727 mapped to LLVM Dialect.
729 ### Direct conversions
731 SPIR-V Dialect op | LLVM Dialect op
732 :---------------: | :----------------:
733 `spirv.GL.Ceil`     | `llvm.intr.ceil`
734 `spirv.GL.Cos`      | `llvm.intr.cos`
735 `spirv.GL.Exp`      | `llvm.intr.exp`
736 `spirv.GL.FAbs`     | `llvm.intr.fabs`
737 `spirv.GL.Floor`    | `llvm.intr.floor`
738 `spirv.GL.FMax`     | `llvm.intr.maxnum`
739 `spirv.GL.FMin`     | `llvm.intr.minnum`
740 `spirv.GL.Log`      | `llvm.intr.log`
741 `spirv.GL.Sin`      | `llvm.intr.sin`
742 `spirv.GL.Sqrt`     | `llvm.intr.sqrt`
743 `spirv.GL.SMax`     | `llvm.intr.smax`
744 `spirv.GL.SMin`     | `llvm.intr.smin`
746 ### Special cases
748 `spirv.InverseSqrt` is mapped to:
750 ```mlir
751                                            %one  = llvm.mlir.constant(1.0 : f32) : f32
752 %res = spirv.InverseSqrt %arg : f32    =>    %sqrt = "llvm.intr.sqrt"(%arg) : (f32) -> f32
753                                            %res  = fdiv %one, %sqrt : f32
756 `spirv.Tan` is mapped to:
758 ```mlir
759                                    %sin = "llvm.intr.sin"(%arg) : (f32) -> f32
760 %res = spirv.Tan %arg : f32    =>    %cos = "llvm.intr.cos"(%arg) : (f32) -> f32
761                                    %res = fdiv %sin, %cos : f32
764 `spirv.Tanh` is modelled using the equality `tanh(x) = {exp(2x) - 1}/{exp(2x) +
765 1}`:
767 ```mlir
768                                      %two   = llvm.mlir.constant(2.0: f32) : f32
769                                      %2xArg = llvm.fmul %two, %arg : f32
770                                      %exp   = "llvm.intr.exp"(%2xArg) : (f32) -> f32
771 %res = spirv.Tanh %arg : f32     =>    %one   = llvm.mlir.constant(1.0 : f32) : f32
772                                      %num   = llvm.fsub %exp, %one : f32
773                                      %den   = llvm.fadd %exp, %one : f32
774                                      %res   = llvm.fdiv %num, %den : f32
777 ## Function conversion and related ops
779 This section describes the conversion of function-related operations from SPIR-V
780 to LLVM dialect.
782 ### `spirv.func`
784 This op declares or defines a SPIR-V function and it is converted to
785 `llvm.func`. This conversion handles signature conversion, and function control
786 attributes remapping to LLVM dialect function
787 [`passthrough` attribute](Dialects/LLVM.md/#attribute-pass-through).
789 The following mapping is used to map
790 [SPIR-V function control][SPIRVFunctionAttributes] to
791 [LLVM function attributes][LLVMFunctionAttributes]:
793 SPIR-V Function Control Attributes | LLVM Function Attributes
794 :--------------------------------: | :---------------------------:
795 None                               | No function attributes passed
796 Inline                             | `alwaysinline`
797 DontInline                         | `noinline`
798 Pure                               | `readonly`
799 Const                              | `readnone`
801 ### `spirv.Return` and `spirv.ReturnValue`
803 In LLVM IR, functions may return either 1 or 0 value. Hence, we map both ops to
804 `llvm.return` with or without a return value.
806 ## Module ops
808 Module in SPIR-V has one region that contains one block. It is defined via
809 `spirv.module` op that also takes a range of attributes:
811 *   Addressing model
812 *   Memory model
813 *   Version-Capability-Extension attribute
815 `spirv.module` is converted into `ModuleOp`. This plays a role of enclosing scope
816 to LLVM ops. At the moment, SPIR-V module attributes are ignored.
818 ## SPIR-V CPU Runner Tests
820 The `mlir-cpu-runner` has support for executing a `gpu` dialect kernel on the
821 CPU via SPIR-V to LLVM dialect conversion. This is referred to as the "SPIR-V
822 CPU Runner". The `--link-nested-modules` flag needs to be passed for this.
823 Currently, only single-threaded kernels are supported.
825 To build the required runtime libaries, add the following option to `cmake`:
826 `-DMLIR_ENABLE_SPIRV_CPU_RUNNER=1`
828 ### Pipeline
830 The `gpu` module with the kernel and the host code undergo the following
831 transformations:
833 *   Convert the `gpu` module into SPIR-V dialect, lower ABI attributes and
834     update version, capability and extension.
836 *   Emulate the kernel call by converting the launching operation into a normal
837     function call. The data from the host side to the device is passed via
838     copying to global variables. These are created in both the host and the
839     kernel code and later linked when nested modules are folded.
841 *   Convert SPIR-V dialect kernel to LLVM dialect via the new conversion path.
843 After these passes, the IR transforms into a nested LLVM module - a main module
844 representing the host code and a kernel module. These modules are linked and
845 executed using `ExecutionEngine`.
847 ### Walk-through
849 This section gives a detailed overview of the IR changes while running
850 SPIR-V CPU Runner tests. First, consider that we have the following IR. (For
851 simplicity some type annotations and function implementations have been
852 omitted).
854 ```mlir
855 gpu.module @foo {
856   gpu.func @bar(%arg: memref<8xi32>) {
857     // Kernel code.
858     gpu.return
859   }
862 func.func @main() {
863   // Fill the buffer with some data
864   %buffer = memref.alloc : memref<8xi32>
865   %data = ...
866   call fillBuffer(%buffer, %data)
868   "gpu.launch_func"(/*grid dimensions*/, %buffer) {
869     kernel = @foo::bar
870   }
874 Lowering `gpu` dialect to SPIR-V dialect results in
876 ```mlir
877 spirv.module @__spv__foo /*VCE triple and other metadata here*/ {
878   spirv.GlobalVariable @__spv__foo_arg bind(0,0) : ...
879   spirv.func @bar() {
880     // Kernel code.
881   }
882   spirv.EntryPoint @bar, ...
885 func.func @main() {
886   // Fill the buffer with some data.
887   %buffer = memref.alloc : memref<8xi32>
888   %data = ...
889   call fillBuffer(%buffer, %data)
891   "gpu.launch_func"(/*grid dimensions*/, %buffer) {
892     kernel = @foo::bar
893   }
897 Then, the lowering from standard dialect to LLVM dialect is applied to the host
898 code.
900 ```mlir
901 spirv.module @__spv__foo /*VCE triple and other metadata here*/ {
902   spirv.GlobalVariable @__spv__foo_arg bind(0,0) : ...
903   spirv.func @bar() {
904     // Kernel code.
905   }
906   spirv.EntryPoint @bar, ...
909 // Kernel function declaration.
910 llvm.func @__spv__foo_bar() : ...
912 llvm.func @main() {
913   // Fill the buffer with some data.
914   llvm.call fillBuffer(%buffer, %data)
916   // Copy data to the global variable, call kernel, and copy the data back.
917   %addr = llvm.mlir.addressof @__spv__foo_arg_descriptor_set0_binding0 : ...
918   "llvm.intr.memcpy"(%addr, %buffer) : ...
919   llvm.call @__spv__foo_bar()
920   "llvm.intr.memcpy"(%buffer, %addr) : ...
922   llvm.return
926 Finally, SPIR-V module is converted to LLVM and the symbol names are resolved
927 for the linkage.
929 ```mlir
930 module @__spv__foo {
931   llvm.mlir.global @__spv__foo_arg_descriptor_set0_binding0 : ...
932   llvm.func @__spv__foo_bar() {
933     // Kernel code.
934   }
937 // Kernel function declaration.
938 llvm.func @__spv__foo_bar() : ...
940 llvm.func @main() {
941   // Fill the buffer with some data.
942   llvm.call fillBuffer(%buffer, %data)
944   // Copy data to the global variable, call kernel, and copy the data back.
945   %addr = llvm.mlir.addressof @__spv__foo_arg_descriptor_set0_binding0 : ...
946   "llvm.intr.memcpy"(%addr, %buffer) : ...
947   llvm.call @__spv__foo_bar()
948   "llvm.intr.memcpy"(%buffer, %addr) : ...
950   llvm.return
954 [LLVMFunctionAttributes]: https://llvm.org/docs/LangRef.html#function-attributes
955 [SPIRVFunctionAttributes]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_a_id_function_control_a_function_control
956 [VulkanLayoutUtils]: https://github.com/llvm/llvm-project/blob/main/mlir/include/mlir/Dialect/SPIRV/LayoutUtils.h