1 | /* |
2 | * Copyright (C) 2017 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 | #pragma once |
27 | |
28 | #if ENABLE(JIT) |
29 | |
30 | #include "JSFunctionInlines.h" |
31 | #include "ObjectPropertyConditionSet.h" |
32 | #include "PolyProtoAccessChain.h" |
33 | #include <wtf/Box.h> |
34 | #include <wtf/CommaPrinter.h> |
35 | |
36 | namespace JSC { |
37 | |
38 | struct AccessGenerationState; |
39 | |
40 | // An AccessCase describes one of the cases of a PolymorphicAccess. A PolymorphicAccess represents a |
41 | // planned (to generate in future) or generated stub for some inline cache. That stub contains fast |
42 | // path code for some finite number of fast cases, each described by an AccessCase object. |
43 | // |
44 | // An AccessCase object has a lifecycle that proceeds through several states. Note that the states |
45 | // of AccessCase have a lot to do with the global effect epoch (we'll say epoch for short). This is |
46 | // a simple way of reasoning about the state of the system outside this AccessCase. Any observable |
47 | // effect - like storing to a property, changing an object's structure, etc. - increments the epoch. |
48 | // The states are: |
49 | // |
50 | // Primordial: This is an AccessCase that was just allocated. It does not correspond to any actual |
51 | // code and it is not owned by any PolymorphicAccess. In this state, the AccessCase |
52 | // assumes that it is in the same epoch as when it was created. This is important |
53 | // because it may make claims about itself ("I represent a valid case so long as you |
54 | // register a watchpoint on this set") that could be contradicted by some outside |
55 | // effects (like firing and deleting the watchpoint set in question). This is also the |
56 | // state that an AccessCase is in when it is cloned (AccessCase::clone()). |
57 | // |
58 | // Committed: This happens as soon as some PolymorphicAccess takes ownership of this AccessCase. |
59 | // In this state, the AccessCase no longer assumes anything about the epoch. To |
60 | // accomplish this, PolymorphicAccess calls AccessCase::commit(). This must be done |
61 | // during the same epoch when the AccessCase was created, either by the client or by |
62 | // clone(). When created by the client, committing during the same epoch works because |
63 | // we can be sure that whatever watchpoint sets they spoke of are still valid. When |
64 | // created by clone(), we can be sure that the set is still valid because the original |
65 | // of the clone still has watchpoints on it. |
66 | // |
67 | // Generated: This is the state when the PolymorphicAccess generates code for this case by |
68 | // calling AccessCase::generate() or AccessCase::generateWithGuard(). At this point |
69 | // the case object will have some extra stuff in it, like possibly the CallLinkInfo |
70 | // object associated with the inline cache. |
71 | // FIXME: Moving into the Generated state should not mutate the AccessCase object or |
72 | // put more stuff into it. If we fix this, then we can get rid of AccessCase::clone(). |
73 | // https://bugs.webkit.org/show_bug.cgi?id=156456 |
74 | // |
75 | // An AccessCase may be destroyed while in any of these states. |
76 | // |
77 | // We will sometimes buffer committed AccessCases in the PolymorphicAccess object before generating |
78 | // code. This allows us to only regenerate once we've accumulated (hopefully) more than one new |
79 | // AccessCase. |
80 | class AccessCase { |
81 | WTF_MAKE_FAST_ALLOCATED; |
82 | public: |
83 | enum AccessType : uint8_t { |
84 | Load, |
85 | Transition, |
86 | Replace, |
87 | Miss, |
88 | GetGetter, |
89 | Getter, |
90 | Setter, |
91 | CustomValueGetter, |
92 | CustomAccessorGetter, |
93 | CustomValueSetter, |
94 | CustomAccessorSetter, |
95 | IntrinsicGetter, |
96 | InHit, |
97 | InMiss, |
98 | ArrayLength, |
99 | StringLength, |
100 | DirectArgumentsLength, |
101 | ScopedArgumentsLength, |
102 | ModuleNamespaceLoad, |
103 | InstanceOfHit, |
104 | InstanceOfMiss, |
105 | InstanceOfGeneric, |
106 | IndexedInt32Load, |
107 | IndexedDoubleLoad, |
108 | IndexedContiguousLoad, |
109 | IndexedArrayStorageLoad, |
110 | IndexedScopedArgumentsLoad, |
111 | IndexedDirectArgumentsLoad, |
112 | IndexedTypedArrayInt8Load, |
113 | IndexedTypedArrayUint8Load, |
114 | IndexedTypedArrayUint8ClampedLoad, |
115 | IndexedTypedArrayInt16Load, |
116 | IndexedTypedArrayUint16Load, |
117 | IndexedTypedArrayInt32Load, |
118 | IndexedTypedArrayUint32Load, |
119 | IndexedTypedArrayFloat32Load, |
120 | IndexedTypedArrayFloat64Load, |
121 | IndexedStringLoad |
122 | }; |
123 | |
124 | enum State : uint8_t { |
125 | Primordial, |
126 | Committed, |
127 | Generated |
128 | }; |
129 | |
130 | template<typename T> |
131 | T& as() { return *static_cast<T*>(this); } |
132 | |
133 | template<typename T> |
134 | const T& as() const { return *static_cast<const T*>(this); } |
135 | |
136 | |
137 | template<typename AccessCaseType, typename... Arguments> |
138 | static std::unique_ptr<AccessCaseType> create(Arguments... arguments) |
139 | { |
140 | return std::unique_ptr<AccessCaseType>(new AccessCaseType(arguments...)); |
141 | } |
142 | |
143 | static std::unique_ptr<AccessCase> create(VM&, JSCell* owner, AccessType, const Identifier&, PropertyOffset = invalidOffset, |
144 | Structure* = nullptr, const ObjectPropertyConditionSet& = ObjectPropertyConditionSet(), std::unique_ptr<PolyProtoAccessChain> = nullptr); |
145 | |
146 | // This create method should be used for transitions. |
147 | static std::unique_ptr<AccessCase> create(VM&, JSCell* owner, const Identifier&, PropertyOffset, Structure* oldStructure, |
148 | Structure* newStructure, const ObjectPropertyConditionSet&, std::unique_ptr<PolyProtoAccessChain>); |
149 | |
150 | static std::unique_ptr<AccessCase> fromStructureStubInfo(VM&, JSCell* owner, const Identifier&, StructureStubInfo&); |
151 | |
152 | AccessType type() const { return m_type; } |
153 | State state() const { return m_state; } |
154 | PropertyOffset offset() const { return m_offset; } |
155 | |
156 | Structure* structure() const |
157 | { |
158 | if (m_type == Transition) |
159 | return m_structure->previousID(); |
160 | return m_structure.get(); |
161 | } |
162 | bool guardedByStructureCheck(const StructureStubInfo&) const; |
163 | |
164 | Structure* newStructure() const |
165 | { |
166 | ASSERT(m_type == Transition); |
167 | return m_structure.get(); |
168 | } |
169 | |
170 | ObjectPropertyConditionSet conditionSet() const { return m_conditionSet; } |
171 | |
172 | virtual bool hasAlternateBase() const; |
173 | virtual JSObject* alternateBase() const; |
174 | |
175 | virtual WatchpointSet* additionalSet() const { return nullptr; } |
176 | bool viaProxy() const { return m_viaProxy; } |
177 | |
178 | // If you supply the optional vector, this will append the set of cells that this will need to keep alive |
179 | // past the call. |
180 | bool doesCalls(Vector<JSCell*>* cellsToMark = nullptr) const; |
181 | |
182 | bool isGetter() const |
183 | { |
184 | switch (type()) { |
185 | case Getter: |
186 | case CustomValueGetter: |
187 | case CustomAccessorGetter: |
188 | return true; |
189 | default: |
190 | return false; |
191 | } |
192 | } |
193 | |
194 | bool isAccessor() const { return isGetter() || type() == Setter; } |
195 | |
196 | // Is it still possible for this case to ever be taken? Must call this as a prerequisite for |
197 | // calling generate() and friends. If this returns true, then you can call generate(). If |
198 | // this returns false, then generate() will crash. You must call generate() in the same epoch |
199 | // as when you called couldStillSucceed(). |
200 | bool couldStillSucceed() const; |
201 | |
202 | // If this method returns true, then it's a good idea to remove 'other' from the access once 'this' |
203 | // is added. This method assumes that in case of contradictions, 'this' represents a newer, and so |
204 | // more useful, truth. This method can be conservative; it will return false when it doubt. |
205 | bool canReplace(const AccessCase& other) const; |
206 | |
207 | void dump(PrintStream& out) const; |
208 | virtual void dumpImpl(PrintStream&, CommaPrinter&) const { } |
209 | |
210 | virtual ~AccessCase(); |
211 | |
212 | bool usesPolyProto() const |
213 | { |
214 | return !!m_polyProtoAccessChain; |
215 | } |
216 | |
217 | bool requiresIdentifierNameMatch() const; |
218 | bool requiresInt32PropertyCheck() const; |
219 | bool needsScratchFPR() const; |
220 | |
221 | static TypedArrayType toTypedArrayType(AccessType); |
222 | |
223 | UniquedStringImpl* uid() const { return m_identifier->impl(); } |
224 | Box<Identifier> identifier() const { return m_identifier; } |
225 | |
226 | |
227 | #if !ASSERT_DISABLED |
228 | void checkConsistency(StructureStubInfo&); |
229 | #else |
230 | ALWAYS_INLINE void checkConsistency(StructureStubInfo&) { } |
231 | #endif |
232 | |
233 | protected: |
234 | AccessCase(VM&, JSCell* owner, AccessType, const Identifier&, PropertyOffset, Structure*, const ObjectPropertyConditionSet&, std::unique_ptr<PolyProtoAccessChain>); |
235 | AccessCase(AccessCase&&) = default; |
236 | AccessCase(const AccessCase& other) |
237 | : m_type(other.m_type) |
238 | , m_state(other.m_state) |
239 | , m_viaProxy(other.m_viaProxy) |
240 | , m_offset(other.m_offset) |
241 | , m_structure(other.m_structure) |
242 | , m_conditionSet(other.m_conditionSet) |
243 | , m_identifier(other.m_identifier) |
244 | { |
245 | if (other.m_polyProtoAccessChain) |
246 | m_polyProtoAccessChain = other.m_polyProtoAccessChain->clone(); |
247 | } |
248 | |
249 | AccessCase& operator=(const AccessCase&) = delete; |
250 | void resetState() { m_state = Primordial; } |
251 | |
252 | private: |
253 | friend class CodeBlock; |
254 | friend class PolymorphicAccess; |
255 | |
256 | template<typename Functor> |
257 | void forEachDependentCell(const Functor&) const; |
258 | |
259 | bool visitWeak(VM&) const; |
260 | bool propagateTransitions(SlotVisitor&) const; |
261 | |
262 | // FIXME: This only exists because of how AccessCase puts post-generation things into itself. |
263 | // https://bugs.webkit.org/show_bug.cgi?id=156456 |
264 | virtual std::unique_ptr<AccessCase> clone() const; |
265 | |
266 | // Perform any action that must be performed before the end of the epoch in which the case |
267 | // was created. Returns a set of watchpoint sets that will need to be watched. |
268 | Vector<WatchpointSet*, 2> commit(VM&); |
269 | |
270 | // Fall through on success. Two kinds of failures are supported: fall-through, which means that we |
271 | // should try a different case; and failure, which means that this was the right case but it needs |
272 | // help from the slow path. |
273 | void generateWithGuard(AccessGenerationState&, MacroAssembler::JumpList& fallThrough); |
274 | |
275 | // Fall through on success, add a jump to the failure list on failure. |
276 | void generate(AccessGenerationState&); |
277 | |
278 | void generateImpl(AccessGenerationState&); |
279 | |
280 | bool guardedByStructureCheckSkippingConstantIdentifierCheck() const; |
281 | |
282 | AccessType m_type; |
283 | State m_state { Primordial }; |
284 | protected: |
285 | // m_viaProxy is true only if the instance inherits (or it is) ProxyableAccessCase. |
286 | // We put this value here instead of ProxyableAccessCase to reduce the size of ProxyableAccessCase and its |
287 | // derived classes, which are super frequently allocated. |
288 | bool m_viaProxy { false }; |
289 | private: |
290 | PropertyOffset m_offset; |
291 | |
292 | // Usually this is the structure that we expect the base object to have. But, this is the *new* |
293 | // structure for a transition and we rely on the fact that it has a strong reference to the old |
294 | // structure. For proxies, this is the structure of the object behind the proxy. |
295 | WriteBarrier<Structure> m_structure; |
296 | |
297 | ObjectPropertyConditionSet m_conditionSet; |
298 | |
299 | std::unique_ptr<PolyProtoAccessChain> m_polyProtoAccessChain; |
300 | |
301 | Box<Identifier> m_identifier; // We use this indirection so the concurrent compiler can concurrently ref this Box. |
302 | }; |
303 | |
304 | } // namespace JSC |
305 | |
306 | #endif |
307 | |