2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2013 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 #ifndef incl_HPHP_DATA_BLOCK_H
18 #define incl_HPHP_DATA_BLOCK_H
24 #include "hphp/util/assertions.h"
25 #include "hphp/util/util.h"
30 constexpr int nosize
= 0;
31 constexpr int byte
= 1;
32 constexpr int word
= 2;
33 constexpr int dword
= 4;
34 constexpr int qword
= 8;
37 typedef uint8_t* Address
;
38 typedef uint8_t* CodeAddress
;
40 #define BLOCK_EMISSION_ASSERT(canEmitCheck) \
41 always_assert_log(canEmitCheck, \
43 "Data block emission failed. This almost certainly means the TC is " \
44 "full. If this is the case, increasing Eval.JitASize, " \
45 "Eval.JitAStubsSize and Eval.JitGlobalDataSize in the configuration " \
46 "file when running this script or application should fix this " \
52 * DataBlock is a simple bump-allocating wrapper around a chunk of memory.
56 DataBlock() : m_base(nullptr), m_frontier(nullptr), m_size(0) {}
58 DataBlock(const DataBlock
& other
) = delete;
59 DataBlock
& operator=(const DataBlock
& other
) = delete;
61 DataBlock(DataBlock
&& other
)
62 : m_base(other
.m_base
), m_frontier(other
.m_frontier
), m_size(other
.m_size
) {
63 other
.m_base
= other
.m_frontier
= nullptr;
67 DataBlock
& operator=(DataBlock
&& other
) {
68 m_base
= other
.m_base
;
69 m_frontier
= other
.m_frontier
;
70 m_size
= other
.m_size
;
71 other
.m_base
= other
.m_frontier
= nullptr;
77 * Uses an existing chunk of memory.
79 void init(Address start
, size_t sz
) {
80 m_base
= m_frontier
= start
;
87 * Simple bump allocator.
91 * Some clients need to allocate with an externally maintained frontier.
92 * allocAt supports this.
94 void* allocAt(size_t &frontierOff
, size_t sz
, size_t align
= 16) {
95 align
= Util::roundUpToPowerOfTwo(align
);
96 uint8_t* frontier
= m_base
+ frontierOff
;
97 assert(m_base
&& frontier
);
98 int slop
= uintptr_t(frontier
) & (align
- 1);
100 int leftInBlock
= (align
- slop
);
101 frontier
+= leftInBlock
;
102 frontierOff
+= leftInBlock
;
104 assert((uintptr_t(frontier
) & (align
- 1)) == 0);
106 assert(frontierOff
<= m_size
);
110 template<typename T
> T
* alloc(size_t align
= 16, int n
= 1) {
111 size_t frontierOff
= m_frontier
- m_base
;
112 T
* retval
= (T
*)allocAt(frontierOff
, sizeof(T
) * n
, align
);
113 m_frontier
= m_base
+ frontierOff
;
117 bool canEmit(size_t nBytes
) {
118 assert(m_frontier
>= m_base
);
119 assert(m_frontier
<= m_base
+ m_size
);
120 return m_frontier
+ nBytes
<= m_base
+ m_size
;
123 bool isValidAddress(const CodeAddress tca
) const {
124 return tca
>= m_base
&& tca
< (m_base
+ m_size
);
127 bool isFrontierAligned(const size_t alignment
) const {
128 return ((uintptr_t)m_frontier
& (alignment
- 1)) == 0;
131 void byte(const uint8_t byte
) {
132 BLOCK_EMISSION_ASSERT(canEmit(sz::byte
));
134 m_frontier
+= sz::byte
;
136 void word(const uint16_t word
) {
137 BLOCK_EMISSION_ASSERT(canEmit(sz::word
));
138 *(uint16_t*)m_frontier
= word
;
139 m_frontier
+= sz::word
;
141 void dword(const uint32_t dword
) {
142 BLOCK_EMISSION_ASSERT(canEmit(sz::dword
));
143 *(uint32_t*)m_frontier
= dword
;
144 m_frontier
+= sz::dword
;
146 void qword(const uint64_t qword
) {
147 BLOCK_EMISSION_ASSERT(canEmit(sz::qword
));
148 *(uint64_t*)m_frontier
= qword
;
149 m_frontier
+= sz::qword
;
152 void bytes(size_t n
, const uint8_t *bs
) {
153 BLOCK_EMISSION_ASSERT(canEmit(n
));
155 // If it is a modest number of bytes, try executing in one machine
156 // store. This allows control-flow edges, including nop, to be
157 // appear idempotent on other CPUs.
162 u
.qword
= *(uint64_t*)m_frontier
;
163 for (size_t i
= 0; i
< n
; ++i
) {
167 // If this address doesn't span cache lines, on x64 this is an
168 // atomic store. We're not using atomic_release_store() because
169 // this code path occurs even when it may span cache lines, and
170 // that function asserts about this.
171 *reinterpret_cast<uint64_t*>(m_frontier
) = u
.qword
;
173 memcpy(m_frontier
, bs
, n
);
178 void skip(size_t nbytes
) {
179 alloc
<uint8_t>(1, nbytes
);
182 Address
base() const { return m_base
; }
183 Address
frontier() const { return m_frontier
; }
185 void setFrontier(Address addr
) {
189 size_t capacity() const {
193 size_t used() const {
194 return m_frontier
- m_base
;
197 size_t available() const {
198 return m_size
- (m_frontier
- m_base
);
201 bool contains(CodeAddress addr
) const {
202 return addr
>= m_base
&& addr
< (m_base
+ m_size
);
206 return m_base
== m_frontier
;
214 memset(m_base
, 0, m_frontier
- m_base
);
224 typedef DataBlock CodeBlock
;
226 //////////////////////////////////////////////////////////////////////
230 CodeAddress m_oldFrontier
;
232 explicit UndoMarker(CodeBlock
& cb
)
234 , m_oldFrontier(cb
.frontier()) {
238 m_cb
.setFrontier(m_oldFrontier
);
243 * RAII bookmark for scoped rewinding of frontier.
245 class CodeCursor
: public UndoMarker
{
247 CodeCursor(CodeBlock
& cb
, CodeAddress newFrontier
) : UndoMarker(cb
) {
248 cb
.setFrontier(newFrontier
);
251 ~CodeCursor() { undo(); }