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
This commit is contained in:
River Riddle 2019-07-03 13:21:24 -07:00 committed by jpienaar
parent 2e1187dd25
commit e7d594bb1c
56 changed files with 757 additions and 1545 deletions

View File

@ -43,9 +43,9 @@ class EdscTest:
b.arg(0) + b.arg(1)
printWithCurrentFunctionName(str(fun))
# CHECK-LABEL: testBlockArguments
# CHECK: %c42 = constant 42 : index
# CHECK: ^bb1(%0: f32, %1: f32):
# CHECK: %2 = addf %0, %1 : f32
# CHECK: %{{.*}} = constant 42 : index
# CHECK: ^bb{{.*}}(%{{.*}}: f32, %{{.*}}: f32):
# CHECK: %{{.*}} = addf %{{.*}}, %{{.*}} : f32
def testBlockContext(self):
self.setUp()
@ -55,9 +55,9 @@ class EdscTest:
cst + cst
printWithCurrentFunctionName(str(fun))
# CHECK-LABEL: testBlockContext
# CHECK: %c42 = constant 42 : index
# CHECK: ^bb1:
# CHECK: %0 = "affine.apply"() {map = () -> (84)} : () -> index
# CHECK: %{{.*}} = constant 42 : index
# CHECK: ^bb
# CHECK: %{{.*}} = "affine.apply"() {map = () -> (84)} : () -> index
def testBlockContextAppend(self):
self.setUp()
@ -71,11 +71,11 @@ class EdscTest:
E.constant_index(1)
printWithCurrentFunctionName(str(fun))
# CHECK-LABEL: testBlockContextAppend
# CHECK: %c41 = constant 41 : index
# CHECK: %c42 = constant 42 : index
# CHECK: ^bb1:
# CHECK: %c0 = constant 0 : index
# CHECK: %c1 = constant 1 : index
# CHECK: %{{.*}} = constant 41 : index
# CHECK: %{{.*}} = constant 42 : index
# CHECK: ^bb
# CHECK: %{{.*}} = constant 0 : index
# CHECK: %{{.*}} = constant 1 : index
def testBlockContextStandalone(self):
self.setUp()
@ -93,14 +93,14 @@ class EdscTest:
E.constant_index(42)
printWithCurrentFunctionName(str(fun))
# CHECK-LABEL: testBlockContextStandalone
# CHECK: %c41 = constant 41 : index
# CHECK: %c42 = constant 42 : index
# CHECK: ^bb1:
# CHECK: %c0 = constant 0 : index
# CHECK: %c1 = constant 1 : index
# CHECK: ^bb2:
# CHECK: %c56 = constant 56 : index
# CHECK: %c57 = constant 57 : index
# CHECK: %{{.*}} = constant 41 : index
# CHECK: %{{.*}} = constant 42 : index
# CHECK: ^bb
# CHECK: %{{.*}} = constant 0 : index
# CHECK: %{{.*}} = constant 1 : index
# CHECK: ^bb
# CHECK: %{{.*}} = constant 56 : index
# CHECK: %{{.*}} = constant 57 : index
def testBooleanOps(self):
self.setUp()
@ -111,19 +111,19 @@ class EdscTest:
stmt2 = ~(stmt1 | (k == l))
printWithCurrentFunctionName(str(fun))
# CHECK-LABEL: testBooleanOps
# CHECK: %0 = cmpi "slt", %arg0, %arg1 : i1
# CHECK: %1 = cmpi "sge", %arg1, %arg2 : i1
# CHECK: %2 = muli %0, %1 : i1
# CHECK: %3 = cmpi "eq", %arg2, %arg3 : i1
# CHECK: %true = constant 1 : i1
# CHECK: %4 = subi %true, %2 : i1
# CHECK: %true_0 = constant 1 : i1
# CHECK: %5 = subi %true_0, %3 : i1
# CHECK: %6 = muli %4, %5 : i1
# CHECK: %true_1 = constant 1 : i1
# CHECK: %7 = subi %true_1, %6 : i1
# CHECK: %true_2 = constant 1 : i1
# CHECK: %8 = subi %true_2, %7 : i1
# CHECK: %{{.*}} = cmpi "slt", %{{.*}}, %{{.*}} : i1
# CHECK: %{{.*}} = cmpi "sge", %{{.*}}, %{{.*}} : i1
# CHECK: %{{.*}} = muli %{{.*}}, %{{.*}} : i1
# CHECK: %{{.*}} = cmpi "eq", %{{.*}}, %{{.*}} : i1
# CHECK: %{{.*}} = constant 1 : i1
# CHECK: %{{.*}} = subi %{{.*}}, %{{.*}} : i1
# CHECK: %{{.*}} = constant 1 : i1
# CHECK: %{{.*}} = subi %{{.*}}, %{{.*}} : i1
# CHECK: %{{.*}} = muli %{{.*}}, %{{.*}} : i1
# CHECK: %{{.*}} = constant 1 : i1
# CHECK: %{{.*}} = subi %{{.*}}, %{{.*}} : i1
# CHECK: %{{.*}} = constant 1 : i1
# CHECK: %{{.*}} = subi %{{.*}}, %{{.*}} : i1
def testBr(self):
self.setUp()
@ -134,8 +134,8 @@ class EdscTest:
E.br(blk)
printWithCurrentFunctionName(str(fun))
# CHECK-LABEL: testBr
# CHECK: br ^bb1
# CHECK: ^bb1:
# CHECK: br ^bb
# CHECK: ^bb
# CHECK: return
def testBrArgs(self):
@ -147,11 +147,11 @@ class EdscTest:
E.br(b, [E.constant_index(0), E.constant_index(1)])
printWithCurrentFunctionName(str(fun))
# CHECK-LABEL: testBrArgs
# CHECK: %c0 = constant 0 : index
# CHECK: %c1 = constant 1 : index
# CHECK: br ^bb1(%c0, %c1 : index, index)
# CHECK: ^bb1(%0: index, %1: index):
# CHECK: br ^bb1(%1, %0 : index, index)
# CHECK: %{{.*}} = constant 0 : index
# CHECK: %{{.*}} = constant 1 : index
# CHECK: br ^bb{{.*}}(%{{.*}}, %{{.*}} : index, index)
# CHECK: ^bb{{.*}}(%{{.*}}: index, %{{.*}}: index):
# CHECK: br ^bb{{.*}}(%{{.*}}, %{{.*}} : index, index)
def testBrDeclaration(self):
self.setUp()
@ -162,8 +162,8 @@ class EdscTest:
E.ret()
printWithCurrentFunctionName(str(fun))
# CHECK-LABEL: testBrDeclaration
# CHECK: br ^bb1
# CHECK: ^bb1:
# CHECK: br ^bb
# CHECK: ^bb
# CHECK: return
def testCallOp(self):
@ -176,8 +176,8 @@ class EdscTest:
printWithCurrentFunctionName(str(self.module))
# CHECK-LABEL: testCallOp
# CHECK: func @sqrtf(f32) -> f32
# CHECK: %f = constant @sqrtf : (f32) -> f32
# CHECK: %0 = call_indirect %f(%arg0) : (f32) -> f32
# CHECK: %{{.*}} = constant @sqrtf : (f32) -> f32
# CHECK: %{{.*}} = call_indirect %{{.*}}(%{{.*}}) : (f32) -> f32
def testCondBr(self):
self.setUp()
@ -190,7 +190,7 @@ class EdscTest:
E.cond_br(fun.arg(0), blk1, [], blk2, [cst])
printWithCurrentFunctionName(str(fun))
# CHECK-LABEL: testCondBr
# CHECK: cond_br %arg0, ^bb1, ^bb2(%c0 : index)
# CHECK: cond_br %{{.*}}, ^bb{{.*}}, ^bb{{.*}}(%{{.*}} : index)
def testConstants(self):
self.setUp()
@ -226,8 +226,8 @@ class EdscTest:
E.op("foo", [fun.arg(0)], [self.f32Type]) + fun.arg(1)
printWithCurrentFunctionName(str(fun))
# CHECK-LABEL: testCustom
# CHECK: %0 = "foo"(%arg0) : (index) -> f32
# CHECK: %1 = addf %0, %arg1 : f32
# CHECK: %{{.*}} = "foo"(%{{.*}}) : (index) -> f32
# CHECK: %{{.*}} = addf %{{.*}}, %{{.*}} : f32
# Create 'addi' using the generic Op interface. We need an operation known
# to the execution engine so that the engine can compile it.
@ -255,7 +255,7 @@ class EdscTest:
printWithCurrentFunctionName(str(self.module))
# CHECK-LABEL: testDivisions
# CHECK: floordiv 42
# CHECK: divis %arg1, %arg2 : i32
# CHECK: divis %{{.*}}, %{{.*}} : i32
def testFunctionArgs(self):
self.setUp()
@ -264,7 +264,7 @@ class EdscTest:
pass
printWithCurrentFunctionName(str(fun))
# CHECK-LABEL: testFunctionArgs
# CHECK: func @foo(%arg0: f32, %arg1: f32) -> index
# CHECK: func @foo(%{{.*}}: f32, %{{.*}}: f32) -> index
def testFunctionContext(self):
self.setUp()
@ -295,7 +295,7 @@ class EdscTest:
# CHECK-LABEL: testFunctionMultiple
# CHECK: func @foo()
# CHECK: func @foo_0()
# CHECK: %c0 = constant 0 : index
# CHECK: %{{.*}} = constant 0 : index
def testIndexedValue(self):
self.setUp()
@ -313,9 +313,9 @@ class EdscTest:
# CHECK-LABEL: testIndexedValue
# CHECK: "affine.for"()
# CHECK: "affine.for"()
# CHECK: %0 = load %arg0[%i0, %i1] : memref<10x42xf32>
# CHECK: %1 = addf %0, %cst : f32
# CHECK: store %1, %arg0[%i0, %i1] : memref<10x42xf32>
# CHECK: %{{.*}} = load %{{.*}}[%{{.*}}, %{{.*}}] : memref<10x42xf32>
# CHECK: %{{.*}} = addf %{{.*}}, %{{.*}} : f32
# CHECK: store %{{.*}}, %{{.*}}[%{{.*}}, %{{.*}}] : memref<10x42xf32>
# CHECK: {lower_bound = () -> (0), step = 1 : index, upper_bound = () -> (42)}
# CHECK: {lower_bound = () -> (0), step = 1 : index, upper_bound = () -> (10)}
@ -331,10 +331,10 @@ class EdscTest:
printWithCurrentFunctionName(str(fun))
# CHECK-LABEL: testLoopContext
# CHECK: "affine.for"() (
# CHECK: ^bb1(%i0: index):
# CHECK: "affine.for"(%c42, %2) (
# CHECK: ^bb2(%i1: index):
# CHECK: "affine.apply"(%i0, %i1) {map = (d0, d1) -> (d0 + d1)} : (index, index) -> index
# CHECK: ^bb{{.*}}(%{{.*}}: index):
# CHECK: "affine.for"(%{{.*}}, %{{.*}}) (
# CHECK: ^bb{{.*}}(%{{.*}}: index):
# CHECK: "affine.apply"(%{{.*}}, %{{.*}}) {map = (d0, d1) -> (d0 + d1)} : (index, index) -> index
# CHECK: {lower_bound = (d0) -> (d0), step = 2 : index, upper_bound = (d0) -> (d0)} : (index, index) -> ()
# CHECK: {lower_bound = () -> (0), step = 1 : index, upper_bound = () -> (42)}
@ -348,14 +348,14 @@ class EdscTest:
printWithCurrentFunctionName(str(fun))
# CHECK-LABEL: testLoopNestContext
# CHECK: "affine.for"() (
# CHECK: ^bb1(%i0: index):
# CHECK: ^bb{{.*}}(%{{.*}}: index):
# CHECK: "affine.for"() (
# CHECK: ^bb2(%i1: index):
# CHECK: ^bb{{.*}}(%{{.*}}: index):
# CHECK: "affine.for"() (
# CHECK: ^bb3(%i2: index):
# CHECK: ^bb{{.*}}(%{{.*}}: index):
# CHECK: "affine.for"() (
# CHECK: ^bb4(%i3: index):
# CHECK: %2 = "affine.apply"(%i0, %i1, %i2, %i3) {map = (d0, d1, d2, d3) -> (d0 + d1 + d2 + d3)} : (index, index, index, index) -> index
# CHECK: ^bb{{.*}}(%{{.*}}: index):
# CHECK: %{{.*}} = "affine.apply"(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}) {map = (d0, d1, d2, d3) -> (d0 + d1 + d2 + d3)} : (index, index, index, index) -> index
def testMLIRBooleanCompilation(self):
self.setUp()
@ -388,8 +388,8 @@ class EdscTest:
# CHECK-LABEL: testMLIRFunctionCreation
# CHECK: f32
# CHECK: memref<3x4x?x5xf32>
# CHECK: func @copy(%arg0: memref<3x4x?x5xf32>, %arg1: memref<3x4x?x5xf32>) {
# CHECK: func @sqrtf(%arg0: f32) -> f32
# CHECK: func @copy(%{{.*}}: memref<3x4x?x5xf32>, %{{.*}}: memref<3x4x?x5xf32>) {
# CHECK: func @sqrtf(%{{.*}}: f32) -> f32
def testMLIRScalarTypes(self):
self.setUp()
@ -433,10 +433,10 @@ class EdscTest:
# CHECK: "affine.for"()
# CHECK: "affine.for"()
# CHECK: "affine.for"()
# CHECK-DAG: %0 = load %arg0[%i0, %i2] : memref<32x32xf32>
# CHECK-DAG: %1 = load %arg1[%i2, %i1] : memref<32x32xf32>
# CHECK: %2 = mulf %0, %1 : f32
# CHECK: store %2, %arg2[%i0, %i1] : memref<32x32xf32>
# CHECK-DAG: %{{.*}} = load %{{.*}}[%{{.*}}, %{{.*}}] : memref<32x32xf32>
# CHECK-DAG: %{{.*}} = load %{{.*}}[%{{.*}}, %{{.*}}] : memref<32x32xf32>
# CHECK: %{{.*}} = mulf %{{.*}}, %{{.*}} : f32
# CHECK: store %{{.*}}, %{{.*}}[%{{.*}}, %{{.*}}] : memref<32x32xf32>
# CHECK: {lower_bound = () -> (0), step = 1 : index, upper_bound = () -> (32)} : () -> ()
# CHECK: {lower_bound = () -> (0), step = 1 : index, upper_bound = () -> (32)} : () -> ()
# CHECK: {lower_bound = () -> (0), step = 1 : index, upper_bound = () -> (32)} : () -> ()
@ -450,9 +450,9 @@ class EdscTest:
E.ret([c42, c0])
printWithCurrentFunctionName(str(fun))
# CHECK-LABEL: testRet
# CHECK: %c42 = constant 42 : index
# CHECK: %c0 = constant 0 : index
# CHECK: return %c42, %c0 : index, index
# CHECK: %{{.*}} = constant 42 : index
# CHECK: %{{.*}} = constant 0 : index
# CHECK: return %{{.*}}, %{{.*}} : index, index
def testSelectOp(self):
self.setUp()
@ -463,7 +463,8 @@ class EdscTest:
E.ret([E.select(fun.arg(0), a, b)])
printWithCurrentFunctionName(str(fun))
# CHECK-LABEL: testSelectOp
# CHECK: %0 = select %arg0, %c42_i32, %c0_i32 : i32
# CHECK: %{{.*}} = select %{{.*}}, %{{.*}}, %{{.*}} : i32
# Until python 3.6 this cannot be used because the order in the dict is not the
# order of method declaration.

