1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=4 sw=4 et tw=99:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 : * May 28, 2008.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Brendan Eich <brendan@mozilla.org>
22 : *
23 : * Contributor(s):
24 : * David Anderson <danderson@mozilla.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 : #include "jscntxt.h"
40 : #include "FrameState.h"
41 : #include "FrameState-inl.h"
42 : #include "StubCompiler.h"
43 :
44 : using namespace js;
45 : using namespace js::mjit;
46 : using namespace js::analyze;
47 :
48 : /* Because of Value alignment */
49 : JS_STATIC_ASSERT(sizeof(FrameEntry) % 8 == 0);
50 :
51 134091 : FrameState::FrameState(JSContext *cx, mjit::Compiler &cc,
52 : Assembler &masm, StubCompiler &stubcc)
53 : : cx(cx),
54 : masm(masm), cc(cc), stubcc(stubcc),
55 : a(NULL), entries(NULL), nentries(0), freeRegs(Registers::AvailAnyRegs),
56 134091 : loop(NULL), inTryBlock(false)
57 : {
58 134091 : }
59 :
60 268182 : FrameState::~FrameState()
61 : {
62 397063 : while (a) {
63 128881 : ActiveFrame *parent = a->parent;
64 128881 : a->script->analysis()->clearAllocations();
65 128881 : cx->free_(a);
66 128881 : a = parent;
67 : }
68 134091 : cx->free_(entries);
69 134091 : }
70 :
71 : void
72 2827 : FrameState::pruneDeadEntries()
73 : {
74 2827 : unsigned shift = 0;
75 40898 : for (unsigned i = 0; i < tracker.nentries; i++) {
76 38071 : FrameEntry *fe = tracker[i];
77 38071 : if (deadEntry(fe)) {
78 6913 : fe->untrack();
79 6913 : shift++;
80 31158 : } else if (shift) {
81 1040 : fe->index_ -= shift;
82 1040 : tracker.entries[fe->index_] = fe;
83 : }
84 : }
85 2827 : tracker.nentries -= shift;
86 2827 : }
87 :
88 : bool
89 131876 : FrameState::pushActiveFrame(JSScript *script, uint32_t argc)
90 : {
91 131876 : if (!a) {
92 128881 : this->nentries = analyze::TotalSlots(script) + (script->nslots - script->nfixed) +
93 128881 : StackSpace::STACK_JIT_EXTRA - VALUES_PER_STACK_FRAME;
94 : size_t totalBytes = sizeof(FrameEntry) * nentries + // entries[]
95 : sizeof(FrameEntry *) * nentries + // tracker.entries
96 128881 : sizeof(StackEntryExtra) * nentries; // extraArray
97 128881 : uint8_t *cursor = (uint8_t *)OffTheBooks::calloc_(totalBytes);
98 128881 : if (!cursor)
99 0 : return false;
100 :
101 128881 : this->entries = (FrameEntry *) cursor;
102 128881 : cursor += sizeof(FrameEntry) * nentries;
103 :
104 128881 : this->tracker.entries = (FrameEntry **)cursor;
105 128881 : cursor += sizeof(FrameEntry *) * nentries;
106 :
107 128881 : this->extraArray = (StackEntryExtra *)cursor;
108 128881 : cursor += sizeof(StackEntryExtra) * nentries;
109 :
110 128881 : JS_ASSERT(reinterpret_cast<uint8_t *>(this->entries) + totalBytes == cursor);
111 :
112 : #if defined JS_NUNBOX32
113 128881 : if (!reifier.init(cx, *this, nentries))
114 0 : return false;
115 : #endif
116 :
117 128881 : this->temporaries = this->temporariesTop = this->entries + nentries - TEMPORARY_LIMIT;
118 : }
119 :
120 : /* We should have already checked that argc == nargs */
121 131876 : JS_ASSERT_IF(a, argc == script->function()->nargs);
122 :
123 131876 : ActiveFrame *newa = OffTheBooks::new_<ActiveFrame>();
124 131876 : if (!newa)
125 0 : return false;
126 :
127 131876 : newa->parent = a;
128 131876 : newa->depth = a ? (totalDepth() + VALUES_PER_STACK_FRAME) : 0;
129 :
130 131876 : newa->script = script;
131 131876 : newa->PC = script->code;
132 131876 : newa->analysis = script->analysis();
133 :
134 : /*
135 : * The callee/this/args in the new frame reuse the same entries as are on
136 : * the stack in the old frame.
137 : */
138 131876 : FrameEntry *entriesStart = a ? a->sp - (argc + 2) : entries;
139 131876 : newa->callee_ = entriesStart + analyze::CalleeSlot();
140 131876 : newa->this_ = entriesStart + analyze::ThisSlot();
141 131876 : newa->args = entriesStart + analyze::ArgSlot(0);
142 131876 : newa->locals = entriesStart + analyze::LocalSlot(script, 0);
143 131876 : newa->spBase = entriesStart + analyze::TotalSlots(script);
144 131876 : newa->sp = newa->spBase;
145 :
146 131876 : this->a = newa;
147 :
148 131876 : return true;
149 : }
150 :
151 : void
152 0 : FrameState::associateReg(FrameEntry *fe, RematInfo::RematType type, AnyRegisterID reg)
153 : {
154 0 : freeRegs.takeReg(reg);
155 :
156 0 : if (type == RematInfo::TYPE)
157 0 : fe->type.setRegister(reg.reg());
158 0 : else if (reg.isReg())
159 0 : fe->data.setRegister(reg.reg());
160 : else
161 0 : fe->data.setFPRegister(reg.fpreg());
162 0 : regstate(reg).associate(fe, type);
163 0 : }
164 :
165 : void
166 2995 : FrameState::popActiveFrame()
167 : {
168 2995 : a->analysis->clearAllocations();
169 :
170 2995 : if (a->parent) {
171 : /* Clear registers and copies used by local variables and stack slots. */
172 7111 : for (FrameEntry *fe = a->sp - 1; fe >= a->locals; fe--) {
173 4116 : if (!fe->isTracked())
174 395 : continue;
175 3721 : forgetAllRegs(fe);
176 3721 : fe->clear();
177 : }
178 : }
179 :
180 2995 : ActiveFrame *parent = a->parent;
181 2995 : cx->delete_(a);
182 2995 : a = parent;
183 2995 : }
184 :
185 : void
186 728263 : FrameState::takeReg(AnyRegisterID reg)
187 : {
188 728263 : modifyReg(reg);
189 728263 : if (freeRegs.hasReg(reg)) {
190 703061 : freeRegs.takeReg(reg);
191 703061 : JS_ASSERT(!regstate(reg).usedBy());
192 : } else {
193 25202 : JS_ASSERT(regstate(reg).fe());
194 25202 : evictReg(reg);
195 : }
196 728263 : }
197 :
198 : #ifdef DEBUG
199 : const char *
200 215064 : FrameState::entryName(const FrameEntry *fe) const
201 : {
202 : static char bufs[4][50];
203 : static unsigned which = 0;
204 215064 : which = (which + 1) & 3;
205 215064 : char *buf = bufs[which];
206 :
207 215064 : if (isTemporary(fe)) {
208 5366 : JS_snprintf(buf, 50, "temp%d", fe - temporaries);
209 5366 : return buf;
210 : }
211 :
212 209698 : if (fe < a->callee_)
213 1153 : return "parent";
214 :
215 208545 : JS_ASSERT(fe >= a->callee_ && fe < a->sp);
216 :
217 208545 : if (fe == a->callee_)
218 285 : return "callee";
219 208260 : if (fe == a->this_)
220 2102 : return "'this'";
221 :
222 206158 : if (isArg(fe))
223 15464 : JS_snprintf(buf, 50, "arg%d", fe - a->args);
224 190694 : else if (isLocal(fe))
225 64927 : JS_snprintf(buf, 50, "local%d", fe - a->locals);
226 : else
227 125767 : JS_snprintf(buf, 50, "slot%d", fe - a->spBase);
228 206158 : return buf;
229 : }
230 : #endif
231 :
232 : void
233 154796 : FrameState::evictReg(AnyRegisterID reg)
234 : {
235 154796 : FrameEntry *fe = regstate(reg).fe();
236 :
237 154796 : JaegerSpew(JSpew_Regalloc, "evicting %s from %s\n", entryName(fe), reg.name());
238 :
239 154796 : if (regstate(reg).type() == RematInfo::TYPE) {
240 63979 : syncType(fe);
241 63979 : fe->type.setMemory();
242 90817 : } else if (reg.isReg()) {
243 90805 : syncData(fe);
244 90805 : fe->data.setMemory();
245 : } else {
246 12 : syncFe(fe);
247 12 : fe->data.setMemory();
248 : }
249 :
250 154796 : regstate(reg).forget();
251 154796 : }
252 :
253 : inline Lifetime *
254 89000 : FrameState::variableLive(FrameEntry *fe, jsbytecode *pc) const
255 : {
256 : /*
257 : * Whether an argument, local or 'this' entry is live at pc. Note: this
258 : * does not account for the 'this' entry when the script is used as a
259 : * constructor, in which case it is live the entire frame.
260 : */
261 89000 : JS_ASSERT(cx->typeInferenceEnabled());
262 89000 : JS_ASSERT(fe > a->callee_ && fe < a->spBase);
263 :
264 89000 : uint32_t offset = pc - a->script->code;
265 89000 : return a->analysis->liveness(entrySlot(fe)).live(offset);
266 : }
267 :
268 : AnyRegisterID
269 54503 : FrameState::bestEvictReg(uint32_t mask, bool includePinned) const
270 : {
271 54503 : JS_ASSERT(cx->typeInferenceEnabled());
272 :
273 : /* Must be looking for a specific type of register. */
274 54503 : JS_ASSERT((mask & Registers::AvailRegs) != (mask & Registers::AvailFPRegs));
275 :
276 54503 : AnyRegisterID fallback;
277 54503 : uint32_t fallbackOffset = UINT32_MAX;
278 :
279 54503 : JaegerSpew(JSpew_Regalloc, "picking best register to evict:\n");
280 :
281 861069 : for (uint32_t i = 0; i < Registers::TotalAnyRegisters; i++) {
282 807452 : AnyRegisterID reg = AnyRegisterID::fromRaw(i);
283 :
284 : /* Register is not allocatable, don't bother. */
285 807452 : if (!(Registers::maskReg(reg) & mask))
286 488474 : continue;
287 :
288 : /* Register is not owned by the FrameState. */
289 318978 : FrameEntry *fe = includePinned ? regstate(reg).usedBy() : regstate(reg).fe();
290 318978 : if (!fe)
291 70913 : continue;
292 :
293 : /*
294 : * Liveness is not tracked for the callee or for stack slot frame entries.
295 : * The callee is evicted as early as needed, stack slots are evicted as
296 : * late as possible. :XXX: This is unfortunate if the stack slot lives
297 : * a long time (especially if it gets spilled anyways when we hit a branch).
298 : */
299 :
300 248065 : if (fe == a->callee_) {
301 285 : JaegerSpew(JSpew_Regalloc, "result: %s is callee\n", reg.name());
302 285 : return reg;
303 : }
304 :
305 247780 : if (fe >= a->spBase && !isTemporary(fe)) {
306 215248 : if (!fallback.isSet()) {
307 47132 : fallback = reg;
308 47132 : fallbackOffset = 0;
309 : }
310 215248 : JaegerSpew(JSpew_Regalloc, " %s is on stack\n", reg.name());
311 215248 : continue;
312 : }
313 :
314 : /* Prioritize keeping copied entries in registers. */
315 32532 : if (fe->isCopied()) {
316 14952 : if (!fallback.isSet()) {
317 4265 : fallback = reg;
318 4265 : fallbackOffset = 0;
319 : }
320 14952 : JaegerSpew(JSpew_Regalloc, " %s has copies\n", reg.name());
321 14952 : continue;
322 : }
323 :
324 17580 : if (isTemporary(fe) || (a->parent && fe < a->locals)) {
325 : /*
326 : * All temporaries we currently generate are for loop invariants,
327 : * which we treat as being live everywhere within the loop.
328 : * Additionally, if this is an inlined frame then any entries
329 : * belonging to parents are treated as live everywhere in the call.
330 : */
331 4801 : uint32_t offset = a->parent ? a->script->length : loop->backedgeOffset();
332 4801 : if (!fallback.isSet() || offset > fallbackOffset) {
333 1932 : fallback = reg;
334 1932 : fallbackOffset = offset;
335 : }
336 4801 : JaegerSpew(JSpew_Regalloc, " %s is a LICM or inline parent entry\n", reg.name());
337 4801 : continue;
338 : }
339 :
340 : /*
341 : * All entries still in registers should have a lifetime, except 'this'
342 : * in constructors which are not accessed later on.
343 : */
344 12779 : Lifetime *lifetime = variableLive(fe, a->PC);
345 :
346 12779 : if (!lifetime) {
347 17 : JS_ASSERT(isConstructorThis(fe));
348 17 : fallback = reg;
349 17 : fallbackOffset = a->script->length;
350 17 : JaegerSpew(JSpew_Regalloc, " %s is 'this' in a constructor\n", reg.name());
351 17 : continue;
352 : }
353 :
354 : /*
355 : * Evict variables which are only live in future loop iterations, and are
356 : * not carried around the loop in a register.
357 : */
358 12762 : if (lifetime->loopTail && (!loop || !loop->carriesLoopReg(fe))) {
359 : JaegerSpew(JSpew_Regalloc, "result: %s (%s) only live in later iterations\n",
360 601 : entryName(fe), reg.name());
361 601 : return reg;
362 : }
363 :
364 12161 : JaegerSpew(JSpew_Regalloc, " %s (%s): %u\n", entryName(fe), reg.name(), lifetime->end);
365 :
366 : /*
367 : * The best live register to evict is the one that will be live for the
368 : * longest time. This may need tweaking for variables that are used in
369 : * many places throughout their lifetime. Note that we don't pay attention
370 : * to whether the register is synced or not --- it is more efficient to
371 : * have things in registers when they're needed than to emit some extra
372 : * writes for things that won't be used again for a while.
373 : */
374 :
375 12161 : if (!fallback.isSet() || lifetime->end > fallbackOffset) {
376 8012 : fallback = reg;
377 8012 : fallbackOffset = lifetime->end;
378 : }
379 : }
380 :
381 53617 : JS_ASSERT(fallback.isSet());
382 :
383 53617 : JaegerSpew(JSpew_Regalloc, "result %s\n", fallback.name());
384 53617 : return fallback;
385 : }
386 :
387 : void
388 67688 : FrameState::evictDeadEntries(bool includePinned)
389 : {
390 1083008 : for (uint32_t i = 0; i < Registers::TotalAnyRegisters; i++) {
391 1015320 : AnyRegisterID reg = AnyRegisterID::fromRaw(i);
392 :
393 : /* Follow along with the same filters as bestEvictReg. */
394 :
395 1015320 : if (!(Registers::maskReg(reg) & Registers::AvailAnyRegs))
396 135376 : continue;
397 :
398 879944 : FrameEntry *fe = includePinned ? regstate(reg).usedBy() : regstate(reg).fe();
399 879944 : if (!fe)
400 589545 : continue;
401 :
402 336986 : if (fe == a->callee_ || isConstructorThis(fe) ||
403 46587 : fe >= a->spBase || fe->isCopied() || (a->parent && fe < a->locals)) {
404 270630 : continue;
405 : }
406 :
407 19769 : Lifetime *lifetime = variableLive(fe, a->PC);
408 19769 : if (lifetime)
409 17738 : continue;
410 :
411 : /*
412 : * If we are about to fake sync for an entry with known type, reset
413 : * that type. We don't want to regard it as correctly synced later.
414 : */
415 2031 : if (!fe->type.synced() && fe->isTypeKnown())
416 225 : fe->type.setMemory();
417 :
418 : /*
419 : * Mark the entry as synced to avoid emitting a store, we don't need
420 : * to keep this value around.
421 : */
422 2031 : fakeSync(fe);
423 2031 : if (regstate(reg).type() == RematInfo::DATA)
424 1489 : fe->data.setMemory();
425 : else
426 542 : fe->type.setMemory();
427 2031 : forgetReg(reg);
428 : }
429 67688 : }
430 :
431 : AnyRegisterID
432 174141 : FrameState::evictSomeReg(uint32_t mask)
433 : {
434 174141 : JS_ASSERT(!freeRegs.hasRegInMask(mask));
435 :
436 174141 : if (cx->typeInferenceEnabled()) {
437 54453 : evictDeadEntries(false);
438 :
439 54453 : if (freeRegs.hasRegInMask(mask)) {
440 : /* There was a register in use by a dead local variable. */
441 1100 : AnyRegisterID reg = freeRegs.takeAnyReg(mask);
442 1100 : modifyReg(reg);
443 1100 : return reg;
444 : }
445 :
446 53353 : AnyRegisterID reg = bestEvictReg(mask, false);
447 53353 : evictReg(reg);
448 53353 : return reg;
449 : }
450 :
451 : /* With inference disabled, only general purpose registers are managed. */
452 119688 : JS_ASSERT((mask & ~Registers::AvailRegs) == 0);
453 :
454 119688 : MaybeRegisterID fallback;
455 :
456 823456 : for (uint32_t i = 0; i < JSC::MacroAssembler::TotalRegisters; i++) {
457 747215 : RegisterID reg = RegisterID(i);
458 :
459 : /* Register is not allocatable, don't bother. */
460 747215 : if (!(Registers::maskReg(reg) & mask))
461 175403 : continue;
462 :
463 : /* Register is not owned by the FrameState. */
464 571812 : FrameEntry *fe = regstate(reg).fe();
465 571812 : if (!fe)
466 99129 : continue;
467 :
468 : /* Try to find a candidate... that doesn't need spilling. */
469 472683 : fallback = reg;
470 :
471 472683 : if (regstate(reg).type() == RematInfo::TYPE && fe->type.synced()) {
472 21225 : fe->type.setMemory();
473 21225 : regstate(reg).forget();
474 21225 : return reg;
475 : }
476 451458 : if (regstate(reg).type() == RematInfo::DATA && fe->data.synced()) {
477 22222 : fe->data.setMemory();
478 22222 : regstate(reg).forget();
479 22222 : return reg;
480 : }
481 : }
482 :
483 76241 : evictReg(fallback.reg());
484 76241 : return fallback.reg();
485 : }
486 :
487 : void
488 1018153 : FrameState::resetInternalState()
489 : {
490 3612897 : for (uint32_t i = 0; i < tracker.nentries; i++)
491 2594744 : tracker[i]->untrack();
492 :
493 1018153 : tracker.reset();
494 1018153 : freeRegs = Registers(Registers::AvailAnyRegs);
495 1018153 : }
496 :
497 : void
498 157483 : FrameState::discardFrame()
499 : {
500 157483 : resetInternalState();
501 157483 : PodArrayZero(regstate_);
502 157483 : }
503 :
504 : FrameEntry *
505 155 : FrameState::snapshotState()
506 : {
507 : /* Everything can be recovered from a copy of the frame entries. */
508 155 : FrameEntry *snapshot = cx->array_new<FrameEntry>(nentries);
509 155 : if (!snapshot)
510 0 : return NULL;
511 155 : PodCopy(snapshot, entries, nentries);
512 155 : return snapshot;
513 : }
514 :
515 : void
516 323 : FrameState::restoreFromSnapshot(FrameEntry *snapshot)
517 : {
518 323 : discardFrame();
519 323 : PodCopy(entries, snapshot, nentries);
520 :
521 85329 : for (unsigned i = 0; i < nentries; i++) {
522 85006 : FrameEntry *fe = entries + i;
523 85006 : if (!fe->isTracked())
524 83158 : continue;
525 1848 : tracker.entries[fe->index_] = fe;
526 1848 : tracker.nentries = Max(tracker.nentries, fe->index_ + 1);
527 1848 : if (fe->isCopy())
528 518 : continue;
529 1330 : if (fe->type.inRegister()) {
530 24 : freeRegs.takeReg(fe->type.reg());
531 24 : regstate(fe->type.reg()).associate(fe, RematInfo::TYPE);
532 : }
533 1330 : if (fe->data.inRegister()) {
534 1048 : freeRegs.takeReg(fe->data.reg());
535 1048 : regstate(fe->data.reg()).associate(fe, RematInfo::DATA);
536 : }
537 1330 : if (fe->data.inFPRegister()) {
538 0 : freeRegs.takeReg(fe->data.fpreg());
539 0 : regstate(fe->data.fpreg()).associate(fe, RematInfo::DATA);
540 : }
541 : }
542 323 : }
543 :
544 : void
545 405333 : FrameState::forgetEverything()
546 : {
547 405333 : resetInternalState();
548 :
549 : #ifdef DEBUG
550 6485328 : for (uint32_t i = 0; i < Registers::TotalAnyRegisters; i++) {
551 6079995 : AnyRegisterID reg = AnyRegisterID::fromRaw(i);
552 6079995 : JS_ASSERT(!regstate(reg).usedBy());
553 : }
554 : #endif
555 405333 : }
556 :
557 : #ifdef DEBUG
558 : void
559 0 : FrameState::dumpAllocation(RegisterAllocation *alloc)
560 : {
561 0 : JS_ASSERT(cx->typeInferenceEnabled());
562 0 : for (unsigned i = 0; i < Registers::TotalAnyRegisters; i++) {
563 0 : AnyRegisterID reg = AnyRegisterID::fromRaw(i);
564 0 : if (alloc->assigned(reg)) {
565 0 : printf(" (%s: %s%s)", reg.name(), entryName(entries + alloc->index(reg)),
566 0 : alloc->synced(reg) ? "" : " unsynced");
567 : }
568 : }
569 0 : printf("\n");
570 0 : }
571 : #endif
572 :
573 : RegisterAllocation *
574 129981 : FrameState::computeAllocation(jsbytecode *target)
575 : {
576 129981 : JS_ASSERT(cx->typeInferenceEnabled());
577 129981 : RegisterAllocation *alloc = cx->typeLifoAlloc().new_<RegisterAllocation>(false);
578 129981 : if (!alloc) {
579 0 : js_ReportOutOfMemory(cx);
580 0 : return NULL;
581 : }
582 :
583 : /*
584 : * State must be synced at exception and switch targets, at traps and when
585 : * crossing between compilation chunks.
586 : */
587 383822 : if (a->analysis->getCode(target).safePoint ||
588 253841 : (!a->parent && !cc.bytecodeInChunk(target))) {
589 : #ifdef DEBUG
590 1167 : if (IsJaegerSpewChannelActive(JSpew_Regalloc)) {
591 0 : JaegerSpew(JSpew_Regalloc, "allocation at %u:", unsigned(target - a->script->code));
592 0 : dumpAllocation(alloc);
593 : }
594 : #endif
595 1167 : return alloc;
596 : }
597 :
598 : /*
599 : * The allocation to use at the target consists of all parent, temporary
600 : * and non-stack entries currently in registers which are live at target.
601 : */
602 128814 : Registers regs = Registers::AvailAnyRegs;
603 1932210 : while (!regs.empty()) {
604 1674582 : AnyRegisterID reg = regs.takeAnyReg();
605 1674582 : if (freeRegs.hasReg(reg) || regstate(reg).type() == RematInfo::TYPE)
606 1591869 : continue;
607 82713 : FrameEntry *fe = regstate(reg).fe();
608 261261 : if (fe < a->callee_ ||
609 80196 : isConstructorThis(fe) ||
610 53004 : (fe > a->callee_ && fe < a->spBase && variableLive(fe, target)) ||
611 45348 : (isTemporary(fe) && (a->parent || uint32_t(target - a->script->code) <= loop->backedgeOffset()))) {
612 : /*
613 : * For entries currently in floating point registers, check they
614 : * are known to be doubles at the target. We don't need to do this
615 : * for entries in normal registers, as fixDoubleTypes must have been
616 : * called to convert them to floats.
617 : */
618 38826 : if (!reg.isReg() && !isTemporary(fe) && fe >= a->callee_ && fe < a->spBase) {
619 486 : if (!a->analysis->trackSlot(entrySlot(fe)))
620 0 : continue;
621 486 : bool nonDoubleTarget = false;
622 486 : const SlotValue *newv = a->analysis->newValues(target);
623 1651 : while (newv && newv->slot) {
624 2003 : if (newv->value.kind() == SSAValue::PHI &&
625 662 : newv->value.phiOffset() == uint32_t(target - a->script->code) &&
626 662 : newv->slot == entrySlot(fe)) {
627 123 : types::TypeSet *types = a->analysis->getValueTypes(newv->value);
628 123 : if (types->getKnownTypeTag(cx) != JSVAL_TYPE_DOUBLE)
629 0 : nonDoubleTarget = true;
630 : }
631 679 : newv++;
632 : }
633 486 : if (nonDoubleTarget)
634 0 : continue;
635 : }
636 38826 : alloc->set(reg, fe - entries, fe->data.synced());
637 : }
638 : }
639 :
640 : #ifdef DEBUG
641 128814 : if (IsJaegerSpewChannelActive(JSpew_Regalloc)) {
642 0 : JaegerSpew(JSpew_Regalloc, "allocation at %u:", unsigned(target - a->script->code));
643 0 : dumpAllocation(alloc);
644 : }
645 : #endif
646 :
647 128814 : return alloc;
648 : }
649 :
650 : void
651 2899 : FrameState::relocateReg(AnyRegisterID reg, RegisterAllocation *alloc, Uses uses)
652 : {
653 2899 : JS_ASSERT(cx->typeInferenceEnabled());
654 :
655 : /*
656 : * The reg needs to be freed to make room for a variable carried across
657 : * a branch. Either evict its entry, or try to move it to a different
658 : * register if it is needed to test the branch condition. :XXX: could also
659 : * watch for variables which are carried across the branch but are in a
660 : * the register for a different carried entry, we just spill these for now.
661 : */
662 2899 : JS_ASSERT(!freeRegs.hasReg(reg));
663 :
664 5226 : for (unsigned i = 0; i < uses.nuses; i++) {
665 3963 : FrameEntry *fe = peek(-1 - i);
666 3963 : if (fe->isCopy())
667 1954 : fe = fe->copyOf();
668 3963 : if (reg.isReg() && fe->data.inRegister() && fe->data.reg() == reg.reg()) {
669 1636 : pinReg(reg);
670 1636 : RegisterID nreg = allocReg();
671 1636 : unpinReg(reg);
672 :
673 1636 : JaegerSpew(JSpew_Regalloc, "relocating %s\n", reg.name());
674 :
675 1636 : masm.move(reg.reg(), nreg);
676 1636 : regstate(reg).forget();
677 1636 : regstate(nreg).associate(fe, RematInfo::DATA);
678 1636 : fe->data.setRegister(nreg);
679 1636 : freeRegs.putReg(reg);
680 1636 : return;
681 : }
682 : }
683 :
684 1263 : JaegerSpew(JSpew_Regalloc, "could not relocate %s\n", reg.name());
685 :
686 1263 : takeReg(reg);
687 1263 : freeRegs.putReg(reg);
688 : }
689 :
690 : bool
691 496478 : FrameState::syncForBranch(jsbytecode *target, Uses uses)
692 : {
693 : /* There should be no unowned or pinned registers. */
694 : #ifdef DEBUG
695 496478 : Registers checkRegs(Registers::AvailAnyRegs);
696 7447170 : while (!checkRegs.empty()) {
697 6454214 : AnyRegisterID reg = checkRegs.takeAnyReg();
698 6454214 : JS_ASSERT_IF(!freeRegs.hasReg(reg), regstate(reg).fe());
699 : }
700 : #endif
701 :
702 496478 : if (!cx->typeInferenceEnabled()) {
703 321105 : syncAndForgetEverything();
704 321105 : return true;
705 : }
706 :
707 175373 : RegisterAllocation *&alloc = a->analysis->getAllocation(target);
708 175373 : if (!alloc) {
709 129637 : alloc = computeAllocation(target);
710 129637 : if (!alloc)
711 0 : return false;
712 : }
713 :
714 175373 : syncForAllocation(alloc, false, uses);
715 :
716 175373 : return true;
717 : }
718 :
719 : void
720 176166 : FrameState::syncForAllocation(RegisterAllocation *alloc, bool inlineReturn, Uses uses)
721 : {
722 : /*
723 : * First pass. Sync all entries which will not be carried in a register,
724 : * and uncopy everything except values popped by the branch or before the
725 : * call returns.
726 : */
727 :
728 176166 : FrameEntry *topEntry = NULL;
729 176166 : if (inlineReturn)
730 793 : topEntry = a->parent->sp - (GET_ARGC(a->parent->PC) + 2);
731 :
732 880054 : for (uint32_t i = tracker.nentries - 1; i < tracker.nentries; i--) {
733 703888 : FrameEntry *fe = tracker[i];
734 :
735 703888 : if (deadEntry(fe, uses.nuses))
736 411880 : continue;
737 292008 : if (inlineReturn && fe >= topEntry && !isTemporary(fe)) {
738 : /*
739 : * The return value has already been stored, so there is no need to
740 : * keep any of the entries for this frame or for values popped once
741 : * the call returns intact. Forcibly evict any registers for these,
742 : * so that we don't emit sync code for them if we need a register
743 : * in syncFe below.
744 : */
745 1775 : forgetAllRegs(fe);
746 1775 : fe->resetSynced();
747 1775 : continue;
748 : }
749 :
750 : /* Force syncs for locals which are dead at the current PC. */
751 290233 : if (isLocal(fe) && !fe->copied && !a->analysis->slotEscapes(entrySlot(fe))) {
752 87518 : Lifetime *lifetime = a->analysis->liveness(entrySlot(fe)).live(a->PC - a->script->code);
753 87518 : if (!lifetime)
754 38872 : fakeSync(fe);
755 : }
756 :
757 : /* If returning from a script, fake syncs for dead locals in the immediate parent. */
758 290709 : if (inlineReturn && fe >= a->parent->locals &&
759 : fe - a->parent->locals < a->parent->script->nfixed &&
760 476 : !a->parent->analysis->slotEscapes(frameSlot(a->parent, fe))) {
761 476 : const LifetimeVariable &var = a->parent->analysis->liveness(frameSlot(a->parent, fe));
762 476 : Lifetime *lifetime = var.live(a->parent->PC - a->parent->script->code);
763 476 : if (!lifetime)
764 47 : fakeSync(fe);
765 : }
766 :
767 290233 : if (!fe->isCopy() && alloc->hasAnyReg(fe - entries)) {
768 : /* Types are always synced, except for known doubles. */
769 51843 : if (!fe->isType(JSVAL_TYPE_DOUBLE))
770 51244 : syncType(fe);
771 : } else {
772 238390 : syncFe(fe);
773 238390 : if (fe->isCopy())
774 3822 : fe->resetSynced();
775 : }
776 : }
777 :
778 : /*
779 : * Second pass. Move entries carried in registers to the right register
780 : * provided no value used in the branch is evicted. After this pass,
781 : * everything will either be in the right register or will be in memory.
782 : */
783 :
784 176166 : Registers regs = Registers(Registers::AvailAnyRegs);
785 2642490 : while (!regs.empty()) {
786 2290158 : AnyRegisterID reg = regs.takeAnyReg();
787 2290158 : if (!alloc->assigned(reg))
788 2235072 : continue;
789 55086 : FrameEntry *fe = getOrTrack(alloc->index(reg));
790 55086 : JS_ASSERT(!fe->isCopy());
791 :
792 55086 : JS_ASSERT_IF(!fe->isType(JSVAL_TYPE_DOUBLE), fe->type.synced());
793 55086 : if (!fe->data.synced() && alloc->synced(reg))
794 3984 : syncFe(fe);
795 :
796 55086 : if (fe->dataInRegister(reg))
797 46919 : continue;
798 :
799 8167 : if (!freeRegs.hasReg(reg))
800 2899 : relocateReg(reg, alloc, uses);
801 :
802 8167 : if (reg.isReg()) {
803 8067 : RegisterID nreg = reg.reg();
804 8067 : if (fe->isType(JSVAL_TYPE_DOUBLE)) {
805 0 : JS_ASSERT(!a->analysis->trackSlot(entrySlot(fe)));
806 0 : syncFe(fe);
807 0 : forgetAllRegs(fe);
808 0 : fe->type.setMemory();
809 0 : fe->data.setMemory();
810 : }
811 8067 : if (fe->data.inMemory()) {
812 4979 : masm.loadPayload(addressOf(fe), nreg);
813 3088 : } else if (fe->isConstant()) {
814 7 : masm.loadValuePayload(fe->getValue(), nreg);
815 : } else {
816 3081 : JS_ASSERT(fe->data.inRegister() && fe->data.reg() != nreg);
817 3081 : masm.move(fe->data.reg(), nreg);
818 3081 : freeRegs.putReg(fe->data.reg());
819 3081 : regstate(fe->data.reg()).forget();
820 : }
821 8067 : fe->data.setRegister(nreg);
822 : } else {
823 100 : FPRegisterID nreg = reg.fpreg();
824 100 : JS_ASSERT(!fe->isNotType(JSVAL_TYPE_DOUBLE));
825 100 : if (!fe->isTypeKnown())
826 88 : learnType(fe, JSVAL_TYPE_DOUBLE, false);
827 100 : if (fe->data.inMemory()) {
828 90 : masm.loadDouble(addressOf(fe), nreg);
829 10 : } else if (fe->isConstant()) {
830 2 : masm.slowLoadConstantDouble(fe->getValue().toDouble(), nreg);
831 : } else {
832 8 : JS_ASSERT(fe->data.inFPRegister() && fe->data.fpreg() != nreg);
833 8 : masm.moveDouble(fe->data.fpreg(), nreg);
834 8 : freeRegs.putReg(fe->data.fpreg());
835 8 : regstate(fe->data.fpreg()).forget();
836 : }
837 100 : fe->data.setFPRegister(nreg);
838 : }
839 :
840 8167 : freeRegs.takeReg(reg);
841 8167 : regstate(reg).associate(fe, RematInfo::DATA);
842 : }
843 176166 : }
844 :
845 : bool
846 455337 : FrameState::discardForJoin(RegisterAllocation *&alloc, uint32_t stackDepth)
847 : {
848 455337 : if (!cx->typeInferenceEnabled()) {
849 281413 : resetInternalState();
850 281413 : PodArrayZero(regstate_);
851 281413 : a->sp = a->spBase + stackDepth;
852 281413 : return true;
853 : }
854 :
855 173924 : if (!alloc) {
856 : /*
857 : * This shows up for loop entries which are not reachable from the
858 : * loop head, and for exception, switch target and trap safe points.
859 : */
860 3978 : alloc = cx->typeLifoAlloc().new_<RegisterAllocation>(false);
861 3978 : if (!alloc) {
862 0 : js_ReportOutOfMemory(cx);
863 0 : return false;
864 : }
865 : }
866 :
867 173924 : resetInternalState();
868 173924 : PodArrayZero(regstate_);
869 :
870 173924 : Registers regs(Registers::AvailAnyRegs);
871 2608860 : while (!regs.empty()) {
872 2261012 : AnyRegisterID reg = regs.takeAnyReg();
873 2261012 : if (!alloc->assigned(reg))
874 2222233 : continue;
875 38779 : FrameEntry *fe = getOrTrack(alloc->index(reg));
876 :
877 38779 : freeRegs.takeReg(reg);
878 :
879 : /*
880 : * We can't look at the type of the fe as we haven't restored analysis types yet,
881 : * but if this is an FP reg it will be set to double type.
882 : */
883 38779 : if (reg.isReg()) {
884 38270 : fe->data.setRegister(reg.reg());
885 : } else {
886 509 : fe->setType(JSVAL_TYPE_DOUBLE);
887 509 : fe->data.setFPRegister(reg.fpreg());
888 : }
889 :
890 38779 : regstate(reg).associate(fe, RematInfo::DATA);
891 38779 : if (!alloc->synced(reg)) {
892 16506 : fe->data.unsync();
893 16506 : if (!reg.isReg())
894 269 : fe->type.unsync();
895 : }
896 : }
897 :
898 173924 : a->sp = a->spBase + stackDepth;
899 :
900 293128 : for (unsigned i = 0; i < stackDepth; i++)
901 119204 : extraArray[a->spBase + i - entries].reset();
902 :
903 173924 : return true;
904 : }
905 :
906 : bool
907 379946 : FrameState::consistentRegisters(jsbytecode *target)
908 : {
909 379946 : if (!cx->typeInferenceEnabled()) {
910 179039 : JS_ASSERT(freeRegs.freeMask == Registers::AvailAnyRegs);
911 179039 : return true;
912 : }
913 :
914 : /*
915 : * Before calling this, either the entire state should have been synced or
916 : * syncForBranch should have been called. These will ensure that any FE
917 : * which is not consistent with the target's register state has already
918 : * been synced, and no stores will need to be issued by prepareForJump.
919 : */
920 200907 : RegisterAllocation *alloc = a->analysis->getAllocation(target);
921 200907 : JS_ASSERT(alloc);
922 :
923 200907 : Registers regs(Registers::AvailAnyRegs);
924 200907 : while (!regs.empty()) {
925 2586411 : AnyRegisterID reg = regs.takeAnyReg();
926 2586411 : if (alloc->assigned(reg)) {
927 61135 : FrameEntry *needed = getOrTrack(alloc->index(reg));
928 61135 : if (!freeRegs.hasReg(reg)) {
929 54337 : FrameEntry *fe = regstate(reg).fe();
930 54337 : if (fe != needed)
931 14 : return false;
932 : } else {
933 6798 : return false;
934 : }
935 : }
936 : }
937 :
938 194095 : return true;
939 : }
940 :
941 : void
942 83360 : FrameState::prepareForJump(jsbytecode *target, Assembler &masm, bool synced)
943 : {
944 83360 : if (!cx->typeInferenceEnabled())
945 0 : return;
946 :
947 83360 : JS_ASSERT_IF(!synced, !consistentRegisters(target));
948 :
949 83360 : RegisterAllocation *alloc = a->analysis->getAllocation(target);
950 83360 : JS_ASSERT(alloc);
951 :
952 83360 : Registers regs = 0;
953 :
954 83360 : regs = Registers(Registers::AvailAnyRegs);
955 1250400 : while (!regs.empty()) {
956 1083680 : AnyRegisterID reg = regs.takeAnyReg();
957 1083680 : if (!alloc->assigned(reg))
958 1021469 : continue;
959 :
960 62211 : const FrameEntry *fe = getOrTrack(alloc->index(reg));
961 62211 : if (synced || !fe->backing()->dataInRegister(reg)) {
962 62168 : JS_ASSERT_IF(!synced, fe->data.synced());
963 62168 : if (reg.isReg())
964 61642 : masm.loadPayload(addressOf(fe), reg.reg());
965 : else
966 526 : masm.loadDouble(addressOf(fe), reg.fpreg());
967 : }
968 : }
969 : }
970 :
971 : void
972 301868 : FrameState::storeTo(FrameEntry *fe, Address address, bool popped)
973 : {
974 301868 : if (fe->isConstant()) {
975 101484 : masm.storeValue(fe->getValue(), address);
976 101484 : return;
977 : }
978 :
979 200384 : if (fe->isCopy())
980 3235 : fe = fe->copyOf();
981 :
982 200384 : JS_ASSERT(!freeRegs.hasReg(address.base));
983 :
984 : /* If loading from memory, ensure destination differs. */
985 340817 : JS_ASSERT_IF((fe->type.inMemory() || fe->data.inMemory()),
986 : addressOf(fe).base != address.base ||
987 340817 : addressOf(fe).offset != address.offset);
988 :
989 200384 : if (fe->data.inFPRegister()) {
990 2430 : masm.storeDouble(fe->data.fpreg(), address);
991 2430 : return;
992 : }
993 :
994 197954 : if (fe->isType(JSVAL_TYPE_DOUBLE)) {
995 101 : JS_ASSERT(fe->data.inMemory());
996 101 : masm.loadDouble(addressOf(fe), Registers::FPConversionTemp);
997 101 : masm.storeDouble(Registers::FPConversionTemp, address);
998 101 : return;
999 : }
1000 :
1001 : /* Don't clobber the address's register. */
1002 197853 : bool pinAddressReg = !!regstate(address.base).fe();
1003 197853 : if (pinAddressReg)
1004 0 : pinReg(address.base);
1005 :
1006 : #if defined JS_PUNBOX64
1007 : if (fe->type.inMemory() && fe->data.inMemory()) {
1008 : /* Future optimization: track that the Value is in a register. */
1009 : RegisterID vreg = Registers::ValueReg;
1010 : masm.loadPtr(addressOf(fe), vreg);
1011 : masm.storePtr(vreg, address);
1012 : if (pinAddressReg)
1013 : unpinReg(address.base);
1014 : return;
1015 : }
1016 :
1017 : JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
1018 :
1019 : /*
1020 : * If dreg is obtained via allocReg(), then calling
1021 : * pinReg() trips an assertion. But in all other cases,
1022 : * calling pinReg() is necessary in the fe->type.inMemory() path.
1023 : * Remember whether pinReg() can be safely called.
1024 : */
1025 : bool canPinDreg = true;
1026 : bool wasInRegister = fe->data.inRegister();
1027 :
1028 : /* Get a register for the payload. */
1029 : MaybeRegisterID dreg;
1030 : if (fe->data.inRegister()) {
1031 : dreg = fe->data.reg();
1032 : } else {
1033 : JS_ASSERT(fe->data.inMemory());
1034 : if (popped) {
1035 : dreg = allocReg();
1036 : masm.loadPayload(addressOf(fe), dreg.reg());
1037 : canPinDreg = false;
1038 : } else {
1039 : dreg = allocAndLoadReg(fe, false, RematInfo::DATA).reg();
1040 : fe->data.setRegister(dreg.reg());
1041 : }
1042 : }
1043 :
1044 : /* Store the Value. */
1045 : if (fe->type.inRegister()) {
1046 : masm.storeValueFromComponents(fe->type.reg(), dreg.reg(), address);
1047 : } else if (fe->isTypeKnown()) {
1048 : masm.storeValueFromComponents(ImmType(fe->getKnownType()), dreg.reg(), address);
1049 : } else {
1050 : JS_ASSERT(fe->type.inMemory());
1051 : if (canPinDreg)
1052 : pinReg(dreg.reg());
1053 :
1054 : RegisterID treg;
1055 : if (popped) {
1056 : treg = allocReg();
1057 : masm.loadTypeTag(addressOf(fe), treg);
1058 : } else {
1059 : treg = allocAndLoadReg(fe, false, RematInfo::TYPE).reg();
1060 : }
1061 : masm.storeValueFromComponents(treg, dreg.reg(), address);
1062 :
1063 : if (popped)
1064 : freeReg(treg);
1065 : else
1066 : fe->type.setRegister(treg);
1067 :
1068 : if (canPinDreg)
1069 : unpinReg(dreg.reg());
1070 : }
1071 :
1072 : /* If register is untracked, free it. */
1073 : if (!wasInRegister && popped)
1074 : freeReg(dreg.reg());
1075 :
1076 : #elif defined JS_NUNBOX32
1077 :
1078 197853 : if (fe->data.inRegister()) {
1079 158558 : masm.storePayload(fe->data.reg(), address);
1080 : } else {
1081 39295 : JS_ASSERT(fe->data.inMemory());
1082 : RegisterID reg;
1083 39295 : if (popped) {
1084 36886 : reg = allocReg();
1085 36886 : masm.loadPayload(addressOf(fe), reg);
1086 : } else {
1087 2409 : reg = allocAndLoadReg(fe, false, RematInfo::DATA).reg();
1088 : }
1089 39295 : masm.storePayload(reg, address);
1090 39295 : if (popped)
1091 36886 : freeReg(reg);
1092 : else
1093 2409 : fe->data.setRegister(reg);
1094 : }
1095 :
1096 197853 : if (fe->isTypeKnown()) {
1097 64181 : masm.storeTypeTag(ImmType(fe->getKnownType()), address);
1098 133672 : } else if (fe->type.inRegister()) {
1099 73575 : masm.storeTypeTag(fe->type.reg(), address);
1100 : } else {
1101 60097 : JS_ASSERT(fe->type.inMemory());
1102 : RegisterID reg;
1103 60097 : if (popped) {
1104 58522 : reg = allocReg();
1105 58522 : masm.loadTypeTag(addressOf(fe), reg);
1106 : } else {
1107 1575 : reg = allocAndLoadReg(fe, false, RematInfo::TYPE).reg();
1108 : }
1109 60097 : masm.storeTypeTag(reg, address);
1110 60097 : if (popped)
1111 58522 : freeReg(reg);
1112 : else
1113 1575 : fe->type.setRegister(reg);
1114 : }
1115 : #endif
1116 197853 : if (pinAddressReg)
1117 0 : unpinReg(address.base);
1118 : }
1119 :
1120 : void
1121 804 : FrameState::loadThisForReturn(RegisterID typeReg, RegisterID dataReg, RegisterID tempReg)
1122 : {
1123 804 : return loadForReturn(getThis(), typeReg, dataReg, tempReg);
1124 : }
1125 :
1126 23150 : void FrameState::loadForReturn(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg)
1127 : {
1128 23150 : JS_ASSERT(dataReg != typeReg && dataReg != tempReg && typeReg != tempReg);
1129 :
1130 23150 : if (fe->isConstant()) {
1131 5989 : masm.loadValueAsComponents(fe->getValue(), typeReg, dataReg);
1132 5989 : return;
1133 : }
1134 :
1135 17161 : if (fe->isType(JSVAL_TYPE_DOUBLE)) {
1136 335 : FPRegisterID fpreg = tempFPRegForData(fe);
1137 335 : masm.breakDouble(fpreg, typeReg, dataReg);
1138 335 : return;
1139 : }
1140 :
1141 16826 : if (fe->isCopy())
1142 4779 : fe = fe->copyOf();
1143 :
1144 16826 : MaybeRegisterID maybeType = maybePinType(fe);
1145 16826 : MaybeRegisterID maybeData = maybePinData(fe);
1146 :
1147 16826 : if (fe->isTypeKnown()) {
1148 : // If the data is in memory, or in the wrong reg, load/move it.
1149 3909 : if (!maybeData.isSet())
1150 1184 : masm.loadPayload(addressOf(fe), dataReg);
1151 2725 : else if (maybeData.reg() != dataReg)
1152 1540 : masm.move(maybeData.reg(), dataReg);
1153 3909 : masm.move(ImmType(fe->getKnownType()), typeReg);
1154 3909 : return;
1155 : }
1156 :
1157 : // If both halves of the value are in memory, make this easier and load
1158 : // both pieces into their respective registers.
1159 12917 : if (fe->type.inMemory() && fe->data.inMemory()) {
1160 6183 : masm.loadValueAsComponents(addressOf(fe), typeReg, dataReg);
1161 6183 : return;
1162 : }
1163 :
1164 : // Now, we should be guaranteed that at least one part is in a register.
1165 6734 : JS_ASSERT(maybeType.isSet() || maybeData.isSet());
1166 :
1167 : // Make sure we have two registers while making sure not clobber either half.
1168 : // Here we are allowed to mess up the FrameState invariants, because this
1169 : // is specialized code for a path that is about to discard the entire frame.
1170 6734 : if (!maybeType.isSet()) {
1171 304 : JS_ASSERT(maybeData.isSet());
1172 304 : if (maybeData.reg() != typeReg)
1173 274 : maybeType = typeReg;
1174 : else
1175 30 : maybeType = tempReg;
1176 304 : masm.loadTypeTag(addressOf(fe), maybeType.reg());
1177 6430 : } else if (!maybeData.isSet()) {
1178 3 : JS_ASSERT(maybeType.isSet());
1179 3 : if (maybeType.reg() != dataReg)
1180 2 : maybeData = dataReg;
1181 : else
1182 1 : maybeData = tempReg;
1183 3 : masm.loadPayload(addressOf(fe), maybeData.reg());
1184 : }
1185 :
1186 6734 : RegisterID type = maybeType.reg();
1187 6734 : RegisterID data = maybeData.reg();
1188 :
1189 6734 : if (data == typeReg && type == dataReg) {
1190 334 : masm.move(type, tempReg);
1191 334 : masm.move(data, dataReg);
1192 334 : masm.move(tempReg, typeReg);
1193 6400 : } else if (data != dataReg) {
1194 1703 : if (type == typeReg) {
1195 271 : masm.move(data, dataReg);
1196 1432 : } else if (type != dataReg) {
1197 1397 : masm.move(data, dataReg);
1198 1397 : if (type != typeReg)
1199 1397 : masm.move(type, typeReg);
1200 : } else {
1201 35 : JS_ASSERT(data != typeReg);
1202 35 : masm.move(type, typeReg);
1203 35 : masm.move(data, dataReg);
1204 : }
1205 4697 : } else if (type != typeReg) {
1206 77 : masm.move(type, typeReg);
1207 : }
1208 : }
1209 :
1210 : #ifdef DEBUG
1211 : void
1212 17343229 : FrameState::assertValidRegisterState() const
1213 : {
1214 17343229 : Registers checkedFreeRegs(Registers::AvailAnyRegs);
1215 :
1216 : /* Check that copied and copy info balance out. */
1217 17343229 : int32_t copyCount = 0;
1218 :
1219 488034474 : for (uint32_t i = 0; i < tracker.nentries; i++) {
1220 470691245 : FrameEntry *fe = tracker[i];
1221 470691245 : if (deadEntry(fe))
1222 32948156 : continue;
1223 :
1224 437743089 : JS_ASSERT(i == fe->trackerIndex());
1225 :
1226 437743089 : if (fe->isCopy()) {
1227 3490954 : JS_ASSERT_IF(!fe->copyOf()->temporary, fe > fe->copyOf());
1228 3490954 : JS_ASSERT(fe->trackerIndex() > fe->copyOf()->trackerIndex());
1229 3490954 : JS_ASSERT(!deadEntry(fe->copyOf()));
1230 3490954 : JS_ASSERT(fe->copyOf()->isCopied());
1231 3490954 : JS_ASSERT(!fe->isCopied());
1232 3490954 : copyCount--;
1233 3490954 : continue;
1234 : }
1235 :
1236 434252135 : copyCount += fe->copied;
1237 :
1238 434252135 : if (fe->type.inRegister()) {
1239 11976274 : checkedFreeRegs.takeReg(fe->type.reg());
1240 11976274 : JS_ASSERT(regstate(fe->type.reg()).fe() == fe);
1241 11976274 : JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
1242 : }
1243 434252135 : if (fe->data.inRegister()) {
1244 16986844 : checkedFreeRegs.takeReg(fe->data.reg());
1245 16986844 : JS_ASSERT(regstate(fe->data.reg()).fe() == fe);
1246 16986844 : JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
1247 : }
1248 434252135 : if (fe->data.inFPRegister()) {
1249 130251 : JS_ASSERT(fe->isType(JSVAL_TYPE_DOUBLE));
1250 130251 : checkedFreeRegs.takeReg(fe->data.fpreg());
1251 130251 : JS_ASSERT(regstate(fe->data.fpreg()).fe() == fe);
1252 : }
1253 : }
1254 :
1255 17343229 : JS_ASSERT(copyCount == 0);
1256 17343229 : JS_ASSERT(checkedFreeRegs == freeRegs);
1257 :
1258 156089061 : for (uint32_t i = 0; i < Registers::TotalRegisters; i++) {
1259 138745832 : AnyRegisterID reg = (RegisterID) i;
1260 138745832 : JS_ASSERT(!regstate(reg).isPinned());
1261 138745832 : JS_ASSERT_IF(regstate(reg).fe(), !freeRegs.hasReg(reg));
1262 138745832 : JS_ASSERT_IF(regstate(reg).fe(), regstate(reg).fe()->isTracked());
1263 : }
1264 :
1265 138745832 : for (uint32_t i = 0; i < Registers::TotalFPRegisters; i++) {
1266 121402603 : AnyRegisterID reg = (FPRegisterID) i;
1267 121402603 : JS_ASSERT(!regstate(reg).isPinned());
1268 121402603 : JS_ASSERT_IF(regstate(reg).fe(), !freeRegs.hasReg(reg));
1269 121402603 : JS_ASSERT_IF(regstate(reg).fe(), regstate(reg).fe()->isTracked());
1270 121402603 : JS_ASSERT_IF(regstate(reg).fe(), regstate(reg).type() == RematInfo::DATA);
1271 : }
1272 17343229 : }
1273 : #endif
1274 :
1275 : #if defined JS_NUNBOX32
1276 : void
1277 171297 : FrameState::syncFancy(Assembler &masm, Registers avail, int trackerIndex) const
1278 : {
1279 171297 : reifier.reset(&masm, avail, a->sp, entries);
1280 :
1281 1206159 : for (; trackerIndex >= 0; trackerIndex--) {
1282 1034862 : FrameEntry *fe = tracker[trackerIndex];
1283 1034862 : if (fe >= a->sp)
1284 228439 : continue;
1285 :
1286 806423 : reifier.sync(fe);
1287 : }
1288 171297 : }
1289 :
1290 : #endif
1291 : void
1292 4124757 : FrameState::sync(Assembler &masm, Uses uses) const
1293 : {
1294 4124757 : if (!entries)
1295 0 : return;
1296 :
1297 : /* Sync all registers up-front. */
1298 4124757 : Registers allRegs(Registers::AvailAnyRegs);
1299 61871355 : while (!allRegs.empty()) {
1300 53621841 : AnyRegisterID reg = allRegs.takeAnyReg();
1301 53621841 : FrameEntry *fe = regstate(reg).usedBy();
1302 53621841 : if (!fe)
1303 44836858 : continue;
1304 :
1305 8784983 : JS_ASSERT(fe->isTracked());
1306 :
1307 : #if defined JS_PUNBOX64
1308 : /* Sync entire FE to prevent loads. */
1309 : ensureFeSynced(fe, masm);
1310 :
1311 : /* Take the other register in the pair, if one exists. */
1312 : if (regstate(reg).type() == RematInfo::DATA && fe->type.inRegister())
1313 : allRegs.takeReg(fe->type.reg());
1314 : else if (regstate(reg).type() == RematInfo::TYPE && fe->data.inRegister())
1315 : allRegs.takeReg(fe->data.reg());
1316 : #elif defined JS_NUNBOX32
1317 : /* Sync register if unsynced. */
1318 8784983 : if (fe->isType(JSVAL_TYPE_DOUBLE)) {
1319 27800 : ensureFeSynced(fe, masm);
1320 8757183 : } else if (regstate(reg).type() == RematInfo::DATA) {
1321 4915358 : JS_ASSERT(fe->data.reg() == reg.reg());
1322 4915358 : ensureDataSynced(fe, masm);
1323 : } else {
1324 3841825 : JS_ASSERT(fe->type.reg() == reg.reg());
1325 3841825 : ensureTypeSynced(fe, masm);
1326 : }
1327 : #endif
1328 : }
1329 :
1330 : /*
1331 : * Keep track of free registers using a bitmask. If we have to drop into
1332 : * syncFancy(), then this mask will help avoid eviction.
1333 : */
1334 4124757 : Registers avail(freeRegs.freeMask & Registers::AvailRegs);
1335 4124757 : Registers temp(Registers::TempAnyRegs);
1336 :
1337 4124757 : unsigned nentries = tracker.nentries;
1338 21862872 : for (int trackerIndex = nentries - 1; trackerIndex >= 0; trackerIndex--) {
1339 17909412 : JS_ASSERT(tracker.nentries == nentries);
1340 17909412 : FrameEntry *fe = tracker[trackerIndex];
1341 17909412 : if (fe >= a->sp)
1342 5621515 : continue;
1343 :
1344 12287897 : if (fe->isType(JSVAL_TYPE_DOUBLE)) {
1345 : /* Copies of in-memory doubles can be synced without spilling. */
1346 40618 : if (fe->isCopy() || !fe->data.inFPRegister())
1347 13174 : ensureFeSynced(fe, masm);
1348 40618 : continue;
1349 : }
1350 :
1351 12247279 : if (!fe->isCopy()) {
1352 11088057 : if (fe->data.inRegister() && !regstate(fe->data.reg()).isPinned())
1353 4631901 : avail.putReg(fe->data.reg());
1354 11088057 : if (fe->type.inRegister() && !regstate(fe->type.reg()).isPinned())
1355 3634805 : avail.putReg(fe->type.reg());
1356 : } else {
1357 1159222 : FrameEntry *backing = fe->copyOf();
1358 1159222 : JS_ASSERT(!backing->isConstant() && !fe->isConstant());
1359 :
1360 : #if defined JS_PUNBOX64
1361 : if ((!fe->type.synced() && backing->type.inMemory()) ||
1362 : (!fe->data.synced() && backing->data.inMemory())) {
1363 :
1364 : RegisterID syncReg = Registers::ValueReg;
1365 :
1366 : /* Load the entire Value into syncReg. */
1367 : if (backing->type.synced() && backing->data.synced()) {
1368 : masm.loadValue(addressOf(backing), syncReg);
1369 : } else if (backing->type.inMemory()) {
1370 : masm.loadTypeTag(addressOf(backing), syncReg);
1371 : masm.orPtr(backing->data.reg(), syncReg);
1372 : } else {
1373 : JS_ASSERT(backing->data.inMemory());
1374 : masm.loadPayload(addressOf(backing), syncReg);
1375 : if (backing->isTypeKnown())
1376 : masm.orPtr(ImmType(backing->getKnownType()), syncReg);
1377 : else
1378 : masm.orPtr(backing->type.reg(), syncReg);
1379 : }
1380 :
1381 : masm.storeValue(syncReg, addressOf(fe));
1382 : continue;
1383 : }
1384 : #elif defined JS_NUNBOX32
1385 : /* Fall back to a slower sync algorithm if load required. */
1386 3310903 : if ((!fe->type.synced() && backing->type.inMemory()) ||
1387 2151681 : (!fe->data.synced() && backing->data.inMemory())) {
1388 171297 : syncFancy(masm, avail, trackerIndex);
1389 171297 : return;
1390 : }
1391 : #endif
1392 : }
1393 :
1394 12075982 : bool copy = fe->isCopy();
1395 :
1396 : /* If a part still needs syncing, it is either a copy or constant. */
1397 : #if defined JS_PUNBOX64
1398 : /* All register-backed FEs have been entirely synced up-front. */
1399 : if (copy || (!fe->type.inRegister() && !fe->data.inRegister()))
1400 : ensureFeSynced(fe, masm);
1401 : #elif defined JS_NUNBOX32
1402 : /* All components held in registers have been already synced. */
1403 12075982 : if (copy || !fe->data.inRegister())
1404 7326817 : ensureDataSynced(fe, masm);
1405 12075982 : if (copy || !fe->type.inRegister())
1406 8421455 : ensureTypeSynced(fe, masm);
1407 : #endif
1408 : }
1409 : }
1410 :
1411 : void
1412 1836353 : FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore)
1413 : {
1414 1836353 : if (loop) {
1415 : /*
1416 : * Drop any remaining loop registers so we don't do any more after-the-fact
1417 : * allocation of the initial register state.
1418 : */
1419 116925 : loop->clearLoopRegisters();
1420 : }
1421 :
1422 : /* Sync all kill-registers up-front. */
1423 1836353 : Registers search(kill.freeMask & ~freeRegs.freeMask);
1424 6365262 : while (!search.empty()) {
1425 2692556 : AnyRegisterID reg = search.takeAnyReg();
1426 2692556 : FrameEntry *fe = regstate(reg).usedBy();
1427 2692556 : if (!fe || deadEntry(fe, ignore.nuses))
1428 203111 : continue;
1429 :
1430 2489445 : JS_ASSERT(fe->isTracked());
1431 :
1432 : #if defined JS_PUNBOX64
1433 : /* Don't use syncFe(), since that may clobber more registers. */
1434 : ensureFeSynced(fe, masm);
1435 :
1436 : if (!fe->type.synced())
1437 : fe->type.sync();
1438 : if (!fe->data.synced())
1439 : fe->data.sync();
1440 :
1441 : /* Take the other register in the pair, if one exists. */
1442 : if (regstate(reg).type() == RematInfo::DATA) {
1443 : if (!fe->isType(JSVAL_TYPE_DOUBLE)) {
1444 : JS_ASSERT(fe->data.reg() == reg.reg());
1445 : if (fe->type.inRegister() && search.hasReg(fe->type.reg()))
1446 : search.takeReg(fe->type.reg());
1447 : }
1448 : } else {
1449 : JS_ASSERT(fe->type.reg() == reg.reg());
1450 : if (fe->data.inRegister() && search.hasReg(fe->data.reg()))
1451 : search.takeReg(fe->data.reg());
1452 : }
1453 : #elif defined JS_NUNBOX32
1454 : /* Sync this register. */
1455 2489445 : if (fe->isType(JSVAL_TYPE_DOUBLE)) {
1456 7087 : syncFe(fe);
1457 2482358 : } else if (regstate(reg).type() == RematInfo::DATA) {
1458 1358356 : JS_ASSERT(fe->data.reg() == reg.reg());
1459 1358356 : syncData(fe);
1460 : } else {
1461 1124002 : JS_ASSERT(fe->type.reg() == reg.reg());
1462 1124002 : syncType(fe);
1463 : }
1464 : #endif
1465 : }
1466 :
1467 :
1468 1836353 : unsigned nentries = tracker.nentries;
1469 11082628 : for (int trackerIndex = nentries - 1; trackerIndex >= 0; trackerIndex--) {
1470 9246275 : JS_ASSERT(tracker.nentries == nentries);
1471 9246275 : FrameEntry *fe = tracker[trackerIndex];
1472 :
1473 9246275 : if (fe >= a->sp || deadEntry(fe, ignore.nuses))
1474 3049319 : continue;
1475 :
1476 6196956 : syncFe(fe);
1477 :
1478 6196956 : if (fe->isCopy())
1479 357641 : continue;
1480 :
1481 : /* Forget registers. */
1482 5839315 : if (fe->data.inRegister() && !regstate(fe->data.reg()).isPinned()) {
1483 1621740 : forgetReg(fe->data.reg());
1484 1621740 : fe->data.setMemory();
1485 : }
1486 5839315 : if (fe->data.inFPRegister() && !regstate(fe->data.fpreg()).isPinned()) {
1487 5803 : forgetReg(fe->data.fpreg());
1488 5803 : fe->data.setMemory();
1489 : }
1490 5839315 : if (fe->type.inRegister() && !regstate(fe->type.reg()).isPinned()) {
1491 1363539 : forgetReg(fe->type.reg());
1492 1363539 : fe->type.setMemory();
1493 : }
1494 : }
1495 :
1496 : /*
1497 : * Anything still alive at this point is guaranteed to be synced. However,
1498 : * it is necessary to evict temporary registers.
1499 : */
1500 1836353 : search = Registers(kill.freeMask & ~freeRegs.freeMask);
1501 4170120 : while (!search.empty()) {
1502 497414 : AnyRegisterID reg = search.takeAnyReg();
1503 497414 : FrameEntry *fe = regstate(reg).usedBy();
1504 497414 : if (!fe || deadEntry(fe, ignore.nuses))
1505 203111 : continue;
1506 :
1507 294303 : JS_ASSERT(fe->isTracked());
1508 :
1509 294303 : if (regstate(reg).type() == RematInfo::DATA) {
1510 170056 : JS_ASSERT_IF(reg.isFPReg(), fe->data.fpreg() == reg.fpreg());
1511 170056 : JS_ASSERT_IF(!reg.isFPReg(), fe->data.reg() == reg.reg());
1512 170056 : JS_ASSERT(fe->data.synced());
1513 170056 : fe->data.setMemory();
1514 : } else {
1515 124247 : JS_ASSERT(fe->type.reg() == reg.reg());
1516 124247 : JS_ASSERT(fe->type.synced());
1517 124247 : fe->type.setMemory();
1518 : }
1519 :
1520 294303 : forgetReg(reg);
1521 : }
1522 1836353 : }
1523 :
1524 : void
1525 3017272 : FrameState::merge(Assembler &masm, Changes changes) const
1526 : {
1527 : /*
1528 : * Note: this should only be called by StubCompiler::rejoin, which will notify
1529 : * this FrameState about the jump to patch up in case a new loop register is
1530 : * allocated later.
1531 : */
1532 :
1533 : /*
1534 : * For any changed values we are merging back which we consider to be doubles,
1535 : * ensure they actually are doubles. They must be doubles or ints, but we
1536 : * do not require stub paths to always generate a double when needed.
1537 : * :FIXME: we check this on OOL stub calls, but not inline stub calls.
1538 : */
1539 3017272 : if (cx->typeInferenceEnabled()) {
1540 1081324 : for (unsigned i = 0; i < changes.nchanges; i++) {
1541 378691 : FrameEntry *fe = a->sp - 1 - i;
1542 378691 : if (fe->isTracked() && fe->isType(JSVAL_TYPE_DOUBLE))
1543 19456 : masm.ensureInMemoryDouble(addressOf(fe));
1544 : }
1545 : }
1546 :
1547 3017272 : uint32_t mask = Registers::AvailAnyRegs & ~freeRegs.freeMask;
1548 3017272 : Registers search(mask);
1549 :
1550 10311738 : while (!search.empty(mask)) {
1551 4277194 : AnyRegisterID reg = search.peekReg(mask);
1552 4277194 : FrameEntry *fe = regstate(reg).usedBy();
1553 :
1554 4277194 : if (!fe) {
1555 21282 : search.takeReg(reg);
1556 21282 : continue;
1557 : }
1558 :
1559 4255912 : if (fe->isType(JSVAL_TYPE_DOUBLE)) {
1560 42071 : JS_ASSERT(fe->data.fpreg() == reg.fpreg());
1561 42071 : search.takeReg(fe->data.fpreg());
1562 42071 : masm.loadDouble(addressOf(fe), fe->data.fpreg());
1563 4213841 : } else if (fe->data.inRegister() && fe->type.inRegister()) {
1564 2390846 : search.takeReg(fe->data.reg());
1565 2390846 : search.takeReg(fe->type.reg());
1566 2390846 : masm.loadValueAsComponents(addressOf(fe), fe->type.reg(), fe->data.reg());
1567 : } else {
1568 1822995 : if (fe->data.inRegister()) {
1569 1741500 : search.takeReg(fe->data.reg());
1570 1741500 : masm.loadPayload(addressOf(fe), fe->data.reg());
1571 : }
1572 1822995 : if (fe->type.inRegister()) {
1573 81495 : search.takeReg(fe->type.reg());
1574 81495 : masm.loadTypeTag(addressOf(fe), fe->type.reg());
1575 : }
1576 : }
1577 : }
1578 3017272 : }
1579 :
1580 : JSC::MacroAssembler::RegisterID
1581 575737 : FrameState::copyDataIntoReg(FrameEntry *fe)
1582 : {
1583 575737 : return copyDataIntoReg(this->masm, fe);
1584 : }
1585 :
1586 : void
1587 3805 : FrameState::copyDataIntoReg(FrameEntry *fe, RegisterID hint)
1588 : {
1589 3805 : JS_ASSERT(!fe->isConstant());
1590 3805 : JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
1591 :
1592 3805 : if (fe->isCopy())
1593 1628 : fe = fe->copyOf();
1594 :
1595 3805 : if (!fe->data.inRegister())
1596 1548 : tempRegForData(fe);
1597 :
1598 3805 : RegisterID reg = fe->data.reg();
1599 3805 : if (reg == hint) {
1600 85 : if (freeRegs.empty(Registers::AvailRegs)) {
1601 71 : ensureDataSynced(fe, masm);
1602 71 : fe->data.setMemory();
1603 : } else {
1604 14 : reg = allocReg();
1605 14 : masm.move(hint, reg);
1606 14 : fe->data.setRegister(reg);
1607 14 : regstate(reg).associate(regstate(hint).fe(), RematInfo::DATA);
1608 : }
1609 85 : regstate(hint).forget();
1610 : } else {
1611 3720 : pinReg(reg);
1612 3720 : takeReg(hint);
1613 3720 : unpinReg(reg);
1614 3720 : masm.move(reg, hint);
1615 : }
1616 :
1617 3805 : modifyReg(hint);
1618 3805 : }
1619 :
1620 : JSC::MacroAssembler::RegisterID
1621 578122 : FrameState::copyDataIntoReg(Assembler &masm, FrameEntry *fe)
1622 : {
1623 578122 : JS_ASSERT(!fe->isConstant());
1624 :
1625 578122 : if (fe->isCopy())
1626 197344 : fe = fe->copyOf();
1627 :
1628 578122 : if (fe->data.inRegister()) {
1629 453840 : RegisterID reg = fe->data.reg();
1630 453840 : if (freeRegs.empty(Registers::AvailRegs)) {
1631 21267 : ensureDataSynced(fe, masm);
1632 21267 : fe->data.setMemory();
1633 21267 : regstate(reg).forget();
1634 21267 : modifyReg(reg);
1635 : } else {
1636 432573 : RegisterID newReg = allocReg();
1637 432573 : masm.move(reg, newReg);
1638 432573 : reg = newReg;
1639 : }
1640 453840 : return reg;
1641 : }
1642 :
1643 124282 : RegisterID reg = allocReg();
1644 :
1645 124282 : if (!freeRegs.empty(Registers::AvailRegs))
1646 118157 : masm.move(tempRegForData(fe), reg);
1647 : else
1648 6125 : masm.loadPayload(addressOf(fe),reg);
1649 :
1650 124282 : return reg;
1651 : }
1652 :
1653 : JSC::MacroAssembler::RegisterID
1654 8529 : FrameState::copyTypeIntoReg(FrameEntry *fe)
1655 : {
1656 8529 : if (fe->isCopy())
1657 2037 : fe = fe->copyOf();
1658 :
1659 8529 : JS_ASSERT(!fe->type.isConstant());
1660 :
1661 8529 : if (fe->type.inRegister()) {
1662 6041 : RegisterID reg = fe->type.reg();
1663 6041 : if (freeRegs.empty(Registers::AvailRegs)) {
1664 4063 : ensureTypeSynced(fe, masm);
1665 4063 : fe->type.setMemory();
1666 4063 : regstate(reg).forget();
1667 4063 : modifyReg(reg);
1668 : } else {
1669 1978 : RegisterID newReg = allocReg();
1670 1978 : masm.move(reg, newReg);
1671 1978 : reg = newReg;
1672 : }
1673 6041 : return reg;
1674 : }
1675 :
1676 2488 : RegisterID reg = allocReg();
1677 :
1678 2488 : if (!freeRegs.empty(Registers::AvailRegs))
1679 871 : masm.move(tempRegForType(fe), reg);
1680 : else
1681 1617 : masm.loadTypeTag(addressOf(fe), reg);
1682 :
1683 2488 : return reg;
1684 : }
1685 :
1686 : JSC::MacroAssembler::RegisterID
1687 0 : FrameState::copyInt32ConstantIntoReg(FrameEntry *fe)
1688 : {
1689 0 : return copyInt32ConstantIntoReg(masm, fe);
1690 : }
1691 :
1692 : JSC::MacroAssembler::RegisterID
1693 0 : FrameState::copyInt32ConstantIntoReg(Assembler &masm, FrameEntry *fe)
1694 : {
1695 0 : JS_ASSERT(fe->data.isConstant());
1696 :
1697 0 : if (fe->isCopy())
1698 0 : fe = fe->copyOf();
1699 :
1700 0 : RegisterID reg = allocReg();
1701 0 : masm.move(Imm32(fe->getValue().toInt32()), reg);
1702 0 : return reg;
1703 : }
1704 :
1705 : JSC::MacroAssembler::RegisterID
1706 1306 : FrameState::ownRegForType(FrameEntry *fe)
1707 : {
1708 1306 : JS_ASSERT(!fe->isTypeKnown());
1709 :
1710 : RegisterID reg;
1711 1306 : if (fe->isCopy()) {
1712 : /* For now, just do an extra move. The reg must be mutable. */
1713 245 : FrameEntry *backing = fe->copyOf();
1714 245 : if (!backing->type.inRegister()) {
1715 157 : JS_ASSERT(backing->type.inMemory());
1716 157 : tempRegForType(backing);
1717 : }
1718 :
1719 245 : if (freeRegs.empty(Registers::AvailRegs)) {
1720 : /* For now... just steal the register that already exists. */
1721 9 : ensureTypeSynced(backing, masm);
1722 9 : reg = backing->type.reg();
1723 9 : backing->type.setMemory();
1724 9 : regstate(reg).forget();
1725 9 : modifyReg(reg);
1726 : } else {
1727 236 : reg = allocReg();
1728 236 : masm.move(backing->type.reg(), reg);
1729 : }
1730 245 : return reg;
1731 : }
1732 :
1733 1061 : if (fe->type.inRegister()) {
1734 1059 : reg = fe->type.reg();
1735 :
1736 : /* Remove ownership of this register. */
1737 1059 : JS_ASSERT(regstate(reg).fe() == fe);
1738 1059 : JS_ASSERT(regstate(reg).type() == RematInfo::TYPE);
1739 1059 : regstate(reg).forget();
1740 1059 : fe->type.setMemory();
1741 1059 : modifyReg(reg);
1742 : } else {
1743 2 : JS_ASSERT(fe->type.inMemory());
1744 2 : reg = allocReg();
1745 2 : masm.loadTypeTag(addressOf(fe), reg);
1746 : }
1747 1061 : return reg;
1748 : }
1749 :
1750 : JSC::MacroAssembler::RegisterID
1751 34951 : FrameState::ownRegForData(FrameEntry *fe)
1752 : {
1753 34951 : JS_ASSERT(!fe->isConstant());
1754 34951 : JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
1755 :
1756 : RegisterID reg;
1757 34951 : if (fe->isCopy()) {
1758 : /* For now, just do an extra move. The reg must be mutable. */
1759 5391 : FrameEntry *backing = fe->copyOf();
1760 5391 : if (!backing->data.inRegister()) {
1761 2876 : JS_ASSERT(backing->data.inMemory());
1762 2876 : tempRegForData(backing);
1763 : }
1764 :
1765 5391 : if (freeRegs.empty(Registers::AvailRegs)) {
1766 : /* For now... just steal the register that already exists. */
1767 1414 : ensureDataSynced(backing, masm);
1768 1414 : reg = backing->data.reg();
1769 1414 : backing->data.setMemory();
1770 1414 : regstate(reg).forget();
1771 1414 : modifyReg(reg);
1772 : } else {
1773 3977 : reg = allocReg();
1774 3977 : masm.move(backing->data.reg(), reg);
1775 : }
1776 5391 : return reg;
1777 : }
1778 :
1779 29560 : if (fe->isCopied())
1780 0 : uncopy(fe);
1781 :
1782 29560 : if (fe->data.inRegister()) {
1783 29147 : reg = fe->data.reg();
1784 : /* Remove ownership of this register. */
1785 29147 : JS_ASSERT(regstate(reg).fe() == fe);
1786 29147 : JS_ASSERT(regstate(reg).type() == RematInfo::DATA);
1787 29147 : regstate(reg).forget();
1788 29147 : fe->data.setMemory();
1789 29147 : modifyReg(reg);
1790 : } else {
1791 413 : JS_ASSERT(fe->data.inMemory());
1792 413 : reg = allocReg();
1793 413 : masm.loadPayload(addressOf(fe), reg);
1794 : }
1795 29560 : return reg;
1796 : }
1797 :
1798 : void
1799 21434 : FrameState::discardFe(FrameEntry *fe)
1800 : {
1801 21434 : forgetEntry(fe);
1802 21434 : fe->type.setMemory();
1803 21434 : fe->data.setMemory();
1804 21434 : fe->clear();
1805 21434 : }
1806 :
1807 : void
1808 8364 : FrameState::pushDouble(FPRegisterID fpreg)
1809 : {
1810 8364 : FrameEntry *fe = rawPush();
1811 8364 : fe->resetUnsynced();
1812 8364 : fe->setType(JSVAL_TYPE_DOUBLE);
1813 8364 : fe->data.setFPRegister(fpreg);
1814 8364 : regstate(fpreg).associate(fe, RematInfo::DATA);
1815 8364 : }
1816 :
1817 : void
1818 0 : FrameState::pushDouble(Address address)
1819 : {
1820 0 : FPRegisterID fpreg = allocFPReg();
1821 0 : masm.loadDouble(address, fpreg);
1822 0 : pushDouble(fpreg);
1823 0 : }
1824 :
1825 : void
1826 1686 : FrameState::ensureDouble(FrameEntry *fe)
1827 : {
1828 1686 : if (fe->isType(JSVAL_TYPE_DOUBLE))
1829 1101 : return;
1830 :
1831 585 : if (fe->isConstant()) {
1832 230 : JS_ASSERT(fe->getValue().isInt32());
1833 230 : Value newValue = DoubleValue(double(fe->getValue().toInt32()));
1834 230 : fe->setConstant(newValue);
1835 230 : return;
1836 : }
1837 :
1838 355 : FrameEntry *backing = fe;
1839 355 : if (fe->isCopy()) {
1840 : /* Forget this entry is a copy. We are converting this entry, not the backing. */
1841 12 : backing = fe->copyOf();
1842 12 : fe->clear();
1843 343 : } else if (fe->isCopied()) {
1844 : /* Sync and forget any copies of this entry. */
1845 38 : for (uint32_t i = fe->trackerIndex() + 1; i < tracker.nentries; i++) {
1846 21 : FrameEntry *nfe = tracker[i];
1847 21 : if (!deadEntry(nfe) && nfe->isCopy() && nfe->copyOf() == fe) {
1848 17 : syncFe(nfe);
1849 17 : nfe->resetSynced();
1850 : }
1851 : }
1852 : }
1853 :
1854 355 : FPRegisterID fpreg = allocFPReg();
1855 :
1856 355 : if (backing->isType(JSVAL_TYPE_INT32)) {
1857 26 : RegisterID data = tempRegForData(backing);
1858 26 : masm.convertInt32ToDouble(data, fpreg);
1859 : } else {
1860 329 : syncFe(backing);
1861 329 : masm.moveInt32OrDouble(addressOf(backing), fpreg);
1862 : }
1863 :
1864 355 : if (fe == backing)
1865 343 : forgetAllRegs(fe);
1866 355 : fe->resetUnsynced();
1867 355 : fe->setType(JSVAL_TYPE_DOUBLE);
1868 355 : fe->data.setFPRegister(fpreg);
1869 355 : regstate(fpreg).associate(fe, RematInfo::DATA);
1870 :
1871 355 : fe->data.unsync();
1872 355 : fe->type.unsync();
1873 : }
1874 :
1875 : void
1876 4 : FrameState::ensureInteger(FrameEntry *fe)
1877 : {
1878 : /*
1879 : * This method is used to revert a previous ensureDouble call made for a
1880 : * branch. The entry is definitely a double, and has had no copies made.
1881 : */
1882 :
1883 4 : if (fe->isConstant()) {
1884 1 : Value newValue = Int32Value(int32_t(fe->getValue().toDouble()));
1885 1 : fe->setConstant(newValue);
1886 1 : return;
1887 : }
1888 :
1889 3 : JS_ASSERT(!fe->isCopy() && !fe->isCopied());
1890 3 : JS_ASSERT_IF(fe->isTypeKnown(), fe->isType(JSVAL_TYPE_DOUBLE));
1891 :
1892 3 : if (!fe->isType(JSVAL_TYPE_DOUBLE)) {
1893 : /*
1894 : * A normal register may have been allocated after calling
1895 : * syncAndForgetEverything.
1896 : */
1897 0 : if (fe->data.inRegister()) {
1898 0 : syncFe(fe);
1899 0 : forgetReg(fe->data.reg());
1900 0 : fe->data.setMemory();
1901 : }
1902 0 : learnType(fe, JSVAL_TYPE_DOUBLE, false);
1903 : }
1904 :
1905 3 : RegisterID reg = allocReg();
1906 3 : FPRegisterID fpreg = tempFPRegForData(fe);
1907 3 : Jump j = masm.branchTruncateDoubleToInt32(fpreg, reg);
1908 3 : j.linkTo(masm.label(), &masm);
1909 :
1910 3 : forgetAllRegs(fe);
1911 3 : fe->resetUnsynced();
1912 3 : fe->setType(JSVAL_TYPE_INT32);
1913 3 : fe->data.setRegister(reg);
1914 3 : regstate(reg).associate(fe, RematInfo::DATA);
1915 :
1916 3 : fe->data.unsync();
1917 3 : fe->type.unsync();
1918 : }
1919 :
1920 : void
1921 0 : FrameState::ensureInMemoryDoubles(Assembler &masm)
1922 : {
1923 0 : JS_ASSERT(!a->parent);
1924 0 : for (uint32_t i = 0; i < tracker.nentries; i++) {
1925 0 : FrameEntry *fe = tracker[i];
1926 0 : if (!deadEntry(fe) && fe->isType(JSVAL_TYPE_DOUBLE) &&
1927 0 : !fe->isCopy() && !fe->isConstant()) {
1928 0 : masm.ensureInMemoryDouble(addressOf(fe));
1929 : }
1930 : }
1931 0 : }
1932 :
1933 : void
1934 3616922 : FrameState::pushCopyOf(FrameEntry *backing)
1935 : {
1936 3616922 : JS_ASSERT(backing->isTracked());
1937 3616922 : FrameEntry *fe = rawPush();
1938 3616922 : fe->resetUnsynced();
1939 3616922 : if (backing->isConstant()) {
1940 852212 : fe->setConstant(backing->getValue());
1941 : } else {
1942 2764710 : if (backing->isCopy())
1943 78759 : backing = backing->copyOf();
1944 2764710 : fe->setCopyOf(backing);
1945 :
1946 : /* Maintain tracker ordering guarantees for copies. */
1947 2764710 : JS_ASSERT(backing->isCopied());
1948 2764710 : if (fe->trackerIndex() < backing->trackerIndex())
1949 204568 : swapInTracker(fe, backing);
1950 : }
1951 3616922 : }
1952 :
1953 : FrameEntry *
1954 50 : FrameState::walkTrackerForUncopy(FrameEntry *original)
1955 : {
1956 50 : uint32_t firstCopy = InvalidIndex;
1957 50 : FrameEntry *bestFe = NULL;
1958 50 : uint32_t ncopies = 0;
1959 204 : for (uint32_t i = original->trackerIndex() + 1; i < tracker.nentries; i++) {
1960 154 : FrameEntry *fe = tracker[i];
1961 154 : if (deadEntry(fe))
1962 15 : continue;
1963 139 : if (fe->isCopy() && fe->copyOf() == original) {
1964 50 : if (firstCopy == InvalidIndex) {
1965 50 : firstCopy = i;
1966 50 : bestFe = fe;
1967 0 : } else if (fe < bestFe) {
1968 0 : bestFe = fe;
1969 : }
1970 50 : ncopies++;
1971 : }
1972 : }
1973 :
1974 50 : if (!ncopies) {
1975 0 : JS_ASSERT(firstCopy == InvalidIndex);
1976 0 : JS_ASSERT(!bestFe);
1977 0 : return NULL;
1978 : }
1979 :
1980 50 : JS_ASSERT(firstCopy != InvalidIndex);
1981 50 : JS_ASSERT(bestFe);
1982 50 : JS_ASSERT_IF(!isTemporary(original), bestFe > original);
1983 :
1984 : /* Mark all extra copies as copies of the new backing index. */
1985 50 : bestFe->setCopyOf(NULL);
1986 50 : if (ncopies > 1) {
1987 0 : for (uint32_t i = firstCopy; i < tracker.nentries; i++) {
1988 0 : FrameEntry *other = tracker[i];
1989 0 : if (deadEntry(other) || other == bestFe)
1990 0 : continue;
1991 :
1992 : /* The original must be tracked before copies. */
1993 0 : JS_ASSERT(other != original);
1994 :
1995 0 : if (!other->isCopy() || other->copyOf() != original)
1996 0 : continue;
1997 :
1998 0 : other->setCopyOf(bestFe);
1999 :
2000 : /*
2001 : * This is safe even though we're mutating during iteration. There
2002 : * are two cases. The first is that both indexes are <= i, and :.
2003 : * will never be observed. The other case is we're placing the
2004 : * other FE such that it will be observed later. Luckily, copyOf()
2005 : * will return != original, so nothing will happen.
2006 : */
2007 0 : if (other->trackerIndex() < bestFe->trackerIndex())
2008 0 : swapInTracker(bestFe, other);
2009 : }
2010 : }
2011 :
2012 50 : return bestFe;
2013 : }
2014 :
2015 : FrameEntry *
2016 499768 : FrameState::walkFrameForUncopy(FrameEntry *original)
2017 : {
2018 499768 : FrameEntry *bestFe = NULL;
2019 499768 : uint32_t ncopies = 0;
2020 :
2021 : /* It's only necessary to visit as many FEs are being tracked. */
2022 499768 : uint32_t maxvisits = tracker.nentries;
2023 :
2024 2579974 : for (FrameEntry *fe = original + 1; fe < a->sp && maxvisits; fe++) {
2025 2080206 : if (!fe->isTracked())
2026 749 : continue;
2027 :
2028 2079457 : maxvisits--;
2029 :
2030 2079457 : if (fe->isCopy() && fe->copyOf() == original) {
2031 499771 : if (!bestFe) {
2032 499768 : bestFe = fe;
2033 499768 : bestFe->setCopyOf(NULL);
2034 : } else {
2035 3 : fe->setCopyOf(bestFe);
2036 3 : if (fe->trackerIndex() < bestFe->trackerIndex())
2037 0 : swapInTracker(bestFe, fe);
2038 : }
2039 499771 : ncopies++;
2040 : }
2041 : }
2042 :
2043 499768 : return bestFe;
2044 : }
2045 :
2046 : FrameEntry *
2047 499818 : FrameState::uncopy(FrameEntry *original)
2048 : {
2049 499818 : JS_ASSERT(original->isCopied());
2050 :
2051 : /*
2052 : * Copies have three critical invariants:
2053 : * 1) The backing store precedes all copies in the tracker.
2054 : * 2) The backing store precedes all copies in the FrameState.
2055 : * 3) The backing store of a copy cannot be popped from the stack
2056 : * while the copy is still live.
2057 : *
2058 : * Maintaining this invariant iteratively is kind of hard, so we choose
2059 : * the "lowest" copy in the frame up-front.
2060 : *
2061 : * For example, if the stack is:
2062 : * [A, B, C, D]
2063 : * And the tracker has:
2064 : * [A, D, C, B]
2065 : *
2066 : * If B, C, and D are copies of A - we will walk the tracker to the end
2067 : * and select B, not D (see bug 583684).
2068 : *
2069 : * Note: |tracker.nentries <= (nslots + nargs)|. However, this walk is
2070 : * sub-optimal if |tracker.nentries - original->trackerIndex() > sp - original|.
2071 : * With large scripts this may be a problem worth investigating. Note that
2072 : * the tracker is walked twice, so we multiply by 2 for pessimism.
2073 : */
2074 : FrameEntry *fe;
2075 499818 : if ((tracker.nentries - original->trackerIndex()) * 2 > uint32_t(a->sp - original))
2076 499768 : fe = walkFrameForUncopy(original);
2077 : else
2078 50 : fe = walkTrackerForUncopy(original);
2079 499818 : JS_ASSERT(fe);
2080 :
2081 : /*
2082 : * Switch the new backing store to the old backing store. During
2083 : * this process we also necessarily make sure the copy can be
2084 : * synced.
2085 : */
2086 499818 : if (!original->isTypeKnown()) {
2087 : /*
2088 : * If the copy is unsynced, and the original is in memory,
2089 : * give the original a register. We do this below too; it's
2090 : * okay if it's spilled.
2091 : */
2092 473508 : if (original->type.inMemory() && !fe->type.synced())
2093 192847 : tempRegForType(original);
2094 473508 : fe->type.inherit(original->type);
2095 473508 : if (fe->type.inRegister())
2096 473496 : regstate(fe->type.reg()).reassociate(fe);
2097 : } else {
2098 26310 : fe->setType(original->getKnownType());
2099 : }
2100 499818 : if (original->isType(JSVAL_TYPE_DOUBLE)) {
2101 100 : if (original->data.inMemory() && !fe->data.synced())
2102 0 : tempFPRegForData(original);
2103 100 : fe->data.inherit(original->data);
2104 100 : if (fe->data.inFPRegister())
2105 100 : regstate(fe->data.fpreg()).reassociate(fe);
2106 : } else {
2107 499718 : if (fe->type.inRegister())
2108 473496 : pinReg(fe->type.reg());
2109 499718 : if (original->data.inMemory() && !fe->data.synced())
2110 198498 : tempRegForData(original);
2111 499718 : if (fe->type.inRegister())
2112 473496 : unpinReg(fe->type.reg());
2113 499718 : fe->data.inherit(original->data);
2114 499718 : if (fe->data.inRegister())
2115 499681 : regstate(fe->data.reg()).reassociate(fe);
2116 : }
2117 :
2118 499818 : return fe;
2119 : }
2120 :
2121 : bool
2122 11956 : FrameState::hasOnlyCopy(FrameEntry *backing, FrameEntry *fe)
2123 : {
2124 11956 : JS_ASSERT(backing->isCopied() && fe->copyOf() == backing);
2125 :
2126 66326 : for (uint32_t i = backing->trackerIndex() + 1; i < tracker.nentries; i++) {
2127 54735 : FrameEntry *nfe = tracker[i];
2128 54735 : if (nfe != fe && !deadEntry(nfe) && nfe->isCopy() && nfe->copyOf() == backing)
2129 365 : return false;
2130 : }
2131 :
2132 11591 : return true;
2133 : }
2134 :
2135 : void
2136 13099 : FrameState::separateBinaryEntries(FrameEntry *lhs, FrameEntry *rhs)
2137 : {
2138 13099 : JS_ASSERT(lhs == a->sp - 2 && rhs == a->sp - 1);
2139 13099 : if (rhs->isCopy() && rhs->copyOf() == lhs) {
2140 0 : syncAndForgetFe(rhs);
2141 0 : syncAndForgetFe(lhs);
2142 0 : uncopy(lhs);
2143 : }
2144 13099 : }
2145 :
2146 : void
2147 287635 : FrameState::storeLocal(uint32_t n, bool popGuaranteed)
2148 : {
2149 287635 : FrameEntry *local = getLocal(n);
2150 :
2151 287635 : if (a->analysis->slotEscapes(entrySlot(local))) {
2152 160333 : JS_ASSERT(local->data.inMemory());
2153 160333 : storeTo(peek(-1), addressOf(local), popGuaranteed);
2154 160333 : return;
2155 : }
2156 :
2157 127302 : storeTop(local);
2158 :
2159 127302 : if (loop)
2160 18660 : local->lastLoop = loop->headOffset();
2161 :
2162 127302 : if (inTryBlock)
2163 2240 : syncFe(local);
2164 : }
2165 :
2166 : void
2167 4099 : FrameState::storeArg(uint32_t n, bool popGuaranteed)
2168 : {
2169 : // Note that args are always immediately synced, because they can be
2170 : // aliased (but not written to) via f.arguments.
2171 4099 : FrameEntry *arg = getArg(n);
2172 :
2173 4099 : if (a->analysis->slotEscapes(entrySlot(arg))) {
2174 1858 : JS_ASSERT(arg->data.inMemory());
2175 1858 : storeTo(peek(-1), addressOf(arg), popGuaranteed);
2176 1858 : return;
2177 : }
2178 :
2179 2241 : storeTop(arg);
2180 :
2181 2241 : if (loop)
2182 373 : arg->lastLoop = loop->headOffset();
2183 :
2184 2241 : syncFe(arg);
2185 : }
2186 :
2187 : void
2188 4649445 : FrameState::forgetEntry(FrameEntry *fe)
2189 : {
2190 4649445 : if (fe->isCopied()) {
2191 499816 : uncopy(fe);
2192 499816 : fe->resetUnsynced();
2193 : } else {
2194 4149629 : forgetAllRegs(fe);
2195 : }
2196 :
2197 4649445 : extraArray[fe - entries].reset();
2198 4649445 : }
2199 :
2200 : void
2201 2728127 : FrameState::storeTop(FrameEntry *target)
2202 : {
2203 2728127 : JS_ASSERT(!isTemporary(target));
2204 :
2205 : /* Detect something like (x = x) which is a no-op. */
2206 2728127 : FrameEntry *top = peek(-1);
2207 2728127 : if (top->isCopy() && top->copyOf() == target) {
2208 8 : JS_ASSERT(target->isCopied());
2209 8 : return;
2210 : }
2211 :
2212 : /*
2213 : * If this is overwriting a known non-double type with another value of the
2214 : * same type, then make sure we keep the type marked as synced after doing
2215 : * the copy.
2216 : */
2217 2728119 : bool wasSynced = target->type.synced();
2218 2728119 : JSValueType oldType = target->isTypeKnown() ? target->getKnownType() : JSVAL_TYPE_UNKNOWN;
2219 2728119 : bool trySyncType = wasSynced && oldType != JSVAL_TYPE_UNKNOWN && oldType != JSVAL_TYPE_DOUBLE;
2220 :
2221 : /* Completely invalidate the local variable. */
2222 2728119 : forgetEntry(target);
2223 2728119 : target->resetUnsynced();
2224 :
2225 : /* Constants are easy to propagate. */
2226 2728119 : if (top->isConstant()) {
2227 713638 : target->clear();
2228 713638 : target->setConstant(top->getValue());
2229 713638 : if (trySyncType && target->isType(oldType))
2230 172 : target->type.sync();
2231 713638 : return;
2232 : }
2233 :
2234 : /*
2235 : * When dealing with copies, there are three important invariants:
2236 : *
2237 : * 1) The backing store precedes all copies in the tracker.
2238 : * 2) The backing store precedes all copies in the FrameState.
2239 : * 2) The backing store of a local is never a stack slot, UNLESS the local
2240 : * variable itself is a stack slot (blocks) that precedes the stack
2241 : * slot.
2242 : *
2243 : * If the top is a copy, and the second condition holds true, the local
2244 : * can be rewritten as a copy of the original backing slot. If the first
2245 : * condition does not hold, force it to hold by swapping in-place.
2246 : */
2247 2014481 : FrameEntry *backing = top;
2248 2014481 : if (top->isCopy()) {
2249 1358258 : backing = top->copyOf();
2250 1358258 : JS_ASSERT(backing->trackerIndex() < top->trackerIndex());
2251 :
2252 1358258 : if (backing < target || isTemporary(backing)) {
2253 : /* local.idx < backing.idx means local cannot be a copy yet */
2254 57630 : if (target->trackerIndex() < backing->trackerIndex())
2255 2882 : swapInTracker(backing, target);
2256 57630 : target->setCopyOf(backing);
2257 57630 : if (trySyncType && target->isType(oldType))
2258 37 : target->type.sync();
2259 57630 : return;
2260 : }
2261 :
2262 : /*
2263 : * If control flow lands here, then there was a bytecode sequence like
2264 : *
2265 : * ENTERBLOCK 2
2266 : * GETLOCAL 1
2267 : * SETLOCAL 0
2268 : *
2269 : * The problem is slot N can't be backed by M if M could be popped
2270 : * before N. We want a guarantee that when we pop M, even if it was
2271 : * copied, it has no outstanding copies.
2272 : *
2273 : * Because of |let| expressions, it's kind of hard to really know
2274 : * whether a region on the stack will be popped all at once. Bleh!
2275 : *
2276 : * This should be rare except in browser code (and maybe even then),
2277 : * but even so there's a quick workaround. We take all copies of the
2278 : * backing fe, and redirect them to be copies of the destination.
2279 : */
2280 5576501 : for (uint32_t i = backing->trackerIndex() + 1; i < tracker.nentries; i++) {
2281 4275873 : FrameEntry *fe = tracker[i];
2282 4275873 : if (deadEntry(fe))
2283 120549 : continue;
2284 4155324 : if (fe->isCopy() && fe->copyOf() == backing)
2285 1300647 : fe->setCopyOf(target);
2286 : }
2287 : }
2288 :
2289 : /*
2290 : * This is valid from the top->isCopy() path because we're guaranteed a
2291 : * consistent ordering - all copies of |backing| are tracked after
2292 : * |backing|. Transitively, only one swap is needed.
2293 : */
2294 1956851 : if (backing->trackerIndex() < target->trackerIndex())
2295 129384 : swapInTracker(backing, target);
2296 :
2297 1956851 : if (backing->isType(JSVAL_TYPE_DOUBLE)) {
2298 2916 : FPRegisterID fpreg = tempFPRegForData(backing);
2299 2916 : target->setType(JSVAL_TYPE_DOUBLE);
2300 2916 : target->data.setFPRegister(fpreg);
2301 2916 : regstate(fpreg).reassociate(target);
2302 : } else {
2303 : /*
2304 : * Move the backing store down - we spill registers here, but we could be
2305 : * smarter and re-use the type reg. If we need registers for both the type
2306 : * and data in the backing, make sure we keep the other components pinned.
2307 : * There is nothing else to keep us from evicting the backing's registers.
2308 : */
2309 1953935 : if (backing->type.inRegister())
2310 1606783 : pinReg(backing->type.reg());
2311 1953935 : RegisterID reg = tempRegForData(backing);
2312 1953935 : if (backing->type.inRegister())
2313 1606783 : unpinReg(backing->type.reg());
2314 1953935 : target->data.setRegister(reg);
2315 1953935 : regstate(reg).reassociate(target);
2316 :
2317 1953935 : if (backing->isTypeKnown()) {
2318 123720 : target->setType(backing->getKnownType());
2319 : } else {
2320 1830215 : pinReg(reg);
2321 1830215 : RegisterID typeReg = tempRegForType(backing);
2322 1830215 : unpinReg(reg);
2323 1830215 : target->type.setRegister(typeReg);
2324 1830215 : regstate(typeReg).reassociate(target);
2325 : }
2326 : }
2327 :
2328 1956851 : backing->setCopyOf(target);
2329 1956851 : JS_ASSERT(top->copyOf() == target);
2330 :
2331 1956851 : if (trySyncType && target->isType(oldType))
2332 43589 : target->type.sync();
2333 : }
2334 :
2335 : void
2336 710008 : FrameState::shimmy(uint32_t n)
2337 : {
2338 710008 : JS_ASSERT(a->sp - n >= a->spBase);
2339 710008 : int32_t depth = 0 - int32_t(n);
2340 710008 : storeTop(peek(depth - 1));
2341 710008 : popn(n);
2342 710008 : }
2343 :
2344 : void
2345 1888576 : FrameState::shift(int32_t n)
2346 : {
2347 1888576 : JS_ASSERT(n < 0);
2348 1888576 : JS_ASSERT(a->sp + n - 1 >= a->spBase);
2349 1888576 : storeTop(peek(n - 1));
2350 1888576 : pop();
2351 1888576 : }
2352 :
2353 : void
2354 0 : FrameState::swap()
2355 : {
2356 : // A B
2357 :
2358 0 : dupAt(-2);
2359 : // A B A
2360 :
2361 0 : dupAt(-2);
2362 : // A B A B
2363 :
2364 0 : shift(-3);
2365 : // B B A
2366 :
2367 0 : shimmy(1);
2368 : // B A
2369 0 : }
2370 :
2371 : void
2372 393 : FrameState::forgetKnownDouble(FrameEntry *fe)
2373 : {
2374 : /*
2375 : * Forget all information indicating fe is a double, so we can use GPRs for its
2376 : * contents. We currently need to do this in order to use the entry in MICs/PICs
2377 : * or to construct its ValueRemat. :FIXME: this needs to get fixed.
2378 : */
2379 393 : JS_ASSERT(!fe->isConstant() && fe->isType(JSVAL_TYPE_DOUBLE));
2380 :
2381 393 : RegisterID typeReg = allocReg();
2382 393 : RegisterID dataReg = allocReg();
2383 :
2384 : /* Copy into a different FP register, as breakDouble can modify fpreg. */
2385 393 : FPRegisterID fpreg = allocFPReg();
2386 393 : masm.moveDouble(tempFPRegForData(fe), fpreg);
2387 393 : masm.breakDouble(fpreg, typeReg, dataReg);
2388 :
2389 393 : forgetAllRegs(fe);
2390 393 : fe->resetUnsynced();
2391 393 : fe->clear();
2392 :
2393 393 : regstate(typeReg).associate(fe, RematInfo::TYPE);
2394 393 : regstate(dataReg).associate(fe, RematInfo::DATA);
2395 393 : fe->type.setRegister(typeReg);
2396 393 : fe->data.setRegister(dataReg);
2397 393 : freeReg(fpreg);
2398 393 : }
2399 :
2400 : void
2401 105468 : FrameState::pinEntry(FrameEntry *fe, ValueRemat &vr, bool breakDouble)
2402 : {
2403 105468 : if (breakDouble && !fe->isConstant() && fe->isType(JSVAL_TYPE_DOUBLE))
2404 384 : forgetKnownDouble(fe);
2405 :
2406 105468 : if (fe->isConstant()) {
2407 23338 : vr = ValueRemat::FromConstant(fe->getValue());
2408 82130 : } else if (fe->isType(JSVAL_TYPE_DOUBLE)) {
2409 1384 : FPRegisterID fpreg = tempFPRegForData(fe);
2410 1384 : pinReg(fpreg);
2411 1384 : vr = ValueRemat::FromFPRegister(fpreg);
2412 : } else {
2413 : // Pin the type register so it can't spill.
2414 80746 : MaybeRegisterID maybePinnedType = maybePinType(fe);
2415 :
2416 : // Get and pin the data register.
2417 80746 : RegisterID dataReg = tempRegForData(fe);
2418 80746 : pinReg(dataReg);
2419 :
2420 80746 : if (fe->isTypeKnown()) {
2421 31068 : vr = ValueRemat::FromKnownType(fe->getKnownType(), dataReg);
2422 : } else {
2423 : // The type might not be loaded yet, so unpin for simplicity.
2424 49678 : maybeUnpinReg(maybePinnedType);
2425 :
2426 49678 : vr = ValueRemat::FromRegisters(tempRegForType(fe), dataReg);
2427 49678 : pinReg(vr.typeReg());
2428 : }
2429 : }
2430 :
2431 : // Set these bits last, since allocation could have caused a sync.
2432 105468 : vr.isDataSynced = fe->data.synced();
2433 105468 : vr.isTypeSynced = fe->type.synced();
2434 105468 : }
2435 :
2436 : void
2437 79326 : FrameState::unpinEntry(const ValueRemat &vr)
2438 : {
2439 79326 : if (vr.isFPRegister()) {
2440 1384 : unpinReg(vr.fpReg());
2441 77942 : } else if (!vr.isConstant()) {
2442 61473 : if (!vr.isTypeKnown())
2443 33667 : unpinReg(vr.typeReg());
2444 61473 : unpinReg(vr.dataReg());
2445 : }
2446 79326 : }
2447 :
2448 : void
2449 26142 : FrameState::ensureValueSynced(Assembler &masm, FrameEntry *fe, const ValueRemat &vr)
2450 : {
2451 : #if defined JS_PUNBOX64
2452 : if (!vr.isDataSynced || !vr.isTypeSynced)
2453 : masm.storeValue(vr, addressOf(fe));
2454 : #elif defined JS_NUNBOX32
2455 26142 : if (vr.isConstant() || vr.isFPRegister()) {
2456 6869 : if (!vr.isDataSynced || !vr.isTypeSynced)
2457 6855 : masm.storeValue(vr.value(), addressOf(fe));
2458 : } else {
2459 19273 : if (!vr.isDataSynced)
2460 16879 : masm.storePayload(vr.dataReg(), addressOf(fe));
2461 19273 : if (!vr.isTypeSynced) {
2462 16776 : if (vr.isTypeKnown())
2463 3243 : masm.storeTypeTag(ImmType(vr.knownType()), addressOf(fe));
2464 : else
2465 13533 : masm.storeTypeTag(vr.typeReg(), addressOf(fe));
2466 : }
2467 : }
2468 : #endif
2469 26142 : }
2470 :
2471 : static inline bool
2472 2567268 : AllocHelper(RematInfo &info, MaybeRegisterID &maybe)
2473 : {
2474 2567268 : if (info.inRegister()) {
2475 1166078 : maybe = info.reg();
2476 1166078 : return true;
2477 : }
2478 1401190 : return false;
2479 : }
2480 :
2481 : void
2482 89 : FrameState::allocForSameBinary(FrameEntry *fe, JSOp op, BinaryAlloc &alloc)
2483 : {
2484 89 : alloc.rhsNeedsRemat = false;
2485 :
2486 89 : if (!fe->isTypeKnown()) {
2487 61 : alloc.lhsType = tempRegForType(fe);
2488 61 : pinReg(alloc.lhsType.reg());
2489 : }
2490 :
2491 89 : alloc.lhsData = tempRegForData(fe);
2492 :
2493 89 : if (!freeRegs.empty(Registers::AvailRegs)) {
2494 69 : alloc.result = allocReg();
2495 69 : masm.move(alloc.lhsData.reg(), alloc.result);
2496 69 : alloc.lhsNeedsRemat = false;
2497 : } else {
2498 20 : alloc.result = alloc.lhsData.reg();
2499 20 : takeReg(alloc.result);
2500 20 : alloc.lhsNeedsRemat = true;
2501 : }
2502 :
2503 89 : if (alloc.lhsType.isSet())
2504 61 : unpinReg(alloc.lhsType.reg());
2505 :
2506 89 : alloc.lhsFP = alloc.rhsFP = allocFPReg();
2507 89 : }
2508 :
2509 : void
2510 150465 : FrameState::ensureFullRegs(FrameEntry *fe, MaybeRegisterID *type, MaybeRegisterID *data)
2511 : {
2512 150465 : fe = fe->isCopy() ? fe->copyOf() : fe;
2513 :
2514 150465 : JS_ASSERT(!data->isSet() && !type->isSet());
2515 150465 : if (!fe->type.inMemory()) {
2516 106208 : if (fe->type.inRegister())
2517 68526 : *type = fe->type.reg();
2518 106208 : if (fe->data.isConstant())
2519 0 : return;
2520 106208 : if (fe->data.inRegister()) {
2521 102457 : *data = fe->data.reg();
2522 102457 : return;
2523 : }
2524 3751 : if (fe->type.inRegister())
2525 843 : pinReg(fe->type.reg());
2526 3751 : *data = tempRegForData(fe);
2527 3751 : if (fe->type.inRegister())
2528 843 : unpinReg(fe->type.reg());
2529 44257 : } else if (!fe->data.inMemory()) {
2530 1114 : if (fe->data.inRegister())
2531 1114 : *data = fe->data.reg();
2532 1114 : if (fe->type.isConstant())
2533 0 : return;
2534 1114 : if (fe->type.inRegister()) {
2535 0 : *type = fe->type.reg();
2536 0 : return;
2537 : }
2538 1114 : if (fe->data.inRegister())
2539 1114 : pinReg(fe->data.reg());
2540 1114 : *type = tempRegForType(fe);
2541 1114 : if (fe->data.inRegister())
2542 1114 : unpinReg(fe->data.reg());
2543 : } else {
2544 43143 : *data = tempRegForData(fe);
2545 43143 : pinReg(data->reg());
2546 43143 : *type = tempRegForType(fe);
2547 43143 : unpinReg(data->reg());
2548 : }
2549 : }
2550 :
2551 : inline bool
2552 53026 : FrameState::binaryEntryLive(FrameEntry *fe) const
2553 : {
2554 : /*
2555 : * Compute whether fe is live after the binary operation performed at the current
2556 : * bytecode. This is similar to variableLive except that it returns false for the
2557 : * top two stack entries and special cases LOCALINC/ARGINC and friends, which fuse
2558 : * a binary operation before writing over the local/arg.
2559 : */
2560 53026 : JS_ASSERT(cx->typeInferenceEnabled());
2561 :
2562 53026 : if (deadEntry(fe, 2))
2563 32368 : return false;
2564 :
2565 20658 : switch (JSOp(*a->PC)) {
2566 : case JSOP_INCLOCAL:
2567 : case JSOP_DECLOCAL:
2568 : case JSOP_LOCALINC:
2569 : case JSOP_LOCALDEC:
2570 11265 : if (fe - a->locals == (int) GET_SLOTNO(a->PC))
2571 10881 : return false;
2572 : case JSOP_INCARG:
2573 : case JSOP_DECARG:
2574 : case JSOP_ARGINC:
2575 : case JSOP_ARGDEC:
2576 581 : if (fe - a->args == (int) GET_SLOTNO(a->PC))
2577 129 : return false;
2578 : default:;
2579 : }
2580 :
2581 9648 : JS_ASSERT(fe != a->callee_);
2582 :
2583 : /* Arguments are always treated as live within inline frames, see bestEvictReg. */
2584 9648 : if (a->parent && fe < a->locals)
2585 89 : return true;
2586 :
2587 : /* Caller must check that no copies are invalidated by rewriting the entry. */
2588 9559 : return fe >= a->spBase || variableLive(fe, a->PC);
2589 : }
2590 :
2591 : void
2592 641817 : FrameState::allocForBinary(FrameEntry *lhs, FrameEntry *rhs, JSOp op, BinaryAlloc &alloc,
2593 : bool needsResult)
2594 : {
2595 641817 : FrameEntry *backingLeft = lhs;
2596 641817 : FrameEntry *backingRight = rhs;
2597 :
2598 641817 : if (backingLeft->isCopy())
2599 242832 : backingLeft = backingLeft->copyOf();
2600 641817 : if (backingRight->isCopy())
2601 8645 : backingRight = backingRight->copyOf();
2602 :
2603 : /*
2604 : * For each remat piece of both FEs, if a register is assigned, get it now
2605 : * and pin it. This is safe - constants and known types will be avoided.
2606 : */
2607 641817 : if (AllocHelper(backingLeft->type, alloc.lhsType))
2608 87849 : pinReg(alloc.lhsType.reg());
2609 641817 : if (AllocHelper(backingLeft->data, alloc.lhsData))
2610 404242 : pinReg(alloc.lhsData.reg());
2611 641817 : if (AllocHelper(backingRight->type, alloc.rhsType))
2612 332239 : pinReg(alloc.rhsType.reg());
2613 641817 : if (AllocHelper(backingRight->data, alloc.rhsData))
2614 341748 : pinReg(alloc.rhsData.reg());
2615 :
2616 : /* For each type without a register, give it a register if needed. */
2617 641817 : if (!alloc.lhsType.isSet() && backingLeft->type.inMemory()) {
2618 525872 : alloc.lhsType = tempRegForType(lhs);
2619 525872 : pinReg(alloc.lhsType.reg());
2620 : }
2621 641817 : if (!alloc.rhsType.isSet() && backingRight->type.inMemory()) {
2622 11760 : alloc.rhsType = tempRegForType(rhs);
2623 11760 : pinReg(alloc.rhsType.reg());
2624 : }
2625 :
2626 : /*
2627 : * Allocate floating point registers. These are temporaries with no pre-existing data;
2628 : * floating point registers are only allocated for known doubles, and BinaryAlloc is not
2629 : * used for such operations.
2630 : */
2631 641817 : JS_ASSERT(!backingLeft->isType(JSVAL_TYPE_DOUBLE));
2632 641817 : JS_ASSERT(!backingRight->isType(JSVAL_TYPE_DOUBLE));
2633 641817 : alloc.lhsFP = allocFPReg();
2634 641817 : alloc.rhsFP = allocFPReg();
2635 :
2636 : bool commu;
2637 641817 : switch (op) {
2638 : case JSOP_EQ:
2639 : case JSOP_GT:
2640 : case JSOP_GE:
2641 : case JSOP_LT:
2642 : case JSOP_LE:
2643 : /* fall through */
2644 : case JSOP_ADD:
2645 : case JSOP_MUL:
2646 : case JSOP_SUB:
2647 641817 : commu = true;
2648 641817 : break;
2649 :
2650 : case JSOP_DIV:
2651 0 : commu = false;
2652 0 : break;
2653 :
2654 : default:
2655 0 : JS_NOT_REACHED("unknown op");
2656 : return;
2657 : }
2658 :
2659 : /*
2660 : * Allocate data registers. If the op is not commutative, the LHS
2661 : * _must_ be in a register.
2662 : */
2663 641817 : JS_ASSERT_IF(lhs->isConstant(), !rhs->isConstant());
2664 641817 : JS_ASSERT_IF(rhs->isConstant(), !lhs->isConstant());
2665 :
2666 641817 : if (!alloc.lhsData.isSet()) {
2667 237575 : if (backingLeft->data.inMemory()) {
2668 236008 : alloc.lhsData = tempRegForData(lhs);
2669 236008 : pinReg(alloc.lhsData.reg());
2670 1567 : } else if (!commu) {
2671 0 : JS_ASSERT(lhs->isConstant());
2672 0 : alloc.lhsData = allocReg();
2673 0 : alloc.extraFree = alloc.lhsData;
2674 0 : masm.move(Imm32(lhs->getValue().toInt32()), alloc.lhsData.reg());
2675 : }
2676 : }
2677 641817 : if (!alloc.rhsData.isSet() && backingRight->data.inMemory()) {
2678 11921 : alloc.rhsData = tempRegForData(rhs);
2679 11921 : pinReg(alloc.rhsData.reg());
2680 : }
2681 :
2682 641817 : alloc.lhsNeedsRemat = false;
2683 641817 : alloc.rhsNeedsRemat = false;
2684 641817 : alloc.resultHasRhs = false;
2685 641817 : alloc.undoResult = false;
2686 :
2687 641817 : if (!needsResult)
2688 44664 : goto skip;
2689 :
2690 : /*
2691 : * Now a result register is needed. It must contain a mutable copy of the
2692 : * LHS. For commutative operations, we can opt to use the RHS instead. At
2693 : * this point, if for some reason either must be in a register, that has
2694 : * already been guaranteed at this point.
2695 : */
2696 :
2697 : /*
2698 : * Try to reuse operand registers without syncing for ADD and constant SUB,
2699 : * so long as the backing for the operand is dead.
2700 : */
2701 743780 : if (cx->typeInferenceEnabled() &&
2702 107225 : backingLeft->data.inRegister() && !binaryEntryLive(backingLeft) &&
2703 27446 : (op == JSOP_ADD || (op == JSOP_SUB && backingRight->isConstant())) &&
2704 11956 : (lhs == backingLeft || hasOnlyCopy(backingLeft, lhs))) {
2705 40964 : alloc.result = backingLeft->data.reg();
2706 40964 : alloc.undoResult = true;
2707 40964 : alloc.resultHasRhs = false;
2708 40964 : goto skip;
2709 : }
2710 :
2711 556189 : if (cx->typeInferenceEnabled())
2712 13235 : evictDeadEntries(true);
2713 :
2714 556189 : if (!freeRegs.empty(Registers::AvailRegs)) {
2715 : /* Free reg - just grab it. */
2716 545664 : alloc.result = allocReg();
2717 545664 : if (!alloc.lhsData.isSet()) {
2718 1362 : JS_ASSERT(alloc.rhsData.isSet());
2719 1362 : JS_ASSERT(commu);
2720 1362 : masm.move(alloc.rhsData.reg(), alloc.result);
2721 1362 : alloc.resultHasRhs = true;
2722 : } else {
2723 544302 : masm.move(alloc.lhsData.reg(), alloc.result);
2724 544302 : alloc.resultHasRhs = false;
2725 : }
2726 10525 : } else if (cx->typeInferenceEnabled()) {
2727 : /* No free regs. Evict a register or reuse one of the operands. */
2728 1150 : bool leftInReg = backingLeft->data.inRegister();
2729 1150 : bool rightInReg = backingRight->data.inRegister();
2730 :
2731 : /* If the LHS/RHS types are in registers, don't use them for the result. */
2732 1150 : uint32_t mask = Registers::AvailRegs;
2733 1150 : if (backingLeft->type.inRegister())
2734 781 : mask &= ~Registers::maskReg(backingLeft->type.reg());
2735 1150 : if (backingRight->type.inRegister())
2736 679 : mask &= ~Registers::maskReg(backingRight->type.reg());
2737 :
2738 1150 : RegisterID result = bestEvictReg(mask, true).reg();
2739 1150 : if (!commu && rightInReg && backingRight->data.reg() == result) {
2740 : /* Can't put the result in the RHS for non-commutative operations. */
2741 0 : alloc.result = allocReg();
2742 0 : masm.move(alloc.lhsData.reg(), alloc.result);
2743 : } else {
2744 1150 : alloc.result = result;
2745 1150 : if (leftInReg && result == backingLeft->data.reg()) {
2746 241 : alloc.lhsNeedsRemat = true;
2747 241 : unpinReg(result);
2748 241 : takeReg(result);
2749 909 : } else if (rightInReg && result == backingRight->data.reg()) {
2750 456 : alloc.rhsNeedsRemat = true;
2751 456 : alloc.resultHasRhs = true;
2752 456 : unpinReg(result);
2753 456 : takeReg(result);
2754 : } else {
2755 453 : JS_ASSERT(!regstate(result).isPinned());
2756 453 : takeReg(result);
2757 453 : if (leftInReg) {
2758 393 : masm.move(alloc.lhsData.reg(), result);
2759 : } else {
2760 60 : masm.move(alloc.rhsData.reg(), result);
2761 60 : alloc.resultHasRhs = true;
2762 : }
2763 : }
2764 : }
2765 : } else {
2766 : /*
2767 : * No free regs. Find a good candidate to re-use. Best candidates don't
2768 : * require syncs on the inline path.
2769 : */
2770 9375 : bool leftInReg = backingLeft->data.inRegister();
2771 9375 : bool rightInReg = backingRight->data.inRegister();
2772 9375 : bool leftSynced = backingLeft->data.synced();
2773 9375 : bool rightSynced = backingRight->data.synced();
2774 9375 : if (!commu || (leftInReg && (leftSynced || (!rightInReg || !rightSynced)))) {
2775 8923 : JS_ASSERT(backingLeft->data.inRegister() || !commu);
2776 26769 : JS_ASSERT_IF(backingLeft->data.inRegister(),
2777 26769 : backingLeft->data.reg() == alloc.lhsData.reg());
2778 8923 : if (backingLeft->data.inRegister()) {
2779 8923 : alloc.result = backingLeft->data.reg();
2780 8923 : unpinReg(alloc.result);
2781 8923 : takeReg(alloc.result);
2782 8923 : alloc.lhsNeedsRemat = true;
2783 : } else {
2784 : /* For now, just spill... */
2785 0 : alloc.result = allocReg();
2786 0 : masm.move(alloc.lhsData.reg(), alloc.result);
2787 : }
2788 8923 : alloc.resultHasRhs = false;
2789 : } else {
2790 452 : JS_ASSERT(commu);
2791 452 : JS_ASSERT(!leftInReg || (rightInReg && rightSynced));
2792 452 : alloc.result = backingRight->data.reg();
2793 452 : unpinReg(alloc.result);
2794 452 : takeReg(alloc.result);
2795 452 : alloc.resultHasRhs = true;
2796 452 : alloc.rhsNeedsRemat = true;
2797 : }
2798 : }
2799 :
2800 : skip:
2801 : /* Unpin everything that was pinned. */
2802 641817 : if (backingLeft->type.inRegister())
2803 613721 : unpinReg(backingLeft->type.reg());
2804 641817 : if (backingRight->type.inRegister())
2805 343999 : unpinReg(backingRight->type.reg());
2806 641817 : if (backingLeft->data.inRegister())
2807 631086 : unpinReg(backingLeft->data.reg());
2808 641817 : if (backingRight->data.inRegister())
2809 352761 : unpinReg(backingRight->data.reg());
2810 641817 : }
2811 :
2812 : void
2813 590874 : FrameState::rematBinary(FrameEntry *lhs, FrameEntry *rhs, const BinaryAlloc &alloc, Assembler &masm)
2814 : {
2815 590874 : if (alloc.rhsNeedsRemat)
2816 1130 : masm.loadPayload(addressForDataRemat(rhs), alloc.rhsData.reg());
2817 590874 : if (alloc.lhsNeedsRemat)
2818 9550 : masm.loadPayload(addressForDataRemat(lhs), alloc.lhsData.reg());
2819 590874 : }
2820 :
2821 : MaybeRegisterID
2822 73166 : FrameState::maybePinData(FrameEntry *fe)
2823 : {
2824 73166 : fe = fe->isCopy() ? fe->copyOf() : fe;
2825 73166 : if (fe->data.inRegister()) {
2826 37663 : pinReg(fe->data.reg());
2827 37663 : return fe->data.reg();
2828 : }
2829 35503 : return MaybeRegisterID();
2830 : }
2831 :
2832 : MaybeRegisterID
2833 124959 : FrameState::maybePinType(FrameEntry *fe)
2834 : {
2835 124959 : fe = fe->isCopy() ? fe->copyOf() : fe;
2836 124959 : if (fe->type.inRegister()) {
2837 50174 : pinReg(fe->type.reg());
2838 50174 : return fe->type.reg();
2839 : }
2840 74785 : return MaybeRegisterID();
2841 : }
2842 :
2843 : void
2844 133518 : FrameState::maybeUnpinReg(MaybeRegisterID reg)
2845 : {
2846 133518 : if (reg.isSet())
2847 71951 : unpinReg(reg.reg());
2848 133518 : }
2849 :
2850 : uint32_t
2851 2429 : FrameState::allocTemporary()
2852 : {
2853 2429 : if (temporariesTop == temporaries + TEMPORARY_LIMIT)
2854 0 : return UINT32_MAX;
2855 2429 : FrameEntry *fe = temporariesTop++;
2856 2429 : fe->lastLoop = 0;
2857 2429 : fe->temporary = true;
2858 2429 : return fe - temporaries;
2859 : }
2860 :
2861 : void
2862 33590 : FrameState::clearTemporaries()
2863 : {
2864 33590 : JS_ASSERT(!a->parent);
2865 :
2866 36019 : for (FrameEntry *fe = temporaries; fe < temporariesTop; fe++) {
2867 2429 : if (!fe->isTracked())
2868 1567 : continue;
2869 862 : if (fe->isCopied())
2870 1 : uncopy(fe);
2871 862 : forgetAllRegs(fe);
2872 862 : fe->resetSynced();
2873 : }
2874 :
2875 33590 : temporariesTop = temporaries;
2876 33590 : }
2877 :
2878 : Vector<TemporaryCopy> *
2879 31293 : FrameState::getTemporaryCopies(Uses uses)
2880 : {
2881 : /* :XXX: handle OOM */
2882 31293 : Vector<TemporaryCopy> *res = NULL;
2883 :
2884 45351 : for (FrameEntry *fe = temporaries; fe < temporariesTop; fe++) {
2885 14058 : if (!fe->isTracked())
2886 3594 : continue;
2887 10464 : if (fe->isCopied()) {
2888 555 : for (uint32_t i = fe->trackerIndex() + 1; i < tracker.nentries; i++) {
2889 446 : FrameEntry *nfe = tracker[i];
2890 446 : if (!deadEntry(nfe, uses.nuses) && nfe->isCopy() && nfe->copyOf() == fe) {
2891 110 : if (!res)
2892 90 : res = OffTheBooks::new_< Vector<TemporaryCopy> >(cx);
2893 110 : res->append(TemporaryCopy(addressOf(nfe), addressOf(fe)));
2894 : }
2895 : }
2896 : }
2897 : }
2898 :
2899 31293 : return res;
2900 : }
|