Index: lib/CodeGen/LiveVariables.cpp =================================================================== --- lib/CodeGen/LiveVariables.cpp +++ lib/CodeGen/LiveVariables.cpp @@ -528,11 +528,6 @@ UseRegs.push_back(MOReg); } else { assert(MO.isDef()); - // FIXME: We should not remove any dead flags. However the MIPS RDDSP - // instruction needs it at the moment: http://llvm.org/PR27116. - if (TargetRegisterInfo::isPhysicalRegister(MOReg) && - !MRI->isReserved(MOReg)) - MO.setIsDead(false); DefRegs.push_back(MOReg); } } Index: lib/Target/Mips/MipsRegisterInfo.h =================================================================== --- lib/Target/Mips/MipsRegisterInfo.h +++ lib/Target/Mips/MipsRegisterInfo.h @@ -74,6 +74,12 @@ /// Return GPR register class. virtual const TargetRegisterClass *intRegClass(unsigned Size) const = 0; + /// Checks if a register is part of DSPControl register. + static bool isDSPControlReg(unsigned Reg); + + /// Return DSPOutFlag if a register is a SubReg of it. + static unsigned getDSPOutFlagIfSubreg(unsigned Reg); + private: virtual void eliminateFI(MachineBasicBlock::iterator II, unsigned OpNo, int FrameIndex, uint64_t StackSize, Index: lib/Target/Mips/MipsRegisterInfo.cpp =================================================================== --- lib/Target/Mips/MipsRegisterInfo.cpp +++ lib/Target/Mips/MipsRegisterInfo.cpp @@ -324,3 +324,17 @@ // sized objects. return MF.getRegInfo().canReserveReg(BP); } + +bool MipsRegisterInfo::isDSPControlReg(unsigned Reg) { + return Reg == Mips::DSPPos || Reg == Mips::DSPSCount || + Reg == Mips::DSPCarry || Reg == Mips::DSPOutFlag || + Reg == Mips::DSPCCond || Reg == Mips::DSPEFI; +} + +unsigned MipsRegisterInfo::getDSPOutFlagIfSubreg(unsigned Reg) { + if (Reg == Mips::DSPOutFlag20 || Reg == Mips::DSPOutFlag21 || + Reg == Mips::DSPOutFlag22 || Reg == Mips::DSPOutFlag23 || + Reg == Mips::DSPOutFlag16_19) + return Mips::DSPOutFlag; + return Reg; +} Index: lib/Target/Mips/MipsSEISelDAGToDAG.h =================================================================== --- lib/Target/Mips/MipsSEISelDAGToDAG.h +++ lib/Target/Mips/MipsSEISelDAGToDAG.h @@ -32,6 +32,27 @@ void addDSPCtrlRegOperands(bool IsDef, MachineInstr &MI, MachineFunction &MF); + /// Instructions using DSPControl register are added as implicit operands and + /// their dead flags need to be handled manualy. Keeps track of operands maked + /// as dead in DeadImplRegs that may need to be removed if a later use is + /// found. + void handleInstrUsingDSPControlReg( + DenseMap &DeadImplRegs, MachineInstr &MI); + + /// Called by 'handleInstrUsingDSPControlReg' when it finds a use of a + /// DSPControl register. If there was an operand which previously used such + /// register it will be in DeadImplRegs. It and it's dead flag will be removed + /// from DeadImplRegs. + void removeOperandFromDeadImplRegs( + DenseMap &DeadImplRegs, unsigned Reg); + + /// Called by 'handleInstrUsingDSPControlReg' when it finds a use of a + /// DSPControl register. If it is also a def then it will be marked dead and + /// saved in DeadImplRegs. + void + markOperandInDeadImplRegs(DenseMap &DeadImplRegs, + MachineOperand &MO); + unsigned getMSACtrlReg(const SDValue RegIdx) const; bool replaceUsesWithZeroReg(MachineRegisterInfo *MRI, const MachineInstr&); Index: lib/Target/Mips/MipsSEISelDAGToDAG.cpp =================================================================== --- lib/Target/Mips/MipsSEISelDAGToDAG.cpp +++ lib/Target/Mips/MipsSEISelDAGToDAG.cpp @@ -52,8 +52,7 @@ MachineFunction &MF) { MachineInstrBuilder MIB(MF, &MI); unsigned Mask = MI.getOperand(1).getImm(); - unsigned Flag = - IsDef ? RegState::ImplicitDefine : RegState::Implicit | RegState::Undef; + unsigned Flag = IsDef ? RegState::ImplicitDefine : RegState::Implicit; if (Mask & 1) MIB.addReg(Mips::DSPPos, Flag); @@ -124,18 +123,57 @@ return true; } +void MipsSEDAGToDAGISel::handleInstrUsingDSPControlReg( + DenseMap &DeadImplRegs, MachineInstr &MI) { + for (MachineOperand &MO : MI.implicit_operands()) { + if (MO.isRegMask() || !MO.isReg()) + continue; + unsigned MOReg = MO.getReg(); + if (!TargetRegisterInfo::isPhysicalRegister(MOReg)) + continue; + MOReg = MipsRegisterInfo::getDSPOutFlagIfSubreg(MOReg); + if (MipsRegisterInfo::isDSPControlReg(MOReg)) { + removeOperandFromDeadImplRegs(DeadImplRegs, MOReg); + markOperandInDeadImplRegs(DeadImplRegs, MO); + } + } +} + +void MipsSEDAGToDAGISel::removeOperandFromDeadImplRegs( + DenseMap &DeadImplRegs, unsigned Reg) { + auto Res = DeadImplRegs.find(Reg); + if (Res != DeadImplRegs.end()) { + if (Res->second->isDead()) + Res->second->setIsDead(false); + DeadImplRegs.erase(Res); + } +} + +void MipsSEDAGToDAGISel::markOperandInDeadImplRegs( + DenseMap &DeadImplRegs, MachineOperand &MO) { + if (!MO.isDef()) + return; + unsigned Reg = MO.getReg(); + Reg = MipsRegisterInfo::getDSPOutFlagIfSubreg(Reg); + MO.setIsDead(); + DeadImplRegs[Reg] = &MO; +} + void MipsSEDAGToDAGISel::processFunctionAfterISel(MachineFunction &MF) { MF.getInfo()->initGlobalBaseReg(); MachineRegisterInfo *MRI = &MF.getRegInfo(); for (auto &MBB: MF) { + DenseMap DeadImplRegs; for (auto &MI: MBB) { switch (MI.getOpcode()) { case Mips::RDDSP: + case Mips::RDDSP_MM: addDSPCtrlRegOperands(false, MI, MF); break; case Mips::WRDSP: + case Mips::WRDSP_MM: addDSPCtrlRegOperands(true, MI, MF); break; case Mips::BuildPairF64_64: @@ -153,6 +191,7 @@ default: replaceUsesWithZeroReg(MRI, MI); } + handleInstrUsingDSPControlReg(DeadImplRegs, MI); } } } Index: test/CodeGen/Mips/dsp-implicit-dead.ll =================================================================== --- /dev/null +++ test/CodeGen/Mips/dsp-implicit-dead.ll @@ -0,0 +1,94 @@ +; NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +; RUN: llc -march=mipsel -mcpu=mips32r2 -mattr=+dspr2 -verify-machineinstrs \ +; RUN: -stop-before=finalize-isel -o - < %s | FileCheck %s + +; Checks that implicit operands that use DSPControl register such as RDDSP and +; WRDSP are not incorrectly marked dead in previous or later instructions. + +declare i32 @llvm.mips.rddsp(i32) nounwind readonly +declare void @llvm.mips.cmpu.le.qb(<4 x i8>, <4 x i8>) nounwind + +define i32 @test__builtin_mips_cmpu_le_qb_before_rddsp(i32 %i0, i32 %a0.coerce, i32 %a1.coerce) nounwind { + ; CHECK-LABEL: name: test__builtin_mips_cmpu_le_qb_before_rddsp + ; CHECK: bb.0.entry: + ; CHECK: liveins: $a1, $a2 + ; CHECK: [[COPY:%[0-9]+]]:gpr32 = COPY $a2 + ; CHECK: [[COPY1:%[0-9]+]]:gpr32 = COPY $a1 + ; CHECK: [[COPY2:%[0-9]+]]:dspr = COPY [[COPY]] + ; CHECK: [[COPY3:%[0-9]+]]:dspr = COPY [[COPY1]] + ; CHECK: CMPU_LE_QB killed [[COPY3]], killed [[COPY2]], implicit-def $dspccond + ; CHECK: [[RDDSP:%[0-9]+]]:gpr32 = RDDSP 16, implicit $dspccond + ; CHECK: $v0 = COPY [[RDDSP]] + ; CHECK: RetRA implicit $v0 +entry: + %0 = bitcast i32 %a0.coerce to <4 x i8> + %1 = bitcast i32 %a1.coerce to <4 x i8> + tail call void @llvm.mips.cmpu.le.qb(<4 x i8> %0, <4 x i8> %1) + %2 = tail call i32 @llvm.mips.rddsp(i32 16) + ret i32 %2 +} + +declare void @llvm.mips.wrdsp(i32, i32) nounwind +declare <4 x i8> @llvm.mips.pick.qb(<4 x i8>, <4 x i8>) nounwind readonly + +define i32 @test__builtin_mips_pick_qb_after_wrdsp(i32 %i0, i32 %a0.coerce, i32 %a1.coerce) nounwind readonly { + ; CHECK-LABEL: name: test__builtin_mips_pick_qb_after_wrdsp + ; CHECK: bb.0.entry: + ; CHECK: liveins: $a0, $a1, $a2 + ; CHECK: [[COPY:%[0-9]+]]:gpr32 = COPY $a2 + ; CHECK: [[COPY1:%[0-9]+]]:gpr32 = COPY $a1 + ; CHECK: [[COPY2:%[0-9]+]]:gpr32 = COPY $a0 + ; CHECK: WRDSP [[COPY2]], 16, implicit-def $dspccond + ; CHECK: [[COPY3:%[0-9]+]]:dspr = COPY [[COPY]] + ; CHECK: [[COPY4:%[0-9]+]]:dspr = COPY [[COPY1]] + ; CHECK: [[PICK_QB:%[0-9]+]]:dspr = PICK_QB killed [[COPY4]], killed [[COPY3]], implicit $dspccond + ; CHECK: [[COPY5:%[0-9]+]]:gpr32 = COPY [[PICK_QB]] + ; CHECK: $v0 = COPY [[COPY5]] + ; CHECK: RetRA implicit $v0 +entry: + %0 = bitcast i32 %a0.coerce to <4 x i8> + %1 = bitcast i32 %a1.coerce to <4 x i8> + tail call void @llvm.mips.wrdsp(i32 %i0, i32 16) + %2 = tail call <4 x i8> @llvm.mips.pick.qb(<4 x i8> %0, <4 x i8> %1) + %3 = bitcast <4 x i8> %2 to i32 + ret i32 %3 +} + +declare i32 @llvm.mips.extpdp(i64, i32) nounwind + +define i32 @test__builtin_mips_extpdp_before_rddsp(i32 %i0, i32, i64 %a0) nounwind { + ; CHECK-LABEL: name: test__builtin_mips_extpdp_before_rddsp + ; CHECK: bb.0.entry: + ; CHECK: liveins: $a2, $a3 + ; CHECK: [[COPY:%[0-9]+]]:gpr32 = COPY $a3 + ; CHECK: [[COPY1:%[0-9]+]]:gpr32 = COPY $a2 + ; CHECK: [[PseudoMTLOHI_DSP:%[0-9]+]]:acc64dsp = PseudoMTLOHI_DSP [[COPY1]], [[COPY]] + ; CHECK: [[EXTPDP:%[0-9]+]]:gpr32 = EXTPDP killed [[PseudoMTLOHI_DSP]], 15, implicit-def $dsppos, implicit-def $dspefi, implicit $dsppos + ; CHECK: [[RDDSP:%[0-9]+]]:gpr32 = RDDSP 33, implicit $dsppos, implicit $dspefi + ; CHECK: [[ADDu:%[0-9]+]]:gpr32 = ADDu killed [[EXTPDP]], killed [[RDDSP]] + ; CHECK: $v0 = COPY [[ADDu]] + ; CHECK: RetRA implicit $v0 +entry: + %1 = tail call i32 @llvm.mips.extpdp(i64 %a0, i32 15) + %2 = tail call i32 @llvm.mips.rddsp(i32 33) + %3 = add i32 %1, %2 + ret i32 %3 +} + +declare i32 @llvm.mips.addwc(i32, i32) nounwind + +define i32 @test__builtin_mips_addwc_before_rddsp(i32 %i0, i32 %a0, i32 %a1) nounwind { + ; CHECK-LABEL: name: test__builtin_mips_addwc_before_rddsp + ; CHECK: bb.0.entry: + ; CHECK: liveins: $a1, $a2 + ; CHECK: [[COPY:%[0-9]+]]:gpr32 = COPY $a2 + ; CHECK: [[COPY1:%[0-9]+]]:gpr32 = COPY $a1 + ; CHECK: [[ADDWC:%[0-9]+]]:gpr32 = ADDWC [[COPY1]], [[COPY]], implicit-def $dspoutflag20, implicit $dspcarry + ; CHECK: [[RDDSP:%[0-9]+]]:gpr32 = RDDSP 12, implicit $dspcarry, implicit $dspoutflag + ; CHECK: $v0 = COPY [[RDDSP]] + ; CHECK: RetRA implicit $v0 +entry: + %0 = tail call i32 @llvm.mips.addwc(i32 %a0, i32 %a1) + %1 = tail call i32 @llvm.mips.rddsp(i32 12) + ret i32 %1 +}