Commit Graph

37 Commits

Author SHA1 Message Date
Alex Zinenko 054e25c079 EDSC: use affine.load/store instead of std.load/store
Standard load and store operations are evolving to be separated from the Affine
constructs.  Special affine.load/store have been introduced to uphold the
restrictions of the Affine control flow constructs on their operands.
EDSC-produced loads and stores were originally intended to uphold those
restrictions as well so they should use affine.load/store instead of
std.load/store.

PiperOrigin-RevId: 257443307
2019-07-12 08:42:28 -07:00
River Riddle e7d594bb1c Replace the implementation of Function and Module with FuncOp and ModuleOp.
This is an important step in allowing for the top-level of the IR to be extensible. FuncOp and ModuleOp contain all of the necessary functionality, while using the existing operation infrastructure. As an interim step, many of the usages of Function and Module, including the name, will remain the same. In the future, many of these will be relaxed to allow for many different types of top-level operations to co-exist.

PiperOrigin-RevId: 256427100
2019-07-03 14:37:18 -07:00
River Riddle 679a3b4191 Change the attribute dictionary syntax to separate name and value with '='.
The current syntax separates the name and value with ':', but ':' is already overloaded by several other things(e.g. trailing types). This makes the syntax difficult to parse in some situtations:

Old:
  "foo: 10 : i32"

New:
  "foo = 10 : i32"
PiperOrigin-RevId: 255097928
2019-06-25 19:06:34 -07:00
Nicolas Vasilache 6aa5cc8b06 Cleanup linalg integration test
This CL performs post-commit cleanups.
    It adds the ability to specify which shared libraries to load dynamically in ExecutionEngine. The linalg integration test is updated to use a shared library.
    Additional minor cleanups related to LLVM lowering of Linalg are also included.

--

PiperOrigin-RevId: 248346589
2019-05-20 13:43:13 -07:00
Nicolas Vasilache 6bdd13f107 Reorder edsc python tests - NFC
This CL orders the python tests to:
    1. allow introspecting on the EdscTest class and avoid the error-prone process of having to add the test call by hand;
    2. account for differences in the order of `dir(edscTest)` between python2, <= python3.5 and >= python 3.6

--

PiperOrigin-RevId: 247609687
2019-05-10 19:28:21 -07:00
Nicolas Vasilache b4c06416df Move edsc python tests to Filecheck
--

PiperOrigin-RevId: 247479507
2019-05-10 19:26:27 -07:00
Alex Zinenko d3380a504f Change syntax of regions in the generic form of operations
The generic form of operations currently supports optional regions to be
    located after the operation type.  As we are going to add a type to each
    region in a leading position in the region syntax, similarly to functions, it
    becomes ambiguous to have regions immediately after the operation type.  Put
    regions between operands the optional list of successors in the generic
    operation syntax and wrap them in parentheses.  The effect on the exisitng IR
    syntax is minimal since only three operations (`affine.for`, `affine.if` and
    `gpu.kernel`) currently use regions.

--

PiperOrigin-RevId: 246787087
2019-05-06 08:29:48 -07:00
Alex Zinenko 7a30ac97c8 Python bindings: drop MLIREmitter and related functionality
This completes the transition of Python bindings to use the declarative
    builders infrastructure instead of the now-deprecated EDSC emitter
    infrastructure.  The relevant unit tests have been replicated using the new
    functionality and the remaining end-to-end compilation tests have been updated
    accordingly.  The latter show an improvement in brevity and readability.

--

PiperOrigin-RevId: 241713489
2019-04-03 08:30:24 -07:00
Alex Zinenko 509619829d Python bindings: support __floordiv__ for index types
The original reimplementation of EDSC as declarative builders and the
    subsequent rework of Python bindings forbade to use the (true) division
    operator for values of the index types without providing an alternative.  Index
    types only support floor and ceil division through affine maps.  Expose this to
    Python bindings through a `__floordiv__` function on `ValueHandle`s.

--

