2 +----------------------------------------------------------------------+
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"
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
39 if (m_cache
.m_selection
== Selection::Default
) {
40 // Profile has higher precedence than Hot.
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
)
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");
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 "
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
);
115 base
= (uint8_t*)malloc(allocationSize
);
118 fprintf(stderr
, "could not allocate %zd bytes for translation cache\n",
123 low_malloc_skip_huge(base
, base
+ allocationSize
- 1);
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
;
138 TRACE(1, "init ahot @%p\n", base
);
139 m_hot
.init(base
, kAHotSize
, "hot");
140 enhugen(base
, kAHotSize
>> 20);
142 m_hot
.skip(misalign
);
146 TRACE(1, "init a @%p\n", base
);
148 m_main
.init(base
, kASize
, "main");
149 enhugen(base
, RuntimeOption::EvalTCNumHugeHotMB
);
152 m_main
.skip(misalign
);
155 TRACE(1, "init aprof @%p\n", base
);
156 m_prof
.init(base
, kAProfSize
, "prof");
159 TRACE(1, "init acold @%p\n", base
);
160 m_cold
.init(base
, kAColdSize
, "cold");
161 enhugen(base
, RuntimeOption::EvalTCNumHugeColdMB
);
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");
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
);
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 {
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.
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
);