Fix spilling bug
[hiphop-php.git] / hphp / runtime / vm / jit / srcdb.h
blobab83752434646cd6a02f5e3669a6a1359a786ed7
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 +----------------------------------------------------------------------+
16 #ifndef incl_HPHP_SRCDB_H_
17 #define incl_HPHP_SRCDB_H_
19 #include <boost/noncopyable.hpp>
21 #include "hphp/util/asm-x64.h"
22 #include "hphp/util/trace.h"
23 #include "hphp/util/mutex.h"
24 #include "hphp/runtime/vm/jit/types.h"
25 #include "hphp/runtime/vm/srckey.h"
26 #include "hphp/runtime/vm/tread_hash_map.h"
28 namespace HPHP {
29 namespace Transl {
32 * Incoming branches between different translations are tracked using
33 * this structure.
35 * This allows us to smash them later to point to different things.
36 * We handle conditional and unconditional jumps, as well as pointers
37 * to code (via IncomingBranch::ADDR, used for example in a switch
38 * table).
40 * We don't need to track which condition code a conditional jump used
41 * because we take care to smash only the address and leave the code
42 * intact.
44 struct IncomingBranch {
45 enum class Tag {
46 JMP,
47 JCC,
48 ADDR,
51 static IncomingBranch jmpFrom(TCA from) {
52 return IncomingBranch(Tag::JMP, from);
54 static IncomingBranch jccFrom(TCA from) {
55 return IncomingBranch(Tag::JCC, from);
57 static IncomingBranch addr(TCA* from) {
58 return IncomingBranch(Tag::ADDR, TCA(from));
61 Tag type() const { return m_type; }
62 TCA toSmash() const { return m_toSmash; }
64 private:
65 explicit IncomingBranch(Tag type, TCA toSmash)
66 : m_type(type)
67 , m_toSmash(toSmash)
70 Tag m_type;
71 TCA m_toSmash;
75 * SrcRec: record of translator output for a given source location.
77 struct SrcRec {
78 SrcRec()
79 : m_topTranslation(nullptr)
80 , m_anchorTranslation(0)
81 , m_dbgBranchGuardSrc(nullptr)
85 * The top translation is our first target, a translation whose type
86 * checks properly chain through all other translations. Usually this will
87 * be the most recently created translation.
89 * This function can be safely called without holding the write
90 * lease.
92 TCA getTopTranslation() const {
93 return atomic_acquire_load(&m_topTranslation);
97 * The following functions are used during creation of new
98 * translations or when inserting debug guards. May only be called
99 * when holding the translator write lease.
101 void setFuncInfo(const Func* f);
102 void chainFrom(IncomingBranch br);
103 void emitFallbackJump(TCA from, int cc = -1);
104 void newTranslation(TCA newStart);
105 void replaceOldTranslations();
106 void addDebuggerGuard(TCA dbgGuard, TCA m_dbgBranchGuardSrc);
107 bool hasDebuggerGuard() const { return m_dbgBranchGuardSrc != nullptr; }
108 const MD5& unitMd5() const { return m_unitMd5; }
110 const vector<TCA>& translations() const {
111 return m_translations;
115 * The anchor translation is a retranslate request for the current
116 * SrcKey that will continue the tracelet chain.
118 void setAnchorTranslation(TCA anc) {
119 assert(!m_anchorTranslation);
120 assert(m_tailFallbackJumps.empty());
121 m_anchorTranslation = anc;
124 const vector<IncomingBranch>& inProgressTailJumps() const {
125 return m_inProgressTailJumps;
128 void clearInProgressTailJumps() {
129 m_inProgressTailJumps.clear();
132 private:
133 TCA getFallbackTranslation() const;
134 void patch(IncomingBranch branch, TCA dest);
135 void patchIncomingBranches(TCA newStart);
137 private:
138 // This either points to the most recent translation in the
139 // translations vector, or if hasDebuggerGuard() it points to the
140 // debug guard. Read/write with atomic primitives only.
141 TCA m_topTranslation;
144 * The following members are all protected by the translator write
145 * lease. They can only be read when the lease is held.
148 // We chain new translations onto the end of the list, so we need to
149 // track all the fallback jumps from the "tail" translation so we
150 // can rewrire them to new ones.
151 TCA m_anchorTranslation;
152 vector<IncomingBranch> m_tailFallbackJumps;
153 vector<IncomingBranch> m_inProgressTailJumps;
155 vector<TCA> m_translations;
156 vector<IncomingBranch> m_incomingBranches;
157 MD5 m_unitMd5;
158 // The branch src for the debug guard, if this has one.
159 TCA m_dbgBranchGuardSrc;
163 * GrowableVector --
165 * We make a large number of these, and they typically only have one entry.
166 * It's a shame to use a 24-byte std::vector for this.
168 * Only gets larger. Non-standard interface because we may realloc
169 * at push_back() time.
171 template<typename T>
172 struct GrowableVector {
173 uint32_t m_size;
174 T m_data[1]; // Actually variable length
175 GrowableVector() : m_size(0) { }
176 size_t size() const {
177 return m_size;
179 T& operator[](const size_t idx) {
180 assert(idx < m_size);
181 return m_data[idx];
183 const T& operator[](const size_t idx) const {
184 assert(idx < m_size);
185 return m_data[idx];
187 GrowableVector* push_back(const T& datum) {
188 GrowableVector* gv;
189 // m_data always has room for at least one element due to the m_data[1]
190 // declaration, so the realloc() code first has to kick in when a second
191 // element is about to be pushed.
192 if (Util::isPowerOfTwo(m_size)) {
193 gv = (GrowableVector*)realloc(this,
194 offsetof(GrowableVector<T>, m_data) +
195 2 * m_size * sizeof(T));
196 } else {
197 gv = this;
199 gv->m_data[gv->m_size++] = datum;
200 return gv;
204 class SrcDB : boost::noncopyable {
205 // SrcKeys that depend on a particular file go here.
206 typedef hphp_hash_map<const Eval::PhpFile*,
207 GrowableVector<SrcKey>*,
208 pointer_hash<Eval::PhpFile> > FileDepMap;
209 FileDepMap m_deps;
211 // Although it seems tempting, in an experiment, trying to stash the
212 // top TCA in place in the hashtable did worse than dereferencing a
213 // SrcRec* to get it. Maybe could be possible with a better hash
214 // function or lower max load factor. (See D450383.)
215 typedef TreadHashMap<SrcKey::AtomicInt,SrcRec*,int64_hash> THM;
216 THM m_map;
217 public:
218 explicit SrcDB()
219 : m_map(1024)
222 typedef THM::iterator iterator;
223 typedef THM::const_iterator const_iterator;
225 iterator begin() { return m_map.begin(); }
226 iterator end() { return m_map.end(); }
227 const_iterator begin() const { return m_map.begin(); }
228 const_iterator end() const { return m_map.end(); }
230 SrcRec* find(const SrcKey& sk) const {
231 SrcRec* const* p = m_map.find(sk.toAtomicInt());
232 return p ? *p : 0;
235 SrcRec* insert(const SrcKey& sk) {
236 return *m_map.insert(sk.toAtomicInt(), new SrcRec);
239 size_t invalidateCode(const Eval::PhpFile* file);
244 #endif