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 134066 : 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 134066 : loop(NULL), inTryBlock(false)
57 : {
58 134066 : }
59 :
60 268132 : FrameState::~FrameState()
61 : {
62 396989 : while (a) {
63 128857 : ActiveFrame *parent = a->parent;
64 128857 : a->script->analysis()->clearAllocations();
65 128857 : cx->free_(a);
66 128857 : a = parent;
67 : }
68 134066 : cx->free_(entries);
69 134066 : }
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 131852 : FrameState::pushActiveFrame(JSScript *script, uint32_t argc)
90 : {
91 131852 : if (!a) {
92 128857 : this->nentries = analyze::TotalSlots(script) + (script->nslots - script->nfixed) +
93 128857 : StackSpace::STACK_JIT_EXTRA - VALUES_PER_STACK_FRAME;
94 : size_t totalBytes = sizeof(FrameEntry) * nentries + // entries[]
95 : sizeof(FrameEntry *) * nentries + // tracker.entries
96 128857 : sizeof(StackEntryExtra) * nentries; // extraArray
97 128857 : uint8_t *cursor = (uint8_t *)OffTheBooks::calloc_(totalBytes);
98 128857 : if (!cursor)
99 0 : return false;
100 :
101 128857 : this->entries = (FrameEntry *) cursor;
102 128857 : cursor += sizeof(FrameEntry) * nentries;
103 :
104 128857 : this->tracker.entries = (FrameEntry **)cursor;
105 128857 : cursor += sizeof(FrameEntry *) * nentries;
106 :
107 128857 : this->extraArray = (StackEntryExtra *)cursor;
108 128857 : cursor += sizeof(StackEntryExtra) * nentries;
109 :
110 128857 : JS_ASSERT(reinterpret_cast<uint8_t *>(this->entries) + totalBytes == cursor);
111 :
112 : #if defined JS_NUNBOX32
113 128857 : if (!reifier.init(cx, *this, nentries))
114 0 : return false;
115 : #endif
116 :
117 128857 : this->temporaries = this->temporariesTop = this->entries + nentries - TEMPORARY_LIMIT;
118 : }
119 :
120 : /* We should have already checked that argc == nargs */
121 131852 : JS_ASSERT_IF(a, argc == script->function()->nargs);
122 :
123 131852 : ActiveFrame *newa = OffTheBooks::new_<ActiveFrame>();
124 131852 : if (!newa)
125 0 : return false;
126 :
127 131852 : newa->parent = a;
128 131852 : newa->depth = a ? (totalDepth() + VALUES_PER_STACK_FRAME) : 0;
129 :
130 131852 : newa->script = script;
131 131852 : newa->PC = script->code;
132 131852 : 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 131852 : FrameEntry *entriesStart = a ? a->sp - (argc + 2) : entries;
139 131852 : newa->callee_ = entriesStart + analyze::CalleeSlot();
140 131852 : newa->this_ = entriesStart + analyze::ThisSlot();
141 131852 : newa->args = entriesStart + analyze::ArgSlot(0);
142 131852 : newa->locals = entriesStart + analyze::LocalSlot(script, 0);
143 131852 : newa->spBase = entriesStart + analyze::TotalSlots(script);
144 131852 : newa->sp = newa->spBase;
145 :
146 131852 : this->a = newa;
147 :
148 131852 : 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 728222 : FrameState::takeReg(AnyRegisterID reg)
187 : {
188 728222 : modifyReg(reg);
189 728222 : if (freeRegs.hasReg(reg)) {
190 703024 : freeRegs.takeReg(reg);
191 703024 : JS_ASSERT(!regstate(reg).usedBy());
192 : } else {
193 25198 : JS_ASSERT(regstate(reg).fe());
194 25198 : evictReg(reg);
195 : }
196 728222 : }
197 :
198 : #ifdef DEBUG
199 : const char *
200 215070 : FrameState::entryName(const FrameEntry *fe) const
201 : {
202 : static char bufs[4][50];
203 : static unsigned which = 0;
204 215070 : which = (which + 1) & 3;
205 215070 : char *buf = bufs[which];
206 :
207 215070 : if (isTemporary(fe)) {
208 5366 : JS_snprintf(buf, 50, "temp%d", fe - temporaries);
209 5366 : return buf;
210 : }
211 :
212 209704 : if (fe < a->callee_)
213 1153 : return "parent";
214 :
215 208551 : JS_ASSERT(fe >= a->callee_ && fe < a->sp);
216 :
217 208551 : if (fe == a->callee_)
218 285 : return "callee";
219 208266 : if (fe == a->this_)
220 2102 : return "'this'";
221 :
222 206164 : if (isArg(fe))
223 15463 : JS_snprintf(buf, 50, "arg%d", fe - a->args);
224 190701 : else if (isLocal(fe))
225 64931 : JS_snprintf(buf, 50, "local%d", fe - a->locals);
226 : else
227 125770 : JS_snprintf(buf, 50, "slot%d", fe - a->spBase);
228 206164 : return buf;
229 : }
230 : #endif
231 :
232 : void
233 154802 : FrameState::evictReg(AnyRegisterID reg)
234 : {
235 154802 : FrameEntry *fe = regstate(reg).fe();
236 :
237 154802 : JaegerSpew(JSpew_Regalloc, "evicting %s from %s\n", entryName(fe), reg.name());
238 :
239 154802 : if (regstate(reg).type() == RematInfo::TYPE) {
240 63983 : syncType(fe);
241 63983 : fe->type.setMemory();
242 90819 : } else if (reg.isReg()) {
243 90807 : syncData(fe);
244 90807 : fe->data.setMemory();
245 : } else {
246 12 : syncFe(fe);
247 12 : fe->data.setMemory();
248 : }
249 :
250 154802 : regstate(reg).forget();
251 154802 : }
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 174135 : FrameState::evictSomeReg(uint32_t mask)
433 : {
434 174135 : JS_ASSERT(!freeRegs.hasRegInMask(mask));
435 :
436 174135 : 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 119682 : JS_ASSERT((mask & ~Registers::AvailRegs) == 0);
453 :
454 119682 : MaybeRegisterID fallback;
455 :
456 823515 : for (uint32_t i = 0; i < JSC::MacroAssembler::TotalRegisters; i++) {
457 747264 : RegisterID reg = RegisterID(i);
458 :
459 : /* Register is not allocatable, don't bother. */
460 747264 : if (!(Registers::maskReg(reg) & mask))
461 175421 : continue;
462 :
463 : /* Register is not owned by the FrameState. */
464 571843 : FrameEntry *fe = regstate(reg).fe();
465 571843 : if (!fe)
466 99133 : continue;
467 :
468 : /* Try to find a candidate... that doesn't need spilling. */
469 472710 : fallback = reg;
470 :
471 472710 : if (regstate(reg).type() == RematInfo::TYPE && fe->type.synced()) {
472 21215 : fe->type.setMemory();
473 21215 : regstate(reg).forget();
474 21215 : return reg;
475 : }
476 451495 : if (regstate(reg).type() == RematInfo::DATA && fe->data.synced()) {
477 22216 : fe->data.setMemory();
478 22216 : regstate(reg).forget();
479 22216 : return reg;
480 : }
481 : }
482 :
483 76251 : evictReg(fallback.reg());
484 76251 : return fallback.reg();
485 : }
486 :
487 : void
488 1017803 : FrameState::resetInternalState()
489 : {
490 3611675 : for (uint32_t i = 0; i < tracker.nentries; i++)
491 2593872 : tracker[i]->untrack();
492 :
493 1017803 : tracker.reset();
494 1017803 : freeRegs = Registers(Registers::AvailAnyRegs);
495 1017803 : }
496 :
497 : void
498 157436 : FrameState::discardFrame()
499 : {
500 157436 : resetInternalState();
501 157436 : PodArrayZero(regstate_);
502 157436 : }
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 405175 : FrameState::forgetEverything()
546 : {
547 405175 : resetInternalState();
548 :
549 : #ifdef DEBUG
550 6482800 : for (uint32_t i = 0; i < Registers::TotalAnyRegisters; i++) {
551 6077625 : AnyRegisterID reg = AnyRegisterID::fromRaw(i);
552 6077625 : JS_ASSERT(!regstate(reg).usedBy());
553 : }
554 : #endif
555 405175 : }
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 496319 : FrameState::syncForBranch(jsbytecode *target, Uses uses)
692 : {
693 : /* There should be no unowned or pinned registers. */
694 : #ifdef DEBUG
695 496319 : Registers checkRegs(Registers::AvailAnyRegs);
696 7444785 : while (!checkRegs.empty()) {
697 6452147 : AnyRegisterID reg = checkRegs.takeAnyReg();
698 6452147 : JS_ASSERT_IF(!freeRegs.hasReg(reg), regstate(reg).fe());
699 : }
700 : #endif
701 :
702 496319 : if (!cx->typeInferenceEnabled()) {
703 320946 : syncAndForgetEverything();
704 320946 : 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 455192 : FrameState::discardForJoin(RegisterAllocation *&alloc, uint32_t stackDepth)
847 : {
848 455192 : if (!cx->typeInferenceEnabled()) {
849 281268 : resetInternalState();
850 281268 : PodArrayZero(regstate_);
851 281268 : a->sp = a->spBase + stackDepth;
852 281268 : 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 379852 : FrameState::consistentRegisters(jsbytecode *target)
908 : {
909 379852 : if (!cx->typeInferenceEnabled()) {
910 178945 : JS_ASSERT(freeRegs.freeMask == Registers::AvailAnyRegs);
911 178945 : 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 301817 : FrameState::storeTo(FrameEntry *fe, Address address, bool popped)
973 : {
974 301817 : if (fe->isConstant()) {
975 101478 : masm.storeValue(fe->getValue(), address);
976 101478 : return;
977 : }
978 :
979 200339 : if (fe->isCopy())
980 3235 : fe = fe->copyOf();
981 :
982 200339 : JS_ASSERT(!freeRegs.hasReg(address.base));
983 :
984 : /* If loading from memory, ensure destination differs. */
985 340733 : JS_ASSERT_IF((fe->type.inMemory() || fe->data.inMemory()),
986 : addressOf(fe).base != address.base ||
987 340733 : addressOf(fe).offset != address.offset);
988 :
989 200339 : if (fe->data.inFPRegister()) {
990 2430 : masm.storeDouble(fe->data.fpreg(), address);
991 2430 : return;
992 : }
993 :
994 197909 : 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 197808 : bool pinAddressReg = !!regstate(address.base).fe();
1003 197808 : 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 197808 : if (fe->data.inRegister()) {
1079 158519 : masm.storePayload(fe->data.reg(), address);
1080 : } else {
1081 39289 : JS_ASSERT(fe->data.inMemory());
1082 : RegisterID reg;
1083 39289 : if (popped) {
1084 36880 : reg = allocReg();
1085 36880 : masm.loadPayload(addressOf(fe), reg);
1086 : } else {
1087 2409 : reg = allocAndLoadReg(fe, false, RematInfo::DATA).reg();
1088 : }
1089 39289 : masm.storePayload(reg, address);
1090 39289 : if (popped)
1091 36880 : freeReg(reg);
1092 : else
1093 2409 : fe->data.setRegister(reg);
1094 : }
1095 :
1096 197808 : if (fe->isTypeKnown()) {
1097 64177 : masm.storeTypeTag(ImmType(fe->getKnownType()), address);
1098 133631 : } else if (fe->type.inRegister()) {
1099 73540 : masm.storeTypeTag(fe->type.reg(), address);
1100 : } else {
1101 60091 : JS_ASSERT(fe->type.inMemory());
1102 : RegisterID reg;
1103 60091 : if (popped) {
1104 58516 : reg = allocReg();
1105 58516 : masm.loadTypeTag(addressOf(fe), reg);
1106 : } else {
1107 1575 : reg = allocAndLoadReg(fe, false, RematInfo::TYPE).reg();
1108 : }
1109 60091 : masm.storeTypeTag(reg, address);
1110 60091 : if (popped)
1111 58516 : freeReg(reg);
1112 : else
1113 1575 : fe->type.setRegister(reg);
1114 : }
1115 : #endif
1116 197808 : 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 23145 : void FrameState::loadForReturn(FrameEntry *fe, RegisterID typeReg, RegisterID dataReg, RegisterID tempReg)
1127 : {
1128 23145 : JS_ASSERT(dataReg != typeReg && dataReg != tempReg && typeReg != tempReg);
1129 :
1130 23145 : if (fe->isConstant()) {
1131 5984 : masm.loadValueAsComponents(fe->getValue(), typeReg, dataReg);
1132 5984 : 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 3910 : if (!maybeData.isSet())
1150 1185 : masm.loadPayload(addressOf(fe), dataReg);
1151 2725 : else if (maybeData.reg() != dataReg)
1152 1540 : masm.move(maybeData.reg(), dataReg);
1153 3910 : masm.move(ImmType(fe->getKnownType()), typeReg);
1154 3910 : 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 12916 : if (fe->type.inMemory() && fe->data.inMemory()) {
1160 6181 : masm.loadValueAsComponents(addressOf(fe), typeReg, dataReg);
1161 6181 : return;
1162 : }
1163 :
1164 : // Now, we should be guaranteed that at least one part is in a register.
1165 6735 : 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 6735 : 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 6431 : } 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 6735 : RegisterID type = maybeType.reg();
1187 6735 : RegisterID data = maybeData.reg();
1188 :
1189 6735 : if (data == typeReg && type == dataReg) {
1190 331 : masm.move(type, tempReg);
1191 331 : masm.move(data, dataReg);
1192 331 : masm.move(tempReg, typeReg);
1193 6404 : } else if (data != dataReg) {
1194 1705 : if (type == typeReg) {
1195 271 : masm.move(data, dataReg);
1196 1434 : } else if (type != dataReg) {
1197 1399 : masm.move(data, dataReg);
1198 1399 : if (type != typeReg)
1199 1399 : 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 4699 : } else if (type != typeReg) {
1206 77 : masm.move(type, typeReg);
1207 : }
1208 : }
1209 :
1210 : #ifdef DEBUG
1211 : void
1212 17338156 : FrameState::assertValidRegisterState() const
1213 : {
1214 17338156 : Registers checkedFreeRegs(Registers::AvailAnyRegs);
1215 :
1216 : /* Check that copied and copy info balance out. */
1217 17338156 : int32_t copyCount = 0;
1218 :
1219 488005013 : for (uint32_t i = 0; i < tracker.nentries; i++) {
1220 470666857 : FrameEntry *fe = tracker[i];
1221 470666857 : if (deadEntry(fe))
1222 32937462 : continue;
1223 :
1224 437729395 : JS_ASSERT(i == fe->trackerIndex());
1225 :
1226 437729395 : if (fe->isCopy()) {
1227 3489970 : JS_ASSERT_IF(!fe->copyOf()->temporary, fe > fe->copyOf());
1228 3489970 : JS_ASSERT(fe->trackerIndex() > fe->copyOf()->trackerIndex());
1229 3489970 : JS_ASSERT(!deadEntry(fe->copyOf()));
1230 3489970 : JS_ASSERT(fe->copyOf()->isCopied());
1231 3489970 : JS_ASSERT(!fe->isCopied());
1232 3489970 : copyCount--;
1233 3489970 : continue;
1234 : }
1235 :
1236 434239425 : copyCount += fe->copied;
1237 :
1238 434239425 : if (fe->type.inRegister()) {
1239 11971860 : checkedFreeRegs.takeReg(fe->type.reg());
1240 11971860 : JS_ASSERT(regstate(fe->type.reg()).fe() == fe);
1241 11971860 : JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
1242 : }
1243 434239425 : if (fe->data.inRegister()) {
1244 16982144 : checkedFreeRegs.takeReg(fe->data.reg());
1245 16982144 : JS_ASSERT(regstate(fe->data.reg()).fe() == fe);
1246 16982144 : JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
1247 : }
1248 434239425 : 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 17338156 : JS_ASSERT(copyCount == 0);
1256 17338156 : JS_ASSERT(checkedFreeRegs == freeRegs);
1257 :
1258 156043404 : for (uint32_t i = 0; i < Registers::TotalRegisters; i++) {
1259 138705248 : AnyRegisterID reg = (RegisterID) i;
1260 138705248 : JS_ASSERT(!regstate(reg).isPinned());
1261 138705248 : JS_ASSERT_IF(regstate(reg).fe(), !freeRegs.hasReg(reg));
1262 138705248 : JS_ASSERT_IF(regstate(reg).fe(), regstate(reg).fe()->isTracked());
1263 : }
1264 :
1265 138705248 : for (uint32_t i = 0; i < Registers::TotalFPRegisters; i++) {
1266 121367092 : AnyRegisterID reg = (FPRegisterID) i;
1267 121367092 : JS_ASSERT(!regstate(reg).isPinned());
1268 121367092 : JS_ASSERT_IF(regstate(reg).fe(), !freeRegs.hasReg(reg));
1269 121367092 : JS_ASSERT_IF(regstate(reg).fe(), regstate(reg).fe()->isTracked());
1270 121367092 : JS_ASSERT_IF(regstate(reg).fe(), regstate(reg).type() == RematInfo::DATA);
1271 : }
1272 17338156 : }
1273 : #endif
1274 :
1275 : #if defined JS_NUNBOX32
1276 : void
1277 171304 : FrameState::syncFancy(Assembler &masm, Registers avail, int trackerIndex) const
1278 : {
1279 171304 : reifier.reset(&masm, avail, a->sp, entries);
1280 :
1281 1206123 : for (; trackerIndex >= 0; trackerIndex--) {
1282 1034819 : FrameEntry *fe = tracker[trackerIndex];
1283 1034819 : if (fe >= a->sp)
1284 228416 : continue;
1285 :
1286 806403 : reifier.sync(fe);
1287 : }
1288 171304 : }
1289 :
1290 : #endif
1291 : void
1292 4123620 : FrameState::sync(Assembler &masm, Uses uses) const
1293 : {
1294 4123620 : if (!entries)
1295 0 : return;
1296 :
1297 : /* Sync all registers up-front. */
1298 4123620 : Registers allRegs(Registers::AvailAnyRegs);
1299 61854300 : while (!allRegs.empty()) {
1300 53607060 : AnyRegisterID reg = allRegs.takeAnyReg();
1301 53607060 : FrameEntry *fe = regstate(reg).usedBy();
1302 53607060 : if (!fe)
1303 44823731 : continue;
1304 :
1305 8783329 : 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 8783329 : if (fe->isType(JSVAL_TYPE_DOUBLE)) {
1319 27800 : ensureFeSynced(fe, masm);
1320 8755529 : } else if (regstate(reg).type() == RematInfo::DATA) {
1321 4914483 : JS_ASSERT(fe->data.reg() == reg.reg());
1322 4914483 : ensureDataSynced(fe, masm);
1323 : } else {
1324 3841046 : JS_ASSERT(fe->type.reg() == reg.reg());
1325 3841046 : 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 4123620 : Registers avail(freeRegs.freeMask & Registers::AvailRegs);
1335 4123620 : Registers temp(Registers::TempAnyRegs);
1336 :
1337 4123620 : unsigned nentries = tracker.nentries;
1338 21857188 : for (int trackerIndex = nentries - 1; trackerIndex >= 0; trackerIndex--) {
1339 17904872 : JS_ASSERT(tracker.nentries == nentries);
1340 17904872 : FrameEntry *fe = tracker[trackerIndex];
1341 17904872 : if (fe >= a->sp)
1342 5619613 : continue;
1343 :
1344 12285259 : 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 12244641 : if (!fe->isCopy()) {
1352 11085758 : if (fe->data.inRegister() && !regstate(fe->data.reg()).isPinned())
1353 4631039 : avail.putReg(fe->data.reg());
1354 11085758 : if (fe->type.inRegister() && !regstate(fe->type.reg()).isPinned())
1355 3634024 : avail.putReg(fe->type.reg());
1356 : } else {
1357 1158883 : FrameEntry *backing = fe->copyOf();
1358 1158883 : 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 3309886 : if ((!fe->type.synced() && backing->type.inMemory()) ||
1387 2151003 : (!fe->data.synced() && backing->data.inMemory())) {
1388 171304 : syncFancy(masm, avail, trackerIndex);
1389 171304 : return;
1390 : }
1391 : #endif
1392 : }
1393 :
1394 12073337 : 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 12073337 : if (copy || !fe->data.inRegister())
1404 7325068 : ensureDataSynced(fe, masm);
1405 12073337 : if (copy || !fe->type.inRegister())
1406 8419591 : ensureTypeSynced(fe, masm);
1407 : #endif
1408 : }
1409 : }
1410 :
1411 : void
1412 1835837 : FrameState::syncAndKill(Registers kill, Uses uses, Uses ignore)
1413 : {
1414 1835837 : 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 1835837 : Registers search(kill.freeMask & ~freeRegs.freeMask);
1424 6363275 : while (!search.empty()) {
1425 2691601 : AnyRegisterID reg = search.takeAnyReg();
1426 2691601 : FrameEntry *fe = regstate(reg).usedBy();
1427 2691601 : if (!fe || deadEntry(fe, ignore.nuses))
1428 203050 : continue;
1429 :
1430 2488551 : 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 2488551 : if (fe->isType(JSVAL_TYPE_DOUBLE)) {
1456 7087 : syncFe(fe);
1457 2481464 : } else if (regstate(reg).type() == RematInfo::DATA) {
1458 1357913 : JS_ASSERT(fe->data.reg() == reg.reg());
1459 1357913 : syncData(fe);
1460 : } else {
1461 1123551 : JS_ASSERT(fe->type.reg() == reg.reg());
1462 1123551 : syncType(fe);
1463 : }
1464 : #endif
1465 : }
1466 :
1467 :
1468 1835837 : unsigned nentries = tracker.nentries;
1469 11079515 : for (int trackerIndex = nentries - 1; trackerIndex >= 0; trackerIndex--) {
1470 9243678 : JS_ASSERT(tracker.nentries == nentries);
1471 9243678 : FrameEntry *fe = tracker[trackerIndex];
1472 :
1473 9243678 : if (fe >= a->sp || deadEntry(fe, ignore.nuses))
1474 3048101 : continue;
1475 :
1476 6195577 : syncFe(fe);
1477 :
1478 6195577 : if (fe->isCopy())
1479 357491 : continue;
1480 :
1481 : /* Forget registers. */
1482 5838086 : if (fe->data.inRegister() && !regstate(fe->data.reg()).isPinned()) {
1483 1621134 : forgetReg(fe->data.reg());
1484 1621134 : fe->data.setMemory();
1485 : }
1486 5838086 : if (fe->data.inFPRegister() && !regstate(fe->data.fpreg()).isPinned()) {
1487 5803 : forgetReg(fe->data.fpreg());
1488 5803 : fe->data.setMemory();
1489 : }
1490 5838086 : if (fe->type.inRegister() && !regstate(fe->type.reg()).isPinned()) {
1491 1362935 : forgetReg(fe->type.reg());
1492 1362935 : 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 1835837 : search = Registers(kill.freeMask & ~freeRegs.freeMask);
1501 4169081 : while (!search.empty()) {
1502 497407 : AnyRegisterID reg = search.takeAnyReg();
1503 497407 : FrameEntry *fe = regstate(reg).usedBy();
1504 497407 : if (!fe || deadEntry(fe, ignore.nuses))
1505 203050 : continue;
1506 :
1507 294357 : JS_ASSERT(fe->isTracked());
1508 :
1509 294357 : if (regstate(reg).type() == RematInfo::DATA) {
1510 170080 : JS_ASSERT_IF(reg.isFPReg(), fe->data.fpreg() == reg.fpreg());
1511 170080 : JS_ASSERT_IF(!reg.isFPReg(), fe->data.reg() == reg.reg());
1512 170080 : JS_ASSERT(fe->data.synced());
1513 170080 : fe->data.setMemory();
1514 : } else {
1515 124277 : JS_ASSERT(fe->type.reg() == reg.reg());
1516 124277 : JS_ASSERT(fe->type.synced());
1517 124277 : fe->type.setMemory();
1518 : }
1519 :
1520 294357 : forgetReg(reg);
1521 : }
1522 1835837 : }
1523 :
1524 : void
1525 3016495 : 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 3016495 : 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 3016495 : uint32_t mask = Registers::AvailAnyRegs & ~freeRegs.freeMask;
1548 3016495 : Registers search(mask);
1549 :
1550 10309430 : while (!search.empty(mask)) {
1551 4276440 : AnyRegisterID reg = search.peekReg(mask);
1552 4276440 : FrameEntry *fe = regstate(reg).usedBy();
1553 :
1554 4276440 : if (!fe) {
1555 21278 : search.takeReg(reg);
1556 21278 : continue;
1557 : }
1558 :
1559 4255162 : 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 4213091 : } else if (fe->data.inRegister() && fe->type.inRegister()) {
1564 2390222 : search.takeReg(fe->data.reg());
1565 2390222 : search.takeReg(fe->type.reg());
1566 2390222 : masm.loadValueAsComponents(addressOf(fe), fe->type.reg(), fe->data.reg());
1567 : } else {
1568 1822869 : if (fe->data.inRegister()) {
1569 1741337 : search.takeReg(fe->data.reg());
1570 1741337 : masm.loadPayload(addressOf(fe), fe->data.reg());
1571 : }
1572 1822869 : if (fe->type.inRegister()) {
1573 81532 : search.takeReg(fe->type.reg());
1574 81532 : masm.loadTypeTag(addressOf(fe), fe->type.reg());
1575 : }
1576 : }
1577 : }
1578 3016495 : }
1579 :
1580 : JSC::MacroAssembler::RegisterID
1581 575533 : FrameState::copyDataIntoReg(FrameEntry *fe)
1582 : {
1583 575533 : return copyDataIntoReg(this->masm, fe);
1584 : }
1585 :
1586 : void
1587 3804 : FrameState::copyDataIntoReg(FrameEntry *fe, RegisterID hint)
1588 : {
1589 3804 : JS_ASSERT(!fe->isConstant());
1590 3804 : JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
1591 :
1592 3804 : if (fe->isCopy())
1593 1629 : fe = fe->copyOf();
1594 :
1595 3804 : if (!fe->data.inRegister())
1596 1549 : tempRegForData(fe);
1597 :
1598 3804 : RegisterID reg = fe->data.reg();
1599 3804 : 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 3719 : pinReg(reg);
1612 3719 : takeReg(hint);
1613 3719 : unpinReg(reg);
1614 3719 : masm.move(reg, hint);
1615 : }
1616 :
1617 3804 : modifyReg(hint);
1618 3804 : }
1619 :
1620 : JSC::MacroAssembler::RegisterID
1621 577918 : FrameState::copyDataIntoReg(Assembler &masm, FrameEntry *fe)
1622 : {
1623 577918 : JS_ASSERT(!fe->isConstant());
1624 :
1625 577918 : if (fe->isCopy())
1626 197327 : fe = fe->copyOf();
1627 :
1628 577918 : if (fe->data.inRegister()) {
1629 453656 : RegisterID reg = fe->data.reg();
1630 453656 : if (freeRegs.empty(Registers::AvailRegs)) {
1631 21286 : ensureDataSynced(fe, masm);
1632 21286 : fe->data.setMemory();
1633 21286 : regstate(reg).forget();
1634 21286 : modifyReg(reg);
1635 : } else {
1636 432370 : RegisterID newReg = allocReg();
1637 432370 : masm.move(reg, newReg);
1638 432370 : reg = newReg;
1639 : }
1640 453656 : return reg;
1641 : }
1642 :
1643 124262 : RegisterID reg = allocReg();
1644 :
1645 124262 : if (!freeRegs.empty(Registers::AvailRegs))
1646 118137 : masm.move(tempRegForData(fe), reg);
1647 : else
1648 6125 : masm.loadPayload(addressOf(fe),reg);
1649 :
1650 124262 : 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 34943 : FrameState::ownRegForData(FrameEntry *fe)
1752 : {
1753 34943 : JS_ASSERT(!fe->isConstant());
1754 34943 : JS_ASSERT(!fe->isType(JSVAL_TYPE_DOUBLE));
1755 :
1756 : RegisterID reg;
1757 34943 : 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 29552 : if (fe->isCopied())
1780 0 : uncopy(fe);
1781 :
1782 29552 : if (fe->data.inRegister()) {
1783 29139 : reg = fe->data.reg();
1784 : /* Remove ownership of this register. */
1785 29139 : JS_ASSERT(regstate(reg).fe() == fe);
1786 29139 : JS_ASSERT(regstate(reg).type() == RematInfo::DATA);
1787 29139 : regstate(reg).forget();
1788 29139 : fe->data.setMemory();
1789 29139 : modifyReg(reg);
1790 : } else {
1791 413 : JS_ASSERT(fe->data.inMemory());
1792 413 : reg = allocReg();
1793 413 : masm.loadPayload(addressOf(fe), reg);
1794 : }
1795 29552 : return reg;
1796 : }
1797 :
1798 : void
1799 21435 : FrameState::discardFe(FrameEntry *fe)
1800 : {
1801 21435 : forgetEntry(fe);
1802 21435 : fe->type.setMemory();
1803 21435 : fe->data.setMemory();
1804 21435 : fe->clear();
1805 21435 : }
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 3615101 : FrameState::pushCopyOf(FrameEntry *backing)
1935 : {
1936 3615101 : JS_ASSERT(backing->isTracked());
1937 3615101 : FrameEntry *fe = rawPush();
1938 3615101 : fe->resetUnsynced();
1939 3615101 : if (backing->isConstant()) {
1940 851716 : fe->setConstant(backing->getValue());
1941 : } else {
1942 2763385 : if (backing->isCopy())
1943 78721 : backing = backing->copyOf();
1944 2763385 : fe->setCopyOf(backing);
1945 :
1946 : /* Maintain tracker ordering guarantees for copies. */
1947 2763385 : JS_ASSERT(backing->isCopied());
1948 2763385 : if (fe->trackerIndex() < backing->trackerIndex())
1949 204566 : swapInTracker(fe, backing);
1950 : }
1951 3615101 : }
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 499522 : FrameState::walkFrameForUncopy(FrameEntry *original)
2017 : {
2018 499522 : FrameEntry *bestFe = NULL;
2019 499522 : uint32_t ncopies = 0;
2020 :
2021 : /* It's only necessary to visit as many FEs are being tracked. */
2022 499522 : uint32_t maxvisits = tracker.nentries;
2023 :
2024 2578618 : for (FrameEntry *fe = original + 1; fe < a->sp && maxvisits; fe++) {
2025 2079096 : if (!fe->isTracked())
2026 749 : continue;
2027 :
2028 2078347 : maxvisits--;
2029 :
2030 2078347 : if (fe->isCopy() && fe->copyOf() == original) {
2031 499525 : if (!bestFe) {
2032 499522 : bestFe = fe;
2033 499522 : bestFe->setCopyOf(NULL);
2034 : } else {
2035 3 : fe->setCopyOf(bestFe);
2036 3 : if (fe->trackerIndex() < bestFe->trackerIndex())
2037 0 : swapInTracker(bestFe, fe);
2038 : }
2039 499525 : ncopies++;
2040 : }
2041 : }
2042 :
2043 499522 : return bestFe;
2044 : }
2045 :
2046 : FrameEntry *
2047 499572 : FrameState::uncopy(FrameEntry *original)
2048 : {
2049 499572 : 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 499572 : if ((tracker.nentries - original->trackerIndex()) * 2 > uint32_t(a->sp - original))
2076 499522 : fe = walkFrameForUncopy(original);
2077 : else
2078 50 : fe = walkTrackerForUncopy(original);
2079 499572 : 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 499572 : 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 473262 : if (original->type.inMemory() && !fe->type.synced())
2093 192722 : tempRegForType(original);
2094 473262 : fe->type.inherit(original->type);
2095 473262 : if (fe->type.inRegister())
2096 473250 : regstate(fe->type.reg()).reassociate(fe);
2097 : } else {
2098 26310 : fe->setType(original->getKnownType());
2099 : }
2100 499572 : 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 499472 : if (fe->type.inRegister())
2108 473250 : pinReg(fe->type.reg());
2109 499472 : if (original->data.inMemory() && !fe->data.synced())
2110 198373 : tempRegForData(original);
2111 499472 : if (fe->type.inRegister())
2112 473250 : unpinReg(fe->type.reg());
2113 499472 : fe->data.inherit(original->data);
2114 499472 : if (fe->data.inRegister())
2115 499435 : regstate(fe->data.reg()).reassociate(fe);
2116 : }
2117 :
2118 499572 : 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 13101 : FrameState::separateBinaryEntries(FrameEntry *lhs, FrameEntry *rhs)
2137 : {
2138 13101 : JS_ASSERT(lhs == a->sp - 2 && rhs == a->sp - 1);
2139 13101 : if (rhs->isCopy() && rhs->copyOf() == lhs) {
2140 0 : syncAndForgetFe(rhs);
2141 0 : syncAndForgetFe(lhs);
2142 0 : uncopy(lhs);
2143 : }
2144 13101 : }
2145 :
2146 : void
2147 287609 : FrameState::storeLocal(uint32_t n, bool popGuaranteed)
2148 : {
2149 287609 : FrameEntry *local = getLocal(n);
2150 :
2151 287609 : if (a->analysis->slotEscapes(entrySlot(local))) {
2152 160306 : JS_ASSERT(local->data.inMemory());
2153 160306 : storeTo(peek(-1), addressOf(local), popGuaranteed);
2154 160306 : return;
2155 : }
2156 :
2157 127303 : storeTop(local);
2158 :
2159 127303 : if (loop)
2160 18660 : local->lastLoop = loop->headOffset();
2161 :
2162 127303 : if (inTryBlock)
2163 2240 : syncFe(local);
2164 : }
2165 :
2166 : void
2167 4096 : 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 4096 : FrameEntry *arg = getArg(n);
2172 :
2173 4096 : 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 2238 : storeTop(arg);
2180 :
2181 2238 : if (loop)
2182 373 : arg->lastLoop = loop->headOffset();
2183 :
2184 2238 : syncFe(arg);
2185 : }
2186 :
2187 : void
2188 4647561 : FrameState::forgetEntry(FrameEntry *fe)
2189 : {
2190 4647561 : if (fe->isCopied()) {
2191 499570 : uncopy(fe);
2192 499570 : fe->resetUnsynced();
2193 : } else {
2194 4147991 : forgetAllRegs(fe);
2195 : }
2196 :
2197 4647561 : extraArray[fe - entries].reset();
2198 4647561 : }
2199 :
2200 : void
2201 2726725 : FrameState::storeTop(FrameEntry *target)
2202 : {
2203 2726725 : JS_ASSERT(!isTemporary(target));
2204 :
2205 : /* Detect something like (x = x) which is a no-op. */
2206 2726725 : FrameEntry *top = peek(-1);
2207 2726725 : 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 2726717 : bool wasSynced = target->type.synced();
2218 2726717 : JSValueType oldType = target->isTypeKnown() ? target->getKnownType() : JSVAL_TYPE_UNKNOWN;
2219 2726717 : bool trySyncType = wasSynced && oldType != JSVAL_TYPE_UNKNOWN && oldType != JSVAL_TYPE_DOUBLE;
2220 :
2221 : /* Completely invalidate the local variable. */
2222 2726717 : forgetEntry(target);
2223 2726717 : target->resetUnsynced();
2224 :
2225 : /* Constants are easy to propagate. */
2226 2726717 : if (top->isConstant()) {
2227 713271 : target->clear();
2228 713271 : target->setConstant(top->getValue());
2229 713271 : if (trySyncType && target->isType(oldType))
2230 172 : target->type.sync();
2231 713271 : 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 2013446 : FrameEntry *backing = top;
2248 2013446 : if (top->isCopy()) {
2249 1357478 : backing = top->copyOf();
2250 1357478 : JS_ASSERT(backing->trackerIndex() < top->trackerIndex());
2251 :
2252 1357478 : if (backing < target || isTemporary(backing)) {
2253 : /* local.idx < backing.idx means local cannot be a copy yet */
2254 57611 : if (target->trackerIndex() < backing->trackerIndex())
2255 2882 : swapInTracker(backing, target);
2256 57611 : target->setCopyOf(backing);
2257 57611 : if (trySyncType && target->isType(oldType))
2258 37 : target->type.sync();
2259 57611 : 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 5573162 : for (uint32_t i = backing->trackerIndex() + 1; i < tracker.nentries; i++) {
2281 4273295 : FrameEntry *fe = tracker[i];
2282 4273295 : if (deadEntry(fe))
2283 120515 : continue;
2284 4152780 : if (fe->isCopy() && fe->copyOf() == backing)
2285 1299886 : 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 1955835 : if (backing->trackerIndex() < target->trackerIndex())
2295 129386 : swapInTracker(backing, target);
2296 :
2297 1955835 : 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 1952919 : if (backing->type.inRegister())
2310 1605886 : pinReg(backing->type.reg());
2311 1952919 : RegisterID reg = tempRegForData(backing);
2312 1952919 : if (backing->type.inRegister())
2313 1605886 : unpinReg(backing->type.reg());
2314 1952919 : target->data.setRegister(reg);
2315 1952919 : regstate(reg).reassociate(target);
2316 :
2317 1952919 : if (backing->isTypeKnown()) {
2318 123726 : target->setType(backing->getKnownType());
2319 : } else {
2320 1829193 : pinReg(reg);
2321 1829193 : RegisterID typeReg = tempRegForType(backing);
2322 1829193 : unpinReg(reg);
2323 1829193 : target->type.setRegister(typeReg);
2324 1829193 : regstate(typeReg).reassociate(target);
2325 : }
2326 : }
2327 :
2328 1955835 : backing->setCopyOf(target);
2329 1955835 : JS_ASSERT(top->copyOf() == target);
2330 :
2331 1955835 : if (trySyncType && target->isType(oldType))
2332 43589 : target->type.sync();
2333 : }
2334 :
2335 : void
2336 709634 : FrameState::shimmy(uint32_t n)
2337 : {
2338 709634 : JS_ASSERT(a->sp - n >= a->spBase);
2339 709634 : int32_t depth = 0 - int32_t(n);
2340 709634 : storeTop(peek(depth - 1));
2341 709634 : popn(n);
2342 709634 : }
2343 :
2344 : void
2345 1887550 : FrameState::shift(int32_t n)
2346 : {
2347 1887550 : JS_ASSERT(n < 0);
2348 1887550 : JS_ASSERT(a->sp + n - 1 >= a->spBase);
2349 1887550 : storeTop(peek(n - 1));
2350 1887550 : pop();
2351 1887550 : }
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 105440 : FrameState::pinEntry(FrameEntry *fe, ValueRemat &vr, bool breakDouble)
2402 : {
2403 105440 : if (breakDouble && !fe->isConstant() && fe->isType(JSVAL_TYPE_DOUBLE))
2404 384 : forgetKnownDouble(fe);
2405 :
2406 105440 : if (fe->isConstant()) {
2407 23341 : vr = ValueRemat::FromConstant(fe->getValue());
2408 82099 : } 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 80715 : MaybeRegisterID maybePinnedType = maybePinType(fe);
2415 :
2416 : // Get and pin the data register.
2417 80715 : RegisterID dataReg = tempRegForData(fe);
2418 80715 : pinReg(dataReg);
2419 :
2420 80715 : if (fe->isTypeKnown()) {
2421 31074 : vr = ValueRemat::FromKnownType(fe->getKnownType(), dataReg);
2422 : } else {
2423 : // The type might not be loaded yet, so unpin for simplicity.
2424 49641 : maybeUnpinReg(maybePinnedType);
2425 :
2426 49641 : vr = ValueRemat::FromRegisters(tempRegForType(fe), dataReg);
2427 49641 : pinReg(vr.typeReg());
2428 : }
2429 : }
2430 :
2431 : // Set these bits last, since allocation could have caused a sync.
2432 105440 : vr.isDataSynced = fe->data.synced();
2433 105440 : vr.isTypeSynced = fe->type.synced();
2434 105440 : }
2435 :
2436 : void
2437 79324 : FrameState::unpinEntry(const ValueRemat &vr)
2438 : {
2439 79324 : if (vr.isFPRegister()) {
2440 1384 : unpinReg(vr.fpReg());
2441 77940 : } else if (!vr.isConstant()) {
2442 61466 : if (!vr.isTypeKnown())
2443 33654 : unpinReg(vr.typeReg());
2444 61466 : unpinReg(vr.dataReg());
2445 : }
2446 79324 : }
2447 :
2448 : void
2449 26116 : 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 26116 : if (vr.isConstant() || vr.isFPRegister()) {
2456 6867 : if (!vr.isDataSynced || !vr.isTypeSynced)
2457 6853 : masm.storeValue(vr.value(), addressOf(fe));
2458 : } else {
2459 19249 : if (!vr.isDataSynced)
2460 16856 : masm.storePayload(vr.dataReg(), addressOf(fe));
2461 19249 : if (!vr.isTypeSynced) {
2462 16753 : if (vr.isTypeKnown())
2463 3243 : masm.storeTypeTag(ImmType(vr.knownType()), addressOf(fe));
2464 : else
2465 13510 : masm.storeTypeTag(vr.typeReg(), addressOf(fe));
2466 : }
2467 : }
2468 : #endif
2469 26116 : }
2470 :
2471 : static inline bool
2472 2566700 : AllocHelper(RematInfo &info, MaybeRegisterID &maybe)
2473 : {
2474 2566700 : if (info.inRegister()) {
2475 1166028 : maybe = info.reg();
2476 1166028 : return true;
2477 : }
2478 1400672 : 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 150490 : FrameState::ensureFullRegs(FrameEntry *fe, MaybeRegisterID *type, MaybeRegisterID *data)
2511 : {
2512 150490 : fe = fe->isCopy() ? fe->copyOf() : fe;
2513 :
2514 150490 : JS_ASSERT(!data->isSet() && !type->isSet());
2515 150490 : if (!fe->type.inMemory()) {
2516 106227 : if (fe->type.inRegister())
2517 68545 : *type = fe->type.reg();
2518 106227 : if (fe->data.isConstant())
2519 0 : return;
2520 106227 : if (fe->data.inRegister()) {
2521 102464 : *data = fe->data.reg();
2522 102464 : return;
2523 : }
2524 3763 : if (fe->type.inRegister())
2525 855 : pinReg(fe->type.reg());
2526 3763 : *data = tempRegForData(fe);
2527 3763 : if (fe->type.inRegister())
2528 855 : unpinReg(fe->type.reg());
2529 44263 : } 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 43149 : *data = tempRegForData(fe);
2545 43149 : pinReg(data->reg());
2546 43149 : *type = tempRegForType(fe);
2547 43149 : 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 641675 : FrameState::allocForBinary(FrameEntry *lhs, FrameEntry *rhs, JSOp op, BinaryAlloc &alloc,
2593 : bool needsResult)
2594 : {
2595 641675 : FrameEntry *backingLeft = lhs;
2596 641675 : FrameEntry *backingRight = rhs;
2597 :
2598 641675 : if (backingLeft->isCopy())
2599 242706 : backingLeft = backingLeft->copyOf();
2600 641675 : if (backingRight->isCopy())
2601 8646 : 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 641675 : if (AllocHelper(backingLeft->type, alloc.lhsType))
2608 87834 : pinReg(alloc.lhsType.reg());
2609 641675 : if (AllocHelper(backingLeft->data, alloc.lhsData))
2610 404227 : pinReg(alloc.lhsData.reg());
2611 641675 : if (AllocHelper(backingRight->type, alloc.rhsType))
2612 332229 : pinReg(alloc.rhsType.reg());
2613 641675 : if (AllocHelper(backingRight->data, alloc.rhsData))
2614 341738 : pinReg(alloc.rhsData.reg());
2615 :
2616 : /* For each type without a register, give it a register if needed. */
2617 641675 : if (!alloc.lhsType.isSet() && backingLeft->type.inMemory()) {
2618 525745 : alloc.lhsType = tempRegForType(lhs);
2619 525745 : pinReg(alloc.lhsType.reg());
2620 : }
2621 641675 : if (!alloc.rhsType.isSet() && backingRight->type.inMemory()) {
2622 11762 : alloc.rhsType = tempRegForType(rhs);
2623 11762 : 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 641675 : JS_ASSERT(!backingLeft->isType(JSVAL_TYPE_DOUBLE));
2632 641675 : JS_ASSERT(!backingRight->isType(JSVAL_TYPE_DOUBLE));
2633 641675 : alloc.lhsFP = allocFPReg();
2634 641675 : alloc.rhsFP = allocFPReg();
2635 :
2636 : bool commu;
2637 641675 : 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 641675 : commu = true;
2648 641675 : 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 641675 : JS_ASSERT_IF(lhs->isConstant(), !rhs->isConstant());
2664 641675 : JS_ASSERT_IF(rhs->isConstant(), !lhs->isConstant());
2665 :
2666 641675 : if (!alloc.lhsData.isSet()) {
2667 237448 : if (backingLeft->data.inMemory()) {
2668 235881 : alloc.lhsData = tempRegForData(lhs);
2669 235881 : 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 641675 : if (!alloc.rhsData.isSet() && backingRight->data.inMemory()) {
2678 11923 : alloc.rhsData = tempRegForData(rhs);
2679 11923 : pinReg(alloc.rhsData.reg());
2680 : }
2681 :
2682 641675 : alloc.lhsNeedsRemat = false;
2683 641675 : alloc.rhsNeedsRemat = false;
2684 641675 : alloc.resultHasRhs = false;
2685 641675 : alloc.undoResult = false;
2686 :
2687 641675 : if (!needsResult)
2688 44657 : 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 743645 : 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 556054 : if (cx->typeInferenceEnabled())
2712 13235 : evictDeadEntries(true);
2713 :
2714 556054 : if (!freeRegs.empty(Registers::AvailRegs)) {
2715 : /* Free reg - just grab it. */
2716 545533 : alloc.result = allocReg();
2717 545533 : 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 544171 : masm.move(alloc.lhsData.reg(), alloc.result);
2724 544171 : alloc.resultHasRhs = false;
2725 : }
2726 10521 : } 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 9371 : bool leftInReg = backingLeft->data.inRegister();
2771 9371 : bool rightInReg = backingRight->data.inRegister();
2772 9371 : bool leftSynced = backingLeft->data.synced();
2773 9371 : bool rightSynced = backingRight->data.synced();
2774 9371 : if (!commu || (leftInReg && (leftSynced || (!rightInReg || !rightSynced)))) {
2775 8919 : JS_ASSERT(backingLeft->data.inRegister() || !commu);
2776 26757 : JS_ASSERT_IF(backingLeft->data.inRegister(),
2777 26757 : backingLeft->data.reg() == alloc.lhsData.reg());
2778 8919 : if (backingLeft->data.inRegister()) {
2779 8919 : alloc.result = backingLeft->data.reg();
2780 8919 : unpinReg(alloc.result);
2781 8919 : takeReg(alloc.result);
2782 8919 : 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 8919 : 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 641675 : if (backingLeft->type.inRegister())
2803 613579 : unpinReg(backingLeft->type.reg());
2804 641675 : if (backingRight->type.inRegister())
2805 343991 : unpinReg(backingRight->type.reg());
2806 641675 : if (backingLeft->data.inRegister())
2807 630948 : unpinReg(backingLeft->data.reg());
2808 641675 : if (backingRight->data.inRegister())
2809 352753 : unpinReg(backingRight->data.reg());
2810 641675 : }
2811 :
2812 : void
2813 590741 : FrameState::rematBinary(FrameEntry *lhs, FrameEntry *rhs, const BinaryAlloc &alloc, Assembler &masm)
2814 : {
2815 590741 : if (alloc.rhsNeedsRemat)
2816 1130 : masm.loadPayload(addressForDataRemat(rhs), alloc.rhsData.reg());
2817 590741 : if (alloc.lhsNeedsRemat)
2818 9546 : masm.loadPayload(addressForDataRemat(lhs), alloc.lhsData.reg());
2819 590741 : }
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 37664 : pinReg(fe->data.reg());
2827 37664 : return fe->data.reg();
2828 : }
2829 35502 : return MaybeRegisterID();
2830 : }
2831 :
2832 : MaybeRegisterID
2833 124928 : FrameState::maybePinType(FrameEntry *fe)
2834 : {
2835 124928 : fe = fe->isCopy() ? fe->copyOf() : fe;
2836 124928 : if (fe->type.inRegister()) {
2837 50139 : pinReg(fe->type.reg());
2838 50139 : return fe->type.reg();
2839 : }
2840 74789 : return MaybeRegisterID();
2841 : }
2842 :
2843 : void
2844 133481 : FrameState::maybeUnpinReg(MaybeRegisterID reg)
2845 : {
2846 133481 : if (reg.isSet())
2847 71915 : unpinReg(reg.reg());
2848 133481 : }
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 : }
|