This is an archive of the discontinued LLVM Phabricator instance.

[globalisel][tblgen] Add support for ComplexPatterns
ClosedPublic

Authored by dsanders on Feb 17 2017, 4:24 AM.

Details

Summary

Adds a new kind of MachineOperand: MO_Placeholder.
This operand must not appear in the MIR and only exists as a way of
creating an 'uninitialized' operand until a matcher function overwrites it.

Depends on D30046, D29712

Event Timeline

dsanders created this revision.Feb 17 2017, 4:24 AM
qcolombet added inline comments.Feb 17 2017, 9:28 AM
lib/Target/AArch64/AArch64InstructionSelector.cpp
1264

Can't we modify the existing placeholder instead of creating a temporary object and then use the copy constructor?

dsanders added inline comments.Feb 17 2017, 9:58 AM
lib/Target/AArch64/AArch64InstructionSelector.cpp
1264

I think we should be able to use ChangeToImmediate(), but I haven't tried it yet. The bit that worried me about doing that was having stale values in members that aren't affected by the ChangeTo*() functions. Most of them look ok (since they only matter for registers and ChangeToRegister() does reset them) but I'm not sure about MachineOperand::ParentMI.

qcolombet edited edge metadata.Feb 17 2017, 3:37 PM

One remark: I can see how a placeholder operand can help detecting errors (like we forgot to update one of the operand), however, I think it gets in the way of efficiency.
Indeed, I imagine that each time a complex pattern will fail half way through, we will have to reset all the operands to placeholder instead of just keeping them in the state they are.

What I am saying is that we may not introduce a placeholder operand at all. We should have a placeholder opcode though that reset the operands array to 0 (without freeing memory).

lib/Target/AArch64/AArch64InstructionSelector.cpp
1264

That should be easy to check/fix :).

Sorry for the slow reply on this one.

One remark: I can see how a placeholder operand can help detecting errors (like we forgot to update one of the operand), however, I think it gets in the way of efficiency.
Indeed, I imagine that each time a complex pattern will fail half way through, we will have to reset all the operands to placeholder instead of just keeping them in the state they are.

It's not really aimed at detecting errors but rather at constructing the MachineOperand without creating fake data to put in it. Each successful ComplexPattern predicate will fully overwrite the placeholder (using the default copy constructor) so I don't think we need to reset it between rules. I can see how it would be useful to reset it to the placeholder data to ensure the predicate is behaving correctly though since if a predicate returns true but the placeholder data is still there then there's a bug in the predicate.

One other thing to mention is that I'd like to skip the constructor call completely and leave it uninitialized until the first successful ComplexPattern predicate overwrites it. Initializing the operands to a placeholder is just unnecessary overhead.

What I am saying is that we may not introduce a placeholder operand at all. We should have a placeholder opcode though that reset the operands array to 0 (without freeing memory).

What would be the benefit of a placeholder instruction over placeholder operands? I think it might be slightly higher cost since if we reset the operand list between rules then it increases the number of MachineOperand constructor calls (but not allocations since it uses the placement new operator). A placeholder instruction also means the instruction and operands are heap allocated instead of stack allocated and will therefore cause a memory leak unless additional overhead to free the instruction is added.

lib/Target/AArch64/AArch64InstructionSelector.cpp
1264

When we create new operands, the ChangeTo*() family are ok so long as you also call clearParent() afterwards to clear and parents left over from previous predicates.

dsanders updated this revision to Diff 90967.Mar 7 2017, 5:08 PM

Use the ChangeTo*() family.

What would be the benefit of a placeholder instruction over placeholder operands?

I expect the placeholder reset to be something just a single assignment of the style size of array operand = 0. The operands remain as they are, they are just not accessed. In other words, no constructor call.

dsanders updated this revision to Diff 91416.Mar 10 2017, 2:30 PM

Further reduce constructor call overhead by moving the temporaries to the class.

What would be the benefit of a placeholder instruction over placeholder operands?

I expect the placeholder reset to be something just a single assignment of the style size of array operand = 0. The operands remain as they are, they are just not accessed. In other words, no constructor call.

The constructor call comes from the need to call addOperand(). If we reset the operand list for the placeholder instruction on each call to selectImpl() then we would need something like:

