ARMJITInfo.cpp revision a7b3e7c33f1ef7be844fde18dcd9e24afdc97748
1//===-- ARMJITInfo.cpp - Implement the JIT interfaces for the ARM target --===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file was developed by the Raul Herbster and is distributed under the 6// University of Illinois Open Source License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file implements the JIT interfaces for the ARM target. 11// 12//===----------------------------------------------------------------------===// 13 14#define DEBUG_TYPE "jit" 15#include "ARMJITInfo.h" 16#include "ARMRelocations.h" 17#include "ARMSubtarget.h" 18#include "llvm/CodeGen/MachineCodeEmitter.h" 19#include "llvm/Config/alloca.h" 20#include <cstdlib> 21using namespace llvm; 22 23void ARMJITInfo::replaceMachineCodeForFunction(void *Old, void *New) { 24 unsigned char *OldByte = (unsigned char *)Old; 25 *OldByte++ = 0xEA; // Emit B opcode. 26 unsigned *OldWord = (unsigned *)OldByte; 27 unsigned NewAddr = (intptr_t)New; 28 unsigned OldAddr = (intptr_t)OldWord; 29 *OldWord = NewAddr - OldAddr - 4; // Emit PC-relative addr of New code. 30} 31 32/// JITCompilerFunction - This contains the address of the JIT function used to 33/// compile a function lazily. 34static TargetJITInfo::JITCompilerFn JITCompilerFunction; 35 36// CompilationCallback stub - We can't use a C function with inline assembly in 37// it, because we the prolog/epilog inserted by GCC won't work for us. Instead, 38// write our own wrapper, which does things our way, so we have complete control 39// over register saving and restoring. 40extern "C" { 41#if defined(__arm__) 42 void ARMCompilationCallback(void); 43 asm( 44 ".text\n" 45 ".align 2\n" 46 ".globl ARMCompilationCallback\n" 47 "ARMCompilationCallback:\n" 48 // save main registers 49 "mov ip, sp\n" 50 "stmfd sp!, {fp, ip, lr, pc}\n" 51 "sub fp, ip, #4\n" 52 // arguments to Compilation Callback 53 // r0 - our lr (address of the call instruction in stub plus 4) 54 // r1 - stub's lr (address of instruction that called the stub plus 4) 55 "mov r0, fp\n" // stub's frame 56 "mov r1, lr\n" // stub's lr 57 "bl ARMCompilationCallbackC\n" 58 // restore main registers 59 "ldmfd sp, {fp, sp, pc}\n"); 60#else // Not an ARM host 61 void ARMCompilationCallback() { 62 assert(0 && "Cannot call ARMCompilationCallback() on a non-ARM arch!\n"); 63 abort(); 64 } 65#endif 66} 67 68/// ARMCompilationCallbackC - This is the target-specific function invoked by the 69/// function stub when we did not know the real target of a call. This function 70/// must locate the start of the stub or call site and pass it into the JIT 71/// compiler function. 72extern "C" void ARMCompilationCallbackC(intptr_t *StackPtr, intptr_t RetAddr) { 73 intptr_t *RetAddrLoc = &StackPtr[-1]; 74 75 assert(*RetAddrLoc == RetAddr && 76 "Could not find return address on the stack!"); 77#if 0 78 DOUT << "In callback! Addr=" << (void*)RetAddr 79 << " FP=" << (void*)StackPtr 80 << ": Resolving call to function: " 81 << TheVM->getFunctionReferencedName((void*)RetAddr) << "\n"; 82#endif 83 84 // Sanity check to make sure this really is a branch and link instruction. 85 assert(((unsigned char*)RetAddr-1)[3] == 0xEB && "Not a branch and link instr!"); 86 87 intptr_t NewVal = (intptr_t)JITCompilerFunction((void*)RetAddr); 88 89 // Rewrite the call target... so that we don't end up here every time we 90 // execute the call. 91 *(intptr_t *)RetAddr = (intptr_t)(NewVal-RetAddr-4); 92 93 // Change the return address to reexecute the branch and link instruction... 94 *RetAddrLoc -= 1; 95} 96 97TargetJITInfo::LazyResolverFn 98ARMJITInfo::getLazyResolverFunction(JITCompilerFn F) { 99 JITCompilerFunction = F; 100 return ARMCompilationCallback; 101} 102 103void *ARMJITInfo::emitFunctionStub(void *Fn, MachineCodeEmitter &MCE) { 104 unsigned addr = (intptr_t)Fn-MCE.getCurrentPCValue()-4; 105 // If this is just a call to an external function, emit a branch instead of a 106 // call. The code is the same except for one bit of the last instruction. 107 if (Fn != (void*)(intptr_t)ARMCompilationCallback) { 108 MCE.startFunctionStub(4, 2); 109 MCE.emitByte(0xEA); // branch to the corresponding function addr 110 MCE.emitByte((unsigned char)(addr >> 0)); 111 MCE.emitByte((unsigned char)(addr >> 8)); 112 MCE.emitByte((unsigned char)(addr >> 16)); 113 return MCE.finishFunctionStub(0); 114 } else { 115 MCE.startFunctionStub(5, 2); 116 MCE.emitByte(0xEB); // branch and link to the corresponding function addr 117 } 118 MCE.emitByte((unsigned char)(addr >> 0)); 119 MCE.emitByte((unsigned char)(addr >> 8)); 120 MCE.emitByte((unsigned char)(addr >> 16)); 121 122 return MCE.finishFunctionStub(0); 123} 124 125/// relocate - Before the JIT can run a block of code that has been emitted, 126/// it must rewrite the code to contain the actual addresses of any 127/// referenced global symbols. 128void ARMJITInfo::relocate(void *Function, MachineRelocation *MR, 129 unsigned NumRelocs, unsigned char* GOTBase) { 130 for (unsigned i = 0; i != NumRelocs; ++i, ++MR) { 131 void *RelocPos = (char*)Function + MR->getMachineCodeOffset(); 132 intptr_t ResultPtr = (intptr_t)MR->getResultPointer(); 133 switch ((ARM::RelocationType)MR->getRelocationType()) { 134 case ARM::reloc_arm_relative: { 135 // PC relative relocation 136 *((unsigned*)RelocPos) += (unsigned)ResultPtr; 137 break; 138 } 139 case ARM::reloc_arm_absolute: 140 break; 141 case ARM::reloc_arm_branch: { 142 // relocation to b and bl instructions 143 ResultPtr = (ResultPtr-(intptr_t)RelocPos) >> 2; 144 *((unsigned*)RelocPos) |= ResultPtr; 145 break; 146 } 147 } 148 } 149} 150