This is an archive of the discontinued LLVM Phabricator instance.

[MI] Change the array of `MachineMemOperand` pointers to be a generically extensible collection of extra info attached to a `MachineInstr`.
ClosedPublic

Authored by chandlerc on Aug 14 2018, 4:53 AM.

Details

Summary

The primary change here is cleaning up the APIs used for setting and
manipulating the MachineMemOperand pointer arrays so chat we can
change how they are allocated.

Then we introduce an extra info object that using the trailing object
pattern to attach some number of MMOs but also other extra info. The
design of this is specifically so that this extra info has a fixed
necessary cost (the header tracking what extra info is included) and
everything else can be tail allocated. This pattern works especially
well with a BumpPtrAllocator which we use here.

I've also added the basic scaffolding for putting interesting pointers
into this, namely pre- and post-instruction symbols. These aren't used
anywhere yet, they're just there to ensure I've actually gotten the data
structure types correct. I'll flesh out support for these in
a subsequent patch (MIR dumping, parsing, the works).

Finally, I've included an optimization where we store any single pointer
inline in the MachineInstr to avoid the allocation overhead. This is
expected to be the overwhelmingly most common case and so should avoid
any memory usage growth due to slightly less clever / dense allocation
when dealing with >1 MMO. This did require several ergonomic
improvements to the PointerSumType to reasonably support the various
usage models.

This also has a side effect of freeing up 8 bits within the
MachineInstr which could be repurposed for something else.

The suggested direction here came largely from Hal Finkel. I hope it was
worth it. ;] It does hopefully clear a path for subsequent extensions
w/o nearly as much leg work.

Depends on D50680.

Diff Detail

Event Timeline

chandlerc created this revision.Aug 14 2018, 4:53 AM
rnk added inline comments.Aug 14 2018, 4:11 PM
llvm/include/llvm/ADT/PointerSumType.h
76–77

This is... "clever". =P Isn't this going to upset ubsan's active union member sanitizer? Do we need to worry about that?

llvm/include/llvm/CodeGen/MachineInstr.h
25

Might not work, but can we forward declare this?

31

We should be able to forward declare MCSymbol.

1499–1500

Perhaps the API should be something like, getOrCreatePreInstrLabel(const Twine &LabelName = "tmp")

It's not like we need to insert two labels in front of the same instruction. In fact, it's typically better to avoid that.

1499–1501

ditto

llvm/lib/Target/X86/X86InstrInfo.cpp
5448–5449

And, I see you did the thing I commented on in the previous patch. I guess do it in two stages.

chandlerc updated this revision to Diff 160739.Aug 14 2018, 6:06 PM

Update to remove UB from PointerSumType after consulting with Richard Smith.
This should also make the patterns used more likely to work with our supported
compilers.

chandlerc marked 2 inline comments as done.Aug 14 2018, 7:45 PM
chandlerc added inline comments.
llvm/include/llvm/ADT/PointerSumType.h
76–77

Yeah, I meant to revisit this. I've done so with Richard's help and think we have a fully correct version now.

llvm/include/llvm/CodeGen/MachineInstr.h
25

PointerSumType needs the complete type. See below for details.

31

No, and this is also why I can't move ExtraInfo's definition as I had hoped.

Fundamentally, PointerSumType wants to check that the type parameters to it can be unioned into a sum type. To check that, it needs to know whether all of the tags will fit into the empty bits of the pointers. This requires the alignment of the pointer. The alignment requires the type to be complete.

We could delay this checking with some amount of work so that it is only triggered when the PointerSumType is *used*, but after trying several times to do that, I don't think I like the result at all.

First and foremost, the type *should* tell you, as early and as reliably as it can, that the parameters are valid. IMO, this should point the programmer at the *parameters*, not at some unrelated code. The declaration of the member is the best place to do this.

Second, the obvious way to defer this checking would cause unused parameters to *never be checked*. That seems ... really risky and bug prone to me.

