This is an archive of the discontinued LLVM Phabricator instance.

[MLIR][python bindings] invalidate ops after PassManager run
AcceptedPublic

Authored by makslevental on Jul 17 2023, 10:03 PM.

Details

Summary

Apropos of this discussion and this subsequently filed bug, this patch invalidates operations below the root after PassManager run on a root operation.

Suggestions on a better factorization are appreciated.

Diff Detail

Event Timeline

makslevental created this revision.Jul 17 2023, 10:03 PM
Herald added a project: Restricted Project. · View Herald Transcript

cleanup/refactor/add comments

more comments

makslevental edited the summary of this revision. (Show Details)Jul 19 2023, 12:53 PM
makslevental published this revision for review.Jul 19 2023, 12:54 PM
makslevental edited the summary of this revision. (Show Details)
rkayaith added inline comments.Jul 19 2023, 1:02 PM
mlir/lib/Bindings/Python/Pass.cpp
136–137

the while loops look like they could be for loops

mlir/lib/CAPI/IR/IR.cpp
563

I would use a switch to translate the enum, so we don't rely on the encoding being the same

mlir/test/python/pass_manager.py
181–183

could you add some more nesting here to show the traversal works, and also print module to show it's still valid

makslevental marked 2 inline comments as done.

incorporate comments

makslevental marked an inline comment as done.Jul 19 2023, 3:46 PM
rkayaith added inline comments.Jul 20 2023, 6:36 PM
mlir/include/mlir-c/IR.h
606–611

Thinking about this some more, this is missing the options to stop/skip through WalkResult, and the reverse iteration order option. We could maybe add extra functions for some of those later, but we also don't want to end up with too many variants...

Maybe open a separate revision for just the new C api? I think it probably deserves its own dedicated tests anyways.

mlir/lib/Bindings/Python/IRModule.h
273

does this need to be a class? it doesn't seem like it's doing anything

mlir/test/python/pass_manager.py
191–196

these logs aren't actually CHECKd

194

should try printing the nested ones as well

mlir/lib/Bindings/Python/Pass.cpp
119–138

When Mehdi and I were discussing this (years ago), I believe we had decided that it should be possible to disable such safety features for situations where you take your own responsibility to do the right thing. Can we add an optional argument like invalidate_ops = True? This is a rather heavyweight thing being done, and for performance-sensitive code, I would always opt to just do it the right way vs commingling uses of the API.

123

I thought I had devised a clever way to do this without a walk, but I can't remember now. I think it had something to do with maintaining a global table of visible Py ops by parent. Then you could just zip through that instead of having all of these API calls for what (I would think) is most likely a very sparse set of actually visible Python instances.

But I think there are some corners that make that hard.

makslevental added inline comments.Jul 20 2023, 9:50 PM
mlir/lib/Bindings/Python/Pass.cpp
119–138

i definitely support "take your own responsibility" in all possible cases so i'll add that.

123

correct me if i'm wrong but don't you need union-find to make this faster if you don't want to miss any transitive children? but then that union-find is only amortized faster and since this is the first and only place the walk is done, there's no actual amortization.

but maybe i'm misunderstanding what you're describing.

makslevental marked 3 inline comments as done.

quick comment incorporation

mlir/lib/Bindings/Python/Pass.cpp
119–138

added but not sure how to test since if you do indeed pass invalidate_ops=False and then try to print you'll get a segfault...

stellaraccident accepted this revision.Jul 20 2023, 10:33 PM
stellaraccident added inline comments.
mlir/lib/Bindings/Python/Pass.cpp
119–138

That's fine. There are a couple other untestable cases.

123

I'm not sure - I mainly remember thinking about it and believing there was another way. I think the observation was that the python wrappers do not come into existence randomly but as a result of descending down a path of python reachable wrappers. And then you could just keep a flat lookup table of live parents.

Mainly thinking that the common case is to not have dense, live references and wishing we weren't paying an extra traversal.

But probably not worth optimizing now.

This revision is now accepted and ready to land.Jul 20 2023, 10:33 PM

fix overcomplicated impl

Looks good to me modulo comments about the c API.

makslevental added inline comments.Jul 20 2023, 10:50 PM
mlir/lib/Bindings/Python/Pass.cpp
123

i changed the walk/callback code since @rkayaith comment got me thinking i overcomplicated things. it reduces the number api calls but it's still a traversal. let me know if i've made some grievous error/assumption in writing it this way.

Looks good to me modulo comments about the c API.

yea i'm gonna add that before landing.

The other thing we could do: define the invalidation to be very course, invalidating every op except for the op the pass is run on (and possibly its parents). This would only require iterating over the live set we already have and flipping the invalid but on everything, then un-invalidating the target op. That would scale with the number of live references vs the size of the IR.

rkayaith added inline comments.Jul 21 2023, 7:24 AM
mlir/lib/Bindings/Python/Pass.cpp
119–138

in the test you have, printing the func should work with invalidate_ops=False but fail with True