[PATCH] D104149: [MCA] Adding the CustomBehaviour class to llvm-mca

Andrea Di Biagio via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Mon Jun 14 03:27:47 PDT 2021


andreadb added a comment.

In D104149#2815836 <https://reviews.llvm.org/D104149#2815836>, @holland11 wrote:

>> Protected fields of InstrPostProcess should be private (even if at the cost of adding const& getters).
>
> Does this hold true in general for objects that get stored in base classes, but used in derived classes? I also have the `MCSubtargetInfo`, `SrcManager`, and `MCInstrInfo` objects within the base CB as protected. Should I change them to private and then use getters for the derived classes to access them?

In my code, I tend to use `private` instead of `protected` whenever possible. This is just my coding style though. I don't think that LLVM imposes a particular coding style for it. Since mca has been mainly written by me, you'll find out that `protected` is almost never used in practice.

That being said, it is fine for you to use `protected` for your new class. It is your code, and -strictly speaking - there is nothing fundamentally wrong with it.

>> Do we actually even need to add a derived class for x86? For X86 the base class seems already enough.
>
> We probably don't need it. I was originally going to make an empty derived class for every upstream target just so they'd have all their cmake stuff setup already for when/if people wanted to add to them. But then I just decided to keep two of them so that it would be easy for people to refer to how they are setup. Though, keeping the X86 one seems a bit misleading considering that CB doesn't even play a part in the out-of-order pipeline yet. Do you think I should remove it (the X86 derived class)?

Yes please.

In case, any X86 specific changes should go into a separate patch.

>> However, let's just pretend for now that this is all about adding new dependencies.
>> We can re-evaluate the importance of this particular use case in future if we find that it is required by some code.
>
> I'm really tempted to use this altered design to fix the `ds_read` issue that I described at the end of the original post. It would be a much cleaner example use case than the `s_waitcnt` 'fix' (although it wouldn't use the actual CB class at all so it wouldn't be an example of CB directly).
>
> From a high level, all I need to do is remove the `WriteState` that refers to the `M0` register. From a lower level, this `WriteState` exists in the `mca::Instruction` as part of `SmallVector<WriteState, 2> Defs;`. From looking at the way the `WriteState` objects interact with the `RegisterFile` (most notably, what happens in both pipelines when an instruction gets dispatched and needs to add its defs to the register file), I could either **(1)** remove the `WriteState` from the `SmallVector`, **(2)** set the `RegisterID` of the `WriteState` to 0, or **(3)** set the `Latency` of the `WriteState` to 0.

I don't like the idea of using your new framework to workaround problems/bugs that are not in llvm-mca.

If you think that there is a genuine bug in their instruction definitions, then please raise  a bug for it.

We should never "fix" non-mca issues within mca. This is true in general, and it doesn't only apply to mca. If there is a bug in someone else's code, we create a bug report for it, and optionally we send a patch for it. We don't pretend that the issue doesn't exist, and (worse) hack some workarounds for those bugs in our code.

So, I strongly recommend to raise bugs for all these problems you have encountered when testing the AMDGPU models.
If there is a reason why there is a write to `M0`, then we can consider implementing a solution based on your new class/post-processing step.
If instead it that was an oversight, then it will be fixed outside of mca without the need for a custom hack in mca.

> 1. As far as I can tell, there is no way to remove an element from a `SmallVector`. I could maybe build a new `SmallVector` without the specific `WriteState`, but this seems less than ideal.
>
> 2. This would cause an assertion to fail within `RegisterFile::addRegisterWrite`. Would it be alright to change this to an if statement that exits the function instead? **Edit** Actually I just tried this, and there are other assertions that would need to be changed as well (one from `RegisterFile::removeRegisterWrite` and one from `RegisterFile::onInstructionExecuted`) so maybe this isn't a great option.
>
>   void RegisterFile::addRegisterWrite(WriteRef Write,
>                                       MutableArrayRef<unsigned> UsedPhysRegs) {
>     WriteState &WS = *Write.getWriteState();
>     MCPhysReg RegID = WS.getRegisterID();
>     assert(RegID && "Adding an invalid register definition?");
>
> To
>
>   void RegisterFile::addRegisterWrite(WriteRef Write,
>                                       MutableArrayRef<unsigned> UsedPhysRegs) {
>     WriteState &WS = *Write.getWriteState();
>     MCPhysReg RegID = WS.getRegisterID();
>     if (!RegID)  return;
>
>
>
> 3. This is my least desired route since I don't fully understand the ramifications that it would have and doesn't seem as 'explicit' as far as removing a def from an instruction goes.
>
> If none of these options are desirable, another option could be to have a new method `InstrPostProcess::skipRegDef()` that gets called from `InstrBuilder::createInstruction()`:
>
>   // Initialize writes.
>   unsigned WriteIndex = 0;
>   for (const WriteDescriptor &WD : D.Writes) {
>     RegID = WD.isImplicitWrite() ? WD.RegisterID
>                                  : MCI.getOperand(WD.OpIndex).getReg();
>     // Check if this is a optional definition that references NoReg.
>     if (WD.IsOptionalDef && !RegID) {
>       ++WriteIndex;
>       continue;
>     }
>   
>     assert(RegID && "Expected a valid register ID!");
>     NewIS->getDefs().emplace_back(WD, RegID,
>                                   /* ClearsSuperRegs */ WriteMask[WriteIndex],
>                                   /* WritesZero */ IsZeroIdiom);
>     ++WriteIndex;
>   }
>
> To
>
>   // Initialize writes.
>   unsigned WriteIndex = 0;
>   for (const WriteDescriptor &WD : D.Writes) {
>     RegID = WD.isImplicitWrite() ? WD.RegisterID
>                                  : MCI.getOperand(WD.OpIndex).getReg();
>     // Check if this is a optional definition that references NoReg.
>     if ((WD.IsOptionalDef && !RegID) || IPP->skipRegDef(WD, MCI)) {
>       ++WriteIndex;
>       continue;
>     }
>   
>     assert(RegID && "Expected a valid register ID!");
>     NewIS->getDefs().emplace_back(WD, RegID,
>                                   /* ClearsSuperRegs */ WriteMask[WriteIndex],
>                                   /* WritesZero */ IsZeroIdiom);
>     ++WriteIndex;
>   }
>
> The more I think about it, the more I like the last option best. What do you think?

Assuming that it always works, Option 2. seems like the right way to go. A Write to an invalid registers should always be ignored.
That being said, let's wait to see if the issue with the write to `M0` can be fixed in a different way.
If not, then it is OK to add a post-processing step for it in mca. But that would go into a separate patch anyway.

-Andrea


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D104149/new/

https://reviews.llvm.org/D104149



More information about the llvm-commits mailing list