2020-11-10 18:39:12 +00:00
# RUN: %PYTHON %s 2>&1 | FileCheck %s
2024-11-23 20:17:25 +01:00
import gc , os , sys , tempfile
2020-11-10 18:39:12 +00:00
from mlir . ir import *
from mlir . passmanager import *
2022-11-08 22:48:26 -05:00
from mlir . dialects . func import FuncOp
2023-10-20 20:28:32 -05:00
from mlir . dialects . builtin import ModuleOp
2020-11-10 18:39:12 +00:00
# Log everything to stderr and flush so that we have a unified stream to match
2020-11-10 19:56:48 +00:00
# errors/info emitted by MLIR to stderr.
2020-11-10 18:39:12 +00:00
def log ( * args ) :
print ( * args , file = sys . stderr )
sys . stderr . flush ( )
2023-05-17 16:53:39 +02:00
2020-11-10 18:39:12 +00:00
def run ( f ) :
log ( " \n TEST: " , f . __name__ )
f ( )
gc . collect ( )
assert Context . _get_live_count ( ) == 0
2023-05-17 16:53:39 +02:00
2020-11-10 18:39:12 +00:00
2020-11-10 20:59:18 -08:00
# Verify capsule interop.
# CHECK-LABEL: TEST: testCapsule
def testCapsule ( ) :
with Context ( ) :
pm = PassManager ( )
pm_capsule = pm . _CAPIPtr
assert ' " mlir.passmanager.PassManager._CAPIPtr " ' in repr ( pm_capsule )
pm . _testing_release ( )
pm1 = PassManager . _CAPICreate ( pm_capsule )
assert pm1 is not None # And does not crash.
2023-05-17 16:53:39 +02:00
2020-11-10 20:59:18 -08:00
run ( testCapsule )
2023-10-20 20:28:32 -05:00
2022-10-20 01:04:34 -04:00
# CHECK-LABEL: TEST: testConstruct
@run
def testConstruct ( ) :
with Context ( ) :
# CHECK: pm1: 'any()'
# CHECK: pm2: 'builtin.module()'
pm1 = PassManager ( )
pm2 = PassManager ( " builtin.module " )
log ( f " pm1: ' { pm1 } ' " )
log ( f " pm2: ' { pm2 } ' " )
2020-11-10 20:59:18 -08:00
2020-11-10 18:39:12 +00:00
# Verify successful round-trip.
# CHECK-LABEL: TEST: testParseSuccess
def testParseSuccess ( ) :
with Context ( ) :
[mlir] Overhaul C/Python registration APIs to properly scope registration/loading activities.
Since the very first commits, the Python and C MLIR APIs have had mis-placed registration/load functionality for dialects, extensions, etc. This was done pragmatically in order to get bootstrapped and then just grew in. Downstreams largely bypass and do their own thing by providing various APIs to register things they need. Meanwhile, the C++ APIs have stabilized around this and it would make sense to follow suit.
The thing we have observed in canonical usage by downstreams is that each downstream tends to have native entry points that configure its installation to its preferences with one-stop APIs. This patch leans in to this approach with `RegisterEverything.h` and `mlir._mlir_libs._mlirRegisterEverything` being the one-stop entry points for the "upstream packages". The `_mlir_libs.__init__.py` now allows customization of the environment and Context by adding "initialization modules" to the `_mlir_libs` package. If present, `_mlirRegisterEverything` is treated as such a module. Others can be added by downstreams by adding a `_site_initialize_{i}.py` module, where '{i}' is a number starting with zero. The number will be incremented and corresponding module loaded until one is not found. Initialization modules can:
* Perform load time customization to the global environment (i.e. registering passes, hooks, etc).
* Define a `register_dialects(registry: DialectRegistry)` function that can extend the `DialectRegistry` that will be used to bootstrap the `Context`.
* Define a `context_init_hook(context: Context)` function that will be added to a list of callbacks which will be invoked after dialect registration during `Context` initialization.
Note that the `MLIRPythonExtension.RegisterEverything` is not included by default when building a downstream (its corresponding behavior was prior). For downstreams which need the default MLIR initialization to take place, they must add this back in to their Python CMake build just like they add their own components (i.e. to `add_mlir_python_common_capi_library` and `add_mlir_python_modules`). It is perfectly valid to not do this, in which case, only the things explicitly depended on and initialized by downstreams will be built/packaged. If the downstream has not been set up for this, it is recommended to simply add this back for the time being and pay the build time/package size cost.
CMake changes:
* `MLIRCAPIRegistration` -> `MLIRCAPIRegisterEverything` (renamed to signify what it does and force an evaluation: a number of places were incidentally linking this very expensive target)
* `MLIRPythonSoure.Passes` removed (without replacement: just drop)
* `MLIRPythonExtension.AllPassesRegistration` removed (without replacement: just drop)
* `MLIRPythonExtension.Conversions` removed (without replacement: just drop)
* `MLIRPythonExtension.Transforms` removed (without replacement: just drop)
Header changes:
* `mlir-c/Registration.h` is deleted. Dialect registration functionality is now in `IR.h`. Registration of upstream features are in `mlir-c/RegisterEverything.h`. When updating MLIR and a couple of downstreams, I found that proper usage was commingled so required making a choice vs just blind S&R.
Python APIs removed:
* mlir.transforms and mlir.conversions (previously only had an __init__.py which indirectly triggered `mlirRegisterTransformsPasses()` and `mlirRegisterConversionPasses()` respectively). Downstream impact: Remove these imports if present (they now happen as part of default initialization).
* mlir._mlir_libs._all_passes_registration, mlir._mlir_libs._mlirTransforms, mlir._mlir_libs._mlirConversions. Downstream impact: None expected (these were internally used).
C-APIs changed:
* mlirRegisterAllDialects(MlirContext) now takes an MlirDialectRegistry instead. It also used to trigger loading of all dialects, which was already marked with a TODO to remove -- it no longer does, and for direct use, dialects must be explicitly loaded. Downstream impact: Direct C-API users must ensure that needed dialects are loaded or call `mlirContextLoadAllAvailableDialects(MlirContext)` to emulate the prior behavior. Also see the `ir.c` test case (e.g. ` mlirContextGetOrLoadDialect(ctx, mlirStringRefCreateFromCString("func"));`).
* mlirDialectHandle* APIs were moved from Registration.h (which now is restricted to just global/upstream registration) to IR.h, arguably where it should have been. Downstream impact: include correct header (likely already doing so).
C-APIs added:
* mlirContextLoadAllAvailableDialects(MlirContext): Corresponds to C++ API with the same purpose.
Python APIs added:
* mlir.ir.DialectRegistry: Mapping for an MlirDialectRegistry.
* mlir.ir.Context.append_dialect_registry(MlirDialectRegistry)
* mlir.ir.Context.load_all_available_dialects()
* mlir._mlir_libs._mlirAllRegistration: New native extension that exposes a `register_dialects(MlirDialectRegistry)` entry point and performs all upstream pass/conversion/transforms registration on init. In this first step, we eagerly load this as part of the __init__.py and use it to monkey patch the Context to emulate prior behavior.
* Type caster and capsule support for MlirDialectRegistry
This should make it possible to build downstream Python dialects that only depend on a subset of MLIR. See: https://github.com/llvm/llvm-project/issues/56037
Here is an example PR, minimally adapting IREE to these changes: https://github.com/iree-org/iree/pull/9638/files In this situation, IREE is opting to not link everything, since it is already configuring the Context to its liking. For projects that would just like to not think about it and pull in everything, add `MLIRPythonExtension.RegisterEverything` to the list of Python sources getting built, and the old behavior will continue.
Reviewed By: mehdi_amini, ftynse
Differential Revision: https://reviews.llvm.org/D128593
2022-07-16 16:09:03 -07:00
# An unregistered pass should not parse.
2020-11-10 18:39:12 +00:00
try :
[mlir] Overhaul C/Python registration APIs to properly scope registration/loading activities.
Since the very first commits, the Python and C MLIR APIs have had mis-placed registration/load functionality for dialects, extensions, etc. This was done pragmatically in order to get bootstrapped and then just grew in. Downstreams largely bypass and do their own thing by providing various APIs to register things they need. Meanwhile, the C++ APIs have stabilized around this and it would make sense to follow suit.
The thing we have observed in canonical usage by downstreams is that each downstream tends to have native entry points that configure its installation to its preferences with one-stop APIs. This patch leans in to this approach with `RegisterEverything.h` and `mlir._mlir_libs._mlirRegisterEverything` being the one-stop entry points for the "upstream packages". The `_mlir_libs.__init__.py` now allows customization of the environment and Context by adding "initialization modules" to the `_mlir_libs` package. If present, `_mlirRegisterEverything` is treated as such a module. Others can be added by downstreams by adding a `_site_initialize_{i}.py` module, where '{i}' is a number starting with zero. The number will be incremented and corresponding module loaded until one is not found. Initialization modules can:
* Perform load time customization to the global environment (i.e. registering passes, hooks, etc).
* Define a `register_dialects(registry: DialectRegistry)` function that can extend the `DialectRegistry` that will be used to bootstrap the `Context`.
* Define a `context_init_hook(context: Context)` function that will be added to a list of callbacks which will be invoked after dialect registration during `Context` initialization.
Note that the `MLIRPythonExtension.RegisterEverything` is not included by default when building a downstream (its corresponding behavior was prior). For downstreams which need the default MLIR initialization to take place, they must add this back in to their Python CMake build just like they add their own components (i.e. to `add_mlir_python_common_capi_library` and `add_mlir_python_modules`). It is perfectly valid to not do this, in which case, only the things explicitly depended on and initialized by downstreams will be built/packaged. If the downstream has not been set up for this, it is recommended to simply add this back for the time being and pay the build time/package size cost.
CMake changes:
* `MLIRCAPIRegistration` -> `MLIRCAPIRegisterEverything` (renamed to signify what it does and force an evaluation: a number of places were incidentally linking this very expensive target)
* `MLIRPythonSoure.Passes` removed (without replacement: just drop)
* `MLIRPythonExtension.AllPassesRegistration` removed (without replacement: just drop)
* `MLIRPythonExtension.Conversions` removed (without replacement: just drop)
* `MLIRPythonExtension.Transforms` removed (without replacement: just drop)
Header changes:
* `mlir-c/Registration.h` is deleted. Dialect registration functionality is now in `IR.h`. Registration of upstream features are in `mlir-c/RegisterEverything.h`. When updating MLIR and a couple of downstreams, I found that proper usage was commingled so required making a choice vs just blind S&R.
Python APIs removed:
* mlir.transforms and mlir.conversions (previously only had an __init__.py which indirectly triggered `mlirRegisterTransformsPasses()` and `mlirRegisterConversionPasses()` respectively). Downstream impact: Remove these imports if present (they now happen as part of default initialization).
* mlir._mlir_libs._all_passes_registration, mlir._mlir_libs._mlirTransforms, mlir._mlir_libs._mlirConversions. Downstream impact: None expected (these were internally used).
C-APIs changed:
* mlirRegisterAllDialects(MlirContext) now takes an MlirDialectRegistry instead. It also used to trigger loading of all dialects, which was already marked with a TODO to remove -- it no longer does, and for direct use, dialects must be explicitly loaded. Downstream impact: Direct C-API users must ensure that needed dialects are loaded or call `mlirContextLoadAllAvailableDialects(MlirContext)` to emulate the prior behavior. Also see the `ir.c` test case (e.g. ` mlirContextGetOrLoadDialect(ctx, mlirStringRefCreateFromCString("func"));`).
* mlirDialectHandle* APIs were moved from Registration.h (which now is restricted to just global/upstream registration) to IR.h, arguably where it should have been. Downstream impact: include correct header (likely already doing so).
C-APIs added:
* mlirContextLoadAllAvailableDialects(MlirContext): Corresponds to C++ API with the same purpose.
Python APIs added:
* mlir.ir.DialectRegistry: Mapping for an MlirDialectRegistry.
* mlir.ir.Context.append_dialect_registry(MlirDialectRegistry)
* mlir.ir.Context.load_all_available_dialects()
* mlir._mlir_libs._mlirAllRegistration: New native extension that exposes a `register_dialects(MlirDialectRegistry)` entry point and performs all upstream pass/conversion/transforms registration on init. In this first step, we eagerly load this as part of the __init__.py and use it to monkey patch the Context to emulate prior behavior.
* Type caster and capsule support for MlirDialectRegistry
This should make it possible to build downstream Python dialects that only depend on a subset of MLIR. See: https://github.com/llvm/llvm-project/issues/56037
Here is an example PR, minimally adapting IREE to these changes: https://github.com/iree-org/iree/pull/9638/files In this situation, IREE is opting to not link everything, since it is already configuring the Context to its liking. For projects that would just like to not think about it and pull in everything, add `MLIRPythonExtension.RegisterEverything` to the list of Python sources getting built, and the old behavior will continue.
Reviewed By: mehdi_amini, ftynse
Differential Revision: https://reviews.llvm.org/D128593
2022-07-16 16:09:03 -07:00
pm = PassManager . parse (
" builtin.module(func.func(not-existing-pass { json=false})) "
)
2020-11-10 18:39:12 +00:00
except ValueError as e :
2022-10-20 16:40:32 -04:00
# CHECK: ValueError exception: {{.+}} 'not-existing-pass' does not refer to a registered pass
2020-11-10 18:39:12 +00:00
log ( " ValueError exception: " , e )
else :
log ( " Exception not produced " )
2023-05-17 16:53:39 +02:00
[mlir] Overhaul C/Python registration APIs to properly scope registration/loading activities.
Since the very first commits, the Python and C MLIR APIs have had mis-placed registration/load functionality for dialects, extensions, etc. This was done pragmatically in order to get bootstrapped and then just grew in. Downstreams largely bypass and do their own thing by providing various APIs to register things they need. Meanwhile, the C++ APIs have stabilized around this and it would make sense to follow suit.
The thing we have observed in canonical usage by downstreams is that each downstream tends to have native entry points that configure its installation to its preferences with one-stop APIs. This patch leans in to this approach with `RegisterEverything.h` and `mlir._mlir_libs._mlirRegisterEverything` being the one-stop entry points for the "upstream packages". The `_mlir_libs.__init__.py` now allows customization of the environment and Context by adding "initialization modules" to the `_mlir_libs` package. If present, `_mlirRegisterEverything` is treated as such a module. Others can be added by downstreams by adding a `_site_initialize_{i}.py` module, where '{i}' is a number starting with zero. The number will be incremented and corresponding module loaded until one is not found. Initialization modules can:
* Perform load time customization to the global environment (i.e. registering passes, hooks, etc).
* Define a `register_dialects(registry: DialectRegistry)` function that can extend the `DialectRegistry` that will be used to bootstrap the `Context`.
* Define a `context_init_hook(context: Context)` function that will be added to a list of callbacks which will be invoked after dialect registration during `Context` initialization.
Note that the `MLIRPythonExtension.RegisterEverything` is not included by default when building a downstream (its corresponding behavior was prior). For downstreams which need the default MLIR initialization to take place, they must add this back in to their Python CMake build just like they add their own components (i.e. to `add_mlir_python_common_capi_library` and `add_mlir_python_modules`). It is perfectly valid to not do this, in which case, only the things explicitly depended on and initialized by downstreams will be built/packaged. If the downstream has not been set up for this, it is recommended to simply add this back for the time being and pay the build time/package size cost.
CMake changes:
* `MLIRCAPIRegistration` -> `MLIRCAPIRegisterEverything` (renamed to signify what it does and force an evaluation: a number of places were incidentally linking this very expensive target)
* `MLIRPythonSoure.Passes` removed (without replacement: just drop)
* `MLIRPythonExtension.AllPassesRegistration` removed (without replacement: just drop)
* `MLIRPythonExtension.Conversions` removed (without replacement: just drop)
* `MLIRPythonExtension.Transforms` removed (without replacement: just drop)
Header changes:
* `mlir-c/Registration.h` is deleted. Dialect registration functionality is now in `IR.h`. Registration of upstream features are in `mlir-c/RegisterEverything.h`. When updating MLIR and a couple of downstreams, I found that proper usage was commingled so required making a choice vs just blind S&R.
Python APIs removed:
* mlir.transforms and mlir.conversions (previously only had an __init__.py which indirectly triggered `mlirRegisterTransformsPasses()` and `mlirRegisterConversionPasses()` respectively). Downstream impact: Remove these imports if present (they now happen as part of default initialization).
* mlir._mlir_libs._all_passes_registration, mlir._mlir_libs._mlirTransforms, mlir._mlir_libs._mlirConversions. Downstream impact: None expected (these were internally used).
C-APIs changed:
* mlirRegisterAllDialects(MlirContext) now takes an MlirDialectRegistry instead. It also used to trigger loading of all dialects, which was already marked with a TODO to remove -- it no longer does, and for direct use, dialects must be explicitly loaded. Downstream impact: Direct C-API users must ensure that needed dialects are loaded or call `mlirContextLoadAllAvailableDialects(MlirContext)` to emulate the prior behavior. Also see the `ir.c` test case (e.g. ` mlirContextGetOrLoadDialect(ctx, mlirStringRefCreateFromCString("func"));`).
* mlirDialectHandle* APIs were moved from Registration.h (which now is restricted to just global/upstream registration) to IR.h, arguably where it should have been. Downstream impact: include correct header (likely already doing so).
C-APIs added:
* mlirContextLoadAllAvailableDialects(MlirContext): Corresponds to C++ API with the same purpose.
Python APIs added:
* mlir.ir.DialectRegistry: Mapping for an MlirDialectRegistry.
* mlir.ir.Context.append_dialect_registry(MlirDialectRegistry)
* mlir.ir.Context.load_all_available_dialects()
* mlir._mlir_libs._mlirAllRegistration: New native extension that exposes a `register_dialects(MlirDialectRegistry)` entry point and performs all upstream pass/conversion/transforms registration on init. In this first step, we eagerly load this as part of the __init__.py and use it to monkey patch the Context to emulate prior behavior.
* Type caster and capsule support for MlirDialectRegistry
This should make it possible to build downstream Python dialects that only depend on a subset of MLIR. See: https://github.com/llvm/llvm-project/issues/56037
Here is an example PR, minimally adapting IREE to these changes: https://github.com/iree-org/iree/pull/9638/files In this situation, IREE is opting to not link everything, since it is already configuring the Context to its liking. For projects that would just like to not think about it and pull in everything, add `MLIRPythonExtension.RegisterEverything` to the list of Python sources getting built, and the old behavior will continue.
Reviewed By: mehdi_amini, ftynse
Differential Revision: https://reviews.llvm.org/D128593
2022-07-16 16:09:03 -07:00
# A registered pass should parse successfully.
2022-06-14 14:16:26 -07:00
pm = PassManager . parse ( " builtin.module(func.func(print-op-stats { json=false})) " )
2022-10-19 23:36:15 -04:00
# CHECK: Roundtrip: builtin.module(func.func(print-op-stats{json=false}))
2020-11-10 18:39:12 +00:00
log ( " Roundtrip: " , pm )
2023-05-17 16:53:39 +02:00
2020-11-10 18:39:12 +00:00
run ( testParseSuccess )
2023-10-20 20:28:32 -05:00
2023-01-30 12:49:19 -08:00
# Verify successful round-trip.
# CHECK-LABEL: TEST: testParseSpacedPipeline
def testParseSpacedPipeline ( ) :
with Context ( ) :
# A registered pass should parse successfully even if has extras spaces for readability
pm = PassManager . parse (
""" builtin.module(
func . func ( print - op - stats { json = false } )
) """
)
# CHECK: Roundtrip: builtin.module(func.func(print-op-stats{json=false}))
log ( " Roundtrip: " , pm )
2023-05-17 16:53:39 +02:00
2023-01-30 12:49:19 -08:00
run ( testParseSpacedPipeline )
2023-10-20 20:28:32 -05:00
2020-11-10 18:39:12 +00:00
# Verify failure on unregistered pass.
# CHECK-LABEL: TEST: testParseFail
def testParseFail ( ) :
with Context ( ) :
try :
2022-10-19 23:36:15 -04:00
pm = PassManager . parse ( " any(unknown-pass) " )
2020-11-10 18:39:12 +00:00
except ValueError as e :
2022-10-20 16:40:32 -04:00
# CHECK: ValueError exception: MLIR Textual PassPipeline Parser:1:1: error:
# CHECK-SAME: 'unknown-pass' does not refer to a registered pass or pass pipeline
# CHECK: unknown-pass
# CHECK: ^
2020-11-10 18:39:12 +00:00
log ( " ValueError exception: " , e )
else :
log ( " Exception not produced " )
2023-05-17 16:53:39 +02:00
2020-11-10 18:39:12 +00:00
run ( testParseFail )
2020-11-10 19:56:48 +00:00
2023-10-20 20:28:32 -05:00
2022-10-20 00:27:09 -04:00
# Check that adding to a pass manager works
# CHECK-LABEL: TEST: testAdd
@run
def testAdd ( ) :
pm = PassManager ( " any " , Context ( ) )
# CHECK: pm: 'any()'
log ( f " pm: ' { pm } ' " )
# CHECK: pm: 'any(cse)'
pm . add ( " cse " )
log ( f " pm: ' { pm } ' " )
# CHECK: pm: 'any(cse,cse)'
pm . add ( " cse " )
log ( f " pm: ' { pm } ' " )
2020-11-10 19:56:48 +00:00
2020-11-11 05:33:47 +00:00
# Verify failure on incorrect level of nesting.
# CHECK-LABEL: TEST: testInvalidNesting
def testInvalidNesting ( ) :
with Context ( ) :
try :
2022-03-07 19:16:03 -08:00
pm = PassManager . parse ( " func.func(normalize-memrefs) " )
2020-11-11 05:33:47 +00:00
except ValueError as e :
2025-04-23 16:50:00 +02:00
# CHECK: ValueError exception: Can't add pass 'NormalizeMemRefsPass' restricted to 'builtin.module' on a PassManager intended to run on 'func.func', did you intend to nest?
2020-11-11 05:33:47 +00:00
log ( " ValueError exception: " , e )
else :
log ( " Exception not produced " )
2023-05-17 16:53:39 +02:00
2020-11-11 05:33:47 +00:00
run ( testInvalidNesting )
2020-11-10 19:56:48 +00:00
# Verify that a pass manager can execute on IR
[mlir][python] Capture error diagnostics in exceptions
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
2023-02-07 16:07:50 -05:00
# CHECK-LABEL: TEST: testRunPipeline
2020-11-10 19:56:48 +00:00
def testRunPipeline ( ) :
with Context ( ) :
2022-11-08 22:48:26 -05:00
pm = PassManager . parse ( " any(print-op-stats { json=false}) " )
func = FuncOp . parse ( r """ func.func @successfulParse() { return } """ )
pm . run ( func )
2023-05-17 16:53:39 +02:00
2020-11-10 19:56:48 +00:00
# CHECK: Operations encountered:
2022-03-07 19:16:03 -08:00
# CHECK: func.func , 1
2022-02-26 14:49:54 -08:00
# CHECK: func.return , 1
2020-11-10 19:56:48 +00:00
run ( testRunPipeline )
[mlir][python] Capture error diagnostics in exceptions
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
2023-02-07 16:07:50 -05:00
2023-10-20 20:28:32 -05:00
[mlir][python] Capture error diagnostics in exceptions
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
2023-02-07 16:07:50 -05:00
# CHECK-LABEL: TEST: testRunPipelineError
@run
def testRunPipelineError ( ) :
with Context ( ) as ctx :
ctx . allow_unregistered_dialects = True
op = Operation . parse ( ' " test.op " () : () -> () ' )
pm = PassManager . parse ( " any(cse) " )
try :
pm . run ( op )
except MLIRError as e :
# CHECK: Exception: <
# CHECK: Failure while executing pass pipeline:
# CHECK: error: "-":1:1: 'test.op' op trying to schedule a pass on an unregistered operation
# CHECK: note: "-":1:1: see current operation: "test.op"() : () -> ()
# CHECK: >
2023-10-20 20:28:32 -05:00
log ( f " Exception: < { e } > " )
# CHECK-LABEL: TEST: testPostPassOpInvalidation
@run
def testPostPassOpInvalidation ( ) :
with Context ( ) as ctx :
module = ModuleOp . parse (
"""
module {
arith . constant 10
func . func @foo ( ) {
arith . constant 10
return
}
}
"""
)
outer_const_op = module . body . operations [ 0 ]
# CHECK: %[[VAL0:.*]] = arith.constant 10 : i64
log ( outer_const_op )
func_op = module . body . operations [ 1 ]
# CHECK: func.func @[[FOO:.*]]() {
# CHECK: %[[VAL1:.*]] = arith.constant 10 : i64
# CHECK: return
# CHECK: }
log ( func_op )
inner_const_op = func_op . body . blocks [ 0 ] . operations [ 0 ]
# CHECK: %[[VAL1]] = arith.constant 10 : i64
log ( inner_const_op )
2025-09-01 21:53:33 -07:00
PassManager . parse ( " builtin.module(canonicalize) " ) . run ( module )
2023-10-20 20:28:32 -05:00
# CHECK: func.func @foo() {
# CHECK: return
# CHECK: }
log ( func_op )
# CHECK: func.func @foo() {
# CHECK: return
# CHECK: }
log ( module )
# CHECK: invalidate_ops=True
log ( " invalidate_ops=True " )
module = ModuleOp . parse (
"""
module {
arith . constant 10
func . func @foo ( ) {
arith . constant 10
return
}
}
"""
)
2023-10-25 07:17:56 +02:00
2023-10-20 20:28:32 -05:00
PassManager . parse ( " builtin.module(canonicalize) " ) . run ( module )
2023-10-25 07:17:56 +02:00
2025-09-01 21:53:33 -07:00
func_op . _set_invalid ( )
2023-10-20 20:28:32 -05:00
try :
log ( func_op )
except RuntimeError as e :
# CHECK: the operation has been invalidated
log ( e )
2025-09-01 21:53:33 -07:00
outer_const_op . _set_invalid ( )
2023-10-20 20:28:32 -05:00
try :
log ( outer_const_op )
except RuntimeError as e :
# CHECK: the operation has been invalidated
log ( e )
2025-09-01 21:53:33 -07:00
inner_const_op . _set_invalid ( )
2023-10-20 20:28:32 -05:00
try :
log ( inner_const_op )
except RuntimeError as e :
# CHECK: the operation has been invalidated
log ( e )
# CHECK: func.func @foo() {
# CHECK: return
# CHECK: }
log ( module )
2023-12-17 20:24:47 +01:00
# CHECK-LABEL: TEST: testPrintIrAfterAll
@run
def testPrintIrAfterAll ( ) :
with Context ( ) as ctx :
module = ModuleOp . parse (
"""
module {
func . func @main ( ) {
% 0 = arith . constant 10
return
}
}
"""
)
pm = PassManager . parse ( " builtin.module(canonicalize) " )
ctx . enable_multithreading ( False )
pm . enable_ir_printing ( )
2024-09-18 11:54:16 +08:00
# CHECK: // -----// IR Dump After Canonicalizer (canonicalize) //----- //
# CHECK: module {
# CHECK: func.func @main() {
# CHECK: return
# CHECK: }
# CHECK: }
pm . run ( module )
# CHECK-LABEL: TEST: testPrintIrBeforeAndAfterAll
@run
def testPrintIrBeforeAndAfterAll ( ) :
with Context ( ) as ctx :
module = ModuleOp . parse (
"""
module {
func . func @main ( ) {
% 0 = arith . constant 10
return
}
}
"""
)
pm = PassManager . parse ( " builtin.module(canonicalize) " )
ctx . enable_multithreading ( False )
pm . enable_ir_printing ( print_before_all = True , print_after_all = True )
# CHECK: // -----// IR Dump Before Canonicalizer (canonicalize) //----- //
2023-12-17 20:24:47 +01:00
# CHECK: module {
# CHECK: func.func @main() {
# CHECK: %[[C10:.*]] = arith.constant 10 : i64
# CHECK: return
# CHECK: }
# CHECK: }
2024-09-18 11:54:16 +08:00
# CHECK: // -----// IR Dump After Canonicalizer (canonicalize) //----- //
2023-12-17 20:24:47 +01:00
# CHECK: module {
# CHECK: func.func @main() {
# CHECK: return
# CHECK: }
# CHECK: }
pm . run ( module )
2024-11-23 20:17:25 +01:00
2024-12-05 17:31:04 +08:00
# CHECK-LABEL: TEST: testPrintIrLargeLimitElements
@run
def testPrintIrLargeLimitElements ( ) :
with Context ( ) as ctx :
module = ModuleOp . parse (
"""
module {
func . func @main ( ) - > tensor < 3 xi64 > {
% 0 = arith . constant dense < [ 1 , 2 , 3 ] > : tensor < 3 xi64 >
return % 0 : tensor < 3 xi64 >
}
}
"""
)
pm = PassManager . parse ( " builtin.module(canonicalize) " )
ctx . enable_multithreading ( False )
pm . enable_ir_printing ( large_elements_limit = 2 )
# CHECK: %[[CST:.*]] = arith.constant dense_resource<__elided__> : tensor<3xi64>
pm . run ( module )
2025-07-17 12:57:04 -04:00
# CHECK-LABEL: TEST: testPrintIrLargeResourceLimit
@run
def testPrintIrLargeResourceLimit ( ) :
with Context ( ) as ctx :
module = ModuleOp . parse (
"""
module {
func . func @main ( ) - > tensor < 3 xi64 > {
% 0 = arith . constant dense_resource < blob1 > : tensor < 3 xi64 >
return % 0 : tensor < 3 xi64 >
}
}
{ - #
dialect_resources : {
builtin : {
blob1 : " 0x010000000000000002000000000000000300000000000000 "
}
}
#-}
"""
)
pm = PassManager . parse ( " builtin.module(canonicalize) " )
ctx . enable_multithreading ( False )
pm . enable_ir_printing ( large_resource_limit = 4 )
# CHECK-NOT: blob1: "0x01
pm . run ( module )
# CHECK-LABEL: TEST: testPrintIrLargeResourceLimitVsElementsLimit
@run
def testPrintIrLargeResourceLimitVsElementsLimit ( ) :
""" Test that large_elements_limit does not affect the printing of resources. """
with Context ( ) as ctx :
module = ModuleOp . parse (
"""
module {
func . func @main ( ) - > tensor < 3 xi64 > {
% 0 = arith . constant dense_resource < blob1 > : tensor < 3 xi64 >
return % 0 : tensor < 3 xi64 >
}
}
{ - #
dialect_resources : {
builtin : {
blob1 : " 0x010000000000000002000000000000000300000000000000 "
}
}
#-}
"""
)
pm = PassManager . parse ( " builtin.module(canonicalize) " )
ctx . enable_multithreading ( False )
pm . enable_ir_printing ( large_elements_limit = 1 )
# CHECK-NOT: blob1: "0x01
pm . run ( module )
2024-11-23 20:17:25 +01:00
# CHECK-LABEL: TEST: testPrintIrTree
@run
def testPrintIrTree ( ) :
with Context ( ) as ctx :
module = ModuleOp . parse (
"""
module {
func . func @main ( ) {
% 0 = arith . constant 10
return
}
}
"""
)
pm = PassManager . parse ( " builtin.module(canonicalize) " )
ctx . enable_multithreading ( False )
pm . enable_ir_printing ( )
# CHECK-LABEL: // Tree printing begin
# CHECK: \-- builtin_module_no-symbol-name
# CHECK: \-- 0_canonicalize.mlir
# CHECK-LABEL: // Tree printing end
pm . run ( module )
log ( " // Tree printing begin " )
with tempfile . TemporaryDirectory ( ) as temp_dir :
pm . enable_ir_printing ( tree_printing_dir_path = temp_dir )
pm . run ( module )
def print_file_tree ( directory , prefix = " " ) :
entries = sorted ( os . listdir ( directory ) )
for i , entry in enumerate ( entries ) :
path = os . path . join ( directory , entry )
connector = " \ -- " if i == len ( entries ) - 1 else " |-- "
log ( f " { prefix } { connector } { entry } " )
if os . path . isdir ( path ) :
print_file_tree (
path , prefix + ( " " if i == len ( entries ) - 1 else " │ " )
)
print_file_tree ( temp_dir )
log ( " // Tree printing end " )
2025-10-09 20:11:19 +08:00
# CHECK-LABEL: TEST: testEnableStatistics
@run
def testEnableStatistics ( ) :
with Context ( ) as ctx :
module = ModuleOp . parse (
"""
module {
func . func @main ( ) {
% 0 = arith . constant 10
return
}
}
"""
)
pm = PassManager . parse ( " builtin.module(canonicalize) " )
pm . enable_statistics ( )
# CHECK: Pass statistics report
pm . run ( module )