9e96da96a7026e02dae0d2a4f2d870f575ed6af6
[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     CallLinkInfo* callLinkInfo() const
220     {
221         if (!m_rareData)
222             return nullptr;
223         return m_rareData->callLinkInfo.get();
224     }
225     
226     // Is it still possible for this case to ever be taken?  Must call this as a prerequisite for
227     // calling generate() and friends.  If this returns true, then you can call generate().  If
228     // this returns false, then generate() will crash.  You must call generate() in the same epoch
229     // as when you called couldStillSucceed().
230     bool couldStillSucceed() const;
231     
232     static bool canEmitIntrinsicGetter(JSFunction*, Structure*);
233
234     bool canBeReplacedByMegamorphicLoad() const;
235
236     // If this method returns true, then it's a good idea to remove 'other' from the access once 'this'
237     // is added. This method assumes that in case of contradictions, 'this' represents a newer, and so
238     // more useful, truth. This method can be conservative; it will return false when it doubt.
239     bool canReplace(const AccessCase& other) const;
240
241     void dump(PrintStream& out) const;
242     
243 private:
244     friend class CodeBlock;
245     friend class PolymorphicAccess;
246
247     AccessCase();
248
249     bool visitWeak(VM&) const;
250     
251     // FIXME: This only exists because of how AccessCase puts post-generation things into itself.
252     // https://bugs.webkit.org/show_bug.cgi?id=156456
253     std::unique_ptr<AccessCase> clone() const;
254     
255     // Perform any action that must be performed before the end of the epoch in which the case
256     // was created. Returns a set of watchpoint sets that will need to be watched.
257     Vector<WatchpointSet*, 2> commit(VM&, const Identifier&);
258
259     // Fall through on success. Two kinds of failures are supported: fall-through, which means that we
260     // should try a different case; and failure, which means that this was the right case but it needs
261     // help from the slow path.
262     void generateWithGuard(AccessGenerationState&, MacroAssembler::JumpList& fallThrough);
263
264     // Fall through on success, add a jump to the failure list on failure.
265     void generate(AccessGenerationState&);
266     
267     void generateImpl(AccessGenerationState&);
268     void emitIntrinsicGetter(AccessGenerationState&);
269     
270     AccessType m_type { Load };
271     State m_state { Primordial };
272     PropertyOffset m_offset { invalidOffset };
273
274     // Usually this is the structure that we expect the base object to have. But, this is the *new*
275     // structure for a transition and we rely on the fact that it has a strong reference to the old
276     // structure. For proxies, this is the structure of the object behind the proxy.
277     WriteBarrier<Structure> m_structure;
278
279     ObjectPropertyConditionSet m_conditionSet;
280
281     class RareData {
282         WTF_MAKE_FAST_ALLOCATED;
283     public:
284         RareData()
285             : viaProxy(false)
286         {
287             customAccessor.opaque = nullptr;
288         }
289         
290         bool viaProxy;
291         RefPtr<WatchpointSet> additionalSet;
292         // FIXME: This should probably live in the stub routine object.
293         // https://bugs.webkit.org/show_bug.cgi?id=156456
294         std::unique_ptr<CallLinkInfo> callLinkInfo;
295         union {
296             PropertySlot::GetValueFunc getter;
297             PutPropertySlot::PutValueFunc setter;
298             void* opaque;
299         } customAccessor;
300         WriteBarrier<JSObject> customSlotBase;
301         WriteBarrier<JSFunction> intrinsicFunction;
302     };
303
304     std::unique_ptr<RareData> m_rareData;
305 };
306
307 class AccessGenerationResult {
308 public:
309     enum Kind {
310         MadeNoChanges,
311         GaveUp,
312         GeneratedNewCode
313     };
314     
315     AccessGenerationResult()
316     {
317     }
318     
319     AccessGenerationResult(Kind kind)
320         : m_kind(kind)
321     {
322         ASSERT(kind != GeneratedNewCode);
323     }
324     
325     AccessGenerationResult(MacroAssemblerCodePtr code)
326         : m_kind(GeneratedNewCode)
327         , m_code(code)
328     {
329         RELEASE_ASSERT(code);
330     }
331     
332     bool operator==(const AccessGenerationResult& other) const
333     {
334         return m_kind == other.m_kind && m_code == other.m_code;
335     }
336     
337     bool operator!=(const AccessGenerationResult& other) const
338     {
339         return !(*this == other);
340     }
341     
342     explicit operator bool() const
343     {
344         return *this != AccessGenerationResult();
345     }
346     
347     Kind kind() const { return m_kind; }
348     
349     const MacroAssemblerCodePtr& code() const { return m_code; }
350     
351     bool madeNoChanges() const { return m_kind == MadeNoChanges; }
352     bool gaveUp() const { return m_kind == GaveUp; }
353     bool generatedNewCode() const { return m_kind == GeneratedNewCode; }
354     
355     void dump(PrintStream&) const;
356     
357 private:
358     Kind m_kind;
359     MacroAssemblerCodePtr m_code;
360 };
361
362 class PolymorphicAccess {
363     WTF_MAKE_NONCOPYABLE(PolymorphicAccess);
364     WTF_MAKE_FAST_ALLOCATED;
365 public:
366     PolymorphicAccess();
367     ~PolymorphicAccess();
368
369     // This may return null, in which case the old stub routine is left intact. You are required to
370     // pass a vector of non-null access cases. This will prune the access cases by rejecting any case
371     // in the list that is subsumed by a later case in the list.
372     AccessGenerationResult regenerateWithCases(
373         VM&, CodeBlock*, StructureStubInfo&, const Identifier&, Vector<std::unique_ptr<AccessCase>>);
374
375     AccessGenerationResult regenerateWithCase(
376         VM&, CodeBlock*, StructureStubInfo&, const Identifier&, std::unique_ptr<AccessCase>);
377     
378     bool isEmpty() const { return m_list.isEmpty(); }
379     unsigned size() const { return m_list.size(); }
380     const AccessCase& at(unsigned i) const { return *m_list[i]; }
381     const AccessCase& operator[](unsigned i) const { return *m_list[i]; }
382
383     // If this returns false then we are requesting a reset of the owning StructureStubInfo.
384     bool visitWeak(VM&) const;
385
386     void aboutToDie();
387
388     void dump(PrintStream& out) const;
389     bool containsPC(void* pc) const
390     { 
391         if (!m_stubRoutine)
392             return false;
393
394         uintptr_t pcAsInt = bitwise_cast<uintptr_t>(pc);
395         return m_stubRoutine->startAddress() <= pcAsInt && pcAsInt <= m_stubRoutine->endAddress();
396     }
397
398 private:
399     friend class AccessCase;
400     friend class CodeBlock;
401     friend struct AccessGenerationState;
402     
403     typedef Vector<std::unique_ptr<AccessCase>, 2> ListType;
404
405     MacroAssemblerCodePtr regenerate(
406         VM&, CodeBlock*, StructureStubInfo&, const Identifier&, ListType& cases);
407
408     ListType m_list;
409     RefPtr<JITStubRoutine> m_stubRoutine;
410     std::unique_ptr<WatchpointsOnStructureStubInfo> m_watchpoints;
411     std::unique_ptr<Vector<WriteBarrier<JSCell>>> m_weakReferences;
412 };
413
414 struct AccessGenerationState {
415     AccessGenerationState()
416         : m_calculatedRegistersForCallAndExceptionHandling(false)
417         , m_needsToRestoreRegistersIfException(false)
418         , m_calculatedCallSiteIndex(false)
419     {
420     }
421     CCallHelpers* jit { nullptr };
422     ScratchRegisterAllocator* allocator;
423     ScratchRegisterAllocator::PreservedState preservedReusedRegisterState;
424     PolymorphicAccess* access { nullptr };
425     StructureStubInfo* stubInfo { nullptr };
426     MacroAssembler::JumpList success;
427     MacroAssembler::JumpList failAndRepatch;
428     MacroAssembler::JumpList failAndIgnore;
429     GPRReg baseGPR { InvalidGPRReg };
430     JSValueRegs valueRegs;
431     GPRReg scratchGPR { InvalidGPRReg };
432     const Identifier* ident;
433     std::unique_ptr<WatchpointsOnStructureStubInfo> watchpoints;
434     Vector<WriteBarrier<JSCell>> weakReferences;
435
436     Watchpoint* addWatchpoint(const ObjectPropertyCondition& = ObjectPropertyCondition());
437
438     void restoreScratch();
439     void succeed();
440
441     void calculateLiveRegistersForCallAndExceptionHandling(const RegisterSet& extra = RegisterSet());
442
443     void preserveLiveRegistersToStackForCall(const RegisterSet& extra = RegisterSet());
444
445     void restoreLiveRegistersFromStackForCall(bool isGetter = false);
446     void restoreLiveRegistersFromStackForCallWithThrownException();
447     void restoreLiveRegistersFromStackForCall(const RegisterSet& dontRestore);
448
449     const RegisterSet& liveRegistersForCall()
450     {
451         RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
452         return m_liveRegistersForCall;
453     }
454
455     CallSiteIndex callSiteIndexForExceptionHandlingOrOriginal();
456     CallSiteIndex callSiteIndexForExceptionHandling()
457     {
458         RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
459         RELEASE_ASSERT(m_needsToRestoreRegistersIfException);
460         RELEASE_ASSERT(m_calculatedCallSiteIndex);
461         return m_callSiteIndex;
462     }
463
464     const HandlerInfo& originalExceptionHandler() const;
465     unsigned numberOfStackBytesUsedForRegisterPreservation() const
466     {
467         RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
468         return m_numberOfStackBytesUsedForRegisterPreservation;
469     }
470
471     bool needsToRestoreRegistersIfException() const { return m_needsToRestoreRegistersIfException; }
472     CallSiteIndex originalCallSiteIndex() const;
473     
474     void emitExplicitExceptionHandler();
475     
476 private:
477     const RegisterSet& liveRegistersToPreserveAtExceptionHandlingCallSite()
478     {
479         RELEASE_ASSERT(m_calculatedRegistersForCallAndExceptionHandling);
480         return m_liveRegistersToPreserveAtExceptionHandlingCallSite;
481     }
482     
483     RegisterSet m_liveRegistersToPreserveAtExceptionHandlingCallSite;
484     RegisterSet m_liveRegistersForCall;
485     CallSiteIndex m_callSiteIndex { CallSiteIndex(std::numeric_limits<unsigned>::max()) };
486     unsigned m_numberOfStackBytesUsedForRegisterPreservation { std::numeric_limits<unsigned>::max() };
487     bool m_calculatedRegistersForCallAndExceptionHandling : 1;
488     bool m_needsToRestoreRegistersIfException : 1;
489     bool m_calculatedCallSiteIndex : 1;
490 };
491
492 } // namespace JSC
493
494 namespace WTF {
495
496 void printInternal(PrintStream&, JSC::AccessGenerationResult::Kind);
497 void printInternal(PrintStream&, JSC::AccessCase::AccessType);
498 void printInternal(PrintStream&, JSC::AccessCase::State);
499
500 } // namespace WTF
501
502 #endif // ENABLE(JIT)
503
504 #endif // PolymorphicAccess_h
505