Index: llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h =================================================================== --- llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -112,6 +112,14 @@ /// - 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 Index: llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h =================================================================== --- llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h +++ llvm/include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h @@ -154,24 +154,31 @@ 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++]; Index: llvm/test/CodeGen/AMDGPU/GlobalISel/andn2.ll =================================================================== --- llvm/test/CodeGen/AMDGPU/GlobalISel/andn2.ll +++ llvm/test/CodeGen/AMDGPU/GlobalISel/andn2.ll @@ -346,8 +346,7 @@ ; ; 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, %and = and <2 x i16> %src0, %not.src1 @@ -371,8 +370,7 @@ ; ; 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, %and = and <2 x i16> %not.src1, %src0 @@ -397,7 +395,7 @@ ; 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, %and = and <2 x i16> %src0, %not.src1 @@ -429,9 +427,8 @@ ; ; 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, %and0 = and <2 x i16> %src0, %not.src2 Index: llvm/test/CodeGen/AMDGPU/GlobalISel/orn2.ll =================================================================== --- llvm/test/CodeGen/AMDGPU/GlobalISel/orn2.ll +++ llvm/test/CodeGen/AMDGPU/GlobalISel/orn2.ll @@ -346,8 +346,7 @@ ; ; 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, %or = or <2 x i16> %src0, %not.src1 @@ -371,8 +370,7 @@ ; ; 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, %or = or <2 x i16> %not.src1, %src0 @@ -397,7 +395,7 @@ ; 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, %or = or <2 x i16> %src0, %not.src1 @@ -429,9 +427,8 @@ ; ; 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, %or0 = or <2 x i16> %src0, %not.src2 Index: llvm/test/TableGen/GlobalISelEmitter-immAllZeroOne.td =================================================================== --- llvm/test/TableGen/GlobalISelEmitter-immAllZeroOne.td +++ llvm/test/TableGen/GlobalISelEmitter-immAllZeroOne.td @@ -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 @@ // 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 @@ // 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, Index: llvm/utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- llvm/utils/TableGen/GlobalISelEmitter.cpp +++ llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -1671,10 +1671,23 @@ /// 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 Insts; static DenseMap 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 @@ OpcodeValues[I] = OpcodeValue++; } - InstructionOpcodeMatcher(unsigned InsnVarID, const CodeGenInstruction *I) - : InstructionPredicateMatcher(IPM_Opcode, InsnVarID), I(I) {} + InstructionOpcodeMatcher(unsigned InsnVarID, + ArrayRef 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 @@ bool isIdentical(const PredicateMatcher &B) const override { return InstructionPredicateMatcher::isIdentical(B) && - I == cast(&B)->I; + Insts == cast(&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 @@ // using instruction frequency information to improve compile time. if (const InstructionOpcodeMatcher *BO = dyn_cast(&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"; + } + + // The first opcode is the canonical opcode, and later are alternatives. + StringRef getOpcode() const { + return Insts[0]->TheDef->getName(); + } + + ArrayRef getAlternativeOpcodes() { + return Insts; } - StringRef getOpcode() const { return I->TheDef->getName(); } - bool isVariadicNumOperands() const { return I->Operands.isVariadic; } + 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; } }; @@ -4124,8 +4168,16 @@ 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( - &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