aboutsummaryrefslogtreecommitdiff
path: root/lib/Target/Mips/MipsJITInfo.cpp
blob: 1b2a325d3ce67400d18daee7ad2b91dcc5ea50a1 (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
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
//===-- MipsJITInfo.cpp - Implement the Mips JIT Interface ----------------===//
//
//                     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 Mips target.
//
//===----------------------------------------------------------------------===//

#define DEBUG_TYPE "jit"
#include "MipsJITInfo.h"
#include "MipsInstrInfo.h"
#include "MipsRelocations.h"
#include "MipsSubtarget.h"
#include "llvm/CodeGen/JITCodeEmitter.h"
#include "llvm/IR/Function.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Memory.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdlib>
using namespace llvm;


void MipsJITInfo::replaceMachineCodeForFunction(void *Old, void *New) {
  unsigned NewAddr = (intptr_t)New;
  unsigned OldAddr = (intptr_t)Old;
  const unsigned NopInstr = 0x0;

  // If the functions are in the same memory segment, insert PC-region branch.
  if ((NewAddr & 0xF0000000) == ((OldAddr + 4) & 0xF0000000)) {
    unsigned *OldInstruction = (unsigned *)Old;
    *OldInstruction = 0x08000000;
    unsigned JTargetAddr = NewAddr & 0x0FFFFFFC;

    JTargetAddr >>= 2;
    *OldInstruction |= JTargetAddr;

    // Insert a NOP.
    OldInstruction++;
    *OldInstruction = NopInstr;

    sys::Memory::InvalidateInstructionCache(Old, 2 * 4);
  } else {
    // We need to clear hint bits from the instruction, in case it is 'jr ra'.
    const unsigned HintMask = 0xFFFFF83F, ReturnSequence = 0x03e00008;
    unsigned* CurrentInstr = (unsigned*)Old;
    unsigned CurrInstrHintClear = (*CurrentInstr) & HintMask;
    unsigned* NextInstr = CurrentInstr + 1;
    unsigned NextInstrHintClear = (*NextInstr) & HintMask;

    // Do absolute jump if there are 2 or more instructions before return from
    // the old function.
    if ((CurrInstrHintClear != ReturnSequence) &&
        (NextInstrHintClear != ReturnSequence)) {
      const unsigned LuiT0Instr = 0x3c080000, AddiuT0Instr = 0x25080000;
      const unsigned JrT0Instr = 0x01000008;
      // lui  t0,  high 16 bit of the NewAddr
      (*(CurrentInstr++)) = LuiT0Instr | ((NewAddr & 0xffff0000) >> 16);
      // addiu  t0, t0, low 16 bit of the NewAddr
      (*(CurrentInstr++)) = AddiuT0Instr | (NewAddr & 0x0000ffff);
      // jr t0
      (*(CurrentInstr++)) = JrT0Instr;
      (*CurrentInstr) = NopInstr;

      sys::Memory::InvalidateInstructionCache(Old, 4 * 4);
    } else {
      // Unsupported case
      report_fatal_error("MipsJITInfo::replaceMachineCodeForFunction");
    }
  }
}

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

// Get the ASMPREFIX for the current host.  This is often '_'.
#ifndef __USER_LABEL_PREFIX__
#define __USER_LABEL_PREFIX__
#endif
#define GETASMPREFIX2(X) #X
#define GETASMPREFIX(X) GETASMPREFIX2(X)
#define ASMPREFIX GETASMPREFIX(__USER_LABEL_PREFIX__)

// CompilationCallback stub - We can't use a C function with inline assembly in
// it, because 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. This code saves registers, calls
// MipsCompilationCallbackC and restores registers.
extern "C" {
#if defined (__mips__)
void MipsCompilationCallback();

  asm(
    ".text\n"
    ".align 2\n"
    ".globl " ASMPREFIX "MipsCompilationCallback\n"
    ASMPREFIX "MipsCompilationCallback:\n"
    ".ent " ASMPREFIX "MipsCompilationCallback\n"
    ".frame  $sp, 32, $ra\n"
    ".set  noreorder\n"
    ".cpload $t9\n"

    "addiu $sp, $sp, -64\n"
    ".cprestore 16\n"

    // Save argument registers a0, a1, a2, a3, f12, f14 since they may contain
    // stuff for the real target function right now. We have to act as if this
    // whole compilation callback doesn't exist as far as the caller is
    // concerned. We also need to save the ra register since it contains the
    // original return address, and t8 register since it contains the address
    // of the end of function stub.
    "sw $a0, 20($sp)\n"
    "sw $a1, 24($sp)\n"
    "sw $a2, 28($sp)\n"
    "sw $a3, 32($sp)\n"
    "sw $ra, 36($sp)\n"
    "sw $t8, 40($sp)\n"
    "sdc1 $f12, 48($sp)\n"
    "sdc1 $f14, 56($sp)\n"

    // t8 points at the end of function stub. Pass the beginning of the stub
    // to the MipsCompilationCallbackC.
    "addiu $a0, $t8, -16\n"
    "jal " ASMPREFIX "MipsCompilationCallbackC\n"
    "nop\n"

    // Restore registers.
    "lw $a0, 20($sp)\n"
    "lw $a1, 24($sp)\n"
    "lw $a2, 28($sp)\n"
    "lw $a3, 32($sp)\n"
    "lw $ra, 36($sp)\n"
    "lw $t8, 40($sp)\n"
    "ldc1 $f12, 48($sp)\n"
    "ldc1 $f14, 56($sp)\n"
    "addiu $sp, $sp, 64\n"

    // Jump to the (newly modified) stub to invoke the real function.
    "addiu $t8, $t8, -16\n"
    "jr $t8\n"
    "nop\n"

    ".set  reorder\n"
    ".end " ASMPREFIX "MipsCompilationCallback\n"
      );
#else  // host != Mips
  void MipsCompilationCallback() {
    llvm_unreachable(
      "Cannot call MipsCompilationCallback() on a non-Mips arch!");
  }
#endif
}

/// MipsCompilationCallbackC - 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 MipsCompilationCallbackC(intptr_t StubAddr) {
  // Get the address of the compiled code for this function.
  intptr_t NewVal = (intptr_t) JITCompilerFunction((void*) StubAddr);

  // Rewrite the function stub so that we don't end up here every time we
  // execute the call. We're replacing the first four instructions of the
  // stub with code that jumps to the compiled function:
  //   lui $t9, %hi(NewVal)
  //   addiu $t9, $t9, %lo(NewVal)
  //   jr $t9
  //   nop

  int Hi = ((unsigned)NewVal & 0xffff0000) >> 16;
  if ((NewVal & 0x8000) != 0)
    Hi++;
  int Lo = (int)(NewVal & 0xffff);

  *(intptr_t *)(StubAddr) = 0xf << 26 | 25 << 16 | Hi;
  *(intptr_t *)(StubAddr + 4) = 9 << 26 | 25 << 21 | 25 << 16 | Lo;
  *(intptr_t *)(StubAddr + 8) = 25 << 21 | 8;
  *(intptr_t *)(StubAddr + 12) = 0;

  sys::Memory::InvalidateInstructionCache((void*) StubAddr, 16);
}

