mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 03:02:36 +01:00
[globalisel][tablegen] Simplify named operand/operator lookups and fix a wrong-code bug this revealed.
Summary: Operand variable lookups are now performed by the RuleMatcher rather than searching the whole matcher hierarchy for a match. This revealed a wrong-code bug that currently affects ARM and X86 where patterns that use a variable more than once in the match pattern will be imported but won't check that the operands are identical. This can cause the tablegen-erated matcher to accept matches that should be rejected. Depends on D36569 Reviewers: ab, t.p.northover, qcolombet, rovka, aditya_nandakumar Subscribers: aemerson, igorb, llvm-commits, kristof.beyls Differential Revision: https://reviews.llvm.org/D36618 llvm-svn: 315780
This commit is contained in:
parent
efdd00d795
commit
d400a290e5
@ -150,6 +150,13 @@ enum {
|
||||
/// - InsnID - Instruction ID
|
||||
GIM_CheckIsSafeToFold,
|
||||
|
||||
/// Check the specified operands are identical.
|
||||
/// - InsnID - Instruction ID
|
||||
/// - OpIdx - Operand index
|
||||
/// - OtherInsnID - Other instruction ID
|
||||
/// - OtherOpIdx - Other operand index
|
||||
GIM_CheckIsSameOperand,
|
||||
|
||||
/// Fail the current try-block, or completely fail to match if there is no
|
||||
/// current try-block.
|
||||
GIM_Reject,
|
||||
|
@ -334,7 +334,25 @@ bool InstructionSelector::executeMatchTable(
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GIM_CheckIsSameOperand: {
|
||||
int64_t InsnID = MatchTable[CurrentIdx++];
|
||||
int64_t OpIdx = MatchTable[CurrentIdx++];
|
||||
int64_t OtherInsnID = MatchTable[CurrentIdx++];
|
||||
int64_t OtherOpIdx = MatchTable[CurrentIdx++];
|
||||
DEBUG(dbgs() << CurrentIdx << ": GIM_CheckIsSameOperand(MIs[" << InsnID
|
||||
<< "][" << OpIdx << "], MIs[" << OtherInsnID << "]["
|
||||
<< OtherOpIdx << "])\n");
|
||||
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
|
||||
assert(State.MIs[OtherInsnID] != nullptr && "Used insn before defined");
|
||||
State.MIs[InsnID]->getOperand(OpIdx).dump();
|
||||
State.MIs[OtherInsnID]->getOperand(OtherOpIdx).dump();
|
||||
if (!State.MIs[InsnID]->getOperand(OpIdx).isIdenticalTo(
|
||||
State.MIs[OtherInsnID]->getOperand(OtherInsnID))) {
|
||||
if (handleReject() == RejectAndGiveUp)
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case GIM_Reject:
|
||||
DEBUG(dbgs() << CurrentIdx << ": GIM_Reject");
|
||||
if (handleReject() == RejectAndGiveUp)
|
||||
|
58
test/CodeGen/X86/GlobalISel/select-blsr.mir
Normal file
58
test/CodeGen/X86/GlobalISel/select-blsr.mir
Normal file
@ -0,0 +1,58 @@
|
||||
# RUN: llc -mtriple=x86_64-linux-gnu -mattr=+bmi -global-isel -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s
|
||||
#
|
||||
# Test that rules where multiple operands must be the same operand successfully
|
||||
# match. Also test that the rules do not match when they're not the same
|
||||
# operand.
|
||||
|
||||
---
|
||||
name: test_blsr32rr
|
||||
# CHECK-LABEL: name: test_blsr32rr
|
||||
alignment: 4
|
||||
legalized: true
|
||||
regBankSelected: true
|
||||
# CHECK: registers:
|
||||
# CHECK-NEXT: - { id: 0, class: gr32, preferred-register: '' }
|
||||
# CHECK-NEXT: - { id: 1, class: gpr, preferred-register: '' }
|
||||
# CHECK-NEXT: - { id: 2, class: gpr, preferred-register: '' }
|
||||
# CHECK-NEXT: - { id: 3, class: gr32, preferred-register: '' }
|
||||
registers:
|
||||
- { id: 0, class: gpr }
|
||||
- { id: 1, class: gpr }
|
||||
- { id: 2, class: gpr }
|
||||
- { id: 3, class: gpr }
|
||||
# G_ADD and G_AND both use %0 so we should match this.
|
||||
# CHECK: %3 = BLSR32rr %0
|
||||
body: |
|
||||
bb.1:
|
||||
liveins: %edi
|
||||
|
||||
%0(s32) = COPY %edi
|
||||
%1(s32) = G_CONSTANT i32 -1
|
||||
%2(s32) = G_ADD %0, %1
|
||||
%3(s32) = G_AND %2, %0
|
||||
%edi = COPY %3
|
||||
|
||||
...
|
||||
---
|
||||
name: test_blsr32rr_nomatch
|
||||
# CHECK-LABEL: name: test_blsr32rr_nomatch
|
||||
alignment: 4
|
||||
legalized: true
|
||||
regBankSelected: true
|
||||
registers:
|
||||
- { id: 0, class: gpr }
|
||||
- { id: 1, class: gpr }
|
||||
- { id: 2, class: gpr }
|
||||
- { id: 3, class: gpr }
|
||||
# G_ADD and G_AND use different operands so we shouldn't match this.
|
||||
# CHECK-NOT: BLSR32rr
|
||||
body: |
|
||||
bb.1:
|
||||
liveins: %edi
|
||||
|
||||
%0(s32) = COPY %edi
|
||||
%1(s32) = G_CONSTANT i32 -1
|
||||
%2(s32) = G_ADD %1, %1
|
||||
%3(s32) = G_AND %2, %0
|
||||
%edi = COPY %3
|
||||
...
|
@ -259,7 +259,7 @@ def : Pat<(select GPR32:$src1, complex:$src2, complex:$src3),
|
||||
def ADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2),
|
||||
[(set GPR32:$dst, (add GPR32:$src1, GPR32:$src2))]>;
|
||||
|
||||
//===- Test a simple pattern with ValueType operands. ----------------------===//
|
||||
//===- Test a pattern with a tied operand in the matcher ------------------===//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 3*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
|
||||
@ -267,6 +267,30 @@ def ADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2),
|
||||
// CHECK-NEXT: // MIs[0] dst
|
||||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
|
||||
// CHECK-NEXT: // MIs[0] src{{$}}
|
||||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID,
|
||||
// CHECK-NEXT: // MIs[0] src{{$}}
|
||||
// CHECK-NEXT: GIM_CheckIsSameOperand, /*MI*/0, /*OpIdx*/2, /*OtherMI*/0, /*OtherOpIdx*/1,
|
||||
// CHECK-NEXT: // (add:{ *:[i32] } GPR32:{ *:[i32] }:$src, GPR32:{ *:[i32] }:$src) => (DOUBLE:{ *:[i32] } GPR32:{ *:[i32] }:$src)
|
||||
// CHECK-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::DOUBLE,
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst
|
||||
// CHECK-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 3: @[[LABEL]]
|
||||
|
||||
def DOUBLE : I<(outs GPR32:$dst), (ins GPR32:$src), [(set GPR32:$dst, (add GPR32:$src, GPR32:$src))]>;
|
||||
|
||||
//===- Test a simple pattern with ValueType operands. ----------------------===//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 4*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_ADD,
|
||||
// CHECK-NEXT: // MIs[0] dst
|
||||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID,
|
||||
// CHECK-NEXT: // MIs[0] src1
|
||||
// CHECK-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32,
|
||||
// CHECK-NEXT: // MIs[0] src2
|
||||
@ -275,7 +299,7 @@ def ADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2),
|
||||
// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::ADD,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 3: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 4: @[[LABEL]]
|
||||
|
||||
def : Pat<(add i32:$src1, i32:$src2),
|
||||
(ADD i32:$src1, i32:$src2)>;
|
||||
@ -283,7 +307,7 @@ def : Pat<(add i32:$src1, i32:$src2),
|
||||
//===- Test a simple pattern with an intrinsic. ---------------------------===//
|
||||
//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 4*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 5*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_INTRINSIC,
|
||||
// CHECK-NEXT: // MIs[0] dst
|
||||
@ -302,14 +326,14 @@ def : Pat<(add i32:$src1, i32:$src2),
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 4: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 5: @[[LABEL]]
|
||||
|
||||
def MOV : I<(outs GPR32:$dst), (ins GPR32:$src1),
|
||||
[(set GPR32:$dst, (int_mytarget_nop GPR32:$src1))]>;
|
||||
|
||||
//===- Test a nested instruction match. -----------------------------------===//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 5*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 6*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckFeatures, GIFBS_HasA,
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
|
||||
// CHECK-NEXT: GIM_RecordInsn, /*DefineMI*/1, /*MI*/0, /*OpIdx*/1, // MIs[1]
|
||||
@ -342,10 +366,10 @@ def MOV : I<(outs GPR32:$dst), (ins GPR32:$src1),
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 5: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 6: @[[LABEL]]
|
||||
|
||||
// We also get a second rule by commutativity.
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 6*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 7*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckFeatures, GIFBS_HasA,
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
|
||||
// CHECK-NEXT: GIM_RecordInsn, /*DefineMI*/1, /*MI*/0, /*OpIdx*/2,
|
||||
@ -378,7 +402,7 @@ def MOV : I<(outs GPR32:$dst), (ins GPR32:$src1),
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 6: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 7: @[[LABEL]]
|
||||
|
||||
def MULADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3),
|
||||
[(set GPR32:$dst,
|
||||
@ -387,7 +411,7 @@ def MULADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3),
|
||||
|
||||
//===- Test another simple pattern with regclass operands. ----------------===//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 7*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 8*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckFeatures, GIFBS_HasA_HasB_HasC,
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_MUL,
|
||||
@ -408,7 +432,7 @@ def MULADD : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3),
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 7: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 8: @[[LABEL]]
|
||||
|
||||
def MUL : I<(outs GPR32:$dst), (ins GPR32:$src2, GPR32:$src1),
|
||||
[(set GPR32:$dst, (mul GPR32:$src1, GPR32:$src2))]>,
|
||||
@ -416,7 +440,7 @@ def MUL : I<(outs GPR32:$dst), (ins GPR32:$src2, GPR32:$src1),
|
||||
|
||||
//===- Test a more complex multi-instruction match. -----------------------===//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 8*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 9*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckFeatures, GIFBS_HasA,
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
|
||||
// CHECK-NEXT: GIM_RecordInsn, /*DefineMI*/1, /*MI*/0, /*OpIdx*/1, // MIs[1]
|
||||
@ -461,7 +485,7 @@ def MUL : I<(outs GPR32:$dst), (ins GPR32:$src2, GPR32:$src1),
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 8: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 9: @[[LABEL]]
|
||||
|
||||
def INSNBOB : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3, GPR32:$src4),
|
||||
[(set GPR32:$dst,
|
||||
@ -471,7 +495,7 @@ def INSNBOB : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3, G
|
||||
//===- Test a pattern with ComplexPattern operands. -----------------------===//
|
||||
//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 9*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 10*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_SUB,
|
||||
// CHECK-NEXT: // MIs[0] dst
|
||||
@ -491,7 +515,7 @@ def INSNBOB : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2, GPR32:$src3, G
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 9: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 10: @[[LABEL]]
|
||||
|
||||
def INSN1 : I<(outs GPR32:$dst), (ins GPR32:$src1, complex:$src2), []>;
|
||||
def : Pat<(sub GPR32:$src1, complex:$src2), (INSN1 GPR32:$src1, complex:$src2)>;
|
||||
@ -499,7 +523,7 @@ def : Pat<(sub GPR32:$src1, complex:$src2), (INSN1 GPR32:$src1, complex:$src2)>;
|
||||
//===- Test a simple pattern with a default operand. ----------------------===//
|
||||
//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 10*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 11*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_XOR,
|
||||
// CHECK-NEXT: // MIs[0] dst
|
||||
@ -519,7 +543,7 @@ def : Pat<(sub GPR32:$src1, complex:$src2), (INSN1 GPR32:$src1, complex:$src2)>;
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 10: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 11: @[[LABEL]]
|
||||
|
||||
// The -2 is just to distinguish it from the 'not' case below.
|
||||
def XORI : I<(outs GPR32:$dst), (ins m1:$src2, GPR32:$src1),
|
||||
@ -528,7 +552,7 @@ def XORI : I<(outs GPR32:$dst), (ins m1:$src2, GPR32:$src1),
|
||||
//===- Test a simple pattern with a default register operand. -------------===//
|
||||
//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 11*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 12*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_XOR,
|
||||
// CHECK-NEXT: // MIs[0] dst
|
||||
@ -548,7 +572,7 @@ def XORI : I<(outs GPR32:$dst), (ins m1:$src2, GPR32:$src1),
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 11: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 12: @[[LABEL]]
|
||||
|
||||
// The -3 is just to distinguish it from the 'not' case below and the other default op case above.
|
||||
def XOR : I<(outs GPR32:$dst), (ins Z:$src2, GPR32:$src1),
|
||||
@ -557,7 +581,7 @@ def XOR : I<(outs GPR32:$dst), (ins Z:$src2, GPR32:$src1),
|
||||
//===- Test a simple pattern with a multiple default operands. ------------===//
|
||||
//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 12*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 13*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_XOR,
|
||||
// CHECK-NEXT: // MIs[0] dst
|
||||
@ -578,7 +602,7 @@ def XOR : I<(outs GPR32:$dst), (ins Z:$src2, GPR32:$src1),
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 12: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 13: @[[LABEL]]
|
||||
|
||||
// The -4 is just to distinguish it from the other 'not' cases.
|
||||
def XORlike : I<(outs GPR32:$dst), (ins m1Z:$src2, GPR32:$src1),
|
||||
@ -587,7 +611,7 @@ def XORlike : I<(outs GPR32:$dst), (ins m1Z:$src2, GPR32:$src1),
|
||||
//===- Test a simple pattern with multiple operands with defaults. --------===//
|
||||
//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 13*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 14*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_XOR,
|
||||
// CHECK-NEXT: // MIs[0] dst
|
||||
@ -609,7 +633,7 @@ def XORlike : I<(outs GPR32:$dst), (ins m1Z:$src2, GPR32:$src1),
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 13: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 14: @[[LABEL]]
|
||||
|
||||
// The -5 is just to distinguish it from the other cases.
|
||||
def XORManyDefaults : I<(outs GPR32:$dst), (ins m1Z:$src3, Z:$src2, GPR32:$src1),
|
||||
@ -620,7 +644,7 @@ def XORManyDefaults : I<(outs GPR32:$dst), (ins m1Z:$src3, Z:$src2, GPR32:$src1)
|
||||
// This must precede the 3-register variants because constant immediates have
|
||||
// priority over register banks.
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 14*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 15*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_XOR,
|
||||
// CHECK-NEXT: // MIs[0] dst
|
||||
@ -640,7 +664,7 @@ def XORManyDefaults : I<(outs GPR32:$dst), (ins m1Z:$src3, Z:$src2, GPR32:$src1)
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 14: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 15: @[[LABEL]]
|
||||
|
||||
def ORN : I<(outs GPR32:$dst), (ins GPR32:$src1, GPR32:$src2), []>;
|
||||
def : Pat<(not GPR32:$Wm), (ORN R0, GPR32:$Wm)>;
|
||||
@ -648,7 +672,7 @@ def : Pat<(not GPR32:$Wm), (ORN R0, GPR32:$Wm)>;
|
||||
//===- Test a COPY_TO_REGCLASS --------------------------------------------===//
|
||||
//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 15*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 16*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_BITCAST,
|
||||
// CHECK-NEXT: // MIs[0] dst
|
||||
@ -661,14 +685,14 @@ def : Pat<(not GPR32:$Wm), (ORN R0, GPR32:$Wm)>;
|
||||
// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/TargetOpcode::COPY,
|
||||
// CHECK-NEXT: GIR_ConstrainOperandRC, /*InsnID*/0, /*Op*/0, /*RC GPR32*/1,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 15: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 16: @[[LABEL]]
|
||||
|
||||
def : Pat<(i32 (bitconvert FPR32:$src1)),
|
||||
(COPY_TO_REGCLASS FPR32:$src1, GPR32)>;
|
||||
|
||||
//===- Test a simple pattern with just a specific leaf immediate. ---------===//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 16*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 17*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_CONSTANT,
|
||||
// CHECK-NEXT: // MIs[0] dst
|
||||
@ -682,13 +706,13 @@ def : Pat<(i32 (bitconvert FPR32:$src1)),
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 16: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 17: @[[LABEL]]
|
||||
|
||||
def MOV1 : I<(outs GPR32:$dst), (ins), [(set GPR32:$dst, 1)]>;
|
||||
|
||||
//===- Test a simple pattern with a leaf immediate and a predicate. -------===//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 17*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 18*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_CONSTANT,
|
||||
// CHECK-NEXT: GIM_CheckI64ImmPredicate, /*MI*/0, /*Predicate*/GIPFP_I64_Predicate_simm8,
|
||||
@ -704,14 +728,14 @@ def MOV1 : I<(outs GPR32:$dst), (ins), [(set GPR32:$dst, 1)]>;
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 17: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 18: @[[LABEL]]
|
||||
|
||||
def simm8 : ImmLeaf<i32, [{ return isInt<8>(Imm); }]>;
|
||||
def MOVimm8 : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, simm8:$imm)]>;
|
||||
|
||||
//===- Same again but use an IntImmLeaf. ----------------------------------===//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 18*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 19*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_CONSTANT,
|
||||
// CHECK-NEXT: GIM_CheckAPIntImmPredicate, /*MI*/0, /*Predicate*/GIPFP_APInt_Predicate_simm9,
|
||||
@ -727,14 +751,14 @@ def MOVimm8 : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, simm8:$i
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 18: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 19: @[[LABEL]]
|
||||
|
||||
def simm9 : IntImmLeaf<i32, [{ return isInt<9>(Imm->getSExtValue()); }]>;
|
||||
def MOVimm9 : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, simm9:$imm)]>;
|
||||
|
||||
//===- Test a simple pattern with just a leaf immediate. ------------------===//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 19*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 20*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_CONSTANT,
|
||||
// CHECK-NEXT: // MIs[0] dst
|
||||
@ -749,13 +773,13 @@ def MOVimm9 : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, simm9:$i
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 19: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 20: @[[LABEL]]
|
||||
|
||||
def MOVimm : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, imm:$imm)]>;
|
||||
|
||||
//===- Test a simple pattern with a FP immediate and a predicate. ---------===//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 20*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 21*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/2,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_FCONSTANT,
|
||||
// CHECK-NEXT: GIM_CheckAPFloatImmPredicate, /*MI*/0, /*Predicate*/GIPFP_APFloat_Predicate_fpimmz,
|
||||
@ -771,14 +795,14 @@ def MOVimm : I<(outs GPR32:$dst), (ins i32imm:$imm), [(set GPR32:$dst, imm:$imm)
|
||||
// CHECK-NEXT: GIR_EraseFromParent, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 20: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 21: @[[LABEL]]
|
||||
|
||||
def fpimmz : FPImmLeaf<f32, [{ return Imm->isExactlyValue(0.0); }]>;
|
||||
def MOVfpimmz : I<(outs FPR32:$dst), (ins f32imm:$imm), [(set FPR32:$dst, fpimmz:$imm)]>;
|
||||
|
||||
//===- Test a pattern with an MBB operand. --------------------------------===//
|
||||
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 21*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_Try, /*On fail goto*//*Label 22*/ [[LABEL:[0-9]+]],
|
||||
// CHECK-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/1,
|
||||
// CHECK-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_BR,
|
||||
// CHECK-NEXT: // MIs[0] target
|
||||
@ -787,7 +811,7 @@ def MOVfpimmz : I<(outs FPR32:$dst), (ins f32imm:$imm), [(set FPR32:$dst, fpimmz
|
||||
// CHECK-NEXT: GIR_MutateOpcode, /*InsnID*/0, /*RecycleInsnID*/0, /*Opcode*/MyTarget::BR,
|
||||
// CHECK-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0,
|
||||
// CHECK-NEXT: GIR_Done,
|
||||
// CHECK-NEXT: // Label 21: @[[LABEL]]
|
||||
// CHECK-NEXT: // Label 22: @[[LABEL]]
|
||||
|
||||
def BR : I<(outs), (ins unknown:$target),
|
||||
[(br bb:$target)]>;
|
||||
|
@ -478,14 +478,21 @@ class RuleMatcher {
|
||||
/// emitCaptureOpcodes().
|
||||
DefinedInsnVariablesMap InsnVariableIDs;
|
||||
|
||||
/// A map of named operands defined by the matchers that may be referenced by
|
||||
/// the renderers.
|
||||
StringMap<OperandMatcher *> DefinedOperands;
|
||||
|
||||
/// ID for the next instruction variable defined with defineInsnVar()
|
||||
unsigned NextInsnVarID;
|
||||
|
||||
std::vector<Record *> RequiredFeatures;
|
||||
|
||||
ArrayRef<SMLoc> SrcLoc;
|
||||
|
||||
public:
|
||||
RuleMatcher()
|
||||
: Matchers(), Actions(), InsnVariableIDs(), NextInsnVarID(0) {}
|
||||
RuleMatcher(ArrayRef<SMLoc> SrcLoc)
|
||||
: Matchers(), Actions(), InsnVariableIDs(), DefinedOperands(),
|
||||
NextInsnVarID(0), SrcLoc(SrcLoc) {}
|
||||
RuleMatcher(RuleMatcher &&Other) = default;
|
||||
RuleMatcher &operator=(RuleMatcher &&Other) = default;
|
||||
|
||||
@ -513,7 +520,10 @@ public:
|
||||
return make_range(defined_insn_vars_begin(), defined_insn_vars_end());
|
||||
}
|
||||
|
||||
void defineOperand(StringRef SymbolicName, OperandMatcher &OM);
|
||||
|
||||
const InstructionMatcher &getInstructionMatcher(StringRef SymbolicName) const;
|
||||
const OperandMatcher &getOperandMatcher(StringRef Name) const;
|
||||
|
||||
void emitCaptureOpcodes(MatchTable &Table);
|
||||
|
||||
@ -544,10 +554,10 @@ private:
|
||||
public:
|
||||
/// Construct a new operand predicate and add it to the matcher.
|
||||
template <class Kind, class... Args>
|
||||
Kind &addPredicate(Args&&... args) {
|
||||
Optional<Kind *> addPredicate(Args&&... args) {
|
||||
Predicates.emplace_back(
|
||||
llvm::make_unique<Kind>(std::forward<Args>(args)...));
|
||||
return *static_cast<Kind *>(Predicates.back().get());
|
||||
return static_cast<Kind *>(Predicates.back().get());
|
||||
}
|
||||
|
||||
typename PredicateVec::const_iterator predicates_begin() const {
|
||||
@ -593,6 +603,7 @@ public:
|
||||
/// but OPM_Int must have priority over OPM_RegBank since constant integers
|
||||
/// are represented by a virtual register defined by a G_CONSTANT instruction.
|
||||
enum PredicateKind {
|
||||
OPM_Tie,
|
||||
OPM_ComplexPattern,
|
||||
OPM_IntrinsicID,
|
||||
OPM_Instruction,
|
||||
@ -612,17 +623,6 @@ public:
|
||||
|
||||
PredicateKind getKind() const { return Kind; }
|
||||
|
||||
/// Return the OperandMatcher for the specified operand or nullptr if there
|
||||
/// isn't one by that name in this operand predicate matcher.
|
||||
///
|
||||
/// InstructionOperandMatcher is the only subclass that can return non-null
|
||||
/// for this.
|
||||
virtual Optional<const OperandMatcher *>
|
||||
getOptionalOperand(StringRef SymbolicName) const {
|
||||
assert(!SymbolicName.empty() && "Cannot lookup unnamed operand");
|
||||
return None;
|
||||
}
|
||||
|
||||
/// Emit MatchTable opcodes to capture instructions into the MIs table.
|
||||
///
|
||||
/// Only InstructionOperandMatcher needs to do anything for this method the
|
||||
@ -651,6 +651,23 @@ PredicateListMatcher<OperandPredicateMatcher>::getNoPredicateComment() const {
|
||||
return "No operand predicates";
|
||||
}
|
||||
|
||||
/// Generates code to check that a register operand is defined by the same exact
|
||||
/// one as another.
|
||||
class SameOperandMatcher : public OperandPredicateMatcher {
|
||||
std::string TiedTo;
|
||||
|
||||
public:
|
||||
SameOperandMatcher(StringRef TiedTo)
|
||||
: OperandPredicateMatcher(OPM_Tie), TiedTo(TiedTo) {}
|
||||
|
||||
static bool classof(const OperandPredicateMatcher *P) {
|
||||
return P->getKind() == OPM_Tie;
|
||||
}
|
||||
|
||||
void emitPredicateOpcodes(MatchTable &Table, RuleMatcher &Rule,
|
||||
unsigned InsnVarID, unsigned OpIdx) const override;
|
||||
};
|
||||
|
||||
/// Generates code to check that an operand is a particular LLT.
|
||||
class LLTOperandMatcher : public OperandPredicateMatcher {
|
||||
protected:
|
||||
@ -857,19 +874,6 @@ public:
|
||||
llvm::to_string(OpIdx) + ")";
|
||||
}
|
||||
|
||||
Optional<const OperandMatcher *>
|
||||
getOptionalOperand(StringRef DesiredSymbolicName) const {
|
||||
assert(!DesiredSymbolicName.empty() && "Cannot lookup unnamed operand");
|
||||
if (DesiredSymbolicName == SymbolicName)
|
||||
return this;
|
||||
for (const auto &OP : predicates()) {
|
||||
const auto &MaybeOperand = OP->getOptionalOperand(DesiredSymbolicName);
|
||||
if (MaybeOperand.hasValue())
|
||||
return MaybeOperand.getValue();
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
InstructionMatcher &getInstructionMatcher() const { return Insn; }
|
||||
|
||||
/// Emit MatchTable opcodes to capture instructions into the MIs table.
|
||||
@ -930,8 +934,27 @@ public:
|
||||
unsigned getAllocatedTemporariesBaseID() const {
|
||||
return AllocatedTemporariesBaseID;
|
||||
}
|
||||
|
||||
bool isSameAsAnotherOperand() const {
|
||||
for (const auto &Predicate : predicates())
|
||||
if (isa<SameOperandMatcher>(Predicate))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Specialize OperandMatcher::addPredicate() to refrain from adding redundant
|
||||
// predicates.
|
||||
template <>
|
||||
template <class Kind, class... Args>
|
||||
Optional<Kind *>
|
||||
PredicateListMatcher<OperandPredicateMatcher>::addPredicate(Args &&... args) {
|
||||
if (static_cast<OperandMatcher *>(this)->isSameAsAnotherOperand())
|
||||
return None;
|
||||
Predicates.emplace_back(llvm::make_unique<Kind>(std::forward<Args>(args)...));
|
||||
return static_cast<Kind *>(Predicates.back().get());
|
||||
}
|
||||
|
||||
unsigned ComplexPatternOperandMatcher::getAllocatedTemporariesBaseID() const {
|
||||
return Operand.getAllocatedTemporariesBaseID();
|
||||
}
|
||||
@ -1088,6 +1111,8 @@ class InstructionMatcher
|
||||
protected:
|
||||
typedef std::vector<std::unique_ptr<OperandMatcher>> OperandVec;
|
||||
|
||||
RuleMatcher &Rule;
|
||||
|
||||
/// The operands to match. All rendered operands must be present even if the
|
||||
/// condition is always true.
|
||||
OperandVec Operands;
|
||||
@ -1095,13 +1120,19 @@ protected:
|
||||
std::string SymbolicName;
|
||||
|
||||
public:
|
||||
InstructionMatcher(StringRef SymbolicName) : SymbolicName(SymbolicName) {}
|
||||
InstructionMatcher(RuleMatcher &Rule, StringRef SymbolicName)
|
||||
: Rule(Rule), SymbolicName(SymbolicName) {}
|
||||
|
||||
RuleMatcher &getRuleMatcher() const { return Rule; }
|
||||
|
||||
/// Add an operand to the matcher.
|
||||
OperandMatcher &addOperand(unsigned OpIdx, const std::string &SymbolicName,
|
||||
unsigned AllocatedTemporariesBaseID) {
|
||||
Operands.emplace_back(new OperandMatcher(*this, OpIdx, SymbolicName,
|
||||
AllocatedTemporariesBaseID));
|
||||
if (!SymbolicName.empty())
|
||||
Rule.defineOperand(SymbolicName, *Operands.back());
|
||||
|
||||
return *Operands.back();
|
||||
}
|
||||
|
||||
@ -1115,24 +1146,6 @@ public:
|
||||
llvm_unreachable("Failed to lookup operand");
|
||||
}
|
||||
|
||||
Optional<const OperandMatcher *>
|
||||
getOptionalOperand(StringRef SymbolicName) const {
|
||||
assert(!SymbolicName.empty() && "Cannot lookup unnamed operand");
|
||||
for (const auto &Operand : Operands) {
|
||||
const auto &OM = Operand->getOptionalOperand(SymbolicName);
|
||||
if (OM.hasValue())
|
||||
return OM.getValue();
|
||||
}
|
||||
return None;
|
||||
}
|
||||
|
||||
const OperandMatcher &getOperand(StringRef SymbolicName) const {
|
||||
Optional<const OperandMatcher *>OM = getOptionalOperand(SymbolicName);
|
||||
if (OM.hasValue())
|
||||
return *OM.getValue();
|
||||
llvm_unreachable("Failed to lookup operand");
|
||||
}
|
||||
|
||||
StringRef getSymbolicName() const { return SymbolicName; }
|
||||
unsigned getNumOperands() const { return Operands.size(); }
|
||||
OperandVec::iterator operands_begin() { return Operands.begin(); }
|
||||
@ -1233,9 +1246,9 @@ protected:
|
||||
std::unique_ptr<InstructionMatcher> InsnMatcher;
|
||||
|
||||
public:
|
||||
InstructionOperandMatcher(StringRef SymbolicName)
|
||||
InstructionOperandMatcher(RuleMatcher &Rule, StringRef SymbolicName)
|
||||
: OperandPredicateMatcher(OPM_Instruction),
|
||||
InsnMatcher(new InstructionMatcher(SymbolicName)) {}
|
||||
InsnMatcher(new InstructionMatcher(Rule, SymbolicName)) {}
|
||||
|
||||
static bool classof(const OperandPredicateMatcher *P) {
|
||||
return P->getKind() == OPM_Instruction;
|
||||
@ -1243,12 +1256,6 @@ public:
|
||||
|
||||
InstructionMatcher &getInsnMatcher() const { return *InsnMatcher; }
|
||||
|
||||
Optional<const OperandMatcher *>
|
||||
getOptionalOperand(StringRef SymbolicName) const override {
|
||||
assert(!SymbolicName.empty() && "Cannot lookup unnamed operand");
|
||||
return InsnMatcher->getOptionalOperand(SymbolicName);
|
||||
}
|
||||
|
||||
void emitCaptureOpcodes(MatchTable &Table, RuleMatcher &Rule,
|
||||
unsigned InsnID, unsigned OpIdx) const override {
|
||||
unsigned InsnVarID = Rule.defineInsnVar(Table, *InsnMatcher, InsnID, OpIdx);
|
||||
@ -1316,7 +1323,7 @@ public:
|
||||
const StringRef getSymbolicName() const { return SymbolicName; }
|
||||
|
||||
void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override {
|
||||
const OperandMatcher &Operand = Matched.getOperand(SymbolicName);
|
||||
const OperandMatcher &Operand = Rule.getOperandMatcher(SymbolicName);
|
||||
unsigned OldInsnVarID = Rule.getInsnVarID(Operand.getInstructionMatcher());
|
||||
Table << MatchTable::Opcode("GIR_Copy") << MatchTable::Comment("NewInsnID")
|
||||
<< MatchTable::IntValue(NewInsnID) << MatchTable::Comment("OldInsnID")
|
||||
@ -1416,7 +1423,7 @@ public:
|
||||
const StringRef getSymbolicName() const { return SymbolicName; }
|
||||
|
||||
void emitRenderOpcodes(MatchTable &Table, RuleMatcher &Rule) const override {
|
||||
const OperandMatcher &Operand = Matched.getOperand(SymbolicName);
|
||||
const OperandMatcher &Operand = Rule.getOperandMatcher(SymbolicName);
|
||||
unsigned OldInsnVarID = Rule.getInsnVarID(Operand.getInstructionMatcher());
|
||||
Table << MatchTable::Opcode("GIR_CopySubReg")
|
||||
<< MatchTable::Comment("NewInsnID") << MatchTable::IntValue(NewInsnID)
|
||||
@ -1556,13 +1563,13 @@ private:
|
||||
std::vector<std::unique_ptr<OperandRenderer>> OperandRenderers;
|
||||
|
||||
/// True if the instruction can be built solely by mutating the opcode.
|
||||
bool canMutate() const {
|
||||
bool canMutate(RuleMatcher &Rule) const {
|
||||
if (OperandRenderers.size() != Matched.getNumOperands())
|
||||
return false;
|
||||
|
||||
for (const auto &Renderer : enumerate(OperandRenderers)) {
|
||||
if (const auto *Copy = dyn_cast<CopyRenderer>(&*Renderer.value())) {
|
||||
const OperandMatcher &OM = Matched.getOperand(Copy->getSymbolicName());
|
||||
const OperandMatcher &OM = Rule.getOperandMatcher(Copy->getSymbolicName());
|
||||
if (&Matched != &OM.getInstructionMatcher() ||
|
||||
OM.getOperandIndex() != Renderer.index())
|
||||
return false;
|
||||
@ -1587,7 +1594,7 @@ public:
|
||||
|
||||
void emitActionOpcodes(MatchTable &Table, RuleMatcher &Rule,
|
||||
unsigned RecycleInsnID) const override {
|
||||
if (canMutate()) {
|
||||
if (canMutate(Rule)) {
|
||||
Table << MatchTable::Opcode("GIR_MutateOpcode")
|
||||
<< MatchTable::Comment("InsnID") << MatchTable::IntValue(InsnID)
|
||||
<< MatchTable::Comment("RecycleInsnID")
|
||||
@ -1695,7 +1702,7 @@ public:
|
||||
};
|
||||
|
||||
InstructionMatcher &RuleMatcher::addInstructionMatcher(StringRef SymbolicName) {
|
||||
Matchers.emplace_back(new InstructionMatcher(SymbolicName));
|
||||
Matchers.emplace_back(new InstructionMatcher(*this, SymbolicName));
|
||||
return *Matchers.back();
|
||||
}
|
||||
|
||||
@ -1740,6 +1747,17 @@ unsigned RuleMatcher::getInsnVarID(const InstructionMatcher &InsnMatcher) const
|
||||
llvm_unreachable("Matched Insn was not captured in a local variable");
|
||||
}
|
||||
|
||||
void RuleMatcher::defineOperand(StringRef SymbolicName, OperandMatcher &OM) {
|
||||
if (DefinedOperands.find(SymbolicName) == DefinedOperands.end()) {
|
||||
DefinedOperands[SymbolicName] = &OM;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the operand is already defined, then we must ensure both references in
|
||||
// the matcher have the exact same node.
|
||||
OM.addPredicate<SameOperandMatcher>(OM.getSymbolicName());
|
||||
}
|
||||
|
||||
const InstructionMatcher &
|
||||
RuleMatcher::getInstructionMatcher(StringRef SymbolicName) const {
|
||||
for (const auto &I : InsnVariableIDs)
|
||||
@ -1749,6 +1767,16 @@ RuleMatcher::getInstructionMatcher(StringRef SymbolicName) const {
|
||||
("Failed to lookup instruction " + SymbolicName).str().c_str());
|
||||
}
|
||||
|
||||
const OperandMatcher &
|
||||
RuleMatcher::getOperandMatcher(StringRef Name) const {
|
||||
const auto &I = DefinedOperands.find(Name);
|
||||
|
||||
if (I == DefinedOperands.end())
|
||||
PrintFatalError(SrcLoc, "Operand " + Name + " was not declared in matcher");
|
||||
|
||||
return *I->second;
|
||||
}
|
||||
|
||||
/// Emit MatchTable opcodes to check the shape of the match and capture
|
||||
/// instructions into local variables.
|
||||
void RuleMatcher::emitCaptureOpcodes(MatchTable &Table) {
|
||||
@ -1908,6 +1936,23 @@ bool OperandPredicateMatcher::isHigherPriorityThan(
|
||||
return Kind < B.Kind;
|
||||
}
|
||||
|
||||
void SameOperandMatcher::emitPredicateOpcodes(MatchTable &Table,
|
||||
RuleMatcher &Rule,
|
||||
unsigned InsnVarID,
|
||||
unsigned OpIdx) const {
|
||||
const OperandMatcher &OtherOM = Rule.getOperandMatcher(TiedTo);
|
||||
unsigned OtherInsnVarID = Rule.getInsnVarID(OtherOM.getInstructionMatcher());
|
||||
|
||||
Table << MatchTable::Opcode("GIM_CheckIsSameOperand")
|
||||
<< MatchTable::Comment("MI") << MatchTable::IntValue(InsnVarID)
|
||||
<< MatchTable::Comment("OpIdx") << MatchTable::IntValue(OpIdx)
|
||||
<< MatchTable::Comment("OtherMI")
|
||||
<< MatchTable::IntValue(OtherInsnVarID)
|
||||
<< MatchTable::Comment("OtherOpIdx")
|
||||
<< MatchTable::IntValue(OtherOM.getOperandIndex())
|
||||
<< MatchTable::LineBreak;
|
||||
}
|
||||
|
||||
//===- GlobalISelEmitter class --------------------------------------------===//
|
||||
|
||||
class GlobalISelEmitter {
|
||||
@ -1947,7 +1992,8 @@ private:
|
||||
Expected<BuildMIAction &>
|
||||
createAndImportInstructionRenderer(RuleMatcher &M, const TreePatternNode *Dst,
|
||||
const InstructionMatcher &InsnMatcher);
|
||||
Error importExplicitUseRenderer(BuildMIAction &DstMIBuilder,
|
||||
Error importExplicitUseRenderer(RuleMatcher &Rule,
|
||||
BuildMIAction &DstMIBuilder,
|
||||
TreePatternNode *DstChild,
|
||||
const InstructionMatcher &InsnMatcher) const;
|
||||
Error importDefaultOperandRenderers(BuildMIAction &DstMIBuilder,
|
||||
@ -2065,7 +2111,8 @@ GlobalISelEmitter::createAndImportSelDAGMatcher(InstructionMatcher &InsnMatcher,
|
||||
if (Src->isLeaf()) {
|
||||
Init *SrcInit = Src->getLeafValue();
|
||||
if (IntInit *SrcIntInit = dyn_cast<IntInit>(SrcInit)) {
|
||||
OperandMatcher &OM = InsnMatcher.addOperand(OpIdx++, "", TempOpIdx);
|
||||
OperandMatcher &OM =
|
||||
InsnMatcher.addOperand(OpIdx++, Src->getName(), TempOpIdx);
|
||||
OM.addPredicate<LiteralIntOperandMatcher>(SrcIntInit->getValue());
|
||||
} else
|
||||
return failedImport(
|
||||
@ -2116,6 +2163,8 @@ Error GlobalISelEmitter::importChildMatcher(InstructionMatcher &InsnMatcher,
|
||||
unsigned &TempOpIdx) const {
|
||||
OperandMatcher &OM =
|
||||
InsnMatcher.addOperand(OpIdx, SrcChild->getName(), TempOpIdx);
|
||||
if (OM.isSameAsAnotherOperand())
|
||||
return Error::success();
|
||||
|
||||
ArrayRef<TypeSetByHwMode> ChildTypes = SrcChild->getExtTypes();
|
||||
if (ChildTypes.size() != 1)
|
||||
@ -2141,9 +2190,17 @@ Error GlobalISelEmitter::importChildMatcher(InstructionMatcher &InsnMatcher,
|
||||
|
||||
// Check for nested instructions.
|
||||
if (!SrcChild->isLeaf()) {
|
||||
auto MaybeInsnOperand = OM.addPredicate<InstructionOperandMatcher>(
|
||||
InsnMatcher.getRuleMatcher(), SrcChild->getName());
|
||||
if (!MaybeInsnOperand.hasValue()) {
|
||||
// This isn't strictly true. If the user were to provide exactly the same
|
||||
// matchers as the original operand then we could allow it. However, it's
|
||||
// simpler to not permit the redundant specification.
|
||||
return failedImport("Nested instruction cannot be the same as another operand");
|
||||
}
|
||||
|
||||
// Map the node to a gMIR instruction.
|
||||
InstructionOperandMatcher &InsnOperand =
|
||||
OM.addPredicate<InstructionOperandMatcher>(SrcChild->getName());
|
||||
InstructionOperandMatcher &InsnOperand = **MaybeInsnOperand;
|
||||
auto InsnMatcherOrError = createAndImportSelDAGMatcher(
|
||||
InsnOperand.getInsnMatcher(), SrcChild, TempOpIdx);
|
||||
if (auto Error = InsnMatcherOrError.takeError())
|
||||
@ -2203,7 +2260,7 @@ Error GlobalISelEmitter::importChildMatcher(InstructionMatcher &InsnMatcher,
|
||||
}
|
||||
|
||||
Error GlobalISelEmitter::importExplicitUseRenderer(
|
||||
BuildMIAction &DstMIBuilder, TreePatternNode *DstChild,
|
||||
RuleMatcher &Rule, BuildMIAction &DstMIBuilder, TreePatternNode *DstChild,
|
||||
const InstructionMatcher &InsnMatcher) const {
|
||||
if (DstChild->getTransformFn() != nullptr) {
|
||||
return failedImport("Dst pattern child has transform fn " +
|
||||
@ -2272,7 +2329,7 @@ Error GlobalISelEmitter::importExplicitUseRenderer(
|
||||
return failedImport(
|
||||
"SelectionDAG ComplexPattern not mapped to GlobalISel");
|
||||
|
||||
const OperandMatcher &OM = InsnMatcher.getOperand(DstChild->getName());
|
||||
const OperandMatcher &OM = Rule.getOperandMatcher(DstChild->getName());
|
||||
DstMIBuilder.addRenderer<RenderComplexPatternOperand>(
|
||||
0, *ComplexPattern->second, DstChild->getName(),
|
||||
OM.getAllocatedTemporariesBaseID());
|
||||
@ -2373,7 +2430,7 @@ Expected<BuildMIAction &> GlobalISelEmitter::createAndImportInstructionRenderer(
|
||||
}
|
||||
|
||||
if (auto Error = importExplicitUseRenderer(
|
||||
DstMIBuilder, Dst->getChild(Child), InsnMatcher))
|
||||
M, DstMIBuilder, Dst->getChild(Child), InsnMatcher))
|
||||
return std::move(Error);
|
||||
++Child;
|
||||
}
|
||||
@ -2426,7 +2483,7 @@ Error GlobalISelEmitter::importImplicitDefRenderers(
|
||||
|
||||
Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
|
||||
// Keep track of the matchers and actions to emit.
|
||||
RuleMatcher M;
|
||||
RuleMatcher M(P.getSrcRecord()->getLoc());
|
||||
M.addAction<DebugCommentAction>(P);
|
||||
|
||||
if (auto Error = importRulePredicates(M, P.getPredicates()))
|
||||
@ -2465,6 +2522,7 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
|
||||
|
||||
OperandMatcher &OM0 = InsnMatcher.getOperand(0);
|
||||
OM0.setSymbolicName(DstIOperand.Name);
|
||||
M.defineOperand(OM0.getSymbolicName(), OM0);
|
||||
OM0.addPredicate<RegisterBankOperandMatcher>(RC);
|
||||
|
||||
auto &DstMIBuilder = M.addAction<BuildMIAction>(0, &DstI, InsnMatcher);
|
||||
@ -2525,6 +2583,7 @@ Expected<RuleMatcher> GlobalISelEmitter::runOnPattern(const PatternToMatch &P) {
|
||||
|
||||
OperandMatcher &OM = InsnMatcher.getOperand(OpIdx);
|
||||
OM.setSymbolicName(DstIOperand.Name);
|
||||
M.defineOperand(OM.getSymbolicName(), OM);
|
||||
OM.addPredicate<RegisterBankOperandMatcher>(
|
||||
Target.getRegisterClass(DstIOpRec));
|
||||
++OpIdx;
|
||||
|
Loading…
Reference in New Issue
Block a user