mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-02-01 02:33:44 +00:00
Add support for mips32 madd and msub instructions. Patch by Akira Hatanaka
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@123760 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
b2f3c383ec
commit
8be7611245
@ -46,6 +46,10 @@ const char *MipsTargetLowering::getTargetNodeName(unsigned Opcode) const {
|
|||||||
case MipsISD::FPBrcond : return "MipsISD::FPBrcond";
|
case MipsISD::FPBrcond : return "MipsISD::FPBrcond";
|
||||||
case MipsISD::FPCmp : return "MipsISD::FPCmp";
|
case MipsISD::FPCmp : return "MipsISD::FPCmp";
|
||||||
case MipsISD::FPRound : return "MipsISD::FPRound";
|
case MipsISD::FPRound : return "MipsISD::FPRound";
|
||||||
|
case MipsISD::MAdd : return "MipsISD::MAdd";
|
||||||
|
case MipsISD::MAddu : return "MipsISD::MAddu";
|
||||||
|
case MipsISD::MSub : return "MipsISD::MSub";
|
||||||
|
case MipsISD::MSubu : return "MipsISD::MSubu";
|
||||||
default : return NULL;
|
default : return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,6 +158,9 @@ MipsTargetLowering(MipsTargetMachine &TM)
|
|||||||
if (!Subtarget->hasSwap())
|
if (!Subtarget->hasSwap())
|
||||||
setOperationAction(ISD::BSWAP, MVT::i32, Expand);
|
setOperationAction(ISD::BSWAP, MVT::i32, Expand);
|
||||||
|
|
||||||
|
setTargetDAGCombine(ISD::ADDE);
|
||||||
|
setTargetDAGCombine(ISD::SUBE);
|
||||||
|
|
||||||
setStackPointerRegisterToSaveRestore(Mips::SP);
|
setStackPointerRegisterToSaveRestore(Mips::SP);
|
||||||
computeRegisterProperties();
|
computeRegisterProperties();
|
||||||
}
|
}
|
||||||
@ -167,6 +174,194 @@ unsigned MipsTargetLowering::getFunctionAlignment(const Function *) const {
|
|||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelectMadd -
|
||||||
|
// Transforms a subgraph in CurDAG if the following pattern is found:
|
||||||
|
// (addc multLo, Lo0), (adde multHi, Hi0),
|
||||||
|
// where,
|
||||||
|
// multHi/Lo: product of multiplication
|
||||||
|
// Lo0: initial value of Lo register
|
||||||
|
// Hi0: initial value of Hi register
|
||||||
|
// Return true if mattern matching was successful.
|
||||||
|
static bool SelectMadd(SDNode* ADDENode, SelectionDAG* CurDAG) {
|
||||||
|
// ADDENode's second operand must be a flag output of an ADDC node in order
|
||||||
|
// for the matching to be successful.
|
||||||
|
SDNode* ADDCNode = ADDENode->getOperand(2).getNode();
|
||||||
|
|
||||||
|
if (ADDCNode->getOpcode() != ISD::ADDC)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SDValue MultHi = ADDENode->getOperand(0);
|
||||||
|
SDValue MultLo = ADDCNode->getOperand(0);
|
||||||
|
SDNode* MultNode = MultHi.getNode();
|
||||||
|
unsigned MultOpc = MultHi.getOpcode();
|
||||||
|
|
||||||
|
// MultHi and MultLo must be generated by the same node,
|
||||||
|
if (MultLo.getNode() != MultNode)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// and it must be a multiplication.
|
||||||
|
if (MultOpc != ISD::SMUL_LOHI && MultOpc != ISD::UMUL_LOHI)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// MultLo amd MultHi must be the first and second output of MultNode
|
||||||
|
// respectively.
|
||||||
|
if (MultHi.getResNo() != 1 || MultLo.getResNo() != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Transform this to a MADD only if ADDENode and ADDCNode are the only users
|
||||||
|
// of the values of MultNode, in which case MultNode will be removed in later
|
||||||
|
// phases.
|
||||||
|
// If there exist users other than ADDENode or ADDCNode, this function returns
|
||||||
|
// here, which will result in MultNode being mapped to a single MULT
|
||||||
|
// instruction node rather than a pair of MULT and MADD instructions being
|
||||||
|
// produced.
|
||||||
|
if (!MultHi.hasOneUse() || !MultLo.hasOneUse())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SDValue Chain = CurDAG->getEntryNode();
|
||||||
|
DebugLoc dl = ADDENode->getDebugLoc();
|
||||||
|
|
||||||
|
// create MipsMAdd(u) node
|
||||||
|
MultOpc = MultOpc == ISD::UMUL_LOHI ? MipsISD::MAddu : MipsISD::MAdd;
|
||||||
|
|
||||||
|
SDValue MAdd = CurDAG->getNode(MultOpc, dl,
|
||||||
|
MVT::Glue,
|
||||||
|
MultNode->getOperand(0),// Factor 0
|
||||||
|
MultNode->getOperand(1),// Factor 1
|
||||||
|
ADDCNode->getOperand(1),// Lo0
|
||||||
|
ADDENode->getOperand(1));// Hi0
|
||||||
|
|
||||||
|
// create CopyFromReg nodes
|
||||||
|
SDValue CopyFromLo = CurDAG->getCopyFromReg(Chain, dl, Mips::LO, MVT::i32,
|
||||||
|
MAdd);
|
||||||
|
SDValue CopyFromHi = CurDAG->getCopyFromReg(CopyFromLo.getValue(1), dl,
|
||||||
|
Mips::HI, MVT::i32,
|
||||||
|
CopyFromLo.getValue(2));
|
||||||
|
|
||||||
|
// replace uses of adde and addc here
|
||||||
|
if (!SDValue(ADDCNode, 0).use_empty())
|
||||||
|
CurDAG->ReplaceAllUsesOfValueWith(SDValue(ADDCNode, 0), CopyFromLo);
|
||||||
|
|
||||||
|
if (!SDValue(ADDENode, 0).use_empty())
|
||||||
|
CurDAG->ReplaceAllUsesOfValueWith(SDValue(ADDENode, 0), CopyFromHi);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectMsub -
|
||||||
|
// Transforms a subgraph in CurDAG if the following pattern is found:
|
||||||
|
// (addc Lo0, multLo), (sube Hi0, multHi),
|
||||||
|
// where,
|
||||||
|
// multHi/Lo: product of multiplication
|
||||||
|
// Lo0: initial value of Lo register
|
||||||
|
// Hi0: initial value of Hi register
|
||||||
|
// Return true if mattern matching was successful.
|
||||||
|
static bool SelectMsub(SDNode* SUBENode, SelectionDAG* CurDAG) {
|
||||||
|
// SUBENode's second operand must be a flag output of an SUBC node in order
|
||||||
|
// for the matching to be successful.
|
||||||
|
SDNode* SUBCNode = SUBENode->getOperand(2).getNode();
|
||||||
|
|
||||||
|
if (SUBCNode->getOpcode() != ISD::SUBC)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SDValue MultHi = SUBENode->getOperand(1);
|
||||||
|
SDValue MultLo = SUBCNode->getOperand(1);
|
||||||
|
SDNode* MultNode = MultHi.getNode();
|
||||||
|
unsigned MultOpc = MultHi.getOpcode();
|
||||||
|
|
||||||
|
// MultHi and MultLo must be generated by the same node,
|
||||||
|
if (MultLo.getNode() != MultNode)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// and it must be a multiplication.
|
||||||
|
if (MultOpc != ISD::SMUL_LOHI && MultOpc != ISD::UMUL_LOHI)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// MultLo amd MultHi must be the first and second output of MultNode
|
||||||
|
// respectively.
|
||||||
|
if (MultHi.getResNo() != 1 || MultLo.getResNo() != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Transform this to a MSUB only if SUBENode and SUBCNode are the only users
|
||||||
|
// of the values of MultNode, in which case MultNode will be removed in later
|
||||||
|
// phases.
|
||||||
|
// If there exist users other than SUBENode or SUBCNode, this function returns
|
||||||
|
// here, which will result in MultNode being mapped to a single MULT
|
||||||
|
// instruction node rather than a pair of MULT and MSUB instructions being
|
||||||
|
// produced.
|
||||||
|
if (!MultHi.hasOneUse() || !MultLo.hasOneUse())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SDValue Chain = CurDAG->getEntryNode();
|
||||||
|
DebugLoc dl = SUBENode->getDebugLoc();
|
||||||
|
|
||||||
|
// create MipsSub(u) node
|
||||||
|
MultOpc = MultOpc == ISD::UMUL_LOHI ? MipsISD::MSubu : MipsISD::MSub;
|
||||||
|
|
||||||
|
SDValue MSub = CurDAG->getNode(MultOpc, dl,
|
||||||
|
MVT::Glue,
|
||||||
|
MultNode->getOperand(0),// Factor 0
|
||||||
|
MultNode->getOperand(1),// Factor 1
|
||||||
|
SUBCNode->getOperand(0),// Lo0
|
||||||
|
SUBENode->getOperand(0));// Hi0
|
||||||
|
|
||||||
|
// create CopyFromReg nodes
|
||||||
|
SDValue CopyFromLo = CurDAG->getCopyFromReg(Chain, dl, Mips::LO, MVT::i32,
|
||||||
|
MSub);
|
||||||
|
SDValue CopyFromHi = CurDAG->getCopyFromReg(CopyFromLo.getValue(1), dl,
|
||||||
|
Mips::HI, MVT::i32,
|
||||||
|
CopyFromLo.getValue(2));
|
||||||
|
|
||||||
|
// replace uses of sube and subc here
|
||||||
|
if (!SDValue(SUBCNode, 0).use_empty())
|
||||||
|
CurDAG->ReplaceAllUsesOfValueWith(SDValue(SUBCNode, 0), CopyFromLo);
|
||||||
|
|
||||||
|
if (!SDValue(SUBENode, 0).use_empty())
|
||||||
|
CurDAG->ReplaceAllUsesOfValueWith(SDValue(SUBENode, 0), CopyFromHi);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static SDValue PerformADDECombine(SDNode *N, SelectionDAG& DAG,
|
||||||
|
TargetLowering::DAGCombinerInfo &DCI,
|
||||||
|
const MipsSubtarget* Subtarget) {
|
||||||
|
if (DCI.isBeforeLegalize())
|
||||||
|
return SDValue();
|
||||||
|
|
||||||
|
if (Subtarget->isMips32() && SelectMadd(N, &DAG))
|
||||||
|
return SDValue(N, 0);
|
||||||
|
|
||||||
|
return SDValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
static SDValue PerformSUBECombine(SDNode *N, SelectionDAG& DAG,
|
||||||
|
TargetLowering::DAGCombinerInfo &DCI,
|
||||||
|
const MipsSubtarget* Subtarget) {
|
||||||
|
if (DCI.isBeforeLegalize())
|
||||||
|
return SDValue();
|
||||||
|
|
||||||
|
if (Subtarget->isMips32() && SelectMsub(N, &DAG))
|
||||||
|
return SDValue(N, 0);
|
||||||
|
|
||||||
|
return SDValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
SDValue MipsTargetLowering::PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI)
|
||||||
|
const {
|
||||||
|
SelectionDAG &DAG = DCI.DAG;
|
||||||
|
unsigned opc = N->getOpcode();
|
||||||
|
|
||||||
|
switch (opc) {
|
||||||
|
default: break;
|
||||||
|
case ISD::ADDE:
|
||||||
|
return PerformADDECombine(N, DAG, DCI, Subtarget);
|
||||||
|
case ISD::SUBE:
|
||||||
|
return PerformSUBECombine(N, DAG, DCI, Subtarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDValue();
|
||||||
|
}
|
||||||
|
|
||||||
SDValue MipsTargetLowering::
|
SDValue MipsTargetLowering::
|
||||||
LowerOperation(SDValue Op, SelectionDAG &DAG) const
|
LowerOperation(SDValue Op, SelectionDAG &DAG) const
|
||||||
{
|
{
|
||||||
|
@ -56,7 +56,13 @@ namespace llvm {
|
|||||||
FPRound,
|
FPRound,
|
||||||
|
|
||||||
// Return
|
// Return
|
||||||
Ret
|
Ret,
|
||||||
|
|
||||||
|
// MAdd/Sub nodes
|
||||||
|
MAdd,
|
||||||
|
MAddu,
|
||||||
|
MSub,
|
||||||
|
MSubu
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,6 +86,8 @@ namespace llvm {
|
|||||||
|
|
||||||
/// getFunctionAlignment - Return the Log2 alignment of this function.
|
/// getFunctionAlignment - Return the Log2 alignment of this function.
|
||||||
virtual unsigned getFunctionAlignment(const Function *F) const;
|
virtual unsigned getFunctionAlignment(const Function *F) const;
|
||||||
|
|
||||||
|
virtual SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const;
|
||||||
private:
|
private:
|
||||||
// Subtarget Info
|
// Subtarget Info
|
||||||
const MipsSubtarget *Subtarget;
|
const MipsSubtarget *Subtarget;
|
||||||
|
@ -26,6 +26,11 @@ def SDT_MipsCMov : SDTypeProfile<1, 4, [SDTCisSameAs<0, 1>,
|
|||||||
SDTCisInt<4>]>;
|
SDTCisInt<4>]>;
|
||||||
def SDT_MipsCallSeqStart : SDCallSeqStart<[SDTCisVT<0, i32>]>;
|
def SDT_MipsCallSeqStart : SDCallSeqStart<[SDTCisVT<0, i32>]>;
|
||||||
def SDT_MipsCallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>;
|
def SDT_MipsCallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>;
|
||||||
|
def SDT_MipsMAddMSub : SDTypeProfile<0, 4,
|
||||||
|
[SDTCisVT<0, i32>, SDTCisSameAs<0, 1>,
|
||||||
|
SDTCisSameAs<1, 2>,
|
||||||
|
SDTCisSameAs<2, 3>]>;
|
||||||
|
|
||||||
|
|
||||||
// Call
|
// Call
|
||||||
def MipsJmpLink : SDNode<"MipsISD::JmpLink",SDT_MipsJmpLink,
|
def MipsJmpLink : SDNode<"MipsISD::JmpLink",SDT_MipsJmpLink,
|
||||||
@ -52,6 +57,16 @@ def callseq_end : SDNode<"ISD::CALLSEQ_END", SDT_MipsCallSeqEnd,
|
|||||||
// Select Condition Code
|
// Select Condition Code
|
||||||
def MipsSelectCC : SDNode<"MipsISD::SelectCC", SDT_MipsSelectCC>;
|
def MipsSelectCC : SDNode<"MipsISD::SelectCC", SDT_MipsSelectCC>;
|
||||||
|
|
||||||
|
// MAdd*/MSub* nodes
|
||||||
|
def MipsMAdd : SDNode<"MipsISD::MAdd", SDT_MipsMAddMSub,
|
||||||
|
[SDNPOptInGlue, SDNPOutGlue]>;
|
||||||
|
def MipsMAddu : SDNode<"MipsISD::MAddu", SDT_MipsMAddMSub,
|
||||||
|
[SDNPOptInGlue, SDNPOutGlue]>;
|
||||||
|
def MipsMSub : SDNode<"MipsISD::MSub", SDT_MipsMAddMSub,
|
||||||
|
[SDNPOptInGlue, SDNPOutGlue]>;
|
||||||
|
def MipsMSubu : SDNode<"MipsISD::MSubu", SDT_MipsMAddMSub,
|
||||||
|
[SDNPOptInGlue, SDNPOutGlue]>;
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Mips Instruction Predicate Definitions.
|
// Mips Instruction Predicate Definitions.
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
@ -147,10 +162,11 @@ class ArithOverflowI<bits<6> op, string instr_asm, SDNode OpNode,
|
|||||||
!strconcat(instr_asm, "\t$dst, $b, $c"), [], IIAlu>;
|
!strconcat(instr_asm, "\t$dst, $b, $c"), [], IIAlu>;
|
||||||
|
|
||||||
// Arithmetic Multiply ADD/SUB
|
// Arithmetic Multiply ADD/SUB
|
||||||
let rd=0 in
|
let rd = 0, shamt = 0, Defs = [HI, LO], Uses = [HI, LO] in
|
||||||
class MArithR<bits<6> func, string instr_asm> :
|
class MArithR<bits<6> func, string instr_asm, SDNode op> :
|
||||||
FR<0x1c, func, (outs CPURegs:$rs), (ins CPURegs:$rt),
|
FR<0x1c, func, (outs), (ins CPURegs:$rs, CPURegs:$rt),
|
||||||
!strconcat(instr_asm, "\t$rs, $rt"), [], IIImul>;
|
!strconcat(instr_asm, "\t$rs, $rt"),
|
||||||
|
[(op CPURegs:$rs, CPURegs:$rt, LO, HI)], IIImul>;
|
||||||
|
|
||||||
// Logical
|
// Logical
|
||||||
class LogicR<bits<6> func, string instr_asm, SDNode OpNode>:
|
class LogicR<bits<6> func, string instr_asm, SDNode OpNode>:
|
||||||
@ -488,11 +504,11 @@ let addr=0 in
|
|||||||
// can be matched. It's similar to Sparc LEA_ADDRi
|
// can be matched. It's similar to Sparc LEA_ADDRi
|
||||||
def LEA_ADDiu : EffectiveAddress<"addiu\t$dst, ${addr:stackloc}">;
|
def LEA_ADDiu : EffectiveAddress<"addiu\t$dst, ${addr:stackloc}">;
|
||||||
|
|
||||||
// MADD*/MSUB* are not part of MipsI either.
|
// MADD*/MSUB*
|
||||||
//def MADD : MArithR<0x00, "madd">;
|
def MADD : MArithR<0, "madd", MipsMAdd>;
|
||||||
//def MADDU : MArithR<0x01, "maddu">;
|
def MADDU : MArithR<1, "maddu", MipsMAddu>;
|
||||||
//def MSUB : MArithR<0x04, "msub">;
|
def MSUB : MArithR<4, "msub", MipsMSub>;
|
||||||
//def MSUBU : MArithR<0x05, "msubu">;
|
def MSUBU : MArithR<5, "msubu", MipsMSubu>;
|
||||||
|
|
||||||
// MUL is a assembly macro in the current used ISAs. In recent ISA's
|
// MUL is a assembly macro in the current used ISAs. In recent ISA's
|
||||||
// it is a real instruction.
|
// it is a real instruction.
|
||||||
|
65
test/CodeGen/Mips/madd-msub.ll
Normal file
65
test/CodeGen/Mips/madd-msub.ll
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
; RUN: llc -march=mips -mcpu=4ke < %s | FileCheck %s
|
||||||
|
|
||||||
|
; CHECK: madd $5, $4
|
||||||
|
define i64 @madd1(i32 %a, i32 %b, i32 %c) nounwind readnone {
|
||||||
|
entry:
|
||||||
|
%conv = sext i32 %a to i64
|
||||||
|
%conv2 = sext i32 %b to i64
|
||||||
|
%mul = mul nsw i64 %conv2, %conv
|
||||||
|
%conv4 = sext i32 %c to i64
|
||||||
|
%add = add nsw i64 %mul, %conv4
|
||||||
|
ret i64 %add
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK: maddu $5, $4
|
||||||
|
define i64 @madd2(i32 %a, i32 %b, i32 %c) nounwind readnone {
|
||||||
|
entry:
|
||||||
|
%conv = zext i32 %a to i64
|
||||||
|
%conv2 = zext i32 %b to i64
|
||||||
|
%mul = mul nsw i64 %conv2, %conv
|
||||||
|
%conv4 = zext i32 %c to i64
|
||||||
|
%add = add nsw i64 %mul, %conv4
|
||||||
|
ret i64 %add
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK: madd $5, $4
|
||||||
|
define i64 @madd3(i32 %a, i32 %b, i64 %c) nounwind readnone {
|
||||||
|
entry:
|
||||||
|
%conv = sext i32 %a to i64
|
||||||
|
%conv2 = sext i32 %b to i64
|
||||||
|
%mul = mul nsw i64 %conv2, %conv
|
||||||
|
%add = add nsw i64 %mul, %c
|
||||||
|
ret i64 %add
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK: msub $5, $4
|
||||||
|
define i64 @msub1(i32 %a, i32 %b, i32 %c) nounwind readnone {
|
||||||
|
entry:
|
||||||
|
%conv = sext i32 %c to i64
|
||||||
|
%conv2 = sext i32 %a to i64
|
||||||
|
%conv4 = sext i32 %b to i64
|
||||||
|
%mul = mul nsw i64 %conv4, %conv2
|
||||||
|
%sub = sub nsw i64 %conv, %mul
|
||||||
|
ret i64 %sub
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK: msubu $5, $4
|
||||||
|
define i64 @msub2(i32 %a, i32 %b, i32 %c) nounwind readnone {
|
||||||
|
entry:
|
||||||
|
%conv = zext i32 %c to i64
|
||||||
|
%conv2 = zext i32 %a to i64
|
||||||
|
%conv4 = zext i32 %b to i64
|
||||||
|
%mul = mul nsw i64 %conv4, %conv2
|
||||||
|
%sub = sub nsw i64 %conv, %mul
|
||||||
|
ret i64 %sub
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK: msub $5, $4
|
||||||
|
define i64 @msub3(i32 %a, i32 %b, i64 %c) nounwind readnone {
|
||||||
|
entry:
|
||||||
|
%conv = sext i32 %a to i64
|
||||||
|
%conv3 = sext i32 %b to i64
|
||||||
|
%mul = mul nsw i64 %conv3, %conv
|
||||||
|
%sub = sub nsw i64 %c, %mul
|
||||||
|
ret i64 %sub
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user