Expand jit documentation a bit
[hiphop-php.git] / hphp / util / code-cache.cpp
blobe7ddfc374108d8fb3545db7a9c5def2513ec3652
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 +----------------------------------------------------------------------+
7 | This source file is subject to version 3.01 of the PHP license, |
8 | that is bundled with this package in the file LICENSE, and is |
9 | available through the world-wide-web at the following url: |
10 | http://www.php.net/license/3_01.txt |
11 | If you did not receive a copy of the PHP license and are unable to |
12 | obtain it through the world-wide-web, please send a note to |
13 | license@php.net so we can mail you a copy immediately. |
14 +----------------------------------------------------------------------+
17 #include "hphp/util/code-cache.h"
19 #include "hphp/runtime/base/runtime-option.h"
20 #include "hphp/util/alloc.h"
21 #include "hphp/util/asm-x64.h"
22 #include "hphp/util/trace.h"
24 namespace HPHP {
26 TRACE_SET_MOD(mcg);
28 // This value should be enough bytes to emit the "main" part of a
29 // minimal translation, which consists of a single jump (for a
30 // REQ_INTERPRET service request).
31 static const int kMinTranslationBytes = 8;
33 CodeCache::Selector::Selector(const Args& args)
34 : m_cache(args.m_cache)
35 , m_oldSelection(m_cache.m_selection)
37 // If a CodeBlock other than 'main' has already been selected, keep that
38 // selection.
39 if (m_cache.m_selection == Selection::Default) {
40 // Profile has higher precedence than Hot.
41 if (args.m_profile) {
42 m_cache.m_selection = Selection::Profile;
43 } else if (args.m_hot && m_cache.m_hot.available() > kMinTranslationBytes) {
44 m_cache.m_selection = Selection::Hot;
49 CodeCache::Selector::~Selector() {
50 m_cache.m_selection = m_oldSelection;
53 CodeCache::CodeCache()
54 : m_selection(Selection::Default)
55 , m_lock(false)
57 static const size_t kRoundUp = 2 << 20;
59 auto ru = [=] (size_t sz) { return sz + (-sz & (kRoundUp - 1)); };
61 const size_t kAHotSize = ru(RuntimeOption::RepoAuthoritative ?
62 RuntimeOption::EvalJitAHotSize : 0);
63 const size_t kASize = ru(RuntimeOption::EvalJitASize);
64 const size_t kAProfSize = ru(RuntimeOption::EvalJitPGO ?
65 RuntimeOption::EvalJitAProfSize : 0);
66 const size_t kAColdSize = ru(RuntimeOption::EvalJitAColdSize);
67 const size_t kAFrozenSize = ru(RuntimeOption::EvalJitAFrozenSize);
69 const size_t kGDataSize = ru(RuntimeOption::EvalJitGlobalDataSize);
70 m_totalSize = kAHotSize + kASize + kAColdSize + kAProfSize +
71 kAFrozenSize + kGDataSize;
72 m_codeSize = m_totalSize - kGDataSize;
74 if ((kASize < (10 << 20)) ||
75 (kAColdSize < (4 << 20)) ||
76 (kAFrozenSize < (6 << 20)) ||
77 (kGDataSize < (2 << 20))) {
78 fprintf(stderr, "Allocation sizes ASize, AColdSize, AFrozenSize and "
79 "GlobalDataSize are too small.\n");
80 exit(1);
83 if (m_totalSize > (2ul << 30)) {
84 fprintf(stderr,"Combined size of ASize, AColdSize, AFrozenSize and "
85 "GlobalDataSize must be < 2GiB to support 32-bit relative "
86 "addresses\n");
87 exit(1);
90 auto enhugen = [&](void* base, int numMB) {
91 if (RuntimeOption::EvalMapTCHuge) {
92 assert((uintptr_t(base) & (kRoundUp - 1)) == 0);
93 hintHuge(base, numMB << 20);
97 // We want to ensure that all code blocks are close to each other so that we
98 // can short jump/point between them. Thus we allocate one slab and divide it
99 // between the various blocks.
101 // Using sbrk to ensure its in the bottom 2G, so we avoid the need for
102 // trampolines, and get to use shorter instructions for tc addresses.
103 size_t allocationSize = m_totalSize;
104 uint8_t* base = (uint8_t*)sbrk(0);
105 if (base != (uint8_t*)-1) {
106 assert(!(allocationSize & (kRoundUp - 1)));
107 // Make sure that we have space to round up to the start of a huge page
108 allocationSize += -(uint64_t)base & (kRoundUp - 1);
109 base = (uint8_t*)sbrk(allocationSize);
111 if (base == (uint8_t*)-1) {
112 allocationSize = m_totalSize + kRoundUp - 1;
113 base = (uint8_t*)low_malloc(allocationSize);
114 if (!base) {
115 base = (uint8_t*)malloc(allocationSize);
117 if (!base) {
118 fprintf(stderr, "could not allocate %zd bytes for translation cache\n",
119 allocationSize);
120 exit(1);
122 } else {
123 low_malloc_skip_huge(base, base + allocationSize - 1);
125 assert(base);
126 m_base = base;
127 base += -(uint64_t)base & (kRoundUp - 1);
129 numa_interleave(base, m_totalSize);
131 TRACE(1, "init atrampolines @%p\n", base);
133 m_trampolines.init(base, kTrampolinesBlockSize, "trampolines");
135 auto misalign = kTrampolinesBlockSize;
137 if (kAHotSize) {
138 TRACE(1, "init ahot @%p\n", base);
139 m_hot.init(base, kAHotSize, "hot");
140 enhugen(base, kAHotSize >> 20);
141 base += kAHotSize;
142 m_hot.skip(misalign);
143 misalign = 0;
146 TRACE(1, "init a @%p\n", base);
148 m_main.init(base, kASize, "main");
149 enhugen(base, RuntimeOption::EvalTCNumHugeHotMB);
150 m_mainBase = base;
151 base += kASize;
152 m_main.skip(misalign);
153 misalign = 0;
155 TRACE(1, "init aprof @%p\n", base);
156 m_prof.init(base, kAProfSize, "prof");
157 base += kAProfSize;
159 TRACE(1, "init acold @%p\n", base);
160 m_cold.init(base, kAColdSize, "cold");
161 enhugen(base, RuntimeOption::EvalTCNumHugeColdMB);
162 base += kAColdSize;
164 TRACE(1, "init afrozen @%p\n", base);
165 m_frozen.init(base, kAFrozenSize, "afrozen");
166 base += kAFrozenSize;
168 TRACE(1, "init gdata @%p\n", base);
169 m_data.init(base, kGDataSize, "gdata");
170 base += kGDataSize;
172 assert(base - m_base <= allocationSize);
173 assert(base - m_base + kRoundUp > allocationSize);
176 CodeCache::~CodeCache() {
177 int result = munmap(m_trampolines.base(), m_totalSize);
178 if (result != 0) {
179 perror("freeSlab: munmap");
183 CodeBlock& CodeCache::blockFor(CodeAddress addr) {
184 always_assert(!m_lock);
185 return JIT::codeBlockChoose(addr, m_main, m_hot, m_prof,
186 m_cold, m_trampolines, m_frozen);
189 size_t CodeCache::totalUsed() const {
190 size_t ret = 0;
191 forEachBlock([&ret](const char*, const CodeBlock& b) {
192 // A thread with the write lease may be modifying b.m_frontier while we
193 // call b.used() but it should never modify b.m_base. This means that at
194 // worst b.used() will return a slightly stale value.
195 ret += b.used();
197 return ret;
200 bool CodeCache::isValidCodeAddress(CodeAddress addr) const {
201 return addr >= m_base && addr < m_base + m_codeSize;
204 void CodeCache::protect() {
205 mprotect(m_base, m_codeSize, PROT_READ | PROT_EXEC);
208 void CodeCache::unprotect() {
209 mprotect(m_base, m_codeSize, PROT_READ | PROT_WRITE | PROT_EXEC);
212 CodeBlock& CodeCache::main() {
213 always_assert(!m_lock);
214 switch (m_selection) {
215 case Selection::Default: return m_main;
216 case Selection::Hot: return m_hot;
217 case Selection::Profile: return m_prof;
219 always_assert(false && "Invalid Selection");
222 CodeBlock& CodeCache::cold() {
223 always_assert(!m_lock);
224 switch (m_selection) {
225 case Selection::Default:
226 case Selection::Hot: return m_cold;
227 case Selection::Profile: return frozen();
229 always_assert(false && "Invalid Selection");
232 CodeBlock& CodeCache::frozen() {
233 always_assert(!m_lock);
234 return m_frozen;