Skip to content

Conversation

makslevental
Copy link
Contributor

based heavily on #156000

@makslevental makslevental force-pushed the users/makslevental/python-passes branch from 5289c12 to 4b1213f Compare September 7, 2025 23:28
@makslevental makslevental force-pushed the users/makslevental/python-passes branch 3 times, most recently from 786c2ee to 1e0d2d2 Compare September 7, 2025 23:37
Comment on lines 178 to 181
auto src = nb::handle(static_cast<PyObject *>(obj));
nb::callable dst;
nb::inst_copy(dst, src);
return static_cast<void *>(dst.ptr());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the only part i'm not 100% on: i'm not sure if inst_copy copies the ref count or what (and whether construct is called before or after). need to find a way to test this path.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah clone is quite tricky since in the python side I'm not sure how to call it : )

From the code, it seems that we first call callback.clone, and then call callback.construct in constructing an ExternalPass, so maybe here the pointer dst.ptr() will be dangling since the nb::callable is destructed (callable::~callable is called) and the ref_count become 0?

I think it is related to whether we inc_ref in the callback.construct. Maybe we can have such a design:

  • in callback.construct we do not call inc_ref, it leaves to who pass the object into the ExternalPass;
  • in callback.clone, we first call inc_ref and then pass the object (call .release() to prevent the nb::object::~object from decreasing the ref count), so that the ref count of this object can be 1, and in callback.destruct it is decreased to 0 and destructed;
  • before the call to mlirCreateExternalPass, we first call inc_ref for the object to increase the ref count so that it should be valid at least before callback.destruct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so maybe here the pointer dst.ptr() will be dangling since the nb::callable is destructed

Seems not possible since the tests pass? Are you sure that clone is called for our use?

Copy link
Member

@PragmaTwice PragmaTwice left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The patch sounds good to me and definitely much cleaner and simpler than mine! I'll try to merge it into my PR. Just left a few minor comments to discuss some details.

Comment on lines 164 to 166
[](PyPassManager &passManager, const std::string &name,
const std::string &argument, const std::string &description,
const std::string &opName, const nb::callable &run) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible maybe we can make argument, description and opName keyword arguments? So that we can use it like pm.add_python_pass(name, run).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya 100% that's a good idea (like you have in your PR). But I don't think you actually need to make them kw - you just have to make them have default args. Anyway either form is good with me.

Comment on lines 178 to 181
auto src = nb::handle(static_cast<PyObject *>(obj));
nb::callable dst;
nb::inst_copy(dst, src);
return static_cast<void *>(dst.ptr());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah clone is quite tricky since in the python side I'm not sure how to call it : )

From the code, it seems that we first call callback.clone, and then call callback.construct in constructing an ExternalPass, so maybe here the pointer dst.ptr() will be dangling since the nb::callable is destructed (callable::~callable is called) and the ref_count become 0?

I think it is related to whether we inc_ref in the callback.construct. Maybe we can have such a design:

  • in callback.construct we do not call inc_ref, it leaves to who pass the object into the ExternalPass;
  • in callback.clone, we first call inc_ref and then pass the object (call .release() to prevent the nb::object::~object from decreasing the ref count), so that the ref count of this object can be 1, and in callback.destruct it is decreased to 0 and destructed;
  • before the call to mlirCreateExternalPass, we first call inc_ref for the object to increase the ref count so that it should be valid at least before callback.destruct.

@makslevental makslevental force-pushed the users/makslevental/python-passes branch 6 times, most recently from e7034be to 982a52f Compare September 8, 2025 04:00
@makslevental makslevental force-pushed the users/makslevental/python-passes branch from 982a52f to 968b50b Compare September 8, 2025 04:08
PragmaTwice added a commit to PragmaTwice/llvm-project that referenced this pull request Sep 8, 2025
Co-authored-by: Maksim Levental <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants