Skip to content

Commit

Permalink
[mlir] Generate Op builders for Python bindings
Browse files Browse the repository at this point in the history
Add an ODS-backed generator of default builders. This currently does not
support operation with attribute arguments, for which the builder is
just ignored. Attribute support will be introduced separately for
builders and accessors.

Default builders are always generated with the same number of result and
operand groups as the ODS specification, i.e. one group per each operand
or result. Optional elements accept None but cannot be omitted. Variadic
groups accept iterable objects and cannot be replaced with a single
object.

For some operations, it is possible to infer the result type given the
traits, but most traits rely on inline pieces of C++ that we cannot
(yet) forward to Python bindings. Since the Ops where the inference is
possible (having the `SameOperandAndResultTypes` trait or
`TypeMatchesWith` without transform field) are a small minority, they
also require the result type to make the builder syntax more consistent.

Reviewed By: stellaraccident

Differential Revision: https://reviews.llvm.org/D91190
  • Loading branch information
ftynse committed Nov 12, 2020
1 parent a72d384 commit f9265de
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 32 deletions.
2 changes: 1 addition & 1 deletion mlir/include/mlir/Dialect/StandardOps/IR/Ops.td
Expand Up @@ -106,7 +106,7 @@ class ArithmeticOp<string mnemonic, list<OpTrait> traits = []> :
SameOperandsAndResultType,
ElementwiseMappable])> {

let results = (outs AnyType);
let results = (outs AnyType:$result);

let parser = [{
return impl::parseOneResultSameOperandTypeOp(parser, result);
Expand Down
18 changes: 9 additions & 9 deletions mlir/test/Bindings/Python/dialects.py
Expand Up @@ -63,7 +63,7 @@ def testUserDialectClass():
run(testUserDialectClass)


# XHECK-LABEL: TEST: testCustomOpView
# CHECK-LABEL: TEST: testCustomOpView
# This test uses the standard dialect AddFOp as an example of a user op.
# TODO: Op creation and access is still quite verbose: simplify this test as
# additional capabilities come online.
Expand All @@ -82,17 +82,17 @@ def createInput():
# Create via dialects context collection.
input1 = createInput()
input2 = createInput()
op1 = ctx.dialects.std.AddFOp(input1, input2)
op1 = ctx.dialects.std.AddFOp(input1.type, input1, input2)

# Create via an import
from mlir.dialects.std import AddFOp
AddFOp(input1, op1.result)
AddFOp(input1.type, input1, op1.result)

# XHECK: %[[INPUT0:.*]] = "pytest_dummy.intinput"
# XHECK: %[[INPUT1:.*]] = "pytest_dummy.intinput"
# XHECK: %[[R0:.*]] = addf %[[INPUT0]], %[[INPUT1]] : f32
# XHECK: %[[R1:.*]] = addf %[[INPUT0]], %[[R0]] : f32
# CHECK: %[[INPUT0:.*]] = "pytest_dummy.intinput"
# CHECK: %[[INPUT1:.*]] = "pytest_dummy.intinput"
# CHECK: %[[R0:.*]] = addf %[[INPUT0]], %[[INPUT1]] : f32
# CHECK: %[[R1:.*]] = addf %[[INPUT0]], %[[R0]] : f32
m.operation.print()

# TODO: re-enable when constructs are generated again
# run(testCustomOpView)

run(testCustomOpView)
96 changes: 96 additions & 0 deletions mlir/test/mlir-tblgen/op-python-bindings.td
Expand Up @@ -18,6 +18,23 @@ class TestOp<string mnemonic, list<OpTrait> traits = []> :
// CHECK-LABEL: OPERATION_NAME = "test.attr_sized_operands"
def AttrSizedOperandsOp : TestOp<"attr_sized_operands",
[AttrSizedOperandSegments]> {
// CHECK: def __init__(self, variadic1, non_variadic, variadic2, loc=None, ip=None):
// CHECK: operands = []
// CHECK: results = []
// CHECK: attributes = {}
// CHECK: operand_segment_sizes = array.array('L')
// CHECK: operands += [*variadic1]
// CHECK: operand_segment_sizes.append(len(variadic1))
// CHECK: operands.append(non_variadic)
// CHECK: operand_segment_sizes.append(1)
// CHECK: if variadic2 is not None: operands.append(variadic2)
// CHECK: operand_segment_sizes.append(0 if variadic2 is None else 1)
// CHECK: attributes["operand_segment_sizes"] = _ir.DenseElementsAttr.get(operand_segment_sizes,
// CHECK: context=Location.current.context if loc is None else loc.context)
// CHECK: super().__init__(_ir.Operation.create(
// CHECK: "test.attr_sized_operands", attributes=attributes, operands=operands, results=results,
// CHECK: loc=loc, ip=ip))

// CHECK: @property
// CHECK: def variadic1(self):
// CHECK: operand_range = _segmented_accessor(
Expand Down Expand Up @@ -47,6 +64,23 @@ def AttrSizedOperandsOp : TestOp<"attr_sized_operands",
// CHECK-LABEL: OPERATION_NAME = "test.attr_sized_results"
def AttrSizedResultsOp : TestOp<"attr_sized_results",
[AttrSizedResultSegments]> {
// CHECK: def __init__(self, variadic1, non_variadic, variadic2, loc=None, ip=None):
// CHECK: operands = []
// CHECK: results = []
// CHECK: attributes = {}
// CHECK: result_segment_sizes = array.array('L')
// CHECK: if variadic1 is not None: results.append(variadic1)
// CHECK: result_segment_sizes.append(0 if variadic1 is None else 1)
// CHECK: results.append(non_variadic)
// CHECK: result_segment_sizes.append(1) # non_variadic
// CHECK: if variadic2 is not None: results.append(variadic2)
// CHECK: result_segment_sizes.append(0 if variadic2 is None else 1)
// CHECK: attributes["result_segment_sizes"] = _ir.DenseElementsAttr.get(result_segment_sizes,
// CHECK: context=Location.current.context if loc is None else loc.context)
// CHECK: super().__init__(_ir.Operation.create(
// CHECK: "test.attr_sized_results", attributes=attributes, operands=operands, results=results,
// CHECK: loc=loc, ip=ip))

// CHECK: @property
// CHECK: def variadic1(self):
// CHECK: result_range = _segmented_accessor(
Expand Down Expand Up @@ -75,11 +109,32 @@ def AttrSizedResultsOp : TestOp<"attr_sized_results",
// CHECK: class EmptyOp(_ir.OpView):
// CHECK-LABEL: OPERATION_NAME = "test.empty"
def EmptyOp : TestOp<"empty">;
// CHECK: def __init__(self, loc=None, ip=None):
// CHECK: operands = []
// CHECK: results = []
// CHECK: attributes = {}
// CHECK: super().__init__(_ir.Operation.create(
// CHECK: "test.empty", attributes=attributes, operands=operands, results=results,
// CHECK: loc=loc, ip=ip))

// CHECK: @_cext.register_operation(_Dialect)
// CHECK: class MissingNamesOp(_ir.OpView):
// CHECK-LABEL: OPERATION_NAME = "test.missing_names"
def MissingNamesOp : TestOp<"missing_names"> {
// CHECK: def __init__(self, i32, _gen_res_1, i64, _gen_arg_0, f32, _gen_arg_2, loc=None, ip=None):
// CHECK: operands = []
// CHECK: results = []
// CHECK: attributes = {}
// CHECK: results.append(i32)
// CHECK: results.append(_gen_res_1)
// CHECK: results.append(i64)
// CHECK: operands.append(_gen_arg_0)
// CHECK: operands.append(f32)
// CHECK: operands.append(_gen_arg_2)
// CHECK: super().__init__(_ir.Operation.create(
// CHECK: "test.missing_names", attributes=attributes, operands=operands, results=results,
// CHECK: loc=loc, ip=ip))

// CHECK: @property
// CHECK: def f32(self):
// CHECK: return self.operation.operands[1]
Expand All @@ -99,6 +154,16 @@ def MissingNamesOp : TestOp<"missing_names"> {
// CHECK: class OneVariadicOperandOp(_ir.OpView):
// CHECK-LABEL: OPERATION_NAME = "test.one_variadic_operand"
def OneVariadicOperandOp : TestOp<"one_variadic_operand"> {
// CHECK: def __init__(self, non_variadic, variadic, loc=None, ip=None):
// CHECK: operands = []
// CHECK: results = []
// CHECK: attributes = {}
// CHECK: operands.append(non_variadic)
// CHECK: operands += [*variadic]
// CHECK: super().__init__(_ir.Operation.create(
// CHECK: "test.one_variadic_operand", attributes=attributes, operands=operands, results=results,
// CHECK: loc=loc, ip=ip))

// CHECK: @property
// CHECK: def non_variadic(self):
// CHECK: return self.operation.operands[0]
Expand All @@ -114,6 +179,16 @@ def OneVariadicOperandOp : TestOp<"one_variadic_operand"> {
// CHECK: class OneVariadicResultOp(_ir.OpView):
// CHECK-LABEL: OPERATION_NAME = "test.one_variadic_result"
def OneVariadicResultOp : TestOp<"one_variadic_result"> {
// CHECK: def __init__(self, variadic, non_variadic, loc=None, ip=None):
// CHECK: operands = []
// CHECK: results = []
// CHECK: attributes = {}
// CHECK: results += [*variadic]
// CHECK: results.append(non_variadic)
// CHECK: super().__init__(_ir.Operation.create(
// CHECK: "test.one_variadic_result", attributes=attributes, operands=operands, results=results,
// CHECK: loc=loc, ip=ip))

// CHECK: @property
// CHECK: def variadic(self):
// CHECK: variadic_group_length = len(self.operation.results) - 2 + 1
Expand All @@ -130,6 +205,15 @@ def OneVariadicResultOp : TestOp<"one_variadic_result"> {
// CHECK: class PythonKeywordOp(_ir.OpView):
// CHECK-LABEL: OPERATION_NAME = "test.python_keyword"
def PythonKeywordOp : TestOp<"python_keyword"> {
// CHECK: def __init__(self, in_, loc=None, ip=None):
// CHECK: operands = []
// CHECK: results = []
// CHECK: attributes = {}
// CHECK: operands.append(in_)
// CHECK: super().__init__(_ir.Operation.create(
// CHECK: "test.python_keyword", attributes=attributes, operands=operands, results=results,
// CHECK: loc=loc, ip=ip))

// CHECK: @property
// CHECK: def in_(self):
// CHECK: return self.operation.operands[0]
Expand Down Expand Up @@ -186,6 +270,18 @@ def SameVariadicResultSizeOp : TestOp<"same_variadic_result",
// CHECK: class SimpleOp(_ir.OpView):
// CHECK-LABEL: OPERATION_NAME = "test.simple"
def SimpleOp : TestOp<"simple"> {
// CHECK: def __init__(self, i64, f64, i32, f32, loc=None, ip=None):
// CHECK: operands = []
// CHECK: results = []
// CHECK: attributes = {}
// CHECK: results.append(i64)
// CHECK: results.append(f64)
// CHECK: operands.append(i32)
// CHECK: operands.append(f32)
// CHECK: super().__init__(_ir.Operation.create(
// CHECK: "test.simple", attributes=attributes, operands=operands, results=results,
// CHECK: loc=loc, ip=ip))

// CHECK: @property
// CHECK: def i32(self):
// CHECK: return self.operation.operands[0]
Expand Down

0 comments on commit f9265de

Please sign in to comment.