1e2d58cd5622459540af2db55797e8c07812ea43
[oota-llvm.git] / lib / ExecutionEngine / Orc / OrcTargetSupport.cpp
1 //===------- OrcTargetSupport.cpp - Target support utilities for Orc ------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "llvm/ADT/Triple.h"
11 #include "llvm/ExecutionEngine/Orc/OrcTargetSupport.h"
12 #include "llvm/Support/Process.h"
13 #include <array>
14
15 using namespace llvm::orc;
16
17 namespace {
18
19 uint64_t executeCompileCallback(JITCompileCallbackManagerBase *JCBM,
20                                 TargetAddress CallbackID) {
21   return JCBM->executeCompileCallback(CallbackID);
22 }
23
24 }
25
26 namespace llvm {
27 namespace orc {
28
29 const char* OrcX86_64::ResolverBlockName = "orc_resolver_block";
30
31 void OrcX86_64::insertResolverBlock(
32     Module &M, JITCompileCallbackManagerBase &JCBM) {
33
34   // Trampoline code-sequence length, used to get trampoline address from return
35   // address.
36   const unsigned X86_64_TrampolineLength = 6;
37
38   // List of x86-64 GPRs to save. Note - RBP saved separately below.
39   std::array<const char *, 14> GPRs = {{
40       "rax", "rbx", "rcx", "rdx",
41       "rsi", "rdi", "r8", "r9",
42       "r10", "r11", "r12", "r13",
43       "r14", "r15"
44     }};
45
46   // Address of the executeCompileCallback function.
47   uint64_t CallbackAddr =
48       static_cast<uint64_t>(
49         reinterpret_cast<uintptr_t>(executeCompileCallback));
50
51   std::ostringstream AsmStream;
52   Triple TT(M.getTargetTriple());
53
54   // Switch to text section.
55   if (TT.getOS() == Triple::Darwin)
56     AsmStream << ".section __TEXT,__text,regular,pure_instructions\n"
57               << ".align 4, 0x90\n";
58   else
59     AsmStream << ".text\n"
60               << ".align 16, 0x90\n";
61
62   // Bake in a pointer to the callback manager immediately before the
63   // start of the resolver function.
64   AsmStream << "jit_callback_manager_addr:\n"
65             << "  .quad " << &JCBM << "\n";
66
67   // Start the resolver function.
68   AsmStream << ResolverBlockName << ":\n"
69             << "  pushq     %rbp\n"
70             << "  movq      %rsp, %rbp\n";
71
72   // Store the GPRs.
73   for (const auto &GPR : GPRs)
74     AsmStream << "  pushq     %" << GPR << "\n";
75
76   // Store floating-point state with FXSAVE.
77   // Note: We need to keep the stack 16-byte aligned, so if we've emitted an odd
78   //       number of 64-bit pushes so far (GPRs.size() plus 1 for RBP) then add
79   //       an extra 64 bits of padding to the FXSave area.
80   unsigned Padding = (GPRs.size() + 1) % 2 ? 8 : 0;
81   unsigned FXSaveSize = 512 + Padding;
82   AsmStream << "  subq      $" << FXSaveSize << ", %rsp\n"
83             << "  fxsave64  (%rsp)\n"
84
85   // Load callback manager address, compute trampoline address, call JIT.
86             << "  lea       jit_callback_manager_addr(%rip), %rdi\n"
87             << "  movq      (%rdi), %rdi\n"
88             << "  movq      0x8(%rbp), %rsi\n"
89             << "  subq      $" << X86_64_TrampolineLength << ", %rsi\n"
90             << "  movabsq   $" << CallbackAddr << ", %rax\n"
91             << "  callq     *%rax\n"
92
93   // Replace the return to the trampoline with the return address of the
94   // compiled function body.
95             << "  movq      %rax, 0x8(%rbp)\n"
96
97   // Restore the floating point state.
98             << "  fxrstor64 (%rsp)\n"
99             << "  addq      $" << FXSaveSize << ", %rsp\n";
100
101   for (const auto &GPR : make_range(GPRs.rbegin(), GPRs.rend()))
102     AsmStream << "  popq      %" << GPR << "\n";
103
104   // Restore original RBP and return to compiled function body.
105   AsmStream << "  popq      %rbp\n"
106             << "  retq\n";
107
108   M.appendModuleInlineAsm(AsmStream.str());
109 }
110
111 OrcX86_64::LabelNameFtor
112 OrcX86_64::insertCompileCallbackTrampolines(Module &M,
113                                             TargetAddress ResolverBlockAddr,
114                                             unsigned NumCalls,
115                                             unsigned StartIndex) {
116   const char *ResolverBlockPtrName = "Lorc_resolve_block_addr";
117
118   std::ostringstream AsmStream;
119   Triple TT(M.getTargetTriple());
120
121   if (TT.getOS() == Triple::Darwin)
122     AsmStream << ".section __TEXT,__text,regular,pure_instructions\n"
123               << ".align 4, 0x90\n";
124   else
125     AsmStream << ".text\n"
126               << ".align 16, 0x90\n";
127
128   AsmStream << ResolverBlockPtrName << ":\n"
129             << "  .quad " << ResolverBlockAddr << "\n";
130
131   auto GetLabelName =
132     [=](unsigned I) {
133       std::ostringstream LabelStream;
134       LabelStream << "orc_jcc_" << (StartIndex + I);
135       return LabelStream.str();
136   };
137
138   for (unsigned I = 0; I < NumCalls; ++I)
139     AsmStream << GetLabelName(I) << ":\n"
140               << "  callq *" << ResolverBlockPtrName << "(%rip)\n";
141
142   M.appendModuleInlineAsm(AsmStream.str());
143
144   return GetLabelName;
145 }
146
147 OrcX86_64::IndirectStubsInfo::IndirectStubsInfo(IndirectStubsInfo &&Other) {
148   StubsBlock = std::move(Other.StubsBlock);
149   PtrsBlock = std::move(Other.PtrsBlock);
150   Other.StubsBlock = sys::MemoryBlock();
151   Other.PtrsBlock = sys::MemoryBlock();
152 }
153
154 OrcX86_64::IndirectStubsInfo&
155 OrcX86_64::IndirectStubsInfo::operator=(IndirectStubsInfo &&Other) {
156   StubsBlock = std::move(Other.StubsBlock);
157   PtrsBlock = std::move(Other.PtrsBlock);
158   Other.StubsBlock = sys::MemoryBlock();
159   Other.PtrsBlock = sys::MemoryBlock();
160   return *this;
161 }
162
163 OrcX86_64::IndirectStubsInfo::~IndirectStubsInfo() {
164   sys::Memory::releaseMappedMemory(StubsBlock);
165   sys::Memory::releaseMappedMemory(PtrsBlock);
166 }
167
168 std::error_code OrcX86_64::emitIndirectStubsBlock(IndirectStubsInfo &StubsInfo,
169                                                   unsigned MinStubs,
170                                                   void *InitialPtrVal) {
171   // Stub format is:
172   //
173   // .section __orc_stubs
174   // stub1:
175   //                 jmpq    *ptr1(%rip)
176   //                 .byte   0xC4         ; <- Invalid opcode padding.
177   //                 .byte   0xF1
178   // stub2:
179   //                 jmpq    *ptr2(%rip)
180   //
181   // ...
182   //
183   // .section __orc_ptrs
184   // ptr1:
185   //                 .quad 0x0
186   // ptr2:
187   //                 .quad 0x0
188   //
189   // ...
190
191   const unsigned StubSize = IndirectStubsInfo::StubSize;
192
193   // Emit at least MinStubs, rounded up to fill the pages allocated.
194   unsigned PageSize = sys::Process::getPageSize();
195   unsigned NumPages = ((MinStubs * StubSize) + (PageSize - 1)) / PageSize;
196   unsigned NumStubs = (NumPages * PageSize) / StubSize;
197
198   // Allocate memory for stubs and pointers in one call.
199   std::error_code EC;
200   auto InitialBlock = sys::Memory::allocateMappedMemory(2 * NumPages * PageSize,
201                                                         nullptr,
202                                                         sys::Memory::MF_READ |
203                                                         sys::Memory::MF_WRITE,
204                                                         EC);
205
206   if (EC)
207     return EC;
208
209   // Create separate MemoryBlocks representing the stubs and pointers.
210   sys::MemoryBlock StubsBlock(InitialBlock.base(), NumPages * PageSize);
211   sys::MemoryBlock PtrsBlock(static_cast<char*>(InitialBlock.base()) +
212                              NumPages * PageSize,
213                              NumPages * PageSize);
214
215   // Populate the stubs page stubs and mark it executable.
216   uint64_t *Stub = reinterpret_cast<uint64_t*>(StubsBlock.base());
217   uint64_t PtrOffsetField =
218     static_cast<uint64_t>(NumPages * PageSize - 6) << 16;
219   for (unsigned I = 0; I < NumStubs; ++I)
220     Stub[I] = 0xF1C40000000025ff | PtrOffsetField;
221
222   if (auto EC = sys::Memory::protectMappedMemory(StubsBlock,
223                                                  sys::Memory::MF_READ |
224                                                  sys::Memory::MF_EXEC))
225     return EC;
226
227   // Initialize all pointers to point at FailureAddress.
228   void **Ptr = reinterpret_cast<void**>(PtrsBlock.base());
229   for (unsigned I = 0; I < NumStubs; ++I)
230     Ptr[I] = InitialPtrVal;
231
232   StubsInfo.NumStubs = NumStubs;
233   StubsInfo.StubsBlock = std::move(StubsBlock);
234   StubsInfo.PtrsBlock = std::move(PtrsBlock);
235
236   return std::error_code();
237 }
238
239 } // End namespace orc.
240 } // End namespace llvm.