Skip to content

[Mips] Fix mfhi/mflo hazard miscompilation about div and mult #91449

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions llvm/lib/Target/Mips/Mips.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,17 @@
#include "MCTargetDesc/MipsMCTargetDesc.h"
#include "llvm/Target/TargetMachine.h"

#define IsMFLOMFHI(instr) \
(instr == Mips::MFLO || instr == Mips::MFLO64 || instr == Mips::MFHI || \
instr == Mips::MFHI64)
#define IsDIVMULT(instr) \
(instr == Mips::SDIV || instr == Mips::PseudoSDIV || instr == Mips::DSDIV || \
instr == Mips::PseudoDSDIV || instr == Mips::UDIV || \
instr == Mips::PseudoUDIV || instr == Mips::DUDIV || \
instr == Mips::PseudoDUDIV || instr == Mips::MULT || \
instr == Mips::PseudoMULT || instr == Mips::DMULT || \
instr == Mips::PseudoDMULT)

namespace llvm {
class FunctionPass;
class InstructionSelector;
Expand Down
70 changes: 68 additions & 2 deletions llvm/lib/Target/Mips/MipsBranchExpansion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ class MipsBranchExpansion : public MachineFunctionPass {
bool handleFPUDelaySlot();
bool handleLoadDelaySlot();
bool handlePossibleLongBranch();
bool handleMFLO();
template <typename Pred, typename Safe>
bool handleMFLOSlot(Pred Predicate, Safe SafeInSlot);

const MipsSubtarget *STI;
const MipsInstrInfo *TII;
Expand Down Expand Up @@ -741,6 +744,53 @@ static void emitGPDisp(MachineFunction &F, const MipsInstrInfo *TII) {
MBB.removeLiveIn(Mips::V0);
}

template <typename Pred, typename Safe>
bool MipsBranchExpansion::handleMFLOSlot(Pred Predicate, Safe SafeInSlot) {
bool Changed = false;
bool hasPendingMFLO = false;

for (MachineFunction::iterator FI = MFp->begin(); FI != MFp->end(); ++FI) {
for (Iter I = FI->begin(); I != FI->end(); ++I) {

if (!Predicate(*I) && !hasPendingMFLO) {
continue;
}

Iter IInSlot;
bool LastInstInFunction =
std::next(I) == FI->end() && std::next(FI) == MFp->end();
// We need process several situations:
// mflo is last instruction, do not process;
// mflo + div, add two nop between them;
// mflo + none-div + none-div, do not process;
// mflo + none-div + div, add nop between none-div and div.
if (!LastInstInFunction) {
std::pair<Iter, bool> Res = getNextMachineInstr(std::next(I), &*FI);
LastInstInFunction |= Res.second;
IInSlot = Res.first;
if (!SafeInSlot(*IInSlot, *I)) {
Changed = true;
TII->insertNop(*(I->getParent()), std::next(I), I->getDebugLoc())
->bundleWithPred();
NumInsertedNops++;
if (IsMFLOMFHI(I->getOpcode())) {
TII->insertNop(*(I->getParent()), std::next(I), I->getDebugLoc())
->bundleWithPred();
NumInsertedNops++;
}
if (hasPendingMFLO)
hasPendingMFLO = false;
} else if (hasPendingMFLO)
hasPendingMFLO = false;
else if (IsMFLOMFHI(I->getOpcode()))
hasPendingMFLO = true;
}
}
}

return Changed;
}

template <typename Pred, typename Safe>
bool MipsBranchExpansion::handleSlot(Pred Predicate, Safe SafeInSlot) {
bool Changed = false;
Expand Down Expand Up @@ -777,6 +827,19 @@ bool MipsBranchExpansion::handleSlot(Pred Predicate, Safe SafeInSlot) {
return Changed;
}

bool MipsBranchExpansion::handleMFLO() {
// mips1-4 require a minimum of 2 instructions between a mflo/mfhi
// and the next mul/div instruction.
if (STI->hasMips32() || STI->hasMips5())
return false;

return handleMFLOSlot(
[this](auto &I) -> bool { return TII->IsMfloOrMfhi(I); },
[this](auto &IInSlot, auto &I) -> bool {
return TII->SafeAfterMflo(IInSlot);
});
}

bool MipsBranchExpansion::handleForbiddenSlot() {
// Forbidden slot hazards are only defined for MIPSR6 but not microMIPSR6.
if (!STI->hasMips32r6() || STI->inMicroMipsMode())
Expand Down Expand Up @@ -893,16 +956,19 @@ bool MipsBranchExpansion::runOnMachineFunction(MachineFunction &MF) {
bool forbiddenSlotChanged = handleForbiddenSlot();
bool fpuDelaySlotChanged = handleFPUDelaySlot();
bool loadDelaySlotChanged = handleLoadDelaySlot();
bool MfloChanged = handleMFLO();

bool Changed = longBranchChanged || forbiddenSlotChanged ||
fpuDelaySlotChanged || loadDelaySlotChanged;
fpuDelaySlotChanged || loadDelaySlotChanged || MfloChanged;

// Then run them alternatively while there are changes.
while (forbiddenSlotChanged) {
longBranchChanged = handlePossibleLongBranch();
fpuDelaySlotChanged = handleFPUDelaySlot();
loadDelaySlotChanged = handleLoadDelaySlot();
if (!longBranchChanged && !fpuDelaySlotChanged && !loadDelaySlotChanged)
MfloChanged = handleMFLO();
if (!longBranchChanged && !fpuDelaySlotChanged && !loadDelaySlotChanged &&
!MfloChanged)
break;
forbiddenSlotChanged = handleForbiddenSlot();
}
Expand Down
6 changes: 6 additions & 0 deletions llvm/lib/Target/Mips/MipsDelaySlotFiller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,12 @@ bool MipsDelaySlotFiller::searchRange(MachineBasicBlock &MBB, IterTy Begin,
bool InMicroMipsMode = STI.inMicroMipsMode();
const MipsInstrInfo *TII = STI.getInstrInfo();
unsigned Opcode = (*Slot).getOpcode();

// In mips1-4, should not put mflo into the delay slot for the return.
if ((IsMFLOMFHI(CurrI->getOpcode())) &&
(!STI.hasMips32() && !STI.hasMips5()))
continue;

// This is complicated by the tail call optimization. For non-PIC code
// there is only a 32bit sized unconditional branch which can be assumed
// to be able to reach the target. b16 only has a range of +/- 1 KB.
Expand Down
15 changes: 15 additions & 0 deletions llvm/lib/Target/Mips/MipsInstrInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "MipsInstrInfo.h"
#include "MCTargetDesc/MipsBaseInfo.h"
#include "MCTargetDesc/MipsMCTargetDesc.h"
#include "Mips.h"
#include "MipsSubtarget.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/CodeGen/MachineBasicBlock.h"
Expand Down Expand Up @@ -571,6 +572,13 @@ unsigned MipsInstrInfo::getEquivalentCompactForm(
return 0;
}

bool MipsInstrInfo::SafeAfterMflo(const MachineInstr &MI) const {
if (IsDIVMULT(MI.getOpcode()))
return false;

return true;
}

/// Predicate for distingushing between control transfer instructions and all
/// other instructions for handling forbidden slots. Consider inline assembly
/// as unsafe as well.
Expand Down Expand Up @@ -623,6 +631,13 @@ bool MipsInstrInfo::SafeInLoadDelaySlot(const MachineInstr &MIInSlot,
});
}

bool MipsInstrInfo::IsMfloOrMfhi(const MachineInstr &MI) const {
if (IsMFLOMFHI(MI.getOpcode()))
return true;

return false;
}

/// Predicate for distingushing instructions that have forbidden slots.
bool MipsInstrInfo::HasForbiddenSlot(const MachineInstr &MI) const {
return (MI.getDesc().TSFlags & MipsII::HasForbiddenSlot) != 0;
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/Mips/MipsInstrInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ class MipsInstrInfo : public MipsGenInstrInfo {
bool isBranchOffsetInRange(unsigned BranchOpc,
int64_t BrOffset) const override;

bool SafeAfterMflo(const MachineInstr &MI) const;

/// Predicate to determine if an instruction can go in a forbidden slot.
bool SafeInForbiddenSlot(const MachineInstr &MI) const;

Expand All @@ -100,6 +102,8 @@ class MipsInstrInfo : public MipsGenInstrInfo {
bool SafeInLoadDelaySlot(const MachineInstr &MIInSlot,
const MachineInstr &LoadMI) const;

bool IsMfloOrMfhi(const MachineInstr &MI) const;

/// Predicate to determine if an instruction has a forbidden slot.
bool HasForbiddenSlot(const MachineInstr &MI) const;

Expand Down
Loading
Loading