Skip to content

Commit 161fd6b

Browse files
sdardisnikic
authored andcommitted
[MIPS] Initial support for MIPS-I load delay slots
LLVM so far has only supported the MIPS-II and above architectures. MIPS-II is pretty close to MIPS-I, the major difference being that "load" instructions always take one extra instruction slot to propogate to registers. This patch adds support for MIPS-I by adding hazard handling for load delay slots, alongside MIPSR6 forbidden slots and FPU slots, inserting a NOP instruction between a load and any instruction immediately following that reads the load's destination register. I also included a simple regression test. Since no existing tests target MIPS-I, those all still pass. Issue ref: simias/psx-sdk-rs#1 I also tested by building a simple demo app with Clang and running it in an emulator. Patch by: @impiaaa Differential Revision: https://reviews.llvm.org/D122427
1 parent fd33681 commit 161fd6b

File tree

7 files changed

+116
-13
lines changed

7 files changed

+116
-13
lines changed

llvm/lib/Target/Mips/MipsBranchExpansion.cpp

+22-7
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@
3636
///
3737
/// Regarding compact branch hazard prevention:
3838
///
39-
/// Hazards handled: forbidden slots for MIPSR6, FPU slots for MIPS3 and below.
39+
/// Hazards handled: forbidden slots for MIPSR6, FPU slots for MIPS3 and below,
40+
/// load delay slots for MIPS1.
4041
///
4142
/// A forbidden slot hazard occurs when a compact branch instruction is executed
4243
/// and the adjacent instruction in memory is a control transfer instruction
@@ -164,6 +165,7 @@ class MipsBranchExpansion : public MachineFunctionPass {
164165
bool handleSlot(Pred Predicate, Safe SafeInSlot);
165166
bool handleForbiddenSlot();
166167
bool handleFPUDelaySlot();
168+
bool handleLoadDelaySlot();
167169
bool handlePossibleLongBranch();
168170

169171
const MipsSubtarget *STI;
@@ -762,7 +764,6 @@ bool MipsBranchExpansion::handleSlot(Pred Predicate, Safe SafeInSlot) {
762764
}
763765

764766
if (LastInstInFunction || !SafeInSlot(*IInSlot, *I)) {
765-
766767
MachineBasicBlock::instr_iterator Iit = I->getIterator();
767768
if (std::next(Iit) == FI->end() ||
768769
std::next(Iit)->getOpcode() != Mips::NOP) {
@@ -801,6 +802,18 @@ bool MipsBranchExpansion::handleFPUDelaySlot() {
801802
});
802803
}
803804

805+
bool MipsBranchExpansion::handleLoadDelaySlot() {
806+
// Load delay slot hazards are only for MIPS1.
807+
if (STI->hasMips2())
808+
return false;
809+
810+
return handleSlot(
811+
[this](auto &I) -> bool { return TII->HasLoadDelaySlot(I); },
812+
[this](auto &IInSlot, auto &I) -> bool {
813+
return TII->SafeInLoadDelaySlot(IInSlot, I);
814+
});
815+
}
816+
804817
bool MipsBranchExpansion::handlePossibleLongBranch() {
805818
if (STI->inMips16Mode() || !STI->enableLongBranchPass())
806819
return false;
@@ -877,19 +890,21 @@ bool MipsBranchExpansion::runOnMachineFunction(MachineFunction &MF) {
877890
MFp = &MF;
878891

879892
ForceLongBranchFirstPass = ForceLongBranch;
880-
// Run these two at least once
893+
// Run these at least once.
881894
bool longBranchChanged = handlePossibleLongBranch();
882895
bool forbiddenSlotChanged = handleForbiddenSlot();
883896
bool fpuDelaySlotChanged = handleFPUDelaySlot();
897+
bool loadDelaySlotChanged = handleLoadDelaySlot();
884898

885-
bool Changed =
886-
longBranchChanged || forbiddenSlotChanged || fpuDelaySlotChanged;
899+
bool Changed = longBranchChanged || forbiddenSlotChanged ||
900+
fpuDelaySlotChanged || loadDelaySlotChanged;
887901

888-
// Then run them alternatively while there are changes
902+
// Then run them alternatively while there are changes.
889903
while (forbiddenSlotChanged) {
890904
longBranchChanged = handlePossibleLongBranch();
891905
fpuDelaySlotChanged = handleFPUDelaySlot();
892-
if (!longBranchChanged && !fpuDelaySlotChanged)
906+
loadDelaySlotChanged = handleLoadDelaySlot();
907+
if (!longBranchChanged && !fpuDelaySlotChanged && !loadDelaySlotChanged)
893908
break;
894909
forbiddenSlotChanged = handleForbiddenSlot();
895910
}

llvm/lib/Target/Mips/MipsInstrInfo.cpp

+28
Original file line numberDiff line numberDiff line change
@@ -598,6 +598,18 @@ bool MipsInstrInfo::SafeInFPUDelaySlot(const MachineInstr &MIInSlot,
598598
return true;
599599
}
600600

601+
/// Predicate for distinguishing instructions that are hazardous in a load delay
602+
/// slot. Consider inline assembly as unsafe as well.
603+
bool MipsInstrInfo::SafeInLoadDelaySlot(const MachineInstr &MIInSlot,
604+
const MachineInstr &LoadMI) const {
605+
if (MIInSlot.isInlineAsm())
606+
return false;
607+
608+
return !llvm::any_of(LoadMI.defs(), [&](const MachineOperand &Op) {
609+
return Op.isReg() && MIInSlot.readsRegister(Op.getReg());
610+
});
611+
}
612+
601613
/// Predicate for distingushing instructions that have forbidden slots.
602614
bool MipsInstrInfo::HasForbiddenSlot(const MachineInstr &MI) const {
603615
return (MI.getDesc().TSFlags & MipsII::HasForbiddenSlot) != 0;
@@ -622,6 +634,22 @@ bool MipsInstrInfo::HasFPUDelaySlot(const MachineInstr &MI) const {
622634
}
623635
}
624636

637+
/// Predicate for distingushing instructions that have load delay slots.
638+
bool MipsInstrInfo::HasLoadDelaySlot(const MachineInstr &MI) const {
639+
switch (MI.getOpcode()) {
640+
case Mips::LB:
641+
case Mips::LBu:
642+
case Mips::LH:
643+
case Mips::LHu:
644+
case Mips::LW:
645+
case Mips::LWR:
646+
case Mips::LWL:
647+
return true;
648+
default:
649+
return false;
650+
}
651+
}
652+
625653
/// Return the number of bytes of code the specified instruction may be.
626654
unsigned MipsInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const {
627655
switch (MI.getOpcode()) {

llvm/lib/Target/Mips/MipsInstrInfo.h

+7
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,19 @@ class MipsInstrInfo : public MipsGenInstrInfo {
9696
bool SafeInFPUDelaySlot(const MachineInstr &MIInSlot,
9797
const MachineInstr &FPUMI) const;
9898

99+
/// Predicate to determine if an instruction can go in a load delay slot.
100+
bool SafeInLoadDelaySlot(const MachineInstr &MIInSlot,
101+
const MachineInstr &LoadMI) const;
102+
99103
/// Predicate to determine if an instruction has a forbidden slot.
100104
bool HasForbiddenSlot(const MachineInstr &MI) const;
101105

102106
/// Predicate to determine if an instruction has an FPU delay slot.
103107
bool HasFPUDelaySlot(const MachineInstr &MI) const;
104108

109+
/// Predicate to determine if an instruction has a load delay slot.
110+
bool HasLoadDelaySlot(const MachineInstr &MI) const;
111+
105112
/// Insert nop instruction when hazard condition is found
106113
void insertNoop(MachineBasicBlock &MBB,
107114
MachineBasicBlock::iterator MI) const override;

llvm/lib/Target/Mips/MipsSubtarget.cpp

+9-4
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ bool MipsSubtarget::MSAWarningPrinted = false;
6464
bool MipsSubtarget::VirtWarningPrinted = false;
6565
bool MipsSubtarget::CRCWarningPrinted = false;
6666
bool MipsSubtarget::GINVWarningPrinted = false;
67+
bool MipsSubtarget::MIPS1WarningPrinted = false;
6768

6869
void MipsSubtarget::anchor() {}
6970

@@ -91,10 +92,14 @@ MipsSubtarget::MipsSubtarget(const Triple &TT, StringRef CPU, StringRef FS,
9192
if (MipsArchVersion == MipsDefault)
9293
MipsArchVersion = Mips32;
9394

94-
// Don't even attempt to generate code for MIPS-I and MIPS-V. They have not
95-
// been tested and currently exist for the integrated assembler only.
96-
if (MipsArchVersion == Mips1)
97-
report_fatal_error("Code generation for MIPS-I is not implemented", false);
95+
// MIPS-I has not been tested.
96+
if (MipsArchVersion == Mips1 && !MIPS1WarningPrinted) {
97+
errs() << "warning: MIPS-I support is experimental\n";
98+
MIPS1WarningPrinted = true;
99+
}
100+
101+
// Don't even attempt to generate code for MIPS-V. It has not
102+
// been tested and currently exists for the integrated assembler only.
98103
if (MipsArchVersion == Mips5)
99104
report_fatal_error("Code generation for MIPS-V is not implemented", false);
100105

llvm/lib/Target/Mips/MipsSubtarget.h

+3
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ class MipsSubtarget : public MipsGenSubtargetInfo {
5959
// Used to avoid printing ginv warnings multiple times.
6060
static bool GINVWarningPrinted;
6161

62+
// Used to avoid printing Mips1 warnings multiple times.
63+
static bool MIPS1WarningPrinted;
64+
6265
// Used to avoid printing virt warnings multiple times.
6366
static bool VirtWarningPrinted;
6467

llvm/test/CodeGen/Mips/cpus.ll

+3-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
; RUN: | llvm-readelf -A - | FileCheck %s --check-prefix=GENERIC
55
; GENERIC: ISA: MIPS32
66

7+
; RUN: llc -mtriple=mips -mcpu=mips1 -filetype=obj < %s \
8+
; RUN: | llvm-readelf -A - | FileCheck %s --check-prefix=MIPS1
9+
; MIPS1: ISA: MIPS1
710
; RUN: llc -mtriple=mips -mcpu=mips2 -filetype=obj < %s \
811
; RUN: | llvm-readelf -A - | FileCheck %s --check-prefix=MIPS2
912
; MIPS2: ISA: MIPS2
@@ -57,8 +60,6 @@
5760

5861
; Check that we reject CPUs that are not implemented.
5962

60-
; RUN: not --crash llc < %s -o /dev/null -mtriple=mips -mcpu=mips1 2>&1 \
61-
; RUN: | FileCheck %s --check-prefix=ERROR
6263
; RUN: not --crash llc < %s -o /dev/null -mtriple=mips64 -mcpu=mips5 2>&1 \
6364
; RUN: | FileCheck %s --check-prefix=ERROR
6465

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
; RUN: llc < %s -mtriple=mips -mcpu=mips1 | FileCheck %s -check-prefixes=ALL,MIPS1
2+
; RUN: llc < %s -mtriple=mips -mcpu=mips2 | FileCheck %s -check-prefixes=ALL,MIPS2
3+
; RUN: llc < %s -mtriple=mips -mcpu=mips32r2 | FileCheck %s -check-prefixes=ALL,MIPS32
4+
target datalayout = "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64"
5+
target triple = "mipsel-unknown-unknown-elf"
6+
7+
; Function Attrs: noinline nounwind optnone
8+
define dso_local i32 @add_two_pointers(i32* %a, i32* %b) #0 {
9+
entry:
10+
; ALL-LABEL: add_two_pointers:
11+
%a.addr = alloca i32*, align 4
12+
%b.addr = alloca i32*, align 4
13+
store i32* %a, i32** %a.addr, align 4
14+
store i32* %b, i32** %b.addr, align 4
15+
%0 = load i32*, i32** %a.addr, align 4
16+
%1 = load i32, i32* %0, align 4
17+
; ALL: lw $1, 4($fp)
18+
; MIPS1: nop
19+
; MIPS2-NOT: nop
20+
; MIPS32-NOT: nop
21+
; ALL: lw $1, 0($1)
22+
%2 = load i32*, i32** %b.addr, align 4
23+
%3 = load i32, i32* %2, align 4
24+
; ALL: lw $2, 0($fp)
25+
; MIPS1: nop
26+
; MIPS2-NOT: nop
27+
; MIPS32-NOT: nop
28+
; ALL: lw $2, 0($2)
29+
%add = add nsw i32 %1, %3
30+
ret i32 %add
31+
; ALL: lw $ra, 12($sp)
32+
; MIPS1: nop
33+
; MIPS2-NOT: nop
34+
; MIPS32-NOT: nop
35+
; ALL: jr $ra
36+
}
37+
38+
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" }
39+
40+
!llvm.module.flags = !{!0, !1}
41+
42+
!0 = !{i32 1, !"wchar_size", i32 4}
43+
!1 = !{i32 7, !"frame-pointer", i32 2}
44+

0 commit comments

Comments
 (0)