This is an archive of the discontinued LLVM Phabricator instance.

[IntervalMap] Add move and copy ctors and assignment operators
ClosedPublic

Authored by Orlando on Oct 19 2022, 3:42 AM.

Details

Summary

And update the unittest.


These changes will be used by an upcoming patch.

Diff Detail

Event Timeline

Orlando created this revision.Oct 19 2022, 3:42 AM
Herald added a project: Restricted Project. · View Herald TranscriptOct 19 2022, 3:42 AM
Orlando requested review of this revision.Oct 19 2022, 3:42 AM
dblaikie accepted this revision.Oct 19 2022, 10:29 AM

Sounds good

llvm/include/llvm/ADT/IntervalMap.h
1040–1041

could drop the height and rootSize initializations now that they're initialized at the member declaration

This revision is now accepted and ready to land.Oct 19 2022, 10:29 AM

Thanks @dblaikie. I was about to land this and noticed some potential UB - I have put a question inline.

llvm/include/llvm/ADT/IntervalMap.h
1040–1041

Good point, will do.

1084

Hmmm. Thinking about it, should I construct data in the move constructor like in the explicit constructor ? Since IntervalMap's dtor explicitly calls data's dtor (rootLeaf().~RootLeaf()). Otherwise the RHS map containing the swapped data will try to call the RootLeaf dtor explicitly on an object that hasn't been constructed, which... feels like UB territory to me?

dblaikie added inline comments.Oct 20 2022, 8:08 AM
llvm/include/llvm/ADT/IntervalMap.h
1084

Yep, sounds problematic (any chance you can test under sanitizers and see if they detect the problem? Otherwise I guess manually stepping through with a debugger might verify the behavior). Could use a forwarding ctor, perhaps?

Same would be true of the copy constructor too? Since that calls the assignment operator which calls clear, which presumably tries to destroy some things. Hmm, maybe it skates through OK - because it 's not-branched and just sets the size to zero?

(I suspect there's some other "technical" UB around here - like calling rootLeaf to /then/ construct it in the Allocator ctor is probably UB because we've bound a reference to an object before we ran the ctor on it - but that's pre-existing. Probably the right thing to do is to get a void pointer to the storage, call the ctor on that)

Orlando updated this revision to Diff 470178.Oct 24 2022, 9:15 AM

It turns out my base commit was old and was missing some changes. Notably, the AlignedCharArrayUnion<RootLeaf, RootBranchData> data member has since been replaced with a union. Apologies for the noise and additional review required as a result.

Due to the use of an anonymous union and the already tangled object lifetime situation I've changed from using the std::swap idiom, opting for instead explicitly cleaning up the moved-into object before the move (see destroyTree and createTree).

No issues are reported when running the unittest built with -fsanitize=address,undefined (are there any other sanitizers I should be using?).

dblaikie added inline comments.Oct 24 2022, 1:39 PM
llvm/include/llvm/ADT/IntervalMap.h
1081–1084

This leaves the leaves the rootBranch/rootLeaf in a moved-from state, what does that mean for the state of the object?

What's the state of a moved-from IntervalMap? Is there some test coverage for that? I'd expect to need to change the height/rootSize for the moved-from object in some way?

Orlando updated this revision to Diff 470446.Oct 25 2022, 5:07 AM

This is somewhat fiddly - I think I've got it this time though.

I have used forwarding constructors this time. There's a gottcha - the move/copy constructors set the allocator field before calling the assignment operator, which calls clear. If the IntervalMap constructor was ever changed to allocate nodes then that clear call would deallocate using the wrong allocator. I've added an assertion to the move/copy constructors to help catch this.

I've slightly updated the test (see the inline comment).

I've added a few comments this time round, including a method group docu-comment explaining a requirement of the lifetime of the allocator of RHS.

Orlando marked 2 inline comments as done.Oct 25 2022, 5:08 AM
Orlando added inline comments.
llvm/include/llvm/ADT/IntervalMap.h
1081–1084

This leaves the leaves the rootBranch/rootLeaf in a moved-from state, what does that mean for the state of the object?

Yep you're right, my mistake. Setting RHS.height = 0 stops the allocated data now owned by moved-into being deallocated by moved-from upon destruction, but that it doesn't stop the leaf destructor explicitly being called in the IntervalMap destructor. In the case where moved-from (RHS) was using branchData instead of leaf the destructor of the wrong type is being called.

I think I've fixed this now!

What's the state of a moved-from IntervalMap? Is there some test coverage for that?

I had intended for the test to cover this but it turns out that not enough intervals were added to trigger a change from rootLeaf to rootBranch (see MoveAssignmentDst in the unittests). This meant that no nodes were heap allocated, so asan didn't catch this mistake. Adding more intervals causes asan to find this issue.

dblaikie accepted this revision.Oct 26 2022, 2:54 PM

That all looks about right to me.

llvm/include/llvm/ADT/IntervalMap.h
1064–1066

This /probably/ isn't needed - seems pretty unlikely that an allocator-constructer IntervalMap would be anything other than empty, but no worries if you feel strongly about keeping it.

This revision was automatically updated to reflect the committed changes.
Orlando marked an inline comment as done.

Thanks for the review