PiperOrigin-RevId: 241713093
2019-04-03 08:30:07 -07:00
Jacques Pienaar d5259edefd Update header notices.
PiperOrigin-RevId: 240457737
2019-03-29 17:43:20 -07:00
River Riddle 832567b379 NFC: Rename the 'for' operation in the AffineOps dialect to 'affine.for' and set the namespace of the AffineOps dialect to 'affine'.
PiperOrigin-RevId: 240165792
2019-03-29 17:39:03 -07:00
Alex Zinenko 80e38b6204 Python bindings: expose boolean and comparison operators
In particular, expose comparison operators as Python operator overloads on
ValueHandles.  The comparison currently emits signed integer comparisons only,
which is compatible with the behavior of emitter-based EDSC interface.  This is
sub-optimal and must be reconsidered in the future.  Note that comparison
operators are not overloaded in the C++ declarative builder API precisely
because this avoids the premature decision on the signedness of comparisons.

Implement the declarative construction of boolean expressions using
ValueHandles by overloading the boolean operators in the `op` namespace to
differentiate between `operator!` for nullity check and for boolean negation.
The operands must be of i1 type.  Also expose boolean operations as Python
operator overloads on ValueHandles.

PiperOrigin-RevId: 238421615
2019-03-29 17:17:47 -07:00
Alex Zinenko e904ddf315 Python bindings: expose various Ops through declarative builders
In particular, expose `cond_br`, `select` and `call` operations with syntax
similar to that of the previous emitter-based EDSC interface.  These are
provided for backwards-compatibility.  Ideally, we want them to be
Table-generated from the Op definitions when those definitions are declarative.

Additionally, expose the ability to construct any op given its canonical name,
which also exercises the construction of unregistered ops.

PiperOrigin-RevId: 238421583
2019-03-29 17:17:27 -07:00
Alex Zinenko 269d9bf54e Python bindings: expose IndexedValue
Expose edsc::IndexedValue using a syntax smilar to that of edsc::Indexed to
ensure backwards-compatibility.  It remains possible to write array-indexed
loads and stores as

    C.store([i, j], A.load([i, k]) * B.load([k, j]))

after taking a "view" of some value handle using IndexedValue as

    A = IndexedValue(originalValueHandle)

provided that all indices are also value handles.

PiperOrigin-RevId: 238421544
2019-03-29 17:17:12 -07:00
Alex Zinenko 48d0d1f172 Python bindings: use MLIR operations to define constant values
In the original implementation, constants could be bound to EDSC expressions in
the binder, independently from other MLIR Values.  A rework of EDSC including
early typing provided the functionality to use MLIR's `constant` operation to
define typed constants instead of binding them separately, but only used it for
index types.  The new declarative builder implementation followed by providing
a call for building `constant` operations of index types but nothing more.
Expose similar builders for integers, floats and functions to match the what
binders allow one to use.

PiperOrigin-RevId: 238421508
2019-03-29 17:16:57 -07:00
Alex Zinenko d940c52183 Python bindings: make FunctionContext behave more like BlockContext
Provide a function `arg` that returns the function argument as a value handle,
similar to block arguments.  This makes function context managers in Python
similar to block context managers, which is more consistent given that the
function context manager sets the insertion point to the first block of the
function and that arguments of that block are those of the function.  This
prepares the removal of PythonMLIREmitter class and its bind_function_arguments
helper.

Additionally, provide a helper method in PythonMLIRModule to define a function
and immediately create a context for it.  Update the tests that are already
using context managers to use the function context manager instead of creating
the function manually.

PiperOrigin-RevId: 238421087
2019-03-29 17:16:42 -07:00
Alex Zinenko 9abea4a466 Python bindings: provide context managers for the Blocks
Expose EDSC block builders as Python context managers, similarly to loop
builders.  Note that blocks, unlike loops, are addressable and may need to be
"declared" without necessarily filling their bodies with instructions.  This is
the case, for example, when branching to a new block from the existing block.
Therefore, creating the block context manager immediately creates the block
(unless the manager captures an existing block) by creating and destroying the
block builder.  With this approach, one can either fill in the block and refer
to it later leveraging Python's dynamic variable lookup

    with BlockContext([indexType]) as b:
      op(...)  # operation inside the block
      ret()
    op(...)  # operation outside the block (in the function entry block)
    br(b, [...])    # branching to the block created above

