1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-24 11:42:57 +01:00

Correctly handle calls to functions which are further away than 2**32 bits will

allow, i.e. make a sequence of instructions to enable an indirect call using
jump-and-link and 2 temporary registers (which we save and ultimately restore).

Warning: if the delay slot of a function call is used to do meaningful work and
not just a NOP, this behavior is incorrect. However, the Sparc backend does not
yet utilize the delay slots effectively, so it is not necessary to make an
overly complicated algorithm for something that's not used.

llvm-svn: 7178
This commit is contained in:
Misha Brukman 2003-07-15 19:09:43 +00:00
parent 45dc28808e
commit 8cd8690ad5
2 changed files with 97 additions and 6 deletions

View File

@ -414,6 +414,79 @@ SparcV9CodeEmitter::getRealRegNum(unsigned fakeReg,
}
// WARNING: if the call used the delay slot to do meaningful work, that's not
// being accounted for, and the behavior will be incorrect!!
inline void SparcV9CodeEmitter::emitFarCall(uint64_t Target) {
static const unsigned i1 = SparcIntRegClass::i1, i2 = SparcIntRegClass::i2,
i7 = SparcIntRegClass::i7,
o6 = SparcIntRegClass::o6, g0 = SparcIntRegClass::g0;
//
// Save %i1, %i2 to the stack so we can form a 64-bit constant in %i2
//
// stx %i1, [%sp + 2119] ;; save %i1 to the stack, used as temp
MachineInstr *STX = BuildMI(V9::STXi, 3).addReg(i1).addReg(o6).addSImm(2119);
emitWord(getBinaryCodeForInstr(*STX));
delete STX;
// stx %i2, [%sp + 2127] ;; save %i2 to the stack
STX = BuildMI(V9::STXi, 3).addReg(i2).addReg(o6).addSImm(2127);
emitWord(getBinaryCodeForInstr(*STX));
delete STX;
//
// Get address to branch into %i2, using %i1 as a temporary
//
// sethi %uhi(Target), %i1 ;; get upper 22 bits of Target into %i1
MachineInstr *SH = BuildMI(V9::SETHI, 2).addSImm(Target >> 42).addReg(i1);
emitWord(getBinaryCodeForInstr(*SH));
delete SH;
// or %i1, %ulo(Target), %i1 ;; get 10 lower bits of upper word into %1
MachineInstr *OR = BuildMI(V9::ORi, 3)
.addReg(i1).addSImm((Target >> 32) & 0x03ff).addReg(i1);
emitWord(getBinaryCodeForInstr(*OR));
delete OR;
// sllx %i1, 32, %i1 ;; shift those 10 bits to the upper word
MachineInstr *SL = BuildMI(V9::SLLXi6, 3).addReg(i1).addSImm(32).addReg(i1);
emitWord(getBinaryCodeForInstr(*SL));
delete SL;
// sethi %hi(Target), %i2 ;; extract bits 10-31 into the dest reg
SH = BuildMI(V9::SETHI, 2).addSImm((Target >> 10) & 0x03fffff).addReg(i2);
emitWord(getBinaryCodeForInstr(*SH));
delete SH;
// or %i1, %i2, %i2 ;; get upper word (in %i1) into %i2
OR = BuildMI(V9::ORr, 3).addReg(i1).addReg(i2).addReg(i2);
emitWord(getBinaryCodeForInstr(*OR));
delete OR;
// or %i2, %lo(Target), %i2 ;; get lowest 10 bits of Target into %i2
OR = BuildMI(V9::ORi, 3).addReg(i2).addSImm(Target & 0x03ff).addReg(i2);
emitWord(getBinaryCodeForInstr(*OR));
delete OR;
// ldx [%sp + 2119], %i1 ;; restore %i1 -> 2119 = BIAS(2047) + 72
MachineInstr *LDX = BuildMI(V9::LDXi, 3).addReg(o6).addSImm(2119).addReg(i1);
emitWord(getBinaryCodeForInstr(*LDX));
delete LDX;
// jmpl %i2, %g0, %07 ;; indirect call on %i2
MachineInstr *J = BuildMI(V9::JMPLRETr, 3).addReg(i2).addReg(g0).addReg(07);
emitWord(getBinaryCodeForInstr(*J));
delete J;
// ldx [%sp + 2127], %i2 ;; restore %i2 -> 2127 = BIAS(2047) + 80
LDX = BuildMI(V9::LDXi, 3).addReg(o6).addSImm(2127).addReg(i2);
emitWord(getBinaryCodeForInstr(*LDX));
delete LDX;
}
int64_t SparcV9CodeEmitter::getMachineOpValue(MachineInstr &MI,
MachineOperand &MO) {
int64_t rv = 0; // Return value; defaults to 0 for unhandled cases
@ -479,10 +552,18 @@ int64_t SparcV9CodeEmitter::getMachineOpValue(MachineInstr &MI,
int64_t CurrPC = MCE.getCurrentPCValue();
DEBUG(std::cerr << "rv addr: 0x" << std::hex << rv << "\n"
<< "curr PC: 0x" << CurrPC << "\n");
rv = (rv - CurrPC) >> 2;
if (rv >= (1<<29) || rv <= -(1<<29)) {
std::cerr << "addr out of bounds for the 30-bit call: " << rv << "\n";
abort();
int64_t CallInstTarget = (rv - CurrPC) >> 2;
if (CallInstTarget >= (1<<29) || CallInstTarget <= -(1<<29)) {
DEBUG(std::cerr << "Making far call!\n");
// addresss is out of bounds for the 30-bit call,
// make an indirect jump-and-link
emitFarCall(rv);
// this invalidates the instruction so that the call with an incorrect
// address will not be emitted
rv = 0;
} else {
// The call fits into 30 bits, so just return the corrected address
rv = CallInstTarget;
}
DEBUG(std::cerr << "returning addr: 0x" << rv << "\n");
}
@ -627,8 +708,16 @@ bool SparcV9CodeEmitter::runOnMachineFunction(MachineFunction &MF) {
void SparcV9CodeEmitter::emitBasicBlock(MachineBasicBlock &MBB) {
currBB = MBB.getBasicBlock();
BBLocations[currBB] = MCE.getCurrentPCValue();
for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end(); I != E; ++I)
emitWord(getBinaryCodeForInstr(**I));
for (MachineBasicBlock::iterator I = MBB.begin(), E = MBB.end(); I != E; ++I){
unsigned binCode = getBinaryCodeForInstr(**I);
if (binCode == (1 << 30)) {
// this is an invalid call: the addr is out of bounds. that means a code
// sequence has already been emitted, and this is a no-op
DEBUG(std::cerr << "Call supressed: already emitted far call.\n");
} else {
emitWord(binCode);
}
}
}
void* SparcV9CodeEmitter::getGlobalAddress(GlobalValue *V, MachineInstr &MI,

View File

@ -49,6 +49,8 @@ private:
bool isPCRelative);
bool isFPInstr(MachineInstr &MI);
unsigned getRealRegNum(unsigned fakeReg, MachineInstr &MI);
inline void emitFarCall(uint64_t Addr);
};
#endif