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 // We will sometimes buffer committed AccessCases in the PolymorphicAccess object before generating
88 // code. This allows us to only regenerate once we've accumulated (hopefully) more than one new
91 WTF_MAKE_NONCOPYABLE(AccessCase);
92 WTF_MAKE_FAST_ALLOCATED;
94 enum AccessType : uint8_t {
104 CustomAccessorGetter,
106 CustomAccessorSetter,
112 DirectArgumentsLength,
113 ScopedArgumentsLength
116 enum State : uint8_t {
122 static std::unique_ptr<AccessCase> tryGet(
123 VM&, JSCell* owner, AccessType, PropertyOffset, Structure*,
124 const ObjectPropertyConditionSet& = ObjectPropertyConditionSet(),
125 bool viaProxy = false,
126 WatchpointSet* additionalSet = nullptr);
128 static std::unique_ptr<AccessCase> get(
129 VM&, JSCell* owner, AccessType, PropertyOffset, Structure*,
130 const ObjectPropertyConditionSet& = ObjectPropertyConditionSet(),
131 bool viaProxy = false,
132 WatchpointSet* additionalSet = nullptr,
133 PropertySlot::GetValueFunc = nullptr,
134 JSObject* customSlotBase = nullptr);
136 static std::unique_ptr<AccessCase> megamorphicLoad(VM&, JSCell* owner);
138 static std::unique_ptr<AccessCase> replace(VM&, JSCell* owner, Structure*, PropertyOffset);
140 static std::unique_ptr<AccessCase> transition(
141 VM&, JSCell* owner, Structure* oldStructure, Structure* newStructure, PropertyOffset,
142 const ObjectPropertyConditionSet& = ObjectPropertyConditionSet());
144 static std::unique_ptr<AccessCase> setter(
145 VM&, JSCell* owner, AccessType, Structure*, PropertyOffset,
146 const ObjectPropertyConditionSet&, PutPropertySlot::PutValueFunc = nullptr,
147 JSObject* customSlotBase = nullptr);
149 static std::unique_ptr<AccessCase> in(
150 VM&, JSCell* owner, AccessType, Structure*,
151 const ObjectPropertyConditionSet& = ObjectPropertyConditionSet());
153 static std::unique_ptr<AccessCase> getLength(VM&, JSCell* owner, AccessType);
154 static std::unique_ptr<AccessCase> getIntrinsic(VM&, JSCell* owner, JSFunction* intrinsic, PropertyOffset, Structure*, const ObjectPropertyConditionSet&);
156 static std::unique_ptr<AccessCase> fromStructureStubInfo(VM&, JSCell* owner, StructureStubInfo&);
160 AccessType type() const { return m_type; }
161 State state() const { return m_state; }
162 PropertyOffset offset() const { return m_offset; }
163 bool viaProxy() const { return m_rareData ? m_rareData->viaProxy : false; }
165 Structure* structure() const
167 if (m_type == Transition)
168 return m_structure->previousID();
169 return m_structure.get();
171 bool guardedByStructureCheck() const;
173 Structure* newStructure() const
175 ASSERT(m_type == Transition);
176 return m_structure.get();
179 ObjectPropertyConditionSet conditionSet() const { return m_conditionSet; }
180 JSFunction* intrinsicFunction() const
182 ASSERT(type() == IntrinsicGetter && m_rareData);
183 return m_rareData->intrinsicFunction.get();
185 Intrinsic intrinsic() const
187 return intrinsicFunction()->intrinsic();
190 WatchpointSet* additionalSet() const
192 return m_rareData ? m_rareData->additionalSet.get() : nullptr;
195 JSObject* customSlotBase() const
197 return m_rareData ? m_rareData->customSlotBase.get() : nullptr;
200 JSObject* alternateBase() const;
202 // If you supply the optional vector, this will append the set of cells that this will need to keep alive
204 bool doesCalls(Vector<JSCell*>* cellsToMark = nullptr) const;
206 bool isGetter() const
210 case CustomValueGetter:
211 case CustomAccessorGetter:
218 // This can return null even for a getter/setter, if it hasn't been generated yet. That's
219 // actually somewhat likely because of how we do buffering of new cases.
220 CallLinkInfo* callLinkInfo() const
224 return m_rareData->callLinkInfo.get();
227 // Is it still possible for this case to ever be taken? Must call this as a prerequisite for
228 // calling generate() and friends. If this returns true, then you can call generate(). If
229 // this returns false, then generate() will crash. You must call generate() in the same epoch
230 // as when you called couldStillSucceed().
231 bool couldStillSucceed() const;
233 static bool canEmitIntrinsicGetter(JSFunction*, Structure*);
235 bool canBeReplacedByMegamorphicLoad() const;
237 // If this method returns true, then it's a good idea to remove 'other' from the access once 'this'
238 // is added. This method assumes that in case of contradictions, 'this' represents a newer, and so
239 // more useful, truth. This method can be conservative; it will return false when it doubt.
240 bool canReplace(const AccessCase& other) const;
242 void dump(PrintStream& out) const;
245 friend class CodeBlock;
246 friend class PolymorphicAccess;
250 bool visitWeak(VM&) const;
251 bool propagateTransitions(SlotVisitor&) 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;
401 // This returns true if it has marked everything it will ever marked. This can be used as an
402 // optimization to then avoid calling this method again during the fixpoint.
403 bool propagateTransitions(SlotVisitor&) const;
407 void dump(PrintStream& out) const;
408 bool containsPC(void* pc) const
413 uintptr_t pcAsInt = bitwise_cast<uintptr_t>(pc);
414 return m_stubRoutine->startAddress() <= pcAsInt && pcAsInt <= m_stubRoutine->endAddress();
418 friend class AccessCase;
419 friend class CodeBlock;
420 friend struct AccessGenerationState;
422 typedef Vector<std::unique_ptr<AccessCase>, 2> ListType;
425 VM&, std::unique_ptr<WatchpointsOnStructureStubInfo>&, CodeBlock*, StructureStubInfo&,
426 const Identifier&, AccessCase&);
428 MacroAssemblerCodePtr regenerate(
429 VM&, CodeBlock*, StructureStubInfo&, const Identifier&, ListType& cases);
432 RefPtr<JITStubRoutine> m_stubRoutine;
433 std::unique_ptr<WatchpointsOnStructureStubInfo> m_watchpoints;
434 std::unique_ptr<Vector<WriteBarrier<JSCell>>> m_weakReferences;
437 struct AccessGenerationState {
438 AccessGenerationState()
439 : m_calculatedRegistersForCallAndExceptionHandling(false)
440 , m_needsToRestoreRegistersIfException(false)
441 , m_calculatedCallSiteIndex(false)
444 CCallHelpers* jit { nullptr };
445 ScratchRegisterAllocator* allocator;
446 ScratchRegisterAllocator::PreservedState preservedReusedRegisterState;
447 PolymorphicAccess* access { nullptr };
448 StructureStubInfo* stubInfo { nullptr };
449 MacroAssembler::JumpList success;
450 MacroAssembler::JumpList failAndRepatch;
451 MacroAssembler::JumpList failAndIgnore;
452 GPRReg baseGPR { InvalidGPRReg };
453 JSValueRegs valueRegs;
454 GPRReg scratchGPR { InvalidGPRReg };
455 const Identifier* ident;
456 std::unique_ptr<WatchpointsOnStructureStubInfo> watchpoints;
457 Vector<WriteBarrier<JSCell>> weakReferences;
459 Watchpoint* addWatchpoint(const ObjectPropertyCondition& = ObjectPropertyCondition());
461 void restoreScratch();
464 void calculateLiveRegistersForCallAndExceptionHandling(const RegisterSet& extra = RegisterSet());
466 void preserveLiveRegistersToStackForCall(const RegisterSet& extra = RegisterSet());
468 void restoreLiveRegistersFromStackForCall(bool isGetter = false);
469 void restoreLiveRegistersFromStackForCallWithThrownException();
470 void restoreLiveRegistersFromStackForCall(const RegisterSet& dontRestore);
472 const RegisterSet& liveRegistersForCall()
474 RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
475 return m_liveRegistersForCall;
478 CallSiteIndex callSiteIndexForExceptionHandlingOrOriginal();
479 CallSiteIndex callSiteIndexForExceptionHandling()
481 RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
482 RELEASE_ASSERT(m_needsToRestoreRegistersIfException);
483 RELEASE_ASSERT(m_calculatedCallSiteIndex);
484 return m_callSiteIndex;
487 const HandlerInfo& originalExceptionHandler() const;
488 unsigned numberOfStackBytesUsedForRegisterPreservation() const
490 RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
491 return m_numberOfStackBytesUsedForRegisterPreservation;
494 bool needsToRestoreRegistersIfException() const { return m_needsToRestoreRegistersIfException; }
495 CallSiteIndex originalCallSiteIndex() const;
497 void emitExplicitExceptionHandler();
500 const RegisterSet& liveRegistersToPreserveAtExceptionHandlingCallSite()
502 RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
503 return m_liveRegistersToPreserveAtExceptionHandlingCallSite;
506 RegisterSet m_liveRegistersToPreserveAtExceptionHandlingCallSite;
507 RegisterSet m_liveRegistersForCall;
508 CallSiteIndex m_callSiteIndex { CallSiteIndex(std::numeric_limits<unsigned>::max()) };
509 unsigned m_numberOfStackBytesUsedForRegisterPreservation { std::numeric_limits<unsigned>::max() };
510 bool m_calculatedRegistersForCallAndExceptionHandling : 1;
511 bool m_needsToRestoreRegistersIfException : 1;
512 bool m_calculatedCallSiteIndex : 1;
519 void printInternal(PrintStream&, JSC::AccessGenerationResult::Kind);
520 void printInternal(PrintStream&, JSC::AccessCase::AccessType);
521 void printInternal(PrintStream&, JSC::AccessCase::State);
525 #endif // ENABLE(JIT)
527 #endif // PolymorphicAccess_h