This is an archive of the discontinued LLVM Phabricator instance.

[TableGen] Allow identical MnemonicAliases with no predicate
ClosedPublic

Authored by foad on Jun 28 2021, 8:01 AM.

Details

Summary

My use case for this is illustrated in the test case: I want to define
the same instruction twice with different (disjoint) predicates, because
the instruction has different operands on different subtargets. It's
convenient to do this with a multiclass that also defines an alias for
the instruction.

Previously tablegen would complain if this alias was defined twice with
no predicate. One way to fix this would be to add a predicate on each
definition of the alias, matching the predicate on the instruction. But
this (a) is slightly awkward to do in the real world use case I had, and
(b) leads to an inefficient matcher that will do something like this:

if (Mnemonic == "foo_alias") {
  if (Features.test(Feature_Subtarget1Bit))
    Mnemonic == "foo";
  else if (Features.test(Feature_Subtarget2Bit))
    Mnemonic == "foo";
  return;
}

It would be more efficient to skip the feature tests and return "foo"
unconditionally.

Overall it seems better to allow multiple definitions of the identical
alias with no predicate.

Diff Detail

Event Timeline

foad created this revision.Jun 28 2021, 8:01 AM
foad requested review of this revision.Jun 28 2021, 8:01 AM
Herald added a project: Restricted Project. · View Herald TranscriptJun 28 2021, 8:01 AM

In the code sample in the description, is it supposed to be Feature_Subtarget2Bit for the second "if"?

One thing I'm wondering is, how about defining the MnemonicAlias just once outside the multiclass? Like this:

defm FooInst1 : DefInstruction<"foo", (outs), (ins), Subtarget1>;
defm FooInst2 : DefInstruction<"foo", (outs), (ins), Subtarget2>;
def : MnemonicAlias<"foo_alias", "foo">;

Otherwise if the MnemonicAlias is in the multiclass then there are two virtually identical MnemonicAliases being defined right?

I think that generally speaking it makes sense to avoid letting users use duplicate inputs since that might help catch errors in some cases. On the other hand I can see the practical utility in allowing duplicates when it comes to working with multiclasses. I'm not sure, thoughts?

foad edited the summary of this revision. (Show Details)Jun 29 2021, 6:22 AM
foad added a comment.Jun 29 2021, 6:37 AM

In the code sample in the description, is it supposed to be Feature_Subtarget2Bit for the second "if"?

Yes, sorry about that. Fixed.

One thing I'm wondering is, how about defining the MnemonicAlias just once outside the multiclass?

Well, yes. The problem with simplifying the example enough to fit in a commit message, is that it makes it look simple to fix by rewriting the td file. The real world example is more complicated. And yes I'm sure I could fix it by changing the td file, but I was hoping you all would take my word for it that it's pretty awkward to do that.

To show you the complete real world example I would have to spend some time making it open-source-able. But the multiclass I'm really interested in is analogous to:

multiclass VOP1Inst <string opName, VOPProfile P,
                     SDPatternOperator node = null_frag> {
  // We only want to set this on the basic, non-SDWA or DPP forms.
  defvar should_mov_imm = !eq(opName, "v_mov_b32");

  let isMoveImm = should_mov_imm in {
    def _e32 : VOP1_Pseudo <opName, P>;
    def _e64 : VOP3_Pseudo <opName, P, getVOP1Pat64<node, P>.ret>;
  }

  foreach _ = BoolToList<P.HasExtSDWA>.ret in
    def _sdwa : VOP1_SDWA_Pseudo <opName, P>;

  foreach _ = BoolToList<P.HasExtDPP>.ret in
    def _dpp : VOP1_DPP_Pseudo <opName, P>;

  def : MnemonicAlias<opName#"_e32", opName>, LetDummies;
  def : MnemonicAlias<opName#"_e64", opName>, LetDummies;

  foreach _ = BoolToList<P.HasExtSDWA>.ret in
    def : MnemonicAlias<opName#"_sdwa", opName>, LetDummies;

  foreach _ = BoolToList<P.HasExtDPP>.ret in
    def : MnemonicAlias<opName#"_dpp", opName>, LetDummies;
}

And it's used something like this:

let SubtargetPredicate = isGFX7GFX8GFX9 in {
  let TRANS = 1, SchedRW = [WriteTrans32] in {
    defm V_LOG_LEGACY_F32 : VOP1Inst<"v_log_legacy_f32", VOP_F32_F32>;
    defm V_EXP_LEGACY_F32 : VOP1Inst<"v_exp_legacy_f32", VOP_F32_F32>;
  } // End TRANS = 1, SchedRW = [WriteTrans32]
} // End SubtargetPredicate = isGFX7GFX8GFX9

And note that the way the predicate is set on the instructions with let SubtargetPredicate = (where SubtargetPredicate is used by a class called PredicateControl which is a superclass of VOP1_Pseudo, VOP3_Pseudo et al) does not automatically set a corresponding predicate on the MnemonicAliases defined by the multiclass.

This revision is now accepted and ready to land.Jun 29 2021, 10:11 AM
This revision was landed with ongoing or failed builds.Jun 30 2021, 2:53 AM
This revision was automatically updated to reflect the committed changes.