or declare the block contexts upfront and enter them on demand

    bb1 = BlockContext()  # empty block created in the surrounding function
    bb2 = BlockContext()  # context
    cond_br(bb1.handle, [], bb2.handle, [])  # branch to blocks from here
    with bb1:
      op(...)  # operation inside the first block
    with bb2:
      op(...)  # operation inside the second block
    with bb1:
      op(...)  # append operation to the first block

Additionally, one can create multiple throw-away contexts that append to the
same block

    with BlockContext() as b:
      op(...)  # operation inside the block
    with BlockContext(appendTo(b)):
      op(...)  # new context appends to the block

which has a potential of being extended to control the insertion point of the
block at a finer level of granularity.

PiperOrigin-RevId: 238005298
2019-03-29 17:13:57 -07:00
Alex Zinenko 8b4b9b31f1 Python bindings: introduce loop and loop nest contexts
Recently, EDSC introduced an eager mode for building IR in different contexts.
Introduce Python bindings support for loop and loop nest contexts of EDSC
builders.  The eager mode is built around the notion of ValueHandle, which is
convenience class for delayed initialization and operator overloads.  Expose
this class and overloads directly.  The model of insertion contexts maps
naturally to Python context manager mechanism, therefore new bindings are
defined bypassing the C APIs.  The bindings now provide three new context
manager classes: FunctionContext, LoopContext and LoopNestContext.  The last
two can be used with the `with`-construct in Python to create loop (nests) and
obtain handles to the loop induction variables seamlessly:

    with LoopContext(lhs, rhs, 1) as i:
      lhs + rhs + i
      with LoopContext(rhs, rhs + rhs, 2) as j:
        x = i + j

Any statement within the Python context will trigger immediate emission of the
corresponding IR constructs into the context owned by the nearest context
manager.

PiperOrigin-RevId: 237447732
2019-03-29 17:06:36 -07:00
Alex Zinenko 76759395f2 Python bindinds: support functions with attributes and argument attributes
Currently, Python bindings provide support for declarting and defining MLIR
functions given a list of argument and result types.  Extend the support for
both function declaration and function definition to handle optional function
attributes and function argument attributes.  Function attributes are exposed
as keyword arguments on function declaration and definition calls.  Function
argument attributes are exposed through a special object that combines the
argument type and its list of attributes.  Such objects can be passed instead
of bare types into the type declaration and definition calls.  They can be
constructed from bare types and reused in different declarations.

Note that, from the beginning, Python bindings did not pass through C bindings
to declare and define functions.  This commit keeps the direct interaction
between Python and C++.

PiperOrigin-RevId: 237047561
2019-03-29 17:00:41 -07:00
River Riddle eeeef090ef Set the namespace of the StandardOps dialect to "std", but add a special case to the parser to allow parsing standard operations without the "std" prefix. This will now allow for the standard dialect to be looked up dynamically by name.
PiperOrigin-RevId: 236493865
2019-03-29 16:54:20 -07:00
Alex Zinenko 4bd5d28391 EDSC bindings: expose generic Op construction interface
EDSC Expressions can now be used to build arbitrary MLIR operations identified
by their canonical name, i.e. the name obtained from
`OpClass::getOperationName()` for registered operations.  Expose this
functionality to the C API and Python bindings.  This exposes builder-level
interface to Python and avoids the need for experimental Python code to
implement EDSC free function calls for constructing each op type.

This modification required exposing mlir::Attribute to the C API and Python
bindings, which only supports integer attributes for now.

This is step 4/n to making EDSCs more generalizable.

PiperOrigin-RevId: 236306776
2019-03-29 16:51:32 -07:00
Alex Zinenko e7193a70f8 EDSC: support conditional branch instructions
Leverage the recently introduced support for multiple argument groups and
multiple destination blocks in EDSC Expressions to implement conditional
branches in EDSC.  Conditional branches have two successors and three argument
groups.  The first group contains a single expression of i1 type that
corresponds to the condition of the branch.  The two following groups contain
arguments of the two successors of the conditional branch instruction, in the
same order as the successors.  Expose this instruction to the C API and Python
bindings.

