You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This PR replaces the mixin `OpView` extension mechanism with the
standard inheritance mechanism.
Why? Firstly, mixins are not very pythonic (inheritance is usually used
for this), a little convoluted, and too "tight" (can only be used in the
immediately adjacent `_ext.py`). Secondly, it (mixins) are now blocking
are correct implementation of "value builders" (see
[here](llvm#68764)) where the
problem becomes how to choose the correct base class that the value
builder should call.
This PR looks big/complicated but appearances are deceiving; 4 things
were needed to make this work:
1. Drop `skipDefaultBuilders` in
`OpPythonBindingGen::emitDefaultOpBuilders`
2. Former mixin extension classes are converted to inherit from the
generated `OpView` instead of being "mixins"
a. extension classes that simply were calling into an already generated
`super().__init__` continue to do so
b. (almost all) extension classes that were calling `self.build_generic`
because of a lack of default builder being generated can now also just
call `super().__init__`
3. To handle the [lone single
use-case](https://sourcegraph.com/search?q=context%3Aglobal+select_opview_mixin&patternType=standard&sm=1&groupBy=repo)
of `select_opview_mixin`, namely
[linalg](https://github.com/llvm/llvm-project/blob/main/mlir/python/mlir/dialects/_linalg_ops_ext.py#L38),
only a small change was necessary in `opdsl/lang/emitter.py` (thanks to
the emission/generation of default builders/`__init__`s)
4. since the `extend_opview_class` decorator is removed, we need a way
to register extension classes as the desired `OpView` that `op.opview`
conjures into existence; so we do the standard thing and just enable
replacing the existing registered `OpView` i.e.,
`register_operation(_Dialect, replace=True)`.
Note, the upgrade path for the common case is to change an extension to
inherit from the generated builder and decorate it with
`register_operation(_Dialect, replace=True)`. In the slightly more
complicated case where `super().__init(self.build_generic(...))` is
called in the extension's `__init__`, this needs to be updated to call
`__init__` in `OpView`, i.e., the grandparent (see updated docs).
Note, also `<DIALECT>_ext.py` files/modules will no longer be automatically loaded.
Note, the PR has 3 base commits that look funny but this was done for
the purpose of tracking the line history of moving the
`<DIALECT>_ops_ext.py` class into `<DIALECT>.py` and updating (commit
labeled "fix").
raiseNotImplementedError(f"Building `arith.constant` not supported for {result=}{value=}")
1042
1058
```
1043
1059
1044
-
Then for each generated concrete `OpView` subclass, it will apply a decorator
1045
-
like:
1060
+
which enables building an instance of `arith.constant` like so:
1046
1061
1047
1062
```python
1048
-
@_ods_cext.register_operation(_Dialect)
1049
-
@_ods_extend_opview_class(_ods_ext_module)
1050
-
classFuncOp(_ods_ir.OpView):
1063
+
from mlir.ir import F32Type
1064
+
1065
+
a = ConstantOpExt(F32Type.get(), 42.42)
1066
+
b = ConstantOpExt(IntegerType.get_signless(32), 42)
1051
1067
```
1052
1068
1053
-
See the `_ods_common.py``extend_opview_class` function for details of the
1054
-
mechanism. At a high level:
1055
-
1056
-
* If the extension module exists, locate an extension class for the op (in
1057
-
this example, `FuncOp`):
1058
-
* First by looking for an attribute with the exact name in the extension
1059
-
module.
1060
-
* Falling back to calling a `select_opview_mixin(parent_opview_cls)`
1061
-
function defined in the extension module.
1062
-
* If a mixin class is found, a new subclass is dynamically created that
1063
-
multiply inherits from `({_builtin_ops_ext.FuncOp},
1064
-
_builtin_ops_gen.FuncOp)`.
1065
-
1066
-
The mixin class should not inherit from anything (i.e. directly extends `object`
1067
-
only). The facility is typically used to define custom `__init__` methods,
1068
-
properties, instance methods and static methods. Due to the inheritance
1069
-
ordering, the mixin class can act as though it extends the generated `OpView`
1070
-
subclass in most contexts (i.e. `issubclass(_builtin_ops_ext.FuncOp, OpView)`
1071
-
will return `False` but usage generally allows you treat it as duck typed as an
1072
-
`OpView`).
1073
-
1074
-
There are a couple of recommendations, given how the class hierarchy is defined:
1075
-
1076
-
* For static methods that need to instantiate the actual "leaf" op (which is
1077
-
dynamically generated and would result in circular dependencies to try to
1078
-
reference by name), prefer to use `@classmethod` and the concrete subclass
1079
-
will be provided as your first `cls` argument. See
1080
-
`_builtin_ops_ext.FuncOp.from_py_func` as an example.
1081
-
* If seeking to replace the generated `__init__` method entirely, you may
1082
-
actually want to invoke the super-super-class `mlir.ir.OpView` constructor
1083
-
directly, as it takes an `mlir.ir.Operation`, which is likely what you are
1084
-
constructing (i.e. the generated `__init__` method likely adds more API
1085
-
constraints than you want to expose in a custom builder).
1086
-
1087
-
A pattern that comes up frequently is wanting to provide a sugared `__init__`
1088
-
method which has optional or type-polymorphism/implicit conversions but to
1089
-
otherwise want to invoke the default op building logic. For such cases, it is
1090
-
recommended to use an idiom such as:
1069
+
Note, three key aspects of the extension mechanism in this example:
1070
+
1071
+
1.`ConstantOpExt` directly inherits from the generated `ConstantOp`;
1072
+
2. in this, simplest, case all that's required is a call to the super class' initializer, i.e., `super().__init__(...)`;
1073
+
3. in order to register `ConstantOpExt` as the preferred `OpView` that is returned by `mlir.ir.Operation.opview` (see [Operations, Regions and Blocks](#operations-regions-and-blocks))
1074
+
we decorate the class with `@_cext.register_operation(_Dialect, replace=True)`, **where the `replace=True` must be used**.
1075
+
1076
+
In some more complex cases it might be necessary to explicitly build the `OpView` through `OpView.build_generic` (see [Default Builder](#default-builder)), just as is performed by the generated builders.
1077
+
I.e., we must call `OpView.build_generic`**and pass the result to `OpView.__init__`**, where the small issue becomes that the latter is already overridden by the generated builder.
1078
+
Thus, we must call a method of a super class' super class (the "grandparent"); for example:
0 commit comments