This updates most (all?) error-diagnostic-emitting python APIs to
capture error diagnostics and include them in the raised exception's
message:
```
>>> Operation.parse('"arith.addi"() : () -> ()'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
mlir._mlir_libs.MLIRError: Unable to parse operation assembly:
error: "-":1:1: 'arith.addi' op requires one result
note: "-":1:1: see current operation: "arith.addi"() : () -> ()
```
The diagnostic information is available on the exception for users who
may want to customize the error message:
```
>>> try:
... Operation.parse('"arith.addi"() : () -> ()')
... except MLIRError as e:
... print(e.message)
... print(e.error_diagnostics)
... print(e.error_diagnostics[0].message)
...
Unable to parse operation assembly
[<mlir._mlir_libs._mlir.ir.DiagnosticInfo object at 0x7fed32bd6b70>]
'arith.addi' op requires one result
```
Error diagnostics captured in exceptions aren't propagated to diagnostic
handlers, to avoid double-reporting of errors. The context-level
`emit_error_diagnostics` option can be used to revert to the old
behaviour, causing error diagnostics to be reported to handlers instead
of as part of exceptions.
API changes:
- `Operation.verify` now raises an exception on verification failure,
instead of returning `false`
- The exception raised by the following methods has been changed to
`MLIRError`:
- `PassManager.run`
- `{Module,Operation,Type,Attribute}.parse`
- `{RankedTensorType,UnrankedTensorType}.get`
- `{MemRefType,UnrankedMemRefType}.get`
- `VectorType.get`
- `FloatAttr.get`
closes #60595
depends on D144804, D143830
Reviewed By: stellaraccident
Differential Revision: https://reviews.llvm.org/D143869
153 lines
4.1 KiB
Python
153 lines
4.1 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
|
|
# 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
|
|
|