PiperOrigin-RevId: 235542768
2019-03-29 16:41:05 -07:00
Alex Zinenko 83e8db2193 EDSC: support branch instructions
The new implementation of blocks was designed to support blocks with arguments.
More specifically, StmtBlock can be constructed with a list of Bindables that
will be bound to block aguments upon construction.  Leverage this functionality
to implement branch instructions with arguments.

This additionally requires the statement storage to have a list of successors,
similarly to core IR operations.

Becauase successor chains can form loops, we need a possibility to decouple
block declaration, after which it becomes usable by branch instructions, from
block body definition.  This is achieved by creating an empty block and by
resetting its body with a new list of instructions.  Note that assigning a
block from another block will not affect any instructions that may have
designated this block as their successor (this behavior is necessary to make
value-type semantics of EDSC types consistent).  Combined, one can now write
generators like

    EDSCContext context;
    Type indexType = ...;
    Bindable i(indexType), ii(indexType), zero(indexType), one(indexType);
    StmtBlock loopBlock({i}, {});
    loopBlock.set({ii = i + one,
                   Branch(loopBlock, {ii})});
    MLIREmitter(&builder)
        .bindConstant<ConstantIndexOp>(zero, 0)
        .bindConstant<ConstantIndexOp>(one, 1)
	.emitStmt(Branch(loopBlock, {zero}));

where the emitter will emit the statement and its successors, if present.

PiperOrigin-RevId: 235541892
2019-03-29 16:40:50 -07:00
Brian Patton d52e631359 Add a test example of calling a builtin function.
PiperOrigin-RevId: 235149430
2019-03-29 16:37:46 -07:00
Sergei Lebedev 1cc9305c71 Exposed division and remainder operations in EDSC
This change introduces three new operators in EDSC: Div (also exposed
via Expr.__div__ aka /) -- floating-point division, FloorDiv and CeilDiv
for flooring/ceiling index division.

The lowering to LLVM will be implemented in b/124872679.

PiperOrigin-RevId: 234963217
2019-03-29 16:36:41 -07:00
Alex Zinenko 59a209721e EDSC: support call instructions
Introduce support for binding MLIR functions as constant expressions.  Standard
constant operation supports functions as possible constant values.

Provide C APIs to look up existing named functions in an MLIR module and expose
them to the Python bindings.  Provide Python bindings to declare a function in
an MLIR module without defining it and to add a definition given a function
declaration.  These declarations are useful when attempting to link MLIR
modules with, e.g., the standard library.

Introduce EDSC support for direct and indirect calls to other MLIR functions.
Internally, an indirect call is always emitted to leverage existing support for
delayed construction of MLIR Values using EDSC Exprs.  If the expression is
bound to a constant function (looked up or declared beforehand), MLIR constant
folding will be able to replace an indirect call by a direct call.  Currently,
only zero- and one-result functions are supported since we don't have support
for multi-valued expressions in EDSC.

Expose function calling interface to Python bindings on expressions by defining
a `__call__` function accepting a variable number of arguments.

PiperOrigin-RevId: 234959444
2019-03-29 16:36:26 -07:00
Alex Zinenko 21bd4540f3 EDSC: introduce min/max only usable inside for upper/lower bounds of a loop
Introduce a type-safe way of building a 'for' loop with max/min bounds in EDSC.
Define new types MaxExpr and MinExpr in C++ EDSC API and expose them to Python
bindings.  Use values of these type to construct 'for' loops with max/min in
newly introduced overloads of the `edsc::For` factory function.  Note that in C
APIs, we still must expose MaxMinFor as a different function because C has no
overloads.  Also note that MaxExpr and MinExpr do _not_ derive from Expr
because they are not allowed to be used in a regular Expr context (which may
produce `affine.apply` instructions not expecting `min` or `max`).

Factory functions `Min` and `Max` in Python can be further overloaded to
produce chains of comparisons and selects on non-index types.  This is not
trivial in C++ since overloaded functions cannot differ by the return type only
(`MaxExpr` or `Expr`) and making `MaxExpr` derive from `Expr` defies the
purpose of type-safe construction.

