[mips][FastISel] Clobber HI0/LO0 registers in MUL instructions.

Summary:
The contents of the HI/LO registers are unpredictable after the execution of
the MUL instruction. In addition to implicitly defining these registers in the
MUL instruction definition, we have to mark those registers as dead too.

Without this the fast register allocator is running out of registers when the
MUL instruction is followed by another one that tries to allocate the AC0
register.

Based on a patch by Reed Kotler.

Reviewers: dsanders, rkotler

Subscribers: llvm-commits, rfuhler

Differential Revision: http://reviews.llvm.org/D9825

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@238755 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Vasileios Kalintiris 2015-06-01 15:48:09 +00:00
parent 1758082f64
commit 30b5412d92
2 changed files with 51 additions and 0 deletions

View File

@ -156,6 +156,12 @@ private:
unsigned MemReg, int64_t MemOffset) {
return emitInst(Opc, DstReg).addReg(MemReg).addImm(MemOffset);
}
unsigned fastEmitInst_rr(unsigned MachineInstOpcode,
const TargetRegisterClass *RC,
unsigned Op0, bool Op0IsKill,
unsigned Op1, bool Op1IsKill);
// for some reason, this default is not generated by tablegen
// so we explicitly generate it here.
//
@ -1563,6 +1569,33 @@ void MipsFastISel::simplifyAddress(Address &Addr) {
}
}
unsigned MipsFastISel::fastEmitInst_rr(unsigned MachineInstOpcode,
const TargetRegisterClass *RC,
unsigned Op0, bool Op0IsKill,
unsigned Op1, bool Op1IsKill) {
// We treat the MUL instruction in a special way because it clobbers
// the HI0 & LO0 registers. The TableGen definition of this instruction can
// mark these registers only as implicitly defined. As a result, the
// register allocator runs out of registers when this instruction is
// followed by another instruction that defines the same registers too.
// We can fix this by explicitly marking those registers as dead.
if (MachineInstOpcode == Mips::MUL) {
unsigned ResultReg = createResultReg(RC);
const MCInstrDesc &II = TII.get(MachineInstOpcode);
Op0 = constrainOperandRegClass(II, Op0, II.getNumDefs());
Op1 = constrainOperandRegClass(II, Op1, II.getNumDefs() + 1);
BuildMI(*FuncInfo.MBB, FuncInfo.InsertPt, DbgLoc, II, ResultReg)
.addReg(Op0, getKillRegState(Op0IsKill))
.addReg(Op1, getKillRegState(Op1IsKill))
.addReg(Mips::HI0, RegState::ImplicitDefine | RegState::Dead)
.addReg(Mips::LO0, RegState::ImplicitDefine | RegState::Dead);
return ResultReg;
}
return FastISel::fastEmitInst_rr(MachineInstOpcode, RC, Op0, Op0IsKill, Op1,
Op1IsKill);
}
namespace llvm {
FastISel *Mips::createFastISel(FunctionLoweringInfo &funcInfo,
const TargetLibraryInfo *libInfo) {

View File

@ -0,0 +1,18 @@
; RUN: llc < %s -march=mipsel -mcpu=mips32 -O0 \
; RUN: -fast-isel -mips-fast-isel -relocation-model=pic
; RUN: llc < %s -march=mipsel -mcpu=mips32r2 -O0 \
; RUN: -fast-isel -mips-fast-isel -relocation-model=pic
; The test is just to make sure it is able to allocate
; registers for this example. There was an issue with allocating AC0
; after a mul instruction.
declare { i32, i1 } @llvm.smul.with.overflow.i32(i32, i32)
define i32 @foo(i32 %a, i32 %b) {
entry:
%0 = mul i32 %a, %b
%1 = call { i32, i1 } @llvm.smul.with.overflow.i32(i32 %0, i32 %b)
%2 = extractvalue { i32, i1 } %1, 0
ret i32 %2
}