mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-01-24 08:33:39 +00:00
Change the model for FP Stack return to use fp operands on the
RET instruction instead of using FpSET_ST0_32. This also generalizes the code to handling returning of multiple FP results. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@48209 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
c5733ac5d3
commit
447ff68c08
@ -75,26 +75,31 @@ namespace {
|
|||||||
cerr << "\n";
|
cerr << "\n";
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
|
/// isStackEmpty - Return true if the FP stack is empty.
|
||||||
|
bool isStackEmpty() const {
|
||||||
|
return StackTop == 0;
|
||||||
|
}
|
||||||
|
|
||||||
// getSlot - Return the stack slot number a particular register number is
|
// getSlot - Return the stack slot number a particular register number is
|
||||||
// in...
|
// in.
|
||||||
unsigned getSlot(unsigned RegNo) const {
|
unsigned getSlot(unsigned RegNo) const {
|
||||||
assert(RegNo < 8 && "Regno out of range!");
|
assert(RegNo < 8 && "Regno out of range!");
|
||||||
return RegMap[RegNo];
|
return RegMap[RegNo];
|
||||||
}
|
}
|
||||||
|
|
||||||
// getStackEntry - Return the X86::FP<n> register in register ST(i)
|
// getStackEntry - Return the X86::FP<n> register in register ST(i).
|
||||||
unsigned getStackEntry(unsigned STi) const {
|
unsigned getStackEntry(unsigned STi) const {
|
||||||
assert(STi < StackTop && "Access past stack top!");
|
assert(STi < StackTop && "Access past stack top!");
|
||||||
return Stack[StackTop-1-STi];
|
return Stack[StackTop-1-STi];
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSTReg - Return the X86::ST(i) register which contains the specified
|
// getSTReg - Return the X86::ST(i) register which contains the specified
|
||||||
// FP<RegNo> register
|
// FP<RegNo> register.
|
||||||
unsigned getSTReg(unsigned RegNo) const {
|
unsigned getSTReg(unsigned RegNo) const {
|
||||||
return StackTop - 1 - getSlot(RegNo) + llvm::X86::ST0;
|
return StackTop - 1 - getSlot(RegNo) + llvm::X86::ST0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// pushReg - Push the specified FP<n> register onto the stack
|
// pushReg - Push the specified FP<n> register onto the stack.
|
||||||
void pushReg(unsigned Reg) {
|
void pushReg(unsigned Reg) {
|
||||||
assert(Reg < 8 && "Register number out of range!");
|
assert(Reg < 8 && "Register number out of range!");
|
||||||
assert(StackTop < 8 && "Stack overflow!");
|
assert(StackTop < 8 && "Stack overflow!");
|
||||||
@ -103,23 +108,23 @@ namespace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool isAtTop(unsigned RegNo) const { return getSlot(RegNo) == StackTop-1; }
|
bool isAtTop(unsigned RegNo) const { return getSlot(RegNo) == StackTop-1; }
|
||||||
void moveToTop(unsigned RegNo, MachineBasicBlock::iterator &I) {
|
void moveToTop(unsigned RegNo, MachineBasicBlock::iterator I) {
|
||||||
if (!isAtTop(RegNo)) {
|
if (isAtTop(RegNo)) return;
|
||||||
|
|
||||||
unsigned STReg = getSTReg(RegNo);
|
unsigned STReg = getSTReg(RegNo);
|
||||||
unsigned RegOnTop = getStackEntry(0);
|
unsigned RegOnTop = getStackEntry(0);
|
||||||
|
|
||||||
// Swap the slots the regs are in
|
// Swap the slots the regs are in.
|
||||||
std::swap(RegMap[RegNo], RegMap[RegOnTop]);
|
std::swap(RegMap[RegNo], RegMap[RegOnTop]);
|
||||||
|
|
||||||
// Swap stack slot contents
|
// Swap stack slot contents.
|
||||||
assert(RegMap[RegOnTop] < StackTop);
|
assert(RegMap[RegOnTop] < StackTop);
|
||||||
std::swap(Stack[RegMap[RegOnTop]], Stack[StackTop-1]);
|
std::swap(Stack[RegMap[RegOnTop]], Stack[StackTop-1]);
|
||||||
|
|
||||||
// Emit an fxch to update the runtime processors version of the state
|
// Emit an fxch to update the runtime processors version of the state.
|
||||||
BuildMI(*MBB, I, TII->get(X86::XCH_F)).addReg(STReg);
|
BuildMI(*MBB, I, TII->get(X86::XCH_F)).addReg(STReg);
|
||||||
NumFXCH++;
|
NumFXCH++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
void duplicateToTop(unsigned RegNo, unsigned AsReg, MachineInstr *I) {
|
void duplicateToTop(unsigned RegNo, unsigned AsReg, MachineInstr *I) {
|
||||||
unsigned STReg = getSTReg(RegNo);
|
unsigned STReg = getSTReg(RegNo);
|
||||||
@ -268,7 +273,7 @@ bool FPS::processBasicBlock(MachineFunction &MF, MachineBasicBlock &BB) {
|
|||||||
Changed = true;
|
Changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(StackTop == 0 && "Stack not empty at end of basic block?");
|
assert(isStackEmpty() && "Stack not empty at end of basic block?");
|
||||||
return Changed;
|
return Changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -960,6 +965,83 @@ void FPS::handleSpecialFP(MachineBasicBlock::iterator &I) {
|
|||||||
duplicateToTop(SrcReg, DestReg, I);
|
duplicateToTop(SrcReg, DestReg, I);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case X86::RET:
|
||||||
|
case X86::RETI:
|
||||||
|
// If RET has an FP register use operand, pass the first one in ST(0) and
|
||||||
|
// the second one in ST(1).
|
||||||
|
if (isStackEmpty()) return; // Quick check to see if any are possible.
|
||||||
|
|
||||||
|
// Find the register operands.
|
||||||
|
unsigned FirstFPRegOp = ~0U, SecondFPRegOp = ~0U;
|
||||||
|
|
||||||
|
for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
|
||||||
|
MachineOperand &Op = MI->getOperand(i);
|
||||||
|
if (!Op.isReg() || Op.getReg() < X86::FP0 || Op.getReg() > X86::FP6)
|
||||||
|
continue;
|
||||||
|
assert(Op.isUse() && Op.isKill() &&
|
||||||
|
"Ret only defs operands, and values aren't live beyond it");
|
||||||
|
|
||||||
|
if (FirstFPRegOp == ~0U)
|
||||||
|
FirstFPRegOp = getFPReg(Op);
|
||||||
|
else {
|
||||||
|
assert(SecondFPRegOp == ~0U && "More than two fp operands!");
|
||||||
|
SecondFPRegOp = getFPReg(Op);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the operand so that later passes don't see it.
|
||||||
|
MI->RemoveOperand(i);
|
||||||
|
--i, --e;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There are only four possibilities here:
|
||||||
|
// 1) we are returning a single FP value. In this case, it has to be in
|
||||||
|
// ST(0) already, so just declare success by removing the value from the
|
||||||
|
// FP Stack.
|
||||||
|
if (SecondFPRegOp == ~0U) {
|
||||||
|
// Assert that the top of stack contains the right FP register.
|
||||||
|
assert(StackTop == 1 && FirstFPRegOp == getStackEntry(0) &&
|
||||||
|
"Top of stack not the right register for RET!");
|
||||||
|
|
||||||
|
// Ok, everything is good, mark the value as not being on the stack
|
||||||
|
// anymore so that our assertion about the stack being empty at end of
|
||||||
|
// block doesn't fire.
|
||||||
|
StackTop = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(0 && "TODO: This code should work, but has never been tested."
|
||||||
|
"Test it when we have multiple FP return values working");
|
||||||
|
|
||||||
|
// Otherwise, we are returning two values:
|
||||||
|
// 2) If returning the same value for both, we only have one thing in the FP
|
||||||
|
// stack. Consider: RET FP1, FP1
|
||||||
|
if (StackTop == 1) {
|
||||||
|
assert(FirstFPRegOp == SecondFPRegOp && FirstFPRegOp == getStackEntry(0)&&
|
||||||
|
"Stack misconfiguration for RET!");
|
||||||
|
|
||||||
|
// Duplicate the TOS so that we return it twice. Just pick some other FPx
|
||||||
|
// register to hold it.
|
||||||
|
unsigned NewReg = (FirstFPRegOp+1)%7;
|
||||||
|
duplicateToTop(FirstFPRegOp, NewReg, MI);
|
||||||
|
FirstFPRegOp = NewReg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Okay we know we have two different FPx operands now:
|
||||||
|
assert(StackTop == 2 && "Must have two values live!");
|
||||||
|
|
||||||
|
/// 3) If SecondFPRegOp is currently in ST(0) and FirstFPRegOp is currently
|
||||||
|
/// in ST(1). In this case, emit an fxch.
|
||||||
|
if (getStackEntry(0) == SecondFPRegOp) {
|
||||||
|
assert(getStackEntry(1) == FirstFPRegOp && "Unknown regs live");
|
||||||
|
moveToTop(FirstFPRegOp, MI);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 4) Finally, FirstFPRegOp must be in ST(0) and SecondFPRegOp must be in
|
||||||
|
/// ST(1). Just remove both from our understanding of the stack and return.
|
||||||
|
assert(getStackEntry(0) == FirstFPRegOp && "Unknown regs live");
|
||||||
|
assert(getStackEntry(0) == SecondFPRegOp && "Unknown regs live");
|
||||||
|
StackTop = 0;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1168,6 +1168,35 @@ SDNode *X86DAGToDAGISel::Select(SDOperand N) {
|
|||||||
case X86ISD::GlobalBaseReg:
|
case X86ISD::GlobalBaseReg:
|
||||||
return getGlobalBaseReg();
|
return getGlobalBaseReg();
|
||||||
|
|
||||||
|
// FIXME: This is a workaround for a tblgen problem: rdar://5791600
|
||||||
|
case X86ISD::RET_FLAG:
|
||||||
|
if (ConstantSDNode *Amt = dyn_cast<ConstantSDNode>(N.getOperand(1))) {
|
||||||
|
if (Amt->getSignExtended() != 0) break;
|
||||||
|
|
||||||
|
// Match (X86retflag 0).
|
||||||
|
SDOperand Chain = N.getOperand(0);
|
||||||
|
bool HasInFlag = N.getOperand(N.getNumOperands()-1).getValueType()
|
||||||
|
== MVT::Flag;
|
||||||
|
SmallVector<SDOperand, 8> Ops0;
|
||||||
|
AddToISelQueue(Chain);
|
||||||
|
SDOperand InFlag(0, 0);
|
||||||
|
if (HasInFlag) {
|
||||||
|
InFlag = N.getOperand(N.getNumOperands()-1);
|
||||||
|
AddToISelQueue(InFlag);
|
||||||
|
}
|
||||||
|
for (unsigned i = 2, e = N.getNumOperands()-(HasInFlag?1:0); i != e;
|
||||||
|
++i) {
|
||||||
|
AddToISelQueue(N.getOperand(i));
|
||||||
|
Ops0.push_back(N.getOperand(i));
|
||||||
|
}
|
||||||
|
Ops0.push_back(Chain);
|
||||||
|
if (HasInFlag)
|
||||||
|
Ops0.push_back(InFlag);
|
||||||
|
return CurDAG->getTargetNode(X86::RET, MVT::Other,
|
||||||
|
&Ops0[0], Ops0.size());
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case X86ISD::FP_GET_ST0_ST1: {
|
case X86ISD::FP_GET_ST0_ST1: {
|
||||||
SDOperand Chain = N.getOperand(0);
|
SDOperand Chain = N.getOperand(0);
|
||||||
SDOperand InFlag = N.getOperand(1);
|
SDOperand InFlag = N.getOperand(1);
|
||||||
|
@ -853,27 +853,41 @@ SDOperand X86TargetLowering::LowerRET(SDOperand Op, SelectionDAG &DAG) {
|
|||||||
// Regular return.
|
// Regular return.
|
||||||
SDOperand Flag;
|
SDOperand Flag;
|
||||||
|
|
||||||
|
SmallVector<SDOperand, 6> RetOps;
|
||||||
|
RetOps.push_back(Chain); // Operand #0 = Chain (updated below)
|
||||||
|
// Operand #1 = Bytes To Pop
|
||||||
|
RetOps.push_back(DAG.getConstant(getBytesToPopOnReturn(), MVT::i16));
|
||||||
|
|
||||||
// Copy the result values into the output registers.
|
// Copy the result values into the output registers.
|
||||||
for (unsigned i = 0; i != RVLocs.size(); ++i) {
|
for (unsigned i = 0; i != RVLocs.size(); ++i) {
|
||||||
CCValAssign &VA = RVLocs[i];
|
CCValAssign &VA = RVLocs[i];
|
||||||
assert(VA.isRegLoc() && "Can only return in registers!");
|
assert(VA.isRegLoc() && "Can only return in registers!");
|
||||||
SDOperand ValToCopy = Op.getOperand(i*2+1);
|
SDOperand ValToCopy = Op.getOperand(i*2+1);
|
||||||
|
|
||||||
|
// Returns in ST0/ST1 are handled specially: these are pushed as operands to
|
||||||
|
// the RET instruction and handled by the FP Stackifier.
|
||||||
|
if (RVLocs[i].getLocReg() == X86::ST0 ||
|
||||||
|
RVLocs[i].getLocReg() == X86::ST1) {
|
||||||
// If this is a copy from an xmm register to ST(0), use an FPExtend to
|
// If this is a copy from an xmm register to ST(0), use an FPExtend to
|
||||||
// change the value to the FP stack register class.
|
// change the value to the FP stack register class.
|
||||||
if (RVLocs[i].getLocReg() == X86::ST0 &&
|
if (isScalarFPTypeInSSEReg(RVLocs[i].getValVT()))
|
||||||
isScalarFPTypeInSSEReg(RVLocs[i].getValVT()))
|
|
||||||
ValToCopy = DAG.getNode(ISD::FP_EXTEND, MVT::f80, ValToCopy);
|
ValToCopy = DAG.getNode(ISD::FP_EXTEND, MVT::f80, ValToCopy);
|
||||||
|
RetOps.push_back(ValToCopy);
|
||||||
|
// Don't emit a copytoreg.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
Chain = DAG.getCopyToReg(Chain, VA.getLocReg(), ValToCopy, Flag);
|
Chain = DAG.getCopyToReg(Chain, VA.getLocReg(), ValToCopy, Flag);
|
||||||
Flag = Chain.getValue(1);
|
Flag = Chain.getValue(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
SDOperand BytesToPop = DAG.getConstant(getBytesToPopOnReturn(), MVT::i16);
|
RetOps[0] = Chain; // Update chain.
|
||||||
|
|
||||||
|
// Add the flag if we have it.
|
||||||
if (Flag.Val)
|
if (Flag.Val)
|
||||||
return DAG.getNode(X86ISD::RET_FLAG, MVT::Other, Chain, BytesToPop, Flag);
|
RetOps.push_back(Flag);
|
||||||
else
|
|
||||||
return DAG.getNode(X86ISD::RET_FLAG, MVT::Other, Chain, BytesToPop);
|
return DAG.getNode(X86ISD::RET_FLAG, MVT::Other, &RetOps[0], RetOps.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@ def SDTX86cas : SDTypeProfile<0, 3, [SDTCisPtrTy<0>, SDTCisInt<1>,
|
|||||||
SDTCisVT<2, i8>]>;
|
SDTCisVT<2, i8>]>;
|
||||||
def SDTX86cas8 : SDTypeProfile<0, 1, [SDTCisPtrTy<0>]>;
|
def SDTX86cas8 : SDTypeProfile<0, 1, [SDTCisPtrTy<0>]>;
|
||||||
|
|
||||||
def SDTX86Ret : SDTypeProfile<0, 1, [SDTCisVT<0, i16>]>;
|
def SDTX86Ret : SDTypeProfile<0, -1, [SDTCisVT<0, i16>]>;
|
||||||
|
|
||||||
def SDT_X86CallSeqStart : SDCallSeqStart<[ SDTCisVT<0, i32> ]>;
|
def SDT_X86CallSeqStart : SDCallSeqStart<[ SDTCisVT<0, i32> ]>;
|
||||||
def SDT_X86CallSeqEnd : SDCallSeqEnd<[ SDTCisVT<0, i32>,
|
def SDT_X86CallSeqEnd : SDCallSeqEnd<[ SDTCisVT<0, i32>,
|
||||||
@ -269,8 +269,8 @@ def and_su : PatFrag<(ops node:$lhs, node:$rhs), (and node:$lhs, node:$rhs), [{
|
|||||||
// ADJCALLSTACKDOWN/UP implicitly use/def ESP because they may be expanded into
|
// ADJCALLSTACKDOWN/UP implicitly use/def ESP because they may be expanded into
|
||||||
// a stack adjustment and the codegen must know that they may modify the stack
|
// a stack adjustment and the codegen must know that they may modify the stack
|
||||||
// pointer before prolog-epilog rewriting occurs.
|
// pointer before prolog-epilog rewriting occurs.
|
||||||
// Pessimistically assume ADJCALLSTACKDOWN / ADJCALLSTACKUP will become sub / add
|
// Pessimistically assume ADJCALLSTACKDOWN / ADJCALLSTACKUP will become
|
||||||
// which can clobber EFLAGS.
|
// sub / add which can clobber EFLAGS.
|
||||||
let Defs = [ESP, EFLAGS], Uses = [ESP] in {
|
let Defs = [ESP, EFLAGS], Uses = [ESP] in {
|
||||||
def ADJCALLSTACKDOWN : I<0, Pseudo, (outs), (ins i32imm:$amt),
|
def ADJCALLSTACKDOWN : I<0, Pseudo, (outs), (ins i32imm:$amt),
|
||||||
"#ADJCALLSTACKDOWN",
|
"#ADJCALLSTACKDOWN",
|
||||||
@ -306,9 +306,12 @@ let neverHasSideEffects = 1, isNotDuplicable = 1 in
|
|||||||
|
|
||||||
// Return instructions.
|
// Return instructions.
|
||||||
let isTerminator = 1, isReturn = 1, isBarrier = 1,
|
let isTerminator = 1, isReturn = 1, isBarrier = 1,
|
||||||
hasCtrlDep = 1 in {
|
hasCtrlDep = 1, FPForm = SpecialFP, FPFormBits = SpecialFP.Value in {
|
||||||
def RET : I<0xC3, RawFrm, (outs), (ins), "ret", [(X86retflag 0)]>;
|
def RET : I <0xC3, RawFrm, (outs), (ins variable_ops),
|
||||||
def RETI : Ii16<0xC2, RawFrm, (outs), (ins i16imm:$amt), "ret\t$amt",
|
"ret",
|
||||||
|
[/*(X86retflag 0)*/ /*FIXME: Disabled: rdar://5791600*/]>;
|
||||||
|
def RETI : Ii16<0xC2, RawFrm, (outs), (ins i16imm:$amt, variable_ops),
|
||||||
|
"ret\t$amt",
|
||||||
[(X86retflag imm:$amt)]>;
|
[(X86retflag imm:$amt)]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user