This is an archive of the discontinued LLVM Phabricator instance.

[MC] Do not copy MCInstrDescs. NFC.
ClosedPublic

Authored by foad on Jan 20 2023, 7:15 AM.

Details

Summary

Avoid copying MCInstrDesc instances because a future patch will change
them to find their implicit operands and operand info array based on
their own "this" pointer, so it will only work for MCInstrDescs in the
TargetInsts table, not for a copy of an MCInstrDesc at a different
address.

Diff Detail

Event Timeline

foad created this revision.Jan 20 2023, 7:15 AM
Herald added a project: Restricted Project. · View Herald TranscriptJan 20 2023, 7:15 AM
foad requested review of this revision.Jan 20 2023, 7:15 AM
Herald added a project: Restricted Project. · View Herald Transcript
arsenm accepted this revision.Jan 20 2023, 7:31 AM
This revision is now accepted and ready to land.Jan 20 2023, 7:31 AM
foad added a subscriber: dblaikie.Jan 20 2023, 7:32 AM
foad added inline comments.
llvm/include/llvm/MC/MCInstrDesc.h
200

@dblaikie or any other C++ experts - does this seem reasonable? I was not sure if I should be following some kind of rule-of-3/5/7/11/13/... here. All I want to do is prevent users from accidentally writing MCInstrDesc MCID = ... instead of const MCInstrDesc &MCID = ....

barannikov88 added inline comments.
llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVMCCodeEmitter.cpp
64

Doesn't this trigger compilation error (missing const)?

foad added inline comments.Jan 20 2023, 8:16 AM
llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVMCCodeEmitter.cpp
64

Yes! Apparently you have to enable this target via LLVM_EXPERIMENTAL_TARGETS_TO_BUILD and I did not test that. Thanks for spotting it.

foad updated this revision to Diff 490870.Jan 20 2023, 8:19 AM

Fix SPIRV target.

dblaikie added inline comments.Jan 20 2023, 10:21 AM
llvm/include/llvm/MC/MCInstrDesc.h
200

Generally I'd suggest that APIs shouldn't make awkward ergonomic choices to reduce the chance of accidents - there's lots of things that can be expensive to copy in C++ that still have the usual copy ctor/assignment operator.

So if the thing is copyable - forcing users to do that copy in some uncommon way because the usual way is too error prone is, imho, not the right way to go - though I realize trying to teach people that this thing is expensive to copy isn't easy either. Hopefully updating a bunch of places that were copying will help set the right expectations.

If it's really non-copyable in any way, then, yes, the rule of 5 would apply. Implement the copy ctor, copy assignment, move ctor, move assignment, and destructor.

Sounds like deleting all the copy/move operations is what you want? Leave the dtor defaulted, maybe default the default (no-args) ctor? since that seems to be used in a test? (honestly, how are these objects constructed otherwise? I guess with aggregate init?)

foad added inline comments.Jan 20 2023, 11:00 AM
llvm/include/llvm/MC/MCInstrDesc.h
200

Yes they are created with aggregate init, in a big static table generated by tablegen.

They are not expensive to copy. They "can't" be copied in that some methods would no longer work correctly, because they rely on being able to access other data in the same table at a known offset from "this".

dblaikie added inline comments.Jan 22 2023, 9:21 PM
llvm/include/llvm/MC/MCInstrDesc.h
200

Ah, OK - yeah, totally disabling copy/move ops seems fine, then. So probably deleting all copy/move ctors, defaulting the dtor (I think that might still be implicit, despite the deleted copy/move ops, so you can leave that if it works without explicitly defaulting it).
I'm surprised the aggregate init works given the deleted copy ctor here, but if it works, it works I guess *shrug*

foad updated this revision to Diff 491292.Jan 23 2023, 3:53 AM

"Rule of 4" (rule of 5 without the desctructor).

foad marked 2 inline comments as done.Jan 23 2023, 3:54 AM
foad added inline comments.
llvm/include/llvm/MC/MCInstrDesc.h
200

Done, thanks!

This revision was landed with ongoing or failed builds.Jan 23 2023, 3:56 AM
This revision was automatically updated to reflect the committed changes.
foad marked an inline comment as done.