Third, it seems reasonable for types with similar contracts (but perhaps slightly different implementation goals) to actually arrange their *storage* based on the alignment and available bits. It seems somewhat awkward to take advantage of the fact that PointerSumType *happens* to not change its layout / storage strategy based on the alignment just to defer checking of the validity of the template parameters given to it.

I definitely don't think avoiding an include is worth the kind of usability hit this would incur.

I would be somewhat interested in avoiding this type cluttering up the public interface, but it at least needs to come before the *private* data member in which it is used. The correct way to clean this up, if folks want, is to actually the class contents holistically in a way more optimized for the reader of the API:

  1. Private constants and type aliases used to make naming public types substantially simpler. (Optional and expected to be rare.)
  2. Public constants and types, forward declared where the definition is large and unnecessary for public data members or member function declarations.
  3. Public data members.
  4. Public member functions, declaration only where the definition is large.
  5. Private constants and types, and any public constant and type definitions declared (but not defined) in #1 and necessary for private data members or member function declarations.
  6. Private data members.
  7. Private member functions, declaration only where the definition is large.
  8. (out-of-line from class body) Definitions of member functions declared above which are worth making inlinable and the definitions of constants and types only declared above necessary for those member function definitions. All of these ordered relative to each other based on the order in which they are declared within the class body.
  9. (in the .cpp file) Remaining definitions, ordered relative to each other based on the order in which they are declared within the class body.

I much prefer this fairly strict ordering with very narrow and rare exceptions. I think it would also address some of your readability concerns here w/o forcing us to make declaration and definition able to be re-ordered.

That said, this should be a follow-up change if we want to do it. I have tried to clean up the comments and code a bit to make this slightly less of a readibility hit.

1499–1500

I like this API a lot better. I'll mak ethat change and update with it shortly....

chandlerc marked an inline comment as done.

Try to shrink the code of the ExtraInfo class a bit and add some helpful comments about how all of these moving pieces interact.

chandlerc updated this revision to Diff 160752.Aug 15 2018, 1:02 AM
chandlerc marked 3 inline comments as done.

Switch to API suggested in review.

Now using API suggested by Reid.

bogner added inline comments.Aug 15 2018, 10:32 AM
llvm/lib/CodeGen/MachineInstr.cpp
410–415

This comment is overly vague. Why does this need to be treated conservatively?

IIUC from the old comments this was because we could hit the case where we ran out of space and dropped memrefs, which meant that empty memrefs may have already hit that case and merging would give a subset. However, if this is still the case I don't see where handle the case of having too many.

Is there another case that this is trying to handle?

rnk accepted this revision.Aug 15 2018, 5:06 PM

I'm happy with this, but I assume you want to respond to @bogner's comment.

llvm/include/llvm/CodeGen/MachineInstr.h
31

Fair enough. I'm probably less concerned about readability and more concerned about saving C++ template instantiation cruft in object files, but that's my twisted sense of priorities.

This revision is now accepted and ready to land.Aug 15 2018, 5:06 PM
chandlerc updated this revision to Diff 160956.Aug 15 2018, 5:35 PM

Try to improve comments.

In D50701#1201714, @rnk wrote:

I'm happy with this, but I assume you want to respond to @bogner's comment.

Thanks, and yeah. See below where I've tried to respond.

llvm/lib/CodeGen/MachineInstr.cpp
410–415

While the old code *also* had to handle running out of space for the operands, an empty list still can't be merged.

The issue is that the memoperands constrain what possible memory is accessed. An empty list is the *least* constrained, and so there is no way to merge with it. See the comments on the memoperands methods themselves. We're required to assume that in the absence of precise memoperands the instruction can access any memory it wants.

I've tried to clarify the comments here, but not sure it's really working. Feel free to suggest better comments here? Or let me know if this still isn't clear?

bogner accepted this revision.Aug 16 2018, 1:51 PM

lg

llvm/lib/CodeGen/MachineInstr.cpp
410–415

The new comments are much clearer. Thanks.

This revision was automatically updated to reflect the committed changes.