- Fixes a rather egregious bug with respect to the inability to return arbitrary objects from py::init (was causing aliasing of multiple py::object -> native instance).
- Makes Modules and Operations referencable types so that they can be reliably depended on.
- Uniques python operation instances within a context. Opens the door for further accounting.
- Next I will retrofit region and block to be dependent on the operation, and I will attempt to model the API to avoid detached regions/blocks, which will simplify things a lot (in that world, only operations can be detached).
- Added quite a bit of test coverage to check for leaks and reference issues.
- Supercedes: https://reviews.llvm.org/D87213
Details
Diff Detail
- Repository
- rG LLVM Github Monorepo
Event Timeline
mlir/lib/Bindings/Python/IRModules.h | ||
---|---|---|
132 | Why only living operations is recorded here, not others (like modules, attributes, types, etc.)? Should this way be used to record living objects in blocks and regions? |
mlir/lib/Bindings/Python/IRModules.h | ||
---|---|---|
132 | I'm being pretty explicit to only add accounting where there is an ownership complexity that requires it. The question comes down to which entities in the hierarchy must not alias to (potentially) multiple objects on the python side. For those where such aliasing is benign, I opted to not maintain a strict correlation, instead implementing __eq__ so that == reports them as equal, which I believe, is good enough for most. Examples: IntegerType.get(ctx, 32) == IntegerType.get(ctx, 32) This does mean that strict reference equality like IntegerType(ctx, 32) is IntegerType(ctx, 32) will return False. This is likely ok. We may also want to implement __hash__ on them all so that they work correctly in dicts, etc. See D87982 and D87982 for where I took the Operation implementation. In short, PyOperation is the owner for regions and blocks and needs to be a top-level "ref" type that we can count on not aliasing. This will let us do things like selectively invalidate instances when mutations occur without worrying that there is some alias to the same operation in the hierarchy. Operations are also the only entity that I allow to be in a detached state. The C/C++ API allows this for Region/Block, but it simplified the ownership model a lot to eliminate that possibility in this API, and then it allows us to make the Region/Block completely dependent on its owning operation for accounting. Again, the aliasing is benign so I didn't worry about it. If we ever want to re-introduce detached regions/blocks, we could do so with new "DetachedRegion" class or similar and also avoid the complexity of accounting. With the way I have it now, I think we can avoid having a global live list for regions and blocks. We may end up needing an op-local one at some point TBD, depending on how hard it is to guarantee how mutations interact with their Python peer objects. We can cross that bridge easily when we get there, imo. Module, when used purely from the Python API, can't alias anyway, so we can use it as a unique ref type without worry. If the API ever changes such that this cannot be guaranteed (i.e. by letting you marshal a native-defined Module in), then there would need to be a live table for it too. |
mlir/lib/Bindings/Python/IRModules.h | ||
---|---|---|
29–48 | Is this default constructor necessary? | |
56 | return std::move makes me tick, and clang-tidy will complain about it preventing copy elision. I understand it is intentional here so that we always steal the underlying PyObject without changing its reference count, but it is likely worth a comment. | |
132 |
It should be possible to just forward this to LLVM's hashing for all context-owned types.
Starting from this line, it's a great rationale. Could you move it to the documentation? | |
200 | I'm confused here about what is a parent to what else. |
mlir/lib/Bindings/Python/IRModules.h | ||
---|---|---|
29–48 | Good catch - not anymore. Leftover from some dinking with it. | |
56 | Changed to: auto stolen = std::move(object); return stolen; I think clang-tidy doesn't complain about moving an instance variable, but better to be explicit since this is so often flagged as an anti-pattern. | |
132 | Added a section to the bindings doc and expanded on this explanation regarding ownership and the operation hierarchy. Feel free to comment and I'll update post-submit. | |
200 | I've chopped this down and also added a todo on the parentKeepAlive member. I need to address this part in a followup to fully generalize the detached->attached transition. The docs I added do outline the intended design - this implementation just isn't there yet. |
Is this default constructor necessary?