forked from OSchip/llvm-project
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:
parent
2e1187dd25
commit
e7d594bb1c
|
@ -43,9 +43,9 @@ class EdscTest:
|
||||||
b.arg(0) + b.arg(1)
|
b.arg(0) + b.arg(1)
|
||||||
printWithCurrentFunctionName(str(fun))
|
printWithCurrentFunctionName(str(fun))
|
||||||
# CHECK-LABEL: testBlockArguments
|
# CHECK-LABEL: testBlockArguments
|
||||||
# CHECK: %c42 = constant 42 : index
|
# CHECK: %{{.*}} = constant 42 : index
|
||||||
# CHECK: ^bb1(%0: f32, %1: f32):
|
# CHECK: ^bb{{.*}}(%{{.*}}: f32, %{{.*}}: f32):
|
||||||
# CHECK: %2 = addf %0, %1 : f32
|
# CHECK: %{{.*}} = addf %{{.*}}, %{{.*}} : f32
|
||||||
|
|
||||||
def testBlockContext(self):
|
def testBlockContext(self):
|
||||||
self.setUp()
|
self.setUp()
|
||||||
|
@ -55,9 +55,9 @@ class EdscTest:
|
||||||
cst + cst
|
cst + cst
|
||||||
printWithCurrentFunctionName(str(fun))
|
printWithCurrentFunctionName(str(fun))
|
||||||
# CHECK-LABEL: testBlockContext
|
# CHECK-LABEL: testBlockContext
|
||||||
# CHECK: %c42 = constant 42 : index
|
# CHECK: %{{.*}} = constant 42 : index
|
||||||
# CHECK: ^bb1:
|
# CHECK: ^bb
|
||||||
# CHECK: %0 = "affine.apply"() {map = () -> (84)} : () -> index
|
# CHECK: %{{.*}} = "affine.apply"() {map = () -> (84)} : () -> index
|
||||||
|
|
||||||
def testBlockContextAppend(self):
|
def testBlockContextAppend(self):
|
||||||
self.setUp()
|
self.setUp()
|
||||||
|
@ -71,11 +71,11 @@ class EdscTest:
|
||||||
E.constant_index(1)
|
E.constant_index(1)
|
||||||
printWithCurrentFunctionName(str(fun))
|
printWithCurrentFunctionName(str(fun))
|
||||||
# CHECK-LABEL: testBlockContextAppend
|
# CHECK-LABEL: testBlockContextAppend
|
||||||
# CHECK: %c41 = constant 41 : index
|
# CHECK: %{{.*}} = constant 41 : index
|
||||||
# CHECK: %c42 = constant 42 : index
|
# CHECK: %{{.*}} = constant 42 : index
|
||||||
# CHECK: ^bb1:
|
# CHECK: ^bb
|
||||||
# CHECK: %c0 = constant 0 : index
|
# CHECK: %{{.*}} = constant 0 : index
|
||||||
# CHECK: %c1 = constant 1 : index
|
# CHECK: %{{.*}} = constant 1 : index
|
||||||
|
|
||||||
def testBlockContextStandalone(self):
|
def testBlockContextStandalone(self):
|
||||||
self.setUp()
|
self.setUp()
|
||||||
|
@ -93,14 +93,14 @@ class EdscTest:
|
||||||
E.constant_index(42)
|
E.constant_index(42)
|
||||||
printWithCurrentFunctionName(str(fun))
|
printWithCurrentFunctionName(str(fun))
|
||||||
# CHECK-LABEL: testBlockContextStandalone
|
# CHECK-LABEL: testBlockContextStandalone
|
||||||
# CHECK: %c41 = constant 41 : index
|
# CHECK: %{{.*}} = constant 41 : index
|
||||||
# CHECK: %c42 = constant 42 : index
|
# CHECK: %{{.*}} = constant 42 : index
|
||||||
# CHECK: ^bb1:
|
# CHECK: ^bb
|
||||||
# CHECK: %c0 = constant 0 : index
|
# CHECK: %{{.*}} = constant 0 : index
|
||||||
# CHECK: %c1 = constant 1 : index
|
# CHECK: %{{.*}} = constant 1 : index
|
||||||
# CHECK: ^bb2:
|
# CHECK: ^bb
|
||||||
# CHECK: %c56 = constant 56 : index
|
# CHECK: %{{.*}} = constant 56 : index
|
||||||
# CHECK: %c57 = constant 57 : index
|
# CHECK: %{{.*}} = constant 57 : index
|
||||||
|
|
||||||
def testBooleanOps(self):
|
def testBooleanOps(self):
|
||||||
self.setUp()
|
self.setUp()
|
||||||
|
@ -111,19 +111,19 @@ class EdscTest:
|
||||||
stmt2 = ~(stmt1 | (k == l))
|
stmt2 = ~(stmt1 | (k == l))
|
||||||
printWithCurrentFunctionName(str(fun))
|
printWithCurrentFunctionName(str(fun))
|
||||||
# CHECK-LABEL: testBooleanOps
|
# CHECK-LABEL: testBooleanOps
|
||||||
# CHECK: %0 = cmpi "slt", %arg0, %arg1 : i1
|
# CHECK: %{{.*}} = cmpi "slt", %{{.*}}, %{{.*}} : i1
|
||||||
# CHECK: %1 = cmpi "sge", %arg1, %arg2 : i1
|
# CHECK: %{{.*}} = cmpi "sge", %{{.*}}, %{{.*}} : i1
|
||||||
# CHECK: %2 = muli %0, %1 : i1
|
# CHECK: %{{.*}} = muli %{{.*}}, %{{.*}} : i1
|
||||||
# CHECK: %3 = cmpi "eq", %arg2, %arg3 : i1
|
# CHECK: %{{.*}} = cmpi "eq", %{{.*}}, %{{.*}} : i1
|
||||||
# CHECK: %true = constant 1 : i1
|
# CHECK: %{{.*}} = constant 1 : i1
|
||||||
# CHECK: %4 = subi %true, %2 : i1
|
# CHECK: %{{.*}} = subi %{{.*}}, %{{.*}} : i1
|
||||||
# CHECK: %true_0 = constant 1 : i1
|
# CHECK: %{{.*}} = constant 1 : i1
|
||||||
# CHECK: %5 = subi %true_0, %3 : i1
|
# CHECK: %{{.*}} = subi %{{.*}}, %{{.*}} : i1
|
||||||
# CHECK: %6 = muli %4, %5 : i1
|
# CHECK: %{{.*}} = muli %{{.*}}, %{{.*}} : i1
|
||||||
# CHECK: %true_1 = constant 1 : i1
|
# CHECK: %{{.*}} = constant 1 : i1
|
||||||
# CHECK: %7 = subi %true_1, %6 : i1
|
# CHECK: %{{.*}} = subi %{{.*}}, %{{.*}} : i1
|
||||||
# CHECK: %true_2 = constant 1 : i1
|
# CHECK: %{{.*}} = constant 1 : i1
|
||||||
# CHECK: %8 = subi %true_2, %7 : i1
|
# CHECK: %{{.*}} = subi %{{.*}}, %{{.*}} : i1
|
||||||
|
|
||||||
def testBr(self):
|
def testBr(self):
|
||||||
self.setUp()
|
self.setUp()
|
||||||
|
@ -134,8 +134,8 @@ class EdscTest:
|
||||||
E.br(blk)
|
E.br(blk)
|
||||||
printWithCurrentFunctionName(str(fun))
|
printWithCurrentFunctionName(str(fun))
|
||||||
# CHECK-LABEL: testBr
|
# CHECK-LABEL: testBr
|
||||||
# CHECK: br ^bb1
|
# CHECK: br ^bb
|
||||||
# CHECK: ^bb1:
|
# CHECK: ^bb
|
||||||
# CHECK: return
|
# CHECK: return
|
||||||
|
|
||||||
def testBrArgs(self):
|
def testBrArgs(self):
|
||||||
|
@ -147,11 +147,11 @@ class EdscTest:
|
||||||
E.br(b, [E.constant_index(0), E.constant_index(1)])
|
E.br(b, [E.constant_index(0), E.constant_index(1)])
|
||||||
printWithCurrentFunctionName(str(fun))
|
printWithCurrentFunctionName(str(fun))
|
||||||
# CHECK-LABEL: testBrArgs
|
# CHECK-LABEL: testBrArgs
|
||||||
# CHECK: %c0 = constant 0 : index
|
# CHECK: %{{.*}} = constant 0 : index
|
||||||
# CHECK: %c1 = constant 1 : index
|
# CHECK: %{{.*}} = constant 1 : index
|
||||||
# CHECK: br ^bb1(%c0, %c1 : index, index)
|
# CHECK: br ^bb{{.*}}(%{{.*}}, %{{.*}} : index, index)
|
||||||
# CHECK: ^bb1(%0: index, %1: index):
|
# CHECK: ^bb{{.*}}(%{{.*}}: index, %{{.*}}: index):
|
||||||
# CHECK: br ^bb1(%1, %0 : index, index)
|
# CHECK: br ^bb{{.*}}(%{{.*}}, %{{.*}} : index, index)
|
||||||
|
|
||||||
def testBrDeclaration(self):
|
def testBrDeclaration(self):
|
||||||
self.setUp()
|
self.setUp()
|
||||||
|
@ -162,8 +162,8 @@ class EdscTest:
|
||||||
E.ret()
|
E.ret()
|
||||||
printWithCurrentFunctionName(str(fun))
|
printWithCurrentFunctionName(str(fun))
|
||||||
# CHECK-LABEL: testBrDeclaration
|
# CHECK-LABEL: testBrDeclaration
|
||||||
# CHECK: br ^bb1
|
# CHECK: br ^bb
|
||||||
# CHECK: ^bb1:
|
# CHECK: ^bb
|
||||||
# CHECK: return
|
# CHECK: return
|
||||||
|
|
||||||
def testCallOp(self):
|
def testCallOp(self):
|
||||||
|
@ -176,8 +176,8 @@ class EdscTest:
|
||||||
printWithCurrentFunctionName(str(self.module))
|
printWithCurrentFunctionName(str(self.module))
|
||||||
# CHECK-LABEL: testCallOp
|
# CHECK-LABEL: testCallOp
|
||||||
# CHECK: func @sqrtf(f32) -> f32
|
# CHECK: func @sqrtf(f32) -> f32
|
||||||
# CHECK: %f = constant @sqrtf : (f32) -> f32
|
# CHECK: %{{.*}} = constant @sqrtf : (f32) -> f32
|
||||||
# CHECK: %0 = call_indirect %f(%arg0) : (f32) -> f32
|
# CHECK: %{{.*}} = call_indirect %{{.*}}(%{{.*}}) : (f32) -> f32
|
||||||
|
|
||||||
def testCondBr(self):
|
def testCondBr(self):
|
||||||
self.setUp()
|
self.setUp()
|
||||||
|
@ -190,7 +190,7 @@ class EdscTest:
|
||||||
E.cond_br(fun.arg(0), blk1, [], blk2, [cst])
|
E.cond_br(fun.arg(0), blk1, [], blk2, [cst])
|
||||||
printWithCurrentFunctionName(str(fun))
|
printWithCurrentFunctionName(str(fun))
|
||||||
# CHECK-LABEL: testCondBr
|
# CHECK-LABEL: testCondBr
|
||||||
# CHECK: cond_br %arg0, ^bb1, ^bb2(%c0 : index)
|
# CHECK: cond_br %{{.*}}, ^bb{{.*}}, ^bb{{.*}}(%{{.*}} : index)
|
||||||
|
|
||||||
def testConstants(self):
|
def testConstants(self):
|
||||||
self.setUp()
|
self.setUp()
|
||||||
|
@ -226,8 +226,8 @@ class EdscTest:
|
||||||
E.op("foo", [fun.arg(0)], [self.f32Type]) + fun.arg(1)
|
E.op("foo", [fun.arg(0)], [self.f32Type]) + fun.arg(1)
|
||||||
printWithCurrentFunctionName(str(fun))
|
printWithCurrentFunctionName(str(fun))
|
||||||
# CHECK-LABEL: testCustom
|
# CHECK-LABEL: testCustom
|
||||||
# CHECK: %0 = "foo"(%arg0) : (index) -> f32
|
# CHECK: %{{.*}} = "foo"(%{{.*}}) : (index) -> f32
|
||||||
# CHECK: %1 = addf %0, %arg1 : f32
|
# CHECK: %{{.*}} = addf %{{.*}}, %{{.*}} : f32
|
||||||
|
|
||||||
# Create 'addi' using the generic Op interface. We need an operation known
|
# Create 'addi' using the generic Op interface. We need an operation known
|
||||||
# to the execution engine so that the engine can compile it.
|
# to the execution engine so that the engine can compile it.
|
||||||
|
@ -255,7 +255,7 @@ class EdscTest:
|
||||||
printWithCurrentFunctionName(str(self.module))
|
printWithCurrentFunctionName(str(self.module))
|
||||||
# CHECK-LABEL: testDivisions
|
# CHECK-LABEL: testDivisions
|
||||||
# CHECK: floordiv 42
|
# CHECK: floordiv 42
|
||||||
# CHECK: divis %arg1, %arg2 : i32
|
# CHECK: divis %{{.*}}, %{{.*}} : i32
|
||||||
|
|
||||||
def testFunctionArgs(self):
|
def testFunctionArgs(self):
|
||||||
self.setUp()
|
self.setUp()
|
||||||
|
@ -264,7 +264,7 @@ class EdscTest:
|
||||||
pass
|
pass
|
||||||
printWithCurrentFunctionName(str(fun))
|
printWithCurrentFunctionName(str(fun))
|
||||||
# CHECK-LABEL: testFunctionArgs
|
# CHECK-LABEL: testFunctionArgs
|
||||||
# CHECK: func @foo(%arg0: f32, %arg1: f32) -> index
|
# CHECK: func @foo(%{{.*}}: f32, %{{.*}}: f32) -> index
|
||||||
|
|
||||||
def testFunctionContext(self):
|
def testFunctionContext(self):
|
||||||
self.setUp()
|
self.setUp()
|
||||||
|
@ -295,7 +295,7 @@ class EdscTest:
|
||||||
# CHECK-LABEL: testFunctionMultiple
|
# CHECK-LABEL: testFunctionMultiple
|
||||||
# CHECK: func @foo()
|
# CHECK: func @foo()
|
||||||
# CHECK: func @foo_0()
|
# CHECK: func @foo_0()
|
||||||
# CHECK: %c0 = constant 0 : index
|
# CHECK: %{{.*}} = constant 0 : index
|
||||||
|
|
||||||
def testIndexedValue(self):
|
def testIndexedValue(self):
|
||||||
self.setUp()
|
self.setUp()
|
||||||
|
@ -313,9 +313,9 @@ class EdscTest:
|
||||||
# CHECK-LABEL: testIndexedValue
|
# CHECK-LABEL: testIndexedValue
|
||||||
# CHECK: "affine.for"()
|
# CHECK: "affine.for"()
|
||||||
# CHECK: "affine.for"()
|
# CHECK: "affine.for"()
|
||||||
# CHECK: %0 = load %arg0[%i0, %i1] : memref<10x42xf32>
|
# CHECK: %{{.*}} = load %{{.*}}[%{{.*}}, %{{.*}}] : memref<10x42xf32>
|
||||||
# CHECK: %1 = addf %0, %cst : f32
|
# CHECK: %{{.*}} = addf %{{.*}}, %{{.*}} : f32
|
||||||
# CHECK: store %1, %arg0[%i0, %i1] : memref<10x42xf32>
|
# CHECK: store %{{.*}}, %{{.*}}[%{{.*}}, %{{.*}}] : memref<10x42xf32>
|
||||||
# CHECK: {lower_bound = () -> (0), step = 1 : index, upper_bound = () -> (42)}
|
# CHECK: {lower_bound = () -> (0), step = 1 : index, upper_bound = () -> (42)}
|
||||||
# CHECK: {lower_bound = () -> (0), step = 1 : index, upper_bound = () -> (10)}
|
# CHECK: {lower_bound = () -> (0), step = 1 : index, upper_bound = () -> (10)}
|
||||||
|
|
||||||
|
@ -331,10 +331,10 @@ class EdscTest:
|
||||||
printWithCurrentFunctionName(str(fun))
|
printWithCurrentFunctionName(str(fun))
|
||||||
# CHECK-LABEL: testLoopContext
|
# CHECK-LABEL: testLoopContext
|
||||||
# CHECK: "affine.for"() (
|
# CHECK: "affine.for"() (
|
||||||
# CHECK: ^bb1(%i0: index):
|
# CHECK: ^bb{{.*}}(%{{.*}}: index):
|
||||||
# CHECK: "affine.for"(%c42, %2) (
|
# CHECK: "affine.for"(%{{.*}}, %{{.*}}) (
|
||||||
# CHECK: ^bb2(%i1: index):
|
# CHECK: ^bb{{.*}}(%{{.*}}: index):
|
||||||
# CHECK: "affine.apply"(%i0, %i1) {map = (d0, d1) -> (d0 + d1)} : (index, index) -> 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 = (d0) -> (d0), step = 2 : index, upper_bound = (d0) -> (d0)} : (index, index) -> ()
|
||||||
# CHECK: {lower_bound = () -> (0), step = 1 : index, upper_bound = () -> (42)}
|
# CHECK: {lower_bound = () -> (0), step = 1 : index, upper_bound = () -> (42)}
|
||||||
|
|
||||||
|
@ -348,14 +348,14 @@ class EdscTest:
|
||||||
printWithCurrentFunctionName(str(fun))
|
printWithCurrentFunctionName(str(fun))
|
||||||
# CHECK-LABEL: testLoopNestContext
|
# CHECK-LABEL: testLoopNestContext
|
||||||
# CHECK: "affine.for"() (
|
# CHECK: "affine.for"() (
|
||||||
# CHECK: ^bb1(%i0: index):
|
# CHECK: ^bb{{.*}}(%{{.*}}: index):
|
||||||
# CHECK: "affine.for"() (
|
# CHECK: "affine.for"() (
|
||||||
# CHECK: ^bb2(%i1: index):
|
# CHECK: ^bb{{.*}}(%{{.*}}: index):
|
||||||
# CHECK: "affine.for"() (
|
# CHECK: "affine.for"() (
|
||||||
# CHECK: ^bb3(%i2: index):
|
# CHECK: ^bb{{.*}}(%{{.*}}: index):
|
||||||
# CHECK: "affine.for"() (
|
# CHECK: "affine.for"() (
|
||||||
# CHECK: ^bb4(%i3: index):
|
# CHECK: ^bb{{.*}}(%{{.*}}: index):
|
||||||
# CHECK: %2 = "affine.apply"(%i0, %i1, %i2, %i3) {map = (d0, d1, d2, d3) -> (d0 + d1 + d2 + d3)} : (index, index, index, index) -> index
|
# CHECK: %{{.*}} = "affine.apply"(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}) {map = (d0, d1, d2, d3) -> (d0 + d1 + d2 + d3)} : (index, index, index, index) -> index
|
||||||
|
|
||||||
def testMLIRBooleanCompilation(self):
|
def testMLIRBooleanCompilation(self):
|
||||||
self.setUp()
|
self.setUp()
|
||||||
|
@ -388,8 +388,8 @@ class EdscTest:
|
||||||
# CHECK-LABEL: testMLIRFunctionCreation
|
# CHECK-LABEL: testMLIRFunctionCreation
|
||||||
# CHECK: f32
|
# CHECK: f32
|
||||||
# CHECK: memref<3x4x?x5xf32>
|
# CHECK: memref<3x4x?x5xf32>
|
||||||
# CHECK: func @copy(%arg0: memref<3x4x?x5xf32>, %arg1: memref<3x4x?x5xf32>) {
|
# CHECK: func @copy(%{{.*}}: memref<3x4x?x5xf32>, %{{.*}}: memref<3x4x?x5xf32>) {
|
||||||
# CHECK: func @sqrtf(%arg0: f32) -> f32
|
# CHECK: func @sqrtf(%{{.*}}: f32) -> f32
|
||||||
|
|
||||||
def testMLIRScalarTypes(self):
|
def testMLIRScalarTypes(self):
|
||||||
self.setUp()
|
self.setUp()
|
||||||
|
@ -433,10 +433,10 @@ class EdscTest:
|
||||||
# CHECK: "affine.for"()
|
# CHECK: "affine.for"()
|
||||||
# CHECK: "affine.for"()
|
# CHECK: "affine.for"()
|
||||||
# CHECK: "affine.for"()
|
# CHECK: "affine.for"()
|
||||||
# CHECK-DAG: %0 = load %arg0[%i0, %i2] : memref<32x32xf32>
|
# CHECK-DAG: %{{.*}} = load %{{.*}}[%{{.*}}, %{{.*}}] : memref<32x32xf32>
|
||||||
# CHECK-DAG: %1 = load %arg1[%i2, %i1] : memref<32x32xf32>
|
# CHECK-DAG: %{{.*}} = load %{{.*}}[%{{.*}}, %{{.*}}] : memref<32x32xf32>
|
||||||
# CHECK: %2 = mulf %0, %1 : f32
|
# CHECK: %{{.*}} = mulf %{{.*}}, %{{.*}} : f32
|
||||||
# CHECK: store %2, %arg2[%i0, %i1] : memref<32x32xf32>
|
# 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)} : () -> ()
|
# 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])
|
E.ret([c42, c0])
|
||||||
printWithCurrentFunctionName(str(fun))
|
printWithCurrentFunctionName(str(fun))
|
||||||
# CHECK-LABEL: testRet
|
# CHECK-LABEL: testRet
|
||||||
# CHECK: %c42 = constant 42 : index
|
# CHECK: %{{.*}} = constant 42 : index
|
||||||
# CHECK: %c0 = constant 0 : index
|
# CHECK: %{{.*}} = constant 0 : index
|
||||||
# CHECK: return %c42, %c0 : index, index
|
# CHECK: return %{{.*}}, %{{.*}} : index, index
|
||||||
|
|
||||||
def testSelectOp(self):
|
def testSelectOp(self):
|
||||||
self.setUp()
|
self.setUp()
|
||||||
|
@ -463,7 +463,8 @@ class EdscTest:
|
||||||
E.ret([E.select(fun.arg(0), a, b)])
|
E.ret([E.select(fun.arg(0), a, b)])
|
||||||
printWithCurrentFunctionName(str(fun))
|
printWithCurrentFunctionName(str(fun))
|
||||||
# CHECK-LABEL: testSelectOp
|
# 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
|
# Until python 3.6 this cannot be used because the order in the dict is not the
|
||||||
# order of method declaration.
|
# order of method declaration.
|
||||||
|
|
|
@ -27,7 +27,8 @@ namespace mlir {
|
||||||
class ConversionPattern;
|
class ConversionPattern;
|
||||||
class DialectConversion;
|
class DialectConversion;
|
||||||
class MLIRContext;
|
class MLIRContext;
|
||||||
class Module;
|
class ModuleOp;
|
||||||
|
using Module = ModuleOp;
|
||||||
class RewritePattern;
|
class RewritePattern;
|
||||||
class Type;
|
class Type;
|
||||||
using OwningRewritePatternList = std::vector<std::unique_ptr<RewritePattern>>;
|
using OwningRewritePatternList = std::vector<std::unique_ptr<RewritePattern>>;
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
#define LINALG3_CONVERTTOLLVMDIALECT_H_
|
#define LINALG3_CONVERTTOLLVMDIALECT_H_
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class Module;
|
class ModuleOp;
|
||||||
|
using Module = ModuleOp;
|
||||||
} // end namespace mlir
|
} // end namespace mlir
|
||||||
|
|
||||||
namespace linalg {
|
namespace linalg {
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class AffineForOp;
|
class AffineForOp;
|
||||||
class AffineMap;
|
class AffineMap;
|
||||||
class Function;
|
class FuncOp;
|
||||||
|
using Function = FuncOp;
|
||||||
class FunctionPassBase;
|
class FunctionPassBase;
|
||||||
class Operation;
|
class Operation;
|
||||||
class Value;
|
class Value;
|
||||||
|
|
|
@ -170,8 +170,9 @@ public:
|
||||||
auto type = mlir::FunctionType::get(functionToSpecialize.argumentsType,
|
auto type = mlir::FunctionType::get(functionToSpecialize.argumentsType,
|
||||||
{ToyArrayType::get(&getContext())},
|
{ToyArrayType::get(&getContext())},
|
||||||
&getContext());
|
&getContext());
|
||||||
auto newFunction = mlir::Function::create(
|
auto newFunction =
|
||||||
f.getLoc(), functionToSpecialize.mangledName, type, f.getAttrs());
|
mlir::Function::create(f.getLoc(), functionToSpecialize.mangledName,
|
||||||
|
type, f.getDialectAttrs());
|
||||||
getModule().push_back(newFunction);
|
getModule().push_back(newFunction);
|
||||||
|
|
||||||
// Clone the function body
|
// Clone the function body
|
||||||
|
|
|
@ -172,8 +172,9 @@ public:
|
||||||
auto type = mlir::FunctionType::get(functionToSpecialize.argumentsType,
|
auto type = mlir::FunctionType::get(functionToSpecialize.argumentsType,
|
||||||
{ToyArrayType::get(&getContext())},
|
{ToyArrayType::get(&getContext())},
|
||||||
&getContext());
|
&getContext());
|
||||||
auto newFunction = mlir::Function::create(
|
auto newFunction =
|
||||||
f.getLoc(), functionToSpecialize.mangledName, type, f.getAttrs());
|
mlir::Function::create(f.getLoc(), functionToSpecialize.mangledName,
|
||||||
|
type, f.getDialectAttrs());
|
||||||
moduleManager.insert(newFunction);
|
moduleManager.insert(newFunction);
|
||||||
|
|
||||||
// Clone the function body
|
// Clone the function body
|
||||||
|
|
|
@ -52,7 +52,9 @@ These transformation are applied to all levels of IR:
|
||||||
|
|
||||||
These transformations are applied to builtin ops:
|
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.
|
* (TODO) Merge `affine.apply` operations that directly feed each other.
|
||||||
|
|
||||||
## Standard Ops Canonicalizations
|
## Standard Ops Canonicalizations
|
||||||
|
|
|
@ -93,16 +93,14 @@ InFlightDiagnostic emit(Location loc, DiagnosticSeverity severity);
|
||||||
```
|
```
|
||||||
|
|
||||||
Using the `DiagnosticEngine`, though, is generally not the preferred way to emit
|
Using the `DiagnosticEngine`, though, is generally not the preferred way to emit
|
||||||
diagnostics in MLIR. [`function`](LangRef.md#functions), and
|
diagnostics in MLIR. [`operation`](LangRef.md#operations) provides utility
|
||||||
[`operation`](LangRef.md#operations) both provide utility methods for emitting
|
methods for emitting diagnostics:
|
||||||
diagnostics:
|
|
||||||
|
|
||||||
```c++
|
```c++
|
||||||
// `emit` methods available in the mlir namespace.
|
// `emit` methods available in the mlir namespace.
|
||||||
InFlightDiagnostic emitError/Remark/Warning(Location);
|
InFlightDiagnostic emitError/Remark/Warning(Location);
|
||||||
|
|
||||||
// These methods use the location attached to the function/operation.
|
// These methods use the location attached to the operation.
|
||||||
InFlightDiagnostic Function::emitError/Remark/Warning();
|
|
||||||
InFlightDiagnostic Operation::emitError/Remark/Warning();
|
InFlightDiagnostic Operation::emitError/Remark/Warning();
|
||||||
|
|
||||||
// This method creates a diagnostic prefixed with "'op-name' op ".
|
// This method creates a diagnostic prefixed with "'op-name' op ".
|
||||||
|
|
|
@ -280,7 +280,7 @@ Example:
|
||||||
#set42 = (d0, d1)[s0, s1]
|
#set42 = (d0, d1)[s0, s1]
|
||||||
: d0 >= 0, -d0 + s0 - 1 >= 0, d1 >= 0, -d1 + s1 - 1 >= 0
|
: 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] {
|
affine.if #set42(%i, %j)[%M, %N] {
|
||||||
...
|
...
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,9 +27,10 @@ document describes the human-readable textual form.
|
||||||
|
|
||||||
## High-Level Structure
|
## High-Level Structure
|
||||||
|
|
||||||
The top-level unit of code in MLIR is a [Module](#module). A module contains a
|
The unit of code in MLIR is an [Operation](#operation). Operations allow for
|
||||||
list of [Functions](#functions). Functions are represented as a composition of
|
representing many different concepts, including the high-level [Module](#module)
|
||||||
[Operations](#operations) and contain a Control Flow Graph (CFG) of
|
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
|
[Blocks](#blocks), which contain operations and end with
|
||||||
[terminator operations](#terminator-operations) (like branches).
|
[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
|
where a variable is never allowed - e.g. the index of a
|
||||||
[`dim` operation](#dim-operation), or the stride of a convolution. They consist
|
[`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
|
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
|
attach attributes to operations. The set of expected attributes, their
|
||||||
expected attributes, their structure, and their interpretation are all
|
structure, and their interpretation are all contextually dependent on what they
|
||||||
contextually dependent on what they are attached to.
|
are attached to.
|
||||||
|
|
||||||
There are two main classes of attributes; dependent and dialect. Dependent
|
There are two main classes of attributes; dependent and dialect. Dependent
|
||||||
attributes derive their structure and meaning from what they are attached to,
|
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`
|
self/context parameter. The context of this attribute is defined by the `swift`
|
||||||
dialect and not the function argument.
|
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
|
### Operation Attributes
|
||||||
|
|
||||||
Operations, unlike functions and function arguments, may include both dialect
|
Operations, unlike functions and function arguments, may include both dialect
|
||||||
|
@ -913,22 +905,21 @@ func @simple_form(i1 {unitAttr})
|
||||||
## Module
|
## Module
|
||||||
|
|
||||||
``` {.ebnf}
|
``` {.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
|
An MLIR module represents an opaque top-level container operation. It contains a
|
||||||
mappings) at the top of the file, but is principally made up of a list of
|
single region containing a single block that is comprised of any operations.
|
||||||
functions.
|
Operations within this region must not implicitly capture values defined above
|
||||||
|
it.
|
||||||
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).
|
|
||||||
|
|
||||||
## Functions
|
## Functions
|
||||||
|
|
||||||
MLIR functions have a signature (including argument and result types) and
|
An MLIR Function is an operation with a name containing one [region](#regions)
|
||||||
associated attributes according to the following grammar:
|
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}
|
``` {.ebnf}
|
||||||
function ::= `func` function-signature function-attributes? function-body?
|
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
|
An external function declaration (used when referring to a function declared in
|
||||||
some other module) has no body. A function definition contains a
|
some other module) has no body. While the MLIR textual form provides a nice
|
||||||
[region](#regions) made up of one or more blocks forming the function body.
|
inline syntax for function arguments, they are internally represented as "block
|
||||||
While the MLIR textual form provides a nice inline syntax for function
|
arguments" to the first block in the region.
|
||||||
arguments, they are internally represented as "block arguments" to the first
|
|
||||||
block in the region.
|
|
||||||
|
|
||||||
Examples:
|
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
|
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
|
connected blocks, where the semantics is not imposed by the IR. Instead, the
|
||||||
containing entity (operation or function) defines the semantics of the regions
|
containing operation defines the semantics of the regions it contains. Regions
|
||||||
it contains. Regions do not have a name or an address, only the blocks contained
|
do not have a name or an address, only the blocks contained in a region do.
|
||||||
in a region do. Regions are meaningless outside of the containing entity and
|
Regions are meaningless outside of the containing entity and have no type or
|
||||||
have no type or attributes.
|
attributes.
|
||||||
|
|
||||||
The first block in the region cannot be a successor of any other block. The
|
The first block in the region cannot be a successor of any other block. The
|
||||||
syntax for the region is as follows:
|
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
|
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
|
“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
|
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
|
region may be passed to or returned from a region, at which point the values
|
||||||
values defined in dominating blocks are no longer accessible. If this region
|
defined in dominating blocks are no longer accessible. If this region directly
|
||||||
directly uses such values, passing a value “containing” it across function
|
uses such values, passing a value “containing” it across function boundaries or
|
||||||
boundaries or using it in operations leads to undefined behavior. This is
|
using it in operations leads to undefined behavior. This is similar to returning
|
||||||
similar to returning a lambda capturing a reference to a local variable in C++.
|
a lambda capturing a reference to a local variable in C++. Note that if an
|
||||||
Note that if an operation triggers asynchronous execution of the region, it is
|
operation triggers asynchronous execution of the region, it is under the
|
||||||
under the responsibility of the operation caller to wait for the region to be
|
responsibility of the operation caller to wait for the region to be executed
|
||||||
executed guaranteeing that any directly used values remain live.
|
guaranteeing that any directly used values remain live.
|
||||||
|
|
||||||
### Arguments and Results
|
### Arguments and Results
|
||||||
|
|
||||||
The arguments of the first block of a region are treated as arguments of the
|
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. The source of these arguments is defined by the semantics of the parent
|
||||||
region. If a region is a function body, its arguments are the function
|
operation. They may correspond to some of the values the operation itself uses.
|
||||||
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.
|
|
||||||
|
|
||||||
Regions produce a (possibly empty) list of values. For function body regions,
|
Regions produce a (possibly empty) list of values. The operation semantics
|
||||||
`return` is the standard region-exiting terminator, but dialects can provide
|
|
||||||
their own. For regions that belong to an operation, the operation semantics
|
|
||||||
defines the relation between the region results and the operation results.
|
defines the relation between the region results and the operation results.
|
||||||
|
|
||||||
## Blocks
|
## Blocks
|
||||||
|
@ -1157,9 +1141,10 @@ successor-list ::= successor (`,` successor)*
|
||||||
region-list ::= region (`,` region)*
|
region-list ::= region (`,` region)*
|
||||||
```
|
```
|
||||||
|
|
||||||
MLIR represents computations within functions with a uniform concept called
|
MLIR introduces a uniform concept called _operations_ to enable describing many
|
||||||
_operations_. Operations in MLIR are fully extensible (there is no fixed list of
|
different levels of abstractions and computations. Operations in MLIR are fully
|
||||||
operations), and have application-specific semantics. For example, MLIR supports
|
extensible (there is no fixed list of operations), and have application-specific
|
||||||
|
semantics. For example, MLIR supports
|
||||||
[target-independent operations](#memory-operations),
|
[target-independent operations](#memory-operations),
|
||||||
[affine operations](Dialects/Affine.md), and
|
[affine operations](Dialects/Affine.md), and
|
||||||
[target-specific machine operations](#target-specific-operations).
|
[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
|
block. The count and types of operands to the branch must align with the
|
||||||
arguments in the target block.
|
arguments in the target block.
|
||||||
|
|
||||||
The MLIR branch operation is not allowed to target the entry block for a
|
The MLIR branch operation is not allowed to target the entry block for a region.
|
||||||
function.
|
|
||||||
|
|
||||||
##### 'cond_br' terminator operation
|
##### '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.
|
operands must align with the arguments in the corresponding target blocks.
|
||||||
|
|
||||||
The MLIR conditional branch operation is not allowed to target the entry block
|
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.
|
allowed to be the same.
|
||||||
|
|
||||||
The following example illustrates a function with a conditional branch operation
|
The following example illustrates a function with a conditional branch operation
|
||||||
|
|
|
@ -168,7 +168,7 @@ change.
|
||||||
|
|
||||||
### Block Arguments vs PHI nodes
|
### 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.
|
than [PHI instructions](http://llvm.org/docs/LangRef.html#i-phi) used in LLVM.
|
||||||
This choice is representationally identical (the same constructs can be
|
This choice is representationally identical (the same constructs can be
|
||||||
represented in either form) but block arguments have several advantages:
|
represented in either form) but block arguments have several advantages:
|
||||||
|
|
|
@ -143,7 +143,7 @@ above, let's see some examples:
|
||||||
/// An interesting function analysis.
|
/// An interesting function analysis.
|
||||||
struct MyFunctionAnalysis {
|
struct MyFunctionAnalysis {
|
||||||
// Compute this analysis with the provided function.
|
// Compute this analysis with the provided function.
|
||||||
MyFunctionAnalysis(Function *function);
|
MyFunctionAnalysis(Function function);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An interesting module analysis.
|
/// An interesting module analysis.
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//===- Dominance.h - Dominator analysis for CFG Functions -------*- C++ -*-===//
|
//===- Dominance.h - Dominator analysis for CFGs ----------------*- C++ -*-===//
|
||||||
//
|
//
|
||||||
// Copyright 2019 The MLIR Authors.
|
// Copyright 2019 The MLIR Authors.
|
||||||
//
|
//
|
||||||
|
@ -26,7 +26,6 @@ extern template class llvm::DominatorTreeBase<mlir::Block, true>;
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
using DominanceInfoNode = llvm::DomTreeNodeBase<Block>;
|
using DominanceInfoNode = llvm::DomTreeNodeBase<Block>;
|
||||||
class Function;
|
|
||||||
class Operation;
|
class Operation;
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
@ -34,7 +33,6 @@ template <bool IsPostDom> class DominanceInfoBase {
|
||||||
using base = llvm::DominatorTreeBase<Block, IsPostDom>;
|
using base = llvm::DominatorTreeBase<Block, IsPostDom>;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DominanceInfoBase(Function function);
|
|
||||||
DominanceInfoBase(Operation *op) { recalculate(op); }
|
DominanceInfoBase(Operation *op) { recalculate(op); }
|
||||||
DominanceInfoBase(DominanceInfoBase &&) = default;
|
DominanceInfoBase(DominanceInfoBase &&) = default;
|
||||||
DominanceInfoBase &operator=(DominanceInfoBase &&) = default;
|
DominanceInfoBase &operator=(DominanceInfoBase &&) = default;
|
||||||
|
@ -43,7 +41,6 @@ public:
|
||||||
DominanceInfoBase &operator=(const DominanceInfoBase &) = delete;
|
DominanceInfoBase &operator=(const DominanceInfoBase &) = delete;
|
||||||
|
|
||||||
/// Recalculate the dominance info.
|
/// Recalculate the dominance info.
|
||||||
void recalculate(Function function);
|
|
||||||
void recalculate(Operation *op);
|
void recalculate(Operation *op);
|
||||||
|
|
||||||
/// Get the root dominance node of the given region.
|
/// Get the root dominance node of the given region.
|
||||||
|
|
|
@ -19,21 +19,13 @@
|
||||||
#define MLIR_ANALYSIS_VERIFIER_H
|
#define MLIR_ANALYSIS_VERIFIER_H
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class Function;
|
|
||||||
class LogicalResult;
|
class LogicalResult;
|
||||||
class Module;
|
|
||||||
class Operation;
|
class Operation;
|
||||||
|
|
||||||
/// Perform (potentially expensive) checks of invariants, used to detect
|
/// Perform (potentially expensive) checks of invariants, used to detect
|
||||||
/// compiler bugs, on this operation and any nested operations. On error, this
|
/// compiler bugs, on this operation and any nested operations. On error, this
|
||||||
/// reports the error through the MLIRContext and returns failure.
|
/// reports the error through the MLIRContext and returns failure.
|
||||||
LogicalResult verify(Operation *op);
|
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
|
} // end namespace mlir
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -25,7 +25,8 @@
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
|
|
||||||
class ModulePassBase;
|
class ModulePassBase;
|
||||||
class Function;
|
class FuncOp;
|
||||||
|
using Function = FuncOp;
|
||||||
|
|
||||||
using OwnedCubin = std::unique_ptr<std::vector<char>>;
|
using OwnedCubin = std::unique_ptr<std::vector<char>>;
|
||||||
using CubinGenerator =
|
using CubinGenerator =
|
||||||
|
|
|
@ -28,7 +28,8 @@ class Module;
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class DialectConversion;
|
class DialectConversion;
|
||||||
class LLVMTypeConverter;
|
class LLVMTypeConverter;
|
||||||
class Module;
|
class ModuleOp;
|
||||||
|
using Module = ModuleOp;
|
||||||
class ModulePassBase;
|
class ModulePassBase;
|
||||||
class RewritePattern;
|
class RewritePattern;
|
||||||
class Type;
|
class Type;
|
||||||
|
|
|
@ -36,7 +36,8 @@ class Module;
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
|
|
||||||
class Module;
|
class ModuleOp;
|
||||||
|
using Module = ModuleOp;
|
||||||
|
|
||||||
namespace impl {
|
namespace impl {
|
||||||
class OrcJIT;
|
class OrcJIT;
|
||||||
|
|
|
@ -31,7 +31,8 @@ template <typename T> class Expected;
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
|
|
||||||
class Function;
|
class FuncOp;
|
||||||
|
using Function = FuncOp;
|
||||||
|
|
||||||
/// Simple memref descriptor class compatible with the ABI of functions emitted
|
/// Simple memref descriptor class compatible with the ABI of functions emitted
|
||||||
/// by MLIR to LLVM IR conversion for statically-shaped memrefs of float type.
|
/// by MLIR to LLVM IR conversion for statically-shaped memrefs of float type.
|
||||||
|
|
|
@ -24,7 +24,8 @@
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class AffineMap;
|
class AffineMap;
|
||||||
class Dialect;
|
class Dialect;
|
||||||
class Function;
|
class FuncOp;
|
||||||
|
using Function = FuncOp;
|
||||||
class FunctionType;
|
class FunctionType;
|
||||||
class Identifier;
|
class Identifier;
|
||||||
class IntegerSet;
|
class IntegerSet;
|
||||||
|
|
|
@ -25,7 +25,8 @@ namespace mlir {
|
||||||
|
|
||||||
class AffineExpr;
|
class AffineExpr;
|
||||||
class BlockAndValueMapping;
|
class BlockAndValueMapping;
|
||||||
class Module;
|
class ModuleOp;
|
||||||
|
using Module = ModuleOp;
|
||||||
class UnknownLoc;
|
class UnknownLoc;
|
||||||
class FileLineColLoc;
|
class FileLineColLoc;
|
||||||
class Type;
|
class Type;
|
||||||
|
|
|
@ -27,114 +27,94 @@
|
||||||
#include "llvm/ADT/SmallString.h"
|
#include "llvm/ADT/SmallString.h"
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class BlockAndValueMapping;
|
class ModuleOp;
|
||||||
class FunctionType;
|
|
||||||
class Function;
|
|
||||||
class MLIRContext;
|
|
||||||
class Module;
|
|
||||||
|
|
||||||
namespace detail {
|
//===--------------------------------------------------------------------===//
|
||||||
class ModuleStorage;
|
// Function Operation.
|
||||||
|
//===--------------------------------------------------------------------===//
|
||||||
|
|
||||||
/// This class represents all of the internal state of a Function. This allows
|
/// FuncOp represents a function, or an operation containing one region that
|
||||||
/// for the Function class to be value typed.
|
/// forms a CFG(Control Flow Graph). The region of a function is not allowed to
|
||||||
class FunctionStorage
|
/// implicitly capture global values, and all external references must use
|
||||||
: public llvm::ilist_node_with_parent<FunctionStorage, ModuleStorage> {
|
/// Function arguments or attributes that establish a symbolic connection(e.g.
|
||||||
FunctionStorage(Location location, StringRef name, FunctionType type,
|
/// symbols referenced by name via a string attribute).
|
||||||
ArrayRef<NamedAttribute> attrs = {});
|
class FuncOp : public Op<FuncOp, OpTrait::ZeroOperands, OpTrait::ZeroResult,
|
||||||
FunctionStorage(Location location, StringRef name, FunctionType type,
|
OpTrait::IsIsolatedFromAbove> {
|
||||||
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 {
|
|
||||||
public:
|
public:
|
||||||
Function(detail::FunctionStorage *impl = nullptr) : impl(impl) {}
|
using Op::Op;
|
||||||
|
using Op::print;
|
||||||
|
|
||||||
static Function create(Location location, StringRef name, FunctionType type,
|
static StringRef getOperationName() { return "func"; }
|
||||||
ArrayRef<NamedAttribute> attrs = {}) {
|
|
||||||
return new detail::FunctionStorage(location, name, type, attrs);
|
static FuncOp create(Location location, StringRef name, FunctionType type,
|
||||||
}
|
ArrayRef<NamedAttribute> attrs = {});
|
||||||
static Function create(Location location, StringRef name, FunctionType type,
|
static FuncOp create(Location location, StringRef name, FunctionType type,
|
||||||
ArrayRef<NamedAttribute> attrs,
|
llvm::iterator_range<dialect_attr_iterator> attrs);
|
||||||
ArrayRef<NamedAttributeList> argAttrs) {
|
static FuncOp create(Location location, StringRef name, FunctionType type,
|
||||||
return new detail::FunctionStorage(location, name, type, attrs, argAttrs);
|
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.
|
/// Returns the type of this function.
|
||||||
operator bool() const { return impl; }
|
FunctionType getType() {
|
||||||
bool operator==(Function other) const { return impl == other.impl; }
|
return getAttrOfType<TypeAttr>("type").getValue().cast<FunctionType>();
|
||||||
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; }
|
|
||||||
|
|
||||||
/// Change the type of this function in place. This is an extremely dangerous
|
/// 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
|
/// operation and it is up to the caller to ensure that this is legal for this
|
||||||
/// function, and to restore invariants:
|
/// 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
|
/// - the arguments attributes may need an update: if the new type has less
|
||||||
/// parameters we drop the extra attributes, if there are more parameters
|
/// parameters we drop the extra attributes, if there are more parameters
|
||||||
/// they won't have any attributes.
|
/// they won't have any attributes.
|
||||||
void setType(FunctionType newType) {
|
void setType(FunctionType newType) {
|
||||||
impl->type = newType;
|
setAttr("type", TypeAttr::get(newType));
|
||||||
impl->argAttrs.resize(newType.getNumInputs());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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.
|
/// Returns true if this function is external, i.e. it has no body.
|
||||||
bool isExternal() { return empty(); }
|
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
|
// Body Handling
|
||||||
//===--------------------------------------------------------------------===//
|
//===--------------------------------------------------------------------===//
|
||||||
|
|
||||||
Region &getBody() { return impl->body; }
|
Region &getBody() { return getOperation()->getRegion(0); }
|
||||||
|
|
||||||
void eraseBody() { getBody().getBlocks().clear(); }
|
void eraseBody() { getBody().getBlocks().clear(); }
|
||||||
|
|
||||||
/// This is the list of blocks in the function.
|
/// This is the list of blocks in the function.
|
||||||
|
@ -157,266 +137,9 @@ public:
|
||||||
Block &back() { return getBody().back(); }
|
Block &back() { return getBody().back(); }
|
||||||
Block &front() { return getBody().front(); }
|
Block &front() { return getBody().front(); }
|
||||||
|
|
||||||
//===--------------------------------------------------------------------===//
|
/// Add an entry block to an empty function, and set up the block arguments
|
||||||
// Operation Walkers
|
/// to match the signature of the function.
|
||||||
//===--------------------------------------------------------------------===//
|
void addEntryBlock();
|
||||||
|
|
||||||
/// 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(); }
|
|
||||||
|
|
||||||
//===--------------------------------------------------------------------===//
|
//===--------------------------------------------------------------------===//
|
||||||
// Argument Handling
|
// Argument Handling
|
||||||
|
@ -516,57 +239,36 @@ private:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Temporary forward declaration of FuncOp to the legacy Function.
|
||||||
|
using Function = FuncOp;
|
||||||
} // end namespace mlir
|
} // end namespace mlir
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
// ilist_traits for Function
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
namespace llvm {
|
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.
|
// Functions hash just like pointers.
|
||||||
template <> struct DenseMapInfo<mlir::Function> {
|
template <> struct DenseMapInfo<mlir::FuncOp> {
|
||||||
static mlir::Function getEmptyKey() {
|
static mlir::FuncOp getEmptyKey() {
|
||||||
auto pointer = llvm::DenseMapInfo<void *>::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();
|
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());
|
return hash_value(val.getAsOpaquePointer());
|
||||||
}
|
}
|
||||||
static bool isEqual(mlir::Function LHS, mlir::Function RHS) {
|
static bool isEqual(mlir::FuncOp LHS, mlir::FuncOp RHS) { return LHS == RHS; }
|
||||||
return LHS == RHS;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Allow stealing the low bits of FunctionStorage.
|
/// Allow stealing the low bits of FuncOp.
|
||||||
template <> struct PointerLikeTypeTraits<mlir::Function> {
|
template <> struct PointerLikeTypeTraits<mlir::FuncOp> {
|
||||||
public:
|
public:
|
||||||
static inline void *getAsVoidPointer(mlir::Function I) {
|
static inline void *getAsVoidPointer(mlir::FuncOp I) {
|
||||||
return const_cast<void *>(I.getAsOpaquePointer());
|
return const_cast<void *>(I.getAsOpaquePointer());
|
||||||
}
|
}
|
||||||
static inline mlir::Function getFromVoidPointer(void *P) {
|
static inline mlir::FuncOp getFromVoidPointer(void *P) {
|
||||||
return mlir::Function::getFromOpaquePointer(P);
|
return mlir::FuncOp::getFromOpaquePointer(P);
|
||||||
}
|
}
|
||||||
enum { NumLowBitsAvailable = 3 };
|
enum { NumLowBitsAvailable = 3 };
|
||||||
};
|
};
|
||||||
|
|
|
@ -24,88 +24,76 @@
|
||||||
|
|
||||||
#include "mlir/IR/Function.h"
|
#include "mlir/IR/Function.h"
|
||||||
#include "mlir/IR/SymbolTable.h"
|
#include "mlir/IR/SymbolTable.h"
|
||||||
#include "llvm/ADT/ilist.h"
|
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class Module;
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Module Operation.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
namespace detail {
|
/// ModuleOp represents a module, or an operation containing one region with a
|
||||||
class ModuleStorage {
|
/// single block containing opaque operations. The region of a module is not
|
||||||
explicit ModuleStorage(MLIRContext *context) : context(context) {}
|
/// allowed to implicitly capture global values, and all external references
|
||||||
|
/// must use symbolic references via attributes(e.g. via a string name).
|
||||||
/// getSublistAccess() - Returns pointer to member of function list
|
class ModuleOp : public Op<ModuleOp, OpTrait::ZeroOperands, OpTrait::ZeroResult,
|
||||||
static llvm::iplist<FunctionStorage> ModuleStorage::*
|
OpTrait::IsIsolatedFromAbove> {
|
||||||
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 {
|
|
||||||
public:
|
public:
|
||||||
Module(detail::ModuleStorage *impl = nullptr) : impl(impl) {}
|
using Op::Op;
|
||||||
|
using Op::print;
|
||||||
|
|
||||||
/// Construct a new module object with the given context.
|
static StringRef getOperationName() { return "module"; }
|
||||||
static Module create(MLIRContext *context) {
|
|
||||||
return new detail::ModuleStorage(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
MLIRContext *getContext() { return impl->context; }
|
static void build(Builder *builder, OperationState *result);
|
||||||
|
|
||||||
/// Allow converting a Module to bool for null checks.
|
/// Construct a module from the given context.
|
||||||
operator bool() const { return impl; }
|
static ModuleOp create(MLIRContext *context);
|
||||||
bool operator==(Module other) const { return impl == other.impl; }
|
|
||||||
bool operator!=(Module other) const { return !(*this == other); }
|
|
||||||
|
|
||||||
/// An iterator class used to iterate over the held functions.
|
/// Operation hooks.
|
||||||
class iterator : public llvm::mapped_iterator<
|
static ParseResult parse(OpAsmParser *parser, OperationState *result);
|
||||||
llvm::iplist<detail::FunctionStorage>::iterator,
|
void print(OpAsmPrinter *p);
|
||||||
Function (*)(detail::FunctionStorage &)> {
|
LogicalResult verify();
|
||||||
static Function unwrap(detail::FunctionStorage &impl) { return &impl; }
|
|
||||||
|
|
||||||
public:
|
/// Return body of this module.
|
||||||
using reference = Function;
|
Region &getBodyRegion();
|
||||||
|
Block *getBody();
|
||||||
|
|
||||||
/// Initializes the operand type iterator to the specified operand iterator.
|
/// Print the this module in the custom top-level form.
|
||||||
iterator(llvm::iplist<detail::FunctionStorage>::iterator it)
|
void print(raw_ostream &os);
|
||||||
: llvm::mapped_iterator<llvm::iplist<detail::FunctionStorage>::iterator,
|
void dump();
|
||||||
Function (*)(detail::FunctionStorage &)>(
|
|
||||||
it, &unwrap) {}
|
|
||||||
iterator(Function it)
|
|
||||||
: iterator(llvm::iplist<detail::FunctionStorage>::iterator(it.impl)) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
/// 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.
|
// Iteration over the functions in the module.
|
||||||
iterator begin() { return impl->functions.begin(); }
|
iterator begin() { return getBody()->op_begin<FuncOp>(); }
|
||||||
iterator end() { return impl->functions.end(); }
|
iterator end() { return getBody()->op_end<FuncOp>(); }
|
||||||
Function front() { return &impl->functions.front(); }
|
Function front() { return *begin(); }
|
||||||
Function back() { return &impl->functions.back(); }
|
Function back() { return *std::prev(end()); }
|
||||||
void clear() { impl->functions.clear(); }
|
|
||||||
|
|
||||||
void push_back(Function fn) { impl->functions.push_back(fn.impl); }
|
/// This is the list of functions in the module.
|
||||||
void insert(iterator insertPt, Function fn) {
|
llvm::iterator_range<iterator> getFunctions() {
|
||||||
impl->functions.insert(insertPt.getCurrent(), fn.impl);
|
return getBody()->getOps<FuncOp>();
|
||||||
}
|
|
||||||
/// Splice all of the functions from 'other' into this module.
|
|
||||||
void splice(iterator insertPt, Module other) {
|
|
||||||
impl->functions.splice(insertPt.getCurrent(), other.impl->functions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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
|
/// Look up a function with the specified name, returning null if no such
|
||||||
/// name exists. Function names never include the @ on them. Note: This
|
/// 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
|
/// name exists. Function names never include the @ on them. Note: This
|
||||||
/// performs a linear scan of held symbols.
|
/// performs a linear scan of held symbols.
|
||||||
Function getNamedFunction(Identifier name) {
|
Function getNamedFunction(Identifier name) {
|
||||||
auto &functions = impl->functions;
|
auto it = llvm::find_if(getFunctions(),
|
||||||
auto it = llvm::find_if(functions, [name](detail::FunctionStorage &fn) {
|
[name](FuncOp fn) { return fn.getName() == name; });
|
||||||
return Function(&fn).getName() == name;
|
return it == end() ? nullptr : *it;
|
||||||
});
|
|
||||||
return it == functions.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
|
/// The ModuleTerminatorOp is a special terminator operation for the body of a
|
||||||
|
@ -271,18 +129,103 @@ public:
|
||||||
LogicalResult verify();
|
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
|
} // end namespace mlir
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
|
||||||
/// Allow stealing the low bits of ModuleStorage.
|
/// Allow stealing the low bits of ModuleOp.
|
||||||
template <> struct PointerLikeTypeTraits<mlir::Module> {
|
template <> struct PointerLikeTypeTraits<mlir::ModuleOp> {
|
||||||
public:
|
public:
|
||||||
static inline void *getAsVoidPointer(mlir::Module I) {
|
static inline void *getAsVoidPointer(mlir::ModuleOp I) {
|
||||||
return const_cast<void *>(I.getAsOpaquePointer());
|
return const_cast<void *>(I.getAsOpaquePointer());
|
||||||
}
|
}
|
||||||
static inline mlir::Module getFromVoidPointer(void *P) {
|
static inline mlir::ModuleOp getFromVoidPointer(void *P) {
|
||||||
return mlir::Module::getFromOpaquePointer(P);
|
return mlir::ModuleOp::getFromOpaquePointer(P);
|
||||||
}
|
}
|
||||||
enum { NumLowBitsAvailable = 3 };
|
enum { NumLowBitsAvailable = 3 };
|
||||||
};
|
};
|
||||||
|
|
|
@ -850,8 +850,15 @@ public:
|
||||||
|
|
||||||
/// This is a public constructor to enable access via the llvm::cast family of
|
/// This is a public constructor to enable access via the llvm::cast family of
|
||||||
/// methods. This should not be used directly.
|
/// methods. This should not be used directly.
|
||||||
explicit Op(Operation *state) : OpState(state) {
|
explicit Op(Operation *state) : OpState(state) {}
|
||||||
assert(!state || isa<ConcreteOpType>(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:
|
private:
|
||||||
|
|
|
@ -27,16 +27,11 @@
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class BlockAndValueMapping;
|
class BlockAndValueMapping;
|
||||||
|
|
||||||
namespace detail {
|
/// This class contains a list of basic blocks and a link to the parent
|
||||||
class FunctionStorage;
|
/// operation it is attached to.
|
||||||
}
|
|
||||||
|
|
||||||
/// This class contains a list of basic blocks and has a notion of the object it
|
|
||||||
/// is part of - a Function or an Operation.
|
|
||||||
class Region {
|
class Region {
|
||||||
public:
|
public:
|
||||||
Region() = default;
|
Region() = default;
|
||||||
explicit Region(Function container);
|
|
||||||
explicit Region(Operation *container);
|
explicit Region(Operation *container);
|
||||||
~Region();
|
~Region();
|
||||||
|
|
||||||
|
@ -76,10 +71,20 @@ public:
|
||||||
/// region, i.e. a function body region.
|
/// region, i.e. a function body region.
|
||||||
Region *getContainingRegion();
|
Region *getContainingRegion();
|
||||||
|
|
||||||
/// A Region is either a function body or a part of an operation. If it is
|
/// Return the parent operation this region is attached to.
|
||||||
/// part of an operation, then return the operation, otherwise return null.
|
|
||||||
Operation *getContainingOp();
|
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 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.
|
/// a Function body, then return this function, otherwise return null.
|
||||||
Function getContainingFunction();
|
Function getContainingFunction();
|
||||||
|
@ -130,7 +135,7 @@ private:
|
||||||
RegionType blocks;
|
RegionType blocks;
|
||||||
|
|
||||||
/// This is the object we are part of.
|
/// This is the object we are part of.
|
||||||
llvm::PointerUnion<detail::FunctionStorage *, Operation *> container;
|
Operation *container;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // end namespace mlir
|
} // end namespace mlir
|
||||||
|
|
|
@ -22,8 +22,9 @@
|
||||||
#include "llvm/ADT/DenseMap.h"
|
#include "llvm/ADT/DenseMap.h"
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class Function;
|
class FuncOp;
|
||||||
class Module;
|
using Function = FuncOp;
|
||||||
|
class ModuleOp;
|
||||||
class MLIRContext;
|
class MLIRContext;
|
||||||
|
|
||||||
/// This class represents the symbol table used by a module for function
|
/// This class represents the symbol table used by a module for function
|
||||||
|
@ -31,7 +32,7 @@ class MLIRContext;
|
||||||
class SymbolTable {
|
class SymbolTable {
|
||||||
public:
|
public:
|
||||||
/// Build a symbol table with the symbols within the given module.
|
/// 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
|
/// Look up a symbol with the specified name, returning null if no such
|
||||||
/// name exists. Names never include the @ on them.
|
/// name exists. Names never include the @ on them.
|
||||||
|
@ -55,7 +56,7 @@ private:
|
||||||
MLIRContext *context;
|
MLIRContext *context;
|
||||||
|
|
||||||
/// This is a mapping from a name to the function with that name.
|
/// 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.
|
/// This is used when name conflicts are detected.
|
||||||
unsigned uniquingCounter = 0;
|
unsigned uniquingCounter = 0;
|
||||||
|
|
|
@ -28,7 +28,8 @@
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class Block;
|
class Block;
|
||||||
class Function;
|
class FuncOp;
|
||||||
|
using Function = FuncOp;
|
||||||
class Operation;
|
class Operation;
|
||||||
class Region;
|
class Region;
|
||||||
class Value;
|
class Value;
|
||||||
|
|
|
@ -30,7 +30,8 @@ class StringRef;
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class Location;
|
class Location;
|
||||||
class Module;
|
class ModuleOp;
|
||||||
|
using Module = ModuleOp;
|
||||||
class MLIRContext;
|
class MLIRContext;
|
||||||
class Type;
|
class Type;
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,8 @@ class Any;
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class FunctionPassBase;
|
class FunctionPassBase;
|
||||||
class Module;
|
class ModuleOp;
|
||||||
|
using Module = ModuleOp;
|
||||||
class ModulePassBase;
|
class ModulePassBase;
|
||||||
class Pass;
|
class Pass;
|
||||||
class PassInstrumentation;
|
class PassInstrumentation;
|
||||||
|
|
|
@ -32,7 +32,8 @@ class Module;
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
|
|
||||||
class Module;
|
class ModuleOp;
|
||||||
|
using Module = ModuleOp;
|
||||||
|
|
||||||
/// Convert the given MLIR module into LLVM IR. The LLVM context is extracted
|
/// 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
|
/// from the registered LLVM IR dialect. In case of error, report it
|
||||||
|
|
|
@ -35,7 +35,8 @@
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class Attribute;
|
class Attribute;
|
||||||
class Location;
|
class Location;
|
||||||
class Module;
|
class ModuleOp;
|
||||||
|
using Module = ModuleOp;
|
||||||
class Operation;
|
class Operation;
|
||||||
|
|
||||||
namespace LLVM {
|
namespace LLVM {
|
||||||
|
|
|
@ -30,7 +30,8 @@ class Module;
|
||||||
} // namespace llvm
|
} // namespace llvm
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class Module;
|
class ModuleOp;
|
||||||
|
using Module = ModuleOp;
|
||||||
|
|
||||||
/// Convert the given MLIR module into NVVM IR. This conversion requires the
|
/// Convert the given MLIR module into NVVM IR. This conversion requires the
|
||||||
/// registration of the LLVM IR dialect and will extract the LLVM context
|
/// registration of the LLVM IR dialect and will extract the LLVM context
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
#include "mlir/IR/Dialect.h"
|
#include "mlir/IR/Dialect.h"
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class Function;
|
class FuncOp;
|
||||||
|
using Function = FuncOp;
|
||||||
class Operation;
|
class Operation;
|
||||||
class Value;
|
class Value;
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,8 @@
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class AffineMap;
|
class AffineMap;
|
||||||
class AffineForOp;
|
class AffineForOp;
|
||||||
class Function;
|
class FuncOp;
|
||||||
|
using Function = FuncOp;
|
||||||
class OpBuilder;
|
class OpBuilder;
|
||||||
class Value;
|
class Value;
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,8 @@
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class AffineExpr;
|
class AffineExpr;
|
||||||
class AffineForOp;
|
class AffineForOp;
|
||||||
class Function;
|
class FuncOp;
|
||||||
|
using Function = FuncOp;
|
||||||
class Location;
|
class Location;
|
||||||
struct LogicalResult;
|
struct LogicalResult;
|
||||||
class OpBuilder;
|
class OpBuilder;
|
||||||
|
|
|
@ -35,7 +35,8 @@ namespace mlir {
|
||||||
class AffineApplyOp;
|
class AffineApplyOp;
|
||||||
class AffineForOp;
|
class AffineForOp;
|
||||||
class Location;
|
class Location;
|
||||||
class Module;
|
class ModuleOp;
|
||||||
|
using Module = ModuleOp;
|
||||||
class OpBuilder;
|
class OpBuilder;
|
||||||
|
|
||||||
/// Replaces all "deferencing" uses of oldMemRef with newMemRef while optionally
|
/// Replaces all "deferencing" uses of oldMemRef with newMemRef while optionally
|
||||||
|
|
|
@ -26,7 +26,8 @@
|
||||||
|
|
||||||
namespace mlir {
|
namespace mlir {
|
||||||
class MLIRContext;
|
class MLIRContext;
|
||||||
class Module;
|
class ModuleOp;
|
||||||
|
using Module = ModuleOp;
|
||||||
class OwningModuleRef;
|
class OwningModuleRef;
|
||||||
|
|
||||||
/// Interface of the function that translates a file to MLIR. The
|
/// Interface of the function that translates a file to MLIR. The
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
//===- Dominance.cpp - Dominator analysis for functions -------------------===//
|
//===- Dominance.cpp - Dominator analysis for CFGs ------------------------===//
|
||||||
//
|
//
|
||||||
// Copyright 2019 The MLIR Authors.
|
// Copyright 2019 The MLIR Authors.
|
||||||
//
|
//
|
||||||
|
@ -21,7 +21,6 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "mlir/Analysis/Dominance.h"
|
#include "mlir/Analysis/Dominance.h"
|
||||||
#include "mlir/IR/Function.h"
|
|
||||||
#include "mlir/IR/Operation.h"
|
#include "mlir/IR/Operation.h"
|
||||||
#include "llvm/Support/GenericDomTreeConstruction.h"
|
#include "llvm/Support/GenericDomTreeConstruction.h"
|
||||||
|
|
||||||
|
@ -36,34 +35,6 @@ template class llvm::DomTreeNodeBase<Block>;
|
||||||
// DominanceInfoBase
|
// 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 ®ion : 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(®ion, std::move(opDominance));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
template <bool IsPostDom>
|
template <bool IsPostDom>
|
||||||
void DominanceInfoBase<IsPostDom>::recalculate(Operation *op) {
|
void DominanceInfoBase<IsPostDom>::recalculate(Operation *op) {
|
||||||
dominanceInfos.clear();
|
dominanceInfos.clear();
|
||||||
|
@ -88,6 +59,10 @@ bool DominanceInfoBase<IsPostDom>::properlyDominates(Block *a, Block *b) {
|
||||||
if (a == b)
|
if (a == b)
|
||||||
return false;
|
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
|
// 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
|
// 'b' is defined in an operation region that (recursively) ends up being
|
||||||
// dominated by 'a'. Walk up the list of containers enclosing B.
|
// 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;
|
Operation *bAncestor;
|
||||||
do {
|
do {
|
||||||
bAncestor = regionB->getContainingOp();
|
bAncestor = regionB->getContainingOp();
|
||||||
// If 'bAncestor' is the top level function, then 'a' is a block
|
// If 'bAncestor' is the top level region, then 'a' is a block that post
|
||||||
// that post dominates 'b'.
|
// dominates 'b'.
|
||||||
if (!bAncestor)
|
if (!bAncestor || !bAncestor->getBlock())
|
||||||
return IsPostDom;
|
return IsPostDom;
|
||||||
|
|
||||||
regionB = bAncestor->getBlock()->getParent();
|
regionB = bAncestor->getBlock()->getParent();
|
||||||
|
@ -131,6 +106,10 @@ template class mlir::detail::DominanceInfoBase</*IsPostDom=*/false>;
|
||||||
bool DominanceInfo::properlyDominates(Operation *a, Operation *b) {
|
bool DominanceInfo::properlyDominates(Operation *a, Operation *b) {
|
||||||
auto *aBlock = a->getBlock(), *bBlock = b->getBlock();
|
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 the blocks are the same, then check if b is before a in the block.
|
||||||
if (aBlock == bBlock)
|
if (aBlock == bBlock)
|
||||||
return a->isBeforeInBlock(b);
|
return a->isBeforeInBlock(b);
|
||||||
|
@ -165,6 +144,10 @@ bool DominanceInfo::properlyDominates(Value *a, Operation *b) {
|
||||||
bool PostDominanceInfo::properlyPostDominates(Operation *a, Operation *b) {
|
bool PostDominanceInfo::properlyPostDominates(Operation *a, Operation *b) {
|
||||||
auto *aBlock = a->getBlock(), *bBlock = b->getBlock();
|
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 the blocks are the same, check if b is before a in the block.
|
||||||
if (aBlock == bBlock)
|
if (aBlock == bBlock)
|
||||||
return b->isBeforeInBlock(a);
|
return b->isBeforeInBlock(a);
|
||||||
|
|
|
@ -37,13 +37,10 @@
|
||||||
#include "mlir/Analysis/Dominance.h"
|
#include "mlir/Analysis/Dominance.h"
|
||||||
#include "mlir/IR/Attributes.h"
|
#include "mlir/IR/Attributes.h"
|
||||||
#include "mlir/IR/Dialect.h"
|
#include "mlir/IR/Dialect.h"
|
||||||
#include "mlir/IR/Function.h"
|
|
||||||
#include "mlir/IR/Module.h"
|
|
||||||
#include "mlir/IR/Operation.h"
|
#include "mlir/IR/Operation.h"
|
||||||
#include "llvm/Support/FormatVariadic.h"
|
#include "llvm/Support/FormatVariadic.h"
|
||||||
#include "llvm/Support/PrettyStackTrace.h"
|
#include "llvm/Support/PrettyStackTrace.h"
|
||||||
#include "llvm/Support/Regex.h"
|
#include "llvm/Support/Regex.h"
|
||||||
#include "llvm/Support/raw_ostream.h"
|
|
||||||
using namespace mlir;
|
using namespace mlir;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -53,9 +50,6 @@ public:
|
||||||
explicit OperationVerifier(MLIRContext *ctx)
|
explicit OperationVerifier(MLIRContext *ctx)
|
||||||
: ctx(ctx), identifierRegex("^[a-zA-Z_][a-zA-Z_0-9\\.\\$]*$") {}
|
: 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.
|
/// Verify the given operation.
|
||||||
LogicalResult verify(Operation &op);
|
LogicalResult verify(Operation &op);
|
||||||
|
|
||||||
|
@ -92,7 +86,7 @@ private:
|
||||||
/// The current context for the verifier.
|
/// The current context for the verifier.
|
||||||
MLIRContext *ctx;
|
MLIRContext *ctx;
|
||||||
|
|
||||||
/// Dominance information for this function, when checking dominance.
|
/// Dominance information for this operation, when checking dominance.
|
||||||
DominanceInfo *domInfo = nullptr;
|
DominanceInfo *domInfo = nullptr;
|
||||||
|
|
||||||
/// Regex checker for attribute names.
|
/// Regex checker for attribute names.
|
||||||
|
@ -104,25 +98,6 @@ private:
|
||||||
};
|
};
|
||||||
} // end anonymous namespace
|
} // 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.
|
/// Verify the given operation.
|
||||||
LogicalResult OperationVerifier::verify(Operation &op) {
|
LogicalResult OperationVerifier::verify(Operation &op) {
|
||||||
// Verify the operation first.
|
// Verify the operation first.
|
||||||
|
@ -287,7 +262,7 @@ LogicalResult OperationVerifier::verifyDominance(Operation &op) {
|
||||||
}
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Entrypoints
|
// Entrypoint
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
/// Perform (potentially expensive) checks of invariants, used to detect
|
/// Perform (potentially expensive) checks of invariants, used to detect
|
||||||
|
@ -296,94 +271,3 @@ LogicalResult OperationVerifier::verifyDominance(Operation &op) {
|
||||||
LogicalResult mlir::verify(Operation *op) {
|
LogicalResult mlir::verify(Operation *op) {
|
||||||
return OperationVerifier(op->getContext()).verify(*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();
|
|
||||||
}
|
|
||||||
|
|
|
@ -83,7 +83,6 @@ namespace {
|
||||||
static constexpr int kNonAttrKindAlias = -1;
|
static constexpr int kNonAttrKindAlias = -1;
|
||||||
|
|
||||||
class ModuleState {
|
class ModuleState {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// This is the current context if it is knowable, otherwise this is null.
|
/// This is the current context if it is knowable, otherwise this is null.
|
||||||
MLIRContext *const context;
|
MLIRContext *const context;
|
||||||
|
@ -91,7 +90,7 @@ public:
|
||||||
explicit ModuleState(MLIRContext *context) : context(context) {}
|
explicit ModuleState(MLIRContext *context) : context(context) {}
|
||||||
|
|
||||||
// Initializes module state, populating affine map state.
|
// Initializes module state, populating affine map state.
|
||||||
void initialize(Module module);
|
void initialize(Operation *op);
|
||||||
|
|
||||||
Twine getAttributeAlias(Attribute attr) const {
|
Twine getAttributeAlias(Attribute attr) const {
|
||||||
auto alias = attrToAlias.find(attr);
|
auto alias = attrToAlias.find(attr);
|
||||||
|
@ -211,9 +210,12 @@ void ModuleState::visitType(Type type) {
|
||||||
|
|
||||||
void ModuleState::visitAttribute(Attribute attr) {
|
void ModuleState::visitAttribute(Attribute attr) {
|
||||||
recordAttributeReference(attr);
|
recordAttributeReference(attr);
|
||||||
if (auto arrayAttr = attr.dyn_cast<ArrayAttr>())
|
if (auto arrayAttr = attr.dyn_cast<ArrayAttr>()) {
|
||||||
for (auto elt : arrayAttr.getValue())
|
for (auto elt : arrayAttr.getValue())
|
||||||
visitAttribute(elt);
|
visitAttribute(elt);
|
||||||
|
} else if (auto typeAttr = attr.dyn_cast<TypeAttr>()) {
|
||||||
|
visitType(typeAttr.getValue());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModuleState::visitOperation(Operation *op) {
|
void ModuleState::visitOperation(Operation *op) {
|
||||||
|
@ -222,6 +224,10 @@ void ModuleState::visitOperation(Operation *op) {
|
||||||
visitType(type);
|
visitType(type);
|
||||||
for (auto type : op->getResultTypes())
|
for (auto type : op->getResultTypes())
|
||||||
visitType(type);
|
visitType(type);
|
||||||
|
for (auto ®ion : op->getRegions())
|
||||||
|
for (auto &block : region)
|
||||||
|
for (auto *arg : block.getArguments())
|
||||||
|
visitType(arg->getType());
|
||||||
|
|
||||||
// Visit each of the attributes.
|
// Visit each of the attributes.
|
||||||
for (auto elt : op->getAttrs())
|
for (auto elt : op->getAttrs())
|
||||||
|
@ -301,18 +307,12 @@ void ModuleState::initializeSymbolAliases() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initializes module state, populating affine map and integer set state.
|
// Initializes module state, populating affine map and integer set state.
|
||||||
void ModuleState::initialize(Module module) {
|
void ModuleState::initialize(Operation *op) {
|
||||||
// Initialize the symbol aliases.
|
// Initialize the symbol aliases.
|
||||||
initializeSymbolAliases();
|
initializeSymbolAliases();
|
||||||
|
|
||||||
// Walk the module and visit each operation.
|
// Visit each of the nested operations.
|
||||||
for (auto fn : module) {
|
op->walk([&](Operation *op) { visitOperation(op); });
|
||||||
visitType(fn.getType());
|
|
||||||
for (auto attr : fn.getAttrs())
|
|
||||||
ModuleState::visitAttribute(attr.second);
|
|
||||||
|
|
||||||
fn.walk([&](Operation *op) { ModuleState::visitOperation(op); });
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -331,7 +331,7 @@ public:
|
||||||
interleave(c.begin(), c.end(), each_fn, [&]() { os << ", "; });
|
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
|
/// Print the given attribute. If 'mayElideType' is true, some attributes are
|
||||||
/// printed without the type when the type matches the default used in the
|
/// 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 printAttribute(Attribute attr, bool mayElideType = false);
|
||||||
|
|
||||||
void printType(Type type);
|
void printType(Type type);
|
||||||
void print(Function fn);
|
|
||||||
void printLocation(LocationAttr loc);
|
void printLocation(LocationAttr loc);
|
||||||
|
|
||||||
void printAffineMap(AffineMap map);
|
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
|
/// Print a floating point value in a way that the parser will be able to
|
||||||
/// round-trip losslessly.
|
/// round-trip losslessly.
|
||||||
static void printFloatValue(const APFloat &apValue, raw_ostream &os) {
|
static void printFloatValue(const APFloat &apValue, raw_ostream &os) {
|
||||||
|
@ -1179,17 +1168,11 @@ void ModulePrinter::printOptionalAttrDict(ArrayRef<NamedAttribute> attrs,
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// FunctionPrinter contains common functionality for printing
|
// OperationPrinter contains common functionality for printing operations.
|
||||||
// CFG and ML functions.
|
class OperationPrinter : public ModulePrinter, private OpAsmPrinter {
|
||||||
class FunctionPrinter : public ModulePrinter, private OpAsmPrinter {
|
|
||||||
public:
|
public:
|
||||||
FunctionPrinter(Function function, ModulePrinter &other);
|
OperationPrinter(Operation *op, ModulePrinter &other);
|
||||||
|
OperationPrinter(Region *region, ModulePrinter &other);
|
||||||
// Prints the function as a whole.
|
|
||||||
void print();
|
|
||||||
|
|
||||||
// Print the function signature.
|
|
||||||
void printFunctionSignature();
|
|
||||||
|
|
||||||
// Methods to print operations.
|
// Methods to print operations.
|
||||||
void print(Operation *op);
|
void print(Operation *op);
|
||||||
|
@ -1272,8 +1255,6 @@ protected:
|
||||||
void printValueID(Value *value, bool printResultNo = true) const;
|
void printValueID(Value *value, bool printResultNo = true) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Function function;
|
|
||||||
|
|
||||||
/// This is the value ID for each SSA value in the current function. If this
|
/// 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.
|
/// returns ~0, then the valueID has an entry in valueNames.
|
||||||
DenseMap<Value *, unsigned> valueIDs;
|
DenseMap<Value *, unsigned> valueIDs;
|
||||||
|
@ -1302,17 +1283,25 @@ private:
|
||||||
};
|
};
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
FunctionPrinter::FunctionPrinter(Function function, ModulePrinter &other)
|
OperationPrinter::OperationPrinter(Operation *op, ModulePrinter &other)
|
||||||
: ModulePrinter(other), function(function) {
|
: ModulePrinter(other) {
|
||||||
|
for (auto *result : op->getResults())
|
||||||
|
numberValueID(result);
|
||||||
|
for (auto ®ion : 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);
|
numberValuesInBlock(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Number all of the SSA values in the specified block. Values get numbered
|
/// Number all of the SSA values in the specified block. Values get numbered
|
||||||
/// continuously throughout regions. In particular, we traverse the regions
|
/// continuously throughout regions. In particular, we traverse the regions
|
||||||
/// held by operations and number values in depth-first pre-order.
|
/// 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
|
// Each block gets a unique ID, and all of the operations within it get
|
||||||
// numbered as well.
|
// numbered as well.
|
||||||
blockIDs[&block] = nextBlockID++;
|
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");
|
assert(!valueIDs.count(value) && "Value numbered multiple times");
|
||||||
|
|
||||||
SmallString<32> specialNameBuffer;
|
SmallString<32> specialNameBuffer;
|
||||||
|
@ -1412,74 +1401,8 @@ void FunctionPrinter::numberValueID(Value *value) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionPrinter::print() {
|
void OperationPrinter::print(Block *block, bool printBlockArgs,
|
||||||
printFunctionSignature();
|
bool printBlockTerminator) {
|
||||||
|
|
||||||
// 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) {
|
|
||||||
// Print the block label and argument list if requested.
|
// Print the block label and argument list if requested.
|
||||||
if (printBlockArgs) {
|
if (printBlockArgs) {
|
||||||
os.indent(currentIndent);
|
os.indent(currentIndent);
|
||||||
|
@ -1533,13 +1456,13 @@ void FunctionPrinter::print(Block *block, bool printBlockArgs,
|
||||||
currentIndent -= indentWidth;
|
currentIndent -= indentWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionPrinter::print(Operation *op) {
|
void OperationPrinter::print(Operation *op) {
|
||||||
os.indent(currentIndent);
|
os.indent(currentIndent);
|
||||||
printOperation(op);
|
printOperation(op);
|
||||||
printTrailingLocation(op->getLoc());
|
printTrailingLocation(op->getLoc());
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionPrinter::printValueID(Value *value, bool printResultNo) const {
|
void OperationPrinter::printValueID(Value *value, bool printResultNo) const {
|
||||||
int resultNo = -1;
|
int resultNo = -1;
|
||||||
auto lookupValue = value;
|
auto lookupValue = value;
|
||||||
|
|
||||||
|
@ -1572,7 +1495,7 @@ void FunctionPrinter::printValueID(Value *value, bool printResultNo) const {
|
||||||
os << '#' << resultNo;
|
os << '#' << resultNo;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionPrinter::printOperation(Operation *op) {
|
void OperationPrinter::printOperation(Operation *op) {
|
||||||
if (size_t numResults = op->getNumResults()) {
|
if (size_t numResults = op->getNumResults()) {
|
||||||
printValueID(op->getResult(0), /*printResultNo=*/false);
|
printValueID(op->getResult(0), /*printResultNo=*/false);
|
||||||
if (numResults > 1)
|
if (numResults > 1)
|
||||||
|
@ -1580,7 +1503,9 @@ void FunctionPrinter::printOperation(Operation *op) {
|
||||||
os << " = ";
|
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);
|
return printGenericOp(op);
|
||||||
|
|
||||||
// Check to see if this is a known operation. If so, use the registered
|
// 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);
|
printGenericOp(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionPrinter::printGenericOp(Operation *op) {
|
void OperationPrinter::printGenericOp(Operation *op) {
|
||||||
os << '"';
|
os << '"';
|
||||||
printEscapedString(op->getName().getStringRef(), os);
|
printEscapedString(op->getName().getStringRef(), os);
|
||||||
os << "\"(";
|
os << "\"(";
|
||||||
|
@ -1641,8 +1566,8 @@ void FunctionPrinter::printGenericOp(Operation *op) {
|
||||||
printFunctionalType(op);
|
printFunctionalType(op);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionPrinter::printSuccessorAndUseList(Operation *term,
|
void OperationPrinter::printSuccessorAndUseList(Operation *term,
|
||||||
unsigned index) {
|
unsigned index) {
|
||||||
printBlockName(term->getSuccessor(index));
|
printBlockName(term->getSuccessor(index));
|
||||||
|
|
||||||
auto succOperands = term->getSuccessorOperands(index);
|
auto succOperands = term->getSuccessorOperands(index);
|
||||||
|
@ -1658,8 +1583,17 @@ void FunctionPrinter::printSuccessorAndUseList(Operation *term,
|
||||||
os << ')';
|
os << ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prints function with initialized module state.
|
void ModulePrinter::print(ModuleOp module) {
|
||||||
void ModulePrinter::print(Function fn) { FunctionPrinter(fn, *this).print(); }
|
// 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
|
// print and dump methods
|
||||||
|
@ -1734,15 +1668,27 @@ void Value::print(raw_ostream &os) {
|
||||||
void Value::dump() { print(llvm::errs()); }
|
void Value::dump() { print(llvm::errs()); }
|
||||||
|
|
||||||
void Operation::print(raw_ostream &os) {
|
void Operation::print(raw_ostream &os) {
|
||||||
auto function = getFunction();
|
// Handle top-level operations.
|
||||||
if (!function) {
|
if (!getParent()) {
|
||||||
|
ModuleState state(getContext());
|
||||||
|
ModulePrinter modulePrinter(os, state);
|
||||||
|
OperationPrinter(this, modulePrinter).print(this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto region = getContainingRegion();
|
||||||
|
if (!region) {
|
||||||
os << "<<UNLINKED INSTRUCTION>>\n";
|
os << "<<UNLINKED INSTRUCTION>>\n";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModuleState state(function.getContext());
|
// Get the top-level region.
|
||||||
|
while (auto *nextRegion = region->getContainingRegion())
|
||||||
|
region = nextRegion;
|
||||||
|
|
||||||
|
ModuleState state(getContext());
|
||||||
ModulePrinter modulePrinter(os, state);
|
ModulePrinter modulePrinter(os, state);
|
||||||
FunctionPrinter(function, modulePrinter).print(this);
|
OperationPrinter(region, modulePrinter).print(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Operation::dump() {
|
void Operation::dump() {
|
||||||
|
@ -1751,41 +1697,44 @@ void Operation::dump() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Block::print(raw_ostream &os) {
|
void Block::print(raw_ostream &os) {
|
||||||
auto function = getFunction();
|
auto region = getParent();
|
||||||
if (!function) {
|
if (!region) {
|
||||||
os << "<<UNLINKED BLOCK>>\n";
|
os << "<<UNLINKED BLOCK>>\n";
|
||||||
return;
|
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);
|
ModulePrinter modulePrinter(os, state);
|
||||||
FunctionPrinter(function, modulePrinter).print(this);
|
OperationPrinter(region, modulePrinter).print(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Block::dump() { print(llvm::errs()); }
|
void Block::dump() { print(llvm::errs()); }
|
||||||
|
|
||||||
/// Print out the name of the block without printing its body.
|
/// Print out the name of the block without printing its body.
|
||||||
void Block::printAsOperand(raw_ostream &os, bool printType) {
|
void Block::printAsOperand(raw_ostream &os, bool printType) {
|
||||||
if (!getFunction()) {
|
auto region = getParent();
|
||||||
|
if (!region) {
|
||||||
os << "<<UNLINKED BLOCK>>\n";
|
os << "<<UNLINKED BLOCK>>\n";
|
||||||
return;
|
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);
|
ModulePrinter modulePrinter(os, state);
|
||||||
FunctionPrinter(getFunction(), modulePrinter).printBlockName(this);
|
OperationPrinter(region, modulePrinter).printBlockName(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Function::print(raw_ostream &os) {
|
void ModuleOp::print(raw_ostream &os) {
|
||||||
ModuleState state(getContext());
|
|
||||||
ModulePrinter(os, state).print(*this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Function::dump() { print(llvm::errs()); }
|
|
||||||
|
|
||||||
void Module::print(raw_ostream &os) {
|
|
||||||
ModuleState state(getContext());
|
ModuleState state(getContext());
|
||||||
state.initialize(*this);
|
state.initialize(*this);
|
||||||
ModulePrinter(os, state).print(*this);
|
ModulePrinter(os, state).print(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Module::dump() { print(llvm::errs()); }
|
void ModuleOp::dump() { print(llvm::errs()); }
|
||||||
|
|
|
@ -51,15 +51,8 @@ Operation *Block::getContainingOp() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Function Block::getFunction() {
|
Function Block::getFunction() {
|
||||||
Block *block = this;
|
auto *parent = getParent();
|
||||||
while (auto *op = block->getContainingOp()) {
|
return parent ? parent->getParentOfType<FuncOp>() : nullptr;
|
||||||
block = op->getBlock();
|
|
||||||
if (!block)
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (auto *list = block->getParent())
|
|
||||||
return list->getContainingFunction();
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert this block (which must not already be in a function) right before
|
/// 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);
|
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() {
|
void Block::erase() {
|
||||||
assert(getParent() && "Block has no parent");
|
assert(getParent() && "Block has no parent");
|
||||||
getParent()->getBlocks().erase(this);
|
getParent()->getBlocks().erase(this);
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include "mlir/IR/BlockAndValueMapping.h"
|
#include "mlir/IR/BlockAndValueMapping.h"
|
||||||
#include "mlir/IR/Builders.h"
|
#include "mlir/IR/Builders.h"
|
||||||
#include "mlir/IR/Diagnostics.h"
|
#include "mlir/IR/Diagnostics.h"
|
||||||
|
#include "mlir/IR/Dialect.h"
|
||||||
#include "mlir/IR/MLIRContext.h"
|
#include "mlir/IR/MLIRContext.h"
|
||||||
#include "mlir/IR/Module.h"
|
#include "mlir/IR/Module.h"
|
||||||
#include "mlir/IR/OpImplementation.h"
|
#include "mlir/IR/OpImplementation.h"
|
||||||
|
@ -27,177 +28,31 @@
|
||||||
#include "llvm/ADT/Twine.h"
|
#include "llvm/ADT/Twine.h"
|
||||||
|
|
||||||
using namespace mlir;
|
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 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,
|
void FuncOp::build(Builder *builder, OperationState *result, StringRef name,
|
||||||
FunctionType type, ArrayRef<NamedAttribute> attrs) {
|
FunctionType type, ArrayRef<NamedAttribute> attrs) {
|
||||||
result->addAttribute("name", builder->getStringAttr(name));
|
result->addAttribute("name", builder->getStringAttr(name));
|
||||||
|
@ -206,6 +61,23 @@ void FuncOp::build(Builder *builder, OperationState *result, StringRef name,
|
||||||
result->addRegion();
|
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.
|
/// Parsing/Printing methods.
|
||||||
static ParseResult
|
static ParseResult
|
||||||
parseArgumentList(OpAsmParser *parser, SmallVectorImpl<Type> &argTypes,
|
parseArgumentList(OpAsmParser *parser, SmallVectorImpl<Type> &argTypes,
|
||||||
|
@ -376,6 +248,22 @@ void FuncOp::print(OpAsmPrinter *p) {
|
||||||
}
|
}
|
||||||
|
|
||||||
LogicalResult FuncOp::verify() {
|
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 this function is external there is nothing to do.
|
||||||
if (isExternal())
|
if (isExternal())
|
||||||
return success();
|
return success();
|
||||||
|
@ -383,7 +271,6 @@ LogicalResult FuncOp::verify() {
|
||||||
// Verify that the argument list of the function and the arg list of the entry
|
// Verify that the argument list of the function and the arg list of the entry
|
||||||
// block line up.
|
// block line up.
|
||||||
Block &entryBlock = front();
|
Block &entryBlock = front();
|
||||||
auto fnInputTypes = getType().getInputs();
|
|
||||||
if (fnInputTypes.size() != entryBlock.getNumArguments())
|
if (fnInputTypes.size() != entryBlock.getNumArguments())
|
||||||
return emitOpError("entry block must have ")
|
return emitOpError("entry block must have ")
|
||||||
<< fnInputTypes.size() << " arguments to match function signature";
|
<< fnInputTypes.size() << " arguments to match function signature";
|
||||||
|
@ -398,6 +285,70 @@ LogicalResult FuncOp::verify() {
|
||||||
return success();
|
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.
|
// Function Argument Attribute.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
|
@ -37,6 +37,14 @@ void ModuleOp::build(Builder *builder, OperationState *result) {
|
||||||
ensureModuleTerminator(*result->addRegion(), *builder, result->location);
|
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) {
|
ParseResult ModuleOp::parse(OpAsmParser *parser, OperationState *result) {
|
||||||
// If module attributes are present, parse them.
|
// If module attributes are present, parse them.
|
||||||
if (succeeded(parser->parseOptionalKeyword("attributes")))
|
if (succeeded(parser->parseOptionalKeyword("attributes")))
|
||||||
|
@ -81,17 +89,44 @@ LogicalResult ModuleOp::verify() {
|
||||||
return emitOpError("expected body to have no arguments");
|
return emitOpError("expected body to have no arguments");
|
||||||
|
|
||||||
if (body->empty() || !isa<ModuleTerminatorOp>(body->back())) {
|
if (body->empty() || !isa<ModuleTerminatorOp>(body->back())) {
|
||||||
emitOpError("expects region to end with '" +
|
return emitOpError("expects region to end with '" +
|
||||||
ModuleTerminatorOp::getOperationName() + "'")
|
ModuleTerminatorOp::getOperationName() + "'")
|
||||||
.attachNote()
|
.attachNote()
|
||||||
<< "in custom textual format, the absence of terminator implies '"
|
<< "in custom textual format, the absence of terminator implies '"
|
||||||
<< ModuleTerminatorOp::getOperationName() << "'";
|
<< ModuleTerminatorOp::getOperationName() << "'";
|
||||||
return failure();
|
}
|
||||||
|
|
||||||
|
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 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() {
|
LogicalResult ModuleTerminatorOp::verify() {
|
||||||
if (!isa_and_nonnull<ModuleOp>(getOperation()->getParentOp()))
|
if (!isa_and_nonnull<ModuleOp>(getOperation()->getParentOp()))
|
||||||
return emitOpError() << "is expected to terminate a '"
|
return emitOpError() << "is expected to terminate a '"
|
||||||
|
|
|
@ -423,8 +423,10 @@ void llvm::ilist_traits<::mlir::Operation>::transferNodesFromList(
|
||||||
/// Remove this operation (and its descendants) from its Block and delete
|
/// Remove this operation (and its descendants) from its Block and delete
|
||||||
/// all of them.
|
/// all of them.
|
||||||
void Operation::erase() {
|
void Operation::erase() {
|
||||||
assert(getBlock() && "Operation has no block");
|
if (auto *parent = getBlock())
|
||||||
getBlock()->getOperations().erase(this);
|
parent->getOperations().erase(this);
|
||||||
|
else
|
||||||
|
destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unlink this operation from its current block and insert it right before
|
/// Unlink this operation from its current block and insert it right before
|
||||||
|
|
|
@ -21,8 +21,6 @@
|
||||||
#include "mlir/IR/Operation.h"
|
#include "mlir/IR/Operation.h"
|
||||||
using namespace mlir;
|
using namespace mlir;
|
||||||
|
|
||||||
Region::Region(Function container) : container(container.impl) {}
|
|
||||||
|
|
||||||
Region::Region(Operation *container) : container(container) {}
|
Region::Region(Operation *container) : container(container) {}
|
||||||
|
|
||||||
Region::~Region() {
|
Region::~Region() {
|
||||||
|
@ -35,33 +33,32 @@ Region::~Region() {
|
||||||
/// Return the context this region is inserted in. The region must have a valid
|
/// Return the context this region is inserted in. The region must have a valid
|
||||||
/// parent container.
|
/// parent container.
|
||||||
MLIRContext *Region::getContext() {
|
MLIRContext *Region::getContext() {
|
||||||
assert(!container.isNull() && "region is not attached to a container");
|
assert(container && "region is not attached to a container");
|
||||||
if (auto *inst = getContainingOp())
|
return container->getContext();
|
||||||
return inst->getContext();
|
|
||||||
return getContainingFunction().getContext();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return a location for this region. This is the location attached to the
|
/// Return a location for this region. This is the location attached to the
|
||||||
/// parent container. The region must have a valid parent container.
|
/// parent container. The region must have a valid parent container.
|
||||||
Location Region::getLoc() {
|
Location Region::getLoc() {
|
||||||
assert(!container.isNull() && "region is not attached to a container");
|
assert(container && "region is not attached to a container");
|
||||||
if (auto *inst = getContainingOp())
|
return container->getLoc();
|
||||||
return inst->getLoc();
|
|
||||||
return getContainingFunction().getLoc();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Region *Region::getContainingRegion() {
|
Region *Region::getContainingRegion() {
|
||||||
if (auto *inst = getContainingOp())
|
assert(container && "region is not attached to a container");
|
||||||
return inst->getContainingRegion();
|
return container->getContainingRegion();
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Operation *Region::getContainingOp() {
|
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() {
|
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) {
|
bool Region::isProperAncestor(Region *other) {
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
using namespace mlir;
|
using namespace mlir;
|
||||||
|
|
||||||
/// Build a symbol table with the symbols within the given module.
|
/// 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) {
|
for (auto func : module) {
|
||||||
auto inserted = symbolTable.insert({func.getName(), func});
|
auto inserted = symbolTable.insert({func.getName(), func});
|
||||||
(void)inserted;
|
(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
|
/// Look up a symbol with the specified name, returning null if no such name
|
||||||
/// exists. Names never include the @ on them.
|
/// exists. Names never include the @ on them.
|
||||||
Function SymbolTable::lookup(StringRef name) const {
|
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
|
/// Look up a symbol with the specified name, returning null if no such name
|
||||||
/// exists. Names never include the @ on them.
|
/// exists. Names never include the @ on them.
|
||||||
Function SymbolTable::lookup(Identifier name) const {
|
Function SymbolTable::lookup(Identifier name) const {
|
||||||
return symbolTable.lookup(name);
|
return lookup(name.strref());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Erase the given symbol from the table.
|
/// Erase the given symbol from the table.
|
||||||
|
@ -68,6 +68,6 @@ void SymbolTable::insert(Function symbol) {
|
||||||
nameBuffer.resize(originalLength);
|
nameBuffer.resize(originalLength);
|
||||||
nameBuffer += '_';
|
nameBuffer += '_';
|
||||||
nameBuffer += std::to_string(uniquingCounter++);
|
nameBuffer += std::to_string(uniquingCounter++);
|
||||||
symbol.setName(Identifier::get(nameBuffer, context));
|
} while (!symbolTable.insert({nameBuffer, symbol}).second);
|
||||||
} while (!symbolTable.insert({symbol.getName(), symbol}).second);
|
symbol.setName(nameBuffer);
|
||||||
}
|
}
|
||||||
|
|
|
@ -255,7 +255,7 @@ public:
|
||||||
/// trailing-location ::= location?
|
/// trailing-location ::= location?
|
||||||
///
|
///
|
||||||
template <typename Owner>
|
template <typename Owner>
|
||||||
ParseResult parseOptionalTrailingLocation(Owner &owner) {
|
ParseResult parseOptionalTrailingLocation(Owner *owner) {
|
||||||
// If there is a 'loc' we parse a trailing location.
|
// If there is a 'loc' we parse a trailing location.
|
||||||
if (!getToken().is(Token::kw_loc))
|
if (!getToken().is(Token::kw_loc))
|
||||||
return success();
|
return success();
|
||||||
|
@ -264,7 +264,7 @@ public:
|
||||||
LocationAttr directLoc;
|
LocationAttr directLoc;
|
||||||
if (parseLocation(directLoc))
|
if (parseLocation(directLoc))
|
||||||
return failure();
|
return failure();
|
||||||
owner.setLoc(directLoc);
|
owner->setLoc(directLoc);
|
||||||
return success();
|
return success();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2479,14 +2479,15 @@ namespace {
|
||||||
/// operations.
|
/// operations.
|
||||||
class OperationParser : public Parser {
|
class OperationParser : public Parser {
|
||||||
public:
|
public:
|
||||||
OperationParser(ParserState &state, Function function)
|
OperationParser(ParserState &state, ModuleOp moduleOp)
|
||||||
: Parser(state), function(function), opBuilder(function.getBody()) {}
|
: Parser(state), opBuilder(moduleOp.getBodyRegion()), moduleOp(moduleOp) {
|
||||||
|
}
|
||||||
|
|
||||||
~OperationParser();
|
~OperationParser();
|
||||||
|
|
||||||
/// After parsing is finished, this function must be called to see if there
|
/// After parsing is finished, this function must be called to see if there
|
||||||
/// are any remaining issues.
|
/// are any remaining issues.
|
||||||
ParseResult finalize(SMLoc loc);
|
ParseResult finalize();
|
||||||
|
|
||||||
//===--------------------------------------------------------------------===//
|
//===--------------------------------------------------------------------===//
|
||||||
// SSA Value Handling
|
// SSA Value Handling
|
||||||
|
@ -2595,8 +2596,6 @@ public:
|
||||||
Block *defineBlockNamed(StringRef name, SMLoc loc, Block *existing);
|
Block *defineBlockNamed(StringRef name, SMLoc loc, Block *existing);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Function function;
|
|
||||||
|
|
||||||
/// Returns the info for a block at the current scope for the given name.
|
/// Returns the info for a block at the current scope for the given name.
|
||||||
std::pair<Block *, SMLoc> &getBlockInfoByName(StringRef name) {
|
std::pair<Block *, SMLoc> &getBlockInfoByName(StringRef name) {
|
||||||
return blocksByName.back()[name];
|
return blocksByName.back()[name];
|
||||||
|
@ -2643,6 +2642,9 @@ private:
|
||||||
|
|
||||||
/// The builder used when creating parsed operation instances.
|
/// The builder used when creating parsed operation instances.
|
||||||
OpBuilder opBuilder;
|
OpBuilder opBuilder;
|
||||||
|
|
||||||
|
/// The top level module operation.
|
||||||
|
ModuleOp moduleOp;
|
||||||
};
|
};
|
||||||
} // end anonymous namespace
|
} // end anonymous namespace
|
||||||
|
|
||||||
|
@ -2657,7 +2659,7 @@ OperationParser::~OperationParser() {
|
||||||
|
|
||||||
/// After parsing is finished, this function must be called to see if there are
|
/// After parsing is finished, this function must be called to see if there are
|
||||||
/// any remaining issues.
|
/// any remaining issues.
|
||||||
ParseResult OperationParser::finalize(SMLoc loc) {
|
ParseResult OperationParser::finalize() {
|
||||||
// Check for any forward references that are left. If we find any, error
|
// Check for any forward references that are left. If we find any, error
|
||||||
// out.
|
// out.
|
||||||
if (!forwardRefPlaceholders.empty()) {
|
if (!forwardRefPlaceholders.empty()) {
|
||||||
|
@ -2697,7 +2699,7 @@ ParseResult OperationParser::popSSANameScope() {
|
||||||
for (auto entry : forwardRefInCurrentScope) {
|
for (auto entry : forwardRefInCurrentScope) {
|
||||||
errors.push_back({entry.second.getPointer(), entry.first});
|
errors.push_back({entry.second.getPointer(), entry.first});
|
||||||
// Add this block to the top-level region to allow for automatic cleanup.
|
// 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());
|
llvm::array_pod_sort(errors.begin(), errors.end());
|
||||||
|
|
||||||
|
@ -2991,7 +2993,7 @@ ParseResult OperationParser::parseOperation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to parse the optional trailing location.
|
// Try to parse the optional trailing location.
|
||||||
if (parseOptionalTrailingLocation(*op))
|
if (parseOptionalTrailingLocation(op))
|
||||||
return failure();
|
return failure();
|
||||||
|
|
||||||
return success();
|
return success();
|
||||||
|
@ -3105,9 +3107,8 @@ Operation *OperationParser::parseGenericOperation() {
|
||||||
if (consumeIf(Token::l_paren)) {
|
if (consumeIf(Token::l_paren)) {
|
||||||
do {
|
do {
|
||||||
// Create temporary regions with the top level region as parent.
|
// Create temporary regions with the top level region as parent.
|
||||||
result.regions.emplace_back(new Region(function));
|
result.regions.emplace_back(new Region(moduleOp));
|
||||||
if (parseRegion(*result.regions.back(),
|
if (parseRegion(*result.regions.back(), /*entryArguments=*/{}))
|
||||||
/*entryArguments*/ {}))
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
} while (consumeIf(Token::comma));
|
} while (consumeIf(Token::comma));
|
||||||
if (parseToken(Token::r_paren, "expected ')' to end region list"))
|
if (parseToken(Token::r_paren, "expected ')' to end region list"))
|
||||||
|
@ -3504,13 +3505,8 @@ public:
|
||||||
ParseResult parseOptionalRegion(Region ®ion,
|
ParseResult parseOptionalRegion(Region ®ion,
|
||||||
ArrayRef<OperandType> arguments,
|
ArrayRef<OperandType> arguments,
|
||||||
ArrayRef<Type> argTypes) override {
|
ArrayRef<Type> argTypes) override {
|
||||||
if (parser.getToken().isNot(Token::l_brace)) {
|
if (parser.getToken().isNot(Token::l_brace))
|
||||||
if (!arguments.empty())
|
|
||||||
return emitError(
|
|
||||||
parser.getToken().getLoc(),
|
|
||||||
"optional region with explicit entry arguments must be defined");
|
|
||||||
return success();
|
return success();
|
||||||
}
|
|
||||||
return parseRegion(region, arguments, argTypes);
|
return parseRegion(region, arguments, argTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3866,7 +3862,7 @@ class ModuleParser : public Parser {
|
||||||
public:
|
public:
|
||||||
explicit ModuleParser(ParserState &state) : Parser(state) {}
|
explicit ModuleParser(ParserState &state) : Parser(state) {}
|
||||||
|
|
||||||
ParseResult parseModule(Module module);
|
ParseResult parseModule(ModuleOp module);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Parse an attribute alias declaration.
|
/// Parse an attribute alias declaration.
|
||||||
|
@ -3874,17 +3870,6 @@ private:
|
||||||
|
|
||||||
/// Parse an attribute alias declaration.
|
/// Parse an attribute alias declaration.
|
||||||
ParseResult parseTypeAliasDef();
|
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
|
} // end anonymous namespace
|
||||||
|
|
||||||
|
@ -3954,168 +3939,20 @@ ParseResult ModuleParser::parseTypeAliasDef() {
|
||||||
return success();
|
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.
|
/// This is the top-level module parser.
|
||||||
ParseResult ModuleParser::parseModule(Module module) {
|
ParseResult ModuleParser::parseModule(ModuleOp module) {
|
||||||
|
OperationParser opParser(getState(), module);
|
||||||
while (1) {
|
while (1) {
|
||||||
switch (getToken().getKind()) {
|
switch (getToken().getKind()) {
|
||||||
default:
|
default:
|
||||||
emitError("expected a top level entity");
|
// Parse a top-level operation.
|
||||||
return failure();
|
if (opParser.parseOperation())
|
||||||
|
return failure();
|
||||||
|
break;
|
||||||
|
|
||||||
// If we got to the end of the file, then we're done.
|
// If we got to the end of the file, then we're done.
|
||||||
case Token::eof:
|
case Token::eof:
|
||||||
return success();
|
return opParser.finalize();
|
||||||
|
|
||||||
// If we got an error token, then the lexer already emitted an error, just
|
// 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
|
// stop. Someday we could introduce error recovery if there was demand
|
||||||
|
@ -4134,11 +3971,6 @@ ParseResult ModuleParser::parseModule(Module module) {
|
||||||
if (parseTypeAliasDef())
|
if (parseTypeAliasDef())
|
||||||
return failure();
|
return failure();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Token::kw_func:
|
|
||||||
if (parseFunc(module))
|
|
||||||
return failure();
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -464,7 +464,7 @@ static LogicalResult verify(spirv::ModuleOp moduleOp) {
|
||||||
|
|
||||||
static LogicalResult verifyReturn(spirv::ReturnOp returnOp) {
|
static LogicalResult verifyReturn(spirv::ReturnOp returnOp) {
|
||||||
auto funcOp =
|
auto funcOp =
|
||||||
llvm::dyn_cast_or_null<FuncOp>(returnOp.getOperation()->getParentOp());
|
returnOp.getOperation()->getContainingRegion()->getContainingFunction();
|
||||||
if (!funcOp)
|
if (!funcOp)
|
||||||
return returnOp.emitOpError("must appear in a 'func' op");
|
return returnOp.emitOpError("must appear in a 'func' op");
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ std::unique_ptr<llvm::Module> mlir::translateModuleToNVVMIR(Module m) {
|
||||||
if (!func.getAttrOfType<UnitAttr>(gpu::GPUDialect::getKernelFuncAttrName()))
|
if (!func.getAttrOfType<UnitAttr>(gpu::GPUDialect::getKernelFuncAttrName()))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
auto *llvmFunc = llvmModule->getFunction(func.getName().strref());
|
auto *llvmFunc = llvmModule->getFunction(func.getName());
|
||||||
|
|
||||||
llvm::Metadata *llvmMetadata[] = {
|
llvm::Metadata *llvmMetadata[] = {
|
||||||
llvm::ValueAsMetadata::get(llvmFunc),
|
llvm::ValueAsMetadata::get(llvmFunc),
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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() {
|
func @func_op() {
|
||||||
// expected-error@+1 {{entry block must have 1 arguments to match function signature}}
|
// expected-error@+1 {{entry block must have 1 arguments to match function signature}}
|
||||||
func @mixed_named_arguments(f32) {
|
func @mixed_named_arguments(f32) {
|
||||||
|
|
|
@ -41,3 +41,11 @@ func @module_op() {
|
||||||
// expected-error@+1 {{is expected to terminate a 'module' operation}}
|
// expected-error@+1 {{is expected to terminate a 'module' operation}}
|
||||||
"module_terminator"() : () -> ()
|
"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)
|
||||||
|
}
|
||||||
|
|
|
@ -71,11 +71,6 @@ func @memrefs(memref<42xi8, #map0>) // expected-error {{memref affine map dimens
|
||||||
#map1 = (d0) -> (d0)
|
#map1 = (d0) -> (d0)
|
||||||
func @memrefs(memref<42x42xi8, #map0, #map1>) // expected-error {{memref affine map dimension mismatch}}
|
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() {
|
func @bad_branch() {
|
||||||
|
@ -143,11 +138,6 @@ func @block_first_has_predecessor() {
|
||||||
|
|
||||||
// -----
|
// -----
|
||||||
|
|
||||||
func @empty() { // expected-error {{function must have a body}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----
|
|
||||||
|
|
||||||
func @no_return() {
|
func @no_return() {
|
||||||
%x = constant 0 : i32 // expected-error {{block with no terminator}}
|
%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() {
|
func @br_mismatch() {
|
||||||
^bb0:
|
^bb0:
|
||||||
%0:2 = "foo"() : () -> (i1, i17)
|
%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)
|
#map1 = (i)[j] -> (i+j)
|
||||||
|
|
||||||
func @bound_symbol_mismatch(%N : index) {
|
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})
|
func @invalid_func_arg_attr(i1 {non_dialect_attr = 10})
|
||||||
|
|
||||||
// -----
|
// -----
|
||||||
|
|
|
@ -3,9 +3,8 @@
|
||||||
|
|
||||||
#set0 = (d0) : (1 == 0)
|
#set0 = (d0) : (1 == 0)
|
||||||
|
|
||||||
// CHECK-LABEL: inline_notation
|
// CHECK-LABEL: func @inline_notation
|
||||||
// CHECK () -> i32 loc("mysource.cc":10:8)
|
func @inline_notation() -> i32 {
|
||||||
func @inline_notation() -> i32 loc("mysource.cc":10:8) {
|
|
||||||
// CHECK: -> i32 loc("foo")
|
// CHECK: -> i32 loc("foo")
|
||||||
%1 = "foo"() : () -> i32 loc("foo")
|
%1 = "foo"() : () -> i32 loc("foo")
|
||||||
|
|
||||||
|
|
|
@ -2,9 +2,8 @@
|
||||||
|
|
||||||
#set0 = (d0) : (1 == 0)
|
#set0 = (d0) : (1 == 0)
|
||||||
|
|
||||||
// CHECK-LABEL: inline_notation
|
// CHECK-LABEL: func @inline_notation
|
||||||
// CHECK () -> i32 mysource.cc:10:8
|
func @inline_notation() -> i32 {
|
||||||
func @inline_notation() -> i32 loc("mysource.cc":10:8) {
|
|
||||||
// CHECK: -> i32 "foo"
|
// CHECK: -> i32 "foo"
|
||||||
%1 = "foo"() : () -> i32 loc("foo")
|
%1 = "foo"() : () -> i32 loc("foo")
|
||||||
|
|
||||||
|
|
|
@ -166,10 +166,10 @@ func @aligned_load_incorrect_attributes() -> () {
|
||||||
// spv.Return
|
// spv.Return
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
func @return_not_in_func() -> () {
|
"foo.function"() ({
|
||||||
// expected-error @+1 {{must appear in a 'func' op}}
|
// expected-error @+1 {{must appear in a 'func' op}}
|
||||||
spv.Return
|
spv.Return
|
||||||
}
|
}) : () -> ()
|
||||||
|
|
||||||
// -----
|
// -----
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,8 @@
|
||||||
|
|
||||||
#set0 = (d0) : (1 == 0)
|
#set0 = (d0) : (1 == 0)
|
||||||
|
|
||||||
// CHECK-LABEL: inline_notation
|
// CHECK-LABEL: func @inline_notation
|
||||||
// CHECK: () -> i32 loc(unknown) {
|
func @inline_notation() -> i32 {
|
||||||
func @inline_notation() -> i32 loc("mysource.cc":10:8) {
|
|
||||||
// CHECK: "foo"() : () -> i32 loc(unknown)
|
// CHECK: "foo"() : () -> i32 loc(unknown)
|
||||||
%1 = "foo"() : () -> i32 loc("foo")
|
%1 = "foo"() : () -> i32 loc("foo")
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue