1# Conversion to the LLVM Dialect
2
3Conversion from the Standard to the [LLVM Dialect](Dialects/LLVM.md) can be
4performed by the specialized dialect conversion pass by running:
5
6```shell
7mlir-opt -convert-std-to-llvm <filename.mlir>
8```
9
10It performs type and operation conversions for a subset of operations from
11standard dialect (operations on scalars and vectors, control flow operations) as
12described in this document. We use the terminology defined by the
13[LLVM IR Dialect description](Dialects/LLVM.md) throughout this document.
14
15[TOC]
16
17## Type Conversion
18
19### Scalar Types
20
21Scalar types are converted to their LLVM counterparts if they exist. The
22following conversions are currently implemented:
23
24-   `i*` converts to `!llvm.i*`
25-   `f16` converts to `!llvm.half`
26-   `f32` converts to `!llvm.float`
27-   `f64` converts to `!llvm.double`
28
29Note: `bf16` type is not supported by LLVM IR and cannot be converted.
30
31### Index Type
32
33Index type is converted to a wrapped LLVM IR integer with bitwidth equal to the
34bitwidth of the pointer size as specified by the
35[data layout](https://llvm.org/docs/LangRef.html#data-layout) of the LLVM module
36[contained](Dialects/LLVM.md#context-and-module-association) in the LLVM Dialect
37object. For example, on x86-64 CPUs it converts to `!llvm.i64`.
38
39### Vector Types
40
41LLVM IR only supports *one-dimensional* vectors, unlike MLIR where vectors can
42be multi-dimensional. Vector types cannot be nested in either IR. In the
43one-dimensional case, MLIR vectors are converted to LLVM IR vectors of the same
44size with element type converted using these conversion rules. In the
45n-dimensional case, MLIR vectors are converted to (n-1)-dimensional array types
46of one-dimensional vectors.
47
48For example, `vector<4 x f32>` converts to `!llvm<"<4 x float>">` and `vector<4
49x 8 x 16 x f32>` converts to `!llvm<"[4 x [8 x <16 x float>]]">`.
50
51### Memref Types
52
53Memref types in MLIR have both static and dynamic information associated with
54them. The dynamic information comprises the buffer pointer as well as sizes and
55strides of any dynamically-sized dimensions. Memref types are normalized and
56converted to a descriptor that is only dependent on the rank of the memref. The
57descriptor contains:
58
591.  the pointer to the data buffer, followed by
602.  the pointer to properly aligned data payload that the memref indexes,
61    followed by
623.  a lowered `index`-type integer containing the distance between the beginning
63    of the buffer and the first element to be accessed through the memref,
64    followed by
654.  an array containing as many `index`-type integers as the rank of the memref:
66    the array represents the size, in number of elements, of the memref along
67    the given dimension. For constant MemRef dimensions, the corresponding size
68    entry is a constant whose runtime value must match the static value,
69    followed by
705.  a second array containing as many 64-bit integers as the rank of the MemRef:
71    the second array represents the "stride" (in tensor abstraction sense), i.e.
72    the number of consecutive elements of the underlying buffer.
73
74For constant memref dimensions, the corresponding size entry is a constant whose
75runtime value matches the static value. This normalization serves as an ABI for
76the memref type to interoperate with externally linked functions. In the
77particular case of rank `0` memrefs, the size and stride arrays are omitted,
78resulting in a struct containing two pointers + offset.
79
80Examples:
81
82```mlir
83memref<f32> -> !llvm<"{ float*, float*, i64 }">
84memref<1 x f32> -> !llvm<"{ float*, float*, i64, [1 x i64], [1 x i64] }">
85memref<? x f32> -> !llvm<"{ float*, float*, i64, [1 x i64], [1 x i64] }">
86memref<10x42x42x43x123 x f32> -> !llvm<"{ float*, float*, i64, [5 x i64], [5 x i64] }">
87memref<10x?x42x?x123 x f32> -> !llvm<"{ float*, float*, i64, [5 x i64], [5 x i64]  }">
88
89// Memref types can have vectors as element types
90memref<1x? x vector<4xf32>> -> !llvm<"{ <4 x float>*, <4 x float>*, i64, [1 x i64], [1 x i64] }">
91```
92
93If the rank of the memref is unknown at compile time, the memref is converted to
94an unranked descriptor that contains:
95
961.  a 64-bit integer representing the dynamic rank of the memref, followed by
972.  a pointer to a ranked memref descriptor with the contents listed above.
98
99Dynamic ranked memrefs should be used only to pass arguments to external library
100calls that expect a unified memref type. The called functions can parse any
101unranked memref descriptor by reading the rank and parsing the enclosed ranked
102descriptor pointer.
103
104Examples:
105
106```mlir
107// unranked descriptor
108memref<*xf32> -> !llvm<"{i64, i8*}">
109```
110
111**In function signatures,** `memref` is passed as a _pointer_ to the structured
112defined above to comply with the calling convention.
113
114Example:
115
116```mlir
117// A function type with memref as argument
118(memref<?xf32>) -> ()
119// is transformed into the LLVM function with pointer-to-structure argument.
120!llvm<"void({ float*, float*, i64, [1 x i64], [1 x i64]}*) ">
121```
122
123### Function Types
124
125Function types get converted to LLVM function types. The arguments are converted
126individually according to these rules. The result types need to accommodate the
127fact that LLVM IR functions always have a return type, which may be a Void type.
128The converted function always has a single result type. If the original function
129type had no results, the converted function will have one result of the wrapped
130`void` type. If the original function type had one result, the converted
131function will also have one result converted using these rules. Otherwise, the result
132type will be a wrapped LLVM IR structure type where each element of the
133structure corresponds to one of the results of the original function, converted
134using these rules. In high-order functions, function-typed arguments and results
135are converted to a wrapped LLVM IR function pointer type (since LLVM IR does not
136allow passing functions to functions without indirection) with the pointee type
137converted using these rules.
138
139Examples:
140
141```mlir
142// zero-ary function type with no results.
143() -> ()
144// is converted to a zero-ary function with `void` result
145!llvm<"void ()">
146
147// unary function with one result
148(i32) -> (i64)
149// has its argument and result type converted, before creating the LLVM IR function type
150!llvm<"i64 (i32)">
151
152// binary function with one result
153(i32, f32) -> (i64)
154// has its arguments handled separately
155!llvm<"i64 (i32, float)">
156
157// binary function with two results
158(i32, f32) -> (i64, f64)
159// has its result aggregated into a structure type
160!llvm<"{i64, double} (i32, f32)">
161
162// function-typed arguments or results in higher-order functions
163(() -> ()) -> (() -> ())
164// are converted into pointers to functions
165!llvm<"void ()* (void ()*)">
166```
167
168## Calling Convention
169
170### Function Signature Conversion
171
172LLVM IR functions are defined by a custom operation. The function itself has a
173wrapped LLVM IR function type converted as described above. The function
174definition operation uses MLIR syntax.
175
176Examples:
177
178```mlir
179// zero-ary function type with no results.
180func @foo() -> ()
181// gets LLVM type void().
182llvm.func @foo() -> ()
183
184// function with one result
185func @bar(i32) -> (i64)
186// gets converted to LLVM type i64(i32).
187func @bar(!llvm.i32) -> !llvm.i64
188
189// function with two results
190func @qux(i32, f32) -> (i64, f64)
191// has its result aggregated into a structure type
192func @qux(!llvm.i32, !llvm.float) -> !llvm<"{i64, double}">
193
194// function-typed arguments or results in higher-order functions
195func @quux(() -> ()) -> (() -> ())
196// are converted into pointers to functions
197func @quux(!llvm<"void ()*">) -> !llvm<"void ()*">
198// the call flow is handled by the LLVM dialect `call` operation supporting both
199// direct and indirect calls
200```
201
202### Result Packing
203
204In case of multi-result functions, the returned values are inserted into a
205structure-typed value before being returned and extracted from it at the call
206site. This transformation is a part of the conversion and is transparent to the
207defines and uses of the values being returned.
208
209Example:
210
211```mlir
212func @foo(%arg0: i32, %arg1: i64) -> (i32, i64) {
213  return %arg0, %arg1 : i32, i64
214}
215func @bar() {
216  %0 = constant 42 : i32
217  %1 = constant 17 : i64
218  %2:2 = call @foo(%0, %1) : (i32, i64) -> (i32, i64)
219  "use_i32"(%2#0) : (i32) -> ()
220  "use_i64"(%2#1) : (i64) -> ()
221}
222
223// is transformed into
224
225func @foo(%arg0: !llvm.i32, %arg1: !llvm.i64) -> !llvm<"{i32, i64}"> {
226  // insert the vales into a structure
227  %0 = llvm.mlir.undef :  !llvm<"{i32, i64}">
228  %1 = llvm.insertvalue %arg0, %0[0] : !llvm<"{i32, i64}">
229  %2 = llvm.insertvalue %arg1, %1[1] : !llvm<"{i32, i64}">
230
231  // return the structure value
232  llvm.return %2 : !llvm<"{i32, i64}">
233}
234func @bar() {
235  %0 = llvm.mlir.constant(42 : i32) : !llvm.i32
236  %1 = llvm.mlir.constant(17) : !llvm.i64
237
238  // call and extract the values from the structure
239  %2 = llvm.call @bar(%0, %1) : (%arg0: !llvm.i32, %arg1: !llvm.i32) -> !llvm<"{i32, i64}">
240  %3 = llvm.extractvalue %2[0] : !llvm<"{i32, i64}">
241  %4 = llvm.extractvalue %2[1] : !llvm<"{i32, i64}">
242
243  // use as before
244  "use_i32"(%3) : (!llvm.i32) -> ()
245  "use_i64"(%4) : (!llvm.i64) -> ()
246}
247```
248
249### Calling Convention for Ranked `memref`
250
251Function _arguments_ of `memref` type, ranked or unranked, are _expanded_ into a
252list of arguments of non-aggregate types that the memref descriptor defined
253above comprises. That is, the outer struct type and the inner array types are
254replaced with individual arguments.
255
256This convention is implemented in the conversion of `std.func` and `std.call` to
257the LLVM dialect, with the former unpacking the descriptor into a set of
258individual values and the latter packing those values back into a descriptor so
259as to make it transparently usable by other operations. Conversions from other
260dialects should take this convention into account.
261
262This specific convention is motivated by the necessity to specify alignment and
263aliasing attributes on the raw pointers underpinning the memref.
264
265Examples:
266
267```mlir
268func @foo(%arg0: memref<?xf32>) -> () {
269  "use"(%arg0) : (memref<?xf32>) -> ()
270  return
271}
272
273// Gets converted to the following.
274
275llvm.func @foo(%arg0: !llvm<"float*">,   // Allocated pointer.
276               %arg1: !llvm<"float*">,   // Aligned pointer.
277               %arg2: !llvm.i64,         // Offset.
278               %arg3: !llvm.i64,         // Size in dim 0.
279               %arg4: !llvm.i64) {       // Stride in dim 0.
280  // Populate memref descriptor structure.
281  %0 = llvm.mlir.undef : !llvm<"{ float*, float*, i64, [1 x i64], [1 x i64] }">
282  %1 = llvm.insertvalue %arg0, %0[0] : !llvm<"{ float*, float*, i64, [1 x i64], [1 x i64] }">
283  %2 = llvm.insertvalue %arg1, %1[1] : !llvm<"{ float*, float*, i64, [1 x i64], [1 x i64] }">
284  %3 = llvm.insertvalue %arg2, %2[2] : !llvm<"{ float*, float*, i64, [1 x i64], [1 x i64] }">
285  %4 = llvm.insertvalue %arg3, %3[3, 0] : !llvm<"{ float*, float*, i64, [1 x i64], [1 x i64] }">
286  %5 = llvm.insertvalue %arg4, %4[4, 0] : !llvm<"{ float*, float*, i64, [1 x i64], [1 x i64] }">
287
288  // Descriptor is now usable as a single value.
289  "use"(%5) : (!llvm<"{ float*, float*, i64, [1 x i64], [1 x i64] }">) -> ()
290  llvm.return
291}
292```
293
294```mlir
295func @bar() {
296  %0 = "get"() : () -> (memref<?xf32>)
297  call @foo(%0) : (memref<?xf32>) -> ()
298  return
299}
300
301// Gets converted to the following.
302
303llvm.func @bar() {
304  %0 = "get"() : () -> !llvm<"{ float*, float*, i64, [1 x i64], [1 x i64] }">
305
306  // Unpack the memref descriptor.
307  %1 = llvm.extractvalue %0[0] : !llvm<"{ float*, float*, i64, [1 x i64], [1 x i64] }">
308  %2 = llvm.extractvalue %0[1] : !llvm<"{ float*, float*, i64, [1 x i64], [1 x i64] }">
309  %3 = llvm.extractvalue %0[2] : !llvm<"{ float*, float*, i64, [1 x i64], [1 x i64] }">
310  %4 = llvm.extractvalue %0[3, 0] : !llvm<"{ float*, float*, i64, [1 x i64], [1 x i64] }">
311  %5 = llvm.extractvalue %0[4, 0] : !llvm<"{ float*, float*, i64, [1 x i64], [1 x i64] }">
312
313  // Pass individual values to the callee.
314  llvm.call @foo(%1, %2, %3, %4, %5) : (!llvm<"float*">, !llvm<"float*">, !llvm.i64, !llvm.i64, !llvm.i64) -> ()
315  llvm.return
316}
317
318```
319
320### Calling Convention for Unranked `memref`
321
322For unranked memrefs, the list of function arguments always contains two
323elements, same as the unranked memref descriptor: an integer rank, and a
324type-erased (`!llvm<"i8*">`) pointer to the ranked memref descriptor. Note that
325while the _calling convention_ does not require stack allocation, _casting_ to
326unranked memref does since one cannot take an address of an SSA value containing
327the ranked memref. The caller is in charge of ensuring the thread safety and
328eventually removing unnecessary stack allocations in cast operations.
329
330Example
331
332```mlir
333llvm.func @foo(%arg0: memref<*xf32>) -> () {
334  "use"(%arg0) : (memref<*xf32>) -> ()
335  return
336}
337
338// Gets converted to the following.
339
340llvm.func @foo(%arg0: !llvm.i64       // Rank.
341               %arg1: !llvm<"i8*">) { // Type-erased pointer to descriptor.
342  // Pack the unranked memref descriptor.
343  %0 = llvm.mlir.undef : !llvm<"{ i64, i8* }">
344  %1 = llvm.insertvalue %arg0, %0[0] : !llvm<"{ i64, i8* }">
345  %2 = llvm.insertvalue %arg1, %1[1] : !llvm<"{ i64, i8* }">
346
347  "use"(%2) : (!llvm<"{ i64, i8* }">) -> ()
348  llvm.return
349}
350```
351
352```mlir
353llvm.func @bar() {
354  %0 = "get"() : () -> (memref<*xf32>)
355  call @foo(%0): (memref<*xf32>) -> ()
356  return
357}
358
359// Gets converted to the following.
360
361llvm.func @bar() {
362  %0 = "get"() : () -> (!llvm<"{ i64, i8* }">)
363
364  // Unpack the memref descriptor.
365  %1 = llvm.extractvalue %0[0] : !llvm<"{ i64, i8* }">
366  %2 = llvm.extractvalue %0[1] : !llvm<"{ i64, i8* }">
367
368  // Pass individual values to the callee.
369  llvm.call @foo(%1, %2) : (!llvm.i64, !llvm<"i8*">)
370  llvm.return
371}
372```
373
374**Lifetime.** The second element of the unranked memref descriptor points to
375some memory in which the ranked memref descriptor is stored. By convention, this
376memory is allocated on stack and has the lifetime of the function. (*Note:* due
377to function-length lifetime, creation of multiple unranked memref descriptors,
378e.g., in a loop, may lead to stack overflows.) If an unranked descriptor has to
379be returned from a function, the ranked descriptor it points to is copied into
380dynamically allocated memory, and the pointer in the unranked descriptor is
381updated accordingly. The allocation happens immediately before returning. It is
382the responsibility of the caller to free the dynamically allocated memory. The
383default conversion of `std.call` and `std.call_indirect` copies the ranked
384descriptor to newly allocated memory on the caller's stack. Thus, the convention
385of the ranked memref descriptor pointed to by an unranked memref descriptor
386being stored on stack is respected.
387
388*This convention may or may not apply if the conversion of MemRef types is
389overridden by the user.*
390
391### C-compatible wrapper emission
392
393In practical cases, it may be desirable to have externally-facing functions with
394a single attribute corresponding to a MemRef argument. When interfacing with
395LLVM IR produced from C, the code needs to respect the corresponding calling
396convention. The conversion to the LLVM dialect provides an option to generate
397wrapper functions that take memref descriptors as pointers-to-struct compatible
398with data types produced by Clang when compiling C sources. The generation of
399such wrapper functions can additionally be controlled at a function granularity
400by setting the `llvm.emit_c_interface` unit attribute.
401
402More specifically, a memref argument is converted into a pointer-to-struct
403argument of type `{T*, T*, i64, i64[N], i64[N]}*` in the wrapper function, where
404`T` is the converted element type and `N` is the memref rank. This type is
405compatible with that produced by Clang for the following C++ structure template
406instantiations or their equivalents in C.
407
408```cpp
409template<typename T, size_t N>
410struct MemRefDescriptor {
411  T *allocated;
412  T *aligned;
413  intptr_t offset;
414  intptr_t sizes[N];
415  intptr_t strides[N];
416};
417```
418
419If enabled, the option will do the following. For _external_ functions declared
420in the MLIR module.
421
4221. Declare a new function `_mlir_ciface_<original name>` where memref arguments
423   are converted to pointer-to-struct and the remaining arguments are converted
424   as usual.
4251. Add a body to the original function (making it non-external) that
426   1. allocates a memref descriptor,
427   1. populates it, and
428   1. passes the pointer to it into the newly declared interface function, then
429   1. collects the result of the call and returns it to the caller.
430
431For (non-external) functions defined in the MLIR module.
432
4331. Define a new function `_mlir_ciface_<original name>` where memref arguments
434   are converted to pointer-to-struct and the remaining arguments are converted
435   as usual.
4361. Populate the body of the newly defined function with IR that
437   1. loads descriptors from pointers;
438   1. unpacks descriptor into individual non-aggregate values;
439   1. passes these values into the original function;
440   1. collects the result of the call and returns it to the caller.
441
442Examples:
443
444```mlir
445
446func @qux(%arg0: memref<?x?xf32>)
447
448// Gets converted into the following.
449
450// Function with unpacked arguments.
451llvm.func @qux(%arg0: !llvm<"float*">, %arg1: !llvm<"float*">, %arg2: !llvm.i64,
452               %arg3: !llvm.i64, %arg4: !llvm.i64, %arg5: !llvm.i64,
453               %arg6: !llvm.i64) {
454  // Populate memref descriptor (as per calling convention).
455  %0 = llvm.mlir.undef : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
456  %1 = llvm.insertvalue %arg0, %0[0] : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
457  %2 = llvm.insertvalue %arg1, %1[1] : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
458  %3 = llvm.insertvalue %arg2, %2[2] : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
459  %4 = llvm.insertvalue %arg3, %3[3, 0] : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
460  %5 = llvm.insertvalue %arg5, %4[4, 0] : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
461  %6 = llvm.insertvalue %arg4, %5[3, 1] : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
462  %7 = llvm.insertvalue %arg6, %6[4, 1] : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
463
464  // Store the descriptor in a stack-allocated space.
465  %8 = llvm.mlir.constant(1 : index) : !llvm.i64
466  %9 = llvm.alloca %8 x !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
467                 : (!llvm.i64) -> !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }*">
468  llvm.store %7, %9 : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }*">
469
470  // Call the interface function.
471  llvm.call @_mlir_ciface_qux(%9) : (!llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }*">) -> ()
472
473  // The stored descriptor will be freed on return.
474  llvm.return
475}
476
477// Interface function.
478llvm.func @_mlir_ciface_qux(!llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }*">)
479```
480
481```mlir
482func @foo(%arg0: memref<?x?xf32>) {
483  return
484}
485
486// Gets converted into the following.
487
488// Function with unpacked arguments.
489llvm.func @foo(%arg0: !llvm<"float*">, %arg1: !llvm<"float*">, %arg2: !llvm.i64,
490               %arg3: !llvm.i64, %arg4: !llvm.i64, %arg5: !llvm.i64,
491               %arg6: !llvm.i64) {
492  llvm.return
493}
494
495// Interface function callable from C.
496llvm.func @_mlir_ciface_foo(%arg0: !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }*">) {
497  // Load the descriptor.
498  %0 = llvm.load %arg0 : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }*">
499
500  // Unpack the descriptor as per calling convention.
501  %1 = llvm.extractvalue %0[0] : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
502  %2 = llvm.extractvalue %0[1] : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
503  %3 = llvm.extractvalue %0[2] : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
504  %4 = llvm.extractvalue %0[3, 0] : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
505  %5 = llvm.extractvalue %0[3, 1] : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
506  %6 = llvm.extractvalue %0[4, 0] : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
507  %7 = llvm.extractvalue %0[4, 1] : !llvm<"{ float*, float*, i64, [2 x i64], [2 x i64] }">
508  llvm.call @foo(%1, %2, %3, %4, %5, %6, %7)
509    : (!llvm<"float*">, !llvm<"float*">, !llvm.i64, !llvm.i64, !llvm.i64,
510       !llvm.i64, !llvm.i64) -> ()
511  llvm.return
512}
513```
514
515Rationale: Introducing auxiliary functions for C-compatible interfaces is
516preferred to modifying the calling convention since it will minimize the effect
517of C compatibility on intra-module calls or calls between MLIR-generated
518functions. In particular, when calling external functions from an MLIR module in
519a (parallel) loop, the fact of storing a memref descriptor on stack can lead to
520stack exhaustion and/or concurrent access to the same address. Auxiliary
521interface function serves as an allocation scope in this case. Furthermore, when
522targeting accelerators with separate memory spaces such as GPUs, stack-allocated
523descriptors passed by pointer would have to be transferred to the device memory,
524which introduces significant overhead. In such situations, auxiliary interface
525functions are executed on host and only pass the values through device function
526invocation mechanism.
527
528## Repeated Successor Removal
529
530Since the goal of the LLVM IR dialect is to reflect LLVM IR in MLIR, the dialect
531and the conversion procedure must account for the differences between block
532arguments and LLVM IR PHI nodes. In particular, LLVM IR disallows PHI nodes with
533different values coming from the same source. Therefore, the LLVM IR dialect
534disallows operations that have identical successors accepting arguments, which
535would lead to invalid PHI nodes. The conversion process resolves the potential
536PHI source ambiguity by injecting dummy blocks if the same block is used more
537than once as a successor in an instruction. These dummy blocks branch
538unconditionally to the original successors, pass them the original operands
539(available in the dummy block because it is dominated by the original block) and
540are used instead of them in the original terminator operation.
541
542Example:
543
544```mlir
545  cond_br %0, ^bb1(%1 : i32), ^bb1(%2 : i32)
546^bb1(%3 : i32)
547  "use"(%3) : (i32) -> ()
548```
549
550leads to a new basic block being inserted,
551
552```mlir
553  cond_br %0, ^bb1(%1 : i32), ^dummy
554^bb1(%3 : i32):
555  "use"(%3) : (i32) -> ()
556^dummy:
557  br ^bb1(%4 : i32)
558```
559
560before the conversion to the LLVM IR dialect:
561
562```mlir
563  llvm.cond_br  %0, ^bb1(%1 : !llvm.i32), ^dummy
564^bb1(%3 : !llvm<"i32">):
565  "use"(%3) : (!llvm.i32) -> ()
566^dummy:
567  llvm.br ^bb1(%2 : !llvm.i32)
568```
569
570## Default Memref Model
571
572### Memref Descriptor
573
574Within a converted function, a `memref`-typed value is represented by a memref
575_descriptor_, the type of which is the structure type obtained by converting
576from the memref type. This descriptor holds all the necessary information to
577produce an address of a specific element. In particular, it holds dynamic values
578for static sizes, and they are expected to match at all times.
579
580It is created by the allocation operation and is updated by the conversion
581operations that may change static dimensions into dynamic dimensions and vice versa.
582
583**Note**: LLVM IR conversion does not support `memref`s with layouts that are
584not amenable to the strided form.
585
586### Index Linearization
587
588Accesses to a memref element are transformed into an access to an element of the
589buffer pointed to by the descriptor. The position of the element in the buffer
590is calculated by linearizing memref indices in row-major order (lexically first
591index is the slowest varying, similar to C, but accounting for strides). The
592computation of the linear address is emitted as arithmetic operation in the LLVM
593IR dialect. Strides are extracted from the memref descriptor.
594
595Accesses to zero-dimensional memref (that are interpreted as pointers to the
596elemental type) are directly converted into `llvm.load` or `llvm.store` without
597any pointer manipulations.
598
599Examples:
600
601An access to a zero-dimensional memref is converted into a plain load:
602
603```mlir
604// before
605%0 = load %m[] : memref<f32>
606
607// after
608%0 = llvm.load %m : !llvm<"float*">
609```
610
611An access to a memref with indices:
612
613```mlir
614%0 = load %m[1,2,3,4] : memref<10x?x13x?xf32>
615```
616
617is transformed into the equivalent of the following code:
618
619```mlir
620// Compute the linearized index from strides. Each block below extracts one
621// stride from the descriptor, multiplies it with the index and accumulates
622// the total offset.
623%stride1 = llvm.extractvalue[4, 0] : !llvm<"{float*, float*, i64, i64[4], i64[4]}">
624%idx1 = llvm.mlir.constant(1 : index) !llvm.i64
625%addr1 = muli %stride1, %idx1 : !llvm.i64
626
627%stride2 = llvm.extractvalue[4, 1] : !llvm<"{float*, float*, i64, i64[4], i64[4]}">
628%idx2 = llvm.mlir.constant(2 : index) !llvm.i64
629%addr2 = muli %stride2, %idx2 : !llvm.i64
630%addr3 = addi %addr1, %addr2 : !llvm.i64
631
632%stride3 = llvm.extractvalue[4, 2] : !llvm<"{float*, float*, i64, i64[4], i64[4]}">
633%idx3 = llvm.mlir.constant(3 : index) !llvm.i64
634%addr4 = muli %stride3, %idx3 : !llvm.i64
635%addr5 = addi %addr3, %addr4 : !llvm.i64
636
637%stride4 = llvm.extractvalue[4, 3] : !llvm<"{float*, float*, i64, i64[4], i64[4]}">
638%idx4 = llvm.mlir.constant(4 : index) !llvm.i64
639%addr6 = muli %stride4, %idx4 : !llvm.i64
640%addr7 = addi %addr5, %addr6 : !llvm.i64
641
642// Add the linear offset to the address.
643%offset = llvm.extractvalue[2] : !llvm<"{float*, float*, i64, i64[4], i64[4]}">
644%addr8 = addi %addr7, %offset : !llvm.i64
645
646// Obtain the aligned pointer.
647%aligned = llvm.extractvalue[1] : !llvm<"{float*, float*, i64, i64[4], i64[4]}">
648
649// Get the address of the data pointer.
650%ptr = llvm.getelementptr %aligned[%addr8]
651    : !llvm<"{float*, float*, i64, i64[4], i64[4]}"> -> !llvm<"float*">
652
653// Perform the actual load.
654%0 = llvm.load %ptr : !llvm<"float*">
655```
656
657For stores, the address computation code is identical and only the actual store
658operation is different.
659
660Note: the conversion does not perform any sort of common subexpression
661elimination when emitting memref accesses.
662