Stella Laurenzo c1ded6a759 Add mlir python APIs for creating operations, regions and blocks.
* The API is a bit more verbose than I feel like it needs to be. In a follow-up I'd like to abbreviate some things and look in to creating aliases for common accessors.
* There is a lingering lifetime hazard between the module and newly added operations. We have the facilities now to solve for this but I will do that in a follow-up.
* We may need to craft a more limited API for safely referencing successors when creating operations. We need more facilities to really prove that out and should defer for now.

Differential Revision: https://reviews.llvm.org/D87996
2020-09-23 07:57:50 -07:00

198 lines
5.9 KiB
Python

# RUN: %PYTHON %s | FileCheck %s
import gc
import itertools
import mlir
def run(f):
print("\nTEST:", f.__name__)
f()
gc.collect()
assert mlir.ir.Context._get_live_count() == 0
# Verify iterator based traversal of the op/region/block hierarchy.
# CHECK-LABEL: TEST: testTraverseOpRegionBlockIterators
def testTraverseOpRegionBlockIterators():
ctx = mlir.ir.Context()
ctx.allow_unregistered_dialects = True
module = ctx.parse_module(r"""
func @f1(%arg0: i32) -> i32 {
%1 = "custom.addi"(%arg0, %arg0) : (i32, i32) -> i32
return %1 : i32
}
""")
op = module.operation
# Get the block using iterators off of the named collections.
regions = list(op.regions)
blocks = list(regions[0].blocks)
# CHECK: MODULE REGIONS=1 BLOCKS=1
print(f"MODULE REGIONS={len(regions)} BLOCKS={len(blocks)}")
# Get the regions and blocks from the default collections.
default_regions = list(op)
default_blocks = list(default_regions[0])
# They should compare equal regardless of how obtained.
assert default_regions == regions
assert default_blocks == blocks
# Should be able to get the operations from either the named collection
# or the block.
operations = list(blocks[0].operations)
default_operations = list(blocks[0])
assert default_operations == operations
def walk_operations(indent, op):
for i, region in enumerate(op):
print(f"{indent}REGION {i}:")
for j, block in enumerate(region):
print(f"{indent} BLOCK {j}:")
for k, child_op in enumerate(block):
print(f"{indent} OP {k}: {child_op}")
walk_operations(indent + " ", child_op)
# CHECK: REGION 0:
# CHECK: BLOCK 0:
# CHECK: OP 0: func
# CHECK: REGION 0:
# CHECK: BLOCK 0:
# CHECK: OP 0: %0 = "custom.addi"
# CHECK: OP 1: return
# CHECK: OP 1: "module_terminator"
walk_operations("", op)
run(testTraverseOpRegionBlockIterators)
# Verify index based traversal of the op/region/block hierarchy.
# CHECK-LABEL: TEST: testTraverseOpRegionBlockIndices
def testTraverseOpRegionBlockIndices():
ctx = mlir.ir.Context()
ctx.allow_unregistered_dialects = True
module = ctx.parse_module(r"""
func @f1(%arg0: i32) -> i32 {
%1 = "custom.addi"(%arg0, %arg0) : (i32, i32) -> i32
return %1 : i32
}
""")
def walk_operations(indent, op):
for i in range(len(op.regions)):
region = op.regions[i]
print(f"{indent}REGION {i}:")
for j in range(len(region.blocks)):
block = region.blocks[j]
print(f"{indent} BLOCK {j}:")
for k in range(len(block.operations)):
child_op = block.operations[k]
print(f"{indent} OP {k}: {child_op}")
walk_operations(indent + " ", child_op)
# CHECK: REGION 0:
# CHECK: BLOCK 0:
# CHECK: OP 0: func
# CHECK: REGION 0:
# CHECK: BLOCK 0:
# CHECK: OP 0: %0 = "custom.addi"
# CHECK: OP 1: return
# CHECK: OP 1: "module_terminator"
walk_operations("", module.operation)
run(testTraverseOpRegionBlockIndices)
# CHECK-LABEL: TEST: testDetachedOperation
def testDetachedOperation():
ctx = mlir.ir.Context()
ctx.allow_unregistered_dialects = True
loc = ctx.get_unknown_location()
i32 = mlir.ir.IntegerType.get_signed(ctx, 32)
op1 = ctx.create_operation(
"custom.op1", loc, results=[i32, i32], regions=1, attributes={
"foo": mlir.ir.StringAttr.get(ctx, "foo_value"),
"bar": mlir.ir.StringAttr.get(ctx, "bar_value"),
})
# CHECK: %0:2 = "custom.op1"() ( {
# CHECK: }) {bar = "bar_value", foo = "foo_value"} : () -> (si32, si32)
print(op1)
# TODO: Check successors once enough infra exists to do it properly.
run(testDetachedOperation)
# CHECK-LABEL: TEST: testOperationInsert
def testOperationInsert():
ctx = mlir.ir.Context()
ctx.allow_unregistered_dialects = True
module = ctx.parse_module(r"""
func @f1(%arg0: i32) -> i32 {
%1 = "custom.addi"(%arg0, %arg0) : (i32, i32) -> i32
return %1 : i32
}
""")
# Create test op.
loc = ctx.get_unknown_location()
op1 = ctx.create_operation("custom.op1", loc)
op2 = ctx.create_operation("custom.op2", loc)
func = module.operation.regions[0].blocks[0].operations[0]
entry_block = func.regions[0].blocks[0]
entry_block.operations.insert(0, op1)
entry_block.operations.insert(1, op2)
# CHECK: func @f1
# CHECK: "custom.op1"()
# CHECK: "custom.op2"()
# CHECK: %0 = "custom.addi"
print(module)
# Trying to add a previously added op should raise.
try:
entry_block.operations.insert(0, op1)
except ValueError:
pass
else:
assert False, "expected insert of attached op to raise"
run(testOperationInsert)
# CHECK-LABEL: TEST: testOperationWithRegion
def testOperationWithRegion():
ctx = mlir.ir.Context()
ctx.allow_unregistered_dialects = True
loc = ctx.get_unknown_location()
i32 = mlir.ir.IntegerType.get_signed(ctx, 32)
op1 = ctx.create_operation("custom.op1", loc, regions=1)
block = op1.regions[0].blocks.append(i32, i32)
# CHECK: "custom.op1"() ( {
# CHECK: ^bb0(%arg0: si32, %arg1: si32): // no predecessors
# CHECK: "custom.terminator"() : () -> ()
# CHECK: }) : () -> ()
terminator = ctx.create_operation("custom.terminator", loc)
block.operations.insert(0, terminator)
print(op1)
# Now add the whole operation to another op.
# TODO: Verify lifetime hazard by nulling out the new owning module and
# accessing op1.
# TODO: Also verify accessing the terminator once both parents are nulled
# out.
module = ctx.parse_module(r"""
func @f1(%arg0: i32) -> i32 {
%1 = "custom.addi"(%arg0, %arg0) : (i32, i32) -> i32
return %1 : i32
}
""")
func = module.operation.regions[0].blocks[0].operations[0]
entry_block = func.regions[0].blocks[0]
entry_block.operations.insert(0, op1)
# CHECK: func @f1
# CHECK: "custom.op1"()
# CHECK: "custom.terminator"
# CHECK: %0 = "custom.addi"
print(module)
run(testOperationWithRegion)