diff options
author | Misha Brukman <brukman+llvm@gmail.com> | 2003-06-05 20:52:06 +0000 |
---|---|---|
committer | Misha Brukman <brukman+llvm@gmail.com> | 2003-06-05 20:52:06 +0000 |
commit | f47d9c28d9b395a0c0b14ceb07124c39ab13e493 (patch) | |
tree | 467ed343d7e15dc34279b8325425aea4a193c690 | |
parent | e77d65a8ed7b0da7bd1c5c4252706f2c1d879f6b (diff) | |
download | external_llvm-f47d9c28d9b395a0c0b14ceb07124c39ab13e493.zip external_llvm-f47d9c28d9b395a0c0b14ceb07124c39ab13e493.tar.gz external_llvm-f47d9c28d9b395a0c0b14ceb07124c39ab13e493.tar.bz2 |
Added lazy function resolution to the JIT.
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@6633 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | lib/Target/SparcV9/SparcV9CodeEmitter.cpp | 250 | ||||
-rw-r--r-- | lib/Target/SparcV9/SparcV9CodeEmitter.h | 4 |
2 files changed, 223 insertions, 31 deletions
diff --git a/lib/Target/SparcV9/SparcV9CodeEmitter.cpp b/lib/Target/SparcV9/SparcV9CodeEmitter.cpp index 00a7e7c..3ec0656 100644 --- a/lib/Target/SparcV9/SparcV9CodeEmitter.cpp +++ b/lib/Target/SparcV9/SparcV9CodeEmitter.cpp @@ -54,10 +54,13 @@ namespace { return SparcV9.getBinaryCodeForInstr(MI); } + inline uint64_t insertFarJumpAtAddr(int64_t Value, uint64_t Addr); + private: uint64_t emitStubForFunction(Function *F); static void CompilationCallback(); uint64_t resolveFunctionReference(uint64_t RetAddr); + }; JITResolver *TheJITResolver; @@ -92,27 +95,175 @@ uint64_t JITResolver::getLazyResolver(Function *F) { return Stub; } -void JITResolver::CompilationCallback() { - uint64_t *StackPtr = (uint64_t*)__builtin_frame_address(0); - uint64_t RetAddr = (uint64_t)(intptr_t)__builtin_return_address(0); - - std::cerr << "In callback! Addr=0x" << std::hex << RetAddr - << " SP=0x" << (uint64_t)(intptr_t)StackPtr << std::dec << "\n"; +uint64_t JITResolver::insertFarJumpAtAddr(int64_t Target, uint64_t Addr) { + + 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); + *((unsigned*)(intptr_t)Addr) = getBinaryCodeForInstr(*STX); + delete STX; + Addr += 4; + + // stx %i2, [%sp + 2127] ;; save %i2 to the stack + STX = BuildMI(V9::STXi, 3).addReg(i2).addReg(o6).addSImm(2127); + *((unsigned*)(intptr_t)Addr) = getBinaryCodeForInstr(*STX); + delete STX; + Addr += 4; + + // + // 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); + *((unsigned*)(intptr_t)Addr) = getBinaryCodeForInstr(*SH); + delete SH; + Addr += 4; + + // 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); + *((unsigned*)(intptr_t)Addr) = getBinaryCodeForInstr(*OR); + delete OR; + Addr += 4; + + // sllx %i1, 32, %i1 ;; shift those 10 bits to the upper word + MachineInstr *SL = BuildMI(V9::SLLXi6, 3).addReg(i1).addSImm(32).addReg(i1); + *((unsigned*)(intptr_t)Addr) = getBinaryCodeForInstr(*SL); + delete SL; + Addr += 4; + + // sethi %hi(Target), %i2 ;; extract bits 10-31 into the dest reg + SH = BuildMI(V9::SETHI, 2).addSImm((Target >> 10) & 0x03fffff).addReg(i2); + *((unsigned*)(intptr_t)Addr) = getBinaryCodeForInstr(*SH); + delete SH; + Addr += 4; + + // or %i1, %i2, %i2 ;; get upper word (in %i1) into %i2 + OR = BuildMI(V9::ORr, 3).addReg(i1).addReg(i2).addReg(i2); + *((unsigned*)(intptr_t)Addr) = getBinaryCodeForInstr(*OR); + delete OR; + Addr += 4; + + // 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); + *((unsigned*)(intptr_t)Addr) = getBinaryCodeForInstr(*OR); + delete OR; + Addr += 4; + + // ldx [%sp + 2119], %i1 ;; restore %i1 -> 2119 = BIAS(2047) + 72 + MachineInstr *LDX = BuildMI(V9::LDXi, 3).addReg(o6).addSImm(2119).addReg(i1); + *((unsigned*)(intptr_t)Addr) = getBinaryCodeForInstr(*LDX); + delete LDX; + Addr += 4; + + // jmpl %i2, %g0, %g0 ;; indirect branch on %i2 + MachineInstr *J = BuildMI(V9::JMPLRETr, 3).addReg(i2).addReg(g0).addReg(g0); + *((unsigned*)(intptr_t)Addr) = getBinaryCodeForInstr(*J); + delete J; + Addr += 4; + + // ldx [%sp + 2127], %i2 ;; restore %i2 -> 2127 = BIAS(2047) + 80 + LDX = BuildMI(V9::LDXi, 3).addReg(o6).addSImm(2127).addReg(i2); + *((unsigned*)(intptr_t)Addr) = getBinaryCodeForInstr(*LDX); + delete LDX; + Addr += 4; + + return Addr; +} - int64_t NewVal = (int64_t)TheJITResolver->resolveFunctionReference(RetAddr); +void JITResolver::CompilationCallback() { + uint64_t CameFrom = (uint64_t)(intptr_t)__builtin_return_address(0); + int64_t Target = (int64_t)TheJITResolver->resolveFunctionReference(CameFrom); + std::cerr << "In callback! Addr=0x" << std::hex << CameFrom << "\n"; // Rewrite the call target... so that we don't fault every time we execute // the call. +#if 0 int64_t RealCallTarget = (int64_t) ((NewVal - TheJITResolver->getCurrentPCValue()) >> 4); - MachineInstr *MI = BuildMI(V9::CALL, 1); - MI->addSignExtImmOperand(RealCallTarget); - // FIXME: this could be in the wrong byte order!! - *((unsigned*)(intptr_t)RetAddr) = TheJITResolver->getBinaryCodeForInstr(*MI); + if (RealCallTarget >= (1<<22) || RealCallTarget <= -(1<<22)) { + std::cerr << "Address out of bounds for 22bit BA: " << RealCallTarget<<"\n"; + abort(); + } +#endif + + //uint64_t CurrPC = TheJITResolver->getCurrentPCValue(); + // we will insert 9 instructions before we do the actual jump + //int64_t NewTarget = (NewVal - 9*4 - InstAddr) >> 2; + + static const unsigned i1 = SparcIntRegClass::i1, i2 = SparcIntRegClass::i2, + i7 = SparcIntRegClass::i7, o6 = SparcIntRegClass::o6, + o7 = SparcIntRegClass::o7, g0 = SparcIntRegClass::g0; + + // Subtract 4 to overwrite the 'save' that's there now + uint64_t InstAddr = CameFrom-4; + + InstAddr = TheJITResolver->insertFarJumpAtAddr(Target, InstAddr); + + // CODE SHOULD NEVER GO PAST THIS LOAD!! The real function should return to + // the original caller, not here!! + + // FIXME: add call 0 to make sure?!? + + // =============== THE REAL STUB ENDS HERE ========================= + + // What follows below is one-time restore code, because this callback may be + // changing registers in unpredictible ways. However, since it is executed + // only once per function (after the function is resolved, the callback is no + // longer in the path), this has to be done only once. + // + // Thus, it is after the regular stub code. The call back returns to THIS + // point, but every other call to the target function will execute the code + // above. Hence, this code is one-time use. + + uint64_t OneTimeRestore = InstAddr; + + // restore %g0, 0, %g0 + //MachineInstr *R = BuildMI(V9::RESTOREi, 3).addMReg(g0).addSImm(0) + // .addMReg(g0, MOTy::Def); + //*((unsigned*)(intptr_t)InstAddr)=TheJITResolver->getBinaryCodeForInstr(*R); + //delete R; + + // FIXME: BuildMI() above crashes. Encode the instruction directly. + // restore %g0, 0, %g0 + *((unsigned*)(intptr_t)InstAddr) = 0x81e82000U; + InstAddr += 4; + + InstAddr = TheJITResolver->insertFarJumpAtAddr(Target, InstAddr); + + // FIXME: if the target function is close enough to fit into the 19bit disp of + // BA, we should use this version, as its much cheaper to generate. + /* + MachineInstr *MI = BuildMI(V9::BA, 1).addSImm(RealCallTarget); + *((unsigned*)(intptr_t)InstAddr) = TheJITResolver->getBinaryCodeForInstr(*MI); delete MI; - + InstAddr += 4; + + // Add another NOP + MachineInstr *Nop = BuildMI(V9::NOP, 0); + *((unsigned*)(intptr_t)InstAddr)=TheJITResolver->getBinaryCodeForInstr(*Nop); + delete Nop; + InstAddr += 4; + + MachineInstr *BA = BuildMI(V9::BA, 1).addSImm(RealCallTarget-2); + *((unsigned*)(intptr_t)InstAddr) = TheJITResolver->getBinaryCodeForInstr(*BA); + delete BA; + */ + // Change the return address to reexecute the call instruction... - StackPtr[1] -= 4; + // The return address is really %o7, but will disappear after this function + // returns, and the register windows are rotated away. +#if defined(sparc) || defined(__sparc__) || defined(__sparcv9) + __asm__ __volatile__ ("or %%g0, %0, %%i7" : : "r" (OneTimeRestore-8)); +#endif } /// emitStubForFunction - This method is used by the JIT when it needs to emit @@ -122,26 +273,31 @@ void JITResolver::CompilationCallback() { /// directly. /// uint64_t JITResolver::emitStubForFunction(Function *F) { -#if 0 MCE.startFunctionStub(*F, 6); - MCE.emitByte(0xE8); // Call with 32 bit pc-rel destination... - uint64_t Address = addFunctionReference(MCE.getCurrentPCValue(), F); - MCE.emitWord(Address-MCE.getCurrentPCValue()-4); + std::cerr << "Emitting stub at addr: 0x" + << std::hex << MCE.getCurrentPCValue() << "\n"; - MCE.emitByte(0xCD); // Interrupt - Just a marker identifying the stub! - return (intptr_t)MCE.finishFunctionStub(*F); -#endif - MCE.startFunctionStub(*F, 6); + unsigned o6 = SparcIntRegClass::o6; + // save %sp, -192, %sp + MachineInstr *SV = BuildMI(V9::SAVEi, 3).addReg(o6).addSImm(-192).addReg(o6); + SparcV9.emitWord(SparcV9.getBinaryCodeForInstr(*SV)); + delete SV; int64_t CurrPC = MCE.getCurrentPCValue(); int64_t Addr = (int64_t)addFunctionReference(CurrPC, F); + int64_t CallTarget = (Addr-CurrPC) >> 2; - MachineInstr *Call = BuildMI(V9::CALL, 1); - Call->addSignExtImmOperand(CallTarget); + if (CallTarget >= (1 << 30) || CallTarget <= -(1 << 30)) { + std::cerr << "Call target beyond 30 bit limit of CALL: " <<CallTarget<<"\n"; + abort(); + } + // call CallTarget ;; invoke the callback + MachineInstr *Call = BuildMI(V9::CALL, 1).addSImm(CallTarget); SparcV9.emitWord(SparcV9.getBinaryCodeForInstr(*Call)); delete Call; + // nop ;; call delay slot MachineInstr *Nop = BuildMI(V9::NOP, 0); SparcV9.emitWord(SparcV9.getBinaryCodeForInstr(*Nop)); delete Nop; @@ -170,7 +326,25 @@ void SparcV9CodeEmitter::emitWord(unsigned Val) { } } -unsigned getRealRegNum(unsigned fakeReg, unsigned regClass) { +bool SparcV9CodeEmitter::isFPInstr(MachineInstr &MI) { + for (unsigned i = 0, e = MI.getNumOperands(); i < e; ++i) { + const MachineOperand &MO = MI.getOperand(i); + if (MO.isPhysicalRegister()) { + unsigned fakeReg = MO.getReg(), realReg, regClass, regType; + regType = TM.getRegInfo().getRegType(fakeReg); + // At least map fakeReg into its class + fakeReg = TM.getRegInfo().getClassRegNum(fakeReg, regClass); + if (regClass == UltraSparcRegInfo::FPSingleRegType || + regClass == UltraSparcRegInfo::FPDoubleRegType) + return true; + } + } + return false; +} + +unsigned +SparcV9CodeEmitter::getRealRegNum(unsigned fakeReg, unsigned regClass, + MachineInstr &MI) { switch (regClass) { case UltraSparcRegInfo::IntRegType: { // Sparc manual, p31 @@ -199,11 +373,23 @@ unsigned getRealRegNum(unsigned fakeReg, unsigned regClass) { return fakeReg; } case UltraSparcRegInfo::FloatCCRegType: { + /* These are laid out %fcc0 - %fcc3 => 0 - 3, so are correct */ return fakeReg; } case UltraSparcRegInfo::IntCCRegType: { - return fakeReg; + static const unsigned FPInstrIntCCReg[] = { 6 /* xcc */, 4 /* icc */ }; + static const unsigned IntInstrIntCCReg[] = { 2 /* xcc */, 0 /* icc */ }; + + if (isFPInstr(MI)) { + assert(fakeReg < sizeof(FPInstrIntCCReg)/sizeof(FPInstrIntCCReg[0]) + && "Int CC register out of bounds for FPInstr IntCCReg map"); + return FPInstrIntCCReg[fakeReg]; + } else { + assert(fakeReg < sizeof(IntInstrIntCCReg)/sizeof(IntInstrIntCCReg[0]) + && "Int CC register out of bounds for IntInstr IntCCReg map"); + return IntInstrIntCCReg[fakeReg]; + } } default: assert(0 && "Invalid unified register number in getRegType"); @@ -278,7 +464,9 @@ int64_t SparcV9CodeEmitter::getMachineOpValue(MachineInstr &MI, std::cerr << "ERROR: PC relative disp unhandled:" << MO << "\n"; abort(); } - } else if (MO.isPhysicalRegister()) { + } else if (MO.isPhysicalRegister() || + MO.getType() == MachineOperand::MO_CCRegister) + { // This is necessary because the Sparc doesn't actually lay out registers // in the real fashion -- it skips those that it chooses not to allocate, // i.e. those that are the SP, etc. @@ -287,7 +475,7 @@ int64_t SparcV9CodeEmitter::getMachineOpValue(MachineInstr &MI, // At least map fakeReg into its class fakeReg = TM.getRegInfo().getClassRegNum(fakeReg, regClass); // Find the real register number for use in an instruction - realReg = getRealRegNum(fakeReg, regClass); + realReg = getRealRegNum(fakeReg, regClass, MI); std::cerr << "Reg[" << std::dec << fakeReg << "] = " << realReg << "\n"; rv = realReg; } else if (MO.isImmediate()) { @@ -327,13 +515,13 @@ int64_t SparcV9CodeEmitter::getMachineOpValue(MachineInstr &MI, // are used in SPARC assembly. (Some of these make no sense in combination // with some of the above; we'll trust that the instruction selector // will not produce nonsense, and not check for valid combinations here.) - if (MO.opLoBits32()) { // %lo(val) + if (MO.opLoBits32()) { // %lo(val) == %lo() in Sparc ABI doc return rv & 0x03ff; - } else if (MO.opHiBits32()) { // %lm(val) + } else if (MO.opHiBits32()) { // %lm(val) == %hi() in Sparc ABI doc return (rv >> 10) & 0x03fffff; - } else if (MO.opLoBits64()) { // %hm(val) + } else if (MO.opLoBits64()) { // %hm(val) == %ulo() in Sparc ABI doc return (rv >> 32) & 0x03ff; - } else if (MO.opHiBits64()) { // %hh(val) + } else if (MO.opHiBits64()) { // %hh(val) == %uhi() in Sparc ABI doc return rv >> 42; } else { // (unadorned) val return rv; diff --git a/lib/Target/SparcV9/SparcV9CodeEmitter.h b/lib/Target/SparcV9/SparcV9CodeEmitter.h index 15cb13c..d9a03e5 100644 --- a/lib/Target/SparcV9/SparcV9CodeEmitter.h +++ b/lib/Target/SparcV9/SparcV9CodeEmitter.h @@ -47,6 +47,10 @@ private: void emitBasicBlock(MachineBasicBlock &MBB); void* getGlobalAddress(GlobalValue *V, MachineInstr &MI, bool isPCRelative); + bool isFPInstr(MachineInstr &MI); + unsigned getRealRegNum(unsigned fakeReg, unsigned regClass, + MachineInstr &MI); + }; #endif |