source: webkit/trunk/Source/JavaScriptCore/jit/CallFrameShuffler.cpp

Last change on this file was 292014, checked in by [email protected], 3 years ago

[JSC] Clean up some 32bit load/store with 64bit load/store
https://bugs.webkit.org/show_bug.cgi?id=238440

Reviewed by Mark Lam.

  1. On OSR entry, we should copy values from scratch to stack via loadValue / storeValue instead of 32bit load/store.
  2. We should initialize tail-call's argumentCountIncludingThis slot via store64.
  • dfg/DFGThunks.cpp:

(JSC::DFG::osrEntryThunkGenerator):

  • jit/CallFrameShuffler.cpp:

(JSC::CallFrameShuffler::prepareAny):

File size: 30.7 KB
Line 
1/*
2 * Copyright (C) 2015-2020 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 "CallFrameShuffler.h"
28
29#if ENABLE(JIT)
30
31#include "CachedRecovery.h"
32#include "CCallHelpers.h"
33#include "CodeBlock.h"
34
35namespace JSC {
36
37CallFrameShuffler::CallFrameShuffler(CCallHelpers& jit, const CallFrameShuffleData& data)
38 : m_jit(jit)
39 , m_oldFrame(data.numLocals + CallerFrameAndPC::sizeInRegisters, nullptr)
40 , m_newFrame(data.args.size() + CallFrame::headerSizeInRegisters, nullptr)
41 , m_alignedOldFrameSize(CallFrame::headerSizeInRegisters + roundArgumentCountToAlignFrame(data.numParameters))
42 , m_alignedNewFrameSize(CallFrame::headerSizeInRegisters
43 + roundArgumentCountToAlignFrame(data.args.size()))
44 , m_frameDelta(m_alignedNewFrameSize - m_alignedOldFrameSize)
45 , m_lockedRegisters(RegisterSet::allRegisters())
46 , m_numPassedArgs(data.numPassedArgs)
47 , m_numParameters(data.numParameters)
48{
49 // We are allowed all the usual registers...
50 for (unsigned i = GPRInfo::numberOfRegisters; i--; )
51 m_lockedRegisters.clear(GPRInfo::toRegister(i));
52 for (unsigned i = FPRInfo::numberOfRegisters; i--; )
53 m_lockedRegisters.clear(FPRInfo::toRegister(i));
54
55 // ... as well as the callee saved registers
56 m_lockedRegisters.exclude(RegisterSet::vmCalleeSaveRegisters());
57
58 ASSERT(!data.callee.isInJSStack() || data.callee.virtualRegister().isLocal());
59 addNew(CallFrameSlot::callee, data.callee);
60
61 for (size_t i = 0; i < data.args.size(); ++i) {
62 ASSERT(!data.args[i].isInJSStack() || data.args[i].virtualRegister().isLocal());
63 addNew(virtualRegisterForArgumentIncludingThis(i), data.args[i]);
64 }
65
66 for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
67 if (!data.registers[reg].isSet())
68 continue;
69
70 if (reg.isGPR()) {
71#if USE(JSVALUE64)
72 addNew(JSValueRegs(reg.gpr()), data.registers[reg]);
73#elif USE(JSVALUE32_64)
74 addNew(reg.gpr(), data.registers[reg]);
75#endif
76 } else
77 addNew(reg.fpr(), data.registers[reg]);
78 }
79
80#if USE(JSVALUE64)
81 m_numberTagRegister = data.numberTagRegister;
82 if (m_numberTagRegister != InvalidGPRReg)
83 lockGPR(m_numberTagRegister);
84#endif
85}
86
87void CallFrameShuffler::dump(PrintStream& out) const
88{
89 static const char* delimiter = " +-------------------------------+ ";
90 static const char* dangerDelimiter = " X-------------------------------X ";
91 static const char* dangerBoundsDelimiter = " XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ";
92 static const char* emptySpace = " ";
93 out.print(" ");
94 out.print(" Old frame ");
95 out.print(" New frame ");
96 out.print("\n");
97 int totalSize = m_alignedOldFrameSize + std::max(numLocals(), m_alignedNewFrameSize) + 3;
98 for (int i = 0; i < totalSize; ++i) {
99 VirtualRegister old { m_alignedOldFrameSize - i - 1 };
100 VirtualRegister newReg { old + m_frameDelta };
101
102 if (!isValidOld(old) && old != firstOld() - 1
103 && !isValidNew(newReg) && newReg != firstNew() - 1)
104 continue;
105
106 out.print(" ");
107 if (dangerFrontier() >= firstNew()
108 && (newReg == dangerFrontier() || newReg == firstNew() - 1))
109 out.print(dangerBoundsDelimiter);
110 else if (isValidOld(old))
111 out.print(isValidNew(newReg) && isDangerNew(newReg) ? dangerDelimiter : delimiter);
112 else if (old == firstOld() - 1)
113 out.print(delimiter);
114 else
115 out.print(emptySpace);
116 if (dangerFrontier() >= firstNew()
117 && (newReg == dangerFrontier() || newReg == firstNew() - 1))
118 out.print(dangerBoundsDelimiter);
119 else if (isValidNew(newReg) || newReg == firstNew() - 1)
120 out.print(isDangerNew(newReg) ? dangerDelimiter : delimiter);
121 else
122 out.print(emptySpace);
123 out.print("\n");
124 if (old == firstOld())
125 out.print(" sp --> ");
126 else if (!old.offset())
127 out.print(" fp --> ");
128 else
129 out.print(" ");
130 if (isValidOld(old)) {
131 if (getOld(old)) {
132 auto str = toCString(old);
133 if (isValidNew(newReg) && isDangerNew(newReg))
134 out.printf(" X %18s X ", str.data());
135 else
136 out.printf(" | %18s | ", str.data());
137 } else if (isValidNew(newReg) && isDangerNew(newReg))
138 out.printf(" X%30s X ", "");
139 else
140 out.printf(" |%30s | ", "");
141 } else
142 out.print(emptySpace);
143 if (isValidNew(newReg)) {
144 const char d = isDangerNew(newReg) ? 'X' : '|';
145 auto str = toCString(newReg);
146 if (getNew(newReg)) {
147 if (getNew(newReg)->recovery().isConstant())
148 out.printf(" %c%8s <- constant %c ", d, str.data(), d);
149 else {
150 auto recoveryStr = toCString(getNew(newReg)->recovery());
151 out.printf(" %c%8s <- %18s %c ", d, str.data(),
152 recoveryStr.data(), d);
153 }
154 } else if (newReg == VirtualRegister { CallFrameSlot::argumentCountIncludingThis })
155 out.printf(" %c%8s <- %18zu %c ", d, str.data(), argCount(), d);
156 else
157 out.printf(" %c%30s %c ", d, "", d);
158 } else
159 out.print(emptySpace);
160 if (newReg == firstNew() - m_newFrameOffset && !isSlowPath())
161 out.print(" <-- new sp before jump (current ", m_newFrameBase, ") ");
162 if (newReg == firstNew())
163 out.print(" <-- new fp after prologue");
164 out.print("\n");
165 }
166 out.print(" ");
167 out.print(" Live registers ");
168 out.print(" Wanted registers ");
169 out.print("\n");
170 for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
171 CachedRecovery* oldCachedRecovery { m_registers[reg] };
172 CachedRecovery* newCachedRecovery { m_newRegisters[reg] };
173 if (!oldCachedRecovery && !newCachedRecovery)
174 continue;
175 out.print(" ");
176 if (oldCachedRecovery) {
177 auto str = toCString(reg);
178 out.printf(" %8s ", str.data());
179 } else
180 out.print(emptySpace);
181#if USE(JSVALUE32_64)
182 if (newCachedRecovery) {
183 JSValueRegs wantedJSValueRegs { newCachedRecovery->wantedJSValueRegs() };
184 if (reg.isFPR())
185 out.print(reg, " <- ", newCachedRecovery->recovery());
186 else {
187 if (reg.gpr() == wantedJSValueRegs.tagGPR())
188 out.print(reg.gpr(), " <- tag(", newCachedRecovery->recovery(), ")");
189 else
190 out.print(reg.gpr(), " <- payload(", newCachedRecovery->recovery(), ")");
191 }
192 }
193#else
194 if (newCachedRecovery)
195 out.print(" ", reg, " <- ", newCachedRecovery->recovery());
196#endif
197 out.print("\n");
198 }
199 out.print(" Locked registers: ");
200 bool firstLocked { true };
201 for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
202 if (m_lockedRegisters.get(reg)) {
203 out.print(firstLocked ? "" : ", ", reg);
204 firstLocked = false;
205 }
206 }
207 out.print("\n");
208
209 if (isSlowPath())
210 out.print(" Using fp-relative addressing for slow path call\n");
211 else
212 out.print(" Using sp-relative addressing for jump (using ", m_newFrameBase, " as new sp)\n");
213 if (m_oldFrameOffset)
214 out.print(" Old frame offset is ", m_oldFrameOffset, "\n");
215 if (m_newFrameOffset)
216 out.print(" New frame offset is ", m_newFrameOffset, "\n");
217#if USE(JSVALUE64)
218 if (m_numberTagRegister != InvalidGPRReg)
219 out.print(" NumberTag is currently in ", m_numberTagRegister, "\n");
220#endif
221}
222
223CachedRecovery* CallFrameShuffler::getCachedRecovery(ValueRecovery recovery)
224{
225 ASSERT(!recovery.isConstant());
226 if (recovery.isInGPR())
227 return m_registers[recovery.gpr()];
228 if (recovery.isInFPR())
229 return m_registers[recovery.fpr()];
230#if USE(JSVALUE32_64)
231 if (recovery.technique() == InPair) {
232 ASSERT(m_registers[recovery.tagGPR()] == m_registers[recovery.payloadGPR()]);
233 return m_registers[recovery.payloadGPR()];
234 }
235#endif
236 ASSERT(recovery.isInJSStack());
237 return getOld(recovery.virtualRegister());
238}
239
240CachedRecovery* CallFrameShuffler::setCachedRecovery(ValueRecovery recovery, CachedRecovery* cachedRecovery)
241{
242 ASSERT(!recovery.isConstant());
243 if (recovery.isInGPR())
244 return m_registers[recovery.gpr()] = cachedRecovery;
245 if (recovery.isInFPR())
246 return m_registers[recovery.fpr()] = cachedRecovery;
247#if USE(JSVALUE32_64)
248 if (recovery.technique() == InPair) {
249 m_registers[recovery.tagGPR()] = cachedRecovery;
250 return m_registers[recovery.payloadGPR()] = cachedRecovery;
251 }
252#endif
253 ASSERT(recovery.isInJSStack());
254 setOld(recovery.virtualRegister(), cachedRecovery);
255 return cachedRecovery;
256}
257
258void CallFrameShuffler::spill(CachedRecovery& cachedRecovery)
259{
260 ASSERT(!isSlowPath());
261 ASSERT(cachedRecovery.recovery().isInRegisters());
262
263 VirtualRegister spillSlot { 0 };
264 for (VirtualRegister slot = firstOld(); slot <= lastOld(); slot += 1) {
265 if (slot >= newAsOld(firstNew()))
266 break;
267
268 if (getOld(slot))
269 continue;
270
271 spillSlot = slot;
272 break;
273 }
274 // We must have enough slots to be able to fit the whole callee's
275 // frame for the slow path - unless we are in the FTL. In that
276 // case, we are allowed to extend the frame *once*, since we are
277 // guaranteed to have enough available space for that.
278 if (spillSlot >= newAsOld(firstNew()) || !spillSlot.isLocal()) {
279 RELEASE_ASSERT(!m_didExtendFrame);
280 extendFrameIfNeeded();
281 spill(cachedRecovery);
282 return;
283 }
284
285 if (verbose)
286 dataLog(" * Spilling ", cachedRecovery.recovery(), " into ", spillSlot, "\n");
287 auto format = emitStore(cachedRecovery, addressForOld(spillSlot));
288 ASSERT(format != DataFormatNone);
289 updateRecovery(cachedRecovery, ValueRecovery::displacedInJSStack(spillSlot, format));
290}
291
292void CallFrameShuffler::emitDeltaCheck()
293{
294 if (!ASSERT_ENABLED)
295 return;
296
297 GPRReg scratchGPR { getFreeGPR() };
298 if (scratchGPR != InvalidGPRReg) {
299 if (verbose)
300 dataLog(" Using ", scratchGPR, " for the fp-sp delta check\n");
301 m_jit.move(MacroAssembler::stackPointerRegister, scratchGPR);
302 m_jit.subPtr(GPRInfo::callFrameRegister, scratchGPR);
303 MacroAssembler::Jump ok = m_jit.branch32(
304 MacroAssembler::Equal, scratchGPR,
305 MacroAssembler::TrustedImm32(-numLocals() * sizeof(Register)));
306 m_jit.abortWithReason(JITUnexpectedCallFrameSize);
307 ok.link(&m_jit);
308 } else if (verbose)
309 dataLog(" Skipping the fp-sp delta check since there is too much pressure");
310}
311
312void CallFrameShuffler::extendFrameIfNeeded()
313{
314 ASSERT(!m_didExtendFrame);
315
316 VirtualRegister firstRead { firstOld() };
317 for (; firstRead <= virtualRegisterForLocal(0); firstRead += 1) {
318 if (getOld(firstRead))
319 break;
320 }
321 size_t availableSize = static_cast<size_t>(firstRead.offset() - firstOld().offset());
322 size_t wantedSize = m_newFrame.size() + m_newFrameOffset;
323
324 if (availableSize < wantedSize) {
325 size_t delta = WTF::roundUpToMultipleOf(stackAlignmentRegisters(), wantedSize - availableSize);
326 m_oldFrame.grow(m_oldFrame.size() + delta);
327 for (size_t i = 0; i < delta; ++i)
328 m_oldFrame[m_oldFrame.size() - i - 1] = nullptr;
329 m_jit.subPtr(MacroAssembler::TrustedImm32(delta * sizeof(Register)), MacroAssembler::stackPointerRegister);
330
331 if (isSlowPath())
332 m_frameDelta = numLocals() + CallerFrameAndPC::sizeInRegisters;
333 else
334 m_oldFrameOffset = numLocals();
335
336 if (verbose)
337 dataLogF(" Not enough space - extending the old frame %zu slot\n", delta);
338 }
339
340 m_didExtendFrame = true;
341}
342
343void CallFrameShuffler::prepareForSlowPath()
344{
345 ASSERT(isUndecided());
346 emitDeltaCheck();
347
348 m_frameDelta = numLocals() + CallerFrameAndPC::sizeInRegisters;
349 m_newFrameBase = MacroAssembler::stackPointerRegister;
350 m_newFrameOffset = -CallerFrameAndPC::sizeInRegisters;
351
352 if (verbose)
353 dataLog("\n\nPreparing frame for slow path call:\n");
354
355 // When coming from the FTL, we need to extend the frame. In other
356 // cases, we may end up extending the frame if we previously
357 // spilled things (e.g. in polymorphic cache).
358 extendFrameIfNeeded();
359
360 if (verbose)
361 dataLog(*this);
362
363 prepareAny();
364
365 if (verbose)
366 dataLog("Ready for slow path call!\n");
367}
368
369void CallFrameShuffler::prepareForTailCall()
370{
371 ASSERT(isUndecided());
372 emitDeltaCheck();
373
374 // We'll use sp-based indexing so that we can load the
375 // caller's frame pointer into the fpr immediately
376 m_oldFrameBase = MacroAssembler::stackPointerRegister;
377 m_oldFrameOffset = numLocals();
378 m_newFrameBase = acquireGPR();
379#if CPU(ARM_THUMB2) || CPU(MIPS)
380 // We load the frame pointer and link register
381 // manually. We could ask the algorithm to load them for us,
382 // and it would allow us to use the link register as an extra
383 // temporary - but it'd mean that the frame pointer can also
384 // be used as an extra temporary, so we keep the link register
385 // locked instead.
386
387 // sp will point to head1 since the callee's prologue pushes
388 // the call frame and link register.
389 m_newFrameOffset = -1;
390#elif CPU(ARM64) || CPU(RISCV64)
391 // We load the frame pointer and link register manually. We
392 // could ask the algorithm to load the link register for us
393 // (which would allow for its use as an extra temporary), but
394 // since its not in GPRInfo, we can't do it.
395
396 // sp will point to head2 since the callee's prologue pushes the
397 // call frame and link register
398 m_newFrameOffset = -2;
399#elif CPU(X86_64)
400 // We load the frame pointer manually, but we ask the
401 // algorithm to move the return PC for us (it'd probably
402 // require a write in the danger zone)
403 addNew(VirtualRegister { 1 },
404 ValueRecovery::displacedInJSStack(VirtualRegister(1), DataFormatJS));
405
406 // sp will point to head1 since the callee's prologue pushes
407 // the call frame register
408 m_newFrameOffset = -1;
409#else
410 UNREACHABLE_FOR_PLATFORM();
411#endif
412
413 if (verbose)
414 dataLog(" Emitting code for computing the new frame base\n");
415
416 // We compute the new frame base by first computing the top of the
417 // old frame (taking into account an argument count higher than
418 // the number of parameters), then substracting to it the aligned
419 // new frame size (adjusted).
420 m_jit.load32(MacroAssembler::Address(GPRInfo::callFrameRegister, CallFrameSlot::argumentCountIncludingThis * static_cast<int>(sizeof(Register)) + PayloadOffset), m_newFrameBase);
421 MacroAssembler::Jump argumentCountOK =
422 m_jit.branch32(MacroAssembler::BelowOrEqual, m_newFrameBase,
423 MacroAssembler::TrustedImm32(m_numParameters));
424 m_jit.add32(MacroAssembler::TrustedImm32(stackAlignmentRegisters() - 1 + CallFrame::headerSizeInRegisters), m_newFrameBase);
425 m_jit.and32(MacroAssembler::TrustedImm32(-stackAlignmentRegisters()), m_newFrameBase);
426 m_jit.mul32(MacroAssembler::TrustedImm32(sizeof(Register)), m_newFrameBase, m_newFrameBase);
427 MacroAssembler::Jump done = m_jit.jump();
428 argumentCountOK.link(&m_jit);
429 m_jit.move(
430 MacroAssembler::TrustedImm32(m_alignedOldFrameSize * sizeof(Register)),
431 m_newFrameBase);
432 done.link(&m_jit);
433
434 m_jit.addPtr(GPRInfo::callFrameRegister, m_newFrameBase);
435 m_jit.subPtr(
436 MacroAssembler::TrustedImm32(
437 (m_alignedNewFrameSize + m_newFrameOffset) * sizeof(Register)),
438 m_newFrameBase);
439
440 // We load the link register manually for architectures that have one
441#if CPU(ARM_THUMB2) || CPU(ARM64) || CPU(RISCV64)
442 m_jit.loadPtr(MacroAssembler::Address(MacroAssembler::framePointerRegister, CallFrame::returnPCOffset()),
443 MacroAssembler::linkRegister);
444#if CPU(ARM64E)
445 m_jit.addPtr(MacroAssembler::TrustedImm32(sizeof(CallerFrameAndPC)), MacroAssembler::framePointerRegister);
446 m_jit.untagPtr(MacroAssembler::framePointerRegister, MacroAssembler::linkRegister);
447 m_jit.subPtr(MacroAssembler::TrustedImm32(sizeof(CallerFrameAndPC)), MacroAssembler::framePointerRegister);
448 m_jit.validateUntaggedPtr(MacroAssembler::linkRegister);
449#endif
450
451#elif CPU(MIPS)
452 m_jit.loadPtr(MacroAssembler::Address(MacroAssembler::framePointerRegister, sizeof(void*)),
453 MacroAssembler::returnAddressRegister);
454#endif
455
456 // We want the frame pointer to always point to a valid frame, and
457 // we are going to trash the current one. Let's make it point to
458 // our caller's frame, since that's what we want to end up with.
459 m_jit.loadPtr(MacroAssembler::Address(MacroAssembler::framePointerRegister),
460 MacroAssembler::framePointerRegister);
461
462 if (verbose)
463 dataLog("Preparing frame for tail call:\n", *this);
464
465 prepareAny();
466
467 if (verbose)
468 dataLog("Ready for tail call!\n");
469}
470
471bool CallFrameShuffler::tryWrites(CachedRecovery& cachedRecovery)
472{
473 ASSERT(m_newFrameBase != InvalidGPRReg);
474
475 // If the value is already set up correctly, we don't have
476 // anything to do.
477 if (isSlowPath() && cachedRecovery.recovery().isInJSStack()
478 && cachedRecovery.targets().size() == 1
479 && newAsOld(cachedRecovery.targets()[0]) == cachedRecovery.recovery().virtualRegister()) {
480 cachedRecovery.clearTargets();
481 if (!cachedRecovery.wantedJSValueRegs() && cachedRecovery.wantedFPR() == InvalidFPRReg)
482 clearCachedRecovery(cachedRecovery.recovery());
483 return true;
484 }
485
486 if (!canLoadAndBox(cachedRecovery))
487 return false;
488
489 emitLoad(cachedRecovery);
490 emitBox(cachedRecovery);
491 ASSERT(cachedRecovery.recovery().isInRegisters()
492 || cachedRecovery.recovery().isConstant());
493
494 if (verbose)
495 dataLog(" * Storing ", cachedRecovery.recovery());
496 for (size_t i = 0; i < cachedRecovery.targets().size(); ++i) {
497 VirtualRegister target { cachedRecovery.targets()[i] };
498 ASSERT(!isDangerNew(target));
499 if (verbose)
500 dataLog(!i ? " into " : ", and ", "NEW ", target);
501 emitStore(cachedRecovery, addressForNew(target));
502 setNew(target, nullptr);
503 }
504 if (verbose)
505 dataLog("\n");
506 cachedRecovery.clearTargets();
507 if (!cachedRecovery.wantedJSValueRegs() && cachedRecovery.wantedFPR() == InvalidFPRReg)
508 clearCachedRecovery(cachedRecovery.recovery());
509
510 return true;
511}
512
513bool CallFrameShuffler::performSafeWrites()
514{
515 VirtualRegister firstSafe;
516 VirtualRegister end { lastNew() + 1 };
517 Vector<VirtualRegister> failures;
518
519 // For all cachedRecoveries that writes to the safe zone, if it
520 // doesn't also write to the danger zone, we try to perform
521 // the writes. This may free up danger slots, so we iterate
522 // again until it doesn't happen anymore.
523 //
524 // Note that even though we have a while block, we look at
525 // each slot of the new call frame at most once since in each
526 // iteration beyond the first, we only load up the portion of
527 // the new call frame that was dangerous and became safe due
528 // to the previous iteration.
529 do {
530 firstSafe = dangerFrontier() + 1;
531 if (verbose)
532 dataLog(" Trying safe writes (between NEW ", firstSafe, " and NEW ", end - 1, ")\n");
533 bool didProgress = false;
534 for (VirtualRegister reg = firstSafe; reg < end; reg += 1) {
535 CachedRecovery* cachedRecovery = getNew(reg);
536 if (!cachedRecovery) {
537 if (verbose)
538 dataLog(" + ", reg, " is OK.\n");
539 continue;
540 }
541 if (!hasOnlySafeWrites(*cachedRecovery)) {
542 if (verbose) {
543 dataLog(" - ", cachedRecovery->recovery(), " writes to NEW ", reg,
544 " but also has dangerous writes.\n");
545 }
546 continue;
547 }
548 if (cachedRecovery->wantedJSValueRegs()) {
549 if (verbose) {
550 dataLog(" - ", cachedRecovery->recovery(), " writes to NEW ", reg,
551 " but is also needed in registers.\n");
552 }
553 continue;
554 }
555 if (cachedRecovery->wantedFPR() != InvalidFPRReg) {
556 if (verbose) {
557 dataLog(" - ", cachedRecovery->recovery(), " writes to NEW ", reg,
558 " but is also needed in an FPR.\n");
559 }
560 continue;
561 }
562 if (!tryWrites(*cachedRecovery)) {
563 if (verbose)
564 dataLog(" - Unable to write to NEW ", reg, " from ", cachedRecovery->recovery(), "\n");
565 failures.append(reg);
566 }
567 didProgress = true;
568 }
569 end = firstSafe;
570
571 // If we have cachedRecoveries that failed to write, it is
572 // because they are on the stack and we didn't have enough
573 // registers available at the time to load them into. If
574 // we have a free register, we should try again because it
575 // could free up some danger slots.
576 if (didProgress && hasFreeRegister()) {
577 Vector<VirtualRegister> stillFailing;
578 for (VirtualRegister failed : failures) {
579 CachedRecovery* cachedRecovery = getNew(failed);
580 // It could have been handled later if it had
581 // several targets
582 if (!cachedRecovery)
583 continue;
584
585 ASSERT(hasOnlySafeWrites(*cachedRecovery)
586 && !cachedRecovery->wantedJSValueRegs()
587 && cachedRecovery->wantedFPR() == InvalidFPRReg);
588 if (!tryWrites(*cachedRecovery))
589 stillFailing.append(failed);
590 }
591 failures = WTFMove(stillFailing);
592 }
593 if (verbose && firstSafe != dangerFrontier() + 1)
594 dataLog(" We freed up danger slots!\n");
595 } while (firstSafe != dangerFrontier() + 1);
596
597 return failures.isEmpty();
598}
599
600void CallFrameShuffler::prepareAny()
601{
602 ASSERT(!isUndecided());
603
604 updateDangerFrontier();
605
606 // First, we try to store any value that goes above the danger
607 // frontier. This will never use more registers since we are only
608 // loading+storing if we ensure that any register used for the load
609 // will be freed up after the stores (i.e., all stores are above
610 // the danger frontier, and there is no wanted register).
611 performSafeWrites();
612
613 // At this point, we couldn't have more available registers than
614 // we have withouth spilling: all values currently in registers
615 // either require a write to the danger zone, or have a wanted
616 // register, which means that in any case they will have to go
617 // through registers again.
618
619 // We now slowly free up the danger zone by first loading the old
620 // value on the danger frontier, spilling as many registers as
621 // needed to do so and ensuring that the corresponding slot in the
622 // new frame is now ready to be written. Then, we store the old
623 // value to its target location if possible (we could have failed
624 // to load it previously due to high pressure). Finally, we write
625 // to any of the newly safe slots that we can, which could free up
626 // registers (hence why we do it eagerly).
627 for (VirtualRegister reg = dangerFrontier(); reg >= firstNew(); reg -= 1) {
628 if (reg == dangerFrontier()) {
629 if (verbose)
630 dataLog(" Next slot (NEW ", reg, ") is the danger frontier\n");
631 CachedRecovery* cachedRecovery { getOld(newAsOld(dangerFrontier())) };
632 ASSERT(cachedRecovery);
633 ensureLoad(*cachedRecovery);
634 emitLoad(*cachedRecovery);
635 ensureBox(*cachedRecovery);
636 emitBox(*cachedRecovery);
637 if (hasOnlySafeWrites(*cachedRecovery))
638 tryWrites(*cachedRecovery);
639 } else if (verbose)
640 dataLog(" Next slot is NEW ", reg, "\n");
641
642 ASSERT(!isDangerNew(reg));
643 CachedRecovery* cachedRecovery = getNew(reg);
644 // This could be one of the header slots we don't care about.
645 if (!cachedRecovery) {
646 if (verbose)
647 dataLog(" + ", reg, " is OK\n");
648 continue;
649 }
650
651 if (canLoadAndBox(*cachedRecovery) && hasOnlySafeWrites(*cachedRecovery)
652 && !cachedRecovery->wantedJSValueRegs()
653 && cachedRecovery->wantedFPR() == InvalidFPRReg) {
654 emitLoad(*cachedRecovery);
655 emitBox(*cachedRecovery);
656 bool writesOK = tryWrites(*cachedRecovery);
657 ASSERT_UNUSED(writesOK, writesOK);
658 } else if (verbose)
659 dataLog(" - ", cachedRecovery->recovery(), " can't be handled just yet.\n");
660 }
661 ASSERT(dangerFrontier() < firstNew());
662
663 // Now, the danger zone is empty, but we still have a couple of
664 // things to do:
665 //
666 // 1) There could be remaining safe writes that failed earlier due
667 // to high register pressure and had nothing to do with the
668 // danger zone whatsoever.
669 //
670 // 2) Some wanted registers could have to be loaded (this could
671 // happen either when making a call to a new function with a
672 // lower number of arguments - since above here, we only load
673 // wanted registers when they are at the danger frontier -, or
674 // if a wanted register got spilled).
675 //
676 // 3) Some wanted registers could have been loaded in the wrong
677 // registers
678 //
679 // 4) We have to take care of some bookkeeping - namely, storing
680 // the argument count and updating the stack pointer.
681
682 // At this point, we must have enough registers available for
683 // handling 1). None of the loads can fail because we have been
684 // eagerly freeing up registers in all the previous phases - so
685 // the only values that are in registers at this point must have
686 // wanted registers.
687 if (verbose)
688 dataLog(" Danger zone is clear, performing remaining writes.\n");
689 for (VirtualRegister reg = firstNew(); reg <= lastNew(); reg += 1) {
690 CachedRecovery* cachedRecovery { getNew(reg) };
691 if (!cachedRecovery)
692 continue;
693
694 emitLoad(*cachedRecovery);
695 emitBox(*cachedRecovery);
696 bool writesOK = tryWrites(*cachedRecovery);
697 ASSERT_UNUSED(writesOK, writesOK);
698 }
699
700#if USE(JSVALUE64)
701 if (m_numberTagRegister != InvalidGPRReg && m_newRegisters[m_numberTagRegister])
702 releaseGPR(m_numberTagRegister);
703#endif
704
705 // Handle 2) by loading all registers. We don't have to do any
706 // writes, since they have been taken care of above.
707 if (verbose)
708 dataLog(" Loading wanted registers into registers\n");
709 for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
710 CachedRecovery* cachedRecovery { m_newRegisters[reg] };
711 if (!cachedRecovery)
712 continue;
713
714 emitLoad(*cachedRecovery);
715 emitBox(*cachedRecovery);
716 ASSERT(cachedRecovery->targets().isEmpty());
717 }
718
719#if USE(JSVALUE64)
720 if (m_numberTagRegister != InvalidGPRReg)
721 releaseGPR(m_numberTagRegister);
722#endif
723
724 // At this point, we have read everything we cared about from the
725 // stack, and written everything we had to to the stack.
726 if (verbose)
727 dataLog(" Callee frame is fully set up\n");
728 if (ASSERT_ENABLED) {
729 for (VirtualRegister reg = firstNew(); reg <= lastNew(); reg += 1)
730 ASSERT_UNUSED(reg, !getNew(reg));
731
732 for (CachedRecovery* cachedRecovery : m_cachedRecoveries) {
733 ASSERT_UNUSED(cachedRecovery, cachedRecovery->targets().isEmpty());
734 ASSERT(!cachedRecovery->recovery().isInJSStack());
735 }
736 }
737
738 // We need to handle 4) first because it implies releasing
739 // m_newFrameBase, which could be a wanted register.
740 if (verbose)
741 dataLog(" * Storing the argument count into ", VirtualRegister { CallFrameSlot::argumentCountIncludingThis }, "\n");
742 RELEASE_ASSERT(m_numPassedArgs != UINT_MAX);
743#if USE(JSVALUE64)
744 // Initialize CallFrameSlot::argumentCountIncludingThis's TagOffset and PayloadOffset with 0 and m_numPassedArgs.
745 m_jit.store64(MacroAssembler::TrustedImm32(m_numPassedArgs), addressForNew(VirtualRegister { CallFrameSlot::argumentCountIncludingThis }));
746#else
747 m_jit.store32(MacroAssembler::TrustedImm32(0), addressForNew(VirtualRegister { CallFrameSlot::argumentCountIncludingThis }).withOffset(TagOffset));
748 m_jit.store32(MacroAssembler::TrustedImm32(m_numPassedArgs), addressForNew(VirtualRegister { CallFrameSlot::argumentCountIncludingThis }).withOffset(PayloadOffset));
749#endif
750
751 if (!isSlowPath()) {
752 ASSERT(m_newFrameBase != MacroAssembler::stackPointerRegister);
753 if (verbose)
754 dataLog(" Releasing the new frame base pointer\n");
755 m_jit.move(m_newFrameBase, MacroAssembler::stackPointerRegister);
756 releaseGPR(m_newFrameBase);
757 }
758
759 // Finally we handle 3)
760 if (verbose)
761 dataLog(" Ensuring wanted registers are in the right register\n");
762 for (Reg reg = Reg::first(); reg <= Reg::last(); reg = reg.next()) {
763 CachedRecovery* cachedRecovery { m_newRegisters[reg] };
764 if (!cachedRecovery)
765 continue;
766
767 emitDisplace(*cachedRecovery);
768 }
769}
770
771} // namespace JSC
772
773#endif // ENABLE(JIT)
Note: See TracBrowser for help on using the repository browser.