diff --git a/llvm/lib/Target/Mips/MipsBranchExpansion.cpp b/llvm/lib/Target/Mips/MipsBranchExpansion.cpp --- a/llvm/lib/Target/Mips/MipsBranchExpansion.cpp +++ b/llvm/lib/Target/Mips/MipsBranchExpansion.cpp @@ -36,7 +36,8 @@ /// /// Regarding compact branch hazard prevention: /// -/// Hazards handled: forbidden slots for MIPSR6, FPU slots for MIPS3 and below. +/// Hazards handled: forbidden slots for MIPSR6, FPU slots for MIPS3 and below, +/// load delay slots for MIPS1. /// /// A forbidden slot hazard occurs when a compact branch instruction is executed /// and the adjacent instruction in memory is a control transfer instruction @@ -164,6 +165,7 @@ bool handleSlot(Pred Predicate, Safe SafeInSlot); bool handleForbiddenSlot(); bool handleFPUDelaySlot(); + bool handleLoadDelaySlot(); bool handlePossibleLongBranch(); const MipsSubtarget *STI; @@ -762,7 +764,6 @@ } if (LastInstInFunction || !SafeInSlot(*IInSlot, *I)) { - MachineBasicBlock::instr_iterator Iit = I->getIterator(); if (std::next(Iit) == FI->end() || std::next(Iit)->getOpcode() != Mips::NOP) { @@ -801,6 +802,18 @@ }); } +bool MipsBranchExpansion::handleLoadDelaySlot() { + // Load delay slot hazards are only for MIPS1. + if (STI->hasMips2()) + return false; + + return handleSlot( + [this](auto &I) -> bool { return TII->HasLoadDelaySlot(I); }, + [this](auto &IInSlot, auto &I) -> bool { + return TII->SafeInLoadDelaySlot(IInSlot, I); + }); +} + bool MipsBranchExpansion::handlePossibleLongBranch() { if (STI->inMips16Mode() || !STI->enableLongBranchPass()) return false; @@ -877,19 +890,21 @@ MFp = &MF; ForceLongBranchFirstPass = ForceLongBranch; - // Run these two at least once + // Run these at least once. bool longBranchChanged = handlePossibleLongBranch(); bool forbiddenSlotChanged = handleForbiddenSlot(); bool fpuDelaySlotChanged = handleFPUDelaySlot(); + bool loadDelaySlotChanged = handleLoadDelaySlot(); - bool Changed = - longBranchChanged || forbiddenSlotChanged || fpuDelaySlotChanged; + bool Changed = longBranchChanged || forbiddenSlotChanged || + fpuDelaySlotChanged || loadDelaySlotChanged; - // Then run them alternatively while there are changes + // Then run them alternatively while there are changes. while (forbiddenSlotChanged) { longBranchChanged = handlePossibleLongBranch(); fpuDelaySlotChanged = handleFPUDelaySlot(); - if (!longBranchChanged && !fpuDelaySlotChanged) + loadDelaySlotChanged = handleLoadDelaySlot(); + if (!longBranchChanged && !fpuDelaySlotChanged && !loadDelaySlotChanged) break; forbiddenSlotChanged = handleForbiddenSlot(); } diff --git a/llvm/lib/Target/Mips/MipsInstrInfo.h b/llvm/lib/Target/Mips/MipsInstrInfo.h --- a/llvm/lib/Target/Mips/MipsInstrInfo.h +++ b/llvm/lib/Target/Mips/MipsInstrInfo.h @@ -96,12 +96,19 @@ bool SafeInFPUDelaySlot(const MachineInstr &MIInSlot, const MachineInstr &FPUMI) const; + /// Predicate to determine if an instruction can go in a load delay slot. + bool SafeInLoadDelaySlot(const MachineInstr &MIInSlot, + const MachineInstr &LoadMI) const; + /// Predicate to determine if an instruction has a forbidden slot. bool HasForbiddenSlot(const MachineInstr &MI) const; /// Predicate to determine if an instruction has an FPU delay slot. bool HasFPUDelaySlot(const MachineInstr &MI) const; + /// Predicate to determine if an instruction has a load delay slot. + bool HasLoadDelaySlot(const MachineInstr &MI) const; + /// Insert nop instruction when hazard condition is found void insertNoop(MachineBasicBlock &MBB, MachineBasicBlock::iterator MI) const override; diff --git a/llvm/lib/Target/Mips/MipsInstrInfo.cpp b/llvm/lib/Target/Mips/MipsInstrInfo.cpp --- a/llvm/lib/Target/Mips/MipsInstrInfo.cpp +++ b/llvm/lib/Target/Mips/MipsInstrInfo.cpp @@ -598,6 +598,18 @@ return true; } +/// Predicate for distinguishing instructions that are hazardous in a load delay +/// slot. Consider inline assembly as unsafe as well. +bool MipsInstrInfo::SafeInLoadDelaySlot(const MachineInstr &MIInSlot, + const MachineInstr &LoadMI) const { + if (MIInSlot.isInlineAsm()) + return false; + + return !llvm::any_of(LoadMI.defs(), [&](const MachineOperand &Op) { + return Op.isReg() && MIInSlot.readsRegister(Op.getReg()); + }); +} + /// Predicate for distingushing instructions that have forbidden slots. bool MipsInstrInfo::HasForbiddenSlot(const MachineInstr &MI) const { return (MI.getDesc().TSFlags & MipsII::HasForbiddenSlot) != 0; @@ -622,6 +634,22 @@ } } +/// Predicate for distingushing instructions that have load delay slots. +bool MipsInstrInfo::HasLoadDelaySlot(const MachineInstr &MI) const { + switch (MI.getOpcode()) { + case Mips::LB: + case Mips::LBu: + case Mips::LH: + case Mips::LHu: + case Mips::LW: + case Mips::LWR: + case Mips::LWL: + return true; + default: + return false; + } +} + /// Return the number of bytes of code the specified instruction may be. unsigned MipsInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const { switch (MI.getOpcode()) { diff --git a/llvm/lib/Target/Mips/MipsSubtarget.h b/llvm/lib/Target/Mips/MipsSubtarget.h --- a/llvm/lib/Target/Mips/MipsSubtarget.h +++ b/llvm/lib/Target/Mips/MipsSubtarget.h @@ -59,6 +59,9 @@ // Used to avoid printing ginv warnings multiple times. static bool GINVWarningPrinted; + // Used to avoid printing Mips1 warnings multiple times. + static bool MIPS1WarningPrinted; + // Used to avoid printing virt warnings multiple times. static bool VirtWarningPrinted; diff --git a/llvm/lib/Target/Mips/MipsSubtarget.cpp b/llvm/lib/Target/Mips/MipsSubtarget.cpp --- a/llvm/lib/Target/Mips/MipsSubtarget.cpp +++ b/llvm/lib/Target/Mips/MipsSubtarget.cpp @@ -64,6 +64,7 @@ bool MipsSubtarget::VirtWarningPrinted = false; bool MipsSubtarget::CRCWarningPrinted = false; bool MipsSubtarget::GINVWarningPrinted = false; +bool MipsSubtarget::MIPS1WarningPrinted = false; void MipsSubtarget::anchor() {} @@ -91,10 +92,14 @@ if (MipsArchVersion == MipsDefault) MipsArchVersion = Mips32; - // Don't even attempt to generate code for MIPS-I and MIPS-V. They have not - // been tested and currently exist for the integrated assembler only. - if (MipsArchVersion == Mips1) - report_fatal_error("Code generation for MIPS-I is not implemented", false); + // MIPS-I has not been tested. + if (MipsArchVersion == Mips1 && !MIPS1WarningPrinted) { + errs() << "warning: MIPS-I support is experimental\n"; + MIPS1WarningPrinted = true; + } + + // Don't even attempt to generate code for MIPS-V. It has not + // been tested and currently exists for the integrated assembler only. if (MipsArchVersion == Mips5) report_fatal_error("Code generation for MIPS-V is not implemented", false); diff --git a/llvm/test/CodeGen/Mips/cpus.ll b/llvm/test/CodeGen/Mips/cpus.ll --- a/llvm/test/CodeGen/Mips/cpus.ll +++ b/llvm/test/CodeGen/Mips/cpus.ll @@ -4,6 +4,9 @@ ; RUN: | llvm-readelf -A - | FileCheck %s --check-prefix=GENERIC ; GENERIC: ISA: MIPS32 +; RUN: llc -mtriple=mips -mcpu=mips1 -filetype=obj < %s \ +; RUN: | llvm-readelf -A - | FileCheck %s --check-prefix=MIPS1 +; MIPS1: ISA: MIPS1 ; RUN: llc -mtriple=mips -mcpu=mips2 -filetype=obj < %s \ ; RUN: | llvm-readelf -A - | FileCheck %s --check-prefix=MIPS2 ; MIPS2: ISA: MIPS2 @@ -57,8 +60,6 @@ ; Check that we reject CPUs that are not implemented. -; RUN: not --crash llc < %s -o /dev/null -mtriple=mips -mcpu=mips1 2>&1 \ -; RUN: | FileCheck %s --check-prefix=ERROR ; RUN: not --crash llc < %s -o /dev/null -mtriple=mips64 -mcpu=mips5 2>&1 \ ; RUN: | FileCheck %s --check-prefix=ERROR diff --git a/llvm/test/CodeGen/Mips/mips1-load-delay.ll b/llvm/test/CodeGen/Mips/mips1-load-delay.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/Mips/mips1-load-delay.ll @@ -0,0 +1,44 @@ +; RUN: llc < %s -mtriple=mips -mcpu=mips1 | FileCheck %s -check-prefixes=ALL,MIPS1 +; RUN: llc < %s -mtriple=mips -mcpu=mips2 | FileCheck %s -check-prefixes=ALL,MIPS2 +; RUN: llc < %s -mtriple=mips -mcpu=mips32r2 | FileCheck %s -check-prefixes=ALL,MIPS32 +target datalayout = "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64" +target triple = "mipsel-unknown-unknown-elf" + +; Function Attrs: noinline nounwind optnone +define dso_local i32 @add_two_pointers(i32* %a, i32* %b) #0 { +entry: +; ALL-LABEL: add_two_pointers: + %a.addr = alloca i32*, align 4 + %b.addr = alloca i32*, align 4 + store i32* %a, i32** %a.addr, align 4 + store i32* %b, i32** %b.addr, align 4 + %0 = load i32*, i32** %a.addr, align 4 + %1 = load i32, i32* %0, align 4 + ; ALL: lw $1, 4($fp) + ; MIPS1: nop + ; MIPS2-NOT: nop + ; MIPS32-NOT: nop + ; ALL: lw $1, 0($1) + %2 = load i32*, i32** %b.addr, align 4 + %3 = load i32, i32* %2, align 4 + ; ALL: lw $2, 0($fp) + ; MIPS1: nop + ; MIPS2-NOT: nop + ; MIPS32-NOT: nop + ; ALL: lw $2, 0($2) + %add = add nsw i32 %1, %3 + ret i32 %add + ; ALL: lw $ra, 12($sp) + ; MIPS1: nop + ; MIPS2-NOT: nop + ; MIPS32-NOT: nop + ; ALL: jr $ra +} + +attributes #0 = { noinline nounwind optnone "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="-noabicalls" } + +!llvm.module.flags = !{!0, !1} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"frame-pointer", i32 2} +