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 +----------------------------------------------------------------------+
16 #ifndef incl_HPHP_SRCDB_H_
17 #define incl_HPHP_SRCDB_H_
19 #include <boost/noncopyable.hpp>
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
{
35 struct RelocationInfo
;
38 struct GrowableVector
{
40 GrowableVector(GrowableVector
&& other
) noexcept
: m_vec(other
.m_vec
) {
41 other
.m_vec
= nullptr;
43 GrowableVector
& operator=(GrowableVector
&& other
) {
45 other
.m_vec
= nullptr;
48 GrowableVector(const GrowableVector
&) = delete;
49 GrowableVector
& operator=(const GrowableVector
&) = delete;
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
) {
66 m_vec
= m_vec
->push_back(datum
);
68 void swap(GrowableVector
<T
>& other
) {
69 std::swap(m_vec
, other
.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; }
83 T m_data
[1]; // Actually variable length
84 Impl() : m_size(0) { }
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
) {
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
));
103 gv
->m_data
[gv
->m_size
++] = datum
;
107 Impl
* m_vec
{nullptr};
111 * Incoming branches between different translations are tracked using
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
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
123 struct IncomingBranch
{
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
);
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
>;
164 CompactTaggedPtr
<void,Tag
> m_ptr
;
168 * SrcRec: record of translator output for a given source location.
172 : m_topTranslation(nullptr)
173 , m_anchorTranslation(0)
174 , m_dbgBranchGuardSrc(nullptr)
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
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
,
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
251 * m_guard doesn't quite solve it, but its as good as things were
256 return m_guard
.compare_exchange_strong(val
, 1);
264 void patchIncomingBranches(TCA newStart
);
267 // This either points to the most recent translation in the
268 // translations vector, or if hasDebuggerGuard() it points to the
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
;
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
;
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());
316 SrcRec
* insert(SrcKey sk
) {
317 return *m_map
.insert(sk
.toAtomicInt(), new SrcRec
);