2 * Copyright (C) 2014-2016 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #ifndef PolymorphicAccess_h
27 #define PolymorphicAccess_h
31 #include "CodeOrigin.h"
32 #include "JSFunctionInlines.h"
33 #include "MacroAssembler.h"
34 #include "ObjectPropertyConditionSet.h"
36 #include "ScratchRegisterAllocator.h"
37 #include "Structure.h"
38 #include <wtf/Vector.h>
43 class PolymorphicAccess;
44 class StructureStubInfo;
45 class WatchpointsOnStructureStubInfo;
46 class ScratchRegisterAllocator;
48 struct AccessGenerationState;
50 // An AccessCase describes one of the cases of a PolymorphicAccess. A PolymorphicAccess represents a
51 // planned (to generate in future) or generated stub for some inline cache. That stub contains fast
52 // path code for some finite number of fast cases, each described by an AccessCase object.
54 // An AccessCase object has a lifecycle that proceeds through several states. Note that the states
55 // of AccessCase have a lot to do with the global effect epoch (we'll say epoch for short). This is
56 // a simple way of reasoning about the state of the system outside this AccessCase. Any observable
57 // effect - like storing to a property, changing an object's structure, etc. - increments the epoch.
60 // Primordial: This is an AccessCase that was just allocated. It does not correspond to any actual
61 // code and it is not owned by any PolymorphicAccess. In this state, the AccessCase
62 // assumes that it is in the same epoch as when it was created. This is important
63 // because it may make claims about itself ("I represent a valid case so long as you
64 // register a watchpoint on this set") that could be contradicted by some outside
65 // effects (like firing and deleting the watchpoint set in question). This is also the
66 // state that an AccessCase is in when it is cloned (AccessCase::clone()).
68 // Committed: This happens as soon as some PolymorphicAccess takes ownership of this AccessCase.
69 // In this state, the AccessCase no longer assumes anything about the epoch. To
70 // accomplish this, PolymorphicAccess calls AccessCase::commit(). This must be done
71 // during the same epoch when the AccessCase was created, either by the client or by
72 // clone(). When created by the client, committing during the same epoch works because
73 // we can be sure that whatever watchpoint sets they spoke of are still valid. When
74 // created by clone(), we can be sure that the set is still valid because the original
75 // of the clone still has watchpoints on it.
77 // Generated: This is the state when the PolymorphicAccess generates code for this case by
78 // calling AccessCase::generate() or AccessCase::generateWithGuard(). At this point
79 // the case object will have some extra stuff in it, like possibly the CallLinkInfo
80 // object associated with the inline cache.
81 // FIXME: Moving into the Generated state should not mutate the AccessCase object or
82 // put more stuff into it. If we fix this, then we can get rid of AccessCase::clone().
83 // https://bugs.webkit.org/show_bug.cgi?id=156456
85 // An AccessCase may be destroyed while in any of these states.
87 // Note that right now, an AccessCase goes from Primordial to Generated quite quickly.
88 // FIXME: Make it possible for PolymorphicAccess to hold onto AccessCases that haven't been
89 // generated. That would allow us to significantly reduce the number of regeneration events.
90 // https://bugs.webkit.org/show_bug.cgi?id=156457
92 WTF_MAKE_NONCOPYABLE(AccessCase);
93 WTF_MAKE_FAST_ALLOCATED;
95 enum AccessType : uint8_t {
105 CustomAccessorGetter,
107 CustomAccessorSetter,
113 DirectArgumentsLength,
114 ScopedArgumentsLength
117 enum State : uint8_t {
123 static std::unique_ptr<AccessCase> tryGet(
124 VM&, JSCell* owner, AccessType, PropertyOffset, Structure*,
125 const ObjectPropertyConditionSet& = ObjectPropertyConditionSet(),
126 bool viaProxy = false,
127 WatchpointSet* additionalSet = nullptr);
129 static std::unique_ptr<AccessCase> get(
130 VM&, JSCell* owner, AccessType, PropertyOffset, Structure*,
131 const ObjectPropertyConditionSet& = ObjectPropertyConditionSet(),
132 bool viaProxy = false,
133 WatchpointSet* additionalSet = nullptr,
134 PropertySlot::GetValueFunc = nullptr,
135 JSObject* customSlotBase = nullptr);
137 static std::unique_ptr<AccessCase> megamorphicLoad(VM&, JSCell* owner);
139 static std::unique_ptr<AccessCase> replace(VM&, JSCell* owner, Structure*, PropertyOffset);
141 static std::unique_ptr<AccessCase> transition(
142 VM&, JSCell* owner, Structure* oldStructure, Structure* newStructure, PropertyOffset,
143 const ObjectPropertyConditionSet& = ObjectPropertyConditionSet());
145 static std::unique_ptr<AccessCase> setter(
146 VM&, JSCell* owner, AccessType, Structure*, PropertyOffset,
147 const ObjectPropertyConditionSet&, PutPropertySlot::PutValueFunc = nullptr,
148 JSObject* customSlotBase = nullptr);
150 static std::unique_ptr<AccessCase> in(
151 VM&, JSCell* owner, AccessType, Structure*,
152 const ObjectPropertyConditionSet& = ObjectPropertyConditionSet());
154 static std::unique_ptr<AccessCase> getLength(VM&, JSCell* owner, AccessType);
155 static std::unique_ptr<AccessCase> getIntrinsic(VM&, JSCell* owner, JSFunction* intrinsic, PropertyOffset, Structure*, const ObjectPropertyConditionSet&);
157 static std::unique_ptr<AccessCase> fromStructureStubInfo(VM&, JSCell* owner, StructureStubInfo&);
161 AccessType type() const { return m_type; }
162 State state() const { return m_state; }
163 PropertyOffset offset() const { return m_offset; }
164 bool viaProxy() const { return m_rareData ? m_rareData->viaProxy : false; }
166 Structure* structure() const
168 if (m_type == Transition)
169 return m_structure->previousID();
170 return m_structure.get();
172 bool guardedByStructureCheck() const;
174 Structure* newStructure() const
176 ASSERT(m_type == Transition);
177 return m_structure.get();
180 ObjectPropertyConditionSet conditionSet() const { return m_conditionSet; }
181 JSFunction* intrinsicFunction() const
183 ASSERT(type() == IntrinsicGetter && m_rareData);
184 return m_rareData->intrinsicFunction.get();
186 Intrinsic intrinsic() const
188 return intrinsicFunction()->intrinsic();
191 WatchpointSet* additionalSet() const
193 return m_rareData ? m_rareData->additionalSet.get() : nullptr;
196 JSObject* customSlotBase() const
198 return m_rareData ? m_rareData->customSlotBase.get() : nullptr;
201 JSObject* alternateBase() const;
203 // If you supply the optional vector, this will append the set of cells that this will need to keep alive
205 bool doesCalls(Vector<JSCell*>* cellsToMark = nullptr) const;
207 bool isGetter() const
211 case CustomValueGetter:
212 case CustomAccessorGetter:
219 // This can return null even for a getter/setter, if it hasn't been generated yet. That's
220 // actually somewhat likely because of how we do buffering of new cases.
221 CallLinkInfo* callLinkInfo() const
225 return m_rareData->callLinkInfo.get();
228 // Is it still possible for this case to ever be taken? Must call this as a prerequisite for
229 // calling generate() and friends. If this returns true, then you can call generate(). If
230 // this returns false, then generate() will crash. You must call generate() in the same epoch
231 // as when you called couldStillSucceed().
232 bool couldStillSucceed() const;
234 static bool canEmitIntrinsicGetter(JSFunction*, Structure*);
236 bool canBeReplacedByMegamorphicLoad() const;
238 // If this method returns true, then it's a good idea to remove 'other' from the access once 'this'
239 // is added. This method assumes that in case of contradictions, 'this' represents a newer, and so
240 // more useful, truth. This method can be conservative; it will return false when it doubt.
241 bool canReplace(const AccessCase& other) const;
243 void dump(PrintStream& out) const;
246 friend class CodeBlock;
247 friend class PolymorphicAccess;
251 bool visitWeak(VM&) const;
253 // FIXME: This only exists because of how AccessCase puts post-generation things into itself.
254 // https://bugs.webkit.org/show_bug.cgi?id=156456
255 std::unique_ptr<AccessCase> clone() const;
257 // Perform any action that must be performed before the end of the epoch in which the case
258 // was created. Returns a set of watchpoint sets that will need to be watched.
259 Vector<WatchpointSet*, 2> commit(VM&, const Identifier&);
261 // Fall through on success. Two kinds of failures are supported: fall-through, which means that we
262 // should try a different case; and failure, which means that this was the right case but it needs
263 // help from the slow path.
264 void generateWithGuard(AccessGenerationState&, MacroAssembler::JumpList& fallThrough);
266 // Fall through on success, add a jump to the failure list on failure.
267 void generate(AccessGenerationState&);
269 void generateImpl(AccessGenerationState&);
270 void emitIntrinsicGetter(AccessGenerationState&);
272 AccessType m_type { Load };
273 State m_state { Primordial };
274 PropertyOffset m_offset { invalidOffset };
276 // Usually this is the structure that we expect the base object to have. But, this is the *new*
277 // structure for a transition and we rely on the fact that it has a strong reference to the old
278 // structure. For proxies, this is the structure of the object behind the proxy.
279 WriteBarrier<Structure> m_structure;
281 ObjectPropertyConditionSet m_conditionSet;
284 WTF_MAKE_FAST_ALLOCATED;
289 customAccessor.opaque = nullptr;
293 RefPtr<WatchpointSet> additionalSet;
294 // FIXME: This should probably live in the stub routine object.
295 // https://bugs.webkit.org/show_bug.cgi?id=156456
296 std::unique_ptr<CallLinkInfo> callLinkInfo;
298 PropertySlot::GetValueFunc getter;
299 PutPropertySlot::PutValueFunc setter;
302 WriteBarrier<JSObject> customSlotBase;
303 WriteBarrier<JSFunction> intrinsicFunction;
306 std::unique_ptr<RareData> m_rareData;
309 class AccessGenerationResult {
316 GeneratedFinalCode // Generated so much code that we never want to generate code again.
319 AccessGenerationResult()
323 AccessGenerationResult(Kind kind)
326 RELEASE_ASSERT(kind != GeneratedNewCode);
327 RELEASE_ASSERT(kind != GeneratedFinalCode);
330 AccessGenerationResult(Kind kind, MacroAssemblerCodePtr code)
334 RELEASE_ASSERT(kind == GeneratedNewCode || kind == GeneratedFinalCode);
335 RELEASE_ASSERT(code);
338 bool operator==(const AccessGenerationResult& other) const
340 return m_kind == other.m_kind && m_code == other.m_code;
343 bool operator!=(const AccessGenerationResult& other) const
345 return !(*this == other);
348 explicit operator bool() const
350 return *this != AccessGenerationResult();
353 Kind kind() const { return m_kind; }
355 const MacroAssemblerCodePtr& code() const { return m_code; }
357 bool madeNoChanges() const { return m_kind == MadeNoChanges; }
358 bool gaveUp() const { return m_kind == GaveUp; }
359 bool buffered() const { return m_kind == Buffered; }
360 bool generatedNewCode() const { return m_kind == GeneratedNewCode; }
361 bool generatedFinalCode() const { return m_kind == GeneratedFinalCode; }
363 // If we gave up on this attempt to generate code, or if we generated the "final" code, then we
364 // should give up after this.
365 bool shouldGiveUpNow() const { return gaveUp() || generatedFinalCode(); }
367 bool generatedSomeCode() const { return generatedNewCode() || generatedFinalCode(); }
369 void dump(PrintStream&) const;
373 MacroAssemblerCodePtr m_code;
376 class PolymorphicAccess {
377 WTF_MAKE_NONCOPYABLE(PolymorphicAccess);
378 WTF_MAKE_FAST_ALLOCATED;
381 ~PolymorphicAccess();
383 // When this fails (returns GaveUp), this will leave the old stub intact but you should not try
384 // to call this method again for that PolymorphicAccess instance.
385 AccessGenerationResult addCases(
386 VM&, CodeBlock*, StructureStubInfo&, const Identifier&, Vector<std::unique_ptr<AccessCase>>);
388 AccessGenerationResult addCase(
389 VM&, CodeBlock*, StructureStubInfo&, const Identifier&, std::unique_ptr<AccessCase>);
391 AccessGenerationResult regenerate(VM&, CodeBlock*, StructureStubInfo&, const Identifier&);
393 bool isEmpty() const { return m_list.isEmpty(); }
394 unsigned size() const { return m_list.size(); }
395 const AccessCase& at(unsigned i) const { return *m_list[i]; }
396 const AccessCase& operator[](unsigned i) const { return *m_list[i]; }
398 // If this returns false then we are requesting a reset of the owning StructureStubInfo.
399 bool visitWeak(VM&) const;
403 void dump(PrintStream& out) const;
404 bool containsPC(void* pc) const
409 uintptr_t pcAsInt = bitwise_cast<uintptr_t>(pc);
410 return m_stubRoutine->startAddress() <= pcAsInt && pcAsInt <= m_stubRoutine->endAddress();
414 friend class AccessCase;
415 friend class CodeBlock;
416 friend struct AccessGenerationState;
418 typedef Vector<std::unique_ptr<AccessCase>, 2> ListType;
421 VM&, std::unique_ptr<WatchpointsOnStructureStubInfo>&, CodeBlock*, StructureStubInfo&,
422 const Identifier&, AccessCase&);
424 MacroAssemblerCodePtr regenerate(
425 VM&, CodeBlock*, StructureStubInfo&, const Identifier&, ListType& cases);
428 RefPtr<JITStubRoutine> m_stubRoutine;
429 std::unique_ptr<WatchpointsOnStructureStubInfo> m_watchpoints;
430 std::unique_ptr<Vector<WriteBarrier<JSCell>>> m_weakReferences;
433 struct AccessGenerationState {
434 AccessGenerationState()
435 : m_calculatedRegistersForCallAndExceptionHandling(false)
436 , m_needsToRestoreRegistersIfException(false)
437 , m_calculatedCallSiteIndex(false)
440 CCallHelpers* jit { nullptr };
441 ScratchRegisterAllocator* allocator;
442 ScratchRegisterAllocator::PreservedState preservedReusedRegisterState;
443 PolymorphicAccess* access { nullptr };
444 StructureStubInfo* stubInfo { nullptr };
445 MacroAssembler::JumpList success;
446 MacroAssembler::JumpList failAndRepatch;
447 MacroAssembler::JumpList failAndIgnore;
448 GPRReg baseGPR { InvalidGPRReg };
449 JSValueRegs valueRegs;
450 GPRReg scratchGPR { InvalidGPRReg };
451 const Identifier* ident;
452 std::unique_ptr<WatchpointsOnStructureStubInfo> watchpoints;
453 Vector<WriteBarrier<JSCell>> weakReferences;
455 Watchpoint* addWatchpoint(const ObjectPropertyCondition& = ObjectPropertyCondition());
457 void restoreScratch();
460 void calculateLiveRegistersForCallAndExceptionHandling(const RegisterSet& extra = RegisterSet());
462 void preserveLiveRegistersToStackForCall(const RegisterSet& extra = RegisterSet());
464 void restoreLiveRegistersFromStackForCall(bool isGetter = false);
465 void restoreLiveRegistersFromStackForCallWithThrownException();
466 void restoreLiveRegistersFromStackForCall(const RegisterSet& dontRestore);
468 const RegisterSet& liveRegistersForCall()
470 RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
471 return m_liveRegistersForCall;
474 CallSiteIndex callSiteIndexForExceptionHandlingOrOriginal();
475 CallSiteIndex callSiteIndexForExceptionHandling()
477 RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
478 RELEASE_ASSERT(m_needsToRestoreRegistersIfException);
479 RELEASE_ASSERT(m_calculatedCallSiteIndex);
480 return m_callSiteIndex;
483 const HandlerInfo& originalExceptionHandler() const;
484 unsigned numberOfStackBytesUsedForRegisterPreservation() const
486 RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
487 return m_numberOfStackBytesUsedForRegisterPreservation;
490 bool needsToRestoreRegistersIfException() const { return m_needsToRestoreRegistersIfException; }
491 CallSiteIndex originalCallSiteIndex() const;
493 void emitExplicitExceptionHandler();
496 const RegisterSet& liveRegistersToPreserveAtExceptionHandlingCallSite()
498 RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
499 return m_liveRegistersToPreserveAtExceptionHandlingCallSite;
502 RegisterSet m_liveRegistersToPreserveAtExceptionHandlingCallSite;
503 RegisterSet m_liveRegistersForCall;
504 CallSiteIndex m_callSiteIndex { CallSiteIndex(std::numeric_limits<unsigned>::max()) };
505 unsigned m_numberOfStackBytesUsedForRegisterPreservation { std::numeric_limits<unsigned>::max() };
506 bool m_calculatedRegistersForCallAndExceptionHandling : 1;
507 bool m_needsToRestoreRegistersIfException : 1;
508 bool m_calculatedCallSiteIndex : 1;
515 void printInternal(PrintStream&, JSC::AccessGenerationResult::Kind);
516 void printInternal(PrintStream&, JSC::AccessCase::AccessType);
517 void printInternal(PrintStream&, JSC::AccessCase::State);
521 #endif // ENABLE(JIT)
523 #endif // PolymorphicAccess_h