aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ExecutionEngine/Orc/OrcTargetSupport.cpp
blob: fc56e67b85c58d29b7e97bf80d7dd8b06d5e1042 (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
#include "llvm/ADT/Triple.h"
#include "llvm/ExecutionEngine/Orc/OrcTargetSupport.h"
#include <array>

using namespace llvm::orc;

namespace {

uint64_t executeCompileCallback(JITCompileCallbackManagerBase *JCBM,
                                TargetAddress CallbackID) {
  return JCBM->executeCompileCallback(CallbackID);
}

}

namespace llvm {
namespace orc {

const char* OrcX86_64::ResolverBlockName = "orc_resolver_block";

void OrcX86_64::insertResolverBlock(
    Module &M, JITCompileCallbackManagerBase &JCBM) {

  // Trampoline code-sequence length, used to get trampoline address from return
  // address.
  const unsigned X86_64_TrampolineLength = 6;

  // List of x86-64 GPRs to save. Note - RBP saved separately below.
  std::array<const char *, 14> GPRs = {{
      "rax", "rbx", "rcx", "rdx",
      "rsi", "rdi", "r8", "r9",
      "r10", "r11", "r12", "r13",
      "r14", "r15"
    }};

  // Address of the executeCompileCallback function.
  uint64_t CallbackAddr =
      static_cast<uint64_t>(
        reinterpret_cast<uintptr_t>(executeCompileCallback));

  std::ostringstream AsmStream;
  Triple TT(M.getTargetTriple());

  // Switch to text section.
  if (TT.getOS() == Triple::Darwin)
    AsmStream << ".section __TEXT,__text,regular,pure_instructions\n"
              << ".align 4, 0x90\n";
  else
    AsmStream << ".text\n"
              << ".align 16, 0x90\n";

  // Bake in a pointer to the callback manager immediately before the
  // start of the resolver function.
  AsmStream << "jit_callback_manager_addr:\n"
            << "  .quad " << &JCBM << "\n";

  // Start the resolver function.
  AsmStream << ResolverBlockName << ":\n"
            << "  pushq   %rbp\n"
            << "  movq    %rsp, %rbp\n";

  // Store the GPRs.
  for (const auto &GPR : GPRs)
    AsmStream << "  pushq   %" << GPR << "\n";

  // Store floating-point state with FXSAVE.
  // Note: We need to keep the stack 16-byte aligned, so if we've emitted an odd
  //       number of 64-bit pushes so far (GPRs.size() plus 1 for RBP) then add
  //       an extra 64 bits of padding to the FXSave area.
  unsigned Padding = (GPRs.size() + 1) % 2 ? 8 : 0;
  unsigned FXSaveSize = 512 + Padding;
  AsmStream << "  subq    $" << FXSaveSize << ", %rsp\n"
            << "  fxsave  (%rsp)\n"

  // Load callback manager address, compute trampoline address, call JIT.
            << "  lea     jit_callback_manager_addr(%rip), %rdi\n"
            << "  movq    (%rdi), %rdi\n"
            << "  movq    0x8(%rbp), %rsi\n"
            << "  subq    $" << X86_64_TrampolineLength << ", %rsi\n"
            << "  movabsq $" << CallbackAddr << ", %rax\n"
            << "  callq   *%rax\n"

  // Replace the return to the trampoline with the return address of the
  // compiled function body.
            << "  movq    %rax, 0x8(%rbp)\n"

  // Restore the floating point state.
            << "  fxrstor (%rsp)\n"
            << "  addq    $" << FXSaveSize << ", %rsp\n";

  for (const auto &GPR : make_range(GPRs.rbegin(), GPRs.rend()))
    AsmStream << "  popq    %" << GPR << "\n";

  // Restore original RBP and return to compiled function body.
  AsmStream << "  popq    %rbp\n"
            << "  retq\n";

  M.appendModuleInlineAsm(AsmStream.str());
}

OrcX86_64::LabelNameFtor
OrcX86_64::insertCompileCallbackTrampolines(Module &M,
                                            TargetAddress ResolverBlockAddr,
                                            unsigned NumCalls,
                                            unsigned StartIndex) {
  const char *ResolverBlockPtrName = "Lorc_resolve_block_addr";

  std::ostringstream AsmStream;
  Triple TT(M.getTargetTriple());

  if (TT.getOS() == Triple::Darwin)
    AsmStream << ".section __TEXT,__text,regular,pure_instructions\n"
              << ".align 4, 0x90\n";
  else
    AsmStream << ".text\n"
              << ".align 16, 0x90\n";

  AsmStream << ResolverBlockPtrName << ":\n"
            << "  .quad " << ResolverBlockAddr << "\n";

  auto GetLabelName =
    [=](unsigned I) {
      std::ostringstream LabelStream;
      LabelStream << "orc_jcc_" << (StartIndex + I);
      return LabelStream.str();
  };

  for (unsigned I = 0; I < NumCalls; ++I)
    AsmStream << GetLabelName(I) << ":\n"
              << "  callq *" << ResolverBlockPtrName << "(%rip)\n";

  M.appendModuleInlineAsm(AsmStream.str());

  return GetLabelName;
}

} // End namespace orc.
} // End namespace llvm.