TargetJITInfo::LazyResolverFn MipsJITInfo::getLazyResolverFunction(
    JITCompilerFn F) {
  JITCompilerFunction = F;
  return MipsCompilationCallback;
}

TargetJITInfo::StubLayout MipsJITInfo::getStubLayout() {
  // The stub contains 4 4-byte instructions, aligned at 4 bytes. See
  // emitFunctionStub for details.
  StubLayout Result = { 4*4, 4 };
  return Result;
}

void *MipsJITInfo::emitFunctionStub(const Function *F, void *Fn,
                                    JITCodeEmitter &JCE) {
  JCE.emitAlignment(4);
  void *Addr = (void*) (JCE.getCurrentPCValue());
  if (!sys::Memory::setRangeWritable(Addr, 16))
    llvm_unreachable("ERROR: Unable to mark stub writable.");

  intptr_t EmittedAddr;
  if (Fn != (void*)(intptr_t)MipsCompilationCallback)
    EmittedAddr = (intptr_t)Fn;
  else
    EmittedAddr = (intptr_t)MipsCompilationCallback;


  int Hi = ((unsigned)EmittedAddr & 0xffff0000) >> 16;
  if ((EmittedAddr & 0x8000) != 0)
    Hi++;
  int Lo = (int)(EmittedAddr & 0xffff);

  // lui t9, %hi(EmittedAddr)
  // addiu t9, t9, %lo(EmittedAddr)
  // jalr t8, t9
  // nop
  if (IsLittleEndian) {
    JCE.emitWordLE(0xf << 26 | 25 << 16 | Hi);
    JCE.emitWordLE(9 << 26 | 25 << 21 | 25 << 16 | Lo);
    JCE.emitWordLE(25 << 21 | 24 << 11 | 9);
    JCE.emitWordLE(0);
  } else {
    JCE.emitWordBE(0xf << 26 | 25 << 16 | Hi);
    JCE.emitWordBE(9 << 26 | 25 << 21 | 25 << 16 | Lo);
    JCE.emitWordBE(25 << 21 | 24 << 11 | 9);
    JCE.emitWordBE(0);
  }

  sys::Memory::InvalidateInstructionCache(Addr, 16);
  if (!sys::Memory::setRangeExecutable(Addr, 16))
    llvm_unreachable("ERROR: Unable to mark stub executable.");

  return Addr;
}

/// 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 MipsJITInfo::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 ((Mips::RelocationType) MR->getRelocationType()) {
    case Mips::reloc_mips_pc16:
      ResultPtr = (((ResultPtr - (intptr_t) RelocPos) - 4) >> 2) & 0xffff;
      *((unsigned*) RelocPos) |= (unsigned) ResultPtr;
      break;

    case Mips::reloc_mips_26:
      ResultPtr = (ResultPtr & 0x0fffffff) >> 2;
      *((unsigned*) RelocPos) |= (unsigned) ResultPtr;
      break;

    case Mips::reloc_mips_hi:
      ResultPtr = ResultPtr >> 16;
      if ((((intptr_t) (MR->getResultPointer()) & 0xffff) >> 15) == 1) {
        ResultPtr += 1;
      }
      *((unsigned*) RelocPos) |= (unsigned) ResultPtr;
      break;

    case Mips::reloc_mips_lo: {
      // Addend is needed for unaligned load/store instructions, where offset
      // for the second load/store in the expanded instruction sequence must
      // be modified by +1 or +3. Otherwise, Addend is 0.
      int Addend = *((unsigned*) RelocPos) & 0xffff;
      ResultPtr = (ResultPtr + Addend) & 0xffff;
      *((unsigned*) RelocPos) &= 0xffff0000;
      *((unsigned*) RelocPos) |= (unsigned) ResultPtr;
      break;
    }
    }
  }
}