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;
252 // FIXME: This only exists because of how AccessCase puts post-generation things into itself.
253 // https://bugs.webkit.org/show_bug.cgi?id=156456
254 std::unique_ptr<AccessCase> clone() const;
256 // Perform any action that must be performed before the end of the epoch in which the case
257 // was created. Returns a set of watchpoint sets that will need to be watched.
258 Vector<WatchpointSet*, 2> commit(VM&, const Identifier&);
260 // Fall through on success. Two kinds of failures are supported: fall-through, which means that we
261 // should try a different case; and failure, which means that this was the right case but it needs
262 // help from the slow path.
263 void generateWithGuard(AccessGenerationState&, MacroAssembler::JumpList& fallThrough);
265 // Fall through on success, add a jump to the failure list on failure.
266 void generate(AccessGenerationState&);
268 void generateImpl(AccessGenerationState&);
269 void emitIntrinsicGetter(AccessGenerationState&);
271 AccessType m_type { Load };
272 State m_state { Primordial };
273 PropertyOffset m_offset { invalidOffset };
275 // Usually this is the structure that we expect the base object to have. But, this is the *new*
276 // structure for a transition and we rely on the fact that it has a strong reference to the old
277 // structure. For proxies, this is the structure of the object behind the proxy.
278 WriteBarrier<Structure> m_structure;
280 ObjectPropertyConditionSet m_conditionSet;
283 WTF_MAKE_FAST_ALLOCATED;
288 customAccessor.opaque = nullptr;
292 RefPtr<WatchpointSet> additionalSet;
293 // FIXME: This should probably live in the stub routine object.
294 // https://bugs.webkit.org/show_bug.cgi?id=156456
295 std::unique_ptr<CallLinkInfo> callLinkInfo;
297 PropertySlot::GetValueFunc getter;
298 PutPropertySlot::PutValueFunc setter;
301 WriteBarrier<JSObject> customSlotBase;
302 WriteBarrier<JSFunction> intrinsicFunction;
305 std::unique_ptr<RareData> m_rareData;
308 class AccessGenerationResult {
315 GeneratedFinalCode // Generated so much code that we never want to generate code again.
318 AccessGenerationResult()
322 AccessGenerationResult(Kind kind)
325 RELEASE_ASSERT(kind != GeneratedNewCode);
326 RELEASE_ASSERT(kind != GeneratedFinalCode);
329 AccessGenerationResult(Kind kind, MacroAssemblerCodePtr code)
333 RELEASE_ASSERT(kind == GeneratedNewCode || kind == GeneratedFinalCode);
334 RELEASE_ASSERT(code);
337 bool operator==(const AccessGenerationResult& other) const
339 return m_kind == other.m_kind && m_code == other.m_code;
342 bool operator!=(const AccessGenerationResult& other) const
344 return !(*this == other);
347 explicit operator bool() const
349 return *this != AccessGenerationResult();
352 Kind kind() const { return m_kind; }
354 const MacroAssemblerCodePtr& code() const { return m_code; }
356 bool madeNoChanges() const { return m_kind == MadeNoChanges; }
357 bool gaveUp() const { return m_kind == GaveUp; }
358 bool buffered() const { return m_kind == Buffered; }
359 bool generatedNewCode() const { return m_kind == GeneratedNewCode; }
360 bool generatedFinalCode() const { return m_kind == GeneratedFinalCode; }
362 // If we gave up on this attempt to generate code, or if we generated the "final" code, then we
363 // should give up after this.
364 bool shouldGiveUpNow() const { return gaveUp() || generatedFinalCode(); }
366 bool generatedSomeCode() const { return generatedNewCode() || generatedFinalCode(); }
368 void dump(PrintStream&) const;
372 MacroAssemblerCodePtr m_code;
375 class PolymorphicAccess {
376 WTF_MAKE_NONCOPYABLE(PolymorphicAccess);
377 WTF_MAKE_FAST_ALLOCATED;
380 ~PolymorphicAccess();
382 // When this fails (returns GaveUp), this will leave the old stub intact but you should not try
383 // to call this method again for that PolymorphicAccess instance.
384 AccessGenerationResult addCases(
385 VM&, CodeBlock*, StructureStubInfo&, const Identifier&, Vector<std::unique_ptr<AccessCase>>);
387 AccessGenerationResult addCase(
388 VM&, CodeBlock*, StructureStubInfo&, const Identifier&, std::unique_ptr<AccessCase>);
390 AccessGenerationResult regenerate(VM&, CodeBlock*, StructureStubInfo&, const Identifier&);
392 bool isEmpty() const { return m_list.isEmpty(); }
393 unsigned size() const { return m_list.size(); }
394 const AccessCase& at(unsigned i) const { return *m_list[i]; }
395 const AccessCase& operator[](unsigned i) const { return *m_list[i]; }
397 // If this returns false then we are requesting a reset of the owning StructureStubInfo.
398 bool visitWeak(VM&) const;
402 void dump(PrintStream& out) const;
403 bool containsPC(void* pc) const
408 uintptr_t pcAsInt = bitwise_cast<uintptr_t>(pc);
409 return m_stubRoutine->startAddress() <= pcAsInt && pcAsInt <= m_stubRoutine->endAddress();
413 friend class AccessCase;
414 friend class CodeBlock;
415 friend struct AccessGenerationState;
417 typedef Vector<std::unique_ptr<AccessCase>, 2> ListType;
420 VM&, std::unique_ptr<WatchpointsOnStructureStubInfo>&, CodeBlock*, StructureStubInfo&,
421 const Identifier&, AccessCase&);
423 MacroAssemblerCodePtr regenerate(
424 VM&, CodeBlock*, StructureStubInfo&, const Identifier&, ListType& cases);
427 RefPtr<JITStubRoutine> m_stubRoutine;
428 std::unique_ptr<WatchpointsOnStructureStubInfo> m_watchpoints;
429 std::unique_ptr<Vector<WriteBarrier<JSCell>>> m_weakReferences;
432 struct AccessGenerationState {
433 AccessGenerationState()
434 : m_calculatedRegistersForCallAndExceptionHandling(false)
435 , m_needsToRestoreRegistersIfException(false)
436 , m_calculatedCallSiteIndex(false)
439 CCallHelpers* jit { nullptr };
440 ScratchRegisterAllocator* allocator;
441 ScratchRegisterAllocator::PreservedState preservedReusedRegisterState;
442 PolymorphicAccess* access { nullptr };
443 StructureStubInfo* stubInfo { nullptr };
444 MacroAssembler::JumpList success;
445 MacroAssembler::JumpList failAndRepatch;
446 MacroAssembler::JumpList failAndIgnore;
447 GPRReg baseGPR { InvalidGPRReg };
448 JSValueRegs valueRegs;
449 GPRReg scratchGPR { InvalidGPRReg };
450 const Identifier* ident;
451 std::unique_ptr<WatchpointsOnStructureStubInfo> watchpoints;
452 Vector<WriteBarrier<JSCell>> weakReferences;
454 Watchpoint* addWatchpoint(const ObjectPropertyCondition& = ObjectPropertyCondition());
456 void restoreScratch();
459 void calculateLiveRegistersForCallAndExceptionHandling(const RegisterSet& extra = RegisterSet());
461 void preserveLiveRegistersToStackForCall(const RegisterSet& extra = RegisterSet());
463 void restoreLiveRegistersFromStackForCall(bool isGetter = false);
464 void restoreLiveRegistersFromStackForCallWithThrownException();
465 void restoreLiveRegistersFromStackForCall(const RegisterSet& dontRestore);
467 const RegisterSet& liveRegistersForCall()
469 RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
470 return m_liveRegistersForCall;
473 CallSiteIndex callSiteIndexForExceptionHandlingOrOriginal();
474 CallSiteIndex callSiteIndexForExceptionHandling()
476 RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
477 RELEASE_ASSERT(m_needsToRestoreRegistersIfException);
478 RELEASE_ASSERT(m_calculatedCallSiteIndex);
479 return m_callSiteIndex;
482 const HandlerInfo& originalExceptionHandler() const;
483 unsigned numberOfStackBytesUsedForRegisterPreservation() const
485 RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
486 return m_numberOfStackBytesUsedForRegisterPreservation;
489 bool needsToRestoreRegistersIfException() const { return m_needsToRestoreRegistersIfException; }
490 CallSiteIndex originalCallSiteIndex() const;
492 void emitExplicitExceptionHandler();
495 const RegisterSet& liveRegistersToPreserveAtExceptionHandlingCallSite()
497 RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
498 return m_liveRegistersToPreserveAtExceptionHandlingCallSite;
501 RegisterSet m_liveRegistersToPreserveAtExceptionHandlingCallSite;
502 RegisterSet m_liveRegistersForCall;
503 CallSiteIndex m_callSiteIndex { CallSiteIndex(std::numeric_limits<unsigned>::max()) };
504 unsigned m_numberOfStackBytesUsedForRegisterPreservation { std::numeric_limits<unsigned>::max() };
505 bool m_calculatedRegistersForCallAndExceptionHandling : 1;
506 bool m_needsToRestoreRegistersIfException : 1;
507 bool m_calculatedCallSiteIndex : 1;
514 void printInternal(PrintStream&, JSC::AccessGenerationResult::Kind);
515 void printInternal(PrintStream&, JSC::AccessCase::AccessType);
516 void printInternal(PrintStream&, JSC::AccessCase::State);
520 #endif // ENABLE(JIT)
522 #endif // PolymorphicAccess_h