PolymorphicAccess should buffer AccessCases before regenerating
[WebKit-https.git] / Source / JavaScriptCore / bytecode / PolymorphicAccess.h
1 /*
2  * Copyright (C) 2014-2016 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 PolymorphicAccess_h
27 #define PolymorphicAccess_h
28
29 #if ENABLE(JIT)
30
31 #include "CodeOrigin.h"
32 #include "JSFunctionInlines.h"
33 #include "MacroAssembler.h"
34 #include "ObjectPropertyConditionSet.h"
35 #include "Opcode.h"
36 #include "ScratchRegisterAllocator.h"
37 #include "Structure.h"
38 #include <wtf/Vector.h>
39
40 namespace JSC {
41
42 class CodeBlock;
43 class PolymorphicAccess;
44 class StructureStubInfo;
45 class WatchpointsOnStructureStubInfo;
46 class ScratchRegisterAllocator;
47
48 struct AccessGenerationState;
49
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.
53 //
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.
58 // The states are:
59 //
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()).
67 //
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.
76 //
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
84 //
85 // An AccessCase may be destroyed while in any of these states.
86 //
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
91 class AccessCase {
92     WTF_MAKE_NONCOPYABLE(AccessCase);
93     WTF_MAKE_FAST_ALLOCATED;
94 public:
95     enum AccessType : uint8_t {
96         Load,
97         MegamorphicLoad,
98         Transition,
99         Replace,
100         Miss,
101         GetGetter,
102         Getter,
103         Setter,
104         CustomValueGetter,
105         CustomAccessorGetter,
106         CustomValueSetter,
107         CustomAccessorSetter,
108         IntrinsicGetter,
109         InHit,
110         InMiss,
111         ArrayLength,
112         StringLength,
113         DirectArgumentsLength,
114         ScopedArgumentsLength
115     };
116     
117     enum State : uint8_t {
118         Primordial,
119         Committed,
120         Generated
121     };
122
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);
128
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);
136     
137     static std::unique_ptr<AccessCase> megamorphicLoad(VM&, JSCell* owner);
138     
139     static std::unique_ptr<AccessCase> replace(VM&, JSCell* owner, Structure*, PropertyOffset);
140
141     static std::unique_ptr<AccessCase> transition(
142         VM&, JSCell* owner, Structure* oldStructure, Structure* newStructure, PropertyOffset,
143         const ObjectPropertyConditionSet& = ObjectPropertyConditionSet());
144
145     static std::unique_ptr<AccessCase> setter(
146         VM&, JSCell* owner, AccessType, Structure*, PropertyOffset,
147         const ObjectPropertyConditionSet&, PutPropertySlot::PutValueFunc = nullptr,
148         JSObject* customSlotBase = nullptr);
149
150     static std::unique_ptr<AccessCase> in(
151         VM&, JSCell* owner, AccessType, Structure*,
152         const ObjectPropertyConditionSet& = ObjectPropertyConditionSet());
153
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&);
156     
157     static std::unique_ptr<AccessCase> fromStructureStubInfo(VM&, JSCell* owner, StructureStubInfo&);
158
159     ~AccessCase();
160     
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; }
165     
166     Structure* structure() const
167     {
168         if (m_type == Transition)
169             return m_structure->previousID();
170         return m_structure.get();
171     }
172     bool guardedByStructureCheck() const;
173
174     Structure* newStructure() const
175     {
176         ASSERT(m_type == Transition);
177         return m_structure.get();
178     }
179     
180     ObjectPropertyConditionSet conditionSet() const { return m_conditionSet; }
181     JSFunction* intrinsicFunction() const
182     {
183         ASSERT(type() == IntrinsicGetter && m_rareData);
184         return m_rareData->intrinsicFunction.get();
185     }
186     Intrinsic intrinsic() const
187     {
188         return intrinsicFunction()->intrinsic();
189     }
190
191     WatchpointSet* additionalSet() const
192     {
193         return m_rareData ? m_rareData->additionalSet.get() : nullptr;
194     }
195
196     JSObject* customSlotBase() const
197     {
198         return m_rareData ? m_rareData->customSlotBase.get() : nullptr;
199     }
200
201     JSObject* alternateBase() const;
202
203     // If you supply the optional vector, this will append the set of cells that this will need to keep alive
204     // past the call.
205     bool doesCalls(Vector<JSCell*>* cellsToMark = nullptr) const;
206
207     bool isGetter() const
208     {
209         switch (type()) {
210         case Getter:
211         case CustomValueGetter:
212         case CustomAccessorGetter:
213             return true;
214         default:
215             return false;
216         }
217     }
218
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
222     {
223         if (!m_rareData)
224             return nullptr;
225         return m_rareData->callLinkInfo.get();
226     }
227     
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;
233     
234     static bool canEmitIntrinsicGetter(JSFunction*, Structure*);
235
236     bool canBeReplacedByMegamorphicLoad() const;
237
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;
242
243     void dump(PrintStream& out) const;
244     
245 private:
246     friend class CodeBlock;
247     friend class PolymorphicAccess;
248
249     AccessCase();
250
251     bool visitWeak(VM&) const;
252     
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;
256     
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&);
260
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);
265
266     // Fall through on success, add a jump to the failure list on failure.
267     void generate(AccessGenerationState&);
268     
269     void generateImpl(AccessGenerationState&);
270     void emitIntrinsicGetter(AccessGenerationState&);
271     
272     AccessType m_type { Load };
273     State m_state { Primordial };
274     PropertyOffset m_offset { invalidOffset };
275
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;
280
281     ObjectPropertyConditionSet m_conditionSet;
282
283     class RareData {
284         WTF_MAKE_FAST_ALLOCATED;
285     public:
286         RareData()
287             : viaProxy(false)
288         {
289             customAccessor.opaque = nullptr;
290         }
291         
292         bool viaProxy;
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;
297         union {
298             PropertySlot::GetValueFunc getter;
299             PutPropertySlot::PutValueFunc setter;
300             void* opaque;
301         } customAccessor;
302         WriteBarrier<JSObject> customSlotBase;
303         WriteBarrier<JSFunction> intrinsicFunction;
304     };
305
306     std::unique_ptr<RareData> m_rareData;
307 };
308
309 class AccessGenerationResult {
310 public:
311     enum Kind {
312         MadeNoChanges,
313         GaveUp,
314         Buffered,
315         GeneratedNewCode,
316         GeneratedFinalCode // Generated so much code that we never want to generate code again.
317     };
318     
319     AccessGenerationResult()
320     {
321     }
322     
323     AccessGenerationResult(Kind kind)
324         : m_kind(kind)
325     {
326         RELEASE_ASSERT(kind != GeneratedNewCode);
327         RELEASE_ASSERT(kind != GeneratedFinalCode);
328     }
329     
330     AccessGenerationResult(Kind kind, MacroAssemblerCodePtr code)
331         : m_kind(kind)
332         , m_code(code)
333     {
334         RELEASE_ASSERT(kind == GeneratedNewCode || kind == GeneratedFinalCode);
335         RELEASE_ASSERT(code);
336     }
337     
338     bool operator==(const AccessGenerationResult& other) const
339     {
340         return m_kind == other.m_kind && m_code == other.m_code;
341     }
342     
343     bool operator!=(const AccessGenerationResult& other) const
344     {
345         return !(*this == other);
346     }
347     
348     explicit operator bool() const
349     {
350         return *this != AccessGenerationResult();
351     }
352     
353     Kind kind() const { return m_kind; }
354     
355     const MacroAssemblerCodePtr& code() const { return m_code; }
356     
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; }
362     
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(); }
366     
367     bool generatedSomeCode() const { return generatedNewCode() || generatedFinalCode(); }
368     
369     void dump(PrintStream&) const;
370     
371 private:
372     Kind m_kind;
373     MacroAssemblerCodePtr m_code;
374 };
375
376 class PolymorphicAccess {
377     WTF_MAKE_NONCOPYABLE(PolymorphicAccess);
378     WTF_MAKE_FAST_ALLOCATED;
379 public:
380     PolymorphicAccess();
381     ~PolymorphicAccess();
382
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>>);
387
388     AccessGenerationResult addCase(
389         VM&, CodeBlock*, StructureStubInfo&, const Identifier&, std::unique_ptr<AccessCase>);
390     
391     AccessGenerationResult regenerate(VM&, CodeBlock*, StructureStubInfo&, const Identifier&);
392     
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]; }
397
398     // If this returns false then we are requesting a reset of the owning StructureStubInfo.
399     bool visitWeak(VM&) const;
400
401     void aboutToDie();
402
403     void dump(PrintStream& out) const;
404     bool containsPC(void* pc) const
405     { 
406         if (!m_stubRoutine)
407             return false;
408
409         uintptr_t pcAsInt = bitwise_cast<uintptr_t>(pc);
410         return m_stubRoutine->startAddress() <= pcAsInt && pcAsInt <= m_stubRoutine->endAddress();
411     }
412
413 private:
414     friend class AccessCase;
415     friend class CodeBlock;
416     friend struct AccessGenerationState;
417     
418     typedef Vector<std::unique_ptr<AccessCase>, 2> ListType;
419     
420     void commit(
421         VM&, std::unique_ptr<WatchpointsOnStructureStubInfo>&, CodeBlock*, StructureStubInfo&,
422         const Identifier&, AccessCase&);
423
424     MacroAssemblerCodePtr regenerate(
425         VM&, CodeBlock*, StructureStubInfo&, const Identifier&, ListType& cases);
426
427     ListType m_list;
428     RefPtr<JITStubRoutine> m_stubRoutine;
429     std::unique_ptr<WatchpointsOnStructureStubInfo> m_watchpoints;
430     std::unique_ptr<Vector<WriteBarrier<JSCell>>> m_weakReferences;
431 };
432
433 struct AccessGenerationState {
434     AccessGenerationState()
435         : m_calculatedRegistersForCallAndExceptionHandling(false)
436         , m_needsToRestoreRegistersIfException(false)
437         , m_calculatedCallSiteIndex(false)
438     {
439     }
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;
454
455     Watchpoint* addWatchpoint(const ObjectPropertyCondition& = ObjectPropertyCondition());
456
457     void restoreScratch();
458     void succeed();
459
460     void calculateLiveRegistersForCallAndExceptionHandling(const RegisterSet& extra = RegisterSet());
461
462     void preserveLiveRegistersToStackForCall(const RegisterSet& extra = RegisterSet());
463
464     void restoreLiveRegistersFromStackForCall(bool isGetter = false);
465     void restoreLiveRegistersFromStackForCallWithThrownException();
466     void restoreLiveRegistersFromStackForCall(const RegisterSet& dontRestore);
467
468     const RegisterSet& liveRegistersForCall()
469     {
470         RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
471         return m_liveRegistersForCall;
472     }
473
474     CallSiteIndex callSiteIndexForExceptionHandlingOrOriginal();
475     CallSiteIndex callSiteIndexForExceptionHandling()
476     {
477         RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
478         RELEASE_ASSERT(m_needsToRestoreRegistersIfException);
479         RELEASE_ASSERT(m_calculatedCallSiteIndex);
480         return m_callSiteIndex;
481     }
482
483     const HandlerInfo& originalExceptionHandler() const;
484     unsigned numberOfStackBytesUsedForRegisterPreservation() const
485     {
486         RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
487         return m_numberOfStackBytesUsedForRegisterPreservation;
488     }
489
490     bool needsToRestoreRegistersIfException() const { return m_needsToRestoreRegistersIfException; }
491     CallSiteIndex originalCallSiteIndex() const;
492     
493     void emitExplicitExceptionHandler();
494     
495 private:
496     const RegisterSet& liveRegistersToPreserveAtExceptionHandlingCallSite()
497     {
498         RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
499         return m_liveRegistersToPreserveAtExceptionHandlingCallSite;
500     }
501     
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;
509 };
510
511 } // namespace JSC
512
513 namespace WTF {
514
515 void printInternal(PrintStream&, JSC::AccessGenerationResult::Kind);
516 void printInternal(PrintStream&, JSC::AccessCase::AccessType);
517 void printInternal(PrintStream&, JSC::AccessCase::State);
518
519 } // namespace WTF
520
521 #endif // ENABLE(JIT)
522
523 #endif // PolymorphicAccess_h
524