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