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