PlaceholderInsn.addOperand(MachineOperand::CreatePlaceholder());
PlaceholderInsn.addOperand(MachineOperand::CreatePlaceholder());
if (matches && selectComplexPattern(PlaceholderInsn.getOperand(0), PlaceholderInsn.getOperand(1))) {
  BuildMI(...).add(PlaceholderInsn.getOperand(0)).add(PlaceholderInsn.getOperand(1));
  ...
}
PlaceholderInsn.resetOperands();

Each call to addOperand() involves a placement new operator which uses the copy-constructor and therefore pays the same overhead as the current local variable approach.

I think your main concern is that the overhead is unnecessarily paid on each call to selectImpl(). I've updated the patch with a version that only pays the constructor overhead once (during <Target>InstructionSelection's constructor). Does that resolve your concerns?

qcolombet accepted this revision.Mar 10 2017, 3:19 PM

The constructor call comes from the need to call addOperand(). If we reset the operand list for the placeholder instruction on each call to selectImpl() then we would need something like:

PlaceholderInsn.addOperand(MachineOperand::CreatePlaceholder());
PlaceholderInsn.addOperand(MachineOperand::CreatePlaceholder());
if (matches && selectComplexPattern(PlaceholderInsn.getOperand(0), PlaceholderInsn.getOperand(1))) {
BuildMI(...).add(PlaceholderInsn.getOperand(0)).add(PlaceholderInsn.getOperand(1));
...
}
PlaceholderInsn.resetOperands();
Each call to addOperand() involves a placement new operator which uses the copy-constructor and therefore pays the same overhead as the current local variable approach.

I was actually expecting more something like:
PlaceHolderInst.setNumOperand(NbOp);
With that call just allocating/resizing the operand array accordingly. The operands created could be left uninitialized as they are supposed to be all properly set by the matching.

I think your main concern is that the overhead is unnecessarily paid on each call to selectImpl(). I've updated the patch with a version that only pays the constructor overhead once (during <Target>InstructionSelection's constructor). Does that resolve your concerns?

Yes, partly. I believe we don't need the OperandHolder all together, but this is an optimization that we definitely don't need to do now.

All in all, the patch looks sensible to me.

lib/Target/AArch64/AArch64InstructionSelector.cpp
1264

I would have expected this to be handled by the ChangeTo* APIs.
There might be a good reason why this is not the case. Do you know?

This revision is now accepted and ready to land.Mar 10 2017, 3:19 PM

The constructor call comes from the need to call addOperand(). If we reset the operand list for the placeholder instruction on each call to selectImpl() then we would need something like:

PlaceholderInsn.addOperand(MachineOperand::CreatePlaceholder());
PlaceholderInsn.addOperand(MachineOperand::CreatePlaceholder());
if (matches && selectComplexPattern(PlaceholderInsn.getOperand(0), PlaceholderInsn.getOperand(1))) {
BuildMI(...).add(PlaceholderInsn.getOperand(0)).add(PlaceholderInsn.getOperand(1));
...
}
PlaceholderInsn.resetOperands();
Each call to addOperand() involves a placement new operator which uses the copy-constructor and therefore pays the same overhead as the current local variable approach.

I was actually expecting more something like:
PlaceHolderInst.setNumOperand(NbOp);
With that call just allocating/resizing the operand array accordingly. The operands created could be left uninitialized as they are supposed to be all properly set by the matching.

Ah ok, I see how that would avoid the redundant initializations. Wouldn't it be more efficient to call setNumOperands(MaxNumOperands) in advance though to save on re-allocating and copying the array?

I think your main concern is that the overhead is unnecessarily paid on each call to selectImpl(). I've updated the patch with a version that only pays the constructor overhead once (during <Target>InstructionSelection's constructor). Does that resolve your concerns?

Yes, partly. I believe we don't need the OperandHolder all together, but this is an optimization that we definitely don't need to do now.

I'm not sure we can eliminate the placeholders since storing directly to the output instruction requires us to build that instruction before we've finished matching but I'll have a think on how we can do it.

All in all, the patch looks sensible to me.

Thanks.

dsanders closed this revision.Mar 14 2017, 2:44 PM