forked from OSchip/llvm-project
152 Commits
Author | SHA1 | Message | Date |
---|---|---|---|
Alex Zinenko | 90d1b6b5f2 |
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 |
|
Alex Zinenko | ac6bfa6780 |
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 |