This is an archive of the discontinued LLVM Phabricator instance.

[Tablegen] Attempt to add support for patterns containing nodes with multiple results.
ClosedPublic

Authored by craig.topper on Mar 16 2015, 11:45 PM.

Details

Summary

This patch attempts to add support nodes with multiple results. This is necessary to enable AVX512 masked scatter/gather support.

This change exposed some odd behavior with a few instructions in the R600 backend. Previously it was using IMPLICIT_DEF input to represent what was really a second output and this magically worked. But now that we used NumDefs directly this needs to be converted to a proper output. But to do that we need to be able to cast a second output type. So I've introduced a ValueTypeList that can contain multiple ValueTypes.

Diff Detail

Event Timeline

craig.topper retitled this revision from to [Tablegen] Attempt to add support for patterns containing nodes with multiple results..
craig.topper updated this object.
craig.topper edited the test plan for this revision. (Show Details)
craig.topper added a reviewer: hfinkel.
craig.topper added a subscriber: Unknown Object (MLST).
hfinkel edited edge metadata.

[+Tom, Matt for the R600 changes]

The second output on those R600 instructions is just there to reserve a temp register to make it easier to lower the instruction later on. I think this patch should be OK.

Is there a more correct type than i1? I just picked i1 because that happened to be what got picked before due to some code that tries to fix a circular dependency in types. I don't think it was really intended to fire in this particular case but it did.

craig.topper edited edge metadata.

I had lost a change where I removed one of the other places where NumDefs was forced to be 1 if it was non-zero.

The second output on those R600 instructions is just there to reserve a temp register to make it easier to lower the instruction later on. I think this patch should be OK.

Alright, I see what you mean, you have this:

def SI_INDIRECT_SRC : InstSI <
  (outs VGPR_32:$dst, SReg_64:$temp),
  (ins unknown:$src, VSrc_32:$idx, i32imm:$off),
  "si_indirect_src $dst, $temp, $src, $idx, $off",
  []
>;

and so when you use this:

// 1. Extract with offset
def : Pat<
  (vector_extract vt:$vec, (add i32:$idx, imm:$off)),
  (eltvt (SI_INDIRECT_SRC (IMPLICIT_DEF), $vec, $idx, imm:$off))
>;

the pattern generates the IMPLICIT_DEF as the output operand somehow (even though it is written as an input operand), and you get the scratch register that you need.

Would [Any, Any] make sense here too?

Craig, this is indeed a use case we need to support: having a pseudo-instruction with a 'scratch register' output definition. However, it does raise the question that, if we're going to support pattern matching instructions with multiple output operands (which is the point here), what should we do if the input pattern does not itself have multiple output operands?

In short, I don't understand your any_i1 solution. It does not help to match the type of vector_insert, etc., and SI_INDIRECT_SRC, etc. already have multiple output operands. The type of the second output operand might have an ambiguous type, but why does that matter if we're not matching against it? Forcing the user to choose a type that is sure to be ignored / irrelevant seems confusing.

But it's not really ignored. At the end of isel we have an SDNode with 2 results and those results need types.

Today that IMPLICIT_DEF got a type of i1 because this code triggered

if (!IterateInference && InferredAllPatternTypes &&
    !InferredAllResultTypes)
  IterateInference =
      ForceArbitraryInstResultType(Result.getTree(0), Result);

but based on the comment above that code I don't think this case was what that code was intended for. I guess this part of that comment applies for this case "output patterns should have register classes, not MVTs". Maybe I should force an arbitrary result type on extra results that don't show up in the original pattern?

The second output on those R600 instructions is just there to reserve a temp register to make it easier to lower the instruction later on. I think this patch should be OK.

Alright, I see what you mean, you have this:

def SI_INDIRECT_SRC : InstSI <
  (outs VGPR_32:$dst, SReg_64:$temp),
  (ins unknown:$src, VSrc_32:$idx, i32imm:$off),
  "si_indirect_src $dst, $temp, $src, $idx, $off",
  []
>;

and so when you use this:

// 1. Extract with offset
def : Pat<
  (vector_extract vt:$vec, (add i32:$idx, imm:$off)),
  (eltvt (SI_INDIRECT_SRC (IMPLICIT_DEF), $vec, $idx, imm:$off))
>;

the pattern generates the IMPLICIT_DEF as the output operand somehow (even though it is written as an input operand), and you get the scratch register that you need.

Would [Any, Any] make sense here too?

Craig, this is indeed a use case we need to support: having a pseudo-instruction with a 'scratch register' output definition. However, it does raise the question that, if we're going to support pattern matching instructions with multiple output operands (which is the point here), what should we do if the input pattern does not itself have multiple output operands?

Hi Hal,

Do we need to support this use case because there are other targets that do this too? Otherwise if R600 is the only target doing this, would it help if I tried to rework this pseudo to only have one output?

In short, I don't understand your any_i1 solution. It does not help to match the type of vector_insert, etc., and SI_INDIRECT_SRC, etc. already have multiple output operands. The type of the second output operand might have an ambiguous type, but why does that matter if we're not matching against it? Forcing the user to choose a type that is sure to be ignored / irrelevant seems confusing.

But it's not really ignored. At the end of isel we have an SDNode with 2 results and those results need types.

Today that IMPLICIT_DEF got a type of i1 because this code triggered

if (!IterateInference && InferredAllPatternTypes &&
    !InferredAllResultTypes)
  IterateInference =
      ForceArbitraryInstResultType(Result.getTree(0), Result);

but based on the comment above that code I don't think this case was what that code was intended for. I guess this part of that comment applies for this case "output patterns should have register classes, not MVTs". Maybe I should force an arbitrary result type on extra results that don't show up in the original pattern?

Yes, I actually think that would be better. While we do need a type at the SDAG level, all that really matters in the end is that we end up with a register from the correct register class. There's no need for the user to pick a type for a result that is not really used aside from its affect during register allocation.

The second output on those R600 instructions is just there to reserve a temp register to make it easier to lower the instruction later on. I think this patch should be OK.

...

Craig, this is indeed a use case we need to support: having a pseudo-instruction with a 'scratch register' output definition. However, it does raise the question that, if we're going to support pattern matching instructions with multiple output operands (which is the point here), what should we do if the input pattern does not itself have multiple output operands?

Hi Hal,

Do we need to support this use case because there are other targets that do this too? Otherwise if R600 is the only target doing this, would it help if I tried to rework this pseudo to only have one output?

I am fairly certain that you're not the only target doing this. I recall doing this myself at some point (although I selected the instructions manually in *ISelDAGToDAG because I did not discover the same trick that you did). Having this supported by TableGen properly will be convenient.

In short, I don't understand your any_i1 solution. It does not help to match the type of vector_insert, etc., and SI_INDIRECT_SRC, etc. already have multiple output operands. The type of the second output operand might have an ambiguous type, but why does that matter if we're not matching against it? Forcing the user to choose a type that is sure to be ignored / irrelevant seems confusing.

It seems with some other change that I made after adding the typecast in R600 the need for typecast went away. Yay!

hfinkel accepted this revision.Mar 19 2015, 3:02 AM
hfinkel edited edge metadata.

LGTM, thanks!

This revision is now accepted and ready to land.Mar 19 2015, 3:02 AM
craig.topper closed this revision.Mar 19 2015, 10:13 PM

Committed in r232798.