This is an archive of the discontinued LLVM Phabricator instance.

[globalisel][tablegen] Revise API for ComplexPattern operands to improve flexibility.
ClosedPublic

Authored by dsanders on Apr 6 2017, 7:20 AM.

Details

Summary

Some targets need to be able to do more complex rendering than just adding an
operand or two to an instruction. For example, it may need to insert an
instruction to extract a subreg first, or it may need to perform an operation
on the operand.

In SelectionDAG, targets would create SDNode's to achieve the desired effect
during the complex pattern predicate. This worked because SelectionDAG had a
form of garbage collection that would take care of SDNode's that were created
but not used due to a later predicate rejecting a match. This doesn't translate
well to GlobalISel and the churn was wasteful.

The API changes in this patch enable GlobalISel to accomplish the same thing
without the waste. The API is now:
InstructionSelector::OptionalComplexRendererFn selectArithImmed(MachineOperand &Root) const;
where Root is the root of the match. The return value can be omitted to
indicate that the predicate failed to match, or a function with the signature
ComplexRendererFn can be returned. For example:
return OptionalComplexRendererFn(

	       [=](MachineInstrBuilder &MIB) { MIB.addImm(Immed).addImm(ShVal); });

adds two immediate operands to the rendered instruction. Immed and ShVal are
captured from the predicate function.

As an added bonus, this also reduces the amount of information we need to
provide to GIComplexOperandMatcher.

Depends on D31418

Event Timeline

dsanders created this revision.Apr 6 2017, 7:20 AM
ab edited edge metadata.Apr 10 2017, 1:16 PM

I'm slightly worried that this makes the selector functions a bit harder to read, but this seems like a nice simplification otherwise.

One question: does this prevent us from ever selecting operands in-place? The previous implementation seems amenable to that (by only assigning the complex-pattern MO instead of adding them all to a new MI). But now we'd be forced to always append operands, right?

This also means that we can remove MO_Placeholder, yes?

include/llvm/CodeGen/GlobalISel/InstructionSelector.h
82

The Optional seems a bit redundant: std::function is already optional. Can we return a {} default?

test/TableGen/GlobalISelEmitter-GIRule.td
70 ↗(On Diff #94367)

What about:

((Renderer0 = selectComplexPattern(MI0.getOperand(2))))

I found the ',' more surprising than the '='.

In D31761#722903, @ab wrote:

I'm slightly worried that this makes the selector functions a bit harder to read, but this seems like a nice simplification otherwise.

One question: does this prevent us from ever selecting operands in-place? The previous implementation seems amenable to that (by only assigning the complex-pattern MO instead of adding them all to a new MI). But now we'd be forced to always append operands, right?

I think it's still possible to handle the mutation cases we could potentially support before this patch. Supporting one matched operand being rendered as one operand can be supported with a little additional information to specify which operand to replace* and a tablegen field to indicate that the renderer supports mutation. However, it's quite common for one matched operand to be rendered as multiple operands (e.g. for addressing modes like addr+offset, or for ARM's immediates). I'm not sure this case is efficiently supportable before or after this patch since we'd need to make room for the additional operands and the underlying storage is an array.

*This gets trickier when multiple instructions are emitted by a rule though. We'd need to decide which instruction to mutate.

This also means that we can remove MO_Placeholder, yes?

That's right. I'll update the patch to do that.


I've been using this mechanism more thoroughly on an out-of-tree target over the last few days and there's an issue that I need to find a solution to. At the moment, we can create new instructions in the renderer but there's no way to have the instruction selector process them. For example, we can emit a target instruction to load an immediate but we can't create a legal G_CONSTANT and ask the instruction selector to figure out some code for it. I'm currently thinking the instruction selector should have a work list we can add new instructions to and only when the work list is empty would it move on to the next instruction in the BB.

It would also be nice to be able to have the legalizer run on newly-created instructions so targets don't end up implementing parts of legalization in the instruction selector like we've seen in SelectionDAG.

Forgot to reply to the inline comments

include/llvm/CodeGen/GlobalISel/InstructionSelector.h
82

I think that will work. I'll give it a try.

test/TableGen/GlobalISelEmitter-GIRule.td
70 ↗(On Diff #94367)

Assuming the std::function change above works out, this change will be easy to make.

dsanders updated this revision to Diff 95758.Apr 19 2017, 8:30 AM
dsanders marked 4 inline comments as done.

Remove placeholder operands since they're no longer needed.
Drop the Optional<> around ComplexRendererFn in favour of nullptr.
Remove the dependency on D31329.

ab accepted this revision.Apr 20 2017, 10:59 AM

Nice, LGTM!

This revision is now accepted and ready to land.Apr 20 2017, 10:59 AM
dsanders closed this revision.Apr 22 2017, 8:23 AM