summaryrefslogtreecommitdiff
path: root/lib/Target/ARM/ARMJITInfo.cpp
blob: 915963c26cacc847f631a7cb95d055143a805cb3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
//===-- ARMJITInfo.cpp - Implement the JIT interfaces for the ARM target --===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements the JIT interfaces for the ARM target.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "jit"
#include "ARMJITInfo.h"
#include "ARMRelocations.h"
#include "ARMSubtarget.h"
#include "llvm/CodeGen/MachineCodeEmitter.h"
#include "llvm/Config/alloca.h"
#include <cstdlib>
using namespace llvm;

void ARMJITInfo::replaceMachineCodeForFunction(void *Old, void *New) {
  abort();
}

/// JITCompilerFunction - This contains the address of the JIT function used to
/// compile a function lazily.
static TargetJITInfo::JITCompilerFn JITCompilerFunction;

// CompilationCallback stub - We can't use a C function with inline assembly in
// it, because we the prolog/epilog inserted by GCC won't work for us.  Instead,
// write our own wrapper, which does things our way, so we have complete control
// over register saving and restoring.
extern "C" {
#if defined(__arm__)
  void ARMCompilationCallback(void);
  asm(
    ".text\n"
    ".align 2\n"
    ".globl ARMCompilationCallback\n"
    "ARMCompilationCallback:\n"
    // save main registers
    "mov    ip, sp\n"
    "stmfd  sp!, {fp, ip, lr, pc}\n"
    "sub    fp, ip, #4\n"
    // arguments to Compilation Callback
    // r0 - our lr (address of the call instruction in stub plus 4)
    // r1 - stub's lr (address of instruction that called the stub plus 4)
    "mov    r0, fp\n"  // stub's frame
    "mov    r1, lr\n"  // stub's lr
    "bl     ARMCompilationCallbackC\n"
    // restore main registers
    "ldmfd  sp, {fp, sp, pc}\n");
#else // Not an ARM host
  void ARMCompilationCallback() {
    assert(0 && "Cannot call ARMCompilationCallback() on a non-ARM arch!\n");
    abort();
  }
#endif
}

/// ARMCompilationCallbackC - This is the target-specific function invoked by the
/// function stub when we did not know the real target of a call.  This function
/// must locate the start of the stub or call site and pass it into the JIT
/// compiler function.
extern "C" void ARMCompilationCallbackC(intptr_t *StackPtr, intptr_t RetAddr) {
  intptr_t *RetAddrLoc = &StackPtr[-1];

  assert(*RetAddrLoc == RetAddr &&
         "Could not find return address on the stack!");
#if 0
  DOUT << "In callback! Addr=" << (void*)RetAddr
       << " FP=" << (void*)StackPtr
       << ": Resolving call to function: "
       << TheVM->getFunctionReferencedName((void*)RetAddr) << "\n";
#endif
  intptr_t Addr = RetAddr - 4;

  intptr_t NewVal = (intptr_t)JITCompilerFunction((void*)Addr);

  // Rewrite the call target... so that we don't end up here every time we
  // execute the call.
  *(intptr_t *)Addr = NewVal;

  // Change the return address to reexecute the branch and link instruction...
  *RetAddrLoc -= 12;
}

TargetJITInfo::LazyResolverFn
ARMJITInfo::getLazyResolverFunction(JITCompilerFn F) {
  JITCompilerFunction = F;
  return ARMCompilationCallback;
}

void *ARMJITInfo::emitFunctionStub(void *Fn, MachineCodeEmitter &MCE) {
  unsigned addr = (intptr_t)Fn;
  // If this is just a call to an external function, emit a branch instead of a
  // call.  The code is the same except for one bit of the last instruction.
  if (Fn != (void*)(intptr_t)ARMCompilationCallback) {
    // branch to the corresponding function addr
    // the stub is 8-byte size and 4-aligned
    MCE.startFunctionStub(8, 4);
    MCE.emitWordLE(0xE51FF004); // LDR PC, [PC,#-4]
    MCE.emitWordLE(addr);       // addr of function
  } else {
    // branch and link to the corresponding function addr
    // the stub is 20-byte size and 4-aligned
    MCE.startFunctionStub(20, 4);
    MCE.emitWordLE(0xE92D4800); // STMFD SP!, [R11, LR]
    MCE.emitWordLE(0xE28FE004); // ADD LR, PC, #4
    MCE.emitWordLE(0xE51FF004); // LDR PC, [PC,#-4]
    MCE.emitWordLE(addr);       // addr of function
    MCE.emitWordLE(0xE8BD8800); // LDMFD SP!, [R11, PC]
  }

  return MCE.finishFunctionStub(0);
}

/// relocate - Before the JIT can run a block of code that has been emitted,
/// it must rewrite the code to contain the actual addresses of any
/// referenced global symbols.
void ARMJITInfo::relocate(void *Function, MachineRelocation *MR,
                          unsigned NumRelocs, unsigned char* GOTBase) {
  for (unsigned i = 0; i != NumRelocs; ++i, ++MR) {
    void *RelocPos = (char*)Function + MR->getMachineCodeOffset();
    intptr_t ResultPtr = (intptr_t)MR->getResultPointer();
    switch ((ARM::RelocationType)MR->getRelocationType()) {
    case ARM::reloc_arm_relative: {
      // It is necessary to calculate the correct PC relative value. We
      // subtract the base addr from the target addr to form a byte offset.
      ResultPtr = ResultPtr-(intptr_t)RelocPos-8;
      // If the result is positive, set bit U(23) to 1.
      if (ResultPtr >= 0)
        *((unsigned*)RelocPos) |= 1 << 23;
      else {
      // otherwise, obtain the absolute value and set
      // bit U(23) to 0.
        ResultPtr *= -1;
        *((unsigned*)RelocPos) &= 0xFF7FFFFF;
      }
      // set the immed value calculated
      *((unsigned*)RelocPos) |= (unsigned)ResultPtr;
      // set register Rn to PC
      *((unsigned*)RelocPos) |= 0xF << 16;
      break;
    }
    case ARM::reloc_arm_branch: {
      // It is necessary to calculate the correct value of signed_immed_24
      // field. We subtract the base addr from the target addr to form a
      // byte offset, which must be inside the range -33554432 and +33554428.
      // Then, we set the signed_immed_24 field of the instruction to bits
      // [25:2] of the byte offset. More details ARM-ARM p. A4-11.
      ResultPtr = ResultPtr-(intptr_t)RelocPos-8;
      ResultPtr = (ResultPtr & 0x03FFFFFC) >> 2;
      assert(ResultPtr >= -33554432 && ResultPtr <= 33554428);
      *((unsigned*)RelocPos) |= ResultPtr;
      break;
    }
    }
  }
}