View File

@ -27,7 +27,8 @@ namespace mlir {
class ConversionPattern;
class DialectConversion;
class MLIRContext;
class Module;
class ModuleOp;
using Module = ModuleOp;
class RewritePattern;
class Type;
using OwningRewritePatternList = std::vector<std::unique_ptr<RewritePattern>>;

View File

@ -19,7 +19,8 @@
#define LINALG3_CONVERTTOLLVMDIALECT_H_
namespace mlir {
class Module;
class ModuleOp;
using Module = ModuleOp;
} // end namespace mlir
namespace linalg {

View File

@ -26,7 +26,8 @@
namespace mlir {
class AffineForOp;
class AffineMap;
class Function;
class FuncOp;
using Function = FuncOp;
class FunctionPassBase;
class Operation;
class Value;

View File

@ -170,8 +170,9 @@ public:
auto type = mlir::FunctionType::get(functionToSpecialize.argumentsType,
{ToyArrayType::get(&getContext())},
&getContext());
auto newFunction = mlir::Function::create(
f.getLoc(), functionToSpecialize.mangledName, type, f.getAttrs());
auto newFunction =
mlir::Function::create(f.getLoc(), functionToSpecialize.mangledName,
type, f.getDialectAttrs());
getModule().push_back(newFunction);
// Clone the function body

View File

@ -172,8 +172,9 @@ public:
auto type = mlir::FunctionType::get(functionToSpecialize.argumentsType,
{ToyArrayType::get(&getContext())},
&getContext());
auto newFunction = mlir::Function::create(
f.getLoc(), functionToSpecialize.mangledName, type, f.getAttrs());
auto newFunction =
mlir::Function::create(f.getLoc(), functionToSpecialize.mangledName,
type, f.getDialectAttrs());
moduleManager.insert(newFunction);
// Clone the function body

View File

@ -52,7 +52,9 @@ These transformation are applied to all levels of IR:
These transformations are applied to builtin ops:
* `constant` ops are uniqued and hoisted into the entry block of a function.
* `constant` ops are uniqued and hoisted into the entry block of the first
parent region that is isolated from above, e.g. the entry block of a
function.
* (TODO) Merge `affine.apply` operations that directly feed each other.
## Standard Ops Canonicalizations

View File

@ -93,16 +93,14 @@ InFlightDiagnostic emit(Location loc, DiagnosticSeverity severity);
```
Using the `DiagnosticEngine`, though, is generally not the preferred way to emit
diagnostics in MLIR. [`function`](LangRef.md#functions), and
[`operation`](LangRef.md#operations) both provide utility methods for emitting
diagnostics:
diagnostics in MLIR. [`operation`](LangRef.md#operations) provides utility
methods for emitting diagnostics:
```c++
// `emit` methods available in the mlir namespace.
InFlightDiagnostic emitError/Remark/Warning(Location);
// These methods use the location attached to the function/operation.
InFlightDiagnostic Function::emitError/Remark/Warning();
// These methods use the location attached to the operation.
InFlightDiagnostic Operation::emitError/Remark/Warning();
// This method creates a diagnostic prefixed with "'op-name' op ".

View File

@ -280,7 +280,7 @@ Example:
#set42 = (d0, d1)[s0, s1]
: d0 >= 0, -d0 + s0 - 1 >= 0, d1 >= 0, -d1 + s1 - 1 >= 0
// Inside a Function
// Inside a Region
affine.if #set42(%i, %j)[%M, %N] {
...
}

View File

@ -27,9 +27,10 @@ document describes the human-readable textual form.
## High-Level Structure
The top-level unit of code in MLIR is a [Module](#module). A module contains a
list of [Functions](#functions). Functions are represented as a composition of
[Operations](#operations) and contain a Control Flow Graph (CFG) of
The unit of code in MLIR is an [Operation](#operation). Operations allow for
representing many different concepts, including the high-level [Module](#module)
and [Function](#functions) operations. Operations may contain
[Regions](#regions) that contain a Control Flow Graph (CFG) of
[Blocks](#blocks), which contain operations and end with
[terminator operations](#terminator-operations) (like branches).
@ -656,9 +657,9 @@ Attributes are the mechanism for specifying constant data in MLIR in places
where a variable is never allowed - e.g. the index of a
[`dim` operation](#dim-operation), or the stride of a convolution. They consist
of a name and a [concrete attribute value](#attribute-values). It is possible to
attach attributes to operations, functions, and function arguments. The set of
expected attributes, their structure, and their interpretation are all
contextually dependent on what they are attached to.
attach attributes to operations. The set of expected attributes, their
structure, and their interpretation are all contextually dependent on what they
are attached to.
There are two main classes of attributes; dependent and dialect. Dependent
attributes derive their structure and meaning from what they are attached to,
@ -669,15 +670,6 @@ meaning from a specific dialect. An example of a dialect attribute may be a
self/context parameter. The context of this attribute is defined by the `swift`
dialect and not the function argument.
### Function and Argument Attributes
Functions and function arguments in MLIR may have optional attributes attached
to them. The sole constraint for these attributes is that they must be dialect
specific attributes. This is because functions, and function arguments, are a
generic entities and thus cannot apply any meaningful context necessary for
dependent attributes. This has the added benefit of avoiding collisions between
common attribute names, such as `noalias`.
### Operation Attributes
Operations, unlike functions and function arguments, may include both dialect
@ -913,22 +905,21 @@ func @simple_form(i1 {unitAttr})
## Module
``` {.ebnf}
module ::= module-header-def* function*
module ::= `module` (`attributes` attribute-dict)? region
```
An MLIR module may optionally have a list of header definitions (e.g. affine
mappings) at the top of the file, but is principally made up of a list of
functions.
TODO: We should allow specifying a "dialect" in the module header. This will
prepopulate a symbol table with known named types and mappings (e.g. for TPU)
and will define the set of operations that are allowed (allowing the verifier to
detect common errors).
An MLIR module represents an opaque top-level container operation. It contains a
single region containing a single block that is comprised of any operations.
Operations within this region must not implicitly capture values defined above
it.
## Functions
MLIR functions have a signature (including argument and result types) and
associated attributes according to the following grammar:
An MLIR Function is an operation with a name containing one [region](#regions)
that forms a CFG(Control Flow Graph). The region of a function is not allowed to
implicitly capture global values, and all external references must use Function
arguments or attributes that establish a symbolic connection(e.g. symbols
referenced by name via a string attribute):
``` {.ebnf}
function ::= `func` function-signature function-attributes? function-body?
@ -943,11 +934,9 @@ function-body ::= region
```
An external function declaration (used when referring to a function declared in
some other module) has no body. A function definition contains a
[region](#regions) made up of one or more blocks forming the function body.
While the MLIR textual form provides a nice inline syntax for function
arguments, they are internally represented as "block arguments" to the first
block in the region.
some other module) has no body. While the MLIR textual form provides a nice
inline syntax for function arguments, they are internally represented as "block
arguments" to the first block in the region.
Examples:
@ -969,10 +958,10 @@ func @count(%x: i64) -> (i64, i64)
A region is a CFG of MLIR [Blocks](#blocks). Regions serve to group semantically
connected blocks, where the semantics is not imposed by the IR. Instead, the
containing entity (operation or function) defines the semantics of the regions
it contains. Regions do not have a name or an address, only the blocks contained
in a region do. Regions are meaningless outside of the containing entity and
have no type or attributes.
containing operation defines the semantics of the regions it contains. Regions
do not have a name or an address, only the blocks contained in a region do.
Regions are meaningless outside of the containing entity and have no type or
attributes.
The first block in the region cannot be a successor of any other block. The
syntax for the region is as follows:
@ -1062,27 +1051,22 @@ operation.
Regions allow to define an operation that creates a closure, for example by
“boxing” the body of the region into a value they produce. It remains up to the
operation to define its semantics. In this situation, the value “containing” the
region may be passed to or returned from a function/region, at which point the
values defined in dominating blocks are no longer accessible. If this region
directly uses such values, passing a value “containing” it across function
boundaries or using it in operations leads to undefined behavior. This is
similar to returning a lambda capturing a reference to a local variable in C++.
Note that if an operation triggers asynchronous execution of the region, it is
under the responsibility of the operation caller to wait for the region to be
executed guaranteeing that any directly used values remain live.
region may be passed to or returned from a region, at which point the values
defined in dominating blocks are no longer accessible. If this region directly
uses such values, passing a value “containing” it across function boundaries or
using it in operations leads to undefined behavior. This is similar to returning
a lambda capturing a reference to a local variable in C++. Note that if an
operation triggers asynchronous execution of the region, it is under the
responsibility of the operation caller to wait for the region to be executed
guaranteeing that any directly used values remain live.
### Arguments and Results
The arguments of the first block of a region are treated as arguments of the
region. The source of these arguments is defined by the parent entity of the
region. If a region is a function body, its arguments are the function
arguments. If a region is used in an operation, the operation semantics
specified how these values are produced. They may correspond to some of the
values the operation itself uses.
region. The source of these arguments is defined by the semantics of the parent
operation. They may correspond to some of the values the operation itself uses.
Regions produce a (possibly empty) list of values. For function body regions,
`return` is the standard region-exiting terminator, but dialects can provide
their own. For regions that belong to an operation, the operation semantics
Regions produce a (possibly empty) list of values. The operation semantics
defines the relation between the region results and the operation results.
## Blocks
@ -1157,9 +1141,10 @@ successor-list ::= successor (`,` successor)*
region-list ::= region (`,` region)*
```
MLIR represents computations within functions with a uniform concept called
_operations_. Operations in MLIR are fully extensible (there is no fixed list of
operations), and have application-specific semantics. For example, MLIR supports
MLIR introduces a uniform concept called _operations_ to enable describing many
different levels of abstractions and computations. Operations in MLIR are fully
extensible (there is no fixed list of operations), and have application-specific
semantics. For example, MLIR supports
[target-independent operations](#memory-operations),
[affine operations](Dialects/Affine.md), and
[target-specific machine operations](#target-specific-operations).
@ -1238,8 +1223,7 @@ The `br` terminator operation represents an unconditional jump to a target
block. The count and types of operands to the branch must align with the
arguments in the target block.
The MLIR branch operation is not allowed to target the entry block for a
function.
The MLIR branch operation is not allowed to target the entry block for a region.
##### 'cond_br' terminator operation
@ -1256,7 +1240,7 @@ to; if it is false, the second destination is chosen. The count and types of
operands must align with the arguments in the corresponding target blocks.
The MLIR conditional branch operation is not allowed to target the entry block
for a function. The two destinations of the conditional branch operation are
for a region. The two destinations of the conditional branch operation are
allowed to be the same.
The following example illustrates a function with a conditional branch operation

View File

@ -168,7 +168,7 @@ change.
### Block Arguments vs PHI nodes
MLIR Functions represent SSA using "[block arguments](LangRef.md#blocks)" rather
MLIR Regions represent SSA using "[block arguments](LangRef.md#blocks)" rather
than [PHI instructions](http://llvm.org/docs/LangRef.html#i-phi) used in LLVM.
This choice is representationally identical (the same constructs can be
represented in either form) but block arguments have several advantages:

View File

@ -143,7 +143,7 @@ above, let's see some examples:
/// An interesting function analysis.
struct MyFunctionAnalysis {
// Compute this analysis with the provided function.
MyFunctionAnalysis(Function *function);
MyFunctionAnalysis(Function function);
};
/// An interesting module analysis.

View File

@ -1,4 +1,4 @@
//===- Dominance.h - Dominator analysis for CFG Functions -------*- C++ -*-===//
//===- Dominance.h - Dominator analysis for CFGs ----------------*- C++ -*-===//
//
// Copyright 2019 The MLIR Authors.
//
@ -26,7 +26,6 @@ extern template class llvm::DominatorTreeBase<mlir::Block, true>;
namespace mlir {
using DominanceInfoNode = llvm::DomTreeNodeBase<Block>;
class Function;
class Operation;
namespace detail {
@ -34,7 +33,6 @@ template <bool IsPostDom> class DominanceInfoBase {
using base = llvm::DominatorTreeBase<Block, IsPostDom>;
public:
DominanceInfoBase(Function function);
DominanceInfoBase(Operation *op) { recalculate(op); }
DominanceInfoBase(DominanceInfoBase &&) = default;
DominanceInfoBase &operator=(DominanceInfoBase &&) = default;
@ -43,7 +41,6 @@ public:
DominanceInfoBase &operator=(const DominanceInfoBase &) = delete;
/// Recalculate the dominance info.
void recalculate(Function function);
void recalculate(Operation *op);
/// Get the root dominance node of the given region.

View File

@ -19,21 +19,13 @@
#define MLIR_ANALYSIS_VERIFIER_H
namespace mlir {
class Function;
class LogicalResult;
class Module;
class Operation;
/// Perform (potentially expensive) checks of invariants, used to detect
/// compiler bugs, on this operation and any nested operations. On error, this
/// reports the error through the MLIRContext and returns failure.
LogicalResult verify(Operation *op);
/// Perform (potentially expensive) checks of invariants, used to detect
/// compiler bugs, on this IR unit and any nested below. On error, this
/// reports the error through the MLIRContext and returns failure.
LogicalResult verify(Function fn);
LogicalResult verify(Module module);
} // end namespace mlir
#endif

View File

@ -25,7 +25,8 @@
namespace mlir {
class ModulePassBase;
class Function;
class FuncOp;
using Function = FuncOp;
using OwnedCubin = std::unique_ptr<std::vector<char>>;
using CubinGenerator =

View File

@ -28,7 +28,8 @@ class Module;
namespace mlir {
class DialectConversion;
class LLVMTypeConverter;
class Module;
class ModuleOp;
using Module = ModuleOp;
class ModulePassBase;
class RewritePattern;
class Type;

View File

@ -36,7 +36,8 @@ class Module;
namespace mlir {
class Module;
class ModuleOp;
using Module = ModuleOp;
namespace impl {
class OrcJIT;

View File

@ -31,7 +31,8 @@ template <typename T> class Expected;
namespace mlir {
class Function;
class FuncOp;
using Function = FuncOp;
/// Simple memref descriptor class compatible with the ABI of functions emitted
/// by MLIR to LLVM IR conversion for statically-shaped memrefs of float type.

View File

@ -24,7 +24,8 @@
namespace mlir {
class AffineMap;
class Dialect;
class Function;
class FuncOp;
using Function = FuncOp;
class FunctionType;
class Identifier;
class IntegerSet;

View File

@ -25,7 +25,8 @@ namespace mlir {
class AffineExpr;
class BlockAndValueMapping;
class Module;
class ModuleOp;
using Module = ModuleOp;
class UnknownLoc;
class FileLineColLoc;
class Type;

View File

@ -27,114 +27,94 @@
#include "llvm/ADT/SmallString.h"
namespace mlir {
class BlockAndValueMapping;
class FunctionType;
class Function;
class MLIRContext;
class Module;
class ModuleOp;
namespace detail {
class ModuleStorage;
//===--------------------------------------------------------------------===//
// Function Operation.
//===--------------------------------------------------------------------===//
/// This class represents all of the internal state of a Function. This allows
/// for the Function class to be value typed.
class FunctionStorage
: public llvm::ilist_node_with_parent<FunctionStorage, ModuleStorage> {
FunctionStorage(Location location, StringRef name, FunctionType type,
ArrayRef<NamedAttribute> attrs = {});
FunctionStorage(Location location, StringRef name, FunctionType type,
ArrayRef<NamedAttribute> attrs,
ArrayRef<NamedAttributeList> argAttrs);
/// The name of the function.
Identifier name;
/// The module this function is embedded into.
ModuleStorage *module = nullptr;
/// The source location the function was defined or derived from.
Location location;
/// The type of the function.
FunctionType type;
/// This holds general named attributes for the function.
NamedAttributeList attrs;
/// The attributes lists for each of the function arguments.
std::vector<NamedAttributeList> argAttrs;
/// The body of the function.
Region body;
friend struct llvm::ilist_traits<FunctionStorage>;
friend Function;
};
} // namespace detail
/// This class represents an MLIR function, or the common unit of computation.
/// The region of a function is not allowed to implicitly capture global values,
/// and all external references must use Function arguments or attributes.
class Function {
/// FuncOp represents a function, or an operation containing one region that
/// forms a CFG(Control Flow Graph). The region of a function is not allowed to
/// implicitly capture global values, and all external references must use
/// Function arguments or attributes that establish a symbolic connection(e.g.
/// symbols referenced by name via a string attribute).
class FuncOp : public Op<FuncOp, OpTrait::ZeroOperands, OpTrait::ZeroResult,
OpTrait::IsIsolatedFromAbove> {
public:
Function(detail::FunctionStorage *impl = nullptr) : impl(impl) {}
using Op::Op;
using Op::print;
static Function create(Location location, StringRef name, FunctionType type,
ArrayRef<NamedAttribute> attrs = {}) {
return new detail::FunctionStorage(location, name, type, attrs);
}
static Function create(Location location, StringRef name, FunctionType type,
ArrayRef<NamedAttribute> attrs,
ArrayRef<NamedAttributeList> argAttrs) {
return new detail::FunctionStorage(location, name, type, attrs, argAttrs);
static StringRef getOperationName() { return "func"; }
static FuncOp create(Location location, StringRef name, FunctionType type,
ArrayRef<NamedAttribute> attrs = {});
static FuncOp create(Location location, StringRef name, FunctionType type,
llvm::iterator_range<dialect_attr_iterator> attrs);
static FuncOp create(Location location, StringRef name, FunctionType type,
ArrayRef<NamedAttribute> attrs,
ArrayRef<NamedAttributeList> argAttrs);
static void build(Builder *builder, OperationState *result, StringRef name,
FunctionType type, ArrayRef<NamedAttribute> attrs);
static void build(Builder *builder, OperationState *result, StringRef name,
FunctionType type, ArrayRef<NamedAttribute> attrs,
ArrayRef<NamedAttributeList> argAttrs);
/// Get the parent module.
ModuleOp getModule();
/// Operation hooks.
static ParseResult parse(OpAsmParser *parser, OperationState *result);
void print(OpAsmPrinter *p);
LogicalResult verify();
/// Returns the name of this function.
StringRef getName() { return getAttrOfType<StringAttr>("name").getValue(); }
/// Set the name of this function.
void setName(StringRef name) {
return setAttr("name", StringAttr::get(name, getContext()));
}
/// Allow converting a Function to bool for null checks.
operator bool() const { return impl; }
bool operator==(Function other) const { return impl == other.impl; }
bool operator!=(Function other) const { return !(*this == other); }
/// The source location the function was defined or derived from.
Location getLoc() { return impl->location; }
/// Set the source location this function was defined or derived from.
void setLoc(Location loc) { impl->location = loc; }
/// Return the name of this function, without the @.
Identifier getName() { return impl->name; }
/// Return the type of this function.
FunctionType getType() { return impl->type; }
/// Returns the type of this function.
FunctionType getType() {
return getAttrOfType<TypeAttr>("type").getValue().cast<FunctionType>();
}
/// Change the type of this function in place. This is an extremely dangerous
/// operation and it is up to the caller to ensure that this is legal for this
/// function, and to restore invariants:
/// - the entry block args must be updated to match the function params.
/// - the entry block args must be updated to match the function params.
/// - the arguments attributes may need an update: if the new type has less
/// parameters we drop the extra attributes, if there are more parameters
/// they won't have any attributes.
void setType(FunctionType newType) {
impl->type = newType;
impl->argAttrs.resize(newType.getNumInputs());
setAttr("type", TypeAttr::get(newType));
}
MLIRContext *getContext();
Module getModule();
/// Add an entry block to an empty function, and set up the block arguments
/// to match the signature of the function.
void addEntryBlock();
/// Unlink this function from its module and delete it.
void erase();
/// Returns true if this function is external, i.e. it has no body.
bool isExternal() { return empty(); }
/// Create a deep copy of this function and all of its blocks, remapping
/// any operands that use values outside of the function using the map that is
/// provided (leaving them alone if no entry is present). If the mapper
/// contains entries for function arguments, these arguments are not included
/// in the new function. Replaces references to cloned sub-values with the
/// corresponding value that is copied, and adds those mappings to the mapper.
FuncOp clone(BlockAndValueMapping &mapper);
FuncOp clone();
/// Clone the internal blocks and attributes from this function into dest. Any
/// cloned blocks are appended to the back of dest. This function asserts that
/// the attributes of the current function and dest are compatible.
void cloneInto(FuncOp dest, BlockAndValueMapping &mapper);
//===--------------------------------------------------------------------===//
// Body Handling
//===--------------------------------------------------------------------===//
Region &getBody() { return impl->body; }
Region &getBody() { return getOperation()->getRegion(0); }
void eraseBody() { getBody().getBlocks().clear(); }
/// This is the list of blocks in the function.
@ -157,266 +137,9 @@ public:
Block &back() { return getBody().back(); }
Block &front() { return getBody().front(); }
//===--------------------------------------------------------------------===//
// Operation Walkers
//===--------------------------------------------------------------------===//
/// Walk the operations in the function in postorder, calling the callback for
/// each operation.
void walk(llvm::function_ref<void(Operation *)> callback);
/// Specialization of walk to only visit operations of 'OpTy'.
template <typename OpTy> void walk(llvm::function_ref<void(OpTy)> callback) {
walk([&](Operation *opInst) {
if (auto op = dyn_cast<OpTy>(opInst))
callback(op);
});
}
//===--------------------------------------------------------------------===//
// Arguments
//===--------------------------------------------------------------------===//
/// Returns number of arguments.
unsigned getNumArguments() { return getType().getInputs().size(); }
/// Gets argument.
BlockArgument *getArgument(unsigned idx) {
return getBlocks().front().getArgument(idx);
}
// Supports argument iteration.
using args_iterator = Block::args_iterator;
args_iterator args_begin() { return front().args_begin(); }
args_iterator args_end() { return front().args_end(); }
llvm::iterator_range<args_iterator> getArguments() {
return {args_begin(), args_end()};
}
//===--------------------------------------------------------------------===//
// Attributes
//===--------------------------------------------------------------------===//
/// Functions may optionally carry a list of attributes that associate
/// constants to names. Attributes may be dynamically added and removed over
/// the lifetime of an function.
/// Return all of the attributes on this function.
ArrayRef<NamedAttribute> getAttrs() { return impl->attrs.getAttrs(); }
/// Return the internal attribute list on this function.
NamedAttributeList &getAttrList() { return impl->attrs; }
/// Return all of the attributes for the argument at 'index'.
ArrayRef<NamedAttribute> getArgAttrs(unsigned index) {
assert(index < getNumArguments() && "invalid argument number");
return impl->argAttrs[index].getAttrs();
}
/// Set the attributes held by this function.
void setAttrs(ArrayRef<NamedAttribute> attributes) {
impl->attrs.setAttrs(attributes);
}
/// Set the attributes held by the argument at 'index'.
void setArgAttrs(unsigned index, ArrayRef<NamedAttribute> attributes) {
assert(index < getNumArguments() && "invalid argument number");
impl->argAttrs[index].setAttrs(attributes);
}
void setArgAttrs(unsigned index, NamedAttributeList attributes) {
assert(index < getNumArguments() && "invalid argument number");
impl->argAttrs[index] = attributes;
}
void setAllArgAttrs(ArrayRef<NamedAttributeList> attributes) {
assert(attributes.size() == getNumArguments());
for (unsigned i = 0, e = attributes.size(); i != e; ++i)
impl->argAttrs[i] = attributes[i];
}
/// Return all argument attributes of this function.
void getAllArgAttrs(SmallVectorImpl<NamedAttributeList> &result) {
result.assign(impl->argAttrs.begin(), impl->argAttrs.end());
}
/// Return the specified attribute if present, null otherwise.
Attribute getAttr(Identifier name) { return impl->attrs.get(name); }
Attribute getAttr(StringRef name) { return impl->attrs.get(name); }
/// Return the specified attribute, if present, for the argument at 'index',
/// null otherwise.
Attribute getArgAttr(unsigned index, Identifier name) {
assert(index < getNumArguments() && "invalid argument number");
return impl->argAttrs[index].get(name);
}
Attribute getArgAttr(unsigned index, StringRef name) {
assert(index < getNumArguments() && "invalid argument number");
return impl->argAttrs[index].get(name);
}
template <typename AttrClass> AttrClass getAttrOfType(Identifier name) {
return getAttr(name).dyn_cast_or_null<AttrClass>();
}
template <typename AttrClass> AttrClass getAttrOfType(StringRef name) {
return getAttr(name).dyn_cast_or_null<AttrClass>();
}
template <typename AttrClass>
AttrClass getArgAttrOfType(unsigned index, Identifier name) {
return getArgAttr(index, name).dyn_cast_or_null<AttrClass>();
}
template <typename AttrClass>
AttrClass getArgAttrOfType(unsigned index, StringRef name) {
return getArgAttr(index, name).dyn_cast_or_null<AttrClass>();
}
/// If the an attribute exists with the specified name, change it to the new
/// value. Otherwise, add a new attribute with the specified name/value.
void setAttr(Identifier name, Attribute value) {
impl->attrs.set(name, value);
}
void setAttr(StringRef name, Attribute value) {
setAttr(Identifier::get(name, getContext()), value);
}
void setArgAttr(unsigned index, Identifier name, Attribute value) {
assert(index < getNumArguments() && "invalid argument number");
impl->argAttrs[index].set(name, value);
}
void setArgAttr(unsigned index, StringRef name, Attribute value) {
setArgAttr(index, Identifier::get(name, getContext()), value);
}
/// Remove the attribute with the specified name if it exists. The return
/// value indicates whether the attribute was present or not.
NamedAttributeList::RemoveResult removeAttr(Identifier name) {
return impl->attrs.remove(name);
}
NamedAttributeList::RemoveResult removeArgAttr(unsigned index,
Identifier name) {
assert(index < getNumArguments() && "invalid argument number");
return impl->attrs.remove(name);
}
//===--------------------------------------------------------------------===//
// Other
//===--------------------------------------------------------------------===//
void print(raw_ostream &os);
void dump();
/// Emit an error about fatal conditions with this function, reporting up to
/// any diagnostic handlers that may be listening.
InFlightDiagnostic emitError();
InFlightDiagnostic emitError(const Twine &message);
/// Emit a warning about this function, reporting up to any diagnostic
/// handlers that may be listening.
InFlightDiagnostic emitWarning();
InFlightDiagnostic emitWarning(const Twine &message);
/// Emit a remark about this function, reporting up to any diagnostic
/// handlers that may be listening.
InFlightDiagnostic emitRemark();
InFlightDiagnostic emitRemark(const Twine &message);
/// Create a deep copy of this function and all of its blocks, remapping
/// any operands that use values outside of the function using the map that is
/// provided (leaving them alone if no entry is present). If the mapper
/// contains entries for function arguments, these arguments are not included
/// in the new function. Replaces references to cloned sub-values with the
/// corresponding value that is copied, and adds those mappings to the mapper.
Function clone(BlockAndValueMapping &mapper);
Function clone();
/// Clone the internal blocks and attributes from this function into dest. Any
/// cloned blocks are appended to the back of dest. This function asserts that
/// the attributes of the current function and dest are compatible.
void cloneInto(Function dest, BlockAndValueMapping &mapper);
/// Methods for supporting PointerLikeTypeTraits.
const void *getAsOpaquePointer() const {
return static_cast<const void *>(impl);
}
static Function getFromOpaquePointer(const void *pointer) {
return reinterpret_cast<detail::FunctionStorage *>(
const_cast<void *>(pointer));
}
private:
/// Set the name of this function.
void setName(Identifier newName) { impl->name = newName; }
/// A pointer to the impl storage instance for this function. This allows for
/// 'Function' to be treated as a value type.
detail::FunctionStorage *impl = nullptr;
// Allow access to 'setName'.
friend class SymbolTable;
// Allow access to 'impl'.
friend class Module;
friend class Region;
};
//===--------------------------------------------------------------------===//
// Function Operation.
//===--------------------------------------------------------------------===//
/// FuncOp represents a function, or a named operation containing one region
/// that forms a CFG(Control Flow Graph). The region of a function is not
/// allowed to implicitly capture global values, and all external references
/// must use Function arguments or attributes.
class FuncOp : public Op<FuncOp, OpTrait::ZeroOperands, OpTrait::ZeroResult,
OpTrait::IsIsolatedFromAbove> {
public:
using Op::Op;
static StringRef getOperationName() { return "func"; }
static void build(Builder *builder, OperationState *result, StringRef name,
FunctionType type, ArrayRef<NamedAttribute> attrs);
/// Operation hooks.
static ParseResult parse(OpAsmParser *parser, OperationState *result);
void print(OpAsmPrinter *p);
LogicalResult verify();
/// Returns the name of this function.
StringRef getName() { return getAttrOfType<StringAttr>("name").getValue(); }
/// Returns the type of this function.
FunctionType getType() {
return getAttrOfType<TypeAttr>("type").getValue().cast<FunctionType>();
}
/// Returns true if this function is external, i.e. it has no body.
bool isExternal() { return empty(); }
//===--------------------------------------------------------------------===//
// Body Handling
//===--------------------------------------------------------------------===//
Region &getBody() { return getOperation()->getRegion(0); }
/// This is the list of blocks in the function.
using RegionType = Region::RegionType;
RegionType &getBlocks() { return getBody().getBlocks(); }
// Iteration over the block in the function.
using iterator = RegionType::iterator;
using reverse_iterator = RegionType::reverse_iterator;
iterator begin() { return getBody().begin(); }
iterator end() { return getBody().end(); }
reverse_iterator rbegin() { return getBody().rbegin(); }
reverse_iterator rend() { return getBody().rend(); }
bool empty() { return getBody().empty(); }
void push_back(Block *block) { getBody().push_back(block); }
void push_front(Block *block) { getBody().push_front(block); }
Block &back() { return getBody().back(); }
Block &front() { return getBody().front(); }
/// Add an entry block to an empty function, and set up the block arguments
/// to match the signature of the function.
void addEntryBlock();
//===--------------------------------------------------------------------===//
// Argument Handling
@ -516,57 +239,36 @@ private:
}
};
/// Temporary forward declaration of FuncOp to the legacy Function.
using Function = FuncOp;
} // end namespace mlir
//===----------------------------------------------------------------------===//
// ilist_traits for Function
//===----------------------------------------------------------------------===//
namespace llvm {
template <>
struct ilist_traits<::mlir::detail::FunctionStorage>
: public ilist_alloc_traits<::mlir::detail::FunctionStorage> {
using FunctionStorage = ::mlir::detail::FunctionStorage;
using function_iterator = simple_ilist<FunctionStorage>::iterator;
static void deleteNode(FunctionStorage *function) { delete function; }
void addNodeToList(FunctionStorage *function);
void removeNodeFromList(FunctionStorage *function);
void transferNodesFromList(ilist_traits<FunctionStorage> &otherList,
function_iterator first, function_iterator last);
private:
mlir::detail::ModuleStorage *getContainingModule();
};
// Functions hash just like pointers.
template <> struct DenseMapInfo<mlir::Function> {
static mlir::Function getEmptyKey() {
template <> struct DenseMapInfo<mlir::FuncOp> {
static mlir::FuncOp getEmptyKey() {
auto pointer = llvm::DenseMapInfo<void *>::getEmptyKey();
return mlir::Function::getFromOpaquePointer(pointer);
return mlir::FuncOp::getFromOpaquePointer(pointer);
}
static mlir::Function getTombstoneKey() {
static mlir::FuncOp getTombstoneKey() {
auto pointer = llvm::DenseMapInfo<void *>::getTombstoneKey();
return mlir::Function::getFromOpaquePointer(pointer);
return mlir::FuncOp::getFromOpaquePointer(pointer);
}
static unsigned getHashValue(mlir::Function val) {
static unsigned getHashValue(mlir::FuncOp val) {
return hash_value(val.getAsOpaquePointer());
}
static bool isEqual(mlir::Function LHS, mlir::Function RHS) {
return LHS == RHS;
}
static bool isEqual(mlir::FuncOp LHS, mlir::FuncOp RHS) { return LHS == RHS; }
};
/// Allow stealing the low bits of FunctionStorage.
template <> struct PointerLikeTypeTraits<mlir::Function> {
/// Allow stealing the low bits of FuncOp.
template <> struct PointerLikeTypeTraits<mlir::FuncOp> {
public:
static inline void *getAsVoidPointer(mlir::Function I) {
static inline void *getAsVoidPointer(mlir::FuncOp I) {
return const_cast<void *>(I.getAsOpaquePointer());
}
static inline mlir::Function getFromVoidPointer(void *P) {
return mlir::Function::getFromOpaquePointer(P);
static inline mlir::FuncOp getFromVoidPointer(void *P) {
return mlir::FuncOp::getFromOpaquePointer(P);
}
enum { NumLowBitsAvailable = 3 };
};

View File

@ -24,88 +24,76 @@
#include "mlir/IR/Function.h"
#include "mlir/IR/SymbolTable.h"
#include "llvm/ADT/ilist.h"
namespace mlir {
class Module;
//===----------------------------------------------------------------------===//
// Module Operation.
//===----------------------------------------------------------------------===//
namespace detail {
class ModuleStorage {
explicit ModuleStorage(MLIRContext *context) : context(context) {}
/// getSublistAccess() - Returns pointer to member of function list
static llvm::iplist<FunctionStorage> ModuleStorage::*
getSublistAccess(FunctionStorage *) {
return &ModuleStorage::functions;
}
/// The context attached to this module.
MLIRContext *context;
/// This is the actual list of functions the module contains.
llvm::iplist<FunctionStorage> functions;
friend Module;
friend struct llvm::ilist_traits<FunctionStorage>;
friend FunctionStorage;
friend Function;
};
} // end namespace detail
class Module {
/// ModuleOp represents a module, or an operation containing one region with a
/// single block containing opaque operations. The region of a module is not
/// allowed to implicitly capture global values, and all external references
/// must use symbolic references via attributes(e.g. via a string name).
class ModuleOp : public Op<ModuleOp, OpTrait::ZeroOperands, OpTrait::ZeroResult,
OpTrait::IsIsolatedFromAbove> {
public:
Module(detail::ModuleStorage *impl = nullptr) : impl(impl) {}
using Op::Op;
using Op::print;
/// Construct a new module object with the given context.
static Module create(MLIRContext *context) {
return new detail::ModuleStorage(context);
}
static StringRef getOperationName() { return "module"; }
MLIRContext *getContext() { return impl->context; }
static void build(Builder *builder, OperationState *result);
/// Allow converting a Module to bool for null checks.
operator bool() const { return impl; }
bool operator==(Module other) const { return impl == other.impl; }
bool operator!=(Module other) const { return !(*this == other); }
/// Construct a module from the given context.
static ModuleOp create(MLIRContext *context);
/// An iterator class used to iterate over the held functions.
class iterator : public llvm::mapped_iterator<
llvm::iplist<detail::FunctionStorage>::iterator,
Function (*)(detail::FunctionStorage &)> {
static Function unwrap(detail::FunctionStorage &impl) { return &impl; }
/// Operation hooks.
static ParseResult parse(OpAsmParser *parser, OperationState *result);
void print(OpAsmPrinter *p);
LogicalResult verify();
public:
using reference = Function;
/// Return body of this module.
Region &getBodyRegion();
Block *getBody();
/// Initializes the operand type iterator to the specified operand iterator.
iterator(llvm::iplist<detail::FunctionStorage>::iterator it)
: llvm::mapped_iterator<llvm::iplist<detail::FunctionStorage>::iterator,
Function (*)(detail::FunctionStorage &)>(
it, &unwrap) {}
iterator(Function it)
: iterator(llvm::iplist<detail::FunctionStorage>::iterator(it.impl)) {}
};
/// Print the this module in the custom top-level form.
void print(raw_ostream &os);
void dump();
/// This is the list of functions in the module.
llvm::iterator_range<iterator> getFunctions() { return {begin(), end()}; }
//===--------------------------------------------------------------------===//
// Body Management.
//===--------------------------------------------------------------------===//
// Iterate over the functions within the module.
using iterator = Block::op_iterator<FuncOp>;
// Iteration over the functions in the module.
iterator begin() { return impl->functions.begin(); }
iterator end() { return impl->functions.end(); }
Function front() { return &impl->functions.front(); }
Function back() { return &impl->functions.back(); }
void clear() { impl->functions.clear(); }
iterator begin() { return getBody()->op_begin<FuncOp>(); }
iterator end() { return getBody()->op_end<FuncOp>(); }
Function front() { return *begin(); }
Function back() { return *std::prev(end()); }
void push_back(Function fn) { impl->functions.push_back(fn.impl); }
void insert(iterator insertPt, Function fn) {
impl->functions.insert(insertPt.getCurrent(), fn.impl);
}
/// Splice all of the functions from 'other' into this module.
void splice(iterator insertPt, Module other) {
impl->functions.splice(insertPt.getCurrent(), other.impl->functions);
/// This is the list of functions in the module.
llvm::iterator_range<iterator> getFunctions() {
return getBody()->getOps<FuncOp>();
}
// Interfaces for working with the symbol table.
/// Insert the operation into the back of the body, before the terminator.
void push_back(Operation *op) {
insert(Block::iterator(getBody()->getTerminator()), op);
}
/// Inser the operation at the given insertion point. Note: The operation is
/// never inserted after the terminator, even if the insertion point is end().
void insert(Operation *insertPt, Operation *op) {
insert(Block::iterator(insertPt), op);
}
void insert(Block::iterator insertPt, Operation *op) {
auto *body = getBody();
if (insertPt == body->end())
insertPt = Block::iterator(body->getTerminator());
body->getOperations().insert(insertPt, op);
}
/// Look up a function with the specified name, returning null if no such
/// name exists. Function names never include the @ on them. Note: This
@ -118,140 +106,10 @@ public:
/// name exists. Function names never include the @ on them. Note: This
/// performs a linear scan of held symbols.
Function getNamedFunction(Identifier name) {
auto &functions = impl->functions;
auto it = llvm::find_if(functions, [name](detail::FunctionStorage &fn) {
return Function(&fn).getName() == name;
});
return it == functions.end() ? nullptr : &*it;
auto it = llvm::find_if(getFunctions(),
[name](FuncOp fn) { return fn.getName() == name; });
return it == end() ? nullptr : *it;
}
void print(raw_ostream &os);
void dump();
/// Erase the current module.
void erase() {
assert(impl && "expected valid module");
delete impl;
}
/// Methods for supporting PointerLikeTypeTraits.
const void *getAsOpaquePointer() const {
return static_cast<const void *>(impl);
}
static Module getFromOpaquePointer(const void *pointer) {
return reinterpret_cast<detail::ModuleStorage *>(
const_cast<void *>(pointer));
}
private:
friend detail::FunctionStorage;
friend Function;
/// The internal impl storage object.
detail::ModuleStorage *impl = nullptr;
};
/// A class used to manage the symbols held by a module. This class handles
/// ensures that symbols inserted into a module have a unique name, and provides
/// efficent named lookup to held symbols.
class ModuleManager {
public:
ModuleManager(Module module) : module(module), symbolTable(module) {}
/// Look up a symbol with the specified name, returning null if no such
/// name exists. Names must never include the @ on them.
template <typename NameTy> Function getNamedFunction(NameTy &&name) const {
return symbolTable.lookup(name);
}
/// Insert a new symbol into the module, auto-renaming it as necessary.
void insert(Function function) {
symbolTable.insert(function);
module.push_back(function);
}
void insert(Module::iterator insertPt, Function function) {
symbolTable.insert(function);
module.insert(insertPt, function);
}
/// Remove the given symbol from the module symbol table and then erase it.
void erase(Function function) {
symbolTable.erase(function);
function.erase();
}
/// Return the internally held module.
Module getModule() const { return module; }
/// Return the context of the internal module.
MLIRContext *getContext() const { return getModule().getContext(); }
private:
Module module;
SymbolTable symbolTable;
};
/// This class acts as an owning reference to a Module, and will automatically
/// destory the held Module if valid.
class OwningModuleRef {
public:
OwningModuleRef(std::nullptr_t = nullptr) {}
OwningModuleRef(Module module) : module(module) {}
OwningModuleRef(OwningModuleRef &&other) : module(other.release()) {}
~OwningModuleRef() {
if (module)
module.erase();
}
// Assign from another module reference.
OwningModuleRef &operator=(OwningModuleRef &&other) {
if (module)
module.erase();
module = other.release();
return *this;
}
/// Allow accessing the internal module.
Module get() const { return module; }
Module operator*() const { return module; }
Module *operator->() { return &module; }
explicit operator bool() const { return module; }
/// Release the referenced module.
Module release() {
Module released;
std::swap(released, module);
return released;
}
private:
Module module;
};
//===--------------------------------------------------------------------===//
// Module Operation.
//===--------------------------------------------------------------------===//
/// ModuleOp represents a module, or an operation containing one region with a
/// single block containing opaque operations. A ModuleOp contains a symbol
/// table for operations, like FuncOp, held within its region. The region of a
/// module is not allowed to implicitly capture global values, and all external
/// references must use attributes.
class ModuleOp : public Op<ModuleOp, OpTrait::ZeroOperands, OpTrait::ZeroResult,
OpTrait::IsIsolatedFromAbove> {
public:
using Op::Op;
static StringRef getOperationName() { return "module"; }
static void build(Builder *builder, OperationState *result);
/// Operation hooks.
static ParseResult parse(OpAsmParser *parser, OperationState *result);
void print(OpAsmPrinter *p);
LogicalResult verify();
/// Return body of this module.
Block *getBody();
};
/// The ModuleTerminatorOp is a special terminator operation for the body of a
@ -271,18 +129,103 @@ public:
LogicalResult verify();
};
//===----------------------------------------------------------------------===//
// Module Manager.
//===----------------------------------------------------------------------===//
/// A class used to manage the symbols held by a module. This class handles
/// ensures that symbols inserted into a module have a unique name, and provides
/// efficent named lookup to held symbols.
class ModuleManager {
public:
ModuleManager(ModuleOp module) : module(module), symbolTable(module) {}
/// Look up a symbol with the specified name, returning null if no such
/// name exists. Names must never include the @ on them.
template <typename NameTy> Function getNamedFunction(NameTy &&name) const {
return symbolTable.lookup(name);
}
/// Insert a new symbol into the module, auto-renaming it as necessary.
void insert(Function function) {
symbolTable.insert(function);
module.push_back(function);
}
void insert(Block::iterator insertPt, Function function) {
symbolTable.insert(function);
module.insert(insertPt, function);
}
/// Remove the given symbol from the module symbol table and then erase it.
void erase(Function function) {
symbolTable.erase(function);
function.erase();
}
/// Return the internally held module.
ModuleOp getModule() const { return module; }
/// Return the context of the internal module.
MLIRContext *getContext() { return module.getContext(); }
private:
ModuleOp module;
SymbolTable symbolTable;
};
/// This class acts as an owning reference to a module, and will automatically
/// destory the held module if valid.
class OwningModuleRef {
public:
OwningModuleRef(std::nullptr_t = nullptr) {}
OwningModuleRef(ModuleOp module) : module(module) {}
OwningModuleRef(OwningModuleRef &&other) : module(other.release()) {}
~OwningModuleRef() {
if (module)
module.erase();
}
// Assign from another module reference.
OwningModuleRef &operator=(OwningModuleRef &&other) {
if (module)
module.erase();
module = other.release();
return *this;
}
/// Allow accessing the internal module.
ModuleOp get() const { return module; }
ModuleOp operator*() const { return module; }
ModuleOp *operator->() { return &module; }
explicit operator bool() const { return module; }
/// Release the referenced module.
ModuleOp release() {
ModuleOp released;
std::swap(released, module);
return released;
}
private:
ModuleOp module;
};
/// Temporary forward declaration of ModuleOp as Module to support the legacy
/// naming.
using Module = ModuleOp;
} // end namespace mlir
namespace llvm {
/// Allow stealing the low bits of ModuleStorage.
template <> struct PointerLikeTypeTraits<mlir::Module> {
/// Allow stealing the low bits of ModuleOp.
template <> struct PointerLikeTypeTraits<mlir::ModuleOp> {
public:
static inline void *getAsVoidPointer(mlir::Module I) {
static inline void *getAsVoidPointer(mlir::ModuleOp I) {
return const_cast<void *>(I.getAsOpaquePointer());
}
static inline mlir::Module getFromVoidPointer(void *P) {
return mlir::Module::getFromOpaquePointer(P);
static inline mlir::ModuleOp getFromVoidPointer(void *P) {
return mlir::ModuleOp::getFromOpaquePointer(P);
}
enum { NumLowBitsAvailable = 3 };
};

View File

@ -850,8 +850,15 @@ public:
/// This is a public constructor to enable access via the llvm::cast family of
/// methods. This should not be used directly.
explicit Op(Operation *state) : OpState(state) {
assert(!state || isa<ConcreteOpType>(state));
explicit Op(Operation *state) : OpState(state) {}
/// Methods for supporting PointerLikeTypeTraits.
const void *getAsOpaquePointer() const {
return static_cast<const void *>((Operation *)*this);
}
static ConcreteOpType getFromOpaquePointer(const void *pointer) {
return ConcreteOpType(
reinterpret_cast<Operation *>(const_cast<void *>(pointer)));
}
private:

View File

@ -27,16 +27,11 @@
namespace mlir {
class BlockAndValueMapping;
namespace detail {
class FunctionStorage;
}
/// This class contains a list of basic blocks and has a notion of the object it
/// is part of - a Function or an Operation.
/// This class contains a list of basic blocks and a link to the parent
/// operation it is attached to.
class Region {
public:
Region() = default;
explicit Region(Function container);
explicit Region(Operation *container);
~Region();
@ -76,10 +71,20 @@ public:
/// region, i.e. a function body region.
Region *getContainingRegion();
/// A Region is either a function body or a part of an operation. If it is
/// part of an operation, then return the operation, otherwise return null.
/// Return the parent operation this region is attached to.
Operation *getContainingOp();
/// Find the first parent operation of the given type, or nullptr if there is
/// no ancestor operation.
template <typename ParentT> ParentT getParentOfType() {
auto *region = this;
do {
if (auto parent = dyn_cast_or_null<ParentT>(region->container))
return parent;
} while ((region = region->getContainingRegion()));
return ParentT();
}
/// A Region is either a function body or a part of an operation. If it is
/// a Function body, then return this function, otherwise return null.
Function getContainingFunction();
@ -130,7 +135,7 @@ private:
RegionType blocks;
/// This is the object we are part of.
llvm::PointerUnion<detail::FunctionStorage *, Operation *> container;
Operation *container;
};
} // end namespace mlir

View File

@ -22,8 +22,9 @@
#include "llvm/ADT/DenseMap.h"
namespace mlir {
class Function;
class Module;
class FuncOp;
using Function = FuncOp;
class ModuleOp;
class MLIRContext;
/// This class represents the symbol table used by a module for function
@ -31,7 +32,7 @@ class MLIRContext;
class SymbolTable {
public:
/// Build a symbol table with the symbols within the given module.
SymbolTable(Module module);
SymbolTable(ModuleOp module);
/// Look up a symbol with the specified name, returning null if no such
/// name exists. Names never include the @ on them.
@ -55,7 +56,7 @@ private:
MLIRContext *context;
/// This is a mapping from a name to the function with that name.
llvm::DenseMap<Identifier, Function> symbolTable;
llvm::StringMap<Function> symbolTable;
/// This is used when name conflicts are detected.
unsigned uniquingCounter = 0;

View File

@ -28,7 +28,8 @@
namespace mlir {
class Block;
class Function;
class FuncOp;
using Function = FuncOp;
class Operation;
class Region;
class Value;

View File

@ -30,7 +30,8 @@ class StringRef;
namespace mlir {
class Location;
class Module;
class ModuleOp;
using Module = ModuleOp;
class MLIRContext;
class Type;

View File

@ -27,7 +27,8 @@ class Any;
namespace mlir {
class FunctionPassBase;
class Module;
class ModuleOp;
using Module = ModuleOp;
class ModulePassBase;
class Pass;
class PassInstrumentation;

View File

@ -32,7 +32,8 @@ class Module;
namespace mlir {
class Module;
class ModuleOp;
using Module = ModuleOp;
/// Convert the given MLIR module into LLVM IR. The LLVM context is extracted
/// from the registered LLVM IR dialect. In case of error, report it

View File

@ -35,7 +35,8 @@
namespace mlir {
class Attribute;
class Location;
class Module;
class ModuleOp;
using Module = ModuleOp;
class Operation;
namespace LLVM {

View File

@ -30,7 +30,8 @@ class Module;
} // namespace llvm
namespace mlir {
class Module;
class ModuleOp;
using Module = ModuleOp;
/// Convert the given MLIR module into NVVM IR. This conversion requires the
/// registration of the LLVM IR dialect and will extract the LLVM context

View File

@ -27,7 +27,8 @@
#include "mlir/IR/Dialect.h"
namespace mlir {
class Function;
class FuncOp;
using Function = FuncOp;
class Operation;
class Value;

View File

@ -30,7 +30,8 @@
namespace mlir {
class AffineMap;
class AffineForOp;
class Function;
class FuncOp;
using Function = FuncOp;
class OpBuilder;
class Value;

View File

@ -23,7 +23,8 @@
namespace mlir {
class AffineExpr;
class AffineForOp;
class Function;
class FuncOp;
using Function = FuncOp;
class Location;
struct LogicalResult;
class OpBuilder;

View File

@ -35,7 +35,8 @@ namespace mlir {
class AffineApplyOp;
class AffineForOp;
class Location;
class Module;
class ModuleOp;
using Module = ModuleOp;
class OpBuilder;
/// Replaces all "deferencing" uses of oldMemRef with newMemRef while optionally

View File

@ -26,7 +26,8 @@
namespace mlir {
class MLIRContext;
class Module;
class ModuleOp;
using Module = ModuleOp;
class OwningModuleRef;
/// Interface of the function that translates a file to MLIR. The

View File

@ -1,4 +1,4 @@
//===- Dominance.cpp - Dominator analysis for functions -------------------===//
//===- Dominance.cpp - Dominator analysis for CFGs ------------------------===//
//
// Copyright 2019 The MLIR Authors.
//
@ -21,7 +21,6 @@
//===----------------------------------------------------------------------===//
#include "mlir/Analysis/Dominance.h"
#include "mlir/IR/Function.h"
#include "mlir/IR/Operation.h"
#include "llvm/Support/GenericDomTreeConstruction.h"
@ -36,34 +35,6 @@ template class llvm::DomTreeNodeBase<Block>;
// DominanceInfoBase
//===----------------------------------------------------------------------===//
template <bool IsPostDom>
DominanceInfoBase<IsPostDom>::DominanceInfoBase(Function function) {
recalculate(function);
}
/// Recalculate the dominance info.
template <bool IsPostDom>
void DominanceInfoBase<IsPostDom>::recalculate(Function function) {
dominanceInfos.clear();
// Build the top level function dominance.
auto functionDominance = llvm::make_unique<base>();
functionDominance->recalculate(function.getBody());
dominanceInfos.try_emplace(&function.getBody(), std::move(functionDominance));
/// Build the dominance for each of the operation regions.
function.walk([&](Operation *op) {
for (auto &region : op->getRegions()) {
// Don't compute dominance if the region is empty.
if (region.empty())
continue;
auto opDominance = llvm::make_unique<base>();
opDominance->recalculate(region);
dominanceInfos.try_emplace(&region, std::move(opDominance));
}
});
}
template <bool IsPostDom>
void DominanceInfoBase<IsPostDom>::recalculate(Operation *op) {
dominanceInfos.clear();
@ -88,6 +59,10 @@ bool DominanceInfoBase<IsPostDom>::properlyDominates(Block *a, Block *b) {
if (a == b)
return false;
// If either a or b are null, then conservatively return false.
if (!a || !b)
return false;
// If both blocks are not in the same region, 'a' properly dominates 'b' if
// 'b' is defined in an operation region that (recursively) ends up being
// dominated by 'a'. Walk up the list of containers enclosing B.
@ -96,9 +71,9 @@ bool DominanceInfoBase<IsPostDom>::properlyDominates(Block *a, Block *b) {
Operation *bAncestor;
do {
bAncestor = regionB->getContainingOp();
// If 'bAncestor' is the top level function, then 'a' is a block
// that post dominates 'b'.
if (!bAncestor)
// If 'bAncestor' is the top level region, then 'a' is a block that post
// dominates 'b'.
if (!bAncestor || !bAncestor->getBlock())
return IsPostDom;
regionB = bAncestor->getBlock()->getParent();
@ -131,6 +106,10 @@ template class mlir::detail::DominanceInfoBase</*IsPostDom=*/false>;
bool DominanceInfo::properlyDominates(Operation *a, Operation *b) {
auto *aBlock = a->getBlock(), *bBlock = b->getBlock();
// If a or b are not within a block, then a does not dominate b.
if (!aBlock || !bBlock)
return false;
// If the blocks are the same, then check if b is before a in the block.
if (aBlock == bBlock)
return a->isBeforeInBlock(b);
@ -165,6 +144,10 @@ bool DominanceInfo::properlyDominates(Value *a, Operation *b) {
bool PostDominanceInfo::properlyPostDominates(Operation *a, Operation *b) {
auto *aBlock = a->getBlock(), *bBlock = b->getBlock();
// If a or b are not within a block, then a does not post dominate b.
if (!aBlock || !bBlock)
return false;
// If the blocks are the same, check if b is before a in the block.
if (aBlock == bBlock)
return b->isBeforeInBlock(a);

View File

@ -37,13 +37,10 @@
#include "mlir/Analysis/Dominance.h"
#include "mlir/IR/Attributes.h"
#include "mlir/IR/Dialect.h"
#include "mlir/IR/Function.h"
#include "mlir/IR/Module.h"
#include "mlir/IR/Operation.h"
#include "llvm/Support/FormatVariadic.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Regex.h"
#include "llvm/Support/raw_ostream.h"
using namespace mlir;
namespace {
@ -53,9 +50,6 @@ public:
explicit OperationVerifier(MLIRContext *ctx)
: ctx(ctx), identifierRegex("^[a-zA-Z_][a-zA-Z_0-9\\.\\$]*$") {}
/// Verify the body of the given function.
LogicalResult verify(Function fn);
/// Verify the given operation.
LogicalResult verify(Operation &op);
@ -92,7 +86,7 @@ private:
/// The current context for the verifier.
MLIRContext *ctx;
/// Dominance information for this function, when checking dominance.
/// Dominance information for this operation, when checking dominance.
DominanceInfo *domInfo = nullptr;
/// Regex checker for attribute names.
@ -104,25 +98,6 @@ private:
};
} // end anonymous namespace
/// Verify the body of the given function.
LogicalResult OperationVerifier::verify(Function fn) {
// Verify the body first.
if (failed(verifyRegion(fn.getBody())))
return failure();
// Since everything looks structurally ok to this point, we do a dominance
// check. We do this as a second pass since malformed CFG's can cause
// dominator analysis constructure to crash and we want the verifier to be
// resilient to malformed code.
DominanceInfo theDomInfo(fn);
domInfo = &theDomInfo;
if (failed(verifyDominance(fn.getBody())))
return failure();
domInfo = nullptr;
return success();
}
/// Verify the given operation.
LogicalResult OperationVerifier::verify(Operation &op) {
// Verify the operation first.
@ -287,7 +262,7 @@ LogicalResult OperationVerifier::verifyDominance(Operation &op) {
}
//===----------------------------------------------------------------------===//
// Entrypoints
// Entrypoint
//===----------------------------------------------------------------------===//
/// Perform (potentially expensive) checks of invariants, used to detect
@ -296,94 +271,3 @@ LogicalResult OperationVerifier::verifyDominance(Operation &op) {
LogicalResult mlir::verify(Operation *op) {
return OperationVerifier(op->getContext()).verify(*op);
}
/// Perform (potentially expensive) checks of invariants, used to detect
/// compiler bugs. On error, this reports the error through the MLIRContext and
/// returns failure.
LogicalResult mlir::verify(Function fn) {
OperationVerifier opVerifier(fn.getContext());
llvm::PrettyStackTraceFormat fmt("MLIR Verifier: func @%s",
fn.getName().c_str());
// Check that the function name is valid.
if (!opVerifier.isValidName(fn.getName().strref()))
return fn.emitError("invalid function name '") << fn.getName() << "'";
/// Verify that all of the attributes are okay.
for (auto attr : fn.getAttrs()) {
if (!opVerifier.isValidName(attr.first))
return fn.emitError("invalid attribute name '") << attr.first << "'";
/// Check that the attribute is a dialect attribute, i.e. contains a '.' for
/// the namespace.
if (!attr.first.strref().contains('.'))
return fn.emitError("functions may only have dialect attributes");
// Verify this attribute with the defining dialect.
if (auto *dialect = opVerifier.getDialectForAttribute(attr))
if (failed(dialect->verifyFunctionAttribute(fn, attr)))
return failure();
}
/// Verify that all of the argument attributes are okay.
for (unsigned i = 0, e = fn.getNumArguments(); i != e; ++i) {
for (auto attr : fn.getArgAttrs(i)) {
if (!opVerifier.isValidName(attr.first))
return fn.emitError("invalid attribute name '")
<< attr.first << "' on argument " << i;
/// Check that the attribute is a dialect attribute, i.e. contains a '.'
/// for the namespace.
if (!attr.first.strref().contains('.'))
return fn.emitError(
"function arguments may only have dialect attributes");
// Verify this attribute with the defining dialect.
if (auto *dialect = opVerifier.getDialectForAttribute(attr))
if (failed(dialect->verifyFunctionArgAttribute(fn, i, attr)))
return failure();
}
}
// External functions have nothing more to check.
if (fn.isExternal())
return success();
// Verify that the argument list of the function and the arg list of the first
// block line up.
auto *firstBB = &fn.front();
auto fnInputTypes = fn.getType().getInputs();
if (fnInputTypes.size() != firstBB->getNumArguments())
return fn.emitError("first block of function must have ")
<< fnInputTypes.size() << " arguments to match function signature";
for (unsigned i = 0, e = firstBB->getNumArguments(); i != e; ++i)
if (fnInputTypes[i] != firstBB->getArgument(i)->getType())
return fn.emitError("type of argument #")
<< i << " must match corresponding argument in function signature";
// Finally, verify the body of the function.
return opVerifier.verify(fn);
}
/// Perform (potentially expensive) checks of invariants, used to detect
/// compiler bugs. On error, this reports the error through the MLIRContext and
/// returns failure.
LogicalResult mlir::verify(Module module) {
// Check that all functions are uniquely named.
llvm::StringMap<Location> nameToOrigLoc;
for (auto fn : module) {
auto it = nameToOrigLoc.try_emplace(fn.getName(), fn.getLoc());
if (!it.second)
return fn.emitError()
.append("redefinition of symbol named '", fn.getName(), "'")
.attachNote(it.first->second)
.append("see existing symbol definition here");
}
// Check that each function is correct.
for (auto fn : module)
if (failed(verify(fn)))
return failure();
return success();
}

View File

@ -83,7 +83,6 @@ namespace {
static constexpr int kNonAttrKindAlias = -1;
class ModuleState {
public:
/// This is the current context if it is knowable, otherwise this is null.
MLIRContext *const context;
@ -91,7 +90,7 @@ public:
explicit ModuleState(MLIRContext *context) : context(context) {}
// Initializes module state, populating affine map state.
void initialize(Module module);
void initialize(Operation *op);
Twine getAttributeAlias(Attribute attr) const {
auto alias = attrToAlias.find(attr);
@ -211,9 +210,12 @@ void ModuleState::visitType(Type type) {
void ModuleState::visitAttribute(Attribute attr) {
recordAttributeReference(attr);
if (auto arrayAttr = attr.dyn_cast<ArrayAttr>())
if (auto arrayAttr = attr.dyn_cast<ArrayAttr>()) {
for (auto elt : arrayAttr.getValue())
visitAttribute(elt);
} else if (auto typeAttr = attr.dyn_cast<TypeAttr>()) {
visitType(typeAttr.getValue());
}
}
void ModuleState::visitOperation(Operation *op) {
@ -222,6 +224,10 @@ void ModuleState::visitOperation(Operation *op) {
visitType(type);
for (auto type : op->getResultTypes())
visitType(type);
for (auto &region : op->getRegions())
for (auto &block : region)
for (auto *arg : block.getArguments())
visitType(arg->getType());
// Visit each of the attributes.
for (auto elt : op->getAttrs())
@ -301,18 +307,12 @@ void ModuleState::initializeSymbolAliases() {
}
// Initializes module state, populating affine map and integer set state.
void ModuleState::initialize(Module module) {
void ModuleState::initialize(Operation *op) {
// Initialize the symbol aliases.
initializeSymbolAliases();
// Walk the module and visit each operation.
for (auto fn : module) {
visitType(fn.getType());
for (auto attr : fn.getAttrs())
ModuleState::visitAttribute(attr.second);
fn.walk([&](Operation *op) { ModuleState::visitOperation(op); });
}
// Visit each of the nested operations.
op->walk([&](Operation *op) { visitOperation(op); });
}
//===----------------------------------------------------------------------===//
@ -331,7 +331,7 @@ public:
interleave(c.begin(), c.end(), each_fn, [&]() { os << ", "; });
}
void print(Module module);
void print(ModuleOp module);
/// Print the given attribute. If 'mayElideType' is true, some attributes are
/// printed without the type when the type matches the default used in the
@ -339,7 +339,6 @@ public:
void printAttribute(Attribute attr, bool mayElideType = false);
void printType(Type type);
void print(Function fn);
void printLocation(LocationAttr loc);
void printAffineMap(AffineMap map);
@ -451,16 +450,6 @@ void ModulePrinter::printLocationInternal(LocationAttr loc, bool pretty) {
}
}
void ModulePrinter::print(Module module) {
// Output the aliases at the top level.
state.printAttributeAliases(os);
state.printTypeAliases(os);
// Print the module.
for (auto fn : module)
print(fn);
}
/// Print a floating point value in a way that the parser will be able to
/// round-trip losslessly.
static void printFloatValue(const APFloat &apValue, raw_ostream &os) {
@ -1179,17 +1168,11 @@ void ModulePrinter::printOptionalAttrDict(ArrayRef<NamedAttribute> attrs,
namespace {
// FunctionPrinter contains common functionality for printing
// CFG and ML functions.
class FunctionPrinter : public ModulePrinter, private OpAsmPrinter {
// OperationPrinter contains common functionality for printing operations.
class OperationPrinter : public ModulePrinter, private OpAsmPrinter {
public:
FunctionPrinter(Function function, ModulePrinter &other);
// Prints the function as a whole.
void print();
// Print the function signature.
void printFunctionSignature();
OperationPrinter(Operation *op, ModulePrinter &other);
OperationPrinter(Region *region, ModulePrinter &other);
// Methods to print operations.
void print(Operation *op);
@ -1272,8 +1255,6 @@ protected:
void printValueID(Value *value, bool printResultNo = true) const;
private:
Function function;
/// This is the value ID for each SSA value in the current function. If this
/// returns ~0, then the valueID has an entry in valueNames.
DenseMap<Value *, unsigned> valueIDs;
@ -1302,17 +1283,25 @@ private:
};
} // end anonymous namespace
FunctionPrinter::FunctionPrinter(Function function, ModulePrinter &other)
: ModulePrinter(other), function(function) {
OperationPrinter::OperationPrinter(Operation *op, ModulePrinter &other)
: ModulePrinter(other) {
for (auto *result : op->getResults())
numberValueID(result);
for (auto &region : op->getRegions())
for (auto &block : region)
numberValuesInBlock(block);
}
for (auto &block : function)
OperationPrinter::OperationPrinter(Region *region, ModulePrinter &other)
: ModulePrinter(other) {
for (auto &block : *region)
numberValuesInBlock(block);
}
/// Number all of the SSA values in the specified block. Values get numbered
/// continuously throughout regions. In particular, we traverse the regions
/// held by operations and number values in depth-first pre-order.
void FunctionPrinter::numberValuesInBlock(Block &block) {
void OperationPrinter::numberValuesInBlock(Block &block) {
// Each block gets a unique ID, and all of the operations within it get
// numbered as well.
blockIDs[&block] = nextBlockID++;
@ -1331,7 +1320,7 @@ void FunctionPrinter::numberValuesInBlock(Block &block) {
}
}
void FunctionPrinter::numberValueID(Value *value) {
void OperationPrinter::numberValueID(Value *value) {
assert(!valueIDs.count(value) && "Value numbered multiple times");
SmallString<32> specialNameBuffer;
@ -1412,74 +1401,8 @@ void FunctionPrinter::numberValueID(Value *value) {
}
}
void FunctionPrinter::print() {
printFunctionSignature();
// Print out function attributes, if present.
auto attrs = function.getAttrs();
if (!attrs.empty()) {
os << "\n attributes ";
printOptionalAttrDict(attrs);
}
// Print the trailing location.
printTrailingLocation(function.getLoc());
if (!function.empty()) {
printRegion(function.getBody(), /*printEntryBlockArgs=*/false,
/*printBlockTerminators=*/true);
os << "\n";
}
os << '\n';
}
void FunctionPrinter::printFunctionSignature() {
os << "func @" << function.getName() << '(';
auto fnType = function.getType();
bool isExternal = function.isExternal();
for (unsigned i = 0, e = function.getNumArguments(); i != e; ++i) {
if (i > 0)
os << ", ";
// If this is an external function, don't print argument labels.
if (!isExternal) {
printOperand(function.getArgument(i));
os << ": ";
}
printType(fnType.getInput(i));
// Print the attributes for this argument.
printOptionalAttrDict(function.getArgAttrs(i));
}
os << ')';
switch (fnType.getResults().size()) {
case 0:
break;
case 1: {
os << " -> ";
auto resultType = fnType.getResults()[0];
bool resultIsFunc = resultType.isa<FunctionType>();
if (resultIsFunc)
os << '(';
printType(resultType);
if (resultIsFunc)
os << ')';
break;
}
default:
os << " -> (";
interleaveComma(fnType.getResults(),
[&](Type eltType) { printType(eltType); });
os << ')';
break;
}
}
void FunctionPrinter::print(Block *block, bool printBlockArgs,
bool printBlockTerminator) {
void OperationPrinter::print(Block *block, bool printBlockArgs,
bool printBlockTerminator) {
// Print the block label and argument list if requested.
if (printBlockArgs) {
os.indent(currentIndent);
@ -1533,13 +1456,13 @@ void FunctionPrinter::print(Block *block, bool printBlockArgs,
currentIndent -= indentWidth;
}
void FunctionPrinter::print(Operation *op) {
void OperationPrinter::print(Operation *op) {
os.indent(currentIndent);
printOperation(op);
printTrailingLocation(op->getLoc());
}
void FunctionPrinter::printValueID(Value *value, bool printResultNo) const {
void OperationPrinter::printValueID(Value *value, bool printResultNo) const {
int resultNo = -1;
auto lookupValue = value;
@ -1572,7 +1495,7 @@ void FunctionPrinter::printValueID(Value *value, bool printResultNo) const {
os << '#' << resultNo;
}
void FunctionPrinter::printOperation(Operation *op) {
void OperationPrinter::printOperation(Operation *op) {
if (size_t numResults = op->getNumResults()) {
printValueID(op->getResult(0), /*printResultNo=*/false);
if (numResults > 1)
@ -1580,7 +1503,9 @@ void FunctionPrinter::printOperation(Operation *op) {
os << " = ";
}
if (printGenericOpForm)
// TODO(riverriddle): FuncOp cannot be round-tripped currently, as
// FunctionType cannot be used in a TypeAttr.
if (printGenericOpForm && !isa<FuncOp>(op))
return printGenericOp(op);
// Check to see if this is a known operation. If so, use the registered
@ -1594,7 +1519,7 @@ void FunctionPrinter::printOperation(Operation *op) {
printGenericOp(op);
}
void FunctionPrinter::printGenericOp(Operation *op) {
void OperationPrinter::printGenericOp(Operation *op) {
os << '"';
printEscapedString(op->getName().getStringRef(), os);
os << "\"(";
@ -1641,8 +1566,8 @@ void FunctionPrinter::printGenericOp(Operation *op) {
printFunctionalType(op);
}
void FunctionPrinter::printSuccessorAndUseList(Operation *term,
unsigned index) {
void OperationPrinter::printSuccessorAndUseList(Operation *term,
unsigned index) {
printBlockName(term->getSuccessor(index));
auto succOperands = term->getSuccessorOperands(index);
@ -1658,8 +1583,17 @@ void FunctionPrinter::printSuccessorAndUseList(Operation *term,
os << ')';
}
// Prints function with initialized module state.
void ModulePrinter::print(Function fn) { FunctionPrinter(fn, *this).print(); }
void ModulePrinter::print(ModuleOp module) {
// Output the aliases at the top level.
state.printAttributeAliases(os);
state.printTypeAliases(os);
// Print the module body without the terminator. The terminator is not printed
// as part of the custom syntax for modules.
auto *moduleBody = module.getBody();
for (auto &op : llvm::make_range(moduleBody->begin(), --moduleBody->end()))
OperationPrinter(&op, *this).print(&op);
}
//===----------------------------------------------------------------------===//
// print and dump methods
@ -1734,15 +1668,27 @@ void Value::print(raw_ostream &os) {
void Value::dump() { print(llvm::errs()); }
void Operation::print(raw_ostream &os) {
auto function = getFunction();
if (!function) {
// Handle top-level operations.
if (!getParent()) {
ModuleState state(getContext());
ModulePrinter modulePrinter(os, state);
OperationPrinter(this, modulePrinter).print(this);
return;
}
auto region = getContainingRegion();
if (!region) {
os << "<<UNLINKED INSTRUCTION>>\n";
return;
}
ModuleState state(function.getContext());
// Get the top-level region.
while (auto *nextRegion = region->getContainingRegion())
region = nextRegion;
ModuleState state(getContext());
ModulePrinter modulePrinter(os, state);
FunctionPrinter(function, modulePrinter).print(this);
OperationPrinter(region, modulePrinter).print(this);
}
void Operation::dump() {
@ -1751,41 +1697,44 @@ void Operation::dump() {
}
void Block::print(raw_ostream &os) {
auto function = getFunction();
if (!function) {
auto region = getParent();
if (!region) {
os << "<<UNLINKED BLOCK>>\n";
return;
}
ModuleState state(function.getContext());
// Get the top-level region.
while (auto *nextRegion = region->getContainingRegion())
region = nextRegion;
ModuleState state(region->getContext());
ModulePrinter modulePrinter(os, state);
FunctionPrinter(function, modulePrinter).print(this);
OperationPrinter(region, modulePrinter).print(this);
}
void Block::dump() { print(llvm::errs()); }
/// Print out the name of the block without printing its body.
void Block::printAsOperand(raw_ostream &os, bool printType) {
if (!getFunction()) {
auto region = getParent();
if (!region) {
os << "<<UNLINKED BLOCK>>\n";
return;
}
ModuleState state(getFunction().getContext());
// Get the top-level region.
while (auto *nextRegion = region->getContainingRegion())
region = nextRegion;
ModuleState state(region->getContext());
ModulePrinter modulePrinter(os, state);
FunctionPrinter(getFunction(), modulePrinter).printBlockName(this);
OperationPrinter(region, modulePrinter).printBlockName(this);
}
void Function::print(raw_ostream &os) {
ModuleState state(getContext());
ModulePrinter(os, state).print(*this);
}
void Function::dump() { print(llvm::errs()); }
void Module::print(raw_ostream &os) {
void ModuleOp::print(raw_ostream &os) {
ModuleState state(getContext());
state.initialize(*this);
ModulePrinter(os, state).print(*this);
}
void Module::dump() { print(llvm::errs()); }
void ModuleOp::dump() { print(llvm::errs()); }

View File

@ -51,15 +51,8 @@ Operation *Block::getContainingOp() {
}
Function Block::getFunction() {
Block *block = this;
while (auto *op = block->getContainingOp()) {
block = op->getBlock();
if (!block)
return nullptr;
}
if (auto *list = block->getParent())
return list->getContainingFunction();
return nullptr;
auto *parent = getParent();
return parent ? parent->getParentOfType<FuncOp>() : nullptr;
}
/// Insert this block (which must not already be in a function) right before
@ -70,7 +63,7 @@ void Block::insertBefore(Block *block) {
block->getParent()->getBlocks().insert(Region::iterator(block), this);
}
/// Unlink this Block from its Function and delete it.
/// Unlink this Block from its parent Region and delete it.
void Block::erase() {
assert(getParent() && "Block has no parent");
getParent()->getBlocks().erase(this);

View File

@ -19,6 +19,7 @@
#include "mlir/IR/BlockAndValueMapping.h"
#include "mlir/IR/Builders.h"
#include "mlir/IR/Diagnostics.h"
#include "mlir/IR/Dialect.h"
#include "mlir/IR/MLIRContext.h"
#include "mlir/IR/Module.h"
#include "mlir/IR/OpImplementation.h"
@ -27,177 +28,31 @@
#include "llvm/ADT/Twine.h"
using namespace mlir;
using namespace mlir::detail;
FunctionStorage::FunctionStorage(Location location, StringRef name,
FunctionType type,
ArrayRef<NamedAttribute> attrs)
: name(Identifier::get(name, type.getContext())), location(location),
type(type), attrs(attrs), argAttrs(type.getNumInputs()), body(this) {}
FunctionStorage::FunctionStorage(Location location, StringRef name,
FunctionType type,
ArrayRef<NamedAttribute> attrs,
ArrayRef<NamedAttributeList> argAttrs)
: name(Identifier::get(name, type.getContext())), location(location),
type(type), attrs(attrs), argAttrs(argAttrs), body(this) {}
MLIRContext *Function::getContext() { return getType().getContext(); }
Module Function::getModule() { return impl->module; }
ModuleStorage *llvm::ilist_traits<FunctionStorage>::getContainingModule() {
size_t Offset(size_t(
&((ModuleStorage *)nullptr->*ModuleStorage::getSublistAccess(nullptr))));
iplist<FunctionStorage> *Anchor(static_cast<iplist<FunctionStorage> *>(this));
return reinterpret_cast<ModuleStorage *>(reinterpret_cast<char *>(Anchor) -
Offset);
}
/// This is a trait method invoked when a Function is added to a Module. We
/// keep the module pointer and module symbol table up to date.
void llvm::ilist_traits<FunctionStorage>::addNodeToList(
FunctionStorage *function) {
assert(!function->module && "already in a module!");
function->module = getContainingModule();
}
/// This is a trait method invoked when a Function is removed from a Module.
/// We keep the module pointer up to date.
void llvm::ilist_traits<FunctionStorage>::removeNodeFromList(
FunctionStorage *function) {
assert(function->module && "not already in a module!");
function->module = nullptr;
}
/// This is a trait method invoked when an operation is moved from one block
/// to another. We keep the block pointer up to date.
void llvm::ilist_traits<FunctionStorage>::transferNodesFromList(
ilist_traits<FunctionStorage> &otherList, function_iterator first,
function_iterator last) {
// If we are transferring functions within the same module, the Module
// pointer doesn't need to be updated.
ModuleStorage *curParent = getContainingModule();
if (curParent == otherList.getContainingModule())
return;
// Update the 'module' member and symbol table records for each function.
for (; first != last; ++first) {
removeNodeFromList(&*first);
addNodeToList(&*first);
}
}
/// Unlink this function from its Module and delete it.
void Function::erase() {
if (auto module = getModule())
module.impl->functions.erase(impl);
else
delete impl;
}
/// Emit an error about fatal conditions with this function, reporting up to
/// any diagnostic handlers that may be listening. This function always
/// returns failure. NOTE: This may terminate the containing application, only
/// use when the IR is in an inconsistent state.
InFlightDiagnostic Function::emitError() { return emitError({}); }
InFlightDiagnostic Function::emitError(const Twine &message) {
return mlir::emitError(getLoc(), message);
}
/// Emit a warning about this function, reporting up to any diagnostic
/// handlers that may be listening.
InFlightDiagnostic Function::emitWarning() { return emitWarning({}); }
InFlightDiagnostic Function::emitWarning(const Twine &message) {
return mlir::emitWarning(getLoc(), message);
}
/// Emit a remark about this function, reporting up to any diagnostic
/// handlers that may be listening.
InFlightDiagnostic Function::emitRemark() { return emitRemark({}); }
InFlightDiagnostic Function::emitRemark(const Twine &message) {
return mlir::emitRemark(getLoc(), message);
}
/// Clone the internal blocks from this function into dest and all attributes
/// from this function to dest.
void Function::cloneInto(Function dest, BlockAndValueMapping &mapper) {
// Add the attributes of this function to dest.
llvm::MapVector<Identifier, Attribute> newAttrs;
for (auto &attr : dest.getAttrs())
newAttrs.insert(attr);
for (auto &attr : getAttrs()) {
auto insertPair = newAttrs.insert(attr);
// TODO(riverriddle) Verify that the two functions have compatible
// attributes.
(void)insertPair;
assert((insertPair.second || insertPair.first->second == attr.second) &&
"the two functions have incompatible attributes");
}
dest.setAttrs(newAttrs.takeVector());
// Clone the body.
impl->body.cloneInto(&dest.impl->body, mapper);
}
/// Create a deep copy of this function and all of its blocks, remapping
/// any operands that use values outside of the function using the map that is
/// provided (leaving them alone if no entry is present). Replaces references
/// to cloned sub-values with the corresponding value that is copied, and adds
/// those mappings to the mapper.
Function Function::clone(BlockAndValueMapping &mapper) {
FunctionType newType = impl->type;
// If the function has a body, then the user might be deleting arguments to
// the function by specifying them in the mapper. If so, we don't add the
// argument to the input type vector.
bool isExternalFn = isExternal();
if (!isExternalFn) {
SmallVector<Type, 4> inputTypes;
for (unsigned i = 0, e = getNumArguments(); i != e; ++i)
if (!mapper.contains(getArgument(i)))
inputTypes.push_back(newType.getInput(i));
newType = FunctionType::get(inputTypes, newType.getResults(), getContext());
}
// Create the new function.
Function newFunc = Function::create(getLoc(), getName(), newType);
/// Set the argument attributes for arguments that aren't being replaced.
for (unsigned i = 0, e = getNumArguments(), destI = 0; i != e; ++i)
if (isExternalFn || !mapper.contains(getArgument(i)))
newFunc.setArgAttrs(destI++, getArgAttrs(i));
/// Clone the current function into the new one and return it.
cloneInto(newFunc, mapper);
return newFunc;
}
Function Function::clone() {
BlockAndValueMapping mapper;
return clone(mapper);
}
//===----------------------------------------------------------------------===//
// Function implementation.
//===----------------------------------------------------------------------===//
/// Add an entry block to an empty function, and set up the block arguments
/// to match the signature of the function.
void Function::addEntryBlock() {
assert(empty() && "function already has an entry block");
auto *entry = new Block();
push_back(entry);
entry->addArguments(impl->type.getInputs());
}
void Function::walk(llvm::function_ref<void(Operation *)> callback) {
getBody().walk(callback);
}
//===----------------------------------------------------------------------===//
// Function Operation.
//===----------------------------------------------------------------------===//
Function FuncOp::create(Location location, StringRef name, FunctionType type,
ArrayRef<NamedAttribute> attrs) {
OperationState state(location, "func");
Builder builder(location->getContext());
Function::build(&builder, &state, name, type, attrs);
return llvm::cast<FuncOp>(Operation::create(state));
}
Function FuncOp::create(Location location, StringRef name, FunctionType type,
llvm::iterator_range<dialect_attr_iterator> attrs) {
SmallVector<NamedAttribute, 8> attrRef(attrs);
return create(location, name, type, llvm::makeArrayRef(attrRef));
}
Function FuncOp::create(Location location, StringRef name, FunctionType type,
ArrayRef<NamedAttribute> attrs,
ArrayRef<NamedAttributeList> argAttrs) {
Function func = create(location, name, type, attrs);
func.setAllArgAttrs(argAttrs);
return func;
}
void FuncOp::build(Builder *builder, OperationState *result, StringRef name,
FunctionType type, ArrayRef<NamedAttribute> attrs) {
result->addAttribute("name", builder->getStringAttr(name));
@ -206,6 +61,23 @@ void FuncOp::build(Builder *builder, OperationState *result, StringRef name,
result->addRegion();
}
void FuncOp::build(Builder *builder, OperationState *result, StringRef name,
FunctionType type, ArrayRef<NamedAttribute> attrs,
ArrayRef<NamedAttributeList> argAttrs) {
build(builder, result, name, type, attrs);
assert(type.getNumInputs() == argAttrs.size());
SmallString<8> argAttrName;
for (unsigned i = 0, e = type.getNumInputs(); i != e; ++i)
if (auto argDict = argAttrs[i].getDictionary())
result->addAttribute(getArgAttrName(i, argAttrName), argDict);
}
/// Get the parent module.
ModuleOp Function::getModule() {
auto *parent = getOperation()->getContainingRegion();
return parent ? parent->getParentOfType<ModuleOp>() : nullptr;
}
/// Parsing/Printing methods.
static ParseResult
parseArgumentList(OpAsmParser *parser, SmallVectorImpl<Type> &argTypes,
@ -376,6 +248,22 @@ void FuncOp::print(OpAsmPrinter *p) {
}
LogicalResult FuncOp::verify() {
auto fnInputTypes = getType().getInputs();
/// Verify that all of the argument attributes are dialect attributes.
for (unsigned i = 0, e = fnInputTypes.size(); i != e; ++i) {
for (auto attr : getArgAttrs(i)) {
if (!attr.first.strref().contains('.'))
return emitOpError("arguments may only have dialect attributes");
auto dialectNamePair = attr.first.strref().split('.');
if (auto *dialect =
getContext()->getRegisteredDialect(dialectNamePair.first)) {
if (failed(dialect->verifyFunctionArgAttribute(*this, i, attr)))
return failure();
}
}
}
// If this function is external there is nothing to do.
if (isExternal())
return success();
@ -383,7 +271,6 @@ LogicalResult FuncOp::verify() {
// Verify that the argument list of the function and the arg list of the entry
// block line up.
Block &entryBlock = front();
auto fnInputTypes = getType().getInputs();
if (fnInputTypes.size() != entryBlock.getNumArguments())
return emitOpError("entry block must have ")
<< fnInputTypes.size() << " arguments to match function signature";
@ -398,6 +285,70 @@ LogicalResult FuncOp::verify() {
return success();
}
/// Add an entry block to an empty function, and set up the block arguments
/// to match the signature of the function.
void FuncOp::addEntryBlock() {
assert(empty() && "function already has an entry block");
auto *entry = new Block();
push_back(entry);
entry->addArguments(getType().getInputs());
}
/// Clone the internal blocks from this function into dest and all attributes
/// from this function to dest.
void FuncOp::cloneInto(FuncOp dest, BlockAndValueMapping &mapper) {
// Add the attributes of this function to dest.
llvm::MapVector<Identifier, Attribute> newAttrs;
for (auto &attr : dest.getAttrs())
newAttrs.insert(attr);
for (auto &attr : getAttrs())
newAttrs.insert(attr);
dest.getOperation()->setAttrs(
DictionaryAttr::get(newAttrs.takeVector(), getContext()));
// Clone the body.
getBody().cloneInto(&dest.getBody(), mapper);
}
/// Create a deep copy of this function and all of its blocks, remapping
/// any operands that use values outside of the function using the map that is
/// provided (leaving them alone if no entry is present). Replaces references
/// to cloned sub-values with the corresponding value that is copied, and adds
/// those mappings to the mapper.
FuncOp FuncOp::clone(BlockAndValueMapping &mapper) {
FunctionType newType = getType();
// If the function has a body, then the user might be deleting arguments to
// the function by specifying them in the mapper. If so, we don't add the
// argument to the input type vector.
bool isExternalFn = isExternal();
if (!isExternalFn) {
SmallVector<Type, 4> inputTypes;
inputTypes.reserve(newType.getNumInputs());
for (unsigned i = 0, e = getNumArguments(); i != e; ++i)
if (!mapper.contains(getArgument(i)))
inputTypes.push_back(newType.getInput(i));
newType = FunctionType::get(inputTypes, newType.getResults(), getContext());
}
// Create the new function.
FuncOp newFunc = llvm::cast<FuncOp>(getOperation()->cloneWithoutRegions());
newFunc.setType(newType);
/// Set the argument attributes for arguments that aren't being replaced.
for (unsigned i = 0, e = getNumArguments(), destI = 0; i != e; ++i)
if (isExternalFn || !mapper.contains(getArgument(i)))
newFunc.setArgAttrs(destI++, getArgAttrs(i));
/// Clone the current function into the new one and return it.
cloneInto(newFunc, mapper);
return newFunc;
}
FuncOp FuncOp::clone() {
BlockAndValueMapping mapper;
return clone(mapper);
}
//===----------------------------------------------------------------------===//
// Function Argument Attribute.
//===----------------------------------------------------------------------===//

View File

@ -37,6 +37,14 @@ void ModuleOp::build(Builder *builder, OperationState *result) {
ensureModuleTerminator(*result->addRegion(), *builder, result->location);
}
/// Construct a module from the given context.
ModuleOp ModuleOp::create(MLIRContext *context) {
OperationState state(UnknownLoc::get(context), "module");
Builder builder(context);
ModuleOp::build(&builder, &state);
return llvm::cast<ModuleOp>(Operation::create(state));
}
ParseResult ModuleOp::parse(OpAsmParser *parser, OperationState *result) {
// If module attributes are present, parse them.
if (succeeded(parser->parseOptionalKeyword("attributes")))
@ -81,17 +89,44 @@ LogicalResult ModuleOp::verify() {
return emitOpError("expected body to have no arguments");
if (body->empty() || !isa<ModuleTerminatorOp>(body->back())) {
emitOpError("expects region to end with '" +
ModuleTerminatorOp::getOperationName() + "'")
.attachNote()
<< "in custom textual format, the absence of terminator implies '"
<< ModuleTerminatorOp::getOperationName() << "'";
return failure();
return emitOpError("expects region to end with '" +
ModuleTerminatorOp::getOperationName() + "'")
.attachNote()
<< "in custom textual format, the absence of terminator implies '"
<< ModuleTerminatorOp::getOperationName() << "'";
}
for (auto &op : *body) {
if (op.getNumResults() != 0) {
return emitOpError()
.append("may not contain operations that produce results")
.attachNote(op.getLoc())
.append("see offending operation defined here");
}
}
// Check that all functions are uniquely named.
llvm::StringMap<Location> nameToOrigLoc;
for (auto fn : getFunctions()) {
auto it = nameToOrigLoc.try_emplace(fn.getName(), fn.getLoc());
if (!it.second)
return fn.emitError()
.append("redefinition of symbol named '", fn.getName(), "'")
.attachNote(it.first->second)
.append("see existing symbol definition here");
}
return success();
}
/// Return body of this module.
Region &ModuleOp::getBodyRegion() { return getOperation()->getRegion(0); }
Block *ModuleOp::getBody() { return &getBodyRegion().front(); }
//===----------------------------------------------------------------------===//
// Module Terminator Operation.
//===----------------------------------------------------------------------===//
LogicalResult ModuleTerminatorOp::verify() {
if (!isa_and_nonnull<ModuleOp>(getOperation()->getParentOp()))
return emitOpError() << "is expected to terminate a '"

View File

@ -423,8 +423,10 @@ void llvm::ilist_traits<::mlir::Operation>::transferNodesFromList(
/// Remove this operation (and its descendants) from its Block and delete
/// all of them.
void Operation::erase() {
assert(getBlock() && "Operation has no block");
getBlock()->getOperations().erase(this);
if (auto *parent = getBlock())
parent->getOperations().erase(this);
else
destroy();
}
/// Unlink this operation from its current block and insert it right before

View File

@ -21,8 +21,6 @@
#include "mlir/IR/Operation.h"
using namespace mlir;
Region::Region(Function container) : container(container.impl) {}
Region::Region(Operation *container) : container(container) {}
Region::~Region() {
@ -35,33 +33,32 @@ Region::~Region() {
/// Return the context this region is inserted in. The region must have a valid
/// parent container.
MLIRContext *Region::getContext() {
assert(!container.isNull() && "region is not attached to a container");
if (auto *inst = getContainingOp())
return inst->getContext();
return getContainingFunction().getContext();
assert(container && "region is not attached to a container");
return container->getContext();
}
/// Return a location for this region. This is the location attached to the
/// parent container. The region must have a valid parent container.
Location Region::getLoc() {
assert(!container.isNull() && "region is not attached to a container");
if (auto *inst = getContainingOp())
return inst->getLoc();
return getContainingFunction().getLoc();
assert(container && "region is not attached to a container");
return container->getLoc();
}
Region *Region::getContainingRegion() {
if (auto *inst = getContainingOp())
return inst->getContainingRegion();
return nullptr;
assert(container && "region is not attached to a container");
return container->getContainingRegion();
}
Operation *Region::getContainingOp() {
return container.dyn_cast<Operation *>();
// TODO: Several usages of getContainingOp need to be updated to handle
// functions operations.
return getContainingFunction() ? nullptr : container;
}
Function Region::getContainingFunction() {
return container.dyn_cast<detail::FunctionStorage *>();
if (auto func = llvm::dyn_cast<FuncOp>(container))
return func;
return FuncOp();
}
bool Region::isProperAncestor(Region *other) {

View File

@ -21,7 +21,7 @@
using namespace mlir;
/// Build a symbol table with the symbols within the given module.
SymbolTable::SymbolTable(Module module) : context(module.getContext()) {
SymbolTable::SymbolTable(ModuleOp module) : context(module.getContext()) {
for (auto func : module) {
auto inserted = symbolTable.insert({func.getName(), func});
(void)inserted;
@ -33,13 +33,13 @@ SymbolTable::SymbolTable(Module module) : context(module.getContext()) {
/// Look up a symbol with the specified name, returning null if no such name
/// exists. Names never include the @ on them.
Function SymbolTable::lookup(StringRef name) const {
return lookup(Identifier::get(name, context));
return symbolTable.lookup(name);
}
/// Look up a symbol with the specified name, returning null if no such name
/// exists. Names never include the @ on them.
Function SymbolTable::lookup(Identifier name) const {
return symbolTable.lookup(name);
return lookup(name.strref());
}
/// Erase the given symbol from the table.
@ -68,6 +68,6 @@ void SymbolTable::insert(Function symbol) {
nameBuffer.resize(originalLength);
nameBuffer += '_';
nameBuffer += std::to_string(uniquingCounter++);
symbol.setName(Identifier::get(nameBuffer, context));
} while (!symbolTable.insert({symbol.getName(), symbol}).second);
} while (!symbolTable.insert({nameBuffer, symbol}).second);
symbol.setName(nameBuffer);
}

View File

@ -255,7 +255,7 @@ public:
/// trailing-location ::= location?
///
template <typename Owner>
ParseResult parseOptionalTrailingLocation(Owner &owner) {
ParseResult parseOptionalTrailingLocation(Owner *owner) {
// If there is a 'loc' we parse a trailing location.
if (!getToken().is(Token::kw_loc))
return success();
@ -264,7 +264,7 @@ public:
LocationAttr directLoc;
if (parseLocation(directLoc))
return failure();
owner.setLoc(directLoc);
owner->setLoc(directLoc);
return success();
}
@ -2479,14 +2479,15 @@ namespace {
/// operations.
class OperationParser : public Parser {
public:
OperationParser(ParserState &state, Function function)
: Parser(state), function(function), opBuilder(function.getBody()) {}
OperationParser(ParserState &state, ModuleOp moduleOp)
: Parser(state), opBuilder(moduleOp.getBodyRegion()), moduleOp(moduleOp) {
}
~OperationParser();
/// After parsing is finished, this function must be called to see if there
/// are any remaining issues.
ParseResult finalize(SMLoc loc);
ParseResult finalize();
//===--------------------------------------------------------------------===//
// SSA Value Handling
@ -2595,8 +2596,6 @@ public:
Block *defineBlockNamed(StringRef name, SMLoc loc, Block *existing);
private:
Function function;
/// Returns the info for a block at the current scope for the given name.
std::pair<Block *, SMLoc> &getBlockInfoByName(StringRef name) {
return blocksByName.back()[name];
@ -2643,6 +2642,9 @@ private:
/// The builder used when creating parsed operation instances.
OpBuilder opBuilder;
/// The top level module operation.
ModuleOp moduleOp;
};
} // end anonymous namespace
@ -2657,7 +2659,7 @@ OperationParser::~OperationParser() {
/// After parsing is finished, this function must be called to see if there are
/// any remaining issues.
ParseResult OperationParser::finalize(SMLoc loc) {
ParseResult OperationParser::finalize() {
// Check for any forward references that are left. If we find any, error
// out.
if (!forwardRefPlaceholders.empty()) {
@ -2697,7 +2699,7 @@ ParseResult OperationParser::popSSANameScope() {
for (auto entry : forwardRefInCurrentScope) {
errors.push_back({entry.second.getPointer(), entry.first});
// Add this block to the top-level region to allow for automatic cleanup.
function.push_back(entry.first);
moduleOp.getOperation()->getRegion(0).push_back(entry.first);
}
llvm::array_pod_sort(errors.begin(), errors.end());
@ -2991,7 +2993,7 @@ ParseResult OperationParser::parseOperation() {
}
// Try to parse the optional trailing location.
if (parseOptionalTrailingLocation(*op))
if (parseOptionalTrailingLocation(op))
return failure();
return success();
@ -3105,9 +3107,8 @@ Operation *OperationParser::parseGenericOperation() {
if (consumeIf(Token::l_paren)) {
do {
// Create temporary regions with the top level region as parent.
result.regions.emplace_back(new Region(function));
if (parseRegion(*result.regions.back(),
/*entryArguments*/ {}))
result.regions.emplace_back(new Region(moduleOp));
if (parseRegion(*result.regions.back(), /*entryArguments=*/{}))
return nullptr;
} while (consumeIf(Token::comma));
if (parseToken(Token::r_paren, "expected ')' to end region list"))
@ -3504,13 +3505,8 @@ public:
ParseResult parseOptionalRegion(Region &region,
ArrayRef<OperandType> arguments,
ArrayRef<Type> argTypes) override {
if (parser.getToken().isNot(Token::l_brace)) {
if (!arguments.empty())
return emitError(
parser.getToken().getLoc(),
"optional region with explicit entry arguments must be defined");
if (parser.getToken().isNot(Token::l_brace))
return success();
}
return parseRegion(region, arguments, argTypes);
}
@ -3866,7 +3862,7 @@ class ModuleParser : public Parser {
public:
explicit ModuleParser(ParserState &state) : Parser(state) {}
ParseResult parseModule(Module module);
ParseResult parseModule(ModuleOp module);
private:
/// Parse an attribute alias declaration.
@ -3874,17 +3870,6 @@ private:
/// Parse an attribute alias declaration.
ParseResult parseTypeAliasDef();
// Functions.
ParseResult
parseArgumentList(SmallVectorImpl<Type> &argTypes,
SmallVectorImpl<std::pair<SMLoc, StringRef>> &argNames,
SmallVectorImpl<SmallVector<NamedAttribute, 2>> &argAttrs);
ParseResult parseFunctionSignature(
StringRef &name, FunctionType &type,
SmallVectorImpl<std::pair<SMLoc, StringRef>> &argNames,
SmallVectorImpl<SmallVector<NamedAttribute, 2>> &argAttrs);
ParseResult parseFunc(Module module);
};
} // end anonymous namespace
@ -3954,168 +3939,20 @@ ParseResult ModuleParser::parseTypeAliasDef() {
return success();
}
/// Parse a (possibly empty) list of Function arguments with types.
///
/// named-argument ::= ssa-id `:` type attribute-dict?
/// argument-list ::= named-argument (`,` named-argument)* | /*empty*/
/// argument-list ::= type attribute-dict? (`,` type attribute-dict?)*
/// | /*empty*/
///
ParseResult ModuleParser::parseArgumentList(
SmallVectorImpl<Type> &argTypes,
SmallVectorImpl<std::pair<SMLoc, StringRef>> &argNames,
SmallVectorImpl<SmallVector<NamedAttribute, 2>> &argAttrs) {
consumeToken(Token::l_paren);
// The argument list either has to consistently have ssa-id's followed by
// types, or just be a type list. It isn't ok to sometimes have SSA ID's and
// sometimes not.
auto parseElt = [&]() -> ParseResult {
// Parse argument name if present.
auto loc = getToken().getLoc();
StringRef name = getTokenSpelling();
if (consumeIf(Token::percent_identifier)) {
// Reject this if the preceding argument was missing a name.
if (argNames.empty() && !argTypes.empty())
return emitError(loc, "expected type instead of SSA identifier");
argNames.emplace_back(loc, name);
if (parseToken(Token::colon, "expected ':'"))
return failure();
} else {
// Reject this if the preceding argument had a name.
if (!argNames.empty())
return emitError("expected SSA identifier");
}
// Parse argument type
auto elt = parseType();
if (!elt)
return failure();
argTypes.push_back(elt);
// Parse the attribute dict.
SmallVector<NamedAttribute, 2> attrs;
if (getToken().is(Token::l_brace))
if (parseAttributeDict(attrs))
return failure();
argAttrs.push_back(attrs);
return success();
};
return parseCommaSeparatedListUntil(Token::r_paren, parseElt);
}
/// Parse a function signature, starting with a name and including the
/// parameter list.
///
/// function-signature ::=
/// function-id `(` argument-list `)` (`->` type-list)?
///
ParseResult ModuleParser::parseFunctionSignature(
StringRef &name, FunctionType &type,
SmallVectorImpl<std::pair<SMLoc, StringRef>> &argNames,
SmallVectorImpl<SmallVector<NamedAttribute, 2>> &argAttrs) {
if (getToken().isNot(Token::at_identifier))
return emitError("expected a function identifier like '@foo'");
name = getTokenSpelling().drop_front();
consumeToken(Token::at_identifier);
if (getToken().isNot(Token::l_paren))
return emitError("expected '(' in function signature");
SmallVector<Type, 4> argTypes;
if (parseArgumentList(argTypes, argNames, argAttrs))
return failure();
// Parse the return type if present.
SmallVector<Type, 4> results;
if (consumeIf(Token::arrow)) {
if (parseFunctionResultTypes(results))
return failure();
}
type = builder.getFunctionType(argTypes, results);
return success();
}
/// Function declarations.
///
/// function ::= `func` function-signature function-attributes?
/// trailing-location? function-body?
/// function-body ::= `{` block+ `}`
/// function-attributes ::= `attributes` attribute-dict
///
ParseResult ModuleParser::parseFunc(Module module) {
consumeToken();
StringRef name;
FunctionType type;
SmallVector<std::pair<SMLoc, StringRef>, 4> argNames;
SmallVector<SmallVector<NamedAttribute, 2>, 4> argAttrs;
auto loc = getToken().getLoc();
if (parseFunctionSignature(name, type, argNames, argAttrs))
return failure();
// If function attributes are present, parse them.
SmallVector<NamedAttribute, 8> attrs;
if (consumeIf(Token::kw_attributes)) {
if (parseAttributeDict(attrs))
return failure();
}
// Okay, the function signature was parsed correctly, create the function now.
auto function =
Function::create(getEncodedSourceLocation(loc), name, type, attrs);
module.push_back(function);
// Parse an optional trailing location.
if (parseOptionalTrailingLocation(function))
return failure();
// Add the attributes to the function arguments.
for (unsigned i = 0, e = function.getNumArguments(); i != e; ++i)
function.setArgAttrs(i, argAttrs[i]);
// External functions have no body.
if (getToken().isNot(Token::l_brace))
return success();
auto braceLoc = getToken().getLoc();
// Prepare the named function arguments.
SmallVector<std::pair<OperationParser::SSAUseInfo, Type>, 4> entryArgs;
for (unsigned i = 0, e = argNames.size(); i != e; ++i) {
entryArgs.emplace_back(
OperationParser::SSAUseInfo{argNames[i].second, 0, argNames[i].first},
type.getInput(i));
}
// Parse the function body.
auto parser = OperationParser(getState(), function);
if (parser.parseRegion(function.getBody(), entryArgs))
return failure();
// Verify that a valid function body was parsed.
if (function.empty())
return emitError(braceLoc, "function must have a body");
return parser.finalize(braceLoc);
}
/// This is the top-level module parser.
ParseResult ModuleParser::parseModule(Module module) {
ParseResult ModuleParser::parseModule(ModuleOp module) {
OperationParser opParser(getState(), module);
while (1) {
switch (getToken().getKind()) {
default:
emitError("expected a top level entity");
return failure();
// Parse a top-level operation.
if (opParser.parseOperation())
return failure();
break;
// If we got to the end of the file, then we're done.
case Token::eof:
return success();
return opParser.finalize();
// If we got an error token, then the lexer already emitted an error, just
// stop. Someday we could introduce error recovery if there was demand
@ -4134,11 +3971,6 @@ ParseResult ModuleParser::parseModule(Module module) {
if (parseTypeAliasDef())
return failure();
break;
case Token::kw_func:
if (parseFunc(module))
return failure();
break;
}
}
}

View File

@ -464,7 +464,7 @@ static LogicalResult verify(spirv::ModuleOp moduleOp) {
static LogicalResult verifyReturn(spirv::ReturnOp returnOp) {
auto funcOp =
llvm::dyn_cast_or_null<FuncOp>(returnOp.getOperation()->getParentOp());
returnOp.getOperation()->getContainingRegion()->getContainingFunction();
if (!funcOp)
return returnOp.emitOpError("must appear in a 'func' op");

View File

@ -72,7 +72,7 @@ std::unique_ptr<llvm::Module> mlir::translateModuleToNVVMIR(Module m) {
if (!func.getAttrOfType<UnitAttr>(gpu::GPUDialect::getKernelFuncAttrName()))
continue;
auto *llvmFunc = llvmModule->getFunction(func.getName().strref());
auto *llvmFunc = llvmModule->getFunction(func.getName());
llvm::Metadata *llvmMetadata[] = {
llvm::ValueAsMetadata::get(llvmFunc),

View File

@ -1,43 +0,0 @@
// RUN: mlir-opt %s | FileCheck %s
// CHECK-LABEL: func @external_func
func @external_func() {
// CHECK-NEXT: func @external_func(i32, i64)
func @external_func(i32, i64) -> ()
// CHECK: func @external_func_with_result() -> (i1, i32)
func @external_func_with_result() -> (i1, i32)
return
}
// CHECK-LABEL: func @complex_func
func @complex_func() {
// CHECK-NEXT: func @test_dimop(%i0: tensor<4x4x?xf32>) -> index {
func @test_dimop(%i0: tensor<4x4x?xf32>) -> index {
%0 = dim %i0, 2 : tensor<4x4x?xf32>
"foo.return"(%0) : (index) -> ()
}
return
}
// CHECK-LABEL: func @func_attributes
func @func_attributes() {
// CHECK-NEXT: func @foo()
// CHECK-NEXT: attributes {foo = true}
func @foo() attributes {foo = true}
return
}
// CHECK-LABEL: func @func_arg_attributes
func @func_arg_attributes() {
// CHECK-NEXT: func @external_func_arg_attrs(i32, i1 {dialect.attr = 10 : i64}, i32)
func @external_func_arg_attrs(i32, i1 {dialect.attr = 10 : i64}, i32)
// CHECK: func @func_arg_attrs(%i0: i1 {dialect.attr = 10 : i64})
func @func_arg_attrs(%i0: i1 {dialect.attr = 10 : i64}) {
return
}
return
}

View File

@ -30,14 +30,6 @@ func @func_op() {
// -----
func @func_op() {
// expected-error@+2 {{optional region with explicit entry arguments must be defined}}
func @mixed_named_arguments(%a : i32)
return
}
// -----
func @func_op() {
// expected-error@+1 {{entry block must have 1 arguments to match function signature}}
func @mixed_named_arguments(f32) {

View File

@ -41,3 +41,11 @@ func @module_op() {
// expected-error@+1 {{is expected to terminate a 'module' operation}}
"module_terminator"() : () -> ()
}
// -----
module {
// expected-error@-1 {{may not contain operations that produce results}}
// expected-note@+1 {{see offending operation defined here}}
%result = "foo.op"() : () -> (i32)
}

View File

@ -71,11 +71,6 @@ func @memrefs(memref<42xi8, #map0>) // expected-error {{memref affine map dimens
#map1 = (d0) -> (d0)
func @memrefs(memref<42x42xi8, #map0, #map1>) // expected-error {{memref affine map dimension mismatch}}
// -----
func missingsigil() -> (i1, index, f32) // expected-error {{expected a function identifier like}}
// -----
func @bad_branch() {
@ -143,11 +138,6 @@ func @block_first_has_predecessor() {
// -----
func @empty() { // expected-error {{function must have a body}}
}
// -----
func @no_return() {
%x = constant 0 : i32 // expected-error {{block with no terminator}}
}
@ -337,14 +327,6 @@ func @argError() {
// -----
func @bbargMismatch(i32, f32) {
// expected-error @-1 {{first block of function must have 2 arguments to match function signature}}
^bb42(%0: f32):
return
}
// -----
func @br_mismatch() {
^bb0:
%0:2 = "foo"() : () -> (i1, i17)
@ -482,10 +464,6 @@ func @undefined_function() {
// -----
func @invalid_result_type() -> () -> () // expected-error {{expected a top level entity}}
// -----
#map1 = (i)[j] -> (i+j)
func @bound_symbol_mismatch(%N : index) {
@ -893,7 +871,7 @@ func @$invalid_function_name()
// -----
// expected-error @+1 {{function arguments may only have dialect attributes}}
// expected-error @+1 {{arguments may only have dialect attributes}}
func @invalid_func_arg_attr(i1 {non_dialect_attr = 10})
// -----

View File

@ -3,9 +3,8 @@
#set0 = (d0) : (1 == 0)
// CHECK-LABEL: inline_notation
// CHECK () -> i32 loc("mysource.cc":10:8)
func @inline_notation() -> i32 loc("mysource.cc":10:8) {
// CHECK-LABEL: func @inline_notation
func @inline_notation() -> i32 {
// CHECK: -> i32 loc("foo")
%1 = "foo"() : () -> i32 loc("foo")

View File

@ -2,9 +2,8 @@
#set0 = (d0) : (1 == 0)
// CHECK-LABEL: inline_notation
// CHECK () -> i32 mysource.cc:10:8
func @inline_notation() -> i32 loc("mysource.cc":10:8) {
// CHECK-LABEL: func @inline_notation
func @inline_notation() -> i32 {
// CHECK: -> i32 "foo"
%1 = "foo"() : () -> i32 loc("foo")

View File

@ -166,10 +166,10 @@ func @aligned_load_incorrect_attributes() -> () {
// spv.Return
//===----------------------------------------------------------------------===//
func @return_not_in_func() -> () {
"foo.function"() ({
// expected-error @+1 {{must appear in a 'func' op}}
spv.Return
}
}) : () -> ()
// -----

View File

@ -3,9 +3,8 @@
#set0 = (d0) : (1 == 0)
// CHECK-LABEL: inline_notation
// CHECK: () -> i32 loc(unknown) {
func @inline_notation() -> i32 loc("mysource.cc":10:8) {
// CHECK-LABEL: func @inline_notation
func @inline_notation() -> i32 {
// CHECK: "foo"() : () -> i32 loc(unknown)
%1 = "foo"() : () -> i32 loc("foo")