summaryrefslogtreecommitdiff
path: root/tools/lli/ChildTarget/Unix/ChildTarget.inc
blob: cc95810dc8c6bf305b890d070a73e616ffe9451a (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
//===- ChildTarget.inc - Child process for external JIT execution for Unix -==//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Implementation of the Unix-specific parts of the ChildTarget class
// which executes JITed code in a separate process from where it was built.
//
//===----------------------------------------------------------------------===//

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif

#ifdef __APPLE__
#include <mach/mach.h>
#endif

#if defined(__mips__)
#  if defined(__OpenBSD__)
#    include <mips64/sysarch.h>
#  else
#    include <sys/cachectl.h>
#  endif
#endif

#ifdef __APPLE__
extern "C" void sys_icache_invalidate(const void *Addr, size_t len);
#else
extern "C" void __clear_cache(void *, void*);
#endif

namespace {

struct ConnectionData_t {
  int InputPipe;
  int OutputPipe;

  ConnectionData_t(int in, int out) : InputPipe(in), OutputPipe(out) {}
};

} // namespace

LLIChildTarget::~LLIChildTarget() {
  delete static_cast<ConnectionData_t *>(ConnectionData);
}

// OS-specific methods
void LLIChildTarget::initializeConnection() {
  // Store the parent ends of the pipes
  ConnectionData = (void*)new ConnectionData_t(STDIN_FILENO, STDOUT_FILENO);
}

int LLIChildTarget::WriteBytes(const void *Data, size_t Size) {
  return write(((ConnectionData_t*)ConnectionData)->OutputPipe, Data, Size);
}

int LLIChildTarget::ReadBytes(void *Data, size_t Size) {
  return read(((ConnectionData_t*)ConnectionData)->InputPipe, Data, Size);
}

// The functions below duplicate functionality that is implemented in
// Support/Memory.cpp with the goal of avoiding a dependency on any
// llvm libraries.

uint64_t LLIChildTarget::allocate(uint32_t Alignment, uint32_t Size) {
  if (!Alignment)
    Alignment = 16;

  static const size_t PageSize = getpagesize();
  const size_t NumPages = (Size+PageSize-1)/PageSize;
  Size = NumPages*PageSize;

  int fd = -1;
#ifdef NEED_DEV_ZERO_FOR_MMAP
  static int zero_fd = open("/dev/zero", O_RDWR);
  if (zero_fd == -1)
    return 0;
  fd = zero_fd;
#endif

  int MMFlags = MAP_PRIVATE |
#ifdef HAVE_MMAP_ANONYMOUS
  MAP_ANONYMOUS
#else
  MAP_ANON
#endif
  ; // Ends statement above

  uint64_t Addr = (uint64_t)::mmap(0, Size, PROT_READ | PROT_WRITE, MMFlags, fd, 0);
  if (Addr == (uint64_t)MAP_FAILED)
    return 0;

  // Align the address.
  Addr = (Addr + Alignment - 1) & ~(uintptr_t)(Alignment - 1);

  m_AllocatedBufferMap[Addr] = Size;

  // Return aligned address
  return Addr;
}

void LLIChildTarget::makeSectionExecutable(uint64_t Addr, uint32_t Size) {
  // FIXME: We have to mark the memory as RWX because multiple code chunks may
  // be on the same page.  The RemoteTarget interface should be changed to
  // work around that.
  int Result = ::mprotect((void*)Addr, Size, PROT_READ | PROT_WRITE | PROT_EXEC);
  if (Result != 0)
    InvalidateInstructionCache((const void *)Addr, Size);
}

/// InvalidateInstructionCache - Before the JIT can run a block of code
/// that has been emitted it must invalidate the instruction cache on some
/// platforms.
void LLIChildTarget::InvalidateInstructionCache(const void *Addr,
                                        size_t Len) {

// icache invalidation for PPC and ARM.
#if defined(__APPLE__)

#  if (defined(__POWERPC__) || defined (__ppc__) || \
     defined(_POWER) || defined(_ARCH_PPC)) || defined(__arm__)
  sys_icache_invalidate(const_cast<void *>(Addr), Len);
#  endif

#else

#  if (defined(__POWERPC__) || defined (__ppc__) || \
       defined(_POWER) || defined(_ARCH_PPC)) && defined(__GNUC__)
  const size_t LineSize = 32;

  const intptr_t Mask = ~(LineSize - 1);
  const intptr_t StartLine = ((intptr_t) Addr) & Mask;
  const intptr_t EndLine = ((intptr_t) Addr + Len + LineSize - 1) & Mask;

  for (intptr_t Line = StartLine; Line < EndLine; Line += LineSize)
    asm volatile("dcbf 0, %0" : : "r"(Line));
  asm volatile("sync");

  for (intptr_t Line = StartLine; Line < EndLine; Line += LineSize)
    asm volatile("icbi 0, %0" : : "r"(Line));
  asm volatile("isync");
#  elif defined(__arm__) && defined(__GNUC__)
  // FIXME: Can we safely always call this for __GNUC__ everywhere?
  const char *Start = static_cast<const char *>(Addr);
  const char *End = Start + Len;
  __clear_cache(const_cast<char *>(Start), const_cast<char *>(End));
#  elif defined(__mips__)
  const char *Start = static_cast<const char *>(Addr);
  cacheflush(const_cast<char *>(Start), Len, BCACHE);
#  endif

#endif  // end apple
}

void LLIChildTarget::releaseMemory(uint64_t Addr, uint32_t Size) {
  ::munmap((void*)Addr, Size);
}