Index: include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h =================================================================== --- include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h +++ include/llvm/CodeGen/GlobalISel/InstructionSelectorImpl.h @@ -792,11 +792,13 @@ case GIR_AddRegister: { int64_t InsnID = MatchTable[CurrentIdx++]; int64_t RegNum = MatchTable[CurrentIdx++]; + uint64_t RegFlags = MatchTable[CurrentIdx++]; assert(OutMIs[InsnID] && "Attempted to add to undefined instruction"); - OutMIs[InsnID].addReg(RegNum); - DEBUG_WITH_TYPE(TgtInstructionSelector::getName(), - dbgs() << CurrentIdx << ": GIR_AddRegister(OutMIs[" - << InsnID << "], " << RegNum << ")\n"); + OutMIs[InsnID].addReg(RegNum, RegFlags); + DEBUG_WITH_TYPE( + TgtInstructionSelector::getName(), + dbgs() << CurrentIdx << ": GIR_AddRegister(OutMIs[" + << InsnID << "], " << RegNum << ", " << RegFlags << ")\n"); break; } Index: lib/Target/AMDGPU/AMDGPURegisterBankInfo.cpp =================================================================== --- lib/Target/AMDGPU/AMDGPURegisterBankInfo.cpp +++ lib/Target/AMDGPU/AMDGPURegisterBankInfo.cpp @@ -2166,6 +2166,7 @@ // This must be an SGPR, but accept a VGPR. unsigned Bank = getRegBankID(MI.getOperand(2).getReg(), MRI, *TRI, AMDGPU::SGPRRegBankID); + OpdsMapping[1] = AMDGPU::getValueMapping(Bank, 32); OpdsMapping[2] = AMDGPU::getValueMapping(Bank, 32); break; } Index: test/CodeGen/AMDGPU/GlobalISel/inst-select-amdgcn.s.sendmsg.mir =================================================================== --- /dev/null +++ test/CodeGen/AMDGPU/GlobalISel/inst-select-amdgcn.s.sendmsg.mir @@ -0,0 +1,25 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -march=amdgcn -run-pass=instruction-select -verify-machineinstrs %s -o - | FileCheck %s -check-prefix=GCN + +--- +name: test_sendmsg +legalized: true +regBankSelected: true +tracksRegLiveness: true + +body: | + bb.0: + liveins: $sgpr0 + + ; GCN-LABEL: name: test_sendmsg + ; GCN: liveins: $sgpr0 + ; GCN: [[COPY:%[0-9]+]]:sreg_32_xm0 = COPY $sgpr0 + ; GCN: $m0 = COPY [[COPY]] + ; GCN: S_SENDMSG 1, implicit $exec, implicit $m0 + ; GCN: S_ENDPGM 0 + %0:sgpr(s32) = COPY $sgpr0 + %2:sgpr(s32) = G_CONSTANT i32 1 + G_INTRINSIC_W_SIDE_EFFECTS intrinsic(@llvm.amdgcn.s.sendmsg), %2(s32), %0(s32) + S_ENDPGM 0 + +... Index: test/TableGen/gisel-physreg-input.td =================================================================== --- /dev/null +++ test/TableGen/gisel-physreg-input.td @@ -0,0 +1,82 @@ +// RUN: llvm-tblgen -gen-global-isel -optimize-match-table=false -I %p/../../include %s -o - < %s | FileCheck -check-prefix=GISEL %s + +include "llvm/Target/Target.td" + +def TestTargetInstrInfo : InstrInfo; + +def TestTarget : Target { + let InstructionSet = TestTargetInstrInfo; +} + +def R0 : Register<"r0"> { let Namespace = "MyTarget"; } +def SPECIAL : Register<"special"> { let Namespace = "MyTarget"; } +def GPR32 : RegisterClass<"MyTarget", [i32], 32, (add R0)>; +def Special32 : RegisterClass<"MyTarget", [i32], 32, (add SPECIAL)>; + + +class I Pat> + : Instruction { + let Namespace = "MyTarget"; + let OutOperandList = OOps; + let InOperandList = IOps; + let Pattern = Pat; +} + +// Try a normal physical register use. + +// GISEL: GIM_Try, +// GISEL-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3, +// GISEL-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_ADD, +// GISEL-NEXT: // MIs[0] dst +// GISEL-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32, +// GISEL-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID, +// GISEL-NEXT: // MIs[0] src0 +// GISEL-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32, +// GISEL-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID, +// GISEL-NEXT: // MIs[0] SPECIAL +// GISEL-NEXT: GIM_CheckType, /*MI*/0, /*Op*/2, /*Type*/GILLT_s32, +// GISEL-NEXT: // (add:{ *:[i32] } GPR32:{ *:[i32] }:$src0, SPECIAL:{ *:[i32] }) => (ADD_PHYS:{ *:[i32] } GPR32:{ *:[i32] }:$src0) +// GISEL-NEXT: GIR_BuildMI, /*InsnID*/1, /*Opcode*/TargetOpcode::COPY, +// GISEL-NEXT: GIR_AddRegister, /*InsnID*/1, MyTarget::SPECIAL, /*AddRegisterRegFlags*/RegState::Define, +// GISEL-NEXT: GIR_Copy, /*NewInsnID*/1, /*OldInsnID*/0, /*OpIdx*/2, // SPECIAL +// GISEL-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::ADD_PHYS, +// GISEL-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst +// GISEL-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // src0 +// GISEL-NEXT: GIR_EraseFromParent, /*InsnID*/0, +// GISEL-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, +def ADD_PHYS : I<(outs GPR32:$dst), (ins GPR32:$src0), + [(set GPR32:$dst, (add GPR32:$src0, SPECIAL))]> { + let Uses = [SPECIAL]; +} + +// Try using the name of the physreg in another operand FIXME: Does +// this make any sense, or should it be an error? It ends up requiring +// the two be the same operand. + +// GISEL: GIM_Try, +// GISEL-NEXT: GIM_CheckNumOperands, /*MI*/0, /*Expected*/3, +// GISEL-NEXT: GIM_CheckOpcode, /*MI*/0, TargetOpcode::G_MUL, +// GISEL-NEXT: // MIs[0] dst +// GISEL-NEXT: GIM_CheckType, /*MI*/0, /*Op*/0, /*Type*/GILLT_s32, +// GISEL-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/0, /*RC*/MyTarget::GPR32RegClassID, +// GISEL-NEXT: // MIs[0] SPECIAL +// GISEL-NEXT: GIM_CheckType, /*MI*/0, /*Op*/1, /*Type*/GILLT_s32, +// GISEL-NEXT: GIM_CheckRegBankForClass, /*MI*/0, /*Op*/1, /*RC*/MyTarget::GPR32RegClassID, +// GISEL-NEXT: // MIs[0] SPECIAL +// GISEL-NEXT: GIM_CheckIsSameOperand, /*MI*/0, /*OpIdx*/2, /*OtherMI*/0, /*OtherOpIdx*/1, +// GISEL-NEXT: // (mul:{ *:[i32] } GPR32:{ *:[i32] }:$SPECIAL, SPECIAL:{ *:[i32] }) => (MUL_PHYS:{ *:[i32] } GPR32:{ *:[i32] }:$SPECIAL) +// GISEL-NEXT: GIR_BuildMI, /*InsnID*/0, /*Opcode*/MyTarget::MUL_PHYS, +// GISEL-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/0, // dst +// GISEL-NEXT: GIR_Copy, /*NewInsnID*/0, /*OldInsnID*/0, /*OpIdx*/1, // SPECIAL +// GISEL-NEXT: GIR_EraseFromParent, /*InsnID*/0, +// GISEL-NEXT: GIR_ConstrainSelectedInstOperands, /*InsnID*/0, +def MUL_PHYS : I<(outs GPR32:$dst), (ins GPR32:$SPECIAL), + [(set GPR32:$dst, (mul GPR32:$SPECIAL, SPECIAL))]> { + let Uses = [SPECIAL]; +} + +// Try giving the physical operand a name +// def ADD_PHYS : I<(outs GPR32:$dst), (ins GPR32:$src0), +// [(set GPR32:$dst, (add GPR32:$src0, SPECIAL:$special))]> { +// let Uses = [SPECIAL]; +// } Index: utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- utils/TableGen/GlobalISelEmitter.cpp +++ utils/TableGen/GlobalISelEmitter.cpp @@ -1945,6 +1945,11 @@ std::string SymbolicName; unsigned InsnVarID; + /// PhysRegInputs - List list has an entry for each explicitly specified + /// physreg input to the pattern. The first elt is the Register node, the + /// second is the recorded slot number the input pattern match saved it in. + SmallVector, 2> PhysRegInputs; + public: InstructionMatcher(RuleMatcher &Rule, StringRef SymbolicName) : Rule(Rule), SymbolicName(SymbolicName) { @@ -1986,6 +1991,14 @@ llvm_unreachable("Failed to lookup operand"); } + void addPhysRegInput(Record *Reg, unsigned OpNo) { + PhysRegInputs.emplace_back(Reg, OpNo); + } + + ArrayRef> getPhysRegInputs() const { + return PhysRegInputs; + } + StringRef getSymbolicName() const { return SymbolicName; } unsigned getNumOperands() const { return Operands.size(); } OperandVec::iterator operands_begin() { return Operands.begin(); } @@ -2393,11 +2406,13 @@ protected: unsigned InsnID; const Record *RegisterDef; + bool IsDef; public: - AddRegisterRenderer(unsigned InsnID, const Record *RegisterDef) - : OperandRenderer(OR_Register), InsnID(InsnID), RegisterDef(RegisterDef) { - } + AddRegisterRenderer(unsigned InsnID, const Record *RegisterDef, + bool IsDef = false) + : OperandRenderer(OR_Register), InsnID(InsnID), RegisterDef(RegisterDef), + IsDef(IsDef) {} static bool classof(const OperandRenderer *R) { return R->getKind() == OR_Register; @@ -2411,7 +2426,16 @@ ? RegisterDef->getValueAsString("Namespace") : ""), RegisterDef->getName()) - << MatchTable::LineBreak; + << MatchTable::Comment("AddRegisterRegFlags"); + + // TODO: This is encoded as a 64-bit element, but only 16 or 32-bits are + // really needed for a physical register reference. We can pack the + // register and flags in a single field. + if (IsDef) + Table << MatchTable::NamedValue("RegState::Define"); + else + Table << MatchTable::IntValue(0); + Table << MatchTable::LineBreak; } }; @@ -3079,9 +3103,9 @@ bool OperandIsAPointer, unsigned OpIdx, unsigned &TempOpIdx); - Expected - createAndImportInstructionRenderer(RuleMatcher &M, - const TreePatternNode *Dst); + Expected createAndImportInstructionRenderer( + RuleMatcher &M, InstructionMatcher &InsnMatcher, + const TreePatternNode *Src, const TreePatternNode *Dst); Expected createAndImportSubInstructionRenderer( action_iterator InsertPt, RuleMatcher &M, const TreePatternNode *Dst, unsigned TempReg); @@ -3089,6 +3113,7 @@ createInstructionRenderer(action_iterator InsertPt, RuleMatcher &M, const TreePatternNode *Dst); void importExplicitDefRenderers(BuildMIAction &DstMIBuilder); + Expected importExplicitUseRenderers(action_iterator InsertPt, RuleMatcher &M, BuildMIAction &DstMIBuilder, @@ -3473,14 +3498,29 @@ return Error::success(); } +// Get the name to use for a pattern operand. For an anonymous physical register +// input, this should use the register name. +static StringRef getSrcChildName(const TreePatternNode *SrcChild) { + StringRef SrcChildName = SrcChild->getName(); + if (SrcChildName.empty() && SrcChild->isLeaf()) { + if (auto *ChildDefInit = dyn_cast(SrcChild->getLeafValue())) { + auto *ChildRec = ChildDefInit->getDef(); + if (ChildRec->isSubClassOf("Register")) + SrcChildName = ChildRec->getName(); + } + } + + return SrcChildName; +} + Error GlobalISelEmitter::importChildMatcher(RuleMatcher &Rule, InstructionMatcher &InsnMatcher, const TreePatternNode *SrcChild, bool OperandIsAPointer, unsigned OpIdx, unsigned &TempOpIdx) { - OperandMatcher &OM = - InsnMatcher.addOperand(OpIdx, SrcChild->getName(), TempOpIdx); + StringRef SrcChildName = getSrcChildName(SrcChild); + OperandMatcher &OM = InsnMatcher.addOperand(OpIdx, SrcChildName, TempOpIdx); if (OM.isSameAsAnotherOperand()) return Error::success(); @@ -3569,6 +3609,23 @@ return Error::success(); } + if (ChildRec->isSubClassOf("Register")) { + // This just be emitted as a copy to the specific register. + /* + // TODO: Should this check that it's a register, and has the right register bank? + This seems to break wzr on aarch64, and fails to find RC for M0 on AMDGPU. + const CodeGenRegisterClass *RC = CGRegs.getRegClassForRegister(ChildRec); + if (!RC) { + return failedImport( + "Could not determine physical register class of pattern source"); + } + OM.addPredicate(*RC); + */ + + InsnMatcher.addPhysRegInput(ChildRec, OpIdx); + return Error::success(); + } + // Check for ValueType. if (ChildRec->isSubClassOf("ValueType")) { // We already added a type check as standard practice so this doesn't need @@ -3689,6 +3746,8 @@ if (!OpTyOrNone) return failedImport("Dst operand has an unsupported type"); + // FIXME: Is this necessary? Seems to avoid problems with interactions with + // special WZR handling. if (ChildRec->isSubClassOf("Register")) { DstMIBuilder.addRenderer(ChildRec); return InsertPt; @@ -3729,7 +3788,8 @@ } Expected GlobalISelEmitter::createAndImportInstructionRenderer( - RuleMatcher &M, const TreePatternNode *Dst) { + RuleMatcher &M, InstructionMatcher &InsnMatcher, const TreePatternNode *Src, + const TreePatternNode *Dst) { auto InsertPtOrError = createInstructionRenderer(M.actions_end(), M, Dst); if (auto Error = InsertPtOrError.takeError()) return std::move(Error); @@ -3737,6 +3797,19 @@ action_iterator InsertPt = InsertPtOrError.get(); BuildMIAction &DstMIBuilder = *static_cast(InsertPt->get()); + for (auto PhysInput : InsnMatcher.getPhysRegInputs()) { + const CodeGenRegister *Reg = CGRegs.getReg(PhysInput.first); + + InsertPt = M.insertAction( + InsertPt, M.allocateOutputInsnID(), + &Target.getInstruction(RK.getDef("COPY"))); + BuildMIAction &CopyToPhysRegMIBuilder = + *static_cast(InsertPt->get()); + CopyToPhysRegMIBuilder.addRenderer(PhysInput.first, + true); + CopyToPhysRegMIBuilder.addRenderer(Reg->getName()); + } + importExplicitDefRenderers(DstMIBuilder); if (auto Error = importExplicitUseRenderers(InsertPt, M, DstMIBuilder, Dst) @@ -4079,7 +4152,8 @@ ++OpIdx; } - auto DstMIBuilderOrError = createAndImportInstructionRenderer(M, Dst); + auto DstMIBuilderOrError = + createAndImportInstructionRenderer(M, InsnMatcher, Src, Dst); if (auto Error = DstMIBuilderOrError.takeError()) return std::move(Error); BuildMIAction &DstMIBuilder = DstMIBuilderOrError.get();