This is an archive of the discontinued LLVM Phabricator instance.

Clarify comment of erase() and remove()
AcceptedPublic

Authored by matthiaskramm on Feb 18 2022, 10:14 AM.

Details

Summary

Clarify comment of erase().

Diff Detail

Event Timeline

matthiaskramm created this revision.Feb 18 2022, 10:14 AM
matthiaskramm requested review of this revision.Feb 18 2022, 10:14 AM

The name of these APIs has always been confusing to me. I would have called remove detach instead I think.

Anyway, thanks: LG with a nit.

mlir/include/mlir/IR/Operation.h
70

I wouldn't call this an "unsafe operation" more than any other: there is no "safer alternative" and it does not break invariant of the system.

mehdi_amini accepted this revision.Feb 18 2022, 4:18 PM
This revision is now accepted and ready to land.Feb 18 2022, 4:18 PM
rriddle added inline comments.Feb 18 2022, 4:20 PM
mlir/include/mlir/IR/Operation.h
70

I would not advocate the use of dropAllReferences. I would just call out the operation is expected to have no uses.

bondhugula requested changes to this revision.Feb 18 2022, 5:15 PM
bondhugula added a subscriber: bondhugula.
bondhugula added inline comments.
mlir/include/mlir/IR/Operation.h
69–70

it's -> its

70–72

+1 I don't think this change is a net improvement. I see "deallocate its memory" as being obvious and low level. The original comment with the addition that "The operation is expectation to have no uses." makes it clearer.

This revision now requires changes to proceed.Feb 18 2022, 5:15 PM
bondhugula added inline comments.Feb 18 2022, 5:20 PM
mlir/include/mlir/IR/Operation.h
73

-1 on this change here - "delete it" appears better and consistent. It's understood that references to the result values (which are going to be freed) will dangle.

mehdi_amini requested changes to this revision.Feb 18 2022, 5:29 PM

I tend to agree with Uday and River FWIW :)

Updating phrasing according to Uday's suggestion.

For reference, the reason I don't like "delete" in this context is that Operations are stored in linked lists. But linked list nomenclature uses "delete" to mean something that's different from deallocation.

But that's not the original point of this change, and a discussion for another day. :)

mehdi_amini accepted this revision.Feb 19 2022, 3:43 PM

But linked list nomenclature uses "delete" to mean something that's different from deallocation.

I think the "delete" is really the C++ nomemclature, but even in the abstract: isn't "delete" including the deallocation in the context of linked list in general? It removes the node from the linked list and destroy it as far as I can tell?

We can use "destroy" instead of "delete" here as well maybe...

Anyway LGTM again

It removes the node from the linked list and destroy it as far as I can tell?

Right. But my point was that here, the Operation isn't deleted from all the linked lists. In particular, it isn't deleted from the use lists of its OpOperands.

Right. But my point was that here, the Operation isn't deleted from all the linked lists. In particular, it isn't deleted from the use lists of its OpOperands.

Ah I misunderstood what you were referring to, because the linked-list that is the "owner" of an Operation is the Block.
Here the linked-list of def-use chain isn't touched in anyway: it isn't detached nor destroy. The doc is really about the Block list point of view only.

mehdi_amini added inline comments.Feb 19 2022, 4:33 PM
mlir/include/mlir/IR/Operation.h
70

Maybe make this even more explicit like this

matthiaskramm added inline comments.Feb 19 2022, 5:16 PM
mlir/include/mlir/IR/Operation.h
70

Yes, but what I really wanted to warn about is that even if use_empty() returns true, the OpOperand use chain can point to this Operation (through "owner"), and calling erase() will leave a dangling reference.

mehdi_amini added inline comments.Feb 19 2022, 5:38 PM
mlir/include/mlir/IR/Operation.h
70

I don't understand the dangling reference, can you elaborate?

rriddle added inline comments.Feb 19 2022, 5:41 PM
mlir/include/mlir/IR/Operation.h
70

OpOperands are owned by the Operation, so any use of an OpOperand (same with OpResult, etc.) after an operation is erased is a dangling reference.

mehdi_amini added inline comments.Feb 19 2022, 5:58 PM
mlir/include/mlir/IR/Operation.h
70

Sure: but when destroying the Operation, and so the OpOperand, what kind of dangling reference can exist?

I mean sure, someone can have a pointer to the OpOperand or the Operation itself in their own data structure, but surely we won't warn about this right?

Use Mehdi's phrasing.

matthiaskramm added inline comments.Feb 19 2022, 6:22 PM
mlir/include/mlir/IR/Operation.h
70

Oh, you're right! The OpOperand is destroyed and unlinked from the use chain. I missed the explicit call to ~OpOperand.

Changed this to use your phrasing.

bondhugula requested changes to this revision.Feb 20 2022, 7:08 PM
bondhugula added inline comments.
mlir/include/mlir/IR/Operation.h
69–71

Sorry for being nitpicky here but there is a really core operation and it would be good to ensure that the additional clarification doesn't create new ambiguity. "under the assumption ..." here is vague. Methods either have pre-conditions or they don't; pre-conditions are typically asserted on -- however, erase() doesn't assert on use_empty(). With this change, it'd be unclear as to what happens when there are still uses. There could be valid use cases where somehow erase() was called but the user knew what to do to keep the IR valid and not have undefined behavior -- i.e., the users are somehow taken care of later.

In fact, if we want to improve this comment on another aspect, it's the nested/recursive aspect. For eg. the doc comment on the implementation has diverged and it says this:

/// Remove this operation (and its descendants) from its Block and delete
/// all of them.

There isn't a reason to use two different versions. We should unify the two.

70

Sure: but when destroying the Operation, and so the OpOperand, what kind of dangling
reference can exist?

Destroying an Operation does not destroy the OpOperands of operations that use the results of the former operation. Such OpOperands will have a dangling reference.

This revision now requires changes to proceed.Feb 20 2022, 7:08 PM

With this change, it'd be unclear as to what happens when there are still uses.

Sorry, it does actually assert on !use_empty() under NDEBUG. We could just say: "The operation should have no uses." -- it's a pre-condition.

Try to adjust wording to incorporate both Uday's and Medhi's suggestions.

Also, adjust the comment in the implementation to be the same as in the
header.

The operation should have no uses.

I think "should" is ambiguous here, since it can mean both "you should make sure that the operation has no uses" as well as "after calling erase(), the operation should have no uses anymore". Yes, the latter is silly, but that only becomes clear from context, not from the wording. So using "must" now.

bondhugula accepted this revision.Feb 25 2022, 8:12 AM
This revision is now accepted and ready to land.Feb 25 2022, 8:12 AM