Implement reusable service requests for ARM
[hiphop-php.git] / hphp / util / data-block.h
blob82fe9120d42fe539be3ab7f57ed3a796a51e7812
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
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
20 #include <cstdint>
21 #include <cstring>
22 #include <sys/mman.h>
24 #include "hphp/util/assertions.h"
25 #include "hphp/util/util.h"
27 namespace HPHP {
29 namespace sz {
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, \
42 [] { return \
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 " \
47 "problem."; \
48 } \
51 /**
52 * DataBlock is a simple bump-allocating wrapper around a chunk of memory.
54 struct DataBlock {
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;
64 other.m_size = 0;
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;
72 other.m_size = 0;
73 return *this;
76 /**
77 * Uses an existing chunk of memory.
79 void init(Address start, size_t sz) {
80 m_base = m_frontier = start;
81 m_size = sz;
85 * alloc --
87 * Simple bump allocator.
89 * allocAt --
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);
99 if (slop) {
100 int leftInBlock = (align - slop);
101 frontier += leftInBlock;
102 frontierOff += leftInBlock;
104 assert((uintptr_t(frontier) & (align - 1)) == 0);
105 frontierOff += sz;
106 assert(frontierOff <= m_size);
107 return frontier;
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;
114 return retval;
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));
133 *m_frontier = 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));
154 if (n <= 8) {
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.
158 union {
159 uint64_t qword;
160 uint8_t bytes[8];
161 } u;
162 u.qword = *(uint64_t*)m_frontier;
163 for (size_t i = 0; i < n; ++i) {
164 u.bytes[i] = bs[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;
172 } else {
173 memcpy(m_frontier, bs, n);
175 m_frontier += 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) {
186 m_frontier = addr;
189 size_t capacity() const {
190 return m_size;
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);
205 bool empty() const {
206 return m_base == m_frontier;
209 void clear() {
210 m_frontier = m_base;
213 void zero() {
214 memset(m_base, 0, m_frontier - m_base);
215 clear();
218 protected:
219 Address m_base;
220 Address m_frontier;
221 size_t m_size;
224 typedef DataBlock CodeBlock;
226 //////////////////////////////////////////////////////////////////////
228 class UndoMarker {
229 CodeBlock& m_cb;
230 CodeAddress m_oldFrontier;
231 public:
232 explicit UndoMarker(CodeBlock& cb)
233 : m_cb(cb)
234 , m_oldFrontier(cb.frontier()) {
237 void undo() {
238 m_cb.setFrontier(m_oldFrontier);
243 * RAII bookmark for scoped rewinding of frontier.
245 class CodeCursor : public UndoMarker {
246 public:
247 CodeCursor(CodeBlock& cb, CodeAddress newFrontier) : UndoMarker(cb) {
248 cb.setFrontier(newFrontier);
251 ~CodeCursor() { undo(); }
255 #endif