PiperOrigin-RevId: 234786131
2019-03-29 16:34:11 -07:00
Alex Zinenko d055a4e100 EDSC: support multi-expression loop bounds
MLIR supports 'for' loops with lower(upper) bound defined by taking a
maximum(minimum) of a list of expressions, but does not have first-class affine
constructs for the maximum(minimum).  All these expressions must have affine
provenance, similarly to a single-expression bound.  Add support for
constructing such loops using EDSC.  The expression factory function is called
`edsc::MaxMinFor` to (1) highlight that the maximum(minimum) operation is
applied to the lower(upper) bound expressions and (2) differentiate it from a
`edsc::For` that creates multiple perfectly nested loops (and should arguably
be called `edsc::ForNest`).

PiperOrigin-RevId: 234785996
2019-03-29 16:33:56 -07:00
Alex Zinenko a2a433652d EDSC: create constants as expressions
Introduce a functionality to create EDSC expressions from typed constants.
This complements the current functionality that uses "unbound" expressions and
binds them to a specific constant before emission.  It comes in handy in cases
where we want to check if something is a constant early during construciton
rather than late during emission, for example multiplications and divisions in
affine expressions.  This is also consistent with MLIR vision of constants
being defined by an operation (rather than being special kinds of values in the
IR) by exposing this operation as EDSC expression.

PiperOrigin-RevId: 234758020
2019-03-29 16:33:41 -07:00
Alex Zinenko b4dba895a6 EDSC: make Expr typed and extensible
Expose the result types of edsc::Expr, which are now stored for all types of
Exprs and not only for the variadic ones.  Require return types when an Expr is
constructed, if it will ever have some.  An empty return type list is
interpreted as an Expr that does not create a value (e.g. `return` or `store`).

Conceptually, all edss::Exprs are now typed, with the type being a (potentially
empty) tuple of return types.  Unbound expressions and Bindables must now be
constructed with a specific type they will take.  This makes EDSC less
evidently type-polymorphic, but we can still write generic code such as

    Expr sumOfSquares(Expr lhs, Expr rhs) { return lhs * lhs + rhs * rhs; }

and use it to construct different typed expressions as

    sumOfSquares(Bindable(IndexType::get(ctx)), Bindable(IndexType::get(ctx)));
    sumOfSquares(Bindable(FloatType::getF32(ctx)),
                 Bindable(FloatType::getF32(ctx)));

On the positive side, we get the following.
1. We can now perform type checking when constructing Exprs rather than during
   MLIR emission.  Nevertheless, this is still duplicates the Op::verify()
   until we can factor out type checking from that.
2. MLIREmitter is significantly simplified.
3. ExprKind enum is only used for actual kinds of expressions.  Data structures
   are converging with AbstractOperation, and the users can now create a
   VariadicExpr("canonical_op_name", {types}, {exprs}) for any operation, even
   an unregistered one without having to extend the enum and make pervasive
   changes to EDSCs.

On the negative side, we get the following.
1. Typed bindables are more verbose, even in Python.
2. We lose the ability to do print debugging for higher-level EDSC abstractions
   that are implemented as multiple MLIR Ops, for example logical disjunction.

This is the step 2/n towards making EDSC extensible.

***

Move MLIR Op construction from MLIREmitter::emitExpr to Expr::build since Expr
now has sufficient information to build itself.

This is the step 3/n towards making EDSC extensible.

Both of these strive to minimize the amount of irrelevant changes.  In
particular, this introduces more complex pretty-printing for affine and binary
expression to make sure tests continue to pass.  It also relies on string
comparison to identify specific operations that an Expr produces.

PiperOrigin-RevId: 234609882
2019-03-29 16:31:26 -07:00
Alex Zinenko 0a4c940c1b EDSC: introduce support for blocks
EDSC currently implement a block as a statement that is itself a list of
statements.  This suffers from two modeling problems: (1) these blocks are not
addressable, i.e. one cannot create an instruction where thus constructed block
is a successor; (2) they support block nesting, which is not supported by MLIR
blocks.  Furthermore, emitting such "compound statement" (misleadingly named
`Block` in Python bindings) does not actually produce a new Block in the IR.

