1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-19 02:52:53 +02:00

TableGen/GlobalISel: Allow inst matcher to check multiple opcodes

This is to initially handleg immAllOnesV, which should match
G_BUILD_VECTOR or G_BUILD_VECTOR_TRUNC. In the future, it could be
used for other patterns cases that map to multiple G_* instructions,
such as G_ADD and G_PTR_ADD.
This commit is contained in:
Matt Arsenault 2020-08-02 14:52:20 -04:00 committed by Matt Arsenault
parent fe8d2a2f48
commit e9313e5937
6 changed files with 109 additions and 41 deletions

View File

@ -112,6 +112,14 @@ enum {
/// - InsnID - Instruction ID
/// - Expected opcode
GIM_CheckOpcode,
/// Check the opcode on the specified instruction, checking 2 acceptable
/// alternatives.
/// - InsnID - Instruction ID
/// - Expected opcode
/// - Alternative expected opcode
GIM_CheckOpcodeIsEither,
/// Check the instruction has the right number of operands
/// - InsnID - Instruction ID
/// - Expected number of operands

View File

@ -154,24 +154,31 @@ bool InstructionSelector::executeMatchTable(
break;
}
case GIM_CheckOpcode: {
case GIM_CheckOpcode:
case GIM_CheckOpcodeIsEither: {
int64_t InsnID = MatchTable[CurrentIdx++];
int64_t Expected = MatchTable[CurrentIdx++];
int64_t Expected0 = MatchTable[CurrentIdx++];
int64_t Expected1 = -1;
if (MatcherOpcode == GIM_CheckOpcodeIsEither)
Expected1 = MatchTable[CurrentIdx++];
assert(State.MIs[InsnID] != nullptr && "Used insn before defined");
unsigned Opcode = State.MIs[InsnID]->getOpcode();
DEBUG_WITH_TYPE(TgtInstructionSelector::getName(),
dbgs() << CurrentIdx << ": GIM_CheckOpcode(MIs[" << InsnID
<< "], ExpectedOpcode=" << Expected
<< ") // Got=" << Opcode << "\n");
if (Opcode != Expected) {
dbgs() << CurrentIdx << ": GIM_CheckOpcode(MIs[" << InsnID
<< "], ExpectedOpcode=" << Expected0;
if (MatcherOpcode == GIM_CheckOpcodeIsEither)
dbgs() << " || " << Expected1;
dbgs() << ") // Got=" << Opcode << "\n";
);
if (Opcode != Expected0 && Opcode != Expected1) {
if (handleReject() == RejectAndGiveUp)
return false;
}
break;
}
case GIM_SwitchOpcode: {
int64_t InsnID = MatchTable[CurrentIdx++];
int64_t LowerBound = MatchTable[CurrentIdx++];

View File

@ -346,8 +346,7 @@ define amdgpu_ps i32 @s_andn2_v2i16(<2 x i16> inreg %src0, <2 x i16> inreg %src1
;
; GFX9-LABEL: s_andn2_v2i16:
; GFX9: ; %bb.0:
; GFX9-NEXT: s_xor_b32 s0, s3, -1
; GFX9-NEXT: s_and_b32 s0, s2, s0
; GFX9-NEXT: s_andn2_b32 s0, s2, s3
; GFX9-NEXT: ; return to shader part epilog
%not.src1 = xor <2 x i16> %src1, <i16 -1, i16 -1>
%and = and <2 x i16> %src0, %not.src1
@ -371,8 +370,7 @@ define amdgpu_ps i32 @s_andn2_v2i16_commute(<2 x i16> inreg %src0, <2 x i16> inr
;
; GFX9-LABEL: s_andn2_v2i16_commute:
; GFX9: ; %bb.0:
; GFX9-NEXT: s_xor_b32 s0, s3, -1
; GFX9-NEXT: s_and_b32 s0, s0, s2
; GFX9-NEXT: s_andn2_b32 s0, s2, s3
; GFX9-NEXT: ; return to shader part epilog
%not.src1 = xor <2 x i16> %src1, <i16 -1, i16 -1>
%and = and <2 x i16> %not.src1, %src0
@ -397,7 +395,7 @@ define amdgpu_ps { i32, i32 } @s_andn2_v2i16_multi_use(<2 x i16> inreg %src0, <2
; GFX9-LABEL: s_andn2_v2i16_multi_use:
; GFX9: ; %bb.0:
; GFX9-NEXT: s_xor_b32 s1, s3, -1
; GFX9-NEXT: s_and_b32 s0, s2, s1
; GFX9-NEXT: s_andn2_b32 s0, s2, s3
; GFX9-NEXT: ; return to shader part epilog
%not.src1 = xor <2 x i16> %src1, <i16 -1, i16 -1>
%and = and <2 x i16> %src0, %not.src1
@ -429,9 +427,8 @@ define amdgpu_ps { i32, i32 } @s_andn2_v2i16_multi_foldable_use(<2 x i16> inreg
;
; GFX9-LABEL: s_andn2_v2i16_multi_foldable_use:
; GFX9: ; %bb.0:
; GFX9-NEXT: s_xor_b32 s1, s4, -1
; GFX9-NEXT: s_and_b32 s0, s2, s1
; GFX9-NEXT: s_and_b32 s1, s3, s1
; GFX9-NEXT: s_andn2_b32 s0, s2, s4
; GFX9-NEXT: s_andn2_b32 s1, s3, s4
; GFX9-NEXT: ; return to shader part epilog
%not.src2 = xor <2 x i16> %src2, <i16 -1, i16 -1>
%and0 = and <2 x i16> %src0, %not.src2

View File

@ -346,8 +346,7 @@ define amdgpu_ps i32 @s_orn2_v2i16(<2 x i16> inreg %src0, <2 x i16> inreg %src1)
;
; GFX9-LABEL: s_orn2_v2i16:
; GFX9: ; %bb.0:
; GFX9-NEXT: s_xor_b32 s0, s3, -1
; GFX9-NEXT: s_or_b32 s0, s2, s0
; GFX9-NEXT: s_orn2_b32 s0, s2, s3
; GFX9-NEXT: ; return to shader part epilog
%not.src1 = xor <2 x i16> %src1, <i16 -1, i16 -1>
%or = or <2 x i16> %src0, %not.src1
@ -371,8 +370,7 @@ define amdgpu_ps i32 @s_orn2_v2i16_commute(<2 x i16> inreg %src0, <2 x i16> inre
;
; GFX9-LABEL: s_orn2_v2i16_commute:
; GFX9: ; %bb.0:
; GFX9-NEXT: s_xor_b32 s0, s3, -1
; GFX9-NEXT: s_or_b32 s0, s0, s2
; GFX9-NEXT: s_orn2_b32 s0, s2, s3
; GFX9-NEXT: ; return to shader part epilog
%not.src1 = xor <2 x i16> %src1, <i16 -1, i16 -1>
%or = or <2 x i16> %not.src1, %src0
@ -397,7 +395,7 @@ define amdgpu_ps { i32, i32 } @s_orn2_v2i16_multi_use(<2 x i16> inreg %src0, <2
; GFX9-LABEL: s_orn2_v2i16_multi_use:
; GFX9: ; %bb.0:
; GFX9-NEXT: s_xor_b32 s1, s3, -1
; GFX9-NEXT: s_or_b32 s0, s2, s1
; GFX9-NEXT: s_orn2_b32 s0, s2, s3
; GFX9-NEXT: ; return to shader part epilog
%not.src1 = xor <2 x i16> %src1, <i16 -1, i16 -1>
%or = or <2 x i16> %src0, %not.src1
@ -429,9 +427,8 @@ define amdgpu_ps { i32, i32 } @s_orn2_v2i16_multi_foldable_use(<2 x i16> inreg %
;
; GFX9-LABEL: s_orn2_v2i16_multi_foldable_use:
; GFX9: ; %bb.0:
; GFX9-NEXT: s_xor_b32 s1, s4, -1
; GFX9-NEXT: s_or_b32 s0, s2, s1
; GFX9-NEXT: s_or_b32 s1, s3, s1
; GFX9-NEXT: s_orn2_b32 s0, s2, s4
; GFX9-NEXT: s_orn2_b32 s1, s3, s4
; GFX9-NEXT: ; return to shader part epilog
%not.src2 = xor <2 x i16> %src2, <i16 -1, i16 -1>
%or0 = or <2 x i16> %src0, %not.src2

View File

@ -4,13 +4,20 @@
include "llvm/Target/Target.td"
include "GlobalISelEmitterCommon.td"
// GISEL-OPT: GIM_SwitchOpcode
// GISEL-OPT-NEXT: /*TargetOpcode::G_SHL*/
// GISEL-OPT-NEXT: /*TargetOpcode::G_LSHR*/
// GISEL-OPT-NEXT: // Label
// GISEL-OPT: GIM_Try,
// GISEL-OPT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_v4s16,
// GISEL-OPT: GIM_CheckOpcode, /*MI*/1, TargetOpcode::G_BUILD_VECTOR,
// GISEL-OPT: GIM_CheckOpcodeIsEither, /*MI*/1, TargetOpcode::G_BUILD_VECTOR, TargetOpcode::G_BUILD_VECTOR_TRUNC,
// GISEL-OPT: GIM_CheckIsBuildVectorAllZeros, /*MI*/1,
// GISEL-OPT: GIM_Try,
// GISEL-OPT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_v4s16,
// GISEL-OPT: GIM_CheckOpcode, /*MI*/1, TargetOpcode::G_BUILD_VECTOR,
// GISEL-OPT: GIM_CheckOpcodeIsEither, /*MI*/1, TargetOpcode::G_BUILD_VECTOR, TargetOpcode::G_BUILD_VECTOR_TRUNC,
// GISEL-OPT: GIM_CheckIsBuildVectorAllOnes, /*MI*/1,
@ -19,7 +26,7 @@ include "GlobalISelEmitterCommon.td"
// GISEL-NOOPT: // MIs[0] Operand 2
// GISEL-NOOPT-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_v4s16,
// GISEL-NOOPT-NEXT: GIM_RecordInsn, /*DefineMI*/1, /*MI*/0, /*OpIdx*/2, // MIs[1]
// GISEL-NOOPT-NEXT: GIM_CheckOpcode, /*MI*/1, TargetOpcode::G_BUILD_VECTOR,
// GISEL-NOOPT-NEXT: GIM_CheckOpcodeIsEither, /*MI*/1, TargetOpcode::G_BUILD_VECTOR, TargetOpcode::G_BUILD_VECTOR_TRUNC,
// GISEL-NOOPT-NEXT: GIM_CheckIsBuildVectorAllOnes, /*MI*/1,
// GISEL-NOOPT-NEXT: // MIs[1] Operand 0
// GISEL-NOOPT-NEXT: GIM_CheckType, /*MI*/1, /*Op*/0, /*Type*/GILLT_v4s16,
@ -34,7 +41,7 @@ def VFOOONES : I<(outs VecReg128:$dst), (ins VecReg128:$src0),
// GISEL-NOOPT: // MIs[0] Operand 2
// GISEL-NOOPT-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_v4s16,
// GISEL-NOOPT-NEXT: GIM_RecordInsn, /*DefineMI*/1, /*MI*/0, /*OpIdx*/2, // MIs[1]
// GISEL-NOOPT-NEXT: GIM_CheckOpcode, /*MI*/1, TargetOpcode::G_BUILD_VECTOR,
// GISEL-NOOPT-NEXT: GIM_CheckOpcodeIsEither, /*MI*/1, TargetOpcode::G_BUILD_VECTOR, TargetOpcode::G_BUILD_VECTOR_TRUNC,
// GISEL-NOOPT-NEXT: GIM_CheckIsBuildVectorAllZeros, /*MI*/1,
// GISEL-NOOPT-NEXT: // MIs[1] Operand 0
// GISEL-NOOPT-NEXT: GIM_CheckType, /*MI*/1, /*Op*/0, /*Type*/GILLT_v4s16,

View File

@ -1671,10 +1671,23 @@ PredicateListMatcher<PredicateMatcher>::getNoPredicateComment() const {
/// Generates code to check the opcode of an instruction.
class InstructionOpcodeMatcher : public InstructionPredicateMatcher {
protected:
const CodeGenInstruction *I;
// Allow matching one to several, similar opcodes that share properties. This
// is to handle patterns where one SelectionDAG operation maps to multiple
// GlobalISel ones (e.g. G_BUILD_VECTOR and G_BUILD_VECTOR_TRUNC). The first
// is treated as the canonical opcode.
SmallVector<const CodeGenInstruction *, 2> Insts;
static DenseMap<const CodeGenInstruction *, unsigned> OpcodeValues;
MatchTableRecord getInstValue(const CodeGenInstruction *I) const {
const auto VI = OpcodeValues.find(I);
if (VI != OpcodeValues.end())
return MatchTable::NamedValue(I->Namespace, I->TheDef->getName(),
VI->second);
return MatchTable::NamedValue(I->Namespace, I->TheDef->getName());
}
public:
static void initOpcodeValuesMap(const CodeGenTarget &Target) {
OpcodeValues.clear();
@ -1684,8 +1697,13 @@ public:
OpcodeValues[I] = OpcodeValue++;
}
InstructionOpcodeMatcher(unsigned InsnVarID, const CodeGenInstruction *I)
: InstructionPredicateMatcher(IPM_Opcode, InsnVarID), I(I) {}
InstructionOpcodeMatcher(unsigned InsnVarID,
ArrayRef<const CodeGenInstruction *> I)
: InstructionPredicateMatcher(IPM_Opcode, InsnVarID),
Insts(I.begin(), I.end()) {
assert((Insts.size() == 1 || Insts.size() == 2) &&
"unexpected number of opcode alternatives");
}
static bool classof(const PredicateMatcher *P) {
return P->getKind() == IPM_Opcode;
@ -1693,22 +1711,36 @@ public:
bool isIdentical(const PredicateMatcher &B) const override {
return InstructionPredicateMatcher::isIdentical(B) &&
I == cast<InstructionOpcodeMatcher>(&B)->I;
Insts == cast<InstructionOpcodeMatcher>(&B)->Insts;
}
bool hasValue() const override {
return Insts.size() == 1 && OpcodeValues.count(Insts[0]);
}
// TODO: This is used for the SwitchMatcher optimization. We should be able to
// return a list of the opcodes to match.
MatchTableRecord getValue() const override {
assert(Insts.size() == 1);
const CodeGenInstruction *I = Insts[0];
const auto VI = OpcodeValues.find(I);
if (VI != OpcodeValues.end())
return MatchTable::NamedValue(I->Namespace, I->TheDef->getName(),
VI->second);
return MatchTable::NamedValue(I->Namespace, I->TheDef->getName());
}
bool hasValue() const override { return OpcodeValues.count(I); }
void emitPredicateOpcodes(MatchTable &Table,
RuleMatcher &Rule) const override {
Table << MatchTable::Opcode("GIM_CheckOpcode") << MatchTable::Comment("MI")
<< MatchTable::IntValue(InsnVarID) << getValue()
<< MatchTable::LineBreak;
StringRef CheckType = Insts.size() == 1 ?
"GIM_CheckOpcode" : "GIM_CheckOpcodeIsEither";
Table << MatchTable::Opcode(CheckType) << MatchTable::Comment("MI")
<< MatchTable::IntValue(InsnVarID);
for (const CodeGenInstruction *I : Insts)
Table << getInstValue(I);
Table << MatchTable::LineBreak;
}
/// Compare the priority of this object and B.
@ -1726,20 +1758,32 @@ public:
// using instruction frequency information to improve compile time.
if (const InstructionOpcodeMatcher *BO =
dyn_cast<InstructionOpcodeMatcher>(&B))
return I->TheDef->getName() < BO->I->TheDef->getName();
return Insts[0]->TheDef->getName() < BO->Insts[0]->TheDef->getName();
return false;
};
bool isConstantInstruction() const {
return I->TheDef->getName() == "G_CONSTANT";
return Insts.size() == 1 && Insts[0]->TheDef->getName() == "G_CONSTANT";
}
StringRef getOpcode() const { return I->TheDef->getName(); }
bool isVariadicNumOperands() const { return I->Operands.isVariadic; }
// The first opcode is the canonical opcode, and later are alternatives.
StringRef getOpcode() const {
return Insts[0]->TheDef->getName();
}
ArrayRef<const CodeGenInstruction *> getAlternativeOpcodes() {
return Insts;
}
bool isVariadicNumOperands() const {
// If one is variadic, they all should be.
return Insts[0]->Operands.isVariadic;
}
StringRef getOperandType(unsigned OpIdx) const {
return I->Operands[OpIdx].OperandType;
// Types expected to be uniform for all alternatives.
return Insts[0]->Operands[OpIdx].OperandType;
}
};
@ -4141,8 +4185,16 @@ Error GlobalISelEmitter::importChildMatcher(
InstructionOperandMatcher &InsnOperand = **MaybeInsnOperand;
ValueTypeByHwMode VTy = ChildTypes.front().getValueTypeByHwMode();
const CodeGenInstruction &BuildVector
= Target.getInstruction(RK.getDef("G_BUILD_VECTOR"));
const CodeGenInstruction &BuildVectorTrunc
= Target.getInstruction(RK.getDef("G_BUILD_VECTOR_TRUNC"));
// Treat G_BUILD_VECTOR as the canonical opcode, and G_BUILD_VECTOR_TRUNC
// as an alternative.
InsnOperand.getInsnMatcher().addPredicate<InstructionOpcodeMatcher>(
&Target.getInstruction(RK.getDef("G_BUILD_VECTOR")));
makeArrayRef({&BuildVector, &BuildVectorTrunc}));
// TODO: Handle both G_BUILD_VECTOR and G_BUILD_VECTOR_TRUNC We could
// theoretically not emit any opcode check, but getOpcodeMatcher currently