Index: llvm/lib/Target/PowerPC/PPCInstrInfo.h =================================================================== --- llvm/lib/Target/PowerPC/PPCInstrInfo.h +++ llvm/lib/Target/PowerPC/PPCInstrInfo.h @@ -128,12 +128,12 @@ // If the inst has imm-form and one of its operand is produced by a LI, // put the imm into the inst directly and remove the LI if possible. bool transformToImmFormFedByLI(MachineInstr &MI, const ImmInstrInfo &III, - unsigned ConstantOpNo, int64_t Imm) const; + unsigned ConstantOpNo, MachineInstr &DefMI, + int64_t Imm) const; // If the inst has imm-form and one of its operand is produced by an // add-immediate, try to transform it when possible. bool transformToImmFormFedByAdd(MachineInstr &MI, const ImmInstrInfo &III, - unsigned ConstantOpNo, - MachineInstr &DefMI, + unsigned ConstantOpNo, MachineInstr &DefMI, bool KillDefMI) const; // Try to find that, if the instruction 'MI' contains any operand that // could be forwarded from some inst that feeds it. If yes, return the @@ -158,8 +158,8 @@ int64_t &Imm) const; bool isRegElgibleForForwarding(const MachineOperand &RegMO, const MachineInstr &DefMI, - const MachineInstr &MI, - bool KillDefMI) const; + const MachineInstr &MI, bool KillDefMI, + bool &IsFwdFeederRegKilled) const; const unsigned *getStoreOpcodesForSpillArray() const; const unsigned *getLoadOpcodesForSpillArray() const; virtual void anchor(); @@ -411,6 +411,18 @@ bool convertToImmediateForm(MachineInstr &MI, MachineInstr **KilledDef = nullptr) const; + + /// Fixup killed/dead flag for register \p RegNo between instructions [\p + /// StartMI, \p EndMI]. Some PostRA transformations may violate register + /// killed/dead flags semantics, this function is called to fix up. Before + /// calling this function, make sure: + /// 1: \p RegNo liveness is killed after instruction \p EndMI. + /// 2: There is no new definition between (\p StartMI, \p EndMI) and possible + /// definition for \p RegNo is \p StartMI or \p EndMI. + /// 3: All instructions between [\p StartMI, \p EndMI] are in same basic + /// block. + void fixupIsDeadOrKill(MachineInstr &StartMI, MachineInstr &EndMI, + unsigned RegNo) const; void replaceInstrWithLI(MachineInstr &MI, const LoadImmediateInfo &LII) const; void replaceInstrOperandWithImm(MachineInstr &MI, unsigned OpNo, int64_t Imm) const; Index: llvm/lib/Target/PowerPC/PPCInstrInfo.cpp =================================================================== --- llvm/lib/Target/PowerPC/PPCInstrInfo.cpp +++ llvm/lib/Target/PowerPC/PPCInstrInfo.cpp @@ -2416,6 +2416,84 @@ return OpcodesForSpill[(Subtarget.hasP9Vector()) ? 1 : 0]; } +void PPCInstrInfo::fixupIsDeadOrKill(MachineInstr &StartMI, MachineInstr &EndMI, + unsigned RegNo) const { + const MachineRegisterInfo &MRI = + StartMI.getParent()->getParent()->getRegInfo(); + if (MRI.isSSA()) + return; + + // Instructions between [StartMI, EndMI] should be in same basic block. + assert((StartMI.getParent() == EndMI.getParent()) && + "Instructions are not in same basic block"); + + bool IsKillSet = false; + + // Set killed flag for EndMI. + // No need to do anything if EndMI defines RegNo. + int UseIndex = + EndMI.findRegisterUseOperandIdx(RegNo, false, &getRegisterInfo()); + if (UseIndex != -1) { + EndMI.getOperand(UseIndex).setIsKill(true); + IsKillSet = true; + // Clear killed flag for other EndMI operands related to RegNo. In some + // upexpected cases, killed may be set multiple times for same register + // operand in same MI. + for (int i = 0, e = EndMI.getNumOperands(); i != e; ++i) { + if (i != UseIndex) { + MachineOperand &MO = EndMI.getOperand(i); + if (MO.isReg() && MO.isUse() && MO.isKill() && + getRegisterInfo().regsOverlap(MO.getReg(), RegNo)) + MO.setIsKill(false); + } + } + } + + // Walking the inst in reverse order (EndMI -> StartMI]. + MachineBasicBlock::reverse_iterator It = EndMI; + MachineBasicBlock::reverse_iterator E = EndMI.getParent()->rend(); + // EndMI has been handled above, skip it here. + It++; + MachineOperand *MO = nullptr; + for (; It != E; ++It) { + // Skip insturctions which could not be a def/use of RegNo. + if (It->isDebugInstr() || It->isPosition()) + continue; + + // Clear killed flag for all It operands related to RegNo. In some + // upexpected cases, killed may be set multiple times for same register + // operand in same MI. + for (int i = 0, e = It->getNumOperands(); i != e; ++i) { + MachineOperand &MO = It->getOperand(i); + if (MO.isReg() && MO.isUse() && MO.isKill() && + getRegisterInfo().regsOverlap(MO.getReg(), RegNo)) + MO.setIsKill(false); + } + + // If killed is not set, set killed for its last use or set dead for its def + // if no use found. + if (!IsKillSet) { + if ((MO = It->findRegisterUseOperand(RegNo, false, &getRegisterInfo()))) { + // Use found, set it killed. + IsKillSet = true; + MO->setIsKill(true); + continue; + } else if ((MO = It->findRegisterDefOperand(RegNo, false, true, + &getRegisterInfo()))) { + // No use found, set dead for its def. + assert(&*It == &StartMI && "No new def between StartMI and EndMI."); + MO->setIsDead(true); + break; + } + } + + if ((&*It) == &StartMI) + break; + } + assert((IsKillSet || (MO && MO->isDead())) && + "RegNo should be killed or dead"); +} + // If this instruction has an immediate form and one of its operands is a // result of a load-immediate or an add-immediate, convert it to // the immediate form if the constant is in range. @@ -2432,8 +2510,9 @@ return false; assert(ForwardingOperand < MI.getNumOperands() && "The forwarding operand needs to be valid at this point"); - bool KillFwdDefMI = !SeenIntermediateUse && - MI.getOperand(ForwardingOperand).isKill(); + bool IsForwardingOperandKilled = MI.getOperand(ForwardingOperand).isKill(); + bool KillFwdDefMI = !SeenIntermediateUse && IsForwardingOperandKilled; + unsigned ForwardingOperandReg = MI.getOperand(ForwardingOperand).getReg(); if (KilledDef && KillFwdDefMI) *KilledDef = DefMI; @@ -2442,8 +2521,9 @@ // If this is a reg+reg instruction that has a reg+imm form, // and one of the operands is produced by an add-immediate, // try to convert it. - if (HasImmForm && transformToImmFormFedByAdd(MI, III, ForwardingOperand, - *DefMI, KillFwdDefMI)) + if (HasImmForm && + transformToImmFormFedByAdd(MI, III, ForwardingOperand, *DefMI, + KillFwdDefMI)) return true; if ((DefMI->getOpcode() != PPC::LI && DefMI->getOpcode() != PPC::LI8) || @@ -2458,7 +2538,7 @@ // If this is a reg+reg instruction that has a reg+imm form, // and one of the operands is produced by LI, convert it now. if (HasImmForm) - return transformToImmFormFedByLI(MI, III, ForwardingOperand, SExtImm); + return transformToImmFormFedByLI(MI, III, ForwardingOperand, *DefMI, SExtImm); bool ReplaceWithLI = false; bool Is64BitLI = false; @@ -2478,6 +2558,8 @@ case PPC::CMPLDI: { // Doing this post-RA would require dataflow analysis to reliably find uses // of the CR register set by the compare. + // No need to fixup killed/dead flag since this transformation is only valid + // before RA. if (PostRA) return false; // If a compare-immediate is fed by an immediate and is itself an input of @@ -2654,6 +2736,13 @@ if (KilledDef && SetCR) *KilledDef = nullptr; replaceInstrWithLI(MI, LII); + + // Fixup killed/dead flag after transformation. + // ForwardingOperandReg = LI imm1 + // y = op2 imm2, ForwardingOperandReg(killed) + if (IsForwardingOperandKilled) + fixupIsDeadOrKill(*DefMI, MI, ForwardingOperandReg); + LLVM_DEBUG(dbgs() << "With:\n"); LLVM_DEBUG(MI.dump()); return true; @@ -3161,11 +3250,10 @@ return isAnImmediateOperand(*ImmMO); } -bool PPCInstrInfo::isRegElgibleForForwarding(const MachineOperand &RegMO, - const MachineInstr &DefMI, - const MachineInstr &MI, - bool KillDefMI - ) const { +bool PPCInstrInfo::isRegElgibleForForwarding( + const MachineOperand &RegMO, const MachineInstr &DefMI, + const MachineInstr &MI, bool KillDefMI, + bool &IsFwdFeederRegKilled) const { // x = addi y, imm // ... // z = lfdx 0, x -> z = lfd imm(y) @@ -3185,6 +3273,8 @@ for (; It != E; ++It) { if (It->modifiesRegister(Reg, &getRegisterInfo()) && (&*It) != &DefMI) return false; + else if (It->killsRegister(Reg, &getRegisterInfo()) && (&*It) != &DefMI) + IsFwdFeederRegKilled = true; // Made it to DefMI without encountering a clobber. if ((&*It) == &DefMI) break; @@ -3256,11 +3346,9 @@ // is the literal zero, attempt to forward the source of the add-immediate to // the corresponding D-Form instruction with the displacement coming from // the immediate being added. -bool PPCInstrInfo::transformToImmFormFedByAdd(MachineInstr &MI, - const ImmInstrInfo &III, - unsigned OpNoForForwarding, - MachineInstr &DefMI, - bool KillDefMI) const { +bool PPCInstrInfo::transformToImmFormFedByAdd( + MachineInstr &MI, const ImmInstrInfo &III, unsigned OpNoForForwarding, + MachineInstr &DefMI, bool KillDefMI) const { // RegMO ImmMO // | | // x = addi reg, imm <----- DefMI @@ -3285,10 +3373,19 @@ if (!isImmElgibleForForwarding(*ImmMO, DefMI, III, Imm)) return false; + bool IsFwdFeederRegKilled = false; // Check if the RegMO can be forwarded to MI. - if (!isRegElgibleForForwarding(*RegMO, DefMI, MI, KillDefMI)) + if (!isRegElgibleForForwarding(*RegMO, DefMI, MI, KillDefMI, + IsFwdFeederRegKilled)) return false; + // Get killed info in case fixup needed after transformation. + unsigned ForwardKilledOperandReg = ~0U; + MachineRegisterInfo &MRI = MI.getParent()->getParent()->getRegInfo(); + bool PostRA = !MRI.isSSA(); + if (PostRA && MI.getOperand(OpNoForForwarding).isKill()) + ForwardKilledOperandReg = MI.getOperand(OpNoForForwarding).getReg(); + // We know that, the MI and DefMI both meet the pattern, and // the Imm also meet the requirement with the new Imm-form. // It is safe to do the transformation now. @@ -3339,6 +3436,24 @@ // Update the opcode. MI.setDesc(get(III.ImmOpcode)); + // Fix up killed/dead flag after transformation. + // Pattern 1: + // x = op1 KilledFwdFeederReg, imm + // n = opn KilledFwdFeederReg(killed), regn + // y = op2 0, x + if (IsFwdFeederRegKilled) + fixupIsDeadOrKill(DefMI, MI, RegMO->getReg()); + // Pattern 2: + // ForwardKilledOperandReg = op1 reg, imm + // y = op2 0, ForwardKilledOperandReg(killed) + if (ForwardKilledOperandReg != ~0U) + fixupIsDeadOrKill(DefMI, MI, ForwardKilledOperandReg); + // Pattern 3: + // x = op1 reg(killed), imm + // y = op2 0, x + if (RegMO->isKill()) + fixupIsDeadOrKill(DefMI, MI, RegMO->getReg()); + LLVM_DEBUG(dbgs() << "With:\n"); LLVM_DEBUG(MI.dump()); @@ -3348,6 +3463,7 @@ bool PPCInstrInfo::transformToImmFormFedByLI(MachineInstr &MI, const ImmInstrInfo &III, unsigned ConstantOpNo, + MachineInstr &DefMI, int64_t Imm) const { MachineRegisterInfo &MRI = MI.getParent()->getParent()->getRegInfo(); bool PostRA = !MRI.isSSA(); @@ -3386,6 +3502,11 @@ return false; } + // Get killed info in case fixup needed after transformation. + unsigned ForwardKilledOperandReg = ~0U; + if (PostRA && MI.getOperand(ConstantOpNo).isKill()) + ForwardKilledOperandReg = MI.getOperand(ConstantOpNo).getReg(); + unsigned Opc = MI.getOpcode(); bool SpecialShift32 = Opc == PPC::SLW || Opc == PPC::SLWo || Opc == PPC::SRW || Opc == PPC::SRWo; @@ -3468,6 +3589,12 @@ } } } + + // Fix up killed/dead flag after transformation. + // ForwardKilledOperandReg = op1 reg, imm + // y = op2 0, ForwardKilledOperandReg(killed) + if (ForwardKilledOperandReg != ~0U) + fixupIsDeadOrKill(DefMI, MI, ForwardKilledOperandReg); return true; } Index: llvm/test/CodeGen/PowerPC/convert-rr-to-ri-instr-add.mir =================================================================== --- llvm/test/CodeGen/PowerPC/convert-rr-to-ri-instr-add.mir +++ llvm/test/CodeGen/PowerPC/convert-rr-to-ri-instr-add.mir @@ -27,7 +27,7 @@ ; Following instruction $r3 also reads $x3, ADDI8 can not be erased ; CHECK: $x3 = ADDI8 $x5, 100, implicit-def $r3 STW $r3, $x5, 100 - ; CHECK: STW $r3, $x5, 100 + ; CHECK: STW killed $r3, $x5, 100 STFSX killed $f1, $zero8, $x3 ; CHECK: STFS killed $f1, 100, $x5 STD $x5, $x5, 100 Index: llvm/test/CodeGen/PowerPC/convert-rr-to-ri-instrs-kill-flag.mir =================================================================== --- /dev/null +++ llvm/test/CodeGen/PowerPC/convert-rr-to-ri-instrs-kill-flag.mir @@ -0,0 +1,183 @@ +# RUN: llc -mtriple=powerpc64le--linux-gnu -stop-after ppc-pre-emit-peephole %s -o - -verify-machineinstrs | FileCheck %s + +--- +# LI + XFORM -> DFORM, no killed/dead flag fixup. +name: testKillPassUpLI1 +#CHECK : name : testKillPassUpLI1 +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $x3, $f1, $x5 + $x3 = LI8 100 + STFSX killed $f1, $x3, $x5 + ; CHECK: STFS killed $f1, 100, $x5 + STD killed $x3, killed $x5, 100 + ; CHECK: STD killed $x3, killed $x5, 100 + BLR8 implicit $lr8, implicit $rm + +... +--- +# LI + XFORM -> DFORM, fixup killed/dead flag for $x3, find no use, set def as +# dead(LI8 is deleted). +name : testKillPassUpLI2 +# CHECK: name: testKillPassUpLI2 +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $x3, $f1, $x5 + $x3 = LI8 100 + ; CHECK-NOT: LI8 + STFSX killed $f1, killed $x3, killed $x5 + ; CHECK: STFS killed $f1, 100, killed $x5 + BLR8 implicit $lr8, implicit $rm + +... +--- +# LI + XFORM -> DFORM, fixup killed/dead flag for $x3, find last use, set last +# use as killed. +name: testKillPassUpLI3 +# CHECK: name: testKillPassUpLI3 +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $x3, $f1, $x5 + $x3 = LI8 100 + STD $x3, $x5, 100 + ; CHECK: STD killed $x3, $x5, 100 + STFSX killed $f1, killed $x3, $x5 + ; CHECK: STFS killed $f1, 100, $x5 + STD killed $x5, $x5, 100 + ; CHECK: STD killed $x5, $x5, 100 + BLR8 implicit $lr8, implicit $rm + +... +--- +# LI + OP -> LI, fixup killed/dead flag for $x3, find last use, set last use as +# killed. +name: testKillPassUpLI4 +# CHECK: name: testKillPassUpLI4 +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $x3, $x5 + $x3 = LI8 100 + STD $x3, $x5, 100 + ; CHECK: STD killed $x3, killed $x5, 100 + $x5 = ADDI8 killed $x3, 200 + ; CHECK: $x5 = LI8 300 + STD $x5, $x5, 100 + BLR8 implicit $lr8, implicit $rm + +... +--- +# ADD + XFORM -> DFORM, fixup killed/dead flag for $x3, find no use, set def as dead +# (ADDI8 is deleted). +name: testKillPassUpADD1 +# CHECK: name: testKillPassUpADD1 +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $x3, $f1, $x5 + $x3 = ADDI8 killed $x5, 100 + ; CHECK-NOT: ADDI8 + STFSX killed $f1, $zero8, killed $x3 + ; CHECK: STFS killed $f1, 100, killed $x5 + BLR8 implicit $lr8, implicit $rm + +... +--- +# ADD + XFORM -> DFORM, fixup killed/dead flag for $x3, find last use, set last +# use as killed. +name: testKillPassUpADD2 +# CHECK: name: testKillPassUpADD2 +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $x3, $f1, $x5 + $x3 = ADDI8 $x5, 100 + STD $x3, $x5, 100 + ; CHECK: STD killed $x3, $x5, 100 + STFSX killed $f1, $zero8, killed $x3 + ; CHECK: STFS killed $f1, 100, $x5 + STD killed $x5, $x5, 100 + ; CHECK: STD killed $x5, $x5, 100 + BLR8 implicit $lr8, implicit $rm + +... +--- +# ADD + XFORM -> DFORM, fixup killed/dead flag for register $x5, DFORM +# instruction uses $x5 and no other kill uses, set it as killed in +# DFORM instruction. +name: testKillPassDownADD1 +# CHECK: name: testKillPassDownADD1 +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $x3, $f1, $x5 + $x3 = ADDI8 killed $x5, 100 + ; CHECK: $x3 = ADDI8 $x5, 100 + STFSX killed $f1, $zero8, $x3 + ; CHECK: STFS killed $f1, 100, killed $x5 + STD killed $x3, $x3, 100 + ; CHECK: STD killed $x3, $x3, 100 + BLR8 implicit $lr8, implicit $rm + +... +--- +# ADD + XFORM -> DFORM, fixup killed/dead flag for register $x5, DFORM +# instruction uses $x5 and there is one kill use, set $x5 as killed in +# DFORM instruction and clear the other kill use killed flag. +name: testKillPassDownADD2 +# CHECK: name: testKillPassDownADD2 +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $x3, $f1, $x5 + $x3 = ADDI8 $x5, 100 + STD killed $x5, $x5, 100 + ; CHECK: STD $x5, $x5, 100 + STFSX killed $f1, $zero8, killed $x3 + ; CHECK: STFS killed $f1, 100, killed $x5 + BLR8 implicit $lr8, implicit $rm + +... +--- +# ADD + XFORM -> DFORM, fixup killed/dead flag for register $x3, DFORM +# instruction defines $x3, do nothing for killed/dead flag. +name: testKillPassDownADD3 +# CHECK: name: testKillPassDownADD3 +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $x3, $x5 + $x3 = ADDI8 $x5, 100 + $x3 = LDX $zero8, killed $x3 + ; CHECK: $x3 = LD 100, $x5 + STD killed $x5, $x5, 100 + ; CHECK: STD killed $x5, $x5, 100 + STD killed $x3, $x3, 200 + ; CHECK: STD killed $x3, $x3, 200 + BLR8 implicit $lr8, implicit $rm + +... +--- +# ADD + XFORM -> DFORM, fixup killed/dead flag for both register $x5 and $x3, +# DFORM instruction uses $x5 and there is one kill use, set $x5 as killed in +# DFORM instruction and clear the other kill use killed flag. Find last use for +# $x3, set last use as killed. +name: testKillPassDownADD4 +# CHECK: name: testKillPassDownADD4 +tracksRegLiveness: true +body: | + bb.0.entry: + liveins: $x3, $f1, $x5 + $x3 = ADDI8 $x5, 100 + STD killed $x5, $x5, 100 + ; CHECK: STD $x5, $x5, 100 + STD $x3, $x3, 200 + ; CHECK: STD killed $x3, $x3, 200 + STFSX killed $f1, $zero8, killed $x3 + ; CHECK: STFS killed $f1, 100, killed $x5 + BLR8 implicit $lr8, implicit $rm + +...