b68089dcfb1697f0205269d30a7c0f95af90d246
[WebKit-https.git] / Source / JavaScriptCore / bytecode / PolymorphicAccess.cpp
1 /*
2  * Copyright (C) 2014-2018 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 #include "config.h"
27 #include "PolymorphicAccess.h"
28
29 #if ENABLE(JIT)
30
31 #include "BinarySwitch.h"
32 #include "CCallHelpers.h"
33 #include "CodeBlock.h"
34 #include "FullCodeOrigin.h"
35 #include "Heap.h"
36 #include "JITOperations.h"
37 #include "JSCInlines.h"
38 #include "LinkBuffer.h"
39 #include "StructureStubClearingWatchpoint.h"
40 #include "StructureStubInfo.h"
41 #include "SuperSampler.h"
42 #include <wtf/CommaPrinter.h>
43 #include <wtf/ListDump.h>
44
45 namespace JSC {
46
47 namespace PolymorphicAccessInternal {
48 static const bool verbose = false;
49 }
50
51 void AccessGenerationResult::dump(PrintStream& out) const
52 {
53     out.print(m_kind);
54     if (m_code)
55         out.print(":", m_code);
56 }
57
58 Watchpoint* AccessGenerationState::addWatchpoint(const ObjectPropertyCondition& condition)
59 {
60     return WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint(
61         watchpoints, jit->codeBlock(), stubInfo, condition);
62 }
63
64 void AccessGenerationState::restoreScratch()
65 {
66     allocator->restoreReusedRegistersByPopping(*jit, preservedReusedRegisterState);
67 }
68
69 void AccessGenerationState::succeed()
70 {
71     restoreScratch();
72     success.append(jit->jump());
73 }
74
75 const RegisterSet& AccessGenerationState::liveRegistersForCall()
76 {
77     if (!m_calculatedRegistersForCallAndExceptionHandling)
78         calculateLiveRegistersForCallAndExceptionHandling();
79     return m_liveRegistersForCall;
80 }
81
82 const RegisterSet& AccessGenerationState::liveRegistersToPreserveAtExceptionHandlingCallSite()
83 {
84     if (!m_calculatedRegistersForCallAndExceptionHandling)
85         calculateLiveRegistersForCallAndExceptionHandling();
86     return m_liveRegistersToPreserveAtExceptionHandlingCallSite;
87 }
88
89 static RegisterSet calleeSaveRegisters()
90 {
91     RegisterSet result = RegisterSet::registersToNotSaveForJSCall();
92     result.filter(RegisterSet::registersToNotSaveForCCall());
93     return result;
94 }
95
96 const RegisterSet& AccessGenerationState::calculateLiveRegistersForCallAndExceptionHandling()
97 {
98     if (!m_calculatedRegistersForCallAndExceptionHandling) {
99         m_calculatedRegistersForCallAndExceptionHandling = true;
100
101         m_liveRegistersToPreserveAtExceptionHandlingCallSite = jit->codeBlock()->jitCode()->liveRegistersToPreserveAtExceptionHandlingCallSite(jit->codeBlock(), stubInfo->callSiteIndex);
102         m_needsToRestoreRegistersIfException = m_liveRegistersToPreserveAtExceptionHandlingCallSite.numberOfSetRegisters() > 0;
103         if (m_needsToRestoreRegistersIfException)
104             RELEASE_ASSERT(JITCode::isOptimizingJIT(jit->codeBlock()->jitType()));
105
106         m_liveRegistersForCall = RegisterSet(m_liveRegistersToPreserveAtExceptionHandlingCallSite, allocator->usedRegisters());
107         m_liveRegistersForCall.exclude(calleeSaveRegisters());
108     }
109     return m_liveRegistersForCall;
110 }
111
112 auto AccessGenerationState::preserveLiveRegistersToStackForCall(const RegisterSet& extra) -> SpillState
113 {
114     RegisterSet liveRegisters = liveRegistersForCall();
115     liveRegisters.merge(extra);
116     
117     unsigned extraStackPadding = 0;
118     unsigned numberOfStackBytesUsedForRegisterPreservation = ScratchRegisterAllocator::preserveRegistersToStackForCall(*jit, liveRegisters, extraStackPadding);
119     return SpillState {
120         WTFMove(liveRegisters),
121         numberOfStackBytesUsedForRegisterPreservation
122     };
123 }
124
125 void AccessGenerationState::restoreLiveRegistersFromStackForCallWithThrownException(const SpillState& spillState)
126 {
127     // Even if we're a getter, we don't want to ignore the result value like we normally do
128     // because the getter threw, and therefore, didn't return a value that means anything.
129     // Instead, we want to restore that register to what it was upon entering the getter
130     // inline cache. The subtlety here is if the base and the result are the same register,
131     // and the getter threw, we want OSR exit to see the original base value, not the result
132     // of the getter call.
133     RegisterSet dontRestore = spillState.spilledRegisters;
134     // As an optimization here, we only need to restore what is live for exception handling.
135     // We can construct the dontRestore set to accomplish this goal by having it contain only
136     // what is live for call but not live for exception handling. By ignoring things that are
137     // only live at the call but not the exception handler, we will only restore things live
138     // at the exception handler.
139     dontRestore.exclude(liveRegistersToPreserveAtExceptionHandlingCallSite());
140     restoreLiveRegistersFromStackForCall(spillState, dontRestore);
141 }
142
143 void AccessGenerationState::restoreLiveRegistersFromStackForCall(const SpillState& spillState, const RegisterSet& dontRestore)
144 {
145     unsigned extraStackPadding = 0;
146     ScratchRegisterAllocator::restoreRegistersFromStackForCall(*jit, spillState.spilledRegisters, dontRestore, spillState.numberOfStackBytesUsedForRegisterPreservation, extraStackPadding);
147 }
148
149 CallSiteIndex AccessGenerationState::callSiteIndexForExceptionHandlingOrOriginal()
150 {
151     if (!m_calculatedRegistersForCallAndExceptionHandling)
152         calculateLiveRegistersForCallAndExceptionHandling();
153
154     if (!m_calculatedCallSiteIndex) {
155         m_calculatedCallSiteIndex = true;
156
157         if (m_needsToRestoreRegistersIfException)
158             m_callSiteIndex = jit->codeBlock()->newExceptionHandlingCallSiteIndex(stubInfo->callSiteIndex);
159         else
160             m_callSiteIndex = originalCallSiteIndex();
161     }
162
163     return m_callSiteIndex;
164 }
165
166 const HandlerInfo& AccessGenerationState::originalExceptionHandler()
167 {
168     if (!m_calculatedRegistersForCallAndExceptionHandling)
169         calculateLiveRegistersForCallAndExceptionHandling();
170
171     RELEASE_ASSERT(m_needsToRestoreRegistersIfException);
172     HandlerInfo* exceptionHandler = jit->codeBlock()->handlerForIndex(stubInfo->callSiteIndex.bits());
173     RELEASE_ASSERT(exceptionHandler);
174     return *exceptionHandler;
175 }
176
177 CallSiteIndex AccessGenerationState::originalCallSiteIndex() const { return stubInfo->callSiteIndex; }
178
179 void AccessGenerationState::emitExplicitExceptionHandler()
180 {
181     restoreScratch();
182     jit->copyCalleeSavesToEntryFrameCalleeSavesBuffer(m_vm.topEntryFrame);
183     if (needsToRestoreRegistersIfException()) {
184         // To the JIT that produces the original exception handling
185         // call site, they will expect the OSR exit to be arrived
186         // at from genericUnwind. Therefore we must model what genericUnwind
187         // does here. I.e, set callFrameForCatch and copy callee saves.
188
189         jit->storePtr(GPRInfo::callFrameRegister, m_vm.addressOfCallFrameForCatch());
190         CCallHelpers::Jump jumpToOSRExitExceptionHandler = jit->jump();
191
192         // We don't need to insert a new exception handler in the table
193         // because we're doing a manual exception check here. i.e, we'll
194         // never arrive here from genericUnwind().
195         HandlerInfo originalHandler = originalExceptionHandler();
196         jit->addLinkTask(
197             [=] (LinkBuffer& linkBuffer) {
198                 linkBuffer.link(jumpToOSRExitExceptionHandler, originalHandler.nativeCode);
199             });
200     } else {
201         jit->setupArguments<decltype(lookupExceptionHandler)>(CCallHelpers::TrustedImmPtr(&m_vm), GPRInfo::callFrameRegister);
202         CCallHelpers::Call lookupExceptionHandlerCall = jit->call(OperationPtrTag);
203         jit->addLinkTask(
204             [=] (LinkBuffer& linkBuffer) {
205                 linkBuffer.link(lookupExceptionHandlerCall, FunctionPtr<OperationPtrTag>(lookupExceptionHandler));
206             });
207         jit->jumpToExceptionHandler(m_vm);
208     }
209 }
210
211
212 PolymorphicAccess::PolymorphicAccess() { }
213 PolymorphicAccess::~PolymorphicAccess() { }
214
215 AccessGenerationResult PolymorphicAccess::addCases(
216     const GCSafeConcurrentJSLocker& locker, VM& vm, CodeBlock* codeBlock, StructureStubInfo& stubInfo,
217     const Identifier& ident, Vector<std::unique_ptr<AccessCase>, 2> originalCasesToAdd)
218 {
219     SuperSamplerScope superSamplerScope(false);
220     
221     // This method will add the originalCasesToAdd to the list one at a time while preserving the
222     // invariants:
223     // - If a newly added case canReplace() any existing case, then the existing case is removed before
224     //   the new case is added. Removal doesn't change order of the list. Any number of existing cases
225     //   can be removed via the canReplace() rule.
226     // - Cases in the list always appear in ascending order of time of addition. Therefore, if you
227     //   cascade through the cases in reverse order, you will get the most recent cases first.
228     // - If this method fails (returns null, doesn't add the cases), then both the previous case list
229     //   and the previous stub are kept intact and the new cases are destroyed. It's OK to attempt to
230     //   add more things after failure.
231     
232     // First ensure that the originalCasesToAdd doesn't contain duplicates.
233     Vector<std::unique_ptr<AccessCase>> casesToAdd;
234     for (unsigned i = 0; i < originalCasesToAdd.size(); ++i) {
235         std::unique_ptr<AccessCase> myCase = WTFMove(originalCasesToAdd[i]);
236
237         // Add it only if it is not replaced by the subsequent cases in the list.
238         bool found = false;
239         for (unsigned j = i + 1; j < originalCasesToAdd.size(); ++j) {
240             if (originalCasesToAdd[j]->canReplace(*myCase)) {
241                 found = true;
242                 break;
243             }
244         }
245
246         if (found)
247             continue;
248         
249         casesToAdd.append(WTFMove(myCase));
250     }
251
252     if (PolymorphicAccessInternal::verbose)
253         dataLog("casesToAdd: ", listDump(casesToAdd), "\n");
254
255     // If there aren't any cases to add, then fail on the grounds that there's no point to generating a
256     // new stub that will be identical to the old one. Returning null should tell the caller to just
257     // keep doing what they were doing before.
258     if (casesToAdd.isEmpty())
259         return AccessGenerationResult::MadeNoChanges;
260
261     if (stubInfo.accessType != AccessType::InstanceOf) {
262         bool shouldReset = false;
263         AccessGenerationResult resetResult(AccessGenerationResult::ResetStubAndFireWatchpoints);
264         auto considerPolyProtoReset = [&] (Structure* a, Structure* b) {
265             if (Structure::shouldConvertToPolyProto(a, b)) {
266                 // For now, we only reset if this is our first time invalidating this watchpoint.
267                 // The reason we don't immediately fire this watchpoint is that we may be already
268                 // watching the poly proto watchpoint, which if fired, would destroy us. We let
269                 // the person handling the result to do a delayed fire.
270                 ASSERT(a->rareData()->sharedPolyProtoWatchpoint().get() == b->rareData()->sharedPolyProtoWatchpoint().get());
271                 if (a->rareData()->sharedPolyProtoWatchpoint()->isStillValid()) {
272                     shouldReset = true;
273                     resetResult.addWatchpointToFire(*a->rareData()->sharedPolyProtoWatchpoint(), StringFireDetail("Detected poly proto optimization opportunity."));
274                 }
275             }
276         };
277
278         for (auto& caseToAdd : casesToAdd) {
279             for (auto& existingCase : m_list) {
280                 Structure* a = caseToAdd->structure();
281                 Structure* b = existingCase->structure();
282                 considerPolyProtoReset(a, b);
283             }
284         }
285         for (unsigned i = 0; i < casesToAdd.size(); ++i) {
286             for (unsigned j = i + 1; j < casesToAdd.size(); ++j) {
287                 Structure* a = casesToAdd[i]->structure();
288                 Structure* b = casesToAdd[j]->structure();
289                 considerPolyProtoReset(a, b);
290             }
291         }
292
293         if (shouldReset)
294             return resetResult;
295     }
296
297     // Now add things to the new list. Note that at this point, we will still have old cases that
298     // may be replaced by the new ones. That's fine. We will sort that out when we regenerate.
299     for (auto& caseToAdd : casesToAdd) {
300         commit(locker, vm, m_watchpoints, codeBlock, stubInfo, ident, *caseToAdd);
301         m_list.append(WTFMove(caseToAdd));
302     }
303     
304     if (PolymorphicAccessInternal::verbose)
305         dataLog("After addCases: m_list: ", listDump(m_list), "\n");
306
307     return AccessGenerationResult::Buffered;
308 }
309
310 AccessGenerationResult PolymorphicAccess::addCase(
311     const GCSafeConcurrentJSLocker& locker, VM& vm, CodeBlock* codeBlock, StructureStubInfo& stubInfo,
312     const Identifier& ident, std::unique_ptr<AccessCase> newAccess)
313 {
314     Vector<std::unique_ptr<AccessCase>, 2> newAccesses;
315     newAccesses.append(WTFMove(newAccess));
316     return addCases(locker, vm, codeBlock, stubInfo, ident, WTFMove(newAccesses));
317 }
318
319 bool PolymorphicAccess::visitWeak(VM& vm) const
320 {
321     for (unsigned i = 0; i < size(); ++i) {
322         if (!at(i).visitWeak(vm))
323             return false;
324     }
325     if (Vector<WriteBarrier<JSCell>>* weakReferences = m_weakReferences.get()) {
326         for (WriteBarrier<JSCell>& weakReference : *weakReferences) {
327             if (!Heap::isMarked(weakReference.get()))
328                 return false;
329         }
330     }
331     return true;
332 }
333
334 bool PolymorphicAccess::propagateTransitions(SlotVisitor& visitor) const
335 {
336     bool result = true;
337     for (unsigned i = 0; i < size(); ++i)
338         result &= at(i).propagateTransitions(visitor);
339     return result;
340 }
341
342 void PolymorphicAccess::dump(PrintStream& out) const
343 {
344     out.print(RawPointer(this), ":[");
345     CommaPrinter comma;
346     for (auto& entry : m_list)
347         out.print(comma, *entry);
348     out.print("]");
349 }
350
351 void PolymorphicAccess::commit(
352     const GCSafeConcurrentJSLocker&, VM& vm, std::unique_ptr<WatchpointsOnStructureStubInfo>& watchpoints, CodeBlock* codeBlock,
353     StructureStubInfo& stubInfo, const Identifier& ident, AccessCase& accessCase)
354 {
355     // NOTE: We currently assume that this is relatively rare. It mainly arises for accesses to
356     // properties on DOM nodes. For sure we cache many DOM node accesses, but even in
357     // Real Pages (TM), we appear to spend most of our time caching accesses to properties on
358     // vanilla objects or exotic objects from within JSC (like Arguments, those are super popular).
359     // Those common kinds of JSC object accesses don't hit this case.
360     
361     for (WatchpointSet* set : accessCase.commit(vm, ident)) {
362         Watchpoint* watchpoint =
363             WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint(
364                 watchpoints, codeBlock, &stubInfo, ObjectPropertyCondition());
365         
366         set->add(watchpoint);
367     }
368 }
369
370 AccessGenerationResult PolymorphicAccess::regenerate(
371     const GCSafeConcurrentJSLocker& locker, VM& vm, CodeBlock* codeBlock, StructureStubInfo& stubInfo, const Identifier& ident)
372 {
373     SuperSamplerScope superSamplerScope(false);
374     
375     if (PolymorphicAccessInternal::verbose)
376         dataLog("Regenerate with m_list: ", listDump(m_list), "\n");
377     
378     AccessGenerationState state(vm, codeBlock->globalObject());
379
380     state.access = this;
381     state.stubInfo = &stubInfo;
382     state.ident = &ident;
383     
384     state.baseGPR = stubInfo.baseGPR();
385     state.thisGPR = stubInfo.patch.thisGPR;
386     state.valueRegs = stubInfo.valueRegs();
387
388     ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters);
389     state.allocator = &allocator;
390     allocator.lock(state.baseGPR);
391     if (state.thisGPR != InvalidGPRReg)
392         allocator.lock(state.thisGPR);
393     allocator.lock(state.valueRegs);
394 #if USE(JSVALUE32_64)
395     allocator.lock(stubInfo.patch.baseTagGPR);
396 #endif
397
398     state.scratchGPR = allocator.allocateScratchGPR();
399     
400     CCallHelpers jit(codeBlock);
401     state.jit = &jit;
402
403     state.preservedReusedRegisterState =
404         allocator.preserveReusedRegistersByPushing(jit, ScratchRegisterAllocator::ExtraStackSpace::NoExtraSpace);
405
406     // Regenerating is our opportunity to figure out what our list of cases should look like. We
407     // do this here. The newly produced 'cases' list may be smaller than m_list. We don't edit
408     // m_list in-place because we may still fail, in which case we want the PolymorphicAccess object
409     // to be unmutated. For sure, we want it to hang onto any data structures that may be referenced
410     // from the code of the current stub (aka previous).
411     ListType cases;
412     unsigned srcIndex = 0;
413     unsigned dstIndex = 0;
414     while (srcIndex < m_list.size()) {
415         std::unique_ptr<AccessCase> someCase = WTFMove(m_list[srcIndex++]);
416         
417         // If the case had been generated, then we have to keep the original in m_list in case we
418         // fail to regenerate. That case may have data structures that are used by the code that it
419         // had generated. If the case had not been generated, then we want to remove it from m_list.
420         bool isGenerated = someCase->state() == AccessCase::Generated;
421         
422         [&] () {
423             if (!someCase->couldStillSucceed())
424                 return;
425
426             // Figure out if this is replaced by any later case. Given two cases A and B where A
427             // comes first in the case list, we know that A would have triggered first if we had
428             // generated the cases in a cascade. That's why this loop asks B->canReplace(A) but not
429             // A->canReplace(B). If A->canReplace(B) was true then A would never have requested
430             // repatching in cases where Repatch.cpp would have then gone on to generate B. If that
431             // did happen by some fluke, then we'd just miss the redundancy here, which wouldn't be
432             // incorrect - just slow. However, if A's checks failed and Repatch.cpp concluded that
433             // this new condition could be handled by B and B->canReplace(A), then this says that we
434             // don't need A anymore.
435             //
436             // If we can generate a binary switch, then A->canReplace(B) == B->canReplace(A). So,
437             // it doesn't matter that we only do the check in one direction.
438             for (unsigned j = srcIndex; j < m_list.size(); ++j) {
439                 if (m_list[j]->canReplace(*someCase))
440                     return;
441             }
442             
443             if (isGenerated)
444                 cases.append(someCase->clone());
445             else
446                 cases.append(WTFMove(someCase));
447         }();
448         
449         if (isGenerated)
450             m_list[dstIndex++] = WTFMove(someCase);
451     }
452     m_list.resize(dstIndex);
453     
454     bool generatedFinalCode = false;
455
456     // If the resulting set of cases is so big that we would stop caching and this is InstanceOf,
457     // then we want to generate the generic InstanceOf and then stop.
458     if (cases.size() >= Options::maxAccessVariantListSize()
459         && stubInfo.accessType == AccessType::InstanceOf) {
460         while (!cases.isEmpty())
461             m_list.append(cases.takeLast());
462         cases.append(AccessCase::create(vm, codeBlock, AccessCase::InstanceOfGeneric));
463         generatedFinalCode = true;
464     }
465
466     if (PolymorphicAccessInternal::verbose)
467         dataLog("Optimized cases: ", listDump(cases), "\n");
468     
469     // At this point we're convinced that 'cases' contains the cases that we want to JIT now and we
470     // won't change that set anymore.
471     
472     bool allGuardedByStructureCheck = true;
473     bool hasJSGetterSetterCall = false;
474     for (auto& newCase : cases) {
475         commit(locker, vm, state.watchpoints, codeBlock, stubInfo, ident, *newCase);
476         allGuardedByStructureCheck &= newCase->guardedByStructureCheck();
477         if (newCase->type() == AccessCase::Getter || newCase->type() == AccessCase::Setter)
478             hasJSGetterSetterCall = true;
479     }
480
481     if (cases.isEmpty()) {
482         // This is super unlikely, but we make it legal anyway.
483         state.failAndRepatch.append(jit.jump());
484     } else if (!allGuardedByStructureCheck || cases.size() == 1) {
485         // If there are any proxies in the list, we cannot just use a binary switch over the structure.
486         // We need to resort to a cascade. A cascade also happens to be optimal if we only have just
487         // one case.
488         CCallHelpers::JumpList fallThrough;
489
490         // Cascade through the list, preferring newer entries.
491         for (unsigned i = cases.size(); i--;) {
492             fallThrough.link(&jit);
493             fallThrough.clear();
494             cases[i]->generateWithGuard(state, fallThrough);
495         }
496         state.failAndRepatch.append(fallThrough);
497     } else {
498         jit.load32(
499             CCallHelpers::Address(state.baseGPR, JSCell::structureIDOffset()),
500             state.scratchGPR);
501         
502         Vector<int64_t> caseValues(cases.size());
503         for (unsigned i = 0; i < cases.size(); ++i)
504             caseValues[i] = bitwise_cast<int32_t>(cases[i]->structure()->id());
505         
506         BinarySwitch binarySwitch(state.scratchGPR, caseValues, BinarySwitch::Int32);
507         while (binarySwitch.advance(jit))
508             cases[binarySwitch.caseIndex()]->generate(state);
509         state.failAndRepatch.append(binarySwitch.fallThrough());
510     }
511
512     if (!state.failAndIgnore.empty()) {
513         state.failAndIgnore.link(&jit);
514         
515         // Make sure that the inline cache optimization code knows that we are taking slow path because
516         // of something that isn't patchable. The slow path will decrement "countdown" and will only
517         // patch things if the countdown reaches zero. We increment the slow path count here to ensure
518         // that the slow path does not try to patch.
519 #if CPU(X86) || CPU(X86_64)
520         jit.move(CCallHelpers::TrustedImmPtr(&stubInfo.countdown), state.scratchGPR);
521         jit.add8(CCallHelpers::TrustedImm32(1), CCallHelpers::Address(state.scratchGPR));
522 #else
523         jit.load8(&stubInfo.countdown, state.scratchGPR);
524         jit.add32(CCallHelpers::TrustedImm32(1), state.scratchGPR);
525         jit.store8(state.scratchGPR, &stubInfo.countdown);
526 #endif
527     }
528
529     CCallHelpers::JumpList failure;
530     if (allocator.didReuseRegisters()) {
531         state.failAndRepatch.link(&jit);
532         state.restoreScratch();
533     } else
534         failure = state.failAndRepatch;
535     failure.append(jit.jump());
536
537     CodeBlock* codeBlockThatOwnsExceptionHandlers = nullptr;
538     CallSiteIndex callSiteIndexForExceptionHandling;
539     if (state.needsToRestoreRegistersIfException() && hasJSGetterSetterCall) {
540         // Emit the exception handler.
541         // Note that this code is only reachable when doing genericUnwind from a pure JS getter/setter .
542         // Note also that this is not reachable from custom getter/setter. Custom getter/setters will have 
543         // their own exception handling logic that doesn't go through genericUnwind.
544         MacroAssembler::Label makeshiftCatchHandler = jit.label();
545
546         int stackPointerOffset = codeBlock->stackPointerOffset() * sizeof(EncodedJSValue);
547         AccessGenerationState::SpillState spillStateForJSGetterSetter = state.spillStateForJSGetterSetter();
548         ASSERT(!spillStateForJSGetterSetter.isEmpty());
549         stackPointerOffset -= state.preservedReusedRegisterState.numberOfBytesPreserved;
550         stackPointerOffset -= spillStateForJSGetterSetter.numberOfStackBytesUsedForRegisterPreservation;
551
552         jit.loadPtr(vm.addressOfCallFrameForCatch(), GPRInfo::callFrameRegister);
553         jit.addPtr(CCallHelpers::TrustedImm32(stackPointerOffset), GPRInfo::callFrameRegister, CCallHelpers::stackPointerRegister);
554
555         state.restoreLiveRegistersFromStackForCallWithThrownException(spillStateForJSGetterSetter);
556         state.restoreScratch();
557         CCallHelpers::Jump jumpToOSRExitExceptionHandler = jit.jump();
558
559         HandlerInfo oldHandler = state.originalExceptionHandler();
560         CallSiteIndex newExceptionHandlingCallSite = state.callSiteIndexForExceptionHandling();
561         jit.addLinkTask(
562             [=] (LinkBuffer& linkBuffer) {
563                 linkBuffer.link(jumpToOSRExitExceptionHandler, oldHandler.nativeCode);
564
565                 HandlerInfo handlerToRegister = oldHandler;
566                 handlerToRegister.nativeCode = linkBuffer.locationOf<ExceptionHandlerPtrTag>(makeshiftCatchHandler);
567                 handlerToRegister.start = newExceptionHandlingCallSite.bits();
568                 handlerToRegister.end = newExceptionHandlingCallSite.bits() + 1;
569                 codeBlock->appendExceptionHandler(handlerToRegister);
570             });
571
572         // We set these to indicate to the stub to remove itself from the CodeBlock's
573         // exception handler table when it is deallocated.
574         codeBlockThatOwnsExceptionHandlers = codeBlock;
575         ASSERT(JITCode::isOptimizingJIT(codeBlockThatOwnsExceptionHandlers->jitType()));
576         callSiteIndexForExceptionHandling = state.callSiteIndexForExceptionHandling();
577     }
578
579     LinkBuffer linkBuffer(jit, codeBlock, JITCompilationCanFail);
580     if (linkBuffer.didFailToAllocate()) {
581         if (PolymorphicAccessInternal::verbose)
582             dataLog("Did fail to allocate.\n");
583         return AccessGenerationResult::GaveUp;
584     }
585
586     CodeLocationLabel<JSInternalPtrTag> successLabel = stubInfo.doneLocation();
587
588     linkBuffer.link(state.success, successLabel);
589
590     linkBuffer.link(failure, stubInfo.slowPathStartLocation());
591     
592     if (PolymorphicAccessInternal::verbose)
593         dataLog(FullCodeOrigin(codeBlock, stubInfo.codeOrigin), ": Generating polymorphic access stub for ", listDump(cases), "\n");
594
595     MacroAssemblerCodeRef<JITStubRoutinePtrTag> code = FINALIZE_CODE_FOR(
596         codeBlock, linkBuffer, JITStubRoutinePtrTag,
597         "%s", toCString("Access stub for ", *codeBlock, " ", stubInfo.codeOrigin, " with return point ", successLabel, ": ", listDump(cases)).data());
598
599     bool doesCalls = false;
600     Vector<JSCell*> cellsToMark;
601     for (auto& entry : cases)
602         doesCalls |= entry->doesCalls(&cellsToMark);
603     
604     m_stubRoutine = createJITStubRoutine(code, vm, codeBlock, doesCalls, cellsToMark, codeBlockThatOwnsExceptionHandlers, callSiteIndexForExceptionHandling);
605     m_watchpoints = WTFMove(state.watchpoints);
606     if (!state.weakReferences.isEmpty())
607         m_weakReferences = std::make_unique<Vector<WriteBarrier<JSCell>>>(WTFMove(state.weakReferences));
608     if (PolymorphicAccessInternal::verbose)
609         dataLog("Returning: ", code.code(), "\n");
610     
611     m_list = WTFMove(cases);
612     
613     AccessGenerationResult::Kind resultKind;
614     if (m_list.size() >= Options::maxAccessVariantListSize() || generatedFinalCode)
615         resultKind = AccessGenerationResult::GeneratedFinalCode;
616     else
617         resultKind = AccessGenerationResult::GeneratedNewCode;
618     
619     return AccessGenerationResult(resultKind, code.code());
620 }
621
622 void PolymorphicAccess::aboutToDie()
623 {
624     if (m_stubRoutine)
625         m_stubRoutine->aboutToDie();
626 }
627
628 } // namespace JSC
629
630 namespace WTF {
631
632 using namespace JSC;
633
634 void printInternal(PrintStream& out, AccessGenerationResult::Kind kind)
635 {
636     switch (kind) {
637     case AccessGenerationResult::MadeNoChanges:
638         out.print("MadeNoChanges");
639         return;
640     case AccessGenerationResult::GaveUp:
641         out.print("GaveUp");
642         return;
643     case AccessGenerationResult::Buffered:
644         out.print("Buffered");
645         return;
646     case AccessGenerationResult::GeneratedNewCode:
647         out.print("GeneratedNewCode");
648         return;
649     case AccessGenerationResult::GeneratedFinalCode:
650         out.print("GeneratedFinalCode");
651         return;
652     case AccessGenerationResult::ResetStubAndFireWatchpoints:
653         out.print("ResetStubAndFireWatchpoints");
654         return;
655     }
656     
657     RELEASE_ASSERT_NOT_REACHED();
658 }
659
660 void printInternal(PrintStream& out, AccessCase::AccessType type)
661 {
662     switch (type) {
663     case AccessCase::Load:
664         out.print("Load");
665         return;
666     case AccessCase::Transition:
667         out.print("Transition");
668         return;
669     case AccessCase::Replace:
670         out.print("Replace");
671         return;
672     case AccessCase::Miss:
673         out.print("Miss");
674         return;
675     case AccessCase::GetGetter:
676         out.print("GetGetter");
677         return;
678     case AccessCase::Getter:
679         out.print("Getter");
680         return;
681     case AccessCase::Setter:
682         out.print("Setter");
683         return;
684     case AccessCase::CustomValueGetter:
685         out.print("CustomValueGetter");
686         return;
687     case AccessCase::CustomAccessorGetter:
688         out.print("CustomAccessorGetter");
689         return;
690     case AccessCase::CustomValueSetter:
691         out.print("CustomValueSetter");
692         return;
693     case AccessCase::CustomAccessorSetter:
694         out.print("CustomAccessorSetter");
695         return;
696     case AccessCase::IntrinsicGetter:
697         out.print("IntrinsicGetter");
698         return;
699     case AccessCase::InHit:
700         out.print("InHit");
701         return;
702     case AccessCase::InMiss:
703         out.print("InMiss");
704         return;
705     case AccessCase::ArrayLength:
706         out.print("ArrayLength");
707         return;
708     case AccessCase::StringLength:
709         out.print("StringLength");
710         return;
711     case AccessCase::DirectArgumentsLength:
712         out.print("DirectArgumentsLength");
713         return;
714     case AccessCase::ScopedArgumentsLength:
715         out.print("ScopedArgumentsLength");
716         return;
717     case AccessCase::ModuleNamespaceLoad:
718         out.print("ModuleNamespaceLoad");
719         return;
720     case AccessCase::InstanceOfHit:
721         out.print("InstanceOfHit");
722         return;
723     case AccessCase::InstanceOfMiss:
724         out.print("InstanceOfMiss");
725         return;
726     case AccessCase::InstanceOfGeneric:
727         out.print("InstanceOfGeneric");
728         return;
729     }
730
731     RELEASE_ASSERT_NOT_REACHED();
732 }
733
734 void printInternal(PrintStream& out, AccessCase::State state)
735 {
736     switch (state) {
737     case AccessCase::Primordial:
738         out.print("Primordial");
739         return;
740     case AccessCase::Committed:
741         out.print("Committed");
742         return;
743     case AccessCase::Generated:
744         out.print("Generated");
745         return;
746     }
747
748     RELEASE_ASSERT_NOT_REACHED();
749 }
750
751 } // namespace WTF
752
753 #endif // ENABLE(JIT)
754
755