Switch-related cleanup
[hiphop-php.git] / hphp / runtime / vm / jit / srcdb.h
blob30ec5cfa86c52cb3f1bd9c5c2152f549eed98ee0
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 +----------------------------------------------------------------------+
16 #ifndef incl_HPHP_SRCDB_H_
17 #define incl_HPHP_SRCDB_H_
19 #include <boost/noncopyable.hpp>
20 #include <algorithm>
21 #include <atomic>
23 #include "hphp/util/asm-x64.h"
24 #include "hphp/util/trace.h"
25 #include "hphp/util/mutex.h"
27 #include "hphp/runtime/vm/jit/stack-offsets.h"
28 #include "hphp/runtime/vm/jit/types.h"
29 #include "hphp/runtime/vm/srckey.h"
30 #include "hphp/runtime/vm/tread-hash-map.h"
32 namespace HPHP { namespace jit {
34 struct CodeGenFixups;
35 struct RelocationInfo;
37 template<typename T>
38 struct GrowableVector {
39 GrowableVector() {}
40 GrowableVector(GrowableVector&& other) noexcept : m_vec(other.m_vec) {
41 other.m_vec = nullptr;
43 GrowableVector& operator=(GrowableVector&& other) {
44 m_vec = other.m_vec;
45 other.m_vec = nullptr;
46 return *this;
48 GrowableVector(const GrowableVector&) = delete;
49 GrowableVector& operator=(const GrowableVector&) = delete;
51 size_t size() const {
52 return m_vec ? m_vec->m_size : 0;
54 T& operator[](const size_t idx) {
55 assertx(idx < size());
56 return m_vec->m_data[idx];
58 const T& operator[](const size_t idx) const {
59 assertx(idx < size());
60 return m_vec->m_data[idx];
62 void push_back(const T& datum) {
63 if (!m_vec) {
64 m_vec = Impl::make();
66 m_vec = m_vec->push_back(datum);
68 void swap(GrowableVector<T>& other) {
69 std::swap(m_vec, other.m_vec);
71 void clear() {
72 free(m_vec);
73 m_vec = nullptr;
75 bool empty() const {
76 return !m_vec;
78 T* begin() const { return m_vec ? m_vec->m_data : (T*)this; }
79 T* end() const { return m_vec ? &m_vec->m_data[m_vec->m_size] : (T*)this; }
80 private:
81 struct Impl {
82 uint32_t m_size;
83 T m_data[1]; // Actually variable length
84 Impl() : m_size(0) { }
85 static Impl* make() {
86 static_assert(std::is_trivially_destructible<T>::value,
87 "GrowableVector can only hold trivially "
88 "destructible types");
89 auto mem = malloc(sizeof(Impl));
90 return new (mem) Impl;
92 Impl* push_back(const T& datum) {
93 Impl* gv;
94 // m_data always has room for at least one element due to the m_data[1]
95 // declaration, so the realloc() code first has to kick in when a second
96 // element is about to be pushed.
97 if (folly::isPowTwo(m_size)) {
98 gv = (Impl*)realloc(this,
99 offsetof(Impl, m_data) + 2 * m_size * sizeof(T));
100 } else {
101 gv = this;
103 gv->m_data[gv->m_size++] = datum;
104 return gv;
107 Impl* m_vec{nullptr};
111 * Incoming branches between different translations are tracked using
112 * this structure.
114 * This allows us to smash them later to point to different things.
115 * We handle conditional and unconditional jumps, as well as pointers
116 * to code (via IncomingBranch::ADDR, used for example in a switch
117 * table).
119 * We don't need to track which condition code a conditional jump used
120 * because we take care to smash only the address and leave the code
121 * intact.
123 struct IncomingBranch {
124 enum class Tag {
125 JMP,
126 JCC,
127 ADDR,
130 using Opaque = CompactTaggedPtr<void>::Opaque;
132 static IncomingBranch jmpFrom(TCA from) {
133 return IncomingBranch(Tag::JMP, from);
135 static IncomingBranch jccFrom(TCA from) {
136 return IncomingBranch(Tag::JCC, from);
138 static IncomingBranch addr(TCA* from) {
139 return IncomingBranch(Tag::ADDR, TCA(from));
142 Opaque getOpaque() const {
143 return m_ptr.getOpaque();
145 explicit IncomingBranch(CompactTaggedPtr<void>::Opaque v) : m_ptr(v) {}
147 Tag type() const { return m_ptr.tag(); }
148 TCA toSmash() const { return TCA(m_ptr.ptr()); }
149 void relocate(RelocationInfo& rel);
150 void adjust(TCA addr) {
151 m_ptr.set(m_ptr.tag(), addr);
153 void patch(TCA dest);
154 TCA target() const;
155 private:
156 explicit IncomingBranch(Tag type, TCA toSmash) {
157 m_ptr.set(type, toSmash);
160 /* needed to allow IncomingBranch to be put in a GrowableVector */
161 friend class GrowableVector<IncomingBranch>;
162 IncomingBranch() {}
164 CompactTaggedPtr<void,Tag> m_ptr;
168 * SrcRec: record of translator output for a given source location.
170 struct SrcRec {
171 SrcRec()
172 : m_topTranslation(nullptr)
173 , m_anchorTranslation(0)
174 , m_dbgBranchGuardSrc(nullptr)
175 , m_guard(0)
179 * The top translation is our first target, a translation whose type
180 * checks properly chain through all other translations. Usually this will
181 * be the first translation.
183 * This function can be safely called without holding the write
184 * lease.
186 TCA getTopTranslation() const {
187 return m_topTranslation.load(std::memory_order_acquire);
191 * The following functions are used during creation of new
192 * translations or when inserting debug guards. May only be called
193 * when holding the translator write lease.
195 void setFuncInfo(const Func* f);
196 void chainFrom(IncomingBranch br);
197 void emitFallbackJump(CodeBlock& cb, ConditionCode cc = CC_None);
198 void registerFallbackJump(TCA from, ConditionCode cc = CC_None);
199 void emitFallbackJumpCustom(CodeBlock& cb,
200 CodeBlock& frozen,
201 SrcKey sk,
202 TransFlags trflags,
203 ConditionCode cc = CC_None);
204 TCA getFallbackTranslation() const;
205 void newTranslation(TCA newStart,
206 GrowableVector<IncomingBranch>& inProgressTailBranches);
207 void replaceOldTranslations();
208 void addDebuggerGuard(TCA dbgGuard, TCA m_dbgBranchGuardSrc);
209 bool hasDebuggerGuard() const { return m_dbgBranchGuardSrc != nullptr; }
210 const MD5& unitMd5() const { return m_unitMd5; }
212 const GrowableVector<TCA>& translations() const {
213 return m_translations;
216 const GrowableVector<IncomingBranch>& tailFallbackJumps() {
217 return m_tailFallbackJumps;
221 * The anchor translation is a retranslate request for the current
222 * SrcKey that will continue the tracelet chain.
224 void setAnchorTranslation(TCA anc) {
225 assertx(!m_anchorTranslation);
226 assertx(m_tailFallbackJumps.empty());
227 m_anchorTranslation = anc;
231 * Returns the VM stack offset the translations in the SrcRec have, in
232 * situations where we need to and can know.
234 * Pre: this SrcRec is for a non-resumed SrcKey
235 * Pre: setAnchorTranslation has been called
237 FPInvOffset nonResumedSPOff() const;
239 const GrowableVector<IncomingBranch>& incomingBranches() const {
240 return m_incomingBranches;
243 void relocate(RelocationInfo& rel);
246 * There is an unlikely race in retranslate, where two threads
247 * could simultaneously generate the same translation for a
248 * tracelet. In practice its almost impossible to hit this, unless
249 * Eval.JitRequireWriteLease is set. But when it is set, we hit
250 * it a lot.
251 * m_guard doesn't quite solve it, but its as good as things were
252 * before.
254 bool tryLock() {
255 uint32_t val = 0;
256 return m_guard.compare_exchange_strong(val, 1);
259 void freeLock() {
260 m_guard = 0;
263 private:
264 void patchIncomingBranches(TCA newStart);
266 private:
267 // This either points to the most recent translation in the
268 // translations vector, or if hasDebuggerGuard() it points to the
269 // debug guard.
270 std::atomic<TCA> m_topTranslation;
273 * The following members are all protected by the translator write
274 * lease. They can only be read when the lease is held.
277 // We chain new translations onto the end of the list, so we need to
278 // track all the fallback jumps from the "tail" translation so we
279 // can rewrire them to new ones.
280 TCA m_anchorTranslation;
281 GrowableVector<IncomingBranch> m_tailFallbackJumps;
283 GrowableVector<TCA> m_translations;
284 GrowableVector<IncomingBranch> m_incomingBranches;
285 MD5 m_unitMd5;
286 // The branch src for the debug guard, if this has one.
287 TCA m_dbgBranchGuardSrc;
288 std::atomic<uint32_t> m_guard;
291 class SrcDB : boost::noncopyable {
292 // Although it seems tempting, in an experiment, trying to stash the
293 // top TCA in place in the hashtable did worse than dereferencing a
294 // SrcRec* to get it. Maybe could be possible with a better hash
295 // function or lower max load factor. (See D450383.)
296 typedef TreadHashMap<SrcKey::AtomicInt,SrcRec*,int64_hash> THM;
297 THM m_map;
298 public:
299 explicit SrcDB()
300 : m_map(1024)
303 typedef THM::iterator iterator;
304 typedef THM::const_iterator const_iterator;
306 iterator begin() { return m_map.begin(); }
307 iterator end() { return m_map.end(); }
308 const_iterator begin() const { return m_map.begin(); }
309 const_iterator end() const { return m_map.end(); }
311 SrcRec* find(SrcKey sk) const {
312 SrcRec* const* p = m_map.find(sk.toAtomicInt());
313 return p ? *p : 0;
316 SrcRec* insert(SrcKey sk) {
317 return *m_map.insert(sk.toAtomicInt(), new SrcRec);
324 #endif