TL;DR: this doesn't work in C++20, maybe revert?

I'm surprised the aggregate init works given the deleted copy ctor here, but if it works, it works I guess *shrug*

The aggregate init works up to C++17, but not in C++20.
(The rules change from "no user-provided, inherited, or explicit constructors" to "no user-declared or inherited constructors").

I think the current status of building LLVM in C++20 mode is "theoretically supported, but no stable buildbot yet". It's being worked on.

Since C++20 compatibility is something we'll need to do in the medium-term, and incompatible constructs make building a good bot harder in the short-term, I'd reluctantly suggest reverting this.

(Eliminating the aggregate init or finding an alternative technique to break copy/move also seem fine, starting with a revert would still be nice if it's not trivial)

foad added a comment.Jan 24 2023, 1:49 AM

TL;DR: this doesn't work in C++20, maybe revert?

I'm surprised the aggregate init works given the deleted copy ctor here, but if it works, it works I guess *shrug*

The aggregate init works up to C++17, but not in C++20.
(The rules change from "no user-provided, inherited, or explicit constructors" to "no user-declared or inherited constructors").

I think the current status of building LLVM in C++20 mode is "theoretically supported, but no stable buildbot yet". It's being worked on.

Since C++20 compatibility is something we'll need to do in the medium-term, and incompatible constructs make building a good bot harder in the short-term, I'd reluctantly suggest reverting this.

(Eliminating the aggregate init or finding an alternative technique to break copy/move also seem fine, starting with a revert would still be nice if it's not trivial)

Interesting. Is there any easy way I can test building in C++20 mode on my own machine?

TL;DR: this doesn't work in C++20, maybe revert?

I'm surprised the aggregate init works given the deleted copy ctor here, but if it works, it works I guess *shrug*

The aggregate init works up to C++17, but not in C++20.
(The rules change from "no user-provided, inherited, or explicit constructors" to "no user-declared or inherited constructors").

I think the current status of building LLVM in C++20 mode is "theoretically supported, but no stable buildbot yet". It's being worked on.

Since C++20 compatibility is something we'll need to do in the medium-term, and incompatible constructs make building a good bot harder in the short-term, I'd reluctantly suggest reverting this.

(Eliminating the aggregate init or finding an alternative technique to break copy/move also seem fine, starting with a revert would still be nice if it's not trivial)

Interesting. Is there any easy way I can test building in C++20 mode on my own machine?

Looks like you should set -DCMAKE_CXX_STANDARD=20 (this lets me reproduce this class of error locally, with clang++-12 as host compiler).

Interesting. Is there any easy way I can test building in C++20 mode on my own machine?

cmake -DCMAKE_CXX_STANDARD=20 should give the C++20 build. However, make sure to have a ~new compiler. Clang 15 or GCC 12 should do the trick.

I have sent https://github.com/llvm/llvm-project/commit/f3d64644d8d75f9317421fe5b9b0f2d030a1a63f to unbreak our integrate, but actually want to help re-enable the enforcement that the type is not copied today.

To describe the problem, the following code breaks in C++20:

struct WantAggregate {
  WantAggregate(const WantAggregate&) = delete;
  int a;
  int b;
};

WantAggregate a = {1,2}; // does not compile in C++20.

One way to workaround this would be to force an implicitly-deleted constructor:

struct NoCopy {
  NoCopy() = default;
  NoCopy(const &) = delete;
};
struct WantAggregate {
  int a;
  int b;
  NoCopy delete_copy = {};
};

WantAggregate a = {1,2}; // works!

However, this approach increases the size of MCInstrDesc (the extra member needs to have an address). One way to workaround it is

[[no_unique_address]] NoCopy delete_copy = {}; // no increase in size, implicitly deleted copy!

Unfortunately, this attribute was added only in C++20. I am trying to figure out a way to make this work in both C++17 and C++20 without the size increase, but still struggling to find a way without a preprocessor.

(I'm assuming the size increase is undesirable, lmk if it's actually not a big deal. Using a member is probably the easiest workaround.)

For the record, the corresponding standard change is P1008R1.

foad added a comment.Jan 24 2023, 2:59 AM

C++20 build should be fixed properly by D142446

Nevermind, this is already fixed. Thanks.