Currently, a method exists to get the count of the operation objects
which are still alive. This helps for sanity checking, but isn't
terribly useful for debugging. This new method returns the actual
operation objects which are still alive.
This allows Python code like the following:
```
gc.collect()
live_ops = ir.Context.current._get_live_operation_objects()
for op in live_ops:
print(f"Warning: {op} is still live. Referrers:")
for referrer in gc.get_referrers(op)[0]:
print(f" {referrer}")
```
162 lines
4.4 KiB
Python
162 lines
4.4 KiB
Python
# RUN: %PYTHON %s | FileCheck %s
|
|
|
|
import gc
|
|
from mlir.ir import *
|
|
|
|
|
|
def run(f):
|
|
print("\nTEST:", f.__name__)
|
|
f()
|
|
gc.collect()
|
|
assert Context._get_live_count() == 0
|
|
return f
|
|
|
|
|
|
# Verify successful parse.
|
|
# CHECK-LABEL: TEST: testParseSuccess
|
|
# CHECK: module @successfulParse
|
|
@run
|
|
def testParseSuccess():
|
|
ctx = Context()
|
|
module = Module.parse(r"""module @successfulParse {}""", ctx)
|
|
assert module.context is ctx
|
|
print("CLEAR CONTEXT")
|
|
ctx = None # Ensure that module captures the context.
|
|
gc.collect()
|
|
module.dump() # Just outputs to stderr. Verifies that it functions.
|
|
print(str(module))
|
|
|
|
|
|
# Verify parse error.
|
|
# CHECK-LABEL: TEST: testParseError
|
|
# CHECK: testParseError: <
|
|
# CHECK: Unable to parse module assembly:
|
|
# CHECK: error: "-":1:1: expected operation name in quotes
|
|
# CHECK: >
|
|
@run
|
|
def testParseError():
|
|
ctx = Context()
|
|
try:
|
|
module = Module.parse(r"""}SYNTAX ERROR{""", ctx)
|
|
except MLIRError as e:
|
|
print(f"testParseError: <{e}>")
|
|
else:
|
|
print("Exception not produced")
|
|
|
|
|
|
# Verify successful parse.
|
|
# CHECK-LABEL: TEST: testCreateEmpty
|
|
# CHECK: module {
|
|
@run
|
|
def testCreateEmpty():
|
|
ctx = Context()
|
|
loc = Location.unknown(ctx)
|
|
module = Module.create(loc)
|
|
print("CLEAR CONTEXT")
|
|
ctx = None # Ensure that module captures the context.
|
|
gc.collect()
|
|
print(str(module))
|
|
|
|
|
|
# Verify round-trip of ASM that contains unicode.
|
|
# Note that this does not test that the print path converts unicode properly
|
|
# because MLIR asm always normalizes it to the hex encoding.
|
|
# CHECK-LABEL: TEST: testRoundtripUnicode
|
|
# CHECK: func private @roundtripUnicode()
|
|
# CHECK: foo = "\F0\9F\98\8A"
|
|
@run
|
|
def testRoundtripUnicode():
|
|
ctx = Context()
|
|
module = Module.parse(
|
|
r"""
|
|
func.func private @roundtripUnicode() attributes { foo = "😊" }
|
|
""",
|
|
ctx,
|
|
)
|
|
print(str(module))
|
|
|
|
|
|
# Verify round-trip of ASM that contains unicode.
|
|
# Note that this does not test that the print path converts unicode properly
|
|
# because MLIR asm always normalizes it to the hex encoding.
|
|
# CHECK-LABEL: TEST: testRoundtripBinary
|
|
# CHECK: func private @roundtripUnicode()
|
|
# CHECK: foo = "\F0\9F\98\8A"
|
|
@run
|
|
def testRoundtripBinary():
|
|
with Context():
|
|
module = Module.parse(
|
|
r"""
|
|
func.func private @roundtripUnicode() attributes { foo = "😊" }
|
|
"""
|
|
)
|
|
binary_asm = module.operation.get_asm(binary=True)
|
|
assert isinstance(binary_asm, bytes)
|
|
module = Module.parse(binary_asm)
|
|
print(module)
|
|
|
|
|
|
# Tests that module.operation works and correctly interns instances.
|
|
# CHECK-LABEL: TEST: testModuleOperation
|
|
@run
|
|
def testModuleOperation():
|
|
ctx = Context()
|
|
module = Module.parse(r"""module @successfulParse {}""", ctx)
|
|
assert ctx._get_live_module_count() == 1
|
|
op1 = module.operation
|
|
assert ctx._get_live_operation_count() == 1
|
|
live_ops = ctx._get_live_operation_objects()
|
|
assert len(live_ops) == 1
|
|
assert live_ops[0] is op1
|
|
live_ops = None
|
|
# CHECK: module @successfulParse
|
|
print(op1)
|
|
|
|
# Ensure that operations are the same on multiple calls.
|
|
op2 = module.operation
|
|
assert ctx._get_live_operation_count() == 1
|
|
assert op1 is op2
|
|
|
|
# Test live operation clearing.
|
|
op1 = module.operation
|
|
assert ctx._get_live_operation_count() == 1
|
|
num_invalidated = ctx._clear_live_operations()
|
|
assert num_invalidated == 1
|
|
assert ctx._get_live_operation_count() == 0
|
|
op1 = None
|
|
gc.collect()
|
|
op1 = module.operation
|
|
|
|
# Ensure that if module is de-referenced, the operations are still valid.
|
|
module = None
|
|
gc.collect()
|
|
print(op1)
|
|
|
|
# Collect and verify lifetime.
|
|
op1 = None
|
|
op2 = None
|
|
gc.collect()
|
|
print("LIVE OPERATIONS:", ctx._get_live_operation_count())
|
|
assert ctx._get_live_operation_count() == 0
|
|
assert ctx._get_live_module_count() == 0
|
|
|
|
|
|
# CHECK-LABEL: TEST: testModuleCapsule
|
|
@run
|
|
def testModuleCapsule():
|
|
ctx = Context()
|
|
module = Module.parse(r"""module @successfulParse {}""", ctx)
|
|
assert ctx._get_live_module_count() == 1
|
|
# CHECK: "mlir.ir.Module._CAPIPtr"
|
|
module_capsule = module._CAPIPtr
|
|
print(module_capsule)
|
|
module_dup = Module._CAPICreate(module_capsule)
|
|
assert module is module_dup
|
|
assert module_dup.context is ctx
|
|
# Gc and verify destructed.
|
|
module = None
|
|
module_capsule = None
|
|
module_dup = None
|
|
gc.collect()
|
|
assert ctx._get_live_module_count() == 0
|