Implement support for creating actual IR Blocks in EDSC.  In particular, define
a new StmtBlock EDSC class that is neither an Expr nor a Stmt but contains a
list of Stmts.  Additionally, StmtBlock may have (early-) typed arguments.
These arguments are Bindable expressions that can be used inside the block.
Provide two calls in the MLIREmitter, `emitBlock` that actually emits a new
block and `emitBlockBody` that only emits the instructions contained in the
block without creating a new block.  In the latter case, the instructions must
not use block arguments.

Update Python bindings to make it clear when instruction emission happens
without creating a new block.

PiperOrigin-RevId: 234556474
2019-03-29 16:30:56 -07:00
Alex Zinenko ecd403c0e8 EDSC: properly construct FunctionTypes
The existing implementation of makeFunctionType in EDSC contains a bug: the
array of input types is overwritten using output types passed as arguments and
the array of output types is never filled in.  This leads to all sorts of
incorrect memory behavior.  Fill in the array of output types using the proper
argument.

PiperOrigin-RevId: 234177221
2019-03-29 16:29:56 -07:00
Sergei Lebedev d8e5ce0107 Implemented __invert__, __and__ and __or__ in the EDSC Python bindings
This allows to use bitwise operators as logical (accounting for differences
in precedence).

PiperOrigin-RevId: 232489024
2019-03-29 16:14:20 -07:00
Sergei Lebedev 52ec65c85e Implemented __eq__ and __ne__ in EDSC Python bindings
PiperOrigin-RevId: 232473201
2019-03-29 16:13:34 -07:00
Dimitrios Vytiniotis 9ca0691b06 Exposing logical operators in EDSC all the way up to Python.
PiperOrigin-RevId: 232299839
2019-03-29 16:10:08 -07:00
Nicolas Vasilache 0353ef99eb Cleanup EDSCs and start a functional auto-generated library of custom Ops
This CL applies the following simplifications to EDSCs:
1. Rename Block to StmtList because an MLIR Block is a different, not yet
supported, notion;
2. Rework Bindable to drop specific storage and just use it as a simple wrapper
around Expr. The only value of Bindable is to force a static cast when used by
the user to bind into the emitter. For all intended purposes, Bindable is just
a lightweight check that an Expr is Unbound. This simplifies usage and reduces
the API footprint. After playing with it for some time, it wasn't worth the API
cognition overhead;
3. Replace makeExprs and makeBindables by makeNewExprs and copyExprs which is
more explicit and less easy to misuse;
4. Add generally useful functionality to MLIREmitter:
  a. expose zero and one for the ubiquitous common lower bounds and step;
  b. add support to create already bound Exprs for all function arguments as
  well as shapes and views for Exprs bound to memrefs.
5. Delete Stmt::operator= and replace by a `Stmt::set` method which is more
explicit.
6. Make Stmt::operator Expr() explicit.
7. Indexed.indices assertions are removed to pave the way for expressing slices
and views as well as to work with 0-D memrefs.

The CL plugs those simplifications with TableGen and allows emitting a full MLIR function for
pointwise add.

This "x.add" op is both type and rank-agnostic (by allowing ArrayRef of Expr
passed to For loops) and opens the door to spinning up a composable library of
existing and custom ops that should automate a lot of the tedious work in
TF/XLA -> MLIR.

Testing needs to be significantly improved but can be done in a separate CL.

PiperOrigin-RevId: 231982325
2019-03-29 16:05:23 -07:00
Nicolas Vasilache 39d81f246a Introduce python bindings for MLIR EDSCs
This CL also introduces a set of python bindings using pybind11. The bindings
are exercised using a `test_py2andpy3.py` test suite that works for both
python 2 and 3.

`test_py3.py` on the other hand uses the more idiomatic,
python 3 only "PEP 3132 -- Extended Iterable Unpacking" to implement a rank
and type-agnostic copy with transposition.

Because python assignment is by reference, we cannot easily make the
assignment operator use the same type of sugaring as in C++; i.e. the
following:

```cpp
Stmt block = edsc::Block({
  For(ivs, zeros, shapeA, ones, {
    C[ivs] = IA[ivs] + IB[ivs]
})});
```

has no equivalent in the native Python EDSCs at this time.

However, the sugaring can be built as a simple DSL in python and is left as
future work.

PiperOrigin-RevId: 231337667
2019-03-29 15:59:14 -07:00