]> git.saurik.com Git - apple/javascriptcore.git/blob - bytecode/Watchpoint.h
7659c0db8d4c040caa8cc4f93b5b87fbf5e8b91a
[apple/javascriptcore.git] / bytecode / Watchpoint.h
1 /*
2 * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #ifndef Watchpoint_h
27 #define Watchpoint_h
28
29 #include <wtf/Atomics.h>
30 #include <wtf/SentinelLinkedList.h>
31 #include <wtf/ThreadSafeRefCounted.h>
32
33 namespace JSC {
34
35 class Watchpoint : public BasicRawSentinelNode<Watchpoint> {
36 public:
37 Watchpoint()
38 {
39 }
40
41 virtual ~Watchpoint();
42
43 void fire() { fireInternal(); }
44
45 protected:
46 virtual void fireInternal() = 0;
47 };
48
49 enum WatchpointState {
50 ClearWatchpoint,
51 IsWatched,
52 IsInvalidated
53 };
54
55 class InlineWatchpointSet;
56
57 class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> {
58 friend class LLIntOffsetsExtractor;
59 public:
60 JS_EXPORT_PRIVATE WatchpointSet(WatchpointState);
61 JS_EXPORT_PRIVATE ~WatchpointSet(); // Note that this will not fire any of the watchpoints; if you need to know when a WatchpointSet dies then you need a separate mechanism for this.
62
63 // It is safe to call this from another thread. It may return an old
64 // state. Guarantees that if *first* read the state() of the thing being
65 // watched and it returned IsWatched and *second* you actually read its
66 // value then it's safe to assume that if the state being watched changes
67 // then also the watchpoint state() will change to IsInvalidated.
68 WatchpointState state() const
69 {
70 WTF::loadLoadFence();
71 WatchpointState result = static_cast<WatchpointState>(m_state);
72 WTF::loadLoadFence();
73 return result;
74 }
75
76 // It is safe to call this from another thread. It may return true
77 // even if the set actually had been invalidated, but that ought to happen
78 // only in the case of races, and should be rare. Guarantees that if you
79 // call this after observing something that must imply that the set is
80 // invalidated, then you will see this return false. This is ensured by
81 // issuing a load-load fence prior to querying the state.
82 bool isStillValid() const
83 {
84 return state() != IsInvalidated;
85 }
86 // Like isStillValid(), may be called from another thread.
87 bool hasBeenInvalidated() const { return !isStillValid(); }
88
89 // As a convenience, this will ignore 0. That's because code paths in the DFG
90 // that create speculation watchpoints may choose to bail out if speculation
91 // had already been terminated.
92 void add(Watchpoint*);
93
94 // Force the watchpoint set to behave as if it was being watched even if no
95 // watchpoints have been installed. This will result in invalidation if the
96 // watchpoint would have fired. That's a pretty good indication that you
97 // probably don't want to set watchpoints, since we typically don't want to
98 // set watchpoints that we believe will actually be fired.
99 void startWatching()
100 {
101 ASSERT(state() != IsInvalidated);
102 m_state = IsWatched;
103 }
104
105 void fireAll()
106 {
107 if (state() != IsWatched)
108 return;
109 fireAllSlow();
110 }
111
112 void touch()
113 {
114 if (state() == ClearWatchpoint)
115 startWatching();
116 else
117 fireAll();
118 }
119
120 void invalidate()
121 {
122 if (state() == IsWatched)
123 fireAll();
124 m_state = IsInvalidated;
125 }
126
127 int8_t* addressOfState() { return &m_state; }
128 int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; }
129
130 JS_EXPORT_PRIVATE void fireAllSlow(); // Call only if you've checked isWatched.
131
132 private:
133 void fireAllWatchpoints();
134
135 friend class InlineWatchpointSet;
136
137 int8_t m_state;
138 int8_t m_setIsNotEmpty;
139
140 SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint>> m_set;
141 };
142
143 // InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which
144 // it is not possible to quickly query whether it is being watched in a single
145 // branch. There is a fairly simple tradeoff between WatchpointSet and
146 // InlineWatchpointSet:
147 //
148 // Do you have to emit JIT code that rapidly tests whether the watchpoint set
149 // is being watched? If so, use WatchpointSet.
150 //
151 // Do you need multiple parties to have pointers to the same WatchpointSet?
152 // If so, use WatchpointSet.
153 //
154 // Do you have to allocate a lot of watchpoint sets? If so, use
155 // InlineWatchpointSet unless you answered "yes" to the previous questions.
156 //
157 // InlineWatchpointSet will use just one pointer-width word of memory unless
158 // you actually add watchpoints to it, in which case it internally inflates
159 // to a pointer to a WatchpointSet, and transfers its state to the
160 // WatchpointSet.
161
162 class InlineWatchpointSet {
163 WTF_MAKE_NONCOPYABLE(InlineWatchpointSet);
164 public:
165 InlineWatchpointSet(WatchpointState state)
166 : m_data(encodeState(state))
167 {
168 }
169
170 ~InlineWatchpointSet()
171 {
172 if (isThin())
173 return;
174 freeFat();
175 }
176
177 // It is safe to call this from another thread. It may return false
178 // even if the set actually had been invalidated, but that ought to happen
179 // only in the case of races, and should be rare.
180 bool hasBeenInvalidated() const
181 {
182 WTF::loadLoadFence();
183 uintptr_t data = m_data;
184 if (isFat(data)) {
185 WTF::loadLoadFence();
186 return fat(data)->hasBeenInvalidated();
187 }
188 return decodeState(data) == IsInvalidated;
189 }
190
191 // Like hasBeenInvalidated(), may be called from another thread.
192 bool isStillValid() const
193 {
194 return !hasBeenInvalidated();
195 }
196
197 void add(Watchpoint*);
198
199 void startWatching()
200 {
201 if (isFat()) {
202 fat()->startWatching();
203 return;
204 }
205 ASSERT(decodeState(m_data) != IsInvalidated);
206 m_data = encodeState(IsWatched);
207 }
208
209 void fireAll()
210 {
211 if (isFat()) {
212 fat()->fireAll();
213 return;
214 }
215 if (decodeState(m_data) == ClearWatchpoint)
216 return;
217 m_data = encodeState(IsInvalidated);
218 WTF::storeStoreFence();
219 }
220
221 void touch()
222 {
223 if (isFat()) {
224 fat()->touch();
225 return;
226 }
227 if (decodeState(m_data) == ClearWatchpoint)
228 m_data = encodeState(IsWatched);
229 else
230 m_data = encodeState(IsInvalidated);
231 WTF::storeStoreFence();
232 }
233
234 private:
235 static const uintptr_t IsThinFlag = 1;
236 static const uintptr_t StateMask = 6;
237 static const uintptr_t StateShift = 1;
238
239 static bool isThin(uintptr_t data) { return data & IsThinFlag; }
240 static bool isFat(uintptr_t data) { return !isThin(data); }
241
242 static WatchpointState decodeState(uintptr_t data)
243 {
244 ASSERT(isThin(data));
245 return static_cast<WatchpointState>((data & StateMask) >> StateShift);
246 }
247
248 static uintptr_t encodeState(WatchpointState state)
249 {
250 return (state << StateShift) | IsThinFlag;
251 }
252
253 bool isThin() const { return isThin(m_data); }
254 bool isFat() const { return isFat(m_data); };
255
256 static WatchpointSet* fat(uintptr_t data)
257 {
258 return bitwise_cast<WatchpointSet*>(data);
259 }
260
261 WatchpointSet* fat()
262 {
263 ASSERT(isFat());
264 return fat(m_data);
265 }
266
267 const WatchpointSet* fat() const
268 {
269 ASSERT(isFat());
270 return fat(m_data);
271 }
272
273 WatchpointSet* inflate()
274 {
275 if (LIKELY(isFat()))
276 return fat();
277 return inflateSlow();
278 }
279
280 JS_EXPORT_PRIVATE WatchpointSet* inflateSlow();
281 JS_EXPORT_PRIVATE void freeFat();
282
283 uintptr_t m_data;
284 };
285
286 } // namespace JSC
287
288 #endif // Watchpoint_h
289