mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-09-27 00:21:03 +00:00
Generate correct Sparc32 ABI compliant code for functions that return a struct.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@126108 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -79,6 +79,7 @@ namespace {
|
|||||||
MachineBasicBlock::iterator
|
MachineBasicBlock::iterator
|
||||||
findDelayInstr(MachineBasicBlock &MBB, MachineBasicBlock::iterator slot);
|
findDelayInstr(MachineBasicBlock &MBB, MachineBasicBlock::iterator slot);
|
||||||
|
|
||||||
|
bool needsUnimp(MachineBasicBlock::iterator I, unsigned &StructSize);
|
||||||
|
|
||||||
};
|
};
|
||||||
char Filler::ID = 0;
|
char Filler::ID = 0;
|
||||||
@@ -91,6 +92,7 @@ FunctionPass *llvm::createSparcDelaySlotFillerPass(TargetMachine &tm) {
|
|||||||
return new Filler(tm);
|
return new Filler(tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// runOnMachineBasicBlock - Fill in delay slots for the given basic block.
|
/// runOnMachineBasicBlock - Fill in delay slots for the given basic block.
|
||||||
/// We assume there is only one delay slot per delayed instruction.
|
/// We assume there is only one delay slot per delayed instruction.
|
||||||
///
|
///
|
||||||
@@ -112,6 +114,13 @@ bool Filler::runOnMachineBasicBlock(MachineBasicBlock &MBB) {
|
|||||||
BuildMI(MBB, ++J, I->getDebugLoc(), TII->get(SP::NOP));
|
BuildMI(MBB, ++J, I->getDebugLoc(), TII->get(SP::NOP));
|
||||||
else
|
else
|
||||||
MBB.splice(++J, &MBB, D);
|
MBB.splice(++J, &MBB, D);
|
||||||
|
unsigned structSize = 0;
|
||||||
|
if (needsUnimp(I, structSize)) {
|
||||||
|
MachineBasicBlock::iterator J = I;
|
||||||
|
++J; //skip the delay filler.
|
||||||
|
BuildMI(MBB, ++J, I->getDebugLoc(),
|
||||||
|
TII->get(SP::UNIMP)).addImm(structSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return Changed;
|
return Changed;
|
||||||
}
|
}
|
||||||
@@ -287,6 +296,28 @@ bool Filler::isDelayFiller(MachineBasicBlock &MBB,
|
|||||||
{
|
{
|
||||||
if (candidate == MBB.begin())
|
if (candidate == MBB.begin())
|
||||||
return false;
|
return false;
|
||||||
|
if (candidate->getOpcode() == SP::UNIMP)
|
||||||
|
return true;
|
||||||
const TargetInstrDesc &prevdesc = (--candidate)->getDesc();
|
const TargetInstrDesc &prevdesc = (--candidate)->getDesc();
|
||||||
return prevdesc.hasDelaySlot();
|
return prevdesc.hasDelaySlot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Filler::needsUnimp(MachineBasicBlock::iterator I, unsigned &StructSize)
|
||||||
|
{
|
||||||
|
if (!I->getDesc().isCall())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
unsigned structSizeOpNum = 0;
|
||||||
|
switch (I->getOpcode()) {
|
||||||
|
default: llvm_unreachable("Unknown call opcode.");
|
||||||
|
case SP::CALL: structSizeOpNum = 1; break;
|
||||||
|
case SP::JMPLrr:
|
||||||
|
case SP::JMPLri: structSizeOpNum = 2; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const MachineOperand &MO = I->getOperand(structSizeOpNum);
|
||||||
|
if (!MO.isImm())
|
||||||
|
return false;
|
||||||
|
StructSize = MO.getImm();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
@@ -16,7 +16,9 @@
|
|||||||
#include "SparcISelLowering.h"
|
#include "SparcISelLowering.h"
|
||||||
#include "SparcTargetMachine.h"
|
#include "SparcTargetMachine.h"
|
||||||
#include "SparcMachineFunctionInfo.h"
|
#include "SparcMachineFunctionInfo.h"
|
||||||
|
#include "llvm/DerivedTypes.h"
|
||||||
#include "llvm/Function.h"
|
#include "llvm/Function.h"
|
||||||
|
#include "llvm/Module.h"
|
||||||
#include "llvm/CodeGen/CallingConvLower.h"
|
#include "llvm/CodeGen/CallingConvLower.h"
|
||||||
#include "llvm/CodeGen/MachineFrameInfo.h"
|
#include "llvm/CodeGen/MachineFrameInfo.h"
|
||||||
#include "llvm/CodeGen/MachineFunction.h"
|
#include "llvm/CodeGen/MachineFunction.h"
|
||||||
@@ -116,6 +118,8 @@ SparcTargetLowering::LowerReturn(SDValue Chain,
|
|||||||
// Guarantee that all emitted copies are stuck together with flags.
|
// Guarantee that all emitted copies are stuck together with flags.
|
||||||
Flag = Chain.getValue(1);
|
Flag = Chain.getValue(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned RetAddrOffset = 8; //Call Inst + Delay Slot
|
||||||
// If the function returns a struct, copy the SRetReturnReg to I0
|
// If the function returns a struct, copy the SRetReturnReg to I0
|
||||||
if (MF.getFunction()->hasStructRetAttr()) {
|
if (MF.getFunction()->hasStructRetAttr()) {
|
||||||
SparcMachineFunctionInfo *SFI = MF.getInfo<SparcMachineFunctionInfo>();
|
SparcMachineFunctionInfo *SFI = MF.getInfo<SparcMachineFunctionInfo>();
|
||||||
@@ -127,11 +131,16 @@ SparcTargetLowering::LowerReturn(SDValue Chain,
|
|||||||
Flag = Chain.getValue(1);
|
Flag = Chain.getValue(1);
|
||||||
if (MF.getRegInfo().liveout_empty())
|
if (MF.getRegInfo().liveout_empty())
|
||||||
MF.getRegInfo().addLiveOut(SP::I0);
|
MF.getRegInfo().addLiveOut(SP::I0);
|
||||||
|
RetAddrOffset = 12; // CallInst + Delay Slot + Unimp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SDValue RetAddrOffsetNode = DAG.getConstant(RetAddrOffset, MVT::i32);
|
||||||
|
|
||||||
if (Flag.getNode())
|
if (Flag.getNode())
|
||||||
return DAG.getNode(SPISD::RET_FLAG, dl, MVT::Other, Chain, Flag);
|
return DAG.getNode(SPISD::RET_FLAG, dl, MVT::Other, Chain,
|
||||||
return DAG.getNode(SPISD::RET_FLAG, dl, MVT::Other, Chain);
|
RetAddrOffsetNode, Flag);
|
||||||
|
return DAG.getNode(SPISD::RET_FLAG, dl, MVT::Other, Chain,
|
||||||
|
RetAddrOffsetNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// LowerFormalArguments - V8 uses a very simple ABI, where all values are
|
/// LowerFormalArguments - V8 uses a very simple ABI, where all values are
|
||||||
@@ -393,6 +402,7 @@ SparcTargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
|||||||
SmallVector<SDValue, 8> MemOpChains;
|
SmallVector<SDValue, 8> MemOpChains;
|
||||||
|
|
||||||
const unsigned StackOffset = 92;
|
const unsigned StackOffset = 92;
|
||||||
|
bool hasStructRetAttr = false;
|
||||||
// Walk the register/memloc assignments, inserting copies/loads.
|
// Walk the register/memloc assignments, inserting copies/loads.
|
||||||
for (unsigned i = 0, realArgIdx = 0, byvalArgIdx = 0, e = ArgLocs.size();
|
for (unsigned i = 0, realArgIdx = 0, byvalArgIdx = 0, e = ArgLocs.size();
|
||||||
i != e;
|
i != e;
|
||||||
@@ -433,6 +443,7 @@ SparcTargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
|||||||
MemOpChains.push_back(DAG.getStore(Chain, dl, Arg, PtrOff,
|
MemOpChains.push_back(DAG.getStore(Chain, dl, Arg, PtrOff,
|
||||||
MachinePointerInfo(),
|
MachinePointerInfo(),
|
||||||
false, false, 0));
|
false, false, 0));
|
||||||
|
hasStructRetAttr = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -546,6 +557,8 @@ SparcTargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
|||||||
InFlag = Chain.getValue(1);
|
InFlag = Chain.getValue(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned SRetArgSize = (hasStructRetAttr)? getSRetArgSize(DAG, Callee):0;
|
||||||
|
|
||||||
// If the callee is a GlobalAddress node (quite common, every direct call is)
|
// If the callee is a GlobalAddress node (quite common, every direct call is)
|
||||||
// turn it into a TargetGlobalAddress node so that legalize doesn't hack it.
|
// turn it into a TargetGlobalAddress node so that legalize doesn't hack it.
|
||||||
// Likewise ExternalSymbol -> TargetExternalSymbol.
|
// Likewise ExternalSymbol -> TargetExternalSymbol.
|
||||||
@@ -559,6 +572,8 @@ SparcTargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
|||||||
SmallVector<SDValue, 8> Ops;
|
SmallVector<SDValue, 8> Ops;
|
||||||
Ops.push_back(Chain);
|
Ops.push_back(Chain);
|
||||||
Ops.push_back(Callee);
|
Ops.push_back(Callee);
|
||||||
|
if (hasStructRetAttr)
|
||||||
|
Ops.push_back(DAG.getTargetConstant(SRetArgSize, MVT::i32));
|
||||||
for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) {
|
for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) {
|
||||||
unsigned Reg = RegsToPass[i].first;
|
unsigned Reg = RegsToPass[i].first;
|
||||||
if (Reg >= SP::I0 && Reg <= SP::I7)
|
if (Reg >= SP::I0 && Reg <= SP::I7)
|
||||||
@@ -600,7 +615,29 @@ SparcTargetLowering::LowerCall(SDValue Chain, SDValue Callee,
|
|||||||
return Chain;
|
return Chain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned
|
||||||
|
SparcTargetLowering::getSRetArgSize(SelectionDAG &DAG, SDValue Callee) const
|
||||||
|
{
|
||||||
|
const Function *CalleeFn = 0;
|
||||||
|
if (GlobalAddressSDNode *G = dyn_cast<GlobalAddressSDNode>(Callee)) {
|
||||||
|
CalleeFn = dyn_cast<Function>(G->getGlobal());
|
||||||
|
} else if (ExternalSymbolSDNode *E =
|
||||||
|
dyn_cast<ExternalSymbolSDNode>(Callee)) {
|
||||||
|
const Function *Fn = DAG.getMachineFunction().getFunction();
|
||||||
|
const Module *M = Fn->getParent();
|
||||||
|
CalleeFn = M->getFunction(E->getSymbol());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CalleeFn)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
assert(CalleeFn->hasStructRetAttr() &&
|
||||||
|
"Callee does not have the StructRet attribute.");
|
||||||
|
|
||||||
|
const PointerType *Ty = cast<PointerType>(CalleeFn->arg_begin()->getType());
|
||||||
|
const Type *ElementTy = Ty->getElementType();
|
||||||
|
return getTargetData()->getTypeAllocSize(ElementTy);
|
||||||
|
}
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// TargetLowering Implementation
|
// TargetLowering Implementation
|
||||||
|
@@ -101,6 +101,8 @@ namespace llvm {
|
|||||||
|
|
||||||
SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
|
SDValue LowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
|
||||||
SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG) const;
|
SDValue LowerConstantPool(SDValue Op, SelectionDAG &DAG) const;
|
||||||
|
|
||||||
|
unsigned getSRetArgSize(SelectionDAG &DAG, SDValue Callee) const;
|
||||||
};
|
};
|
||||||
} // end namespace llvm
|
} // end namespace llvm
|
||||||
|
|
||||||
|
@@ -124,7 +124,8 @@ def call : SDNode<"SPISD::CALL", SDT_SPCall,
|
|||||||
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
|
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
|
||||||
SDNPVariadic]>;
|
SDNPVariadic]>;
|
||||||
|
|
||||||
def retflag : SDNode<"SPISD::RET_FLAG", SDTNone,
|
def SDT_SPRet : SDTypeProfile<0, 1, [SDTCisVT<0, i32>]>;
|
||||||
|
def retflag : SDNode<"SPISD::RET_FLAG", SDT_SPRet,
|
||||||
[SDNPHasChain, SDNPOptInGlue]>;
|
[SDNPHasChain, SDNPOptInGlue]>;
|
||||||
|
|
||||||
def flushw : SDNode<"SPISD::FLUSHW", SDTNone,
|
def flushw : SDNode<"SPISD::FLUSHW", SDTNone,
|
||||||
@@ -232,6 +233,9 @@ let hasSideEffects = 1, mayStore = 1 in {
|
|||||||
[(flushw)]>;
|
[(flushw)]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def UNIMP : F2_1<0b000, (outs), (ins i32imm:$val),
|
||||||
|
"unimp $val", []>;
|
||||||
|
|
||||||
// FpMOVD/FpNEGD/FpABSD - These are lowered to single-precision ops by the
|
// FpMOVD/FpNEGD/FpABSD - These are lowered to single-precision ops by the
|
||||||
// fpmover pass.
|
// fpmover pass.
|
||||||
let Predicates = [HasNoV9] in { // Only emit these in V8 mode.
|
let Predicates = [HasNoV9] in { // Only emit these in V8 mode.
|
||||||
@@ -292,11 +296,13 @@ let usesCustomInserter = 1, Uses = [FCC] in {
|
|||||||
// Section A.3 - Synthetic Instructions, p. 85
|
// Section A.3 - Synthetic Instructions, p. 85
|
||||||
// special cases of JMPL:
|
// special cases of JMPL:
|
||||||
let isReturn = 1, isTerminator = 1, hasDelaySlot = 1, isBarrier = 1 in {
|
let isReturn = 1, isTerminator = 1, hasDelaySlot = 1, isBarrier = 1 in {
|
||||||
let rd = O7.Num, rs1 = G0.Num, simm13 = 8 in
|
let rd = O7.Num, rs1 = G0.Num in
|
||||||
def RETL: F3_2<2, 0b111000, (outs), (ins), "retl", [(retflag)]>;
|
def RETL: F3_2<2, 0b111000, (outs), (ins i32imm:$val),
|
||||||
|
"jmp %o7+$val", [(retflag simm13:$val)]>;
|
||||||
|
|
||||||
let rd = I7.Num, rs1 = G0.Num, simm13 = 8 in
|
let rd = I7.Num, rs1 = G0.Num in
|
||||||
def RET: F3_2<2, 0b111000, (outs), (ins), "ret", []>;
|
def RET: F3_2<2, 0b111000, (outs), (ins i32imm:$val),
|
||||||
|
"jmp %i7+$val", []>;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Section B.1 - Load Integer Instructions, p. 90
|
// Section B.1 - Load Integer Instructions, p. 90
|
||||||
|
@@ -7,7 +7,7 @@ entry:
|
|||||||
; CHECK: test
|
; CHECK: test
|
||||||
; CHECK: call bar
|
; CHECK: call bar
|
||||||
; CHECK-NOT: nop
|
; CHECK-NOT: nop
|
||||||
; CHECK: ret
|
; CHECK: jmp
|
||||||
; CHECK-NEXT: restore
|
; CHECK-NEXT: restore
|
||||||
%0 = tail call i32 @bar(i32 %a) nounwind
|
%0 = tail call i32 @bar(i32 %a) nounwind
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
@@ -18,7 +18,7 @@ entry:
|
|||||||
; CHECK: test_jmpl
|
; CHECK: test_jmpl
|
||||||
; CHECK: call
|
; CHECK: call
|
||||||
; CHECK-NOT: nop
|
; CHECK-NOT: nop
|
||||||
; CHECK: ret
|
; CHECK: jmp
|
||||||
; CHECK-NEXT: restore
|
; CHECK-NEXT: restore
|
||||||
%0 = tail call i32 %f(i32 %a, i32 %b) nounwind
|
%0 = tail call i32 %f(i32 %a, i32 %b) nounwind
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
@@ -47,7 +47,7 @@ bb: ; preds = %entry, %bb
|
|||||||
|
|
||||||
bb5: ; preds = %bb, %entry
|
bb5: ; preds = %bb, %entry
|
||||||
%a_addr.1.lcssa = phi i32 [ %a, %entry ], [ %a_addr.0, %bb ]
|
%a_addr.1.lcssa = phi i32 [ %a, %entry ], [ %a_addr.0, %bb ]
|
||||||
;CHECK: ret
|
;CHECK: jmp
|
||||||
;CHECK-NEXT: restore
|
;CHECK-NEXT: restore
|
||||||
ret i32 %a_addr.1.lcssa
|
ret i32 %a_addr.1.lcssa
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,7 @@ entry:
|
|||||||
;CHECK: make_foo
|
;CHECK: make_foo
|
||||||
;CHECK: ld [%fp+64], {{.+}}
|
;CHECK: ld [%fp+64], {{.+}}
|
||||||
;CHECK: or {{.+}}, {{.+}}, %i0
|
;CHECK: or {{.+}}, {{.+}}, %i0
|
||||||
;CHECK: ret
|
;CHECK: jmp %i7+12
|
||||||
%0 = getelementptr inbounds %struct.foo_t* %agg.result, i32 0, i32 0
|
%0 = getelementptr inbounds %struct.foo_t* %agg.result, i32 0, i32 0
|
||||||
store i32 %a, i32* %0, align 4
|
store i32 %a, i32* %0, align 4
|
||||||
%1 = getelementptr inbounds %struct.foo_t* %agg.result, i32 0, i32 1
|
%1 = getelementptr inbounds %struct.foo_t* %agg.result, i32 0, i32 1
|
||||||
@@ -22,6 +22,7 @@ entry:
|
|||||||
;CHECK: test
|
;CHECK: test
|
||||||
;CHECK: st {{.+}}, [%sp+64]
|
;CHECK: st {{.+}}, [%sp+64]
|
||||||
;CHECK: make_foo
|
;CHECK: make_foo
|
||||||
|
;CHECK: unimp 12
|
||||||
%f = alloca %struct.foo_t, align 8
|
%f = alloca %struct.foo_t, align 8
|
||||||
call void @make_foo(%struct.foo_t* noalias sret %f, i32 10, i32 20, i32 30) nounwind
|
call void @make_foo(%struct.foo_t* noalias sret %f, i32 10, i32 20, i32 30) nounwind
|
||||||
%0 = getelementptr inbounds %struct.foo_t* %f, i32 0, i32 0
|
%0 = getelementptr inbounds %struct.foo_t* %f, i32 0, i32 0
|
||||||
|
Reference in New Issue
Block a user