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` | `!llvm.half` 32`f32` | `!llvm.float` 33`f64` | `!llvm.double` 34 35### Vector types 36 37SPIR-V Dialect | LLVM Dialect 38:----------------------------------: | :----------------------------------: 39`vector<<count> x <scalar-type>>` | `!llvm.vec<<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) : !llvm.i32 163%0 = spv.Not %op : i32 => %0 = llvm.xor %op, %mask : !llvm.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 : !llvm.vec<2 x i8> 192 %zero = llvm.mlir.constant(0 : i32) : !llvm.i32 193 %offset1 = llvm.insertelement %offset, %offset0[%zero : !llvm.i32] : !llvm.vec<2 x i8> 194 %one = llvm.mlir.constant(1 : i32) : !llvm.i32 195 %vec_offset = llvm.insertelement %offset, %offset1[%one : !llvm.i32] : !llvm.vec<2 x i8> 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: !llvm.vec<2 x i8> to !llvm.vec<2 x i32> 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 have a 213 defined behaviour with `offset` and `count` being less than the size of 214 `base`. It creates a natural upper bound on what values `offset` and `count` 215 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) : !llvm.i32 231// %t0 = llvm.shl %minus_one, %count : !llvm.i32 232// %t1 = llvm.xor %t0, %minus_one : !llvm.i32 233// %t2 = llvm.shl %t1, %offset : !llvm.i32 234// %mask = llvm.xor %t2, %minus_one : !llvm.i32 235 236// Extract unchanged bits from the Base 237// %new_base = llvm.and %base, %mask : !llvm.i32 238 239// Insert new bits 240// %sh_insert = llvm.shl %insert, %offset : !llvm.i32 241// %res = llvm.or %new_base, %sh_insert : !llvm.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) : !llvm.i32 255// %t0 = llvm.add %count, %offset : !llvm.i32 256// %t1 = llvm.sub %size, %t0 : !llvm.i32 257 258// Shift left and then right to extract the bits 259// %sh_left = llvm.shl %base, %t1 : !llvm.i32 260// %t2 = llvm.add %offset, %t1 : !llvm.i32 261// %res = llvm.ashr %sh_left, %t2 : !llvm.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) : !llvm.i32 274// %t0 = llvm.shl %minus_one, %count : !llvm.i32 275// mask = llvm.xor %t0, %minus_one : !llvm.i32 276 277// Shift Base and apply mask 278// %sh_base = llvm.lshr %base, %offset : !llvm.i32 279// %res = llvm.and %sh_base, %mask : !llvm.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 371**Note: these conversions are likely to be changed in the future** 372 373First of all, it is important to note that there is no direct representation of 374entry points in LLVM. At the moment, we choose to **remove these ops**, assuming 375that the module generated from SPIR-V has no other internal functions (This 376assumption is actually made in [`mlir-spirv-cpu-runner`](#`mlir-spirv-cpu-runner`)). 377 378However, these ops can be used to see which functions in the module are entry 379point functions. `spv.ExecutionMode` also carries the metadata associated with 380the entry point such as `LocalSize`, which indicates the workgroup size in the 381x, y, and z dimensions. It will be useful to represent this on the LLVM side 382(TODO). 383 384### Logical ops 385 386Logical ops follow a similar pattern as bitwise ops, with the difference that 387they operate on `i1` or vector of `i1` values. The following mapping is used to 388emulate SPIR-V ops behaviour: 389 390SPIR-V Dialect op | LLVM Dialect op 391:-----------------------------------: | :-----------------------------------: 392`spv.LogicalAnd` | `llvm.and` 393`spv.LogicalOr` | `llvm.or` 394`spv.LogicalEqual` | `llvm.icmp "eq"` 395`spv.LogicalNotEqual` | `llvm.icmp "ne"` 396 397`spv.LogicalNot` has the same conversion pattern as bitwise `spv.Not`. It is 398modelled with `xor` operation with a mask with all bits set. 399 400```mlir 401 %mask = llvm.mlir.constant(-1 : i1) : !llvm.i1 402%0 = spv.LogicalNot %op : i1 => %0 = llvm.xor %op, %mask : !llvm.i1 403``` 404 405### Memory ops 406 407This section describes the conversion patterns for SPIR-V dialect operations 408that concern memory. 409 410#### `spv.AccessChain` 411 412`spv.AccessChain` is mapped to `llvm.getelementptr` op. In order to create a 413valid LLVM op, we also add a 0 index to the `spv.AccessChain`'s indices list in 414order to go through the pointer. 415 416```mlir 417// Access the 1st element of the array 418%i = spv.constant 1: i32 419%var = spv.Variable : !spv.ptr<!spv.struct<f32, !spv.array<4xf32>>, Function> 420%el = spv.AccessChain %var[%i, %i] : !spv.ptr<!spv.struct<f32, !spv.array<4xf32>>, Function>, i32, i32 421 422// Corresponding LLVM dialect code 423%i = ... 424%var = ... 425%0 = llvm.mlir.constant(0 : i32) : !llvm.i32 426%el = llvm.getelementptr %var[%0, %i, %i] : (!llvm.ptr<struct<packed (float, array<4 x float>)>>, !llvm.i32, !llvm.i32, !llvm.i32) 427``` 428 429#### `spv.Load` and `spv.Store` 430 431These ops are converted to their LLVM counterparts: `llvm.load` and 432`llvm.store`. If the op has a memory access attribute, then there are the 433following cases, based on the value of the attribute: 434 435* **Aligned**: alignment is passed on to LLVM op builder, for example: 436 ```mlir 437 // llvm.store %ptr, %val {alignment = 4 : i64} : !llvm.ptr<float> 438 spv.Store "Function" %ptr, %val ["Aligned", 4] : f32 439 ``` 440* **None**: same case as if there is no memory access attribute. 441 442* **Nontemporal**: set `nontemporal` flag, for example: 443 ```mlir 444 // %res = llvm.load %ptr {nontemporal} : !llvm.ptr<float> 445 %res = spv.Load "Function" %ptr ["Nontemporal"] : f32 446 ``` 447* **Volatile**: mark the op as `volatile`, for example: 448 ```mlir 449 // %res = llvm.load volatile %ptr : !llvm.ptr<float> 450 %res = spv.Load "Function" %ptr ["Volatile"] : f32 451 ``` 452Otherwise the conversion fails as other cases (`MakePointerAvailable`, 453`MakePointerVisible`, `NonPrivatePointer`) are not supported yet. 454 455#### `spv.globalVariable` and `spv._address_of` 456 457`spv.globalVariable` is modelled with `llvm.mlir.global` op. However, there 458is a difference that has to be pointed out. 459 460In SPIR-V dialect, the global variable returns a pointer, whereas in LLVM 461dialect the global holds an actual value. This difference is handled by 462`spv._address_of` and `llvm.mlir.addressof` ops that both return a pointer and 463are used to reference the global. 464 465```mlir 466// Original SPIR-V module 467spv.module Logical GLSL450 { 468 spv.globalVariable @struct : !spv.ptr<!spv.struct<f32, !spv.array<10xf32>>, Private> 469 spv.func @func() -> () "None" { 470 %0 = spv._address_of @struct : !spv.ptr<!spv.struct<f32, !spv.array<10xf32>>, Private> 471 spv.Return 472 } 473} 474 475// Converted result 476module { 477 llvm.mlir.global private @struct() : !llvm.struct<packed (float, [10 x float])> 478 llvm.func @func() { 479 %0 = llvm.mlir.addressof @struct : !llvm.ptr<struct<packed (float, [10 x float])>> 480 llvm.return 481 } 482} 483``` 484 485The SPIR-V to LLVM conversion does not involve modelling of workgroups. 486Hence, we say that only current invocation is in conversion's scope. This means 487that global variables with pointers of `Input`, `Output`, and `Private` storage 488classes are supported. Also, `StorageBuffer` storage class is allowed for 489executing [`mlir-spirv-cpu-runner`](#`mlir-spirv-cpu-runner`). 490 491Moreover, `bind` that specifies the descriptor set and the binding number and 492`built_in` that specifies SPIR-V `BuiltIn` decoration have no conversion into 493LLVM dialect. 494 495Currently `llvm.mlir.global`s are created with `private` linkage for `Private` 496storage class and `External` for other storage classes, based on SPIR-V spec: 497 498> By default, functions and global variables are private to a module and cannot 499be accessed by other modules. However, a module may be written to export or 500import functions and global (module scope) variables. 501 502If the global variable's pointer has `Input` storage class, then a `constant` 503flag is added to LLVM op: 504 505```mlir 506spv.globalVariable @var : !spv.ptr<f32, Input> => llvm.mlir.global external constant @var() : !llvm.float 507``` 508 509#### `spv.Variable` 510 511Per SPIR-V dialect spec, `spv.Variable` allocates an object in memory, resulting 512in a pointer to it, which can be used with `spv.Load` and `spv.Store`. It is 513also a function-level variable. 514 515`spv.Variable` is modelled as `llvm.alloca` op. If initialized, an additional 516store instruction is used. Note that there is no initialization for arrays and 517structs since constants of these types are not supported in LLVM dialect (TODO). 518Also, at the moment initialization is only possible via `spv.constant`. 519 520```mlir 521// Conversion of VariableOp without initialization 522 %size = llvm.mlir.constant(1 : i32) : !llvm.i32 523%res = spv.Variable : !spv.ptr<vector<3xf32>, Function> => %res = llvm.alloca %size x !llvm.vec<3 x float> : (!llvm.i32) -> !llvm.ptr<vec<3 x float>> 524 525// Conversion of VariableOp with initialization 526 %c = llvm.mlir.constant(0 : i64) : !llvm.i64 527%c = spv.constant 0 : i64 %size = llvm.mlir.constant(1 : i32) : !llvm.i32 528%res = spv.Variable init(%c) : !spv.ptr<i64, Function> => %res = llvm.alloca %[[SIZE]] x !llvm.i64 : (!llvm.i32) -> !llvm.ptr<i64> 529 llvm.store %c, %res : !llvm.ptr<i64> 530``` 531 532Note that simple conversion to `alloca` may not be sufficient if the code has 533some scoping. For example, if converting ops executed in a loop into `alloca`s, 534a stack overflow may occur. For this case, `stacksave`/`stackrestore` pair can 535be used (TODO). 536 537### Miscellaneous ops with direct conversions 538 539There are multiple SPIR-V ops that do not fit in a particular group but can be 540converted directly to LLVM dialect. Their conversion is addressed in this 541section. 542 543SPIR-V Dialect op | LLVM Dialect op 544:-----------------------------------: | :-----------------------------------: 545`spv.Select` | `llvm.select` 546`spv.Undef` | `llvm.mlir.undef` 547 548### Shift ops 549 550Shift operates on two operands: `shift` and `base`. 551 552In SPIR-V dialect, `shift` and `base` may have different bit width. On the 553contrary, in LLVM Dialect both `base` and `shift` have to be of the same 554bitwidth. This leads to the following conversions: 555 556* if `base` has the same bitwidth as `shift`, the conversion is 557 straightforward. 558 559* if `base` has a greater bit width than `shift`, shift is sign or zero 560 extended first. Then the extended value is passed to the shift. 561 562* otherwise, the conversion is considered to be illegal. 563 564```mlir 565// Shift without extension 566%res0 = spv.ShiftRightArithmetic %0, %2 : i32, i32 => %res0 = llvm.ashr %0, %2 : !llvm.i32 567 568// Shift with extension 569 %ext = llvm.sext %1 : !llvm.i16 to !llvm.i32 570%res1 = spv.ShiftRightArithmetic %0, %1 : i32, i16 => %res1 = llvm.ashr %0, %ext: !llvm.i32 571``` 572 573### `spv.constant` 574 575At the moment `spv.constant` conversion supports scalar and vector constants 576**only**. 577 578#### Mapping 579 580`spv.constant` is mapped to `llvm.mlir.constant`. This is a straightforward 581conversion pattern with a special case when the argument is signed or unsigned. 582 583#### Special case 584 585SPIR-V constant can be a signed or unsigned integer. Since LLVM Dialect does not 586have signedness semantics, this case should be handled separately. 587 588The conversion casts constant value attribute to a signless integer or a vector 589of signless integers. This is correct because in SPIR-V, like in LLVM, how to 590interpret an integer number is also dictated by the opcode. However, in reality 591hardware implementation might show unexpected behavior. Therefore, it is better 592to handle it case-by-case, given that the purpose of the conversion is not to 593cover all possible corner cases. 594 595```mlir 596// %0 = llvm.mlir.constant(0 : i8) : !llvm.i8 597%0 = spv.constant 0 : i8 598 599// %1 = llvm.mlir.constant(dense<[2, 3, 4]> : vector<3xi32>) : !llvm.vec<3 x i32> 600%1 = spv.constant dense<[2, 3, 4]> : vector<3xui32> 601``` 602 603### Not implemented ops 604 605There is no support of the following ops: 606 607* All Atomic ops 608* All matrix ops 609* All GroupNonUniform ops 610 611As well as: 612 613* spv.CompositeConstruct 614* spv.ControlBarrier 615* spv.CopyMemory 616* spv.FMod 617* spv.GLSL.SAbs 618* spv.GLSL.SSign 619* spv.GLSL.FSign 620* spv.MemoryBarrier 621* spv._reference_of 622* spv.SMod 623* spv.specConstant 624* spv.SubgroupBallotKHR 625* spv.Unreachable 626 627## Control flow conversion 628 629### Branch ops 630 631`spv.Branch` and `spv.BranchConditional` are mapped to `llvm.br` and 632`llvm.cond_br`. Branch weights for `spv.BranchConditional` are mapped to 633corresponding `branch_weights` attribute of `llvm.cond_br`. When translated to 634proper LLVM, `branch_weights` are converted into LLVM metadata associated with 635the conditional branch. 636 637### `spv.FunctionCall` 638 639`spv.FunctionCall` maps to `llvm.call`. For example: 640 641```mlir 642%0 = spv.FunctionCall @foo() : () -> i32 => %0 = llvm.call @foo() : () -> !llvm.float 643spv.FunctionCall @bar(%0) : (i32) -> () => llvm.call @bar(%0) : (!llvm.float) -> () 644``` 645 646### `spv.selection` and `spv.loop` 647 648Control flow within `spv.selection` and `spv.loop` is lowered directly to LLVM 649via branch ops. The conversion can only be applied to selection or loop with all 650blocks being reachable. Moreover, selection and loop control attributes (such as 651`Flatten` or `Unroll`) are not supported at the moment. 652 653```mlir 654// Conversion of selection 655%cond = spv.constant true %cond = llvm.mlir.constant(true) : !llvm.i1 656spv.selection { 657 spv.BranchConditional %cond, ^true, ^false llvm.cond_br %cond, ^true, ^false 658 659^true: ^true: 660 // True block code // True block code 661 spv.Branch ^merge => llvm.br ^merge 662 663^false: ^false: 664 // False block code // False block code 665 spv.Branch ^merge llvm.br ^merge 666 667^merge: ^merge: 668 spv._merge llvm.br ^continue 669} 670// Remaining code ^continue: 671 // Remaining code 672``` 673 674```mlir 675// Conversion of loop 676%cond = spv.constant true %cond = llvm.mlir.constant(true) : !llvm.i1 677spv.loop { 678 spv.Branch ^header llvm.br ^header 679 680^header: ^header: 681 // Header code // Header code 682 spv.BranchConditional %cond, ^body, ^merge => llvm.cond_br %cond, ^body, ^merge 683 684^body: ^body: 685 // Body code // Body code 686 spv.Branch ^continue llvm.br ^continue 687 688^continue: ^continue: 689 // Continue code // Continue code 690 spv.Branch ^header llvm.br ^header 691 692^merge: ^merge: 693 spv._merge llvm.br ^remaining 694} 695// Remaining code ^remaining: 696 // Remaining code 697``` 698 699## Decorations conversion 700 701**Note: these conversions have not been implemented yet** 702 703## GLSL extended instruction set 704 705This section describes how SPIR-V ops from GLSL extended instructions set are 706mapped to LLVM Dialect. 707 708### Direct conversions 709 710SPIR-V Dialect op | LLVM Dialect op 711:-----------------------------------: | :-----------------------------------: 712`spv.GLSL.Ceil` | `llvm.intr.ceil` 713`spv.GLSL.Cos` | `llvm.intr.cos` 714`spv.GLSL.Exp` | `llvm.intr.exp` 715`spv.GLSL.FAbs` | `llvm.intr.fabs` 716`spv.GLSL.Floor` | `llvm.intr.floor` 717`spv.GLSL.FMax` | `llvm.intr.maxnum` 718`spv.GLSL.FMin` | `llvm.intr.minnum` 719`spv.GLSL.Log` | `llvm.intr.log` 720`spv.GLSL.Sin` | `llvm.intr.sin` 721`spv.GLSL.Sqrt` | `llvm.intr.sqrt` 722`spv.GLSL.SMax` | `llvm.intr.smax` 723`spv.GLSL.SMin` | `llvm.intr.smin` 724 725### Special cases 726 727`spv.InverseSqrt` is mapped to: 728```mlir 729 %one = llvm.mlir.constant(1.0 : f32) : !llvm.float 730%res = spv.InverseSqrt %arg : f32 => %sqrt = "llvm.intr.sqrt"(%arg) : (!llvm.float) -> !llvm.float 731 %res = fdiv %one, %sqrt : !llvm.float 732``` 733 734`spv.Tan` is mapped to: 735```mlir 736 %sin = "llvm.intr.sin"(%arg) : (!llvm.float) -> !llvm.float 737%res = spv.Tan %arg : f32 => %cos = "llvm.intr.cos"(%arg) : (!llvm.float) -> !llvm.float 738 %res = fdiv %sin, %cos : !llvm.float 739``` 740 741`spv.Tanh` is modelled using the equality `tanh(x) = {exp(2x) - 1}/{exp(2x) + 1}`: 742```mlir 743 %two = llvm.mlir.constant(2.0: f32) : !llvm.float 744 %2xArg = llvm.fmul %two, %arg : !llvm.float 745 %exp = "llvm.intr.exp"(%2xArg) : (!llvm.float) -> !llvm.float 746%res = spv.Tanh %arg : f32 => %one = llvm.mlir.constant(1.0 : f32) : !llvm.float 747 %num = llvm.fsub %exp, %one : !llvm.float 748 %den = llvm.fadd %exp, %one : !llvm.float 749 %res = llvm.fdiv %num, %den : !llvm.float 750``` 751 752## Function conversion and related ops 753 754This section describes the conversion of function-related operations from SPIR-V 755to LLVM dialect. 756 757### `spv.func` 758This op declares or defines a SPIR-V function and it is converted to `llvm.func`. 759This conversion handles signature conversion, and function control attributes 760remapping to LLVM dialect function [`passthrough` attribute](Dialects/LLVM.md#Attribute-pass-through). 761 762The following mapping is used to map [SPIR-V function control](SPIRVFunctionAttributes) to 763[LLVM function attributes](LLVMFunctionAttributes): 764 765SPIR-V Function Control Attributes | LLVM Function Attributes 766:-----------------------------------: | :-----------------------------------: 767None | No function attributes passed 768Inline | `alwaysinline` 769DontInline | `noinline` 770Pure | `readonly` 771Const | `readnone` 772 773### `spv.Return` and `spv.ReturnValue` 774 775In LLVM IR, functions may return either 1 or 0 value. Hence, we map both ops to 776`llvm.return` with or without a return value. 777 778## Module ops 779 780Module in SPIR-V has one region that contains one block. It is defined via 781`spv.module` op that also takes a range of attributes: 782 783* Addressing model 784* Memory model 785* Version-Capability-Extension attribute 786 787`spv.module` is converted into `ModuleOp`. This plays a role of enclosing scope 788to LLVM ops. At the moment, SPIR-V module attributes are ignored. 789 790`spv._module_end` is mapped to an equivalent terminator `ModuleTerminatorOp`. 791 792## `mlir-spirv-cpu-runner` 793 794**Note: this is a section in progress, more information will appear soon** 795 796[LLVMFunctionAttributes]: https://llvm.org/docs/LangRef.html#function-attributes 797[SPIRVFunctionAttributes]: https://www.khronos.org/registry/spir-v/specs/unified1/SPIRV.html#_a_id_function_control_a_function_control 798[VulkanLayoutUtils]: https://github.com/llvm/llvm-project/blob/master/mlir/include/mlir/Dialect/SPIRV/LayoutUtils.h 799