Remove excessive headers from JavaScriptCore
[WebKit-https.git] / Source / JavaScriptCore / jit / CallFrameShuffler.h
1 /*
2  * Copyright (C) 2015 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 #pragma once
27
28 #if ENABLE(JIT)
29
30 #include "CachedRecovery.h"
31 #include "CallFrameShuffleData.h"
32 #include "MacroAssembler.h"
33 #include "RegisterSet.h"
34 #include <wtf/Vector.h>
35
36 namespace JSC {
37
38 class CallFrameShuffler {
39     WTF_MAKE_FAST_ALLOCATED;
40 public:
41     CallFrameShuffler(CCallHelpers&, const CallFrameShuffleData&);
42
43     void dump(PrintStream&) const;
44
45     // Any register that has been locked or acquired must be released
46     // before calling prepareForTailCall() or prepareForSlowPath().
47     void lockGPR(GPRReg gpr)
48     {
49         ASSERT(!m_lockedRegisters.get(gpr));
50         m_lockedRegisters.set(gpr);
51         if (verbose)
52             dataLog("   * Locking ", gpr, "\n");
53     }
54
55     GPRReg acquireGPR()
56     {
57         ensureGPR();
58         GPRReg gpr { getFreeGPR() };
59         ASSERT(!m_registers[gpr]);
60         lockGPR(gpr);
61         return gpr;
62     }
63
64     void releaseGPR(GPRReg gpr)
65     {
66         if (verbose) {
67             if (m_lockedRegisters.get(gpr))
68                 dataLog("   * Releasing ", gpr, "\n");
69             else
70                 dataLog("   * ", gpr, " was not locked\n");
71         }
72         m_lockedRegisters.clear(gpr);
73     }
74
75     void restoreGPR(GPRReg gpr)
76     {
77         if (!m_newRegisters[gpr])
78             return;
79
80         ensureGPR();
81 #if USE(JSVALUE32_64)
82         GPRReg tempGPR { getFreeGPR() };
83         lockGPR(tempGPR);
84         ensureGPR();
85         releaseGPR(tempGPR);
86 #endif
87         emitDisplace(*m_newRegisters[gpr]);
88     }
89
90     // You can only take a snapshot if the recovery has not started
91     // yet. The only operations that are valid before taking a
92     // snapshot are lockGPR(), acquireGPR() and releaseGPR().
93     //
94     // Locking status is *NOT* preserved by the snapshot: it only
95     // contains information about where the
96     // arguments/callee/callee-save registers are by taking into
97     // account any spilling that acquireGPR() could have done.
98     CallFrameShuffleData snapshot() const
99     {
100         ASSERT(isUndecided());
101
102         CallFrameShuffleData data;
103         data.numLocals = numLocals();
104         data.numPassedArgs = m_numPassedArgs;
105         data.callee = getNew(VirtualRegister { CallFrameSlot::callee })->recovery();
106         data.args.resize(argCount());
107         for (size_t i = 0; i < argCount(); ++i)
108             data.args[i] = getNew(virtualRegisterForArgument(i))->recovery();
109         for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
110             CachedRecovery* cachedRecovery { m_newRegisters[reg] };
111             if (!cachedRecovery)
112                 continue;
113
114 #if USE(JSVALUE64)
115             data.registers[reg] = cachedRecovery->recovery();
116 #else
117             RELEASE_ASSERT_NOT_REACHED();
118 #endif
119         }
120         return data;
121     }
122
123     // Ask the shuffler to put the callee into some registers once the
124     // shuffling is done. You should call this before any of the
125     // prepare() methods, and must not take a snapshot afterwards, as
126     // this would crash 32bits platforms.
127     void setCalleeJSValueRegs(JSValueRegs jsValueRegs)
128     {
129         ASSERT(isUndecided());
130         ASSERT(!getNew(jsValueRegs));
131         CachedRecovery* cachedRecovery { getNew(VirtualRegister(CallFrameSlot::callee)) };
132         ASSERT(cachedRecovery);
133         addNew(jsValueRegs, cachedRecovery->recovery());
134     }
135
136     // Ask the suhffler to assume the callee has already be checked to
137     // be a cell. This is a no-op on 64bit platforms, but allows to
138     // free up a GPR on 32bit platforms.
139     // You obviously must have ensured that this is the case before
140     // running any of the prepare methods.
141     void assumeCalleeIsCell()
142     {
143 #if USE(JSVALUE32_64)
144         CachedRecovery& calleeCachedRecovery = *getNew(VirtualRegister(CallFrameSlot::callee));
145         switch (calleeCachedRecovery.recovery().technique()) {
146         case InPair:
147             updateRecovery(
148                 calleeCachedRecovery,
149                 ValueRecovery::inGPR(
150                     calleeCachedRecovery.recovery().payloadGPR(),
151                     DataFormatCell));
152             break;
153         case DisplacedInJSStack:
154             updateRecovery(
155                 calleeCachedRecovery,
156                 ValueRecovery::displacedInJSStack(
157                     calleeCachedRecovery.recovery().virtualRegister(),
158                     DataFormatCell));
159             break;
160         case InFPR:
161         case UnboxedCellInGPR:
162         case CellDisplacedInJSStack:
163             break;
164         case Constant:
165             ASSERT(calleeCachedRecovery.recovery().constant().isCell());
166             break;
167         default:
168             RELEASE_ASSERT_NOT_REACHED();
169             break;
170         }
171 #endif
172     }
173
174     // This will emit code to build the new frame over the old one.
175     void prepareForTailCall();
176
177     // This will emit code to build the new frame as if performing a
178     // regular call. However, the callee save registers will be
179     // restored, and any locals (not the header or arguments) of the
180     // current frame can be overwritten.
181     //
182     // A frame built using prepareForSlowPath() should be used either
183     // to throw an exception in, or destroyed using
184     // CCallHelpers::prepareForTailCallSlow() followed by a tail call.
185     void prepareForSlowPath();
186
187 private:
188     static const bool verbose = false;
189
190     CCallHelpers& m_jit;
191
192     void prepareAny();
193
194     void spill(CachedRecovery&);
195
196     // "box" is arguably a bad name here. The meaning is that after
197     // calling emitBox(), your ensure that subsequently calling
198     // emitStore() will be able to store the value without additional
199     // transformation. In particular, this is a no-op for constants,
200     // and is a complete no-op on 32bits since any unboxed value can
201     // still be stored by storing the payload and a statically known
202     // tag.
203     void emitBox(CachedRecovery&);
204
205     bool canBox(CachedRecovery& cachedRecovery)
206     {
207         if (cachedRecovery.boxingRequiresGPR() && getFreeGPR() == InvalidGPRReg)
208             return false;
209
210         if (cachedRecovery.boxingRequiresFPR() && getFreeFPR() == InvalidFPRReg)
211             return false;
212
213         return true;
214     }
215
216     void ensureBox(CachedRecovery& cachedRecovery)
217     {
218         if (canBox(cachedRecovery))
219             return;
220
221         if (cachedRecovery.boxingRequiresGPR())
222             ensureGPR();
223
224         if (cachedRecovery.boxingRequiresFPR())
225             ensureFPR();
226     }
227
228     void emitLoad(CachedRecovery&);
229
230     bool canLoad(CachedRecovery&);
231
232     void ensureLoad(CachedRecovery& cachedRecovery)
233     {
234         if (canLoad(cachedRecovery))
235             return;
236
237         ASSERT(cachedRecovery.loadsIntoGPR() || cachedRecovery.loadsIntoFPR());
238
239         if (cachedRecovery.loadsIntoFPR()) {
240             if (cachedRecovery.loadsIntoGPR())
241                 ensureRegister();
242             else
243                 ensureFPR();
244         } else
245             ensureGPR();
246     }
247
248     bool canLoadAndBox(CachedRecovery& cachedRecovery)
249     {
250         // We don't have interfering loads & boxes
251         ASSERT(!cachedRecovery.loadsIntoFPR() || !cachedRecovery.boxingRequiresFPR());
252         ASSERT(!cachedRecovery.loadsIntoGPR() || !cachedRecovery.boxingRequiresGPR());
253
254         return canLoad(cachedRecovery) && canBox(cachedRecovery);
255     }
256
257     DataFormat emitStore(CachedRecovery&, MacroAssembler::Address);
258
259     void emitDisplace(CachedRecovery&);
260
261     void emitDeltaCheck();
262
263     Bag<CachedRecovery> m_cachedRecoveries;
264
265     void updateRecovery(CachedRecovery& cachedRecovery, ValueRecovery recovery)
266     {
267         clearCachedRecovery(cachedRecovery.recovery());
268         cachedRecovery.setRecovery(recovery);
269         setCachedRecovery(recovery, &cachedRecovery);
270     }
271
272     CachedRecovery* getCachedRecovery(ValueRecovery);
273
274     CachedRecovery* setCachedRecovery(ValueRecovery, CachedRecovery*);
275
276     void clearCachedRecovery(ValueRecovery recovery)
277     {
278         if (!recovery.isConstant())
279             setCachedRecovery(recovery, nullptr);
280     }
281
282     CachedRecovery* addCachedRecovery(ValueRecovery recovery)
283     {
284         if (recovery.isConstant())
285             return m_cachedRecoveries.add(recovery);
286         CachedRecovery* cachedRecovery = getCachedRecovery(recovery);
287         if (!cachedRecovery)
288             return setCachedRecovery(recovery, m_cachedRecoveries.add(recovery));
289         return cachedRecovery;
290     }
291
292     // This is the current recoveries present in the old frame's
293     // slots. A null CachedRecovery means we can trash the current
294     // value as we don't care about it.
295     Vector<CachedRecovery*> m_oldFrame;
296
297     int numLocals() const
298     {
299         return m_oldFrame.size() - CallerFrameAndPC::sizeInRegisters;
300     }
301
302     CachedRecovery* getOld(VirtualRegister reg) const
303     {
304         return m_oldFrame[CallerFrameAndPC::sizeInRegisters - reg.offset() - 1];
305     }
306
307     void setOld(VirtualRegister reg, CachedRecovery* cachedRecovery)
308     {
309         m_oldFrame[CallerFrameAndPC::sizeInRegisters - reg.offset() - 1] = cachedRecovery;
310     }
311
312     VirtualRegister firstOld() const
313     {
314         return VirtualRegister { static_cast<int>(-numLocals()) };
315     }
316
317     VirtualRegister lastOld() const
318     {
319         return VirtualRegister { CallerFrameAndPC::sizeInRegisters - 1 };
320     }
321
322     bool isValidOld(VirtualRegister reg) const
323     {
324         return reg >= firstOld() && reg <= lastOld();
325     }
326
327     bool m_didExtendFrame { false };
328
329     void extendFrameIfNeeded();
330
331     // This stores, for each slot in the new frame, information about
332     // the recovery for the value that should eventually go into that
333     // slot.
334     //
335     // Once the slot has been written, the corresponding entry in
336     // m_newFrame will be empty.
337     Vector<CachedRecovery*> m_newFrame;
338
339     size_t argCount() const
340     {
341         return m_newFrame.size() - CallFrame::headerSizeInRegisters;
342     }
343
344     CachedRecovery* getNew(VirtualRegister newRegister) const
345     {
346         return m_newFrame[newRegister.offset()];
347     }
348
349     void setNew(VirtualRegister newRegister, CachedRecovery* cachedRecovery)
350     {
351         m_newFrame[newRegister.offset()] = cachedRecovery;
352     }
353
354     void addNew(VirtualRegister newRegister, ValueRecovery recovery)
355     {
356         CachedRecovery* cachedRecovery = addCachedRecovery(recovery);
357         cachedRecovery->addTarget(newRegister);
358         setNew(newRegister, cachedRecovery);
359     }
360
361     VirtualRegister firstNew() const
362     {
363         return VirtualRegister { 0 };
364     }
365
366     VirtualRegister lastNew() const
367     {
368         return VirtualRegister { static_cast<int>(m_newFrame.size()) - 1 };
369     }
370
371     bool isValidNew(VirtualRegister reg) const
372     {
373         return reg >= firstNew() && reg <= lastNew();
374     }
375
376
377     int m_alignedOldFrameSize;
378     int m_alignedNewFrameSize;
379
380     // This is the distance, in slots, between the base of the new
381     // frame and the base of the old frame. It could be negative when
382     // preparing for a tail call to a function with smaller argument
383     // count.
384     //
385     // We will overwrite this appropriately for slow path calls, but
386     // we initialize it as if doing a fast path for the spills we
387     // could do while undecided (typically while calling acquireGPR()
388     // for a polymorphic call).
389     int m_frameDelta;
390
391     VirtualRegister newAsOld(VirtualRegister reg) const
392     {
393         return reg - m_frameDelta;
394     }
395
396     // This stores the set of locked registers, i.e. registers for
397     // which we have an implicit requirement that they are not changed.
398     //
399     // This will usually contains the link register on architectures
400     // that have one, any scratch register used by the macro assembler
401     // (e.g. r11 on X86_64), as well as any register that we use for
402     // addressing (see m_oldFrameBase and m_newFrameBase).
403     //
404     // We also use this to lock registers temporarily, for instance to
405     // ensure that we have at least 2 available registers for loading
406     // a pair on 32bits.
407     mutable RegisterSet m_lockedRegisters;
408
409     // This stores the current recoveries present in registers. A null
410     // CachedRecovery means we can trash the current value as we don't
411     // care about it. 
412     RegisterMap<CachedRecovery*> m_registers;
413
414 #if USE(JSVALUE64)
415     mutable GPRReg m_tagTypeNumber;
416
417     bool tryAcquireTagTypeNumber();
418 #endif
419
420     // This stores, for each register, information about the recovery
421     // for the value that should eventually go into that register. The
422     // only registers that have a target recovery will be callee-save
423     // registers, as well as possibly one JSValueRegs for holding the
424     // callee.
425     //
426     // Once the correct value has been put into the registers, and
427     // contrary to what we do with m_newFrame, we keep the entry in
428     // m_newRegisters to simplify spilling.
429     RegisterMap<CachedRecovery*> m_newRegisters;
430
431     template<typename CheckFunctor>
432     Reg getFreeRegister(const CheckFunctor& check) const
433     {
434         Reg nonTemp { };
435         for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
436             if (m_lockedRegisters.get(reg))
437                 continue;
438
439             if (!check(reg))
440                 continue;
441
442             if (!m_registers[reg]) {
443                 if (!m_newRegisters[reg])
444                     return reg;
445                 if (!nonTemp)
446                     nonTemp = reg;
447             }
448         }
449
450 #if USE(JSVALUE64)
451         if (!nonTemp && m_tagTypeNumber != InvalidGPRReg && check(Reg { m_tagTypeNumber })) {
452             ASSERT(m_lockedRegisters.get(m_tagTypeNumber));
453             m_lockedRegisters.clear(m_tagTypeNumber);
454             nonTemp = Reg { m_tagTypeNumber };
455             m_tagTypeNumber = InvalidGPRReg;
456         }
457 #endif
458         return nonTemp;
459     }
460
461     GPRReg getFreeTempGPR() const
462     {
463         Reg freeTempGPR { getFreeRegister([this] (Reg reg) { return reg.isGPR() && !m_newRegisters[reg]; }) };
464         if (!freeTempGPR)
465             return InvalidGPRReg;
466         return freeTempGPR.gpr();
467     }
468
469     GPRReg getFreeGPR() const
470     {
471         Reg freeGPR { getFreeRegister([] (Reg reg) { return reg.isGPR(); }) };
472         if (!freeGPR)
473             return InvalidGPRReg;
474         return freeGPR.gpr();
475     }
476
477     FPRReg getFreeFPR() const
478     {
479         Reg freeFPR { getFreeRegister([] (Reg reg) { return reg.isFPR(); }) };
480         if (!freeFPR)
481             return InvalidFPRReg;
482         return freeFPR.fpr();
483     }
484
485     bool hasFreeRegister() const
486     {
487         return static_cast<bool>(getFreeRegister([] (Reg) { return true; }));
488     }
489
490     // This frees up a register satisfying the check functor (this
491     // functor could theoretically have any kind of logic, but it must
492     // ensure that it will only return true for registers - spill
493     // assumes and asserts that it is passed a cachedRecovery stored in a
494     // register).
495     template<typename CheckFunctor>
496     void ensureRegister(const CheckFunctor& check)
497     {
498         // If we can spill a callee-save, that's best, because it will
499         // free up a register that would otherwise been taken for the
500         // longest amount of time.
501         //
502         // We could try to bias towards those that are not in their
503         // target registers yet, but the gain is probably super
504         // small. Unless you have a huge number of argument (at least
505         // around twice the number of available registers on your
506         // architecture), no spilling is going to take place anyways.
507         for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
508             if (m_lockedRegisters.get(reg))
509                 continue;
510
511             CachedRecovery* cachedRecovery { m_newRegisters[reg] };
512             if (!cachedRecovery)
513                 continue;
514
515             if (check(*cachedRecovery)) {
516                 if (verbose)
517                     dataLog("  ", cachedRecovery->recovery(), " looks like a good spill candidate\n");
518                 spill(*cachedRecovery);
519                 return;
520             }
521         }
522
523         // We use the cachedRecovery associated with the first new slot we
524         // can, because that is the one for which a write will be
525         // possible the latest, i.e. that is the one that we would
526         // have had to retain in registers for the longest.
527         for (VirtualRegister reg = firstNew(); reg <= lastNew(); reg += 1) {
528             CachedRecovery* cachedRecovery { getNew(reg) };
529             if (!cachedRecovery)
530                 continue;
531
532             if (check(*cachedRecovery)) {
533                 spill(*cachedRecovery);
534                 return;
535             }
536         }
537
538         RELEASE_ASSERT_NOT_REACHED();
539     }
540
541     void ensureRegister()
542     {
543         if (hasFreeRegister())
544             return;
545
546         if (verbose)
547             dataLog("  Finding a register to spill\n");
548         ensureRegister(
549             [this] (const CachedRecovery& cachedRecovery) {
550                 if (cachedRecovery.recovery().isInGPR())
551                     return !m_lockedRegisters.get(cachedRecovery.recovery().gpr());
552                 if (cachedRecovery.recovery().isInFPR())
553                     return !m_lockedRegisters.get(cachedRecovery.recovery().fpr());
554 #if USE(JSVALUE32_64)
555                 if (cachedRecovery.recovery().technique() == InPair) {
556                     return !m_lockedRegisters.get(cachedRecovery.recovery().tagGPR())
557                         && !m_lockedRegisters.get(cachedRecovery.recovery().payloadGPR());
558                 }
559 #endif
560                 return false;
561             });
562     }
563
564     void ensureTempGPR()
565     {
566         if (getFreeTempGPR() != InvalidGPRReg)
567             return;
568
569         if (verbose)
570             dataLog("  Finding a temp GPR to spill\n");
571         ensureRegister(
572             [this] (const CachedRecovery& cachedRecovery) {
573                 if (cachedRecovery.recovery().isInGPR()) {
574                     return !m_lockedRegisters.get(cachedRecovery.recovery().gpr()) 
575                         && !m_newRegisters[cachedRecovery.recovery().gpr()];
576                 }
577 #if USE(JSVALUE32_64)
578                 if (cachedRecovery.recovery().technique() == InPair) {
579                     return !m_lockedRegisters.get(cachedRecovery.recovery().tagGPR())
580                         && !m_lockedRegisters.get(cachedRecovery.recovery().payloadGPR())
581                         && !m_newRegisters[cachedRecovery.recovery().tagGPR()]
582                         && !m_newRegisters[cachedRecovery.recovery().payloadGPR()];
583                 }
584 #endif
585                 return false;
586             });
587     }
588
589     void ensureGPR()
590     {
591         if (getFreeGPR() != InvalidGPRReg)
592             return;
593
594         if (verbose)
595             dataLog("  Finding a GPR to spill\n");
596         ensureRegister(
597             [this] (const CachedRecovery& cachedRecovery) {
598                 if (cachedRecovery.recovery().isInGPR())
599                     return !m_lockedRegisters.get(cachedRecovery.recovery().gpr());
600 #if USE(JSVALUE32_64)
601                 if (cachedRecovery.recovery().technique() == InPair) {
602                     return !m_lockedRegisters.get(cachedRecovery.recovery().tagGPR())
603                         && !m_lockedRegisters.get(cachedRecovery.recovery().payloadGPR());
604                 }
605 #endif
606                 return false;
607             });
608     }
609
610     void ensureFPR()
611     {
612         if (getFreeFPR() != InvalidFPRReg)
613             return;
614
615         if (verbose)
616             dataLog("  Finding an FPR to spill\n");
617         ensureRegister(
618             [this] (const CachedRecovery& cachedRecovery) {
619                 if (cachedRecovery.recovery().isInFPR())
620                     return !m_lockedRegisters.get(cachedRecovery.recovery().fpr());
621                 return false;
622             });
623     }
624
625     CachedRecovery* getNew(JSValueRegs jsValueRegs) const
626     {
627 #if USE(JSVALUE64)
628         return m_newRegisters[jsValueRegs.gpr()];
629 #else
630         ASSERT(
631             jsValueRegs.tagGPR() == InvalidGPRReg || jsValueRegs.payloadGPR() == InvalidGPRReg
632             || m_newRegisters[jsValueRegs.payloadGPR()] == m_newRegisters[jsValueRegs.tagGPR()]);
633         if (jsValueRegs.payloadGPR() == InvalidGPRReg)
634             return m_newRegisters[jsValueRegs.tagGPR()];
635         return m_newRegisters[jsValueRegs.payloadGPR()];
636 #endif
637     }
638
639     void addNew(JSValueRegs jsValueRegs, ValueRecovery recovery)
640     {
641         ASSERT(jsValueRegs && !getNew(jsValueRegs));
642         CachedRecovery* cachedRecovery = addCachedRecovery(recovery);
643 #if USE(JSVALUE64)
644         if (cachedRecovery->wantedJSValueRegs())
645             m_newRegisters[cachedRecovery->wantedJSValueRegs().gpr()] = nullptr;
646         m_newRegisters[jsValueRegs.gpr()] = cachedRecovery;
647 #else
648         if (JSValueRegs oldRegs { cachedRecovery->wantedJSValueRegs() }) {
649             if (oldRegs.payloadGPR())
650                 m_newRegisters[oldRegs.payloadGPR()] = nullptr;
651             if (oldRegs.tagGPR())
652                 m_newRegisters[oldRegs.tagGPR()] = nullptr;
653         }
654         if (jsValueRegs.payloadGPR() != InvalidGPRReg)
655             m_newRegisters[jsValueRegs.payloadGPR()] = cachedRecovery;
656         if (jsValueRegs.tagGPR() != InvalidGPRReg)
657             m_newRegisters[jsValueRegs.tagGPR()] = cachedRecovery;
658 #endif
659         ASSERT(!cachedRecovery->wantedJSValueRegs());
660         cachedRecovery->setWantedJSValueRegs(jsValueRegs);
661     }
662
663     void addNew(FPRReg fpr, ValueRecovery recovery)
664     {
665         ASSERT(fpr != InvalidFPRReg && !m_newRegisters[fpr]);
666         CachedRecovery* cachedRecovery = addCachedRecovery(recovery);
667         m_newRegisters[fpr] = cachedRecovery;
668         ASSERT(cachedRecovery->wantedFPR() == InvalidFPRReg);
669         cachedRecovery->setWantedFPR(fpr);
670     }
671
672     // m_oldFrameBase is the register relative to which we access
673     // slots in the old call frame, with an additional offset of
674     // m_oldFrameOffset.
675     //
676     //  - For an actual tail call, m_oldFrameBase is the stack
677     //    pointer, and m_oldFrameOffset is the number of locals of the
678     //    tail caller's frame. We use such stack pointer-based
679     //    addressing because it allows us to load the tail caller's
680     //    caller's frame pointer in the frame pointer register
681     //    immediately instead of awkwardly keeping it around on the
682     //    stack.
683     //
684     //  - For a slow path call, m_oldFrameBase is just the frame
685     //    pointer, and m_oldFrameOffset is 0.
686     GPRReg m_oldFrameBase { MacroAssembler::framePointerRegister };
687     int m_oldFrameOffset { 0 };
688
689     MacroAssembler::Address addressForOld(VirtualRegister reg) const
690     {
691         return MacroAssembler::Address(m_oldFrameBase,
692             (m_oldFrameOffset + reg.offset()) * sizeof(Register));
693     }
694
695     // m_newFrameBase is the register relative to which we access
696     // slots in the new call frame, and we always make it point to
697     // wherever the stack pointer will be right before making the
698     // actual call/jump. The actual base of the new frame is at offset
699     // m_newFrameOffset relative to m_newFrameBase.
700     //
701     //  - For an actual tail call, m_newFrameBase is computed
702     //    dynamically, and m_newFrameOffset varies between 0 and -2
703     //    depending on the architecture's calling convention (see
704     //    prepareForTailCall).
705     //
706     //  - For a slow path call, m_newFrameBase is the actual stack
707     //    pointer, and m_newFrameOffset is - CallerFrameAndPCSize,
708     //    following the convention for a regular call.
709     GPRReg m_newFrameBase { InvalidGPRReg };
710     int m_newFrameOffset { 0};
711
712     bool isUndecided() const
713     {
714         return m_newFrameBase == InvalidGPRReg;
715     }
716
717     bool isSlowPath() const
718     {
719         return m_newFrameBase == MacroAssembler::stackPointerRegister;
720     }
721
722     MacroAssembler::Address addressForNew(VirtualRegister reg) const
723     {
724         return MacroAssembler::Address(m_newFrameBase,
725             (m_newFrameOffset + reg.offset()) * sizeof(Register));
726     }
727
728     // We use a concept of "danger zone". The danger zone consists of
729     // all the writes in the new frame that could overlap with reads
730     // in the old frame.
731     //
732     // Because we could have a higher actual number of arguments than
733     // parameters, when preparing a tail call, we need to assume that
734     // writing to a slot on the new frame could overlap not only with
735     // the corresponding slot in the old frame, but also with any slot
736     // above it. Thus, the danger zone consists of all writes between
737     // the first write and what I call the "danger frontier": the
738     // highest slot in the old frame we still care about. Thus, the
739     // danger zone contains all the slots between the first slot of
740     // the new frame and the danger frontier. Because the danger
741     // frontier is related to the new frame, it is stored as a virtual
742     // register *in the new frame*.
743     VirtualRegister m_dangerFrontier;
744
745     VirtualRegister dangerFrontier() const
746     {
747         ASSERT(!isUndecided());
748
749         return m_dangerFrontier;
750     }
751
752     bool isDangerNew(VirtualRegister reg) const
753     {
754         ASSERT(!isUndecided() && isValidNew(reg));
755         return reg <= dangerFrontier();
756     }
757
758     void updateDangerFrontier()
759     {
760         ASSERT(!isUndecided());
761
762         m_dangerFrontier = firstNew() - 1;
763         for (VirtualRegister reg = lastNew(); reg >= firstNew(); reg -= 1) {
764             if (!getNew(reg) || !isValidOld(newAsOld(reg)) || !getOld(newAsOld(reg)))
765                 continue;
766
767             m_dangerFrontier = reg;
768             if (verbose)
769                 dataLog("  Danger frontier now at NEW ", m_dangerFrontier, "\n");
770             break;
771         }
772         if (verbose)
773             dataLog("  All clear! Danger zone is empty.\n");
774     }
775
776     // A safe write is a write that never writes into the danger zone.
777     bool hasOnlySafeWrites(CachedRecovery& cachedRecovery) const
778     {
779         for (VirtualRegister target : cachedRecovery.targets()) {
780             if (isDangerNew(target))
781                 return false;
782         }
783         return true;
784     }
785
786     // You must ensure that there is no dangerous writes before
787     // calling this function.
788     bool tryWrites(CachedRecovery&);
789
790     // This function tries to ensure that there is no longer any
791     // possible safe write, i.e. all remaining writes are either to
792     // the danger zone or callee save restorations.
793     //
794     // It returns false if it was unable to perform some safe writes
795     // due to high register pressure.
796     bool performSafeWrites();
797     
798     unsigned m_numPassedArgs { UINT_MAX };
799 };
800
801 } // namespace JSC
802
803 #endif // ENABLE(JIT)