llvm-project/mlir/test/Target/llvmir.mlir

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1216 lines
50 KiB
MLIR
Raw Normal View History

Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// RUN: mlir-translate -mlir-to-llvmir %s | FileCheck %s
// CHECK: @i32_global = internal global i32 42
llvm.mlir.global internal @i32_global(42: i32) : !llvm.i32
// CHECK: @i32_const = internal constant i53 52
llvm.mlir.global internal constant @i32_const(52: i53) : !llvm.i53
// CHECK: @int_global_array = internal global [3 x i32] [i32 62, i32 62, i32 62]
llvm.mlir.global internal @int_global_array(dense<62> : vector<3xi32>) : !llvm<"[3 x i32]">
// CHECK: @i32_global_addr_space = internal addrspace(7) global i32 62
llvm.mlir.global internal @i32_global_addr_space(62: i32) {addr_space = 7 : i32} : !llvm.i32
// CHECK: @float_global = internal global float 0.000000e+00
llvm.mlir.global internal @float_global(0.0: f32) : !llvm.float
// CHECK: @float_global_array = internal global [1 x float] [float -5.000000e+00]
llvm.mlir.global internal @float_global_array(dense<[-5.0]> : vector<1xf32>) : !llvm<"[1 x float]">
// CHECK: @string_const = internal constant [6 x i8] c"foobar"
llvm.mlir.global internal constant @string_const("foobar") : !llvm<"[6 x i8]">
// CHECK: @int_global_undef = internal global i64 undef
llvm.mlir.global internal @int_global_undef() : !llvm.i64
// CHECK: @int_gep = internal constant i32* getelementptr (i32, i32* @i32_global, i32 2)
llvm.mlir.global internal constant @int_gep() : !llvm<"i32*"> {
%addr = llvm.mlir.addressof @i32_global : !llvm<"i32*">
%_c0 = llvm.mlir.constant(2: i32) :!llvm.i32
%gepinit = llvm.getelementptr %addr[%_c0] : (!llvm<"i32*">, !llvm.i32) -> !llvm<"i32*">
llvm.return %gepinit : !llvm<"i32*">
}
//
// Linkage attribute.
//
// CHECK: @private = private global i32 42
llvm.mlir.global private @private(42 : i32) : !llvm.i32
// CHECK: @internal = internal global i32 42
llvm.mlir.global internal @internal(42 : i32) : !llvm.i32
// CHECK: @available_externally = available_externally global i32 42
llvm.mlir.global available_externally @available_externally(42 : i32) : !llvm.i32
// CHECK: @linkonce = linkonce global i32 42
llvm.mlir.global linkonce @linkonce(42 : i32) : !llvm.i32
// CHECK: @weak = weak global i32 42
llvm.mlir.global weak @weak(42 : i32) : !llvm.i32
// CHECK: @common = common global i32 42
llvm.mlir.global common @common(42 : i32) : !llvm.i32
// CHECK: @appending = appending global i32 42
llvm.mlir.global appending @appending(42 : i32) : !llvm.i32
// CHECK: @extern_weak = extern_weak global i32
llvm.mlir.global extern_weak @extern_weak() : !llvm.i32
// CHECK: @linkonce_odr = linkonce_odr global i32 42
llvm.mlir.global linkonce_odr @linkonce_odr(42 : i32) : !llvm.i32
// CHECK: @weak_odr = weak_odr global i32 42
llvm.mlir.global weak_odr @weak_odr(42 : i32) : !llvm.i32
// CHECK: @external = external global i32
llvm.mlir.global external @external() : !llvm.i32
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
//
// Declarations of the allocation functions to be linked against.
//
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
// CHECK: declare i8* @malloc(i64)
llvm.func @malloc(!llvm.i64) -> !llvm<"i8*">
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
// CHECK: declare void @free(i8*)
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
//
// Basic functionality: function and block conversion, function calls,
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// phi nodes, scalar type conversion, arithmetic operations.
//
// CHECK-LABEL: define void @empty()
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: ret void
// CHECK-NEXT: }
llvm.func @empty() {
llvm.return
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
}
// CHECK-LABEL: @global_refs
llvm.func @global_refs() {
// Check load from globals.
// CHECK: load i32, i32* @i32_global
%0 = llvm.mlir.addressof @i32_global : !llvm<"i32*">
%1 = llvm.load %0 : !llvm<"i32*">
// Check the contracted form of load from array constants.
// CHECK: load i8, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @string_const, i64 0, i64 0)
%2 = llvm.mlir.addressof @string_const : !llvm<"[6 x i8]*">
%c0 = llvm.mlir.constant(0 : index) : !llvm.i64
%3 = llvm.getelementptr %2[%c0, %c0] : (!llvm<"[6 x i8]*">, !llvm.i64, !llvm.i64) -> !llvm<"i8*">
%4 = llvm.load %3 : !llvm<"i8*">
llvm.return
}
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-LABEL: declare void @body(i64)
llvm.func @body(!llvm.i64)
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-LABEL: define void @simple_loop()
llvm.func @simple_loop() {
// CHECK: br label %[[SIMPLE_bb1:[0-9]+]]
llvm.br ^bb1
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// Constants are inlined in LLVM rather than a separate instruction.
// CHECK: [[SIMPLE_bb1]]:
// CHECK-NEXT: br label %[[SIMPLE_bb2:[0-9]+]]
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb1: // pred: ^bb0
%0 = llvm.mlir.constant(1 : index) : !llvm.i64
%1 = llvm.mlir.constant(42 : index) : !llvm.i64
llvm.br ^bb2(%0 : !llvm.i64)
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: [[SIMPLE_bb2]]:
// CHECK-NEXT: %{{[0-9]+}} = phi i64 [ %{{[0-9]+}}, %[[SIMPLE_bb3:[0-9]+]] ], [ 1, %[[SIMPLE_bb1]] ]
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: %{{[0-9]+}} = icmp slt i64 %{{[0-9]+}}, 42
// CHECK-NEXT: br i1 %{{[0-9]+}}, label %[[SIMPLE_bb3]], label %[[SIMPLE_bb4:[0-9]+]]
^bb2(%2: !llvm.i64): // 2 preds: ^bb1, ^bb3
%3 = llvm.icmp "slt" %2, %1 : !llvm.i64
llvm.cond_br %3, ^bb3, ^bb4
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: [[SIMPLE_bb3]]:
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: call void @body(i64 %{{[0-9]+}})
// CHECK-NEXT: %{{[0-9]+}} = add i64 %{{[0-9]+}}, 1
// CHECK-NEXT: br label %[[SIMPLE_bb2]]
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb3: // pred: ^bb2
llvm.call @body(%2) : (!llvm.i64) -> ()
%4 = llvm.mlir.constant(1 : index) : !llvm.i64
%5 = llvm.add %2, %4 : !llvm.i64
llvm.br ^bb2(%5 : !llvm.i64)
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: [[SIMPLE_bb4]]:
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: ret void
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb4: // pred: ^bb2
llvm.return
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
}
// CHECK-LABEL: define void @simple_caller()
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: call void @simple_loop()
// CHECK-NEXT: ret void
// CHECK-NEXT: }
llvm.func @simple_caller() {
llvm.call @simple_loop() : () -> ()
llvm.return
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
}
//func @simple_indirect_caller() {
//^bb0:
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// %f = constant @simple_loop : () -> ()
// call_indirect %f() : () -> ()
// return
//}
// CHECK-LABEL: define void @ml_caller()
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: call void @simple_loop()
// CHECK-NEXT: call void @more_imperfectly_nested_loops()
// CHECK-NEXT: ret void
// CHECK-NEXT: }
llvm.func @ml_caller() {
llvm.call @simple_loop() : () -> ()
llvm.call @more_imperfectly_nested_loops() : () -> ()
llvm.return
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
}
// CHECK-LABEL: declare i64 @body_args(i64)
llvm.func @body_args(!llvm.i64) -> !llvm.i64
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-LABEL: declare i32 @other(i64, i32)
llvm.func @other(!llvm.i64, !llvm.i32) -> !llvm.i32
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-LABEL: define i32 @func_args(i32 {{%.*}}, i32 {{%.*}})
// CHECK-NEXT: br label %[[ARGS_bb1:[0-9]+]]
llvm.func @func_args(%arg0: !llvm.i32, %arg1: !llvm.i32) -> !llvm.i32 {
%0 = llvm.mlir.constant(0 : i32) : !llvm.i32
llvm.br ^bb1
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: [[ARGS_bb1]]:
// CHECK-NEXT: br label %[[ARGS_bb2:[0-9]+]]
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb1: // pred: ^bb0
%1 = llvm.mlir.constant(0 : index) : !llvm.i64
%2 = llvm.mlir.constant(42 : index) : !llvm.i64
llvm.br ^bb2(%1 : !llvm.i64)
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: [[ARGS_bb2]]:
// CHECK-NEXT: %5 = phi i64 [ %12, %[[ARGS_bb3:[0-9]+]] ], [ 0, %[[ARGS_bb1]] ]
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: %6 = icmp slt i64 %5, 42
// CHECK-NEXT: br i1 %6, label %[[ARGS_bb3]], label %[[ARGS_bb4:[0-9]+]]
^bb2(%3: !llvm.i64): // 2 preds: ^bb1, ^bb3
%4 = llvm.icmp "slt" %3, %2 : !llvm.i64
llvm.cond_br %4, ^bb3, ^bb4
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: [[ARGS_bb3]]:
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: %8 = call i64 @body_args(i64 %5)
// CHECK-NEXT: %9 = call i32 @other(i64 %8, i32 %0)
// CHECK-NEXT: %10 = call i32 @other(i64 %8, i32 %9)
// CHECK-NEXT: %11 = call i32 @other(i64 %8, i32 %1)
// CHECK-NEXT: %12 = add i64 %5, 1
// CHECK-NEXT: br label %[[ARGS_bb2]]
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb3: // pred: ^bb2
%5 = llvm.call @body_args(%3) : (!llvm.i64) -> !llvm.i64
%6 = llvm.call @other(%5, %arg0) : (!llvm.i64, !llvm.i32) -> !llvm.i32
%7 = llvm.call @other(%5, %6) : (!llvm.i64, !llvm.i32) -> !llvm.i32
%8 = llvm.call @other(%5, %arg1) : (!llvm.i64, !llvm.i32) -> !llvm.i32
%9 = llvm.mlir.constant(1 : index) : !llvm.i64
%10 = llvm.add %3, %9 : !llvm.i64
llvm.br ^bb2(%10 : !llvm.i64)
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: [[ARGS_bb4]]:
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: %14 = call i32 @other(i64 0, i32 0)
// CHECK-NEXT: ret i32 %14
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb4: // pred: ^bb2
%11 = llvm.mlir.constant(0 : index) : !llvm.i64
%12 = llvm.call @other(%11, %0) : (!llvm.i64, !llvm.i32) -> !llvm.i32
llvm.return %12 : !llvm.i32
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
}
// CHECK: declare void @pre(i64)
llvm.func @pre(!llvm.i64)
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: declare void @body2(i64, i64)
llvm.func @body2(!llvm.i64, !llvm.i64)
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: declare void @post(i64)
llvm.func @post(!llvm.i64)
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-LABEL: define void @imperfectly_nested_loops()
// CHECK-NEXT: br label %[[IMPER_bb1:[0-9]+]]
llvm.func @imperfectly_nested_loops() {
llvm.br ^bb1
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: [[IMPER_bb1]]:
// CHECK-NEXT: br label %[[IMPER_bb2:[0-9]+]]
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb1: // pred: ^bb0
%0 = llvm.mlir.constant(0 : index) : !llvm.i64
%1 = llvm.mlir.constant(42 : index) : !llvm.i64
llvm.br ^bb2(%0 : !llvm.i64)
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: [[IMPER_bb2]]:
// CHECK-NEXT: %3 = phi i64 [ %13, %[[IMPER_bb7:[0-9]+]] ], [ 0, %[[IMPER_bb1]] ]
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: %4 = icmp slt i64 %3, 42
// CHECK-NEXT: br i1 %4, label %[[IMPER_bb3:[0-9]+]], label %[[IMPER_bb8:[0-9]+]]
^bb2(%2: !llvm.i64): // 2 preds: ^bb1, ^bb7
%3 = llvm.icmp "slt" %2, %1 : !llvm.i64
llvm.cond_br %3, ^bb3, ^bb8
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: [[IMPER_bb3]]:
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: call void @pre(i64 %3)
// CHECK-NEXT: br label %[[IMPER_bb4:[0-9]+]]
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb3: // pred: ^bb2
llvm.call @pre(%2) : (!llvm.i64) -> ()
llvm.br ^bb4
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: [[IMPER_bb4]]:
// CHECK-NEXT: br label %[[IMPER_bb5:[0-9]+]]
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb4: // pred: ^bb3
%4 = llvm.mlir.constant(7 : index) : !llvm.i64
%5 = llvm.mlir.constant(56 : index) : !llvm.i64
llvm.br ^bb5(%4 : !llvm.i64)
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: [[IMPER_bb5]]:
// CHECK-NEXT: %8 = phi i64 [ %11, %[[IMPER_bb6:[0-9]+]] ], [ 7, %[[IMPER_bb4]] ]
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: %9 = icmp slt i64 %8, 56
// CHECK-NEXT: br i1 %9, label %[[IMPER_bb6]], label %[[IMPER_bb7]]
^bb5(%6: !llvm.i64): // 2 preds: ^bb4, ^bb6
%7 = llvm.icmp "slt" %6, %5 : !llvm.i64
llvm.cond_br %7, ^bb6, ^bb7
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: [[IMPER_bb6]]:
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: call void @body2(i64 %3, i64 %8)
// CHECK-NEXT: %11 = add i64 %8, 2
// CHECK-NEXT: br label %[[IMPER_bb5]]
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb6: // pred: ^bb5
llvm.call @body2(%2, %6) : (!llvm.i64, !llvm.i64) -> ()
%8 = llvm.mlir.constant(2 : index) : !llvm.i64
%9 = llvm.add %6, %8 : !llvm.i64
llvm.br ^bb5(%9 : !llvm.i64)
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: [[IMPER_bb7]]:
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: call void @post(i64 %3)
// CHECK-NEXT: %13 = add i64 %3, 1
// CHECK-NEXT: br label %[[IMPER_bb2]]
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb7: // pred: ^bb5
llvm.call @post(%2) : (!llvm.i64) -> ()
%10 = llvm.mlir.constant(1 : index) : !llvm.i64
%11 = llvm.add %2, %10 : !llvm.i64
llvm.br ^bb2(%11 : !llvm.i64)
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: [[IMPER_bb8]]:
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: ret void
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb8: // pred: ^bb2
llvm.return
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
}
// CHECK: declare void @mid(i64)
llvm.func @mid(!llvm.i64)
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK: declare void @body3(i64, i64)
llvm.func @body3(!llvm.i64, !llvm.i64)
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// A complete function transformation check.
// CHECK-LABEL: define void @more_imperfectly_nested_loops()
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: br label %1
// CHECK: 1: ; preds = %0
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: br label %2
// CHECK: 2: ; preds = %19, %1
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: %3 = phi i64 [ %20, %19 ], [ 0, %1 ]
// CHECK-NEXT: %4 = icmp slt i64 %3, 42
// CHECK-NEXT: br i1 %4, label %5, label %21
// CHECK: 5: ; preds = %2
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: call void @pre(i64 %3)
// CHECK-NEXT: br label %6
// CHECK: 6: ; preds = %5
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: br label %7
// CHECK: 7: ; preds = %10, %6
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: %8 = phi i64 [ %11, %10 ], [ 7, %6 ]
// CHECK-NEXT: %9 = icmp slt i64 %8, 56
// CHECK-NEXT: br i1 %9, label %10, label %12
// CHECK: 10: ; preds = %7
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: call void @body2(i64 %3, i64 %8)
// CHECK-NEXT: %11 = add i64 %8, 2
// CHECK-NEXT: br label %7
// CHECK: 12: ; preds = %7
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: call void @mid(i64 %3)
// CHECK-NEXT: br label %13
// CHECK: 13: ; preds = %12
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: br label %14
// CHECK: 14: ; preds = %17, %13
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: %15 = phi i64 [ %18, %17 ], [ 18, %13 ]
// CHECK-NEXT: %16 = icmp slt i64 %15, 37
// CHECK-NEXT: br i1 %16, label %17, label %19
// CHECK: 17: ; preds = %14
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: call void @body3(i64 %3, i64 %15)
// CHECK-NEXT: %18 = add i64 %15, 3
// CHECK-NEXT: br label %14
// CHECK: 19: ; preds = %14
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: call void @post(i64 %3)
// CHECK-NEXT: %20 = add i64 %3, 1
// CHECK-NEXT: br label %2
// CHECK: 21: ; preds = %2
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
// CHECK-NEXT: ret void
// CHECK-NEXT: }
llvm.func @more_imperfectly_nested_loops() {
llvm.br ^bb1
^bb1: // pred: ^bb0
%0 = llvm.mlir.constant(0 : index) : !llvm.i64
%1 = llvm.mlir.constant(42 : index) : !llvm.i64
llvm.br ^bb2(%0 : !llvm.i64)
^bb2(%2: !llvm.i64): // 2 preds: ^bb1, ^bb11
%3 = llvm.icmp "slt" %2, %1 : !llvm.i64
llvm.cond_br %3, ^bb3, ^bb12
^bb3: // pred: ^bb2
llvm.call @pre(%2) : (!llvm.i64) -> ()
llvm.br ^bb4
^bb4: // pred: ^bb3
%4 = llvm.mlir.constant(7 : index) : !llvm.i64
%5 = llvm.mlir.constant(56 : index) : !llvm.i64
llvm.br ^bb5(%4 : !llvm.i64)
^bb5(%6: !llvm.i64): // 2 preds: ^bb4, ^bb6
%7 = llvm.icmp "slt" %6, %5 : !llvm.i64
llvm.cond_br %7, ^bb6, ^bb7
^bb6: // pred: ^bb5
llvm.call @body2(%2, %6) : (!llvm.i64, !llvm.i64) -> ()
%8 = llvm.mlir.constant(2 : index) : !llvm.i64
%9 = llvm.add %6, %8 : !llvm.i64
llvm.br ^bb5(%9 : !llvm.i64)
^bb7: // pred: ^bb5
llvm.call @mid(%2) : (!llvm.i64) -> ()
llvm.br ^bb8
^bb8: // pred: ^bb7
%10 = llvm.mlir.constant(18 : index) : !llvm.i64
%11 = llvm.mlir.constant(37 : index) : !llvm.i64
llvm.br ^bb9(%10 : !llvm.i64)
^bb9(%12: !llvm.i64): // 2 preds: ^bb8, ^bb10
%13 = llvm.icmp "slt" %12, %11 : !llvm.i64
llvm.cond_br %13, ^bb10, ^bb11
^bb10: // pred: ^bb9
llvm.call @body3(%2, %12) : (!llvm.i64, !llvm.i64) -> ()
%14 = llvm.mlir.constant(3 : index) : !llvm.i64
%15 = llvm.add %12, %14 : !llvm.i64
llvm.br ^bb9(%15 : !llvm.i64)
^bb11: // pred: ^bb9
llvm.call @post(%2) : (!llvm.i64) -> ()
%16 = llvm.mlir.constant(1 : index) : !llvm.i64
%17 = llvm.add %2, %16 : !llvm.i64
llvm.br ^bb2(%17 : !llvm.i64)
^bb12: // pred: ^bb2
llvm.return
Lower scalar parts of CFG functions to LLVM IR Initial restricted implementaiton of the MLIR to LLVM IR translation. Introduce a new flow into the mlir-translate tool taking an MLIR module containing CFG functions only and producing and LLVM IR module. The MLIR features supported by the translator are as follows: - primitive and function types; - integer constants; - cfg and ext functions with 0 or 1 return values; - calls to these functions; - basic block conversion translation of arguments to phi nodes; - conversion between arguments of the first basic block and function arguments; - (conditional) branches; - integer addition and comparison operations. Are NOT supported: - vector and tensor types and operations on them; - memrefs and operations on them; - allocations; - functions returning multiple values; - LLVM Module triple and data layout (index type is hardcoded to i64). Create a new MLIR library and place it under lib/Target/LLVMIR. The "Target" library group is similar to the one present in LLVM and is intended to contain all future public MLIR translation targets. The general flow of MLIR to LLVM IR convresion will include several lowering and simplification passes on the MLIR itself in order to make the translation as simple as possible. In particular, ML functions should be transformed to CFG functions by the recently introduced pass, operations on structured types will be converted to sequences of operations on primitive types, complex operations such as affine_apply will be converted into sequence of primitive operations, primitive operations themselves may eventually be converted to an LLVM dialect that uses LLVM-like operations. Introduce the first translation test so that further changes make sure the basic translation functionality is not broken. PiperOrigin-RevId: 222400112
2018-11-21 22:37:49 +08:00
}
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
//
// MemRef type conversion, allocation and communication with functions.
//
// CHECK-LABEL: define void @memref_alloc()
llvm.func @memref_alloc() {
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
// CHECK-NEXT: %{{[0-9]+}} = call i8* @malloc(i64 400)
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = bitcast i8* %{{[0-9]+}} to float*
// CHECK-NEXT: %{{[0-9]+}} = insertvalue { float* } undef, float* %{{[0-9]+}}, 0
%0 = llvm.mlir.constant(10 : index) : !llvm.i64
%1 = llvm.mlir.constant(10 : index) : !llvm.i64
%2 = llvm.mul %0, %1 : !llvm.i64
%3 = llvm.mlir.undef : !llvm<"{ float* }">
%4 = llvm.mlir.constant(4 : index) : !llvm.i64
%5 = llvm.mul %2, %4 : !llvm.i64
%6 = llvm.call @malloc(%5) : (!llvm.i64) -> !llvm<"i8*">
%7 = llvm.bitcast %6 : !llvm<"i8*"> to !llvm<"float*">
%8 = llvm.insertvalue %7, %3[0] : !llvm<"{ float* }">
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: ret void
llvm.return
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
}
// CHECK-LABEL: declare i64 @get_index()
llvm.func @get_index() -> !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-LABEL: define void @store_load_static()
llvm.func @store_load_static() {
^bb0:
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
// CHECK-NEXT: %{{[0-9]+}} = call i8* @malloc(i64 40)
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = bitcast i8* %{{[0-9]+}} to float*
// CHECK-NEXT: %{{[0-9]+}} = insertvalue { float* } undef, float* %{{[0-9]+}}, 0
%0 = llvm.mlir.constant(10 : index) : !llvm.i64
%1 = llvm.mlir.undef : !llvm<"{ float* }">
%2 = llvm.mlir.constant(4 : index) : !llvm.i64
%3 = llvm.mul %0, %2 : !llvm.i64
%4 = llvm.call @malloc(%3) : (!llvm.i64) -> !llvm<"i8*">
%5 = llvm.bitcast %4 : !llvm<"i8*"> to !llvm<"float*">
%6 = llvm.insertvalue %5, %1[0] : !llvm<"{ float* }">
%7 = llvm.mlir.constant(1.000000e+00 : f32) : !llvm.float
llvm.br ^bb1
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb1: // pred: ^bb0
%8 = llvm.mlir.constant(0 : index) : !llvm.i64
%9 = llvm.mlir.constant(10 : index) : !llvm.i64
llvm.br ^bb2(%8 : !llvm.i64)
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK: %{{[0-9]+}} = phi i64 [ %{{[0-9]+}}, %{{[0-9]+}} ], [ 0, %{{[0-9]+}} ]
^bb2(%10: !llvm.i64): // 2 preds: ^bb1, ^bb3
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = icmp slt i64 %{{[0-9]+}}, 10
%11 = llvm.icmp "slt" %10, %9 : !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: br i1 %{{[0-9]+}}, label %{{[0-9]+}}, label %{{[0-9]+}}
llvm.cond_br %11, ^bb3, ^bb4
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb3: // pred: ^bb2
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK: %{{[0-9]+}} = extractvalue { float* } %{{[0-9]+}}, 0
// CHECK-NEXT: %{{[0-9]+}} = getelementptr float, float* %{{[0-9]+}}, i64 %{{[0-9]+}}
// CHECK-NEXT: store float 1.000000e+00, float* %{{[0-9]+}}
%12 = llvm.mlir.constant(10 : index) : !llvm.i64
%13 = llvm.extractvalue %6[0] : !llvm<"{ float* }">
%14 = llvm.getelementptr %13[%10] : (!llvm<"float*">, !llvm.i64) -> !llvm<"float*">
llvm.store %7, %14 : !llvm<"float*">
%15 = llvm.mlir.constant(1 : index) : !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = add i64 %{{[0-9]+}}, 1
%16 = llvm.add %10, %15 : !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: br label %{{[0-9]+}}
llvm.br ^bb2(%16 : !llvm.i64)
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb4: // pred: ^bb2
llvm.br ^bb5
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb5: // pred: ^bb4
%17 = llvm.mlir.constant(0 : index) : !llvm.i64
%18 = llvm.mlir.constant(10 : index) : !llvm.i64
llvm.br ^bb6(%17 : !llvm.i64)
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK: %{{[0-9]+}} = phi i64 [ %{{[0-9]+}}, %{{[0-9]+}} ], [ 0, %{{[0-9]+}} ]
^bb6(%19: !llvm.i64): // 2 preds: ^bb5, ^bb7
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = icmp slt i64 %{{[0-9]+}}, 10
%20 = llvm.icmp "slt" %19, %18 : !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: br i1 %{{[0-9]+}}, label %{{[0-9]+}}, label %{{[0-9]+}}
llvm.cond_br %20, ^bb7, ^bb8
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb7: // pred: ^bb6
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK: %{{[0-9]+}} = extractvalue { float* } %{{[0-9]+}}, 0
// CHECK-NEXT: %{{[0-9]+}} = getelementptr float, float* %{{[0-9]+}}, i64 %{{[0-9]+}}
// CHECK-NEXT: %{{[0-9]+}} = load float, float* %{{[0-9]+}}
%21 = llvm.mlir.constant(10 : index) : !llvm.i64
%22 = llvm.extractvalue %6[0] : !llvm<"{ float* }">
%23 = llvm.getelementptr %22[%19] : (!llvm<"float*">, !llvm.i64) -> !llvm<"float*">
%24 = llvm.load %23 : !llvm<"float*">
%25 = llvm.mlir.constant(1 : index) : !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = add i64 %{{[0-9]+}}, 1
%26 = llvm.add %19, %25 : !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: br label %{{[0-9]+}}
llvm.br ^bb6(%26 : !llvm.i64)
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb8: // pred: ^bb6
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK: ret void
llvm.return
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
}
// CHECK-LABEL: define void @store_load_dynamic(i64 {{%.*}})
llvm.func @store_load_dynamic(%arg0: !llvm.i64) {
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = mul i64 %{{[0-9]+}}, 4
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
// CHECK-NEXT: %{{[0-9]+}} = call i8* @malloc(i64 %{{[0-9]+}})
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = bitcast i8* %{{[0-9]+}} to float*
// CHECK-NEXT: %{{[0-9]+}} = insertvalue { float*, i64 } undef, float* %{{[0-9]+}}, 0
// CHECK-NEXT: %{{[0-9]+}} = insertvalue { float*, i64 } %{{[0-9]+}}, i64 %{{[0-9]+}}, 1
%0 = llvm.mlir.undef : !llvm<"{ float*, i64 }">
%1 = llvm.mlir.constant(4 : index) : !llvm.i64
%2 = llvm.mul %arg0, %1 : !llvm.i64
%3 = llvm.call @malloc(%2) : (!llvm.i64) -> !llvm<"i8*">
%4 = llvm.bitcast %3 : !llvm<"i8*"> to !llvm<"float*">
%5 = llvm.insertvalue %4, %0[0] : !llvm<"{ float*, i64 }">
%6 = llvm.insertvalue %arg0, %5[1] : !llvm<"{ float*, i64 }">
%7 = llvm.mlir.constant(1.000000e+00 : f32) : !llvm.float
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: br label %{{[0-9]+}}
llvm.br ^bb1
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb1: // pred: ^bb0
%8 = llvm.mlir.constant(0 : index) : !llvm.i64
llvm.br ^bb2(%8 : !llvm.i64)
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK: %{{[0-9]+}} = phi i64 [ %{{[0-9]+}}, %{{[0-9]+}} ], [ 0, %{{[0-9]+}} ]
^bb2(%9: !llvm.i64): // 2 preds: ^bb1, ^bb3
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = icmp slt i64 %{{[0-9]+}}, %{{[0-9]+}}
%10 = llvm.icmp "slt" %9, %arg0 : !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: br i1 %{{[0-9]+}}, label %{{[0-9]+}}, label %{{[0-9]+}}
llvm.cond_br %10, ^bb3, ^bb4
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb3: // pred: ^bb2
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK: %{{[0-9]+}} = extractvalue { float*, i64 } %{{[0-9]+}}, 1
// CHECK-NEXT: %{{[0-9]+}} = extractvalue { float*, i64 } %{{[0-9]+}}, 0
// CHECK-NEXT: %{{[0-9]+}} = getelementptr float, float* %{{[0-9]+}}, i64 %{{[0-9]+}}
// CHECK-NEXT: store float 1.000000e+00, float* %{{[0-9]+}}
%11 = llvm.extractvalue %6[1] : !llvm<"{ float*, i64 }">
%12 = llvm.extractvalue %6[0] : !llvm<"{ float*, i64 }">
%13 = llvm.getelementptr %12[%9] : (!llvm<"float*">, !llvm.i64) -> !llvm<"float*">
llvm.store %7, %13 : !llvm<"float*">
%14 = llvm.mlir.constant(1 : index) : !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = add i64 %{{[0-9]+}}, 1
%15 = llvm.add %9, %14 : !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: br label %{{[0-9]+}}
llvm.br ^bb2(%15 : !llvm.i64)
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb4: // pred: ^bb3
llvm.br ^bb5
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb5: // pred: ^bb4
%16 = llvm.mlir.constant(0 : index) : !llvm.i64
llvm.br ^bb6(%16 : !llvm.i64)
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK: %{{[0-9]+}} = phi i64 [ %{{[0-9]+}}, %{{[0-9]+}} ], [ 0, %{{[0-9]+}} ]
^bb6(%17: !llvm.i64): // 2 preds: ^bb5, ^bb7
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = icmp slt i64 %{{[0-9]+}}, %{{[0-9]+}}
%18 = llvm.icmp "slt" %17, %arg0 : !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: br i1 %{{[0-9]+}}, label %{{[0-9]+}}, label %{{[0-9]+}}
llvm.cond_br %18, ^bb7, ^bb8
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb7: // pred: ^bb6
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK: %{{[0-9]+}} = extractvalue { float*, i64 } %{{[0-9]+}}, 1
// CHECK-NEXT: %{{[0-9]+}} = extractvalue { float*, i64 } %{{[0-9]+}}, 0
// CHECK-NEXT: %{{[0-9]+}} = getelementptr float, float* %{{[0-9]+}}, i64 %{{[0-9]+}}
// CHECK-NEXT: %{{[0-9]+}} = load float, float* %{{[0-9]+}}
%19 = llvm.extractvalue %6[1] : !llvm<"{ float*, i64 }">
%20 = llvm.extractvalue %6[0] : !llvm<"{ float*, i64 }">
%21 = llvm.getelementptr %20[%17] : (!llvm<"float*">, !llvm.i64) -> !llvm<"float*">
%22 = llvm.load %21 : !llvm<"float*">
%23 = llvm.mlir.constant(1 : index) : !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = add i64 %{{[0-9]+}}, 1
%24 = llvm.add %17, %23 : !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: br label %{{[0-9]+}}
llvm.br ^bb6(%24 : !llvm.i64)
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
^bb8: // pred: ^bb6
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK: ret void
llvm.return
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
}
// CHECK-LABEL: define void @store_load_mixed(i64 {{%.*}})
llvm.func @store_load_mixed(%arg0: !llvm.i64) {
%0 = llvm.mlir.constant(10 : index) : !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = mul i64 2, %{{[0-9]+}}
// CHECK-NEXT: %{{[0-9]+}} = mul i64 %{{[0-9]+}}, 4
// CHECK-NEXT: %{{[0-9]+}} = mul i64 %{{[0-9]+}}, 10
// CHECK-NEXT: %{{[0-9]+}} = mul i64 %{{[0-9]+}}, 4
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
// CHECK-NEXT: %{{[0-9]+}} = call i8* @malloc(i64 %{{[0-9]+}})
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = bitcast i8* %{{[0-9]+}} to float*
// CHECK-NEXT: %{{[0-9]+}} = insertvalue { float*, i64, i64 } undef, float* %{{[0-9]+}}, 0
// CHECK-NEXT: %{{[0-9]+}} = insertvalue { float*, i64, i64 } %{{[0-9]+}}, i64 %{{[0-9]+}}, 1
// CHECK-NEXT: %{{[0-9]+}} = insertvalue { float*, i64, i64 } %{{[0-9]+}}, i64 10, 2
%1 = llvm.mlir.constant(2 : index) : !llvm.i64
%2 = llvm.mlir.constant(4 : index) : !llvm.i64
%3 = llvm.mul %1, %arg0 : !llvm.i64
%4 = llvm.mul %3, %2 : !llvm.i64
%5 = llvm.mul %4, %0 : !llvm.i64
%6 = llvm.mlir.undef : !llvm<"{ float*, i64, i64 }">
%7 = llvm.mlir.constant(4 : index) : !llvm.i64
%8 = llvm.mul %5, %7 : !llvm.i64
%9 = llvm.call @malloc(%8) : (!llvm.i64) -> !llvm<"i8*">
%10 = llvm.bitcast %9 : !llvm<"i8*"> to !llvm<"float*">
%11 = llvm.insertvalue %10, %6[0] : !llvm<"{ float*, i64, i64 }">
%12 = llvm.insertvalue %arg0, %11[1] : !llvm<"{ float*, i64, i64 }">
%13 = llvm.insertvalue %0, %12[2] : !llvm<"{ float*, i64, i64 }">
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = call i64 @get_index()
// CHECK-NEXT: %{{[0-9]+}} = call i64 @get_index()
%14 = llvm.mlir.constant(1 : index) : !llvm.i64
%15 = llvm.mlir.constant(2 : index) : !llvm.i64
%16 = llvm.call @get_index() : () -> !llvm.i64
%17 = llvm.call @get_index() : () -> !llvm.i64
%18 = llvm.mlir.constant(4.200000e+01 : f32) : !llvm.float
%19 = llvm.mlir.constant(2 : index) : !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = extractvalue { float*, i64, i64 } %{{[0-9]+}}, 1
// CHECK-NEXT: %{{[0-9]+}} = extractvalue { float*, i64, i64 } %{{[0-9]+}}, 2
// CHECK-NEXT: %{{[0-9]+}} = mul i64 1, %{{[0-9]+}}
// CHECK-NEXT: %{{[0-9]+}} = add i64 %{{[0-9]+}}, 2
// CHECK-NEXT: %{{[0-9]+}} = mul i64 %{{[0-9]+}}, 4
// CHECK-NEXT: %{{[0-9]+}} = add i64 %{{[0-9]+}}, %{{[0-9]+}}
// CHECK-NEXT: %{{[0-9]+}} = mul i64 %{{[0-9]+}}, %{{[0-9]+}}
// CHECK-NEXT: %{{[0-9]+}} = add i64 %{{[0-9]+}}, %{{[0-9]+}}
// CHECK-NEXT: %{{[0-9]+}} = extractvalue { float*, i64, i64 } %{{[0-9]+}}, 0
// CHECK-NEXT: %{{[0-9]+}} = getelementptr float, float* %{{[0-9]+}}, i64 %{{[0-9]+}}
// CHECK-NEXT: store float 4.200000e+01, float* %{{[0-9]+}}
%20 = llvm.extractvalue %13[1] : !llvm<"{ float*, i64, i64 }">
%21 = llvm.mlir.constant(4 : index) : !llvm.i64
%22 = llvm.extractvalue %13[2] : !llvm<"{ float*, i64, i64 }">
%23 = llvm.mul %14, %20 : !llvm.i64
%24 = llvm.add %23, %15 : !llvm.i64
%25 = llvm.mul %24, %21 : !llvm.i64
%26 = llvm.add %25, %16 : !llvm.i64
%27 = llvm.mul %26, %22 : !llvm.i64
%28 = llvm.add %27, %17 : !llvm.i64
%29 = llvm.extractvalue %13[0] : !llvm<"{ float*, i64, i64 }">
%30 = llvm.getelementptr %29[%28] : (!llvm<"float*">, !llvm.i64) -> !llvm<"float*">
llvm.store %18, %30 : !llvm<"float*">
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = extractvalue { float*, i64, i64 } %{{[0-9]+}}, 1
// CHECK-NEXT: %{{[0-9]+}} = extractvalue { float*, i64, i64 } %{{[0-9]+}}, 2
// CHECK-NEXT: %{{[0-9]+}} = mul i64 %{{[0-9]+}}, %{{[0-9]+}}
// CHECK-NEXT: %{{[0-9]+}} = add i64 %{{[0-9]+}}, %{{[0-9]+}}
// CHECK-NEXT: %{{[0-9]+}} = mul i64 %{{[0-9]+}}, 4
// CHECK-NEXT: %{{[0-9]+}} = add i64 %{{[0-9]+}}, 2
// CHECK-NEXT: %{{[0-9]+}} = mul i64 %{{[0-9]+}}, %{{[0-9]+}}
// CHECK-NEXT: %{{[0-9]+}} = add i64 %{{[0-9]+}}, 1
// CHECK-NEXT: %{{[0-9]+}} = extractvalue { float*, i64, i64 } %{{[0-9]+}}, 0
// CHECK-NEXT: %{{[0-9]+}} = getelementptr float, float* %{{[0-9]+}}, i64 %{{[0-9]+}}
// CHECK-NEXT: %{{[0-9]+}} = load float, float* %{{[0-9]+}}
%31 = llvm.mlir.constant(2 : index) : !llvm.i64
%32 = llvm.extractvalue %13[1] : !llvm<"{ float*, i64, i64 }">
%33 = llvm.mlir.constant(4 : index) : !llvm.i64
%34 = llvm.extractvalue %13[2] : !llvm<"{ float*, i64, i64 }">
%35 = llvm.mul %17, %32 : !llvm.i64
%36 = llvm.add %35, %16 : !llvm.i64
%37 = llvm.mul %36, %33 : !llvm.i64
%38 = llvm.add %37, %15 : !llvm.i64
%39 = llvm.mul %38, %34 : !llvm.i64
%40 = llvm.add %39, %14 : !llvm.i64
%41 = llvm.extractvalue %13[0] : !llvm<"{ float*, i64, i64 }">
%42 = llvm.getelementptr %41[%40] : (!llvm<"float*">, !llvm.i64) -> !llvm<"float*">
%43 = llvm.load %42 : !llvm<"float*">
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: ret void
llvm.return
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
}
// CHECK-LABEL: define { float*, i64 } @memref_args_rets({ float* } {{%.*}}, { float*, i64 } {{%.*}}, { float*, i64 } {{%.*}})
llvm.func @memref_args_rets(%arg0: !llvm<"{ float* }">, %arg1: !llvm<"{ float*, i64 }">, %arg2: !llvm<"{ float*, i64 }">) -> !llvm<"{ float*, i64 }"> {
%0 = llvm.mlir.constant(7 : index) : !llvm.i64
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = call i64 @get_index()
%1 = llvm.call @get_index() : () -> !llvm.i64
%2 = llvm.mlir.constant(4.200000e+01 : f32) : !llvm.float
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = extractvalue { float* } %{{[0-9]+}}, 0
// CHECK-NEXT: %{{[0-9]+}} = getelementptr float, float* %{{[0-9]+}}, i64 7
// CHECK-NEXT: store float 4.200000e+01, float* %{{[0-9]+}}
%3 = llvm.mlir.constant(10 : index) : !llvm.i64
%4 = llvm.extractvalue %arg0[0] : !llvm<"{ float* }">
%5 = llvm.getelementptr %4[%0] : (!llvm<"float*">, !llvm.i64) -> !llvm<"float*">
llvm.store %2, %5 : !llvm<"float*">
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = extractvalue { float*, i64 } %{{[0-9]+}}, 1
// CHECK-NEXT: %{{[0-9]+}} = extractvalue { float*, i64 } %{{[0-9]+}}, 0
// CHECK-NEXT: %{{[0-9]+}} = getelementptr float, float* %{{[0-9]+}}, i64 7
// CHECK-NEXT: store float 4.200000e+01, float* %{{[0-9]+}}
%6 = llvm.extractvalue %arg1[1] : !llvm<"{ float*, i64 }">
%7 = llvm.extractvalue %arg1[0] : !llvm<"{ float*, i64 }">
%8 = llvm.getelementptr %7[%0] : (!llvm<"float*">, !llvm.i64) -> !llvm<"float*">
llvm.store %2, %8 : !llvm<"float*">
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = extractvalue { float*, i64 } %{{[0-9]+}}, 1
// CHECK-NEXT: %{{[0-9]+}} = mul i64 7, %{{[0-9]+}}
// CHECK-NEXT: %{{[0-9]+}} = add i64 %{{[0-9]+}}, %{{[0-9]+}}
// CHECK-NEXT: %{{[0-9]+}} = extractvalue { float*, i64 } %{{[0-9]+}}, 0
// CHECK-NEXT: %{{[0-9]+}} = getelementptr float, float* %{{[0-9]+}}, i64 %{{[0-9]+}}
// CHECK-NEXT: store float 4.200000e+01, float* %{{[0-9]+}}
%9 = llvm.mlir.constant(10 : index) : !llvm.i64
%10 = llvm.extractvalue %arg2[1] : !llvm<"{ float*, i64 }">
%11 = llvm.mul %0, %10 : !llvm.i64
%12 = llvm.add %11, %1 : !llvm.i64
%13 = llvm.extractvalue %arg2[0] : !llvm<"{ float*, i64 }">
%14 = llvm.getelementptr %13[%12] : (!llvm<"float*">, !llvm.i64) -> !llvm<"float*">
llvm.store %2, %14 : !llvm<"float*">
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = mul i64 10, %{{[0-9]+}}
// CHECK-NEXT: %{{[0-9]+}} = mul i64 %{{[0-9]+}}, 4
Reimplement LLVM IR translation to use the MLIR LLVM IR dialect Original implementation of the translation from MLIR to LLVM IR operated on the Standard+BuiltIn dialect, with a later addition of the SuperVector dialect. This required the translation to be aware of a potetially large number of other dialects as the infrastructure extended. With the recent introduction of the LLVM IR dialect into MLIR, the translation can be switched to only translate the LLVM IR dialect, and the translation of the operations becomes largely mechanical. The reimplementation of the translator follows the lines of the original translator in function and basic block conversion. In particular, block arguments are converted to LLVM IR PHI nodes, which are connected to their sources after all blocks of a function had been converted. Thanks to LLVM IR types being wrapped in the MLIR LLVM dialect type, type conversion is simplified to only convert function types, all other types are simply unwrapped. Individual instructions are constructed using the LLVM IRBuilder, which has a great potential for being table-generated from the LLVM IR dialect operation definitions. The input of the test/Target/llvmir.mlir is updated to use the MLIR LLVM IR dialect. While it is now redundant with the dialect conversion test, the point of the exercise is to guarantee exactly the same LLVM IR is emitted. (Only the name of the allocation function is changed from `__mlir_alloc` to `alloc` in the CHECK lines.) It will be simplified in a follow-up commit. PiperOrigin-RevId: 233842306
2019-02-14 07:30:24 +08:00
// CHECK-NEXT: %{{[0-9]+}} = call i8* @malloc(i64 %{{[0-9]+}})
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: %{{[0-9]+}} = bitcast i8* %{{[0-9]+}} to float*
// CHECK-NEXT: %{{[0-9]+}} = insertvalue { float*, i64 } undef, float* %{{[0-9]+}}, 0
// CHECK-NEXT: %{{[0-9]+}} = insertvalue { float*, i64 } %{{[0-9]+}}, i64 %{{[0-9]+}}, 1
%15 = llvm.mlir.constant(10 : index) : !llvm.i64
%16 = llvm.mul %15, %1 : !llvm.i64
%17 = llvm.mlir.undef : !llvm<"{ float*, i64 }">
%18 = llvm.mlir.constant(4 : index) : !llvm.i64
%19 = llvm.mul %16, %18 : !llvm.i64
%20 = llvm.call @malloc(%19) : (!llvm.i64) -> !llvm<"i8*">
%21 = llvm.bitcast %20 : !llvm<"i8*"> to !llvm<"float*">
%22 = llvm.insertvalue %21, %17[0] : !llvm<"{ float*, i64 }">
%23 = llvm.insertvalue %1, %22[1] : !llvm<"{ float*, i64 }">
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
// CHECK-NEXT: ret { float*, i64 } %{{[0-9]+}}
llvm.return %23 : !llvm<"{ float*, i64 }">
LLVM IR lowering: support simple MemRef types Introduce initial support for MemRef types, including type conversion, allocation and deallocation, read and write element-wise access, passing MemRefs to and returning from functions. Affine map compositions and non-default memory spaces are NOT YET supported. Lowered code needs to handle potentially dynamic sizes of the MemRef. To do so, it replaces a MemRef-typed value with a special MemRef descriptor that carries the data and the dynamic sizes together. A MemRef type is converted to LLVM's first-class structure type with the first element being the pointer to the data buffer with data layed out linearly, followed by as many integer-typed elements as MemRef has dynamic sizes. The type of these elements is that of MLIR index lowered to LLVM. For example, `memref<?x42x?xf32>` is converted to `{ f32*, i64, i64 }` provided `index` is lowered to `i64`. While it is possible to convert MemRefs with fully static sizes to simple pointers to their elemental types, we opted for consistency and convert them to the single-element structure. This makes the conversion code simpler and the calling convention of the generated LLVM IR functions consistent. Loads from and stores to a MemRef element are lowered to a sequence of LLVM instructions that, first, computes the linearized index of the element in the data buffer using the access indices and combining the static sizes with the dynamic sizes stored in the descriptor, and then loads from or stores to the buffer element indexed by the linearized subscript. While some of the index computations may be redundant (i.e., consecutive load and store to the same location in the same scope could reuse the linearized index), we emit them for every operation. A subsequent optimization pass may eliminate them if necessary. MemRef allocation and deallocation is performed using external functions `__mlir_alloc(index) -> i8*` and `__mlir_free(i8*)` that must be implemented by the caller. These functions behave similarly to `malloc` and `free`, but can be extended to support different memory spaces in future. Allocation and deallocation instructions take care of casting the pointers. Prior to calling the allocation function, the emitted code creates an SSA Value for the descriptor and uses it to store the dynamic sizes of the MemRef passed to the allocation operation. It further emits instructions that compute the dynamic amount of memory to allocate in bytes. Finally, the allocation stores the result of calling the `__mlir_alloc` in the MemRef descriptor. Deallocation extracts the pointer to the allocated memory from the descriptor and calls `__mlir_free` on it. The descriptor itself is not modified and, being stack-allocated, ceases to exist when it goes out of scope. MLIR functions that access MemRef values as arguments or return them are converted to LLVM IR functions that accept MemRef descriptors as LLVM IR structure types by value. This significantly simplifies the calling convention at the LLVM IR level and avoids handling descriptors in the dynamic memory, however is not always comaptible with LLVM IR functions emitted from C code with similar signatures. A separate LLVM pass may be introduced in the future to provide C-compatible calling conventions for LLVM IR functions generated from MLIR. PiperOrigin-RevId: 223134883
2018-11-28 18:32:10 +08:00
}
// CHECK-LABEL: define i64 @memref_dim({ float*, i64, i64 } {{%.*}})
llvm.func @memref_dim(%arg0: !llvm<"{ float*, i64, i64 }">) -> !llvm.i64 {
// Expecting this to create an LLVM constant.
%0 = llvm.mlir.constant(42 : index) : !llvm.i64
// CHECK-NEXT: %2 = extractvalue { float*, i64, i64 } %0, 1
%1 = llvm.extractvalue %arg0[1] : !llvm<"{ float*, i64, i64 }">
// Expecting this to create an LLVM constant.
%2 = llvm.mlir.constant(10 : index) : !llvm.i64
// CHECK-NEXT: %3 = extractvalue { float*, i64, i64 } %0, 2
%3 = llvm.extractvalue %arg0[2] : !llvm<"{ float*, i64, i64 }">
// Checking that the constant for d0 has been created.
// CHECK-NEXT: %4 = add i64 42, %2
%4 = llvm.add %0, %1 : !llvm.i64
// Checking that the constant for d2 has been created.
// CHECK-NEXT: %5 = add i64 10, %3
%5 = llvm.add %2, %3 : !llvm.i64
// CHECK-NEXT: %6 = add i64 %4, %5
%6 = llvm.add %4, %5 : !llvm.i64
// CHECK-NEXT: ret i64 %6
llvm.return %6 : !llvm.i64
}
llvm.func @get_i64() -> !llvm.i64
llvm.func @get_f32() -> !llvm.float
llvm.func @get_memref() -> !llvm<"{ float*, i64, i64 }">
// CHECK-LABEL: define { i64, float, { float*, i64, i64 } } @multireturn()
llvm.func @multireturn() -> !llvm<"{ i64, float, { float*, i64, i64 } }"> {
%0 = llvm.call @get_i64() : () -> !llvm.i64
%1 = llvm.call @get_f32() : () -> !llvm.float
%2 = llvm.call @get_memref() : () -> !llvm<"{ float*, i64, i64 }">
// CHECK: %{{[0-9]+}} = insertvalue { i64, float, { float*, i64, i64 } } undef, i64 %{{[0-9]+}}, 0
// CHECK-NEXT: %{{[0-9]+}} = insertvalue { i64, float, { float*, i64, i64 } } %{{[0-9]+}}, float %{{[0-9]+}}, 1
// CHECK-NEXT: %{{[0-9]+}} = insertvalue { i64, float, { float*, i64, i64 } } %{{[0-9]+}}, { float*, i64, i64 } %{{[0-9]+}}, 2
// CHECK-NEXT: ret { i64, float, { float*, i64, i64 } } %{{[0-9]+}}
%3 = llvm.mlir.undef : !llvm<"{ i64, float, { float*, i64, i64 } }">
%4 = llvm.insertvalue %0, %3[0] : !llvm<"{ i64, float, { float*, i64, i64 } }">
%5 = llvm.insertvalue %1, %4[1] : !llvm<"{ i64, float, { float*, i64, i64 } }">
%6 = llvm.insertvalue %2, %5[2] : !llvm<"{ i64, float, { float*, i64, i64 } }">
llvm.return %6 : !llvm<"{ i64, float, { float*, i64, i64 } }">
}
// CHECK-LABEL: define void @multireturn_caller()
llvm.func @multireturn_caller() {
// CHECK-NEXT: %1 = call { i64, float, { float*, i64, i64 } } @multireturn()
// CHECK-NEXT: [[ret0:%[0-9]+]] = extractvalue { i64, float, { float*, i64, i64 } } %1, 0
// CHECK-NEXT: [[ret1:%[0-9]+]] = extractvalue { i64, float, { float*, i64, i64 } } %1, 1
// CHECK-NEXT: [[ret2:%[0-9]+]] = extractvalue { i64, float, { float*, i64, i64 } } %1, 2
%0 = llvm.call @multireturn() : () -> !llvm<"{ i64, float, { float*, i64, i64 } }">
%1 = llvm.extractvalue %0[0] : !llvm<"{ i64, float, { float*, i64, i64 } }">
%2 = llvm.extractvalue %0[1] : !llvm<"{ i64, float, { float*, i64, i64 } }">
%3 = llvm.extractvalue %0[2] : !llvm<"{ i64, float, { float*, i64, i64 } }">
%4 = llvm.mlir.constant(42) : !llvm.i64
// CHECK: add i64 [[ret0]], 42
%5 = llvm.add %1, %4 : !llvm.i64
%6 = llvm.mlir.constant(4.200000e+01 : f32) : !llvm.float
// CHECK: fadd float [[ret1]], 4.200000e+01
%7 = llvm.fadd %2, %6 : !llvm.float
%8 = llvm.mlir.constant(0 : index) : !llvm.i64
%9 = llvm.mlir.constant(42 : index) : !llvm.i64
// CHECK: extractvalue { float*, i64, i64 } [[ret2]], 0
%10 = llvm.extractvalue %3[1] : !llvm<"{ float*, i64, i64 }">
%11 = llvm.mlir.constant(10 : index) : !llvm.i64
%12 = llvm.extractvalue %3[2] : !llvm<"{ float*, i64, i64 }">
%13 = llvm.mul %8, %10 : !llvm.i64
%14 = llvm.add %13, %8 : !llvm.i64
%15 = llvm.mul %14, %11 : !llvm.i64
%16 = llvm.add %15, %8 : !llvm.i64
%17 = llvm.mul %16, %12 : !llvm.i64
%18 = llvm.add %17, %8 : !llvm.i64
%19 = llvm.extractvalue %3[0] : !llvm<"{ float*, i64, i64 }">
%20 = llvm.getelementptr %19[%18] : (!llvm<"float*">, !llvm.i64) -> !llvm<"float*">
%21 = llvm.load %20 : !llvm<"float*">
llvm.return
}
// CHECK-LABEL: define <4 x float> @vector_ops(<4 x float> {{%.*}}, <4 x i1> {{%.*}}, <4 x i64> {{%.*}})
llvm.func @vector_ops(%arg0: !llvm<"<4 x float>">, %arg1: !llvm<"<4 x i1>">, %arg2: !llvm<"<4 x i64>">) -> !llvm<"<4 x float>"> {
%0 = llvm.mlir.constant(dense<4.200000e+01> : vector<4xf32>) : !llvm<"<4 x float>">
// CHECK-NEXT: %4 = fadd <4 x float> %0, <float 4.200000e+01, float 4.200000e+01, float 4.200000e+01, float 4.200000e+01>
%1 = llvm.fadd %arg0, %0 : !llvm<"<4 x float>">
// CHECK-NEXT: %5 = select <4 x i1> %1, <4 x float> %4, <4 x float> %0
%2 = llvm.select %arg1, %1, %arg0 : !llvm<"<4 x i1>">, !llvm<"<4 x float>">
// CHECK-NEXT: %6 = sdiv <4 x i64> %2, %2
%3 = llvm.sdiv %arg2, %arg2 : !llvm<"<4 x i64>">
// CHECK-NEXT: %7 = udiv <4 x i64> %2, %2
%4 = llvm.udiv %arg2, %arg2 : !llvm<"<4 x i64>">
// CHECK-NEXT: %8 = srem <4 x i64> %2, %2
%5 = llvm.srem %arg2, %arg2 : !llvm<"<4 x i64>">
// CHECK-NEXT: %9 = urem <4 x i64> %2, %2
%6 = llvm.urem %arg2, %arg2 : !llvm<"<4 x i64>">
// CHECK-NEXT: %10 = fdiv <4 x float> %0, <float 4.200000e+01, float 4.200000e+01, float 4.200000e+01, float 4.200000e+01>
%7 = llvm.fdiv %arg0, %0 : !llvm<"<4 x float>">
// CHECK-NEXT: %11 = frem <4 x float> %0, <float 4.200000e+01, float 4.200000e+01, float 4.200000e+01, float 4.200000e+01>
%8 = llvm.frem %arg0, %0 : !llvm<"<4 x float>">
// CHECK-NEXT: %12 = and <4 x i64> %2, %2
%9 = llvm.and %arg2, %arg2 : !llvm<"<4 x i64>">
// CHECK-NEXT: %13 = or <4 x i64> %2, %2
%10 = llvm.or %arg2, %arg2 : !llvm<"<4 x i64>">
// CHECK-NEXT: %14 = xor <4 x i64> %2, %2
%11 = llvm.xor %arg2, %arg2 : !llvm<"<4 x i64>">
// CHECK-NEXT: %15 = shl <4 x i64> %2, %2
%12 = llvm.shl %arg2, %arg2 : !llvm<"<4 x i64>">
// CHECK-NEXT: %16 = lshr <4 x i64> %2, %2
%13 = llvm.lshr %arg2, %arg2 : !llvm<"<4 x i64>">
// CHECK-NEXT: %17 = ashr <4 x i64> %2, %2
%14 = llvm.ashr %arg2, %arg2 : !llvm<"<4 x i64>">
// CHECK-NEXT: ret <4 x float> %4
llvm.return %1 : !llvm<"<4 x float>">
}
// CHECK-LABEL: @vector_splat_1d
llvm.func @vector_splat_1d() -> !llvm<"<4 x float>"> {
// CHECK: ret <4 x float> zeroinitializer
%0 = llvm.mlir.constant(dense<0.000000e+00> : vector<4xf32>) : !llvm<"<4 x float>">
llvm.return %0 : !llvm<"<4 x float>">
}
// CHECK-LABEL: @vector_splat_2d
llvm.func @vector_splat_2d() -> !llvm<"[4 x <16 x float>]"> {
// CHECK: ret [4 x <16 x float>] zeroinitializer
%0 = llvm.mlir.constant(dense<0.000000e+00> : vector<4x16xf32>) : !llvm<"[4 x <16 x float>]">
llvm.return %0 : !llvm<"[4 x <16 x float>]">
}
// CHECK-LABEL: @vector_splat_3d
llvm.func @vector_splat_3d() -> !llvm<"[4 x [16 x <4 x float>]]"> {
// CHECK: ret [4 x [16 x <4 x float>]] zeroinitializer
%0 = llvm.mlir.constant(dense<0.000000e+00> : vector<4x16x4xf32>) : !llvm<"[4 x [16 x <4 x float>]]">
llvm.return %0 : !llvm<"[4 x [16 x <4 x float>]]">
}
// CHECK-LABEL: @vector_splat_nonzero
llvm.func @vector_splat_nonzero() -> !llvm<"<4 x float>"> {
// CHECK: ret <4 x float> <float 1.000000e+00, float 1.000000e+00, float 1.000000e+00, float 1.000000e+00>
%0 = llvm.mlir.constant(dense<1.000000e+00> : vector<4xf32>) : !llvm<"<4 x float>">
llvm.return %0 : !llvm<"<4 x float>">
}
// CHECK-LABEL: @ops
llvm.func @ops(%arg0: !llvm.float, %arg1: !llvm.float, %arg2: !llvm.i32, %arg3: !llvm.i32) -> !llvm<"{ float, i32 }"> {
// CHECK-NEXT: fsub float %0, %1
%0 = llvm.fsub %arg0, %arg1 : !llvm.float
// CHECK-NEXT: %6 = sub i32 %2, %3
%1 = llvm.sub %arg2, %arg3 : !llvm.i32
// CHECK-NEXT: %7 = icmp slt i32 %2, %6
%2 = llvm.icmp "slt" %arg2, %1 : !llvm.i32
// CHECK-NEXT: %8 = select i1 %7, i32 %2, i32 %6
%3 = llvm.select %2, %arg2, %1 : !llvm.i1, !llvm.i32
// CHECK-NEXT: %9 = sdiv i32 %2, %3
%4 = llvm.sdiv %arg2, %arg3 : !llvm.i32
// CHECK-NEXT: %10 = udiv i32 %2, %3
%5 = llvm.udiv %arg2, %arg3 : !llvm.i32
// CHECK-NEXT: %11 = srem i32 %2, %3
%6 = llvm.srem %arg2, %arg3 : !llvm.i32
// CHECK-NEXT: %12 = urem i32 %2, %3
%7 = llvm.urem %arg2, %arg3 : !llvm.i32
%8 = llvm.mlir.undef : !llvm<"{ float, i32 }">
%9 = llvm.insertvalue %0, %8[0] : !llvm<"{ float, i32 }">
%10 = llvm.insertvalue %3, %9[1] : !llvm<"{ float, i32 }">
// CHECK: %15 = fdiv float %0, %1
%11 = llvm.fdiv %arg0, %arg1 : !llvm.float
// CHECK-NEXT: %16 = frem float %0, %1
%12 = llvm.frem %arg0, %arg1 : !llvm.float
// CHECK-NEXT: %17 = and i32 %2, %3
%13 = llvm.and %arg2, %arg3 : !llvm.i32
// CHECK-NEXT: %18 = or i32 %2, %3
%14 = llvm.or %arg2, %arg3 : !llvm.i32
// CHECK-NEXT: %19 = xor i32 %2, %3
%15 = llvm.xor %arg2, %arg3 : !llvm.i32
// CHECK-NEXT: %20 = shl i32 %2, %3
%16 = llvm.shl %arg2, %arg3 : !llvm.i32
// CHECK-NEXT: %21 = lshr i32 %2, %3
%17 = llvm.lshr %arg2, %arg3 : !llvm.i32
// CHECK-NEXT: %22 = ashr i32 %2, %3
%18 = llvm.ashr %arg2, %arg3 : !llvm.i32
// CHECK-NEXT: fneg float %0
%19 = llvm.fneg %arg0 : !llvm.float
llvm.return %10 : !llvm<"{ float, i32 }">
}
//
// Indirect function calls
//
// CHECK-LABEL: define void @indirect_const_call(i64 {{%.*}})
llvm.func @indirect_const_call(%arg0: !llvm.i64) {
// CHECK-NEXT: call void @body(i64 %0)
%0 = llvm.mlir.constant(@body) : !llvm<"void (i64)*">
llvm.call %0(%arg0) : (!llvm.i64) -> ()
// CHECK-NEXT: ret void
llvm.return
}
// CHECK-LABEL: define i32 @indirect_call(i32 (float)* {{%.*}}, float {{%.*}})
llvm.func @indirect_call(%arg0: !llvm<"i32 (float)*">, %arg1: !llvm.float) -> !llvm.i32 {
// CHECK-NEXT: %3 = call i32 %0(float %1)
%0 = llvm.call %arg0(%arg1) : (!llvm.float) -> !llvm.i32
// CHECK-NEXT: ret i32 %3
llvm.return %0 : !llvm.i32
}
//
// Check that we properly construct phi nodes in the blocks that have the same
// predecessor more than once.
//
// CHECK-LABEL: define void @cond_br_arguments(i1 {{%.*}}, i1 {{%.*}})
llvm.func @cond_br_arguments(%arg0: !llvm.i1, %arg1: !llvm.i1) {
// CHECK-NEXT: br i1 %0, label %3, label %5
llvm.cond_br %arg0, ^bb1(%arg0 : !llvm.i1), ^bb2
// CHECK: 3:
// CHECK-NEXT: %4 = phi i1 [ %1, %5 ], [ %0, %2 ]
^bb1(%0 : !llvm.i1):
// CHECK-NEXT: ret void
llvm.return
// CHECK: 5:
^bb2:
// CHECK-NEXT: br label %3
llvm.br ^bb1(%arg1 : !llvm.i1)
}
// CHECK-LABEL: define void @llvm_noalias(float* noalias {{%*.}})
llvm.func @llvm_noalias(%arg0: !llvm<"float*"> {llvm.noalias = true}) {
llvm.return
}
// CHECK-LABEL: @llvm_varargs(...)
llvm.func @llvm_varargs(...)
llvm.func @intpointerconversion(%arg0 : !llvm.i32) -> !llvm.i32 {
// CHECK: %2 = inttoptr i32 %0 to i32*
// CHECK-NEXT: %3 = ptrtoint i32* %2 to i32
%1 = llvm.inttoptr %arg0 : !llvm.i32 to !llvm<"i32*">
%2 = llvm.ptrtoint %1 : !llvm<"i32*"> to !llvm.i32
llvm.return %2 : !llvm.i32
}
llvm.func @fpconversion(%arg0 : !llvm.i32) -> !llvm.i32 {
// CHECK: %2 = sitofp i32 %0 to float
// CHECK-NEXT: %3 = fptosi float %2 to i32
// CHECK-NEXT: %4 = uitofp i32 %3 to float
// CHECK-NEXT: %5 = fptoui float %4 to i32
%1 = llvm.sitofp %arg0 : !llvm.i32 to !llvm.float
%2 = llvm.fptosi %1 : !llvm.float to !llvm.i32
%3 = llvm.uitofp %2 : !llvm.i32 to !llvm.float
%4 = llvm.fptoui %3 : !llvm.float to !llvm.i32
llvm.return %4 : !llvm.i32
}
// CHECK-LABEL: @addrspace
llvm.func @addrspace(%arg0 : !llvm<"i32*">) -> !llvm<"i32 addrspace(2)*"> {
// CHECK: %2 = addrspacecast i32* %0 to i32 addrspace(2)*
%1 = llvm.addrspacecast %arg0 : !llvm<"i32*"> to !llvm<"i32 addrspace(2)*">
llvm.return %1 : !llvm<"i32 addrspace(2)*">
}
llvm.func @stringconstant() -> !llvm<"i8*"> {
%1 = llvm.mlir.constant("Hello world!") : !llvm<"i8*">
// CHECK: ret [12 x i8] c"Hello world!"
llvm.return %1 : !llvm<"i8*">
}
llvm.func @noreach() {
// CHECK: unreachable
llvm.unreachable
}
// CHECK-LABEL: define void @fcmp
llvm.func @fcmp(%arg0: !llvm.float, %arg1: !llvm.float) {
// CHECK: fcmp oeq float %0, %1
// CHECK-NEXT: fcmp ogt float %0, %1
// CHECK-NEXT: fcmp oge float %0, %1
// CHECK-NEXT: fcmp olt float %0, %1
// CHECK-NEXT: fcmp ole float %0, %1
// CHECK-NEXT: fcmp one float %0, %1
// CHECK-NEXT: fcmp ord float %0, %1
// CHECK-NEXT: fcmp ueq float %0, %1
// CHECK-NEXT: fcmp ugt float %0, %1
// CHECK-NEXT: fcmp uge float %0, %1
// CHECK-NEXT: fcmp ult float %0, %1
// CHECK-NEXT: fcmp ule float %0, %1
// CHECK-NEXT: fcmp une float %0, %1
// CHECK-NEXT: fcmp uno float %0, %1
%0 = llvm.fcmp "oeq" %arg0, %arg1 : !llvm.float
%1 = llvm.fcmp "ogt" %arg0, %arg1 : !llvm.float
%2 = llvm.fcmp "oge" %arg0, %arg1 : !llvm.float
%3 = llvm.fcmp "olt" %arg0, %arg1 : !llvm.float
%4 = llvm.fcmp "ole" %arg0, %arg1 : !llvm.float
%5 = llvm.fcmp "one" %arg0, %arg1 : !llvm.float
%6 = llvm.fcmp "ord" %arg0, %arg1 : !llvm.float
%7 = llvm.fcmp "ueq" %arg0, %arg1 : !llvm.float
%8 = llvm.fcmp "ugt" %arg0, %arg1 : !llvm.float
%9 = llvm.fcmp "uge" %arg0, %arg1 : !llvm.float
%10 = llvm.fcmp "ult" %arg0, %arg1 : !llvm.float
%11 = llvm.fcmp "ule" %arg0, %arg1 : !llvm.float
%12 = llvm.fcmp "une" %arg0, %arg1 : !llvm.float
%13 = llvm.fcmp "uno" %arg0, %arg1 : !llvm.float
llvm.return
}
// CHECK-LABEL: @vect
llvm.func @vect(%arg0: !llvm<"<4 x float>">, %arg1: !llvm.i32, %arg2: !llvm.float) {
// CHECK-NEXT: extractelement <4 x float> {{.*}}, i32
// CHECK-NEXT: insertelement <4 x float> {{.*}}, float %2, i32
// CHECK-NEXT: shufflevector <4 x float> {{.*}}, <4 x float> {{.*}}, <5 x i32> <i32 0, i32 0, i32 0, i32 0, i32 7>
%0 = llvm.extractelement %arg0[%arg1 : !llvm.i32] : !llvm<"<4 x float>">
%1 = llvm.insertelement %arg2, %arg0[%arg1 : !llvm.i32] : !llvm<"<4 x float>">
%2 = llvm.shufflevector %arg0, %arg0 [0 : i32, 0 : i32, 0 : i32, 0 : i32, 7 : i32] : !llvm<"<4 x float>">, !llvm<"<4 x float>">
llvm.return
}
// CHECK-LABEL: @vect_i64idx
llvm.func @vect_i64idx(%arg0: !llvm<"<4 x float>">, %arg1: !llvm.i64, %arg2: !llvm.float) {
// CHECK-NEXT: extractelement <4 x float> {{.*}}, i64
// CHECK-NEXT: insertelement <4 x float> {{.*}}, float %2, i64
%0 = llvm.extractelement %arg0[%arg1 : !llvm.i64] : !llvm<"<4 x float>">
%1 = llvm.insertelement %arg2, %arg0[%arg1 : !llvm.i64] : !llvm<"<4 x float>">
llvm.return
}
// CHECK-LABEL: @alloca
llvm.func @alloca(%size : !llvm.i64) {
// CHECK: alloca
// CHECK-NOT: align
llvm.alloca %size x !llvm.i32 {alignment = 0} : (!llvm.i64) -> (!llvm<"i32*">)
// CHECK-NEXT: alloca {{.*}} align 8
llvm.alloca %size x !llvm.i32 {alignment = 8} : (!llvm.i64) -> (!llvm<"i32*">)
llvm.return
}
// CHECK-LABEL: @constants
llvm.func @constants() -> !llvm<"<4 x float>"> {
// CHECK: ret <4 x float> <float 4.2{{0*}}e+01, float 0.{{0*}}e+00, float 0.{{0*}}e+00, float 0.{{0*}}e+00>
%0 = llvm.mlir.constant(sparse<[[0]], [4.2e+01]> : vector<4xf32>) : !llvm<"<4 x float>">
llvm.return %0 : !llvm<"<4 x float>">
}
// CHECK-LABEL: @fp_casts
llvm.func @fp_casts(%fp1 : !llvm<"float">, %fp2 : !llvm<"double">) -> !llvm.i16 {
// CHECK: fptrunc double {{.*}} to float
%a = llvm.fptrunc %fp2 : !llvm<"double"> to !llvm<"float">
// CHECK: fpext float {{.*}} to double
%b = llvm.fpext %fp1 : !llvm<"float"> to !llvm<"double">
// CHECK: fptosi double {{.*}} to i16
%c = llvm.fptosi %b : !llvm<"double"> to !llvm.i16
llvm.return %c : !llvm.i16
}
// CHECK-LABEL: @integer_extension_and_truncation
llvm.func @integer_extension_and_truncation(%a : !llvm.i32) {
// CHECK: sext i32 {{.*}} to i64
// CHECK: zext i32 {{.*}} to i64
// CHECK: trunc i32 {{.*}} to i16
%0 = llvm.sext %a : !llvm.i32 to !llvm.i64
%1 = llvm.zext %a : !llvm.i32 to !llvm.i64
%2 = llvm.trunc %a : !llvm.i32 to !llvm.i16
llvm.return
}
// Check that the auxiliary `null` operation is converted into a `null` value.
// CHECK-LABEL: @null
llvm.func @null() -> !llvm<"i32*"> {
%0 = llvm.mlir.null : !llvm<"i32*">
// CHECK: ret i32* null
llvm.return %0 : !llvm<"i32*">
}
// Check that dense elements attributes are exported properly in constants.
// CHECK-LABEL: @elements_constant_3d_vector
llvm.func @elements_constant_3d_vector() -> !llvm<"[2 x [2 x <2 x i32>]]"> {
// CHECK: ret [2 x [2 x <2 x i32>]]
// CHECK-SAME: {{\[}}[2 x <2 x i32>] [<2 x i32> <i32 1, i32 2>, <2 x i32> <i32 3, i32 4>],
// CHECK-SAME: [2 x <2 x i32>] [<2 x i32> <i32 42, i32 43>, <2 x i32> <i32 44, i32 45>]]
%0 = llvm.mlir.constant(dense<[[[1, 2], [3, 4]], [[42, 43], [44, 45]]]> : vector<2x2x2xi32>) : !llvm<"[2 x [2 x <2 x i32>]]">
llvm.return %0 : !llvm<"[2 x [2 x <2 x i32>]]">
}
// CHECK-LABEL: @elements_constant_3d_array
llvm.func @elements_constant_3d_array() -> !llvm<"[2 x [2 x [2 x i32]]]"> {
// CHECK: ret [2 x [2 x [2 x i32]]]
// CHECK-SAME: {{\[}}[2 x [2 x i32]] {{\[}}[2 x i32] [i32 1, i32 2], [2 x i32] [i32 3, i32 4]],
// CHECK-SAME: [2 x [2 x i32]] {{\[}}[2 x i32] [i32 42, i32 43], [2 x i32] [i32 44, i32 45]]]
%0 = llvm.mlir.constant(dense<[[[1, 2], [3, 4]], [[42, 43], [44, 45]]]> : tensor<2x2x2xi32>) : !llvm<"[2 x [2 x [2 x i32]]]">
llvm.return %0 : !llvm<"[2 x [2 x [2 x i32]]]">
}
// CHECK-LABEL: @atomicrmw
llvm.func @atomicrmw(
%f32_ptr : !llvm<"float*">, %f32 : !llvm.float,
%i32_ptr : !llvm<"i32*">, %i32 : !llvm.i32) {
// CHECK: atomicrmw fadd float* %{{.*}}, float %{{.*}} unordered
%0 = llvm.atomicrmw fadd %f32_ptr, %f32 unordered : !llvm.float
// CHECK: atomicrmw fsub float* %{{.*}}, float %{{.*}} unordered
%1 = llvm.atomicrmw fsub %f32_ptr, %f32 unordered : !llvm.float
// CHECK: atomicrmw xchg float* %{{.*}}, float %{{.*}} monotonic
%2 = llvm.atomicrmw xchg %f32_ptr, %f32 monotonic : !llvm.float
// CHECK: atomicrmw add i32* %{{.*}}, i32 %{{.*}} acquire
%3 = llvm.atomicrmw add %i32_ptr, %i32 acquire : !llvm.i32
// CHECK: atomicrmw sub i32* %{{.*}}, i32 %{{.*}} release
%4 = llvm.atomicrmw sub %i32_ptr, %i32 release : !llvm.i32
// CHECK: atomicrmw and i32* %{{.*}}, i32 %{{.*}} acq_rel
%5 = llvm.atomicrmw _and %i32_ptr, %i32 acq_rel : !llvm.i32
// CHECK: atomicrmw nand i32* %{{.*}}, i32 %{{.*}} seq_cst
%6 = llvm.atomicrmw nand %i32_ptr, %i32 seq_cst : !llvm.i32
// CHECK: atomicrmw or i32* %{{.*}}, i32 %{{.*}} unordered
%7 = llvm.atomicrmw _or %i32_ptr, %i32 unordered : !llvm.i32
// CHECK: atomicrmw xor i32* %{{.*}}, i32 %{{.*}} unordered
%8 = llvm.atomicrmw _xor %i32_ptr, %i32 unordered : !llvm.i32
// CHECK: atomicrmw max i32* %{{.*}}, i32 %{{.*}} unordered
%9 = llvm.atomicrmw max %i32_ptr, %i32 unordered : !llvm.i32
// CHECK: atomicrmw min i32* %{{.*}}, i32 %{{.*}} unordered
%10 = llvm.atomicrmw min %i32_ptr, %i32 unordered : !llvm.i32
// CHECK: atomicrmw umax i32* %{{.*}}, i32 %{{.*}} unordered
%11 = llvm.atomicrmw umax %i32_ptr, %i32 unordered : !llvm.i32
// CHECK: atomicrmw umin i32* %{{.*}}, i32 %{{.*}} unordered
%12 = llvm.atomicrmw umin %i32_ptr, %i32 unordered : !llvm.i32
llvm.return
}
// CHECK-LABEL: @cmpxchg
llvm.func @cmpxchg(%ptr : !llvm<"float*">, %cmp : !llvm.float, %val: !llvm.float) {
// CHECK: cmpxchg float* %{{.*}}, float %{{.*}}, float %{{.*}} acq_rel monotonic
%0 = llvm.cmpxchg %ptr, %cmp, %val acq_rel monotonic : !llvm.float
// CHECK: %{{[0-9]+}} = extractvalue { float, i1 } %{{[0-9]+}}, 0
%1 = llvm.extractvalue %0[0] : !llvm<"{ float, i1 }">
// CHECK: %{{[0-9]+}} = extractvalue { float, i1 } %{{[0-9]+}}, 1
%2 = llvm.extractvalue %0[1] : !llvm<"{ float, i1 }">
llvm.return
}
[MLIR] Added llvm.invoke and llvm.landingpad Summary: I have tried to implement `llvm.invoke` and `llvm.landingpad`. # `llvm.invoke` is similar to `llvm.call` with two successors added, the first one is the normal label and the second one is unwind label. # `llvm.launchpad` takes a variable number of args with either `catch` or `filter` associated with them. Catch clauses are not array types and filter clauses are array types. This is same as the criteria used by LLVM (https://github.com/llvm/llvm-project/blob/4f82af81a04d711721300f6ca32f402f2ea6faf4/llvm/include/llvm/IR/Instructions.h#L2866) Examples: LLVM IR ``` define i32 @caller(i32 %a) personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { invoke i32 @foo(i32 2) to label %success unwind label %fail success: ret i32 2 fail: landingpad {i8*, i32} catch i8** @_ZTIi catch i8** null catch i8* bitcast (i8** @_ZTIi to i8*) filter [1 x i8] [ i8 1 ] ret i32 3 } ``` MLIR LLVM Dialect ``` llvm.func @caller(%arg0: !llvm.i32) -> !llvm.i32 { %0 = llvm.mlir.constant(3 : i32) : !llvm.i32 %1 = llvm.mlir.constant("\01") : !llvm<"[1 x i8]"> %2 = llvm.mlir.addressof @_ZTIi : !llvm<"i8**"> %3 = llvm.bitcast %2 : !llvm<"i8**"> to !llvm<"i8*"> %4 = llvm.mlir.null : !llvm<"i8**"> %5 = llvm.mlir.addressof @_ZTIi : !llvm<"i8**"> %6 = llvm.mlir.constant(2 : i32) : !llvm.i32 %7 = llvm.invoke @foo(%6) to ^bb1 unwind ^bb2 : (!llvm.i32) -> !llvm.i32 ^bb1: // pred: ^bb0 llvm.return %6 : !llvm.i32 ^bb2: // pred: ^bb0 %8 = llvm.landingpad (catch %5 : !llvm<"i8**">) (catch %4 : !llvm<"i8**">) (catch %3 : !llvm<"i8*">) (filter %1 : !llvm<"[1 x i8]">) : !llvm<"{ i8*, i32 }"> llvm.return %0 : !llvm.i32 } ``` Signed-off-by: Shraiysh Vaishay <cs17btech11050@iith.ac.in> Differential Revision: https://reviews.llvm.org/D72006
2020-01-30 19:50:12 +08:00
llvm.mlir.global external constant @_ZTIi() : !llvm<"i8*">
llvm.func @foo(!llvm<"i8*">)
llvm.func @bar(!llvm<"i8*">) -> !llvm<"i8*">
llvm.func @__gxx_personality_v0(...) -> !llvm.i32
// CHECK-LABEL: @invokeLandingpad
llvm.func @invokeLandingpad() -> !llvm.i32 attributes { personality = @__gxx_personality_v0 } {
[MLIR] Added llvm.invoke and llvm.landingpad Summary: I have tried to implement `llvm.invoke` and `llvm.landingpad`. # `llvm.invoke` is similar to `llvm.call` with two successors added, the first one is the normal label and the second one is unwind label. # `llvm.launchpad` takes a variable number of args with either `catch` or `filter` associated with them. Catch clauses are not array types and filter clauses are array types. This is same as the criteria used by LLVM (https://github.com/llvm/llvm-project/blob/4f82af81a04d711721300f6ca32f402f2ea6faf4/llvm/include/llvm/IR/Instructions.h#L2866) Examples: LLVM IR ``` define i32 @caller(i32 %a) personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) { invoke i32 @foo(i32 2) to label %success unwind label %fail success: ret i32 2 fail: landingpad {i8*, i32} catch i8** @_ZTIi catch i8** null catch i8* bitcast (i8** @_ZTIi to i8*) filter [1 x i8] [ i8 1 ] ret i32 3 } ``` MLIR LLVM Dialect ``` llvm.func @caller(%arg0: !llvm.i32) -> !llvm.i32 { %0 = llvm.mlir.constant(3 : i32) : !llvm.i32 %1 = llvm.mlir.constant("\01") : !llvm<"[1 x i8]"> %2 = llvm.mlir.addressof @_ZTIi : !llvm<"i8**"> %3 = llvm.bitcast %2 : !llvm<"i8**"> to !llvm<"i8*"> %4 = llvm.mlir.null : !llvm<"i8**"> %5 = llvm.mlir.addressof @_ZTIi : !llvm<"i8**"> %6 = llvm.mlir.constant(2 : i32) : !llvm.i32 %7 = llvm.invoke @foo(%6) to ^bb1 unwind ^bb2 : (!llvm.i32) -> !llvm.i32 ^bb1: // pred: ^bb0 llvm.return %6 : !llvm.i32 ^bb2: // pred: ^bb0 %8 = llvm.landingpad (catch %5 : !llvm<"i8**">) (catch %4 : !llvm<"i8**">) (catch %3 : !llvm<"i8*">) (filter %1 : !llvm<"[1 x i8]">) : !llvm<"{ i8*, i32 }"> llvm.return %0 : !llvm.i32 } ``` Signed-off-by: Shraiysh Vaishay <cs17btech11050@iith.ac.in> Differential Revision: https://reviews.llvm.org/D72006
2020-01-30 19:50:12 +08:00
// CHECK: %[[a1:[0-9]+]] = alloca i8
%0 = llvm.mlir.constant(0 : i32) : !llvm.i32
%1 = llvm.mlir.constant("\01") : !llvm<"[1 x i8]">
%2 = llvm.mlir.addressof @_ZTIi : !llvm<"i8**">
%3 = llvm.bitcast %2 : !llvm<"i8**"> to !llvm<"i8*">
%4 = llvm.mlir.null : !llvm<"i8**">
%5 = llvm.mlir.constant(1 : i32) : !llvm.i32
%6 = llvm.alloca %5 x !llvm.i8 : (!llvm.i32) -> !llvm<"i8*">
// CHECK: invoke void @foo(i8* %[[a1]])
// CHECK-NEXT: to label %[[normal:[0-9]+]] unwind label %[[unwind:[0-9]+]]
llvm.invoke @foo(%6) to ^bb2 unwind ^bb1 : (!llvm<"i8*">) -> ()
// CHECK: [[unwind]]:
^bb1:
// CHECK: %{{[0-9]+}} = landingpad { i8*, i32 }
// CHECK-NEXT: catch i8** null
// CHECK-NEXT: catch i8* bitcast (i8** @_ZTIi to i8*)
// CHECK-NEXT: filter [1 x i8] c"\01"
%7 = llvm.landingpad (catch %4 : !llvm<"i8**">) (catch %3 : !llvm<"i8*">) (filter %1 : !llvm<"[1 x i8]">) : !llvm<"{ i8*, i32 }">
// CHECK: br label %[[final:[0-9]+]]
llvm.br ^bb3
// CHECK: [[normal]]:
// CHECK-NEXT: ret i32 1
^bb2: // 2 preds: ^bb0, ^bb3
llvm.return %5 : !llvm.i32
// CHECK: [[final]]:
// CHECK-NEXT: %{{[0-9]+}} = invoke i8* @bar(i8* %[[a1]])
// CHECK-NEXT: to label %[[normal]] unwind label %[[unwind]]
^bb3: // pred: ^bb1
%8 = llvm.invoke @bar(%6) to ^bb2 unwind ^bb1 : (!llvm<"i8*">) -> !llvm<"i8*">
}
// CHECK-LABEL: @callFreezeOp
llvm.func @callFreezeOp(%x : !llvm.i32) {
// CHECK: freeze i32 %{{[0-9]+}}
%0 = llvm.freeze %x : !llvm.i32
%1 = llvm.mlir.undef : !llvm.i32
// CHECK: freeze i32 undef
%2 = llvm.freeze %1 : !llvm.i32
llvm.return
}
// CHECK-LABEL: @boolConstArg
llvm.func @boolConstArg() -> !llvm.i1 {
// CHECK: ret i1 false
%0 = llvm.mlir.constant(true) : !llvm.i1
%1 = llvm.mlir.constant(false) : !llvm.i1
%2 = llvm.and %0, %1 : !llvm.i1
llvm.return %2 : !llvm.i1
}
// CHECK-LABEL: @callFenceInst
llvm.func @callFenceInst() {
// CHECK: fence syncscope("agent") release
llvm.fence syncscope("agent") release
// CHECK: fence release
llvm.fence release
// CHECK: fence release
llvm.fence syncscope("") release
llvm.return
}
// CHECK-LABEL: @passthrough
// CHECK: #[[ATTR_GROUP:[0-9]*]]
llvm.func @passthrough() attributes {passthrough = ["noinline", ["alignstack", "4"], "null-pointer-is-valid", ["foo", "bar"]]} {
llvm.return
}
// CHECK: attributes #[[ATTR_GROUP]] = {
// CHECK-DAG: noinline
// CHECK-DAG: alignstack=4
// CHECK-DAG: "null-pointer-is-valid"
// CHECK-DAG: "foo"="bar"