/[jscoverage]/trunk/js/jstracer.cpp
ViewVC logotype

Contents of /trunk/js/jstracer.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 585 - (show annotations)
Sun Sep 12 15:13:23 2010 UTC (8 years, 9 months ago) by siliconforks
File size: 471543 byte(s)
Update to SpiderMonkey from Firefox 3.6.9.

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 * Andreas Gal <gal@mozilla.com>
25 * Mike Shaver <shaver@mozilla.org>
26 * David Anderson <danderson@mozilla.com>
27 *
28 * Alternatively, the contents of this file may be used under the terms of
29 * either of the GNU General Public License Version 2 or later (the "GPL"),
30 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
31 * in which case the provisions of the GPL or the LGPL are applicable instead
32 * of those above. If you wish to allow use of your version of this file only
33 * under the terms of either the GPL or the LGPL, and not to allow others to
34 * use your version of this file under the terms of the MPL, indicate your
35 * decision by deleting the provisions above and replace them with the notice
36 * and other provisions required by the GPL or the LGPL. If you do not delete
37 * the provisions above, a recipient may use your version of this file under
38 * the terms of any one of the MPL, the GPL or the LGPL.
39 *
40 * ***** END LICENSE BLOCK ***** */
41
42 #include "jsstdint.h"
43 #include "jsbit.h" // low-level (NSPR-based) headers next
44 #include "jsprf.h"
45 #include <math.h> // standard headers next
46
47 #if defined(_MSC_VER) || defined(__MINGW32__)
48 #include <malloc.h>
49 #ifdef _MSC_VER
50 #define alloca _alloca
51 #endif
52 #endif
53 #ifdef SOLARIS
54 #include <alloca.h>
55 #endif
56 #include <limits.h>
57
58 #include "nanojit/nanojit.h"
59 #include "jsapi.h" // higher-level library and API headers
60 #include "jsarray.h"
61 #include "jsbool.h"
62 #include "jscntxt.h"
63 #include "jsdate.h"
64 #include "jsdbgapi.h"
65 #include "jsemit.h"
66 #include "jsfun.h"
67 #include "jsinterp.h"
68 #include "jsiter.h"
69 #include "jsmath.h"
70 #include "jsobj.h"
71 #include "jsopcode.h"
72 #include "jsregexp.h"
73 #include "jsscope.h"
74 #include "jsscript.h"
75 #include "jsstaticcheck.h"
76 #include "jstracer.h"
77 #include "jsxml.h"
78
79 #include "jsatominlines.h"
80 #include "jsscriptinlines.h"
81
82 #include "jsautooplen.h" // generated headers last
83 #include "imacros.c.out"
84
85 using namespace nanojit;
86
87 #if JS_HAS_XML_SUPPORT
88 #define ABORT_IF_XML(v) \
89 JS_BEGIN_MACRO \
90 if (!JSVAL_IS_PRIMITIVE(v) && OBJECT_IS_XML(BOGUS_CX, JSVAL_TO_OBJECT(v)))\
91 ABORT_TRACE("xml detected"); \
92 JS_END_MACRO
93 #else
94 #define ABORT_IF_XML(v) ((void) 0)
95 #endif
96
97 /*
98 * Never use JSVAL_IS_BOOLEAN because it restricts the value (true, false) and
99 * the type. What you want to use is JSVAL_IS_SPECIAL(x) and then handle the
100 * undefined case properly (bug 457363).
101 */
102 #undef JSVAL_IS_BOOLEAN
103 #define JSVAL_IS_BOOLEAN(x) JS_STATIC_ASSERT(0)
104
105 JS_STATIC_ASSERT(sizeof(JSTraceType) == 1);
106
107 /* Map to translate a type tag into a printable representation. */
108 static const char typeChar[] = "OIDXSNBF";
109 static const char tagChar[] = "OIDISIBI";
110
111 /* Blacklist parameters. */
112
113 /*
114 * Number of iterations of a loop where we start tracing. That is, we don't
115 * start tracing until the beginning of the HOTLOOP-th iteration.
116 */
117 #define HOTLOOP 2
118
119 /* Attempt recording this many times before blacklisting permanently. */
120 #define BL_ATTEMPTS 2
121
122 /* Skip this many hits before attempting recording again, after an aborted attempt. */
123 #define BL_BACKOFF 32
124
125 /* Number of times we wait to exit on a side exit before we try to extend the tree. */
126 #define HOTEXIT 1
127
128 /* Number of times we try to extend the tree along a side exit. */
129 #define MAXEXIT 3
130
131 /* Maximum number of peer trees allowed. */
132 #define MAXPEERS 9
133
134 /* Max call depths for inlining. */
135 #define MAX_CALLDEPTH 10
136
137 /* Max native stack size. */
138 #define MAX_NATIVE_STACK_SLOTS 1024
139
140 /* Max call stack size. */
141 #define MAX_CALL_STACK_ENTRIES 64
142
143 /* Max global object size. */
144 #define MAX_GLOBAL_SLOTS 4096
145
146 /* Max memory needed to rebuild the interpreter stack when falling off trace. */
147 #define MAX_INTERP_STACK_BYTES \
148 (MAX_NATIVE_STACK_SLOTS * sizeof(jsval) + \
149 MAX_CALL_STACK_ENTRIES * sizeof(JSInlineFrame) + \
150 sizeof(JSInlineFrame)) /* possibly slow native frame at top of stack */
151
152 /* Max number of branches per tree. */
153 #define MAX_BRANCHES 32
154
155 #define CHECK_STATUS(expr) \
156 JS_BEGIN_MACRO \
157 JSRecordingStatus _status = (expr); \
158 if (_status != JSRS_CONTINUE) \
159 return _status; \
160 JS_END_MACRO
161
162 #ifdef JS_JIT_SPEW
163 #define ABORT_TRACE_RV(msg, value) \
164 JS_BEGIN_MACRO \
165 debug_only_printf(LC_TMAbort, "abort: %d: %s\n", __LINE__, (msg)); \
166 return (value); \
167 JS_END_MACRO
168 #else
169 #define ABORT_TRACE_RV(msg, value) return (value)
170 #endif
171
172 #define ABORT_TRACE(msg) ABORT_TRACE_RV(msg, JSRS_STOP)
173 #define ABORT_TRACE_ERROR(msg) ABORT_TRACE_RV(msg, JSRS_ERROR)
174
175 #ifdef JS_JIT_SPEW
176 struct __jitstats {
177 #define JITSTAT(x) uint64 x;
178 #include "jitstats.tbl"
179 #undef JITSTAT
180 } jitstats = { 0LL, };
181
182 JS_STATIC_ASSERT(sizeof(jitstats) % sizeof(uint64) == 0);
183
184 enum jitstat_ids {
185 #define JITSTAT(x) STAT ## x ## ID,
186 #include "jitstats.tbl"
187 #undef JITSTAT
188 STAT_IDS_TOTAL
189 };
190
191 static JSPropertySpec jitstats_props[] = {
192 #define JITSTAT(x) { #x, STAT ## x ## ID, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT },
193 #include "jitstats.tbl"
194 #undef JITSTAT
195 { 0 }
196 };
197
198 static JSBool
199 jitstats_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
200 {
201 int index = -1;
202
203 if (JSVAL_IS_STRING(id)) {
204 JSString* str = JSVAL_TO_STRING(id);
205 if (strcmp(JS_GetStringBytes(str), "HOTLOOP") == 0) {
206 *vp = INT_TO_JSVAL(HOTLOOP);
207 return JS_TRUE;
208 }
209 }
210
211 if (JSVAL_IS_INT(id))
212 index = JSVAL_TO_INT(id);
213
214 uint64 result = 0;
215 switch (index) {
216 #define JITSTAT(x) case STAT ## x ## ID: result = jitstats.x; break;
217 #include "jitstats.tbl"
218 #undef JITSTAT
219 default:
220 *vp = JSVAL_VOID;
221 return JS_TRUE;
222 }
223
224 if (result < JSVAL_INT_MAX) {
225 *vp = INT_TO_JSVAL(result);
226 return JS_TRUE;
227 }
228 char retstr[64];
229 JS_snprintf(retstr, sizeof retstr, "%llu", result);
230 *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, retstr));
231 return JS_TRUE;
232 }
233
234 JSClass jitstats_class = {
235 "jitstats",
236 0,
237 JS_PropertyStub, JS_PropertyStub,
238 jitstats_getProperty, JS_PropertyStub,
239 JS_EnumerateStub, JS_ResolveStub,
240 JS_ConvertStub, NULL,
241 JSCLASS_NO_OPTIONAL_MEMBERS
242 };
243
244 void
245 js_InitJITStatsClass(JSContext *cx, JSObject *glob)
246 {
247 JS_InitClass(cx, glob, NULL, &jitstats_class, NULL, 0, jitstats_props, NULL, NULL, NULL);
248 }
249
250 #define AUDIT(x) (jitstats.x++)
251 #else
252 #define AUDIT(x) ((void)0)
253 #endif /* JS_JIT_SPEW */
254
255 /*
256 * INS_CONSTPTR can be used to embed arbitrary pointers into the native code. It should not
257 * be used directly to embed GC thing pointers. Instead, use the INS_CONSTOBJ/FUN/STR/SPROP
258 * variants which ensure that the embedded pointer will be kept alive across GCs.
259 */
260
261 #define INS_CONST(c) addName(lir->insImm(c), #c)
262 #define INS_CONSTPTR(p) addName(lir->insImmPtr(p), #p)
263 #define INS_CONSTWORD(v) addName(lir->insImmPtr((void *) (v)), #v)
264 #define INS_CONSTVAL(v) addName(insImmVal(v), #v)
265 #define INS_CONSTOBJ(obj) addName(insImmObj(obj), #obj)
266 #define INS_CONSTFUN(fun) addName(insImmFun(fun), #fun)
267 #define INS_CONSTSTR(str) addName(insImmStr(str), #str)
268 #define INS_CONSTSPROP(sprop) addName(insImmSprop(sprop), #sprop)
269 #define INS_ATOM(atom) INS_CONSTSTR(ATOM_TO_STRING(atom))
270 #define INS_NULL() INS_CONSTPTR(NULL)
271 #define INS_VOID() INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID))
272
273 static avmplus::AvmCore s_core = avmplus::AvmCore();
274 static avmplus::AvmCore* core = &s_core;
275
276 /* Allocator SPI implementation. */
277
278 void*
279 nanojit::Allocator::allocChunk(size_t nbytes)
280 {
281 VMAllocator *vma = (VMAllocator*)this;
282 JS_ASSERT(!vma->outOfMemory());
283 void *p = malloc(nbytes);
284 if (!p) {
285 JS_ASSERT(nbytes < sizeof(vma->mReserve));
286 vma->mOutOfMemory = true;
287 p = (void*) &vma->mReserve[0];
288 }
289 vma->mSize += nbytes;
290 return p;
291 }
292
293 void
294 nanojit::Allocator::freeChunk(void *p) {
295 VMAllocator *vma = (VMAllocator*)this;
296 if (p != &vma->mReserve[0])
297 free(p);
298 }
299
300 void
301 nanojit::Allocator::postReset() {
302 VMAllocator *vma = (VMAllocator*)this;
303 vma->mOutOfMemory = false;
304 vma->mSize = 0;
305 }
306
307
308 #ifdef JS_JIT_SPEW
309 static void
310 DumpPeerStability(JSTraceMonitor* tm, const void* ip, JSObject* globalObj, uint32 globalShape, uint32 argc);
311 #endif
312
313 /*
314 * We really need a better way to configure the JIT. Shaver, where is
315 * my fancy JIT object?
316 *
317 * NB: this is raced on, if jstracer.cpp should ever be running MT.
318 * I think it's harmless tho.
319 */
320 static bool did_we_check_processor_features = false;
321
322 /* ------ Debug logging control ------ */
323
324 /*
325 * All the logging control stuff lives in here. It is shared between
326 * all threads, but I think that's OK.
327 */
328 LogControl js_LogController;
329
330 #ifdef JS_JIT_SPEW
331
332 /*
333 * NB: this is raced on too, if jstracer.cpp should ever be running MT.
334 * Also harmless.
335 */
336 static bool did_we_set_up_debug_logging = false;
337
338 static void
339 InitJITLogController()
340 {
341 char *tm, *tmf;
342 uint32_t bits;
343
344 js_LogController.lcbits = 0;
345
346 tm = getenv("TRACEMONKEY");
347 if (tm) {
348 fflush(NULL);
349 printf(
350 "The environment variable $TRACEMONKEY has been replaced by $TMFLAGS.\n"
351 "Try 'TMFLAGS=help js -j' for a list of options.\n"
352 );
353 exit(0);
354 }
355
356 tmf = getenv("TMFLAGS");
357 if (!tmf) return;
358
359 /* Using strstr() is really a cheap hack as far as flag decoding goes. */
360 if (strstr(tmf, "help")) {
361 fflush(NULL);
362 printf(
363 "usage: TMFLAGS=option,option,option,... where options can be:\n"
364 "\n"
365 " help show this message\n"
366 " ------ options for jstracer & jsregexp ------\n"
367 " minimal ultra-minimalist output; try this first\n"
368 " full everything except 'treevis' and 'nocodeaddrs'\n"
369 " tracer tracer lifetime (FIXME:better description)\n"
370 " recorder trace recording stuff (FIXME:better description)\n"
371 " abort show trace recording aborts\n"
372 " stats show trace recording stats\n"
373 " regexp show compilation & entry for regexps\n"
374 " treevis spew that tracevis/tree.py can parse\n"
375 " ------ options for Nanojit ------\n"
376 " fragprofile count entries and exits for each fragment\n"
377 " activation show activation info\n"
378 " liveness show LIR liveness at start of rdr pipeline\n"
379 " readlir show LIR as it enters the reader pipeline\n"
380 " aftersf show LIR after StackFilter\n"
381 " regalloc show regalloc details\n"
382 " assembly show final aggregated assembly code\n"
383 " nocodeaddrs don't show code addresses in assembly listings\n"
384 "\n"
385 );
386 exit(0);
387 /*NOTREACHED*/
388 }
389
390 bits = 0;
391
392 /* flags for jstracer.cpp */
393 if (strstr(tmf, "minimal") || strstr(tmf, "full")) bits |= LC_TMMinimal;
394 if (strstr(tmf, "tracer") || strstr(tmf, "full")) bits |= LC_TMTracer;
395 if (strstr(tmf, "recorder") || strstr(tmf, "full")) bits |= LC_TMRecorder;
396 if (strstr(tmf, "abort") || strstr(tmf, "full")) bits |= LC_TMAbort;
397 if (strstr(tmf, "stats") || strstr(tmf, "full")) bits |= LC_TMStats;
398 if (strstr(tmf, "regexp") || strstr(tmf, "full")) bits |= LC_TMRegexp;
399 if (strstr(tmf, "treevis")) bits |= LC_TMTreeVis;
400
401 /* flags for nanojit */
402 if (strstr(tmf, "fragprofile")) bits |= LC_FragProfile;
403 if (strstr(tmf, "liveness") || strstr(tmf, "full")) bits |= LC_Liveness;
404 if (strstr(tmf, "activation") || strstr(tmf, "full")) bits |= LC_Activation;
405 if (strstr(tmf, "readlir") || strstr(tmf, "full")) bits |= LC_ReadLIR;
406 if (strstr(tmf, "aftersf") || strstr(tmf, "full")) bits |= LC_AfterSF;
407 if (strstr(tmf, "regalloc") || strstr(tmf, "full")) bits |= LC_RegAlloc;
408 if (strstr(tmf, "assembly") || strstr(tmf, "full")) bits |= LC_Assembly;
409 if (strstr(tmf, "nocodeaddrs")) bits |= LC_NoCodeAddrs;
410
411 js_LogController.lcbits = bits;
412 return;
413
414 }
415 #endif
416
417 /* ------------------ Frag-level profiling support ------------------ */
418
419 #ifdef JS_JIT_SPEW
420
421 /*
422 * All the allocations done by this profile data-collection and
423 * display machinery, are done in JSTraceMonitor::profAlloc. That is
424 * emptied out at the end of js_FinishJIT. It has a lifetime from
425 * js_InitJIT to js_FinishJIT, which exactly matches the span
426 * js_FragProfiling_init to js_FragProfiling_showResults.
427 */
428 template<class T>
429 static
430 Seq<T>* reverseInPlace(Seq<T>* seq)
431 {
432 Seq<T>* prev = NULL;
433 Seq<T>* curr = seq;
434 while (curr) {
435 Seq<T>* next = curr->tail;
436 curr->tail = prev;
437 prev = curr;
438 curr = next;
439 }
440 return prev;
441 }
442
443 // The number of top blocks to show in the profile
444 #define N_TOP_BLOCKS 50
445
446 // Contains profile info for a single guard
447 struct GuardPI {
448 uint32_t guardID; // identifying number
449 uint32_t count; // count.
450 };
451
452 struct FragPI {
453 uint32_t count; // entry count for this Fragment
454 uint32_t nStaticExits; // statically: the number of exits
455 size_t nCodeBytes; // statically: the number of insn bytes in the main fragment
456 size_t nExitBytes; // statically: the number of insn bytes in the exit paths
457 Seq<GuardPI>* guards; // guards, each with its own count
458 uint32_t largestGuardID; // that exists in .guards
459 };
460
461 /* A mapping of Fragment.profFragID to FragPI */
462 typedef HashMap<uint32,FragPI> FragStatsMap;
463
464 void
465 js_FragProfiling_FragFinalizer(Fragment* f, JSTraceMonitor* tm)
466 {
467 // Recover profiling data from 'f', which is logically at the end
468 // of its useful lifetime.
469 if (!(js_LogController.lcbits & LC_FragProfile))
470 return;
471
472 NanoAssert(f);
473 // Valid profFragIDs start at 1
474 NanoAssert(f->profFragID >= 1);
475 // Should be called exactly once per Fragment. This will assert if
476 // you issue the same FragID to more than one Fragment.
477 NanoAssert(!tm->profTab->containsKey(f->profFragID));
478
479 FragPI pi = { f->profCount,
480 f->nStaticExits,
481 f->nCodeBytes,
482 f->nExitBytes,
483 NULL, 0 };
484
485 // Begin sanity check on the guards
486 SeqBuilder<GuardPI> guardsBuilder(*tm->profAlloc);
487 GuardRecord* gr;
488 uint32_t nGs = 0;
489 uint32_t sumOfDynExits = 0;
490 for (gr = f->guardsForFrag; gr; gr = gr->nextInFrag) {
491 nGs++;
492 // Also copy the data into our auxiliary structure.
493 // f->guardsForFrag is in reverse order, and so this
494 // copy preserves that ordering (->add adds at end).
495 // Valid profGuardIDs start at 1.
496 NanoAssert(gr->profGuardID > 0);
497 sumOfDynExits += gr->profCount;
498 GuardPI gpi = { gr->profGuardID, gr->profCount };
499 guardsBuilder.add(gpi);
500 if (gr->profGuardID > pi.largestGuardID)
501 pi.largestGuardID = gr->profGuardID;
502 }
503 pi.guards = guardsBuilder.get();
504 // And put the guard list in forwards order
505 pi.guards = reverseInPlace(pi.guards);
506
507 // Why is this so? Because nGs is the number of guards
508 // at the time the LIR was generated, whereas f->nStaticExits
509 // is the number of them observed by the time it makes it
510 // through to the assembler. It can be the case that LIR
511 // optimisation removes redundant guards; hence we expect
512 // nGs to always be the same or higher.
513 NanoAssert(nGs >= f->nStaticExits);
514
515 // Also we can assert that the sum of the exit counts
516 // can't exceed the entry count. It'd be nice to assert that
517 // they are exactly equal, but we can't because we don't know
518 // how many times we got to the end of the trace.
519 NanoAssert(f->profCount >= sumOfDynExits);
520
521 // End sanity check on guards
522
523 tm->profTab->put(f->profFragID, pi);
524 }
525
526 static void
527 js_FragProfiling_showResults(JSTraceMonitor* tm)
528 {
529 uint32_t topFragID[N_TOP_BLOCKS];
530 FragPI topPI[N_TOP_BLOCKS];
531 uint64_t totCount = 0, cumulCount;
532 uint32_t totSE = 0;
533 size_t totCodeB = 0, totExitB = 0;
534 memset(topFragID, 0, sizeof(topFragID));
535 memset(topPI, 0, sizeof(topPI));
536 FragStatsMap::Iter iter(*tm->profTab);
537 while (iter.next()) {
538 uint32_t fragID = iter.key();
539 FragPI pi = iter.value();
540 uint32_t count = pi.count;
541 totCount += (uint64_t)count;
542 /* Find the rank for this entry, in tops */
543 int r = N_TOP_BLOCKS-1;
544 while (true) {
545 if (r == -1)
546 break;
547 if (topFragID[r] == 0) {
548 r--;
549 continue;
550 }
551 if (count > topPI[r].count) {
552 r--;
553 continue;
554 }
555 break;
556 }
557 r++;
558 AvmAssert(r >= 0 && r <= N_TOP_BLOCKS);
559 /* This entry should be placed at topPI[r], and entries
560 at higher numbered slots moved up one. */
561 if (r < N_TOP_BLOCKS) {
562 for (int s = N_TOP_BLOCKS-1; s > r; s--) {
563 topFragID[s] = topFragID[s-1];
564 topPI[s] = topPI[s-1];
565 }
566 topFragID[r] = fragID;
567 topPI[r] = pi;
568 }
569 }
570
571 js_LogController.printf(
572 "\n----------------- Per-fragment execution counts ------------------\n");
573 js_LogController.printf(
574 "\nTotal count = %llu\n\n", (unsigned long long int)totCount);
575
576 js_LogController.printf(
577 " Entry counts Entry counts ----- Static -----\n");
578 js_LogController.printf(
579 " ------Self------ ----Cumulative--- Exits Cbytes Xbytes FragID\n");
580 js_LogController.printf("\n");
581
582 if (totCount == 0)
583 totCount = 1; /* avoid division by zero */
584 cumulCount = 0;
585 int r;
586 for (r = 0; r < N_TOP_BLOCKS; r++) {
587 if (topFragID[r] == 0)
588 break;
589 cumulCount += (uint64_t)topPI[r].count;
590 js_LogController.printf("%3d: %5.2f%% %9u %6.2f%% %9llu"
591 " %3d %5u %5u %06u\n",
592 r,
593 (double)topPI[r].count * 100.0 / (double)totCount,
594 topPI[r].count,
595 (double)cumulCount * 100.0 / (double)totCount,
596 (unsigned long long int)cumulCount,
597 topPI[r].nStaticExits,
598 (unsigned int)topPI[r].nCodeBytes,
599 (unsigned int)topPI[r].nExitBytes,
600 topFragID[r]);
601 totSE += (uint32_t)topPI[r].nStaticExits;
602 totCodeB += topPI[r].nCodeBytes;
603 totExitB += topPI[r].nExitBytes;
604 }
605 js_LogController.printf("\nTotal displayed code bytes = %u, "
606 "exit bytes = %u\n"
607 "Total displayed static exits = %d\n\n",
608 (unsigned int)totCodeB, (unsigned int)totExitB, totSE);
609
610 js_LogController.printf("Analysis by exit counts\n\n");
611
612 for (r = 0; r < N_TOP_BLOCKS; r++) {
613 if (topFragID[r] == 0)
614 break;
615 js_LogController.printf("FragID=%06u, total count %u:\n", topFragID[r],
616 topPI[r].count);
617 uint32_t madeItToEnd = topPI[r].count;
618 uint32_t totThisFrag = topPI[r].count;
619 if (totThisFrag == 0)
620 totThisFrag = 1;
621 GuardPI gpi;
622 // visit the guards, in forward order
623 for (Seq<GuardPI>* guards = topPI[r].guards; guards; guards = guards->tail) {
624 gpi = (*guards).head;
625 if (gpi.count == 0)
626 continue;
627 madeItToEnd -= gpi.count;
628 js_LogController.printf(" GuardID=%03u %7u (%5.2f%%)\n",
629 gpi.guardID, gpi.count,
630 100.0 * (double)gpi.count / (double)totThisFrag);
631 }
632 js_LogController.printf(" Looped (%03u) %7u (%5.2f%%)\n",
633 topPI[r].largestGuardID+1,
634 madeItToEnd,
635 100.0 * (double)madeItToEnd / (double)totThisFrag);
636 NanoAssert(madeItToEnd <= topPI[r].count); // else unsigned underflow
637 js_LogController.printf("\n");
638 }
639
640 tm->profTab = NULL;
641 }
642
643 #endif
644
645 /* ----------------------------------------------------------------- */
646
647 #ifdef DEBUG
648 static const char*
649 getExitName(ExitType type)
650 {
651 static const char* exitNames[] =
652 {
653 #define MAKE_EXIT_STRING(x) #x,
654 JS_TM_EXITCODES(MAKE_EXIT_STRING)
655 #undef MAKE_EXIT_STRING
656 NULL
657 };
658
659 JS_ASSERT(type < TOTAL_EXIT_TYPES);
660
661 return exitNames[type];
662 }
663
664 static JSBool FASTCALL
665 PrintOnTrace(char* format, uint32 argc, double *argv)
666 {
667 union {
668 struct {
669 uint32 lo;
670 uint32 hi;
671 } i;
672 double d;
673 char *cstr;
674 JSObject *o;
675 JSString *s;
676 } u;
677
678 #define GET_ARG() JS_BEGIN_MACRO \
679 if (argi >= argc) { \
680 fprintf(out, "[too few args for format]"); \
681 break; \
682 } \
683 u.d = argv[argi++]; \
684 JS_END_MACRO
685
686 FILE *out = stderr;
687
688 uint32 argi = 0;
689 for (char *p = format; *p; ++p) {
690 if (*p != '%') {
691 putc(*p, out);
692 continue;
693 }
694 char ch = *++p;
695 if (!ch) {
696 fprintf(out, "[trailing %%]");
697 continue;
698 }
699
700 switch (ch) {
701 case 'a':
702 GET_ARG();
703 fprintf(out, "[%u:%u 0x%x:0x%x %f]", u.i.lo, u.i.hi, u.i.lo, u.i.hi, u.d);
704 break;
705 case 'd':
706 GET_ARG();
707 fprintf(out, "%d", u.i.lo);
708 break;
709 case 'u':
710 GET_ARG();
711 fprintf(out, "%u", u.i.lo);
712 break;
713 case 'x':
714 GET_ARG();
715 fprintf(out, "%x", u.i.lo);
716 break;
717 case 'f':
718 GET_ARG();
719 fprintf(out, "%f", u.d);
720 break;
721 case 'o':
722 GET_ARG();
723 js_DumpObject(u.o);
724 break;
725 case 's':
726 GET_ARG();
727 {
728 size_t length = u.s->length();
729 // protect against massive spew if u.s is a bad pointer.
730 if (length > 1 << 16)
731 length = 1 << 16;
732 jschar *chars = u.s->chars();
733 for (unsigned i = 0; i < length; ++i) {
734 jschar co = chars[i];
735 if (co < 128)
736 putc(co, out);
737 else if (co < 256)
738 fprintf(out, "\\u%02x", co);
739 else
740 fprintf(out, "\\u%04x", co);
741 }
742 }
743 break;
744 case 'S':
745 GET_ARG();
746 fprintf(out, "%s", u.cstr);
747 break;
748 default:
749 fprintf(out, "[invalid %%%c]", *p);
750 }
751 }
752
753 #undef GET_ARG
754
755 return JS_TRUE;
756 }
757
758 JS_DEFINE_CALLINFO_3(extern, BOOL, PrintOnTrace, CHARPTR, UINT32, DOUBLEPTR, 0, 0)
759
760 // This version is not intended to be called directly: usually it is easier to
761 // use one of the other overloads.
762 void
763 TraceRecorder::tprint(const char *format, int count, nanojit::LIns *insa[])
764 {
765 size_t size = strlen(format) + 1;
766 char *data = (char*) lir->insSkip(size)->payload();
767 memcpy(data, format, size);
768
769 double *args = (double*) lir->insSkip(count * sizeof(double))->payload();
770 for (int i = 0; i < count; ++i) {
771 JS_ASSERT(insa[i]);
772 lir->insStorei(insa[i], INS_CONSTPTR(args), sizeof(double) * i);
773 }
774
775 LIns* args_ins[] = { INS_CONSTPTR(args), INS_CONST(count), INS_CONSTPTR(data) };
776 LIns* call_ins = lir->insCall(&PrintOnTrace_ci, args_ins);
777 guard(false, lir->ins_eq0(call_ins), MISMATCH_EXIT);
778 }
779
780 // Generate a 'printf'-type call from trace for debugging.
781 void
782 TraceRecorder::tprint(const char *format)
783 {
784 LIns* insa[] = { NULL };
785 tprint(format, 0, insa);
786 }
787
788 void
789 TraceRecorder::tprint(const char *format, LIns *ins)
790 {
791 LIns* insa[] = { ins };
792 tprint(format, 1, insa);
793 }
794
795 void
796 TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2)
797 {
798 LIns* insa[] = { ins1, ins2 };
799 tprint(format, 2, insa);
800 }
801
802 void
803 TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2, LIns *ins3)
804 {
805 LIns* insa[] = { ins1, ins2, ins3 };
806 tprint(format, 3, insa);
807 }
808
809 void
810 TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2, LIns *ins3, LIns *ins4)
811 {
812 LIns* insa[] = { ins1, ins2, ins3, ins4 };
813 tprint(format, 4, insa);
814 }
815
816 void
817 TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2, LIns *ins3, LIns *ins4,
818 LIns *ins5)
819 {
820 LIns* insa[] = { ins1, ins2, ins3, ins4, ins5 };
821 tprint(format, 5, insa);
822 }
823
824 void
825 TraceRecorder::tprint(const char *format, LIns *ins1, LIns *ins2, LIns *ins3, LIns *ins4,
826 LIns *ins5, LIns *ins6)
827 {
828 LIns* insa[] = { ins1, ins2, ins3, ins4, ins5, ins6 };
829 tprint(format, 6, insa);
830 }
831 #endif
832
833 /*
834 * The entire VM shares one oracle. Collisions and concurrent updates are
835 * tolerated and worst case cause performance regressions.
836 */
837 static Oracle oracle;
838
839 /*
840 * This confusing and mysterious expression is used for the Tracker. The
841 * tracker's responsibility is to map opaque, 4-byte aligned addresses to LIns
842 * pointers. To do this efficiently, we observe that the addresses of jsvals
843 * living in the interpreter tend to be aggregated close to each other -
844 * usually on the same page (where a tracker page doesn't have to be the same
845 * size as the OS page size, but it's typically similar).
846 *
847 * For every address, we split it into two values: upper bits which represent
848 * the "base", and lower bits which represent an offset against the base. We
849 * create a list of:
850 * struct TrackerPage {
851 * void* base;
852 * LIns* map;
853 * };
854 * The mapping then becomes:
855 * page = page such that Base(address) == page->base,
856 * page->map[Index(address)]
857 *
858 * The size of the map is allocated as N * sizeof(LIns*), where N is
859 * (TRACKER_PAGE_SIZE >> 2). Since the lower two bits are 0, they are always
860 * discounted.
861 *
862 * TRACKER_PAGE_MASK is the "reverse" expression, with a |- 1| to get a mask
863 * which separates an address into the Base and Index bits. It is necessary to
864 * do all this work rather than use TRACKER_PAGE_SIZE - 1, because on 64-bit
865 * platforms the pointer width is twice as large, and only half as many
866 * indexes can fit into TrackerPage::map. So the "Base" grows by one bit, and
867 * the "Index" shrinks by one bit.
868 */
869 #define TRACKER_PAGE_MASK (((TRACKER_PAGE_SIZE / sizeof(void*)) << 2) - 1)
870
871 #define TRACKER_PAGE_SIZE 4096
872
873 Tracker::Tracker()
874 {
875 pagelist = 0;
876 }
877
878 Tracker::~Tracker()
879 {
880 clear();
881 }
882
883 jsuword
884 Tracker::getTrackerPageBase(const void* v) const
885 {
886 return jsuword(v) & ~jsuword(TRACKER_PAGE_MASK);
887 }
888
889 struct Tracker::TrackerPage*
890 Tracker::findTrackerPage(const void* v) const
891 {
892 jsuword base = getTrackerPageBase(v);
893 struct Tracker::TrackerPage* p = pagelist;
894 while (p) {
895 if (p->base == base) {
896 return p;
897 }
898 p = p->next;
899 }
900 return 0;
901 }
902
903 struct Tracker::TrackerPage*
904 Tracker::addTrackerPage(const void* v) {
905 jsuword base = getTrackerPageBase(v);
906 struct Tracker::TrackerPage* p = (struct Tracker::TrackerPage*)
907 calloc(1, sizeof(*p) - sizeof(p->map) + (TRACKER_PAGE_SIZE >> 2) * sizeof(LIns*));
908 p->base = base;
909 p->next = pagelist;
910 pagelist = p;
911 return p;
912 }
913
914 void
915 Tracker::clear()
916 {
917 while (pagelist) {
918 TrackerPage* p = pagelist;
919 pagelist = pagelist->next;
920 free(p);
921 }
922 }
923
924 bool
925 Tracker::has(const void *v) const
926 {
927 return get(v) != NULL;
928 }
929
930 LIns*
931 Tracker::get(const void* v) const
932 {
933 struct Tracker::TrackerPage* p = findTrackerPage(v);
934 if (!p)
935 return NULL;
936 return p->map[(jsuword(v) & TRACKER_PAGE_MASK) >> 2];
937 }
938
939 void
940 Tracker::set(const void* v, LIns* i)
941 {
942 struct Tracker::TrackerPage* p = findTrackerPage(v);
943 if (!p)
944 p = addTrackerPage(v);
945 p->map[(jsuword(v) & TRACKER_PAGE_MASK) >> 2] = i;
946 }
947
948 static inline jsuint
949 argSlots(JSStackFrame* fp)
950 {
951 return JS_MAX(fp->argc, fp->fun->nargs);
952 }
953
954 static inline bool
955 isNumber(jsval v)
956 {
957 return JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v);
958 }
959
960 static inline jsdouble
961 asNumber(jsval v)
962 {
963 JS_ASSERT(isNumber(v));
964 if (JSVAL_IS_DOUBLE(v))
965 return *JSVAL_TO_DOUBLE(v);
966 return (jsdouble)JSVAL_TO_INT(v);
967 }
968
969 static inline bool
970 isInt32(jsval v)
971 {
972 if (!isNumber(v))
973 return false;
974 jsdouble d = asNumber(v);
975 jsint i;
976 return JSDOUBLE_IS_INT(d, i);
977 }
978
979 static inline jsint
980 asInt32(jsval v)
981 {
982 JS_ASSERT(isNumber(v));
983 if (JSVAL_IS_INT(v))
984 return JSVAL_TO_INT(v);
985 #ifdef DEBUG
986 jsint i;
987 JS_ASSERT(JSDOUBLE_IS_INT(*JSVAL_TO_DOUBLE(v), i));
988 #endif
989 return jsint(*JSVAL_TO_DOUBLE(v));
990 }
991
992 /* Return TT_DOUBLE for all numbers (int and double) and the tag otherwise. */
993 static inline JSTraceType
994 GetPromotedType(jsval v)
995 {
996 if (JSVAL_IS_INT(v))
997 return TT_DOUBLE;
998 if (JSVAL_IS_OBJECT(v)) {
999 if (JSVAL_IS_NULL(v))
1000 return TT_NULL;
1001 if (HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(v)))
1002 return TT_FUNCTION;
1003 return TT_OBJECT;
1004 }
1005 uint8_t tag = JSVAL_TAG(v);
1006 JS_ASSERT(tag == JSVAL_DOUBLE || tag == JSVAL_STRING || tag == JSVAL_SPECIAL);
1007 JS_STATIC_ASSERT(static_cast<jsvaltag>(TT_DOUBLE) == JSVAL_DOUBLE);
1008 JS_STATIC_ASSERT(static_cast<jsvaltag>(TT_STRING) == JSVAL_STRING);
1009 JS_STATIC_ASSERT(static_cast<jsvaltag>(TT_PSEUDOBOOLEAN) == JSVAL_SPECIAL);
1010 return JSTraceType(tag);
1011 }
1012
1013 /* Return TT_INT32 for all whole numbers that fit into signed 32-bit and the tag otherwise. */
1014 static inline JSTraceType
1015 getCoercedType(jsval v)
1016 {
1017 if (isInt32(v))
1018 return TT_INT32;
1019 if (JSVAL_IS_OBJECT(v)) {
1020 if (JSVAL_IS_NULL(v))
1021 return TT_NULL;
1022 if (HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(v)))
1023 return TT_FUNCTION;
1024 return TT_OBJECT;
1025 }
1026 uint8_t tag = JSVAL_TAG(v);
1027 JS_ASSERT(tag == JSVAL_DOUBLE || tag == JSVAL_STRING || tag == JSVAL_SPECIAL);
1028 JS_STATIC_ASSERT(static_cast<jsvaltag>(TT_DOUBLE) == JSVAL_DOUBLE);
1029 JS_STATIC_ASSERT(static_cast<jsvaltag>(TT_STRING) == JSVAL_STRING);
1030 JS_STATIC_ASSERT(static_cast<jsvaltag>(TT_PSEUDOBOOLEAN) == JSVAL_SPECIAL);
1031 return JSTraceType(tag);
1032 }
1033
1034 /* Constant seed and accumulate step borrowed from the DJB hash. */
1035
1036 const uintptr_t ORACLE_MASK = ORACLE_SIZE - 1;
1037 JS_STATIC_ASSERT((ORACLE_MASK & ORACLE_SIZE) == 0);
1038
1039 const uintptr_t FRAGMENT_TABLE_MASK = FRAGMENT_TABLE_SIZE - 1;
1040 JS_STATIC_ASSERT((FRAGMENT_TABLE_MASK & FRAGMENT_TABLE_SIZE) == 0);
1041
1042 const uintptr_t HASH_SEED = 5381;
1043
1044 static inline void
1045 HashAccum(uintptr_t& h, uintptr_t i, uintptr_t mask)
1046 {
1047 h = ((h << 5) + h + (mask & i)) & mask;
1048 }
1049
1050 static JS_REQUIRES_STACK inline int
1051 StackSlotHash(JSContext* cx, unsigned slot)
1052 {
1053 uintptr_t h = HASH_SEED;
1054 HashAccum(h, uintptr_t(cx->fp->script), ORACLE_MASK);
1055 HashAccum(h, uintptr_t(cx->fp->regs->pc), ORACLE_MASK);
1056 HashAccum(h, uintptr_t(slot), ORACLE_MASK);
1057 return int(h);
1058 }
1059
1060 static JS_REQUIRES_STACK inline int
1061 GlobalSlotHash(JSContext* cx, unsigned slot)
1062 {
1063 uintptr_t h = HASH_SEED;
1064 JSStackFrame* fp = cx->fp;
1065
1066 while (fp->down)
1067 fp = fp->down;
1068
1069 HashAccum(h, uintptr_t(fp->script), ORACLE_MASK);
1070 HashAccum(h, uintptr_t(OBJ_SHAPE(JS_GetGlobalForObject(cx, fp->scopeChain))), ORACLE_MASK);
1071 HashAccum(h, uintptr_t(slot), ORACLE_MASK);
1072 return int(h);
1073 }
1074
1075 static inline int
1076 PCHash(jsbytecode* pc)
1077 {
1078 return int(uintptr_t(pc) & ORACLE_MASK);
1079 }
1080
1081 Oracle::Oracle()
1082 {
1083 /* Grow the oracle bitsets to their (fixed) size here, once. */
1084 _stackDontDemote.set(ORACLE_SIZE-1);
1085 _globalDontDemote.set(ORACLE_SIZE-1);
1086 clear();
1087 }
1088
1089 /* Tell the oracle that a certain global variable should not be demoted. */
1090 JS_REQUIRES_STACK void
1091 Oracle::markGlobalSlotUndemotable(JSContext* cx, unsigned slot)
1092 {
1093 _globalDontDemote.set(GlobalSlotHash(cx, slot));
1094 }
1095
1096 /* Consult with the oracle whether we shouldn't demote a certain global variable. */
1097 JS_REQUIRES_STACK bool
1098 Oracle::isGlobalSlotUndemotable(JSContext* cx, unsigned slot) const
1099 {
1100 return _globalDontDemote.get(GlobalSlotHash(cx, slot));
1101 }
1102
1103 /* Tell the oracle that a certain slot at a certain stack slot should not be demoted. */
1104 JS_REQUIRES_STACK void
1105 Oracle::markStackSlotUndemotable(JSContext* cx, unsigned slot)
1106 {
1107 _stackDontDemote.set(StackSlotHash(cx, slot));
1108 }
1109
1110 /* Consult with the oracle whether we shouldn't demote a certain slot. */
1111 JS_REQUIRES_STACK bool
1112 Oracle::isStackSlotUndemotable(JSContext* cx, unsigned slot) const
1113 {
1114 return _stackDontDemote.get(StackSlotHash(cx, slot));
1115 }
1116
1117 /* Tell the oracle that a certain slot at a certain bytecode location should not be demoted. */
1118 void
1119 Oracle::markInstructionUndemotable(jsbytecode* pc)
1120 {
1121 _pcDontDemote.set(PCHash(pc));
1122 }
1123
1124 /* Consult with the oracle whether we shouldn't demote a certain bytecode location. */
1125 bool
1126 Oracle::isInstructionUndemotable(jsbytecode* pc) const
1127 {
1128 return _pcDontDemote.get(PCHash(pc));
1129 }
1130
1131 void
1132 Oracle::clearDemotability()
1133 {
1134 _stackDontDemote.reset();
1135 _globalDontDemote.reset();
1136 _pcDontDemote.reset();
1137 }
1138
1139 JS_REQUIRES_STACK static JS_INLINE void
1140 MarkSlotUndemotable(JSContext* cx, TreeInfo* ti, unsigned slot)
1141 {
1142 if (slot < ti->nStackTypes) {
1143 oracle.markStackSlotUndemotable(cx, slot);
1144 return;
1145 }
1146
1147 uint16* gslots = ti->globalSlots->data();
1148 oracle.markGlobalSlotUndemotable(cx, gslots[slot - ti->nStackTypes]);
1149 }
1150
1151 static JS_REQUIRES_STACK inline bool
1152 IsSlotUndemotable(JSContext* cx, TreeInfo* ti, unsigned slot)
1153 {
1154 if (slot < ti->nStackTypes)
1155 return oracle.isStackSlotUndemotable(cx, slot);
1156
1157 uint16* gslots = ti->globalSlots->data();
1158 return oracle.isGlobalSlotUndemotable(cx, gslots[slot - ti->nStackTypes]);
1159 }
1160
1161 struct PCHashEntry : public JSDHashEntryStub {
1162 size_t count;
1163 };
1164
1165 #define PC_HASH_COUNT 1024
1166
1167 static void
1168 Blacklist(jsbytecode* pc)
1169 {
1170 AUDIT(blacklisted);
1171 JS_ASSERT(*pc == JSOP_TRACE || *pc == JSOP_NOP);
1172 *pc = JSOP_NOP;
1173 }
1174
1175 static void
1176 Backoff(JSContext *cx, jsbytecode* pc, Fragment* tree = NULL)
1177 {
1178 JSDHashTable *table = &JS_TRACE_MONITOR(cx).recordAttempts;
1179
1180 if (table->ops) {
1181 PCHashEntry *entry = (PCHashEntry *)
1182 JS_DHashTableOperate(table, pc, JS_DHASH_ADD);
1183
1184 if (entry) {
1185 if (!entry->key) {
1186 entry->key = pc;
1187 JS_ASSERT(entry->count == 0);
1188 }
1189 JS_ASSERT(JS_DHASH_ENTRY_IS_LIVE(&(entry->hdr)));
1190 if (entry->count++ > (BL_ATTEMPTS * MAXPEERS)) {
1191 entry->count = 0;
1192 Blacklist(pc);
1193 return;
1194 }
1195 }
1196 }
1197
1198 if (tree) {
1199 tree->hits() -= BL_BACKOFF;
1200
1201 /*
1202 * In case there is no entry or no table (due to OOM) or some
1203 * serious imbalance in the recording-attempt distribution on a
1204 * multitree, give each tree another chance to blacklist here as
1205 * well.
1206 */
1207 if (++tree->recordAttempts > BL_ATTEMPTS)
1208 Blacklist(pc);
1209 }
1210 }
1211
1212 static void
1213 ResetRecordingAttempts(JSContext *cx, jsbytecode* pc)
1214 {
1215 JSDHashTable *table = &JS_TRACE_MONITOR(cx).recordAttempts;
1216 if (table->ops) {
1217 PCHashEntry *entry = (PCHashEntry *)
1218 JS_DHashTableOperate(table, pc, JS_DHASH_LOOKUP);
1219
1220 if (JS_DHASH_ENTRY_IS_FREE(&(entry->hdr)))
1221 return;
1222 JS_ASSERT(JS_DHASH_ENTRY_IS_LIVE(&(entry->hdr)));
1223 entry->count = 0;
1224 }
1225 }
1226
1227 static inline size_t
1228 FragmentHash(const void *ip, JSObject* globalObj, uint32 globalShape, uint32 argc)
1229 {
1230 uintptr_t h = HASH_SEED;
1231 HashAccum(h, uintptr_t(ip), FRAGMENT_TABLE_MASK);
1232 HashAccum(h, uintptr_t(globalObj), FRAGMENT_TABLE_MASK);
1233 HashAccum(h, uintptr_t(globalShape), FRAGMENT_TABLE_MASK);
1234 HashAccum(h, uintptr_t(argc), FRAGMENT_TABLE_MASK);
1235 return size_t(h);
1236 }
1237
1238 /*
1239 * argc is cx->fp->argc at the trace loop header, i.e., the number of arguments
1240 * pushed for the innermost JS frame. This is required as part of the fragment
1241 * key because the fragment will write those arguments back to the interpreter
1242 * stack when it exits, using its typemap, which implicitly incorporates a
1243 * given value of argc. Without this feature, a fragment could be called as an
1244 * inner tree with two different values of argc, and entry type checking or
1245 * exit frame synthesis could crash.
1246 */
1247 struct VMFragment : public Fragment
1248 {
1249 VMFragment(const void* _ip, JSObject* _globalObj, uint32 _globalShape, uint32 _argc
1250 verbose_only(, uint32_t profFragID)) :
1251 Fragment(_ip verbose_only(, profFragID)),
1252 first(NULL),
1253 next(NULL),
1254 peer(NULL),
1255 globalObj(_globalObj),
1256 globalShape(_globalShape),
1257 argc(_argc)
1258 { }
1259
1260 inline TreeInfo* getTreeInfo() {
1261 return (TreeInfo*)vmprivate;
1262 }
1263
1264 VMFragment* first;
1265 VMFragment* next;
1266 VMFragment* peer;
1267 JSObject* globalObj;
1268 uint32 globalShape;
1269 uint32 argc;
1270 };
1271
1272 static VMFragment*
1273 getVMFragment(JSTraceMonitor* tm, const void *ip, JSObject* globalObj, uint32 globalShape,
1274 uint32 argc)
1275 {
1276 size_t h = FragmentHash(ip, globalObj, globalShape, argc);
1277 VMFragment* vf = tm->vmfragments[h];
1278 while (vf &&
1279 ! (vf->globalObj == globalObj &&
1280 vf->globalShape == globalShape &&
1281 vf->ip == ip &&
1282 vf->argc == argc)) {
1283 vf = vf->next;
1284 }
1285 return vf;
1286 }
1287
1288 static VMFragment*
1289 getLoop(JSTraceMonitor* tm, const void *ip, JSObject* globalObj, uint32 globalShape, uint32 argc)
1290 {
1291 return getVMFragment(tm, ip, globalObj, globalShape, argc);
1292 }
1293
1294 static VMFragment*
1295 getAnchor(JSTraceMonitor* tm, const void *ip, JSObject* globalObj, uint32 globalShape, uint32 argc)
1296 {
1297 verbose_only(
1298 uint32_t profFragID = (js_LogController.lcbits & LC_FragProfile)
1299 ? (++(tm->lastFragID)) : 0;
1300 )
1301 VMFragment *f = new (*tm->dataAlloc) VMFragment(ip, globalObj, globalShape, argc
1302 verbose_only(, profFragID));
1303 JS_ASSERT(f);
1304
1305 VMFragment *p = getVMFragment(tm, ip, globalObj, globalShape, argc);
1306
1307 if (p) {
1308 f->first = p;
1309 /* append at the end of the peer list */
1310 VMFragment* next;
1311 while ((next = p->peer) != NULL)
1312 p = next;
1313 p->peer = f;
1314 } else {
1315 /* this is the first fragment */
1316 f->first = f;
1317 size_t h = FragmentHash(ip, globalObj, globalShape, argc);
1318 f->next = tm->vmfragments[h];
1319 tm->vmfragments[h] = f;
1320 }
1321 f->root = f;
1322 return f;
1323 }
1324
1325 #ifdef DEBUG
1326 static void
1327 AssertTreeIsUnique(JSTraceMonitor* tm, VMFragment* f, TreeInfo* ti)
1328 {
1329 JS_ASSERT(f->root == f);
1330
1331 /*
1332 * Check for duplicate entry type maps. This is always wrong and hints at
1333 * trace explosion since we are trying to stabilize something without
1334 * properly connecting peer edges.
1335 */
1336 TreeInfo* ti_other;
1337 for (VMFragment* peer = getLoop(tm, f->ip, f->globalObj, f->globalShape, f->argc);
1338 peer != NULL;
1339 peer = peer->peer) {
1340 if (!peer->code() || peer == f)
1341 continue;
1342 ti_other = (TreeInfo*)peer->vmprivate;
1343 JS_ASSERT(ti_other);
1344 JS_ASSERT(!ti->typeMap.matches(ti_other->typeMap));
1345 }
1346 }
1347 #endif
1348
1349 static void
1350 AttemptCompilation(JSContext *cx, JSTraceMonitor* tm, JSObject* globalObj, jsbytecode* pc,
1351 uint32 argc)
1352 {
1353 /* If we already permanently blacklisted the location, undo that. */
1354 JS_ASSERT(*pc == JSOP_NOP || *pc == JSOP_TRACE);
1355 *pc = JSOP_TRACE;
1356 ResetRecordingAttempts(cx, pc);
1357
1358 /* Breathe new life into all peer fragments at the designated loop header. */
1359 VMFragment* f = (VMFragment*)getLoop(tm, pc, globalObj, OBJ_SHAPE(globalObj), argc);
1360 if (!f) {
1361 /*
1362 * If the global object's shape changed, we can't easily find the
1363 * corresponding loop header via a hash table lookup. In this
1364 * we simply bail here and hope that the fragment has another
1365 * outstanding compilation attempt. This case is extremely rare.
1366 */
1367 return;
1368 }
1369 JS_ASSERT(f->root == f);
1370 f = f->first;
1371 while (f) {
1372 JS_ASSERT(f->root == f);
1373 --f->recordAttempts;
1374 f->hits() = HOTLOOP;
1375 f = f->peer;
1376 }
1377 }
1378
1379 // Forward declarations.
1380 JS_DEFINE_CALLINFO_1(static, DOUBLE, i2f, INT32, 1, 1)
1381 JS_DEFINE_CALLINFO_1(static, DOUBLE, u2f, UINT32, 1, 1)
1382
1383 static bool
1384 isi2f(LIns* i)
1385 {
1386 if (i->isop(LIR_i2f))
1387 return true;
1388
1389 if (nanojit::AvmCore::config.soft_float &&
1390 i->isop(LIR_qjoin) &&
1391 i->oprnd1()->isop(LIR_pcall) &&
1392 i->oprnd2()->isop(LIR_callh)) {
1393 if (i->oprnd1()->callInfo() == &i2f_ci)
1394 return true;
1395 }
1396
1397 return false;
1398 }
1399
1400 static bool
1401 isu2f(LIns* i)
1402 {
1403 if (i->isop(LIR_u2f))
1404 return true;
1405
1406 if (nanojit::AvmCore::config.soft_float &&
1407 i->isop(LIR_qjoin) &&
1408 i->oprnd1()->isop(LIR_pcall) &&
1409 i->oprnd2()->isop(LIR_callh)) {
1410 if (i->oprnd1()->callInfo() == &u2f_ci)
1411 return true;
1412 }
1413
1414 return false;
1415 }
1416
1417 static LIns*
1418 iu2fArg(LIns* i)
1419 {
1420 if (nanojit::AvmCore::config.soft_float &&
1421 i->isop(LIR_qjoin)) {
1422 return i->oprnd1()->arg(0);
1423 }
1424
1425 return i->oprnd1();
1426 }
1427
1428 static LIns*
1429 demote(LirWriter *out, LIns* i)
1430 {
1431 if (i->isCall())
1432 return i->callArgN(0);
1433 if (isi2f(i) || isu2f(i))
1434 return iu2fArg(i);
1435 if (i->isconst())
1436 return i;
1437 JS_ASSERT(i->isconstf());
1438 double cf = i->imm64f();
1439 int32_t ci = cf > 0x7fffffff ? uint32_t(cf) : int32_t(cf);
1440 return out->insImm(ci);
1441 }
1442
1443 static bool
1444 isPromoteInt(LIns* i)
1445 {
1446 if (isi2f(i) || i->isconst())
1447 return true;
1448 if (!i->isconstf())
1449 return false;
1450 jsdouble d = i->imm64f();
1451 return d == jsdouble(jsint(d)) && !JSDOUBLE_IS_NEGZERO(d);
1452 }
1453
1454 static bool
1455 isPromoteUint(LIns* i)
1456 {
1457 if (isu2f(i) || i->isconst())
1458 return true;
1459 if (!i->isconstf())
1460 return false;
1461 jsdouble d = i->imm64f();
1462 return d == jsdouble(jsuint(d)) && !JSDOUBLE_IS_NEGZERO(d);
1463 }
1464
1465 static bool
1466 isPromote(LIns* i)
1467 {
1468 return isPromoteInt(i) || isPromoteUint(i);
1469 }
1470
1471 static bool
1472 IsConst(LIns* i, int32_t c)
1473 {
1474 return i->isconst() && i->imm32() == c;
1475 }
1476
1477 /*
1478 * Determine whether this operand is guaranteed to not overflow the specified
1479 * integer operation.
1480 */
1481 static bool
1482 IsOverflowSafe(LOpcode op, LIns* i)
1483 {
1484 LIns* c;
1485 switch (op) {
1486 case LIR_add:
1487 case LIR_sub:
1488 return (i->isop(LIR_and) && ((c = i->oprnd2())->isconst()) &&
1489 ((c->imm32() & 0xc0000000) == 0)) ||
1490 (i->isop(LIR_rsh) && ((c = i->oprnd2())->isconst()) &&
1491 ((c->imm32() > 0)));
1492 default:
1493 JS_ASSERT(op == LIR_mul);
1494 }
1495 return (i->isop(LIR_and) && ((c = i->oprnd2())->isconst()) &&
1496 ((c->imm32() & 0xffff0000) == 0)) ||
1497 (i->isop(LIR_ush) && ((c = i->oprnd2())->isconst()) &&
1498 ((c->imm32() >= 16)));
1499 }
1500
1501 /* soft float support */
1502
1503 static jsdouble FASTCALL
1504 fneg(jsdouble x)
1505 {
1506 return -x;
1507 }
1508 JS_DEFINE_CALLINFO_1(static, DOUBLE, fneg, DOUBLE, 1, 1)
1509
1510 static jsdouble FASTCALL
1511 i2f(int32 i)
1512 {
1513 return i;
1514 }
1515
1516 static jsdouble FASTCALL
1517 u2f(jsuint u)
1518 {
1519 return u;
1520 }
1521
1522 static int32 FASTCALL
1523 fcmpeq(jsdouble x, jsdouble y)
1524 {
1525 return x==y;
1526 }
1527 JS_DEFINE_CALLINFO_2(static, INT32, fcmpeq, DOUBLE, DOUBLE, 1, 1)
1528
1529 static int32 FASTCALL
1530 fcmplt(jsdouble x, jsdouble y)
1531 {
1532 return x < y;
1533 }
1534 JS_DEFINE_CALLINFO_2(static, INT32, fcmplt, DOUBLE, DOUBLE, 1, 1)
1535
1536 static int32 FASTCALL
1537 fcmple(jsdouble x, jsdouble y)
1538 {
1539 return x <= y;
1540 }
1541 JS_DEFINE_CALLINFO_2(static, INT32, fcmple, DOUBLE, DOUBLE, 1, 1)
1542
1543 static int32 FASTCALL
1544 fcmpgt(jsdouble x, jsdouble y)
1545 {
1546 return x > y;
1547 }
1548 JS_DEFINE_CALLINFO_2(static, INT32, fcmpgt, DOUBLE, DOUBLE, 1, 1)
1549
1550 static int32 FASTCALL
1551 fcmpge(jsdouble x, jsdouble y)
1552 {
1553 return x >= y;
1554 }
1555 JS_DEFINE_CALLINFO_2(static, INT32, fcmpge, DOUBLE, DOUBLE, 1, 1)
1556
1557 static jsdouble FASTCALL
1558 fmul(jsdouble x, jsdouble y)
1559 {
1560 return x * y;
1561 }
1562 JS_DEFINE_CALLINFO_2(static, DOUBLE, fmul, DOUBLE, DOUBLE, 1, 1)
1563
1564 static jsdouble FASTCALL
1565 fadd(jsdouble x, jsdouble y)
1566 {
1567 return x + y;
1568 }
1569 JS_DEFINE_CALLINFO_2(static, DOUBLE, fadd, DOUBLE, DOUBLE, 1, 1)
1570
1571 static jsdouble FASTCALL
1572 fdiv(jsdouble x, jsdouble y)
1573 {
1574 return x / y;
1575 }
1576 JS_DEFINE_CALLINFO_2(static, DOUBLE, fdiv, DOUBLE, DOUBLE, 1, 1)
1577
1578 static jsdouble FASTCALL
1579 fsub(jsdouble x, jsdouble y)
1580 {
1581 return x - y;
1582 }
1583 JS_DEFINE_CALLINFO_2(static, DOUBLE, fsub, DOUBLE, DOUBLE, 1, 1)
1584
1585 // replace fpu ops with function calls
1586 class SoftFloatFilter: public LirWriter
1587 {
1588 public:
1589 SoftFloatFilter(LirWriter *out) : LirWriter(out)
1590 {}
1591
1592 LIns *hi(LIns *q) {
1593 return ins1(LIR_qhi, q);
1594 }
1595 LIns *lo(LIns *q) {
1596 return ins1(LIR_qlo, q);
1597 }
1598
1599 LIns *split(LIns *a) {
1600 if (a->isQuad() && !a->isop(LIR_qjoin)) {
1601 // all quad-sized args must be qjoin's for soft-float
1602 a = ins2(LIR_qjoin, lo(a), hi(a));
1603 }
1604 return a;
1605 }
1606
1607 LIns *split(const CallInfo *call, LInsp args[]) {
1608 LIns *lo = out->insCall(call, args);
1609 LIns *hi = out->ins1(LIR_callh, lo);
1610 return out->ins2(LIR_qjoin, lo, hi);
1611 }
1612
1613 LIns *fcall1(const CallInfo *call, LIns *a) {
1614 LIns *args[] = { split(a) };
1615 return split(call, args);
1616 }
1617
1618 LIns *fcall2(const CallInfo *call, LIns *a, LIns *b) {
1619 LIns *args[] = { split(b), split(a) };
1620 return split(call, args);
1621 }
1622
1623 LIns *fcmp(const CallInfo *call, LIns *a, LIns *b) {
1624 LIns *args[] = { split(b), split(a) };
1625 return out->ins2(LIR_eq, out->insCall(call, args), out->insImm(1));
1626 }
1627
1628 LIns *ins1(LOpcode op, LIns *a) {
1629 switch (op) {
1630 case LIR_i2f:
1631 return fcall1(&i2f_ci, a);
1632 case LIR_u2f:
1633 return fcall1(&u2f_ci, a);
1634 case LIR_fneg:
1635 return fcall1(&fneg_ci, a);
1636 case LIR_fret:
1637 return out->ins1(op, split(a));
1638 default:
1639 return out->ins1(op, a);
1640 }
1641 }
1642
1643 LIns *ins2(LOpcode op, LIns *a, LIns *b) {
1644 switch (op) {
1645 case LIR_fadd:
1646 return fcall2(&fadd_ci, a, b);
1647 case LIR_fsub:
1648 return fcall2(&fsub_ci, a, b);
1649 case LIR_fmul:
1650 return fcall2(&fmul_ci, a, b);
1651 case LIR_fdiv:
1652 return fcall2(&fdiv_ci, a, b);
1653 case LIR_feq:
1654 return fcmp(&fcmpeq_ci, a, b);
1655 case LIR_flt:
1656 return fcmp(&fcmplt_ci, a, b);
1657 case LIR_fgt:
1658 return fcmp(&fcmpgt_ci, a, b);
1659 case LIR_fle:
1660 return fcmp(&fcmple_ci, a, b);
1661 case LIR_fge:
1662 return fcmp(&fcmpge_ci, a, b);
1663 default:
1664 ;
1665 }
1666 return out->ins2(op, a, b);
1667 }
1668
1669 LIns *insCall(const CallInfo *ci, LInsp args[]) {
1670 uint32_t argt = ci->_argtypes;
1671
1672 for (uint32_t i = 0, argsizes = argt >> ARGSIZE_SHIFT; argsizes != 0; i++, argsizes >>= ARGSIZE_SHIFT)
1673 args[i] = split(args[i]);
1674
1675 if ((argt & ARGSIZE_MASK_ANY) == ARGSIZE_F) {
1676 // this function returns a double as two 32bit values, so replace
1677 // call with qjoin(qhi(call), call)
1678 return split(ci, args);
1679 } else {
1680 return out->insCall(ci, args);
1681 }
1682 }
1683 };
1684
1685 class FuncFilter: public LirWriter
1686 {
1687 public:
1688 FuncFilter(LirWriter* out):
1689 LirWriter(out)
1690 {
1691 }
1692
1693 LIns* ins2(LOpcode v, LIns* s0, LIns* s1)
1694 {
1695 if (s0 == s1 && v == LIR_feq) {
1696 if (isPromote(s0)) {
1697 // double(int) and double(uint) cannot be nan
1698 return insImm(1);
1699 }
1700 if (s0->isop(LIR_fmul) || s0->isop(LIR_fsub) || s0->isop(LIR_fadd)) {
1701 LIns* lhs = s0->oprnd1();
1702 LIns* rhs = s0->oprnd2();
1703 if (isPromote(lhs) && isPromote(rhs)) {
1704 // add/sub/mul promoted ints can't be nan
1705 return insImm(1);
1706 }
1707 }
1708 } else if (LIR_feq <= v && v <= LIR_fge) {
1709 if (isPromoteInt(s0) && isPromoteInt(s1)) {
1710 // demote fcmp to cmp
1711 v = LOpcode(v + (LIR_eq - LIR_feq));
1712 return out->ins2(v, demote(out, s0), demote(out, s1));
1713 } else if (isPromoteUint(s0) && isPromoteUint(s1)) {
1714 // uint compare
1715 v = LOpcode(v + (LIR_eq - LIR_feq));
1716 if (v != LIR_eq)
1717 v = LOpcode(v + (LIR_ult - LIR_lt)); // cmp -> ucmp
1718 return out->ins2(v, demote(out, s0), demote(out, s1));
1719 }
1720 } else if (v == LIR_or &&
1721 s0->isop(LIR_lsh) && IsConst(s0->oprnd2(), 16) &&
1722 s1->isop(LIR_and) && IsConst(s1->oprnd2(), 0xffff)) {
1723 LIns* msw = s0->oprnd1();
1724 LIns* lsw = s1->oprnd1();
1725 LIns* x;
1726 LIns* y;
1727 if (lsw->isop(LIR_add) &&
1728 lsw->oprnd1()->isop(LIR_and) &&
1729 lsw->oprnd2()->isop(LIR_and) &&
1730 IsConst(lsw->oprnd1()->oprnd2(), 0xffff) &&
1731 IsConst(lsw->oprnd2()->oprnd2(), 0xffff) &&
1732 msw->isop(LIR_add) &&
1733 msw->oprnd1()->isop(LIR_add) &&
1734 msw->oprnd2()->isop(LIR_rsh) &&
1735 msw->oprnd1()->oprnd1()->isop(LIR_rsh) &&
1736 msw->oprnd1()->oprnd2()->isop(LIR_rsh) &&
1737 IsConst(msw->oprnd2()->oprnd2(), 16) &&
1738 IsConst(msw->oprnd1()->oprnd1()->oprnd2(), 16) &&
1739 IsConst(msw->oprnd1()->oprnd2()->oprnd2(), 16) &&
1740 (x = lsw->oprnd1()->oprnd1()) == msw->oprnd1()->oprnd1()->oprnd1() &&
1741 (y = lsw->oprnd2()->oprnd1()) == msw->oprnd1()->oprnd2()->oprnd1() &&
1742 lsw == msw->oprnd2()->oprnd1()) {
1743 return out->ins2(LIR_add, x, y);
1744 }
1745 }
1746
1747 return out->ins2(v, s0, s1);
1748 }
1749
1750 LIns* insCall(const CallInfo *ci, LIns* args[])
1751 {
1752 if (ci == &js_DoubleToUint32_ci) {
1753 LIns* s0 = args[0];
1754 if (s0->isconstf())
1755 return out->insImm(js_DoubleToECMAUint32(s0->imm64f()));
1756 if (isi2f(s0) || isu2f(s0))
1757 return iu2fArg(s0);
1758 } else if (ci == &js_DoubleToInt32_ci) {
1759 LIns* s0 = args[0];
1760 if (s0->isconstf())
1761 return out->insImm(js_DoubleToECMAInt32(s0->imm64f()));
1762 if (s0->isop(LIR_fadd) || s0->isop(LIR_fsub)) {
1763 LIns* lhs = s0->oprnd1();
1764 LIns* rhs = s0->oprnd2();
1765 if (isPromote(lhs) && isPromote(rhs)) {
1766 LOpcode op = LOpcode(s0->opcode() & ~LIR64);
1767 return out->ins2(op, demote(out, lhs), demote(out, rhs));
1768 }
1769 }
1770 if (isi2f(s0) || isu2f(s0))
1771 return iu2fArg(s0);
1772
1773 // XXX ARM -- check for qjoin(call(UnboxDouble),call(UnboxDouble))
1774 if (s0->isCall()) {
1775 const CallInfo* ci2 = s0->callInfo();
1776 if (ci2 == &js_UnboxDouble_ci) {
1777 LIns* args2[] = { s0->callArgN(0) };
1778 return out->insCall(&js_UnboxInt32_ci, args2);
1779 } else if (ci2 == &js_StringToNumber_ci) {
1780 // callArgN's ordering is that as seen by the builtin, not as stored in
1781 // args here. True story!
1782 LIns* args2[] = { s0->callArgN(1), s0->callArgN(0) };
1783 return out->insCall(&js_StringToInt32_ci, args2);
1784 } else if (ci2 == &js_String_p_charCodeAt0_ci) {
1785 // Use a fast path builtin for a charCodeAt that converts to an int right away.
1786 LIns* args2[] = { s0->callArgN(0) };
1787 return out->insCall(&js_String_p_charCodeAt0_int_ci, args2);
1788 } else if (ci2 == &js_String_p_charCodeAt_ci) {
1789 LIns* idx = s0->callArgN(1);
1790 // If the index is not already an integer, force it to be an integer.
1791 idx = isPromote(idx)
1792 ? demote(out, idx)
1793 : out->insCall(&js_DoubleToInt32_ci, &idx);
1794 LIns* args2[] = { idx, s0->callArgN(0) };
1795 return out->insCall(&js_String_p_charCodeAt_int_ci, args2);
1796 }
1797 }
1798 } else if (ci == &js_BoxDouble_ci) {
1799 LIns* s0 = args[0];
1800 JS_ASSERT(s0->isQuad());
1801 if (isPromoteInt(s0)) {
1802 LIns* args2[] = { demote(out, s0), args[1] };
1803 return out->insCall(&js_BoxInt32_ci, args2);
1804 }
1805 if (s0->isCall() && s0->callInfo() == &js_UnboxDouble_ci)
1806 return s0->callArgN(0);
1807 }
1808 return out->insCall(ci, args);
1809 }
1810 };
1811
1812 /*
1813 * Visit the values in the given JSStackFrame that the tracer cares about. This
1814 * visitor function is (implicitly) the primary definition of the native stack
1815 * area layout. There are a few other independent pieces of code that must be
1816 * maintained to assume the same layout. They are marked like this:
1817 *
1818 * Duplicate native stack layout computation: see VisitFrameSlots header comment.
1819 */
1820 template <typename Visitor>
1821 static JS_REQUIRES_STACK bool
1822 VisitFrameSlots(Visitor &visitor, unsigned depth, JSStackFrame *fp,
1823 JSStackFrame *up)
1824 {
1825 if (depth > 0 && !VisitFrameSlots(visitor, depth-1, fp->down, fp))
1826 return false;
1827
1828 if (fp->argv) {
1829 if (depth == 0) {
1830 visitor.setStackSlotKind("args");
1831 if (!visitor.visitStackSlots(&fp->argv[-2], argSlots(fp) + 2, fp))
1832 return false;
1833 }
1834 visitor.setStackSlotKind("arguments");
1835 if (!visitor.visitStackSlots(&fp->argsobj, 1, fp))
1836 return false;
1837 visitor.setStackSlotKind("var");
1838 if (!visitor.visitStackSlots(fp->slots, fp->script->nfixed, fp))
1839 return false;
1840 }
1841 visitor.setStackSlotKind("stack");
1842 JS_ASSERT(fp->regs->sp >= StackBase(fp));
1843 if (!visitor.visitStackSlots(StackBase(fp),
1844 size_t(fp->regs->sp - StackBase(fp)),
1845 fp)) {
1846 return false;
1847 }
1848 if (up) {
1849 int missing = up->fun->nargs - up->argc;
1850 if (missing > 0) {
1851 visitor.setStackSlotKind("missing");
1852 if (!visitor.visitStackSlots(fp->regs->sp, size_t(missing), fp))
1853 return false;
1854 }
1855 }
1856 return true;
1857 }
1858
1859 template <typename Visitor>
1860 static JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
1861 VisitStackSlots(Visitor &visitor, JSContext *cx, unsigned callDepth)
1862 {
1863 return VisitFrameSlots(visitor, callDepth, cx->fp, NULL);
1864 }
1865
1866 template <typename Visitor>
1867 static JS_REQUIRES_STACK JS_ALWAYS_INLINE void
1868 VisitGlobalSlots(Visitor &visitor, JSContext *cx, JSObject *globalObj,
1869 unsigned ngslots, uint16 *gslots)
1870 {
1871 for (unsigned n = 0; n < ngslots; ++n) {
1872 unsigned slot = gslots[n];
1873 visitor.visitGlobalSlot(&STOBJ_GET_SLOT(globalObj, slot), n, slot);
1874 }
1875 }
1876
1877 class AdjustCallerTypeVisitor;
1878
1879 template <typename Visitor>
1880 static JS_REQUIRES_STACK JS_ALWAYS_INLINE void
1881 VisitGlobalSlots(Visitor &visitor, JSContext *cx, SlotList &gslots)
1882 {
1883 VisitGlobalSlots(visitor, cx, JS_GetGlobalForObject(cx, cx->fp->scopeChain),
1884 gslots.length(), gslots.data());
1885 }
1886
1887
1888 template <typename Visitor>
1889 static JS_REQUIRES_STACK JS_ALWAYS_INLINE void
1890 VisitSlots(Visitor& visitor, JSContext* cx, JSObject* globalObj,
1891 unsigned callDepth, unsigned ngslots, uint16* gslots)
1892 {
1893 if (VisitStackSlots(visitor, cx, callDepth))
1894 VisitGlobalSlots(visitor, cx, globalObj, ngslots, gslots);
1895 }
1896
1897 template <typename Visitor>
1898 static JS_REQUIRES_STACK JS_ALWAYS_INLINE void
1899 VisitSlots(Visitor& visitor, JSContext* cx, unsigned callDepth,
1900 unsigned ngslots, uint16* gslots)
1901 {
1902 VisitSlots(visitor, cx, JS_GetGlobalForObject(cx, cx->fp->scopeChain),
1903 callDepth, ngslots, gslots);
1904 }
1905
1906 template <typename Visitor>
1907 static JS_REQUIRES_STACK JS_ALWAYS_INLINE void
1908 VisitSlots(Visitor &visitor, JSContext *cx, JSObject *globalObj,
1909 unsigned callDepth, const SlotList& slots)
1910 {
1911 VisitSlots(visitor, cx, globalObj, callDepth, slots.length(),
1912 slots.data());
1913 }
1914
1915 template <typename Visitor>
1916 static JS_REQUIRES_STACK JS_ALWAYS_INLINE void
1917 VisitSlots(Visitor &visitor, JSContext *cx, unsigned callDepth,
1918 const SlotList& slots)
1919 {
1920 VisitSlots(visitor, cx, JS_GetGlobalForObject(cx, cx->fp->scopeChain),
1921 callDepth, slots.length(), slots.data());
1922 }
1923
1924
1925 class SlotVisitorBase {
1926 #ifdef JS_JIT_SPEW
1927 protected:
1928 char const *mStackSlotKind;
1929 public:
1930 SlotVisitorBase() : mStackSlotKind(NULL) {}
1931 JS_ALWAYS_INLINE const char *stackSlotKind() { return mStackSlotKind; }
1932 JS_ALWAYS_INLINE void setStackSlotKind(char const *k) {
1933 mStackSlotKind = k;
1934 }
1935 #else
1936 public:
1937 JS_ALWAYS_INLINE const char *stackSlotKind() { return NULL; }
1938 JS_ALWAYS_INLINE void setStackSlotKind(char const *k) {}
1939 #endif
1940 };
1941
1942 struct CountSlotsVisitor : public SlotVisitorBase
1943 {
1944 unsigned mCount;
1945 bool mDone;
1946 jsval* mStop;
1947 public:
1948 JS_ALWAYS_INLINE CountSlotsVisitor(jsval* stop = NULL) :
1949 mCount(0),
1950 mDone(false),
1951 mStop(stop)
1952 {}
1953
1954 JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
1955 visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) {
1956 if (mDone)
1957 return false;
1958 if (mStop && size_t(mStop - vp) < count) {
1959 mCount += size_t(mStop - vp);
1960 mDone = true;
1961 return false;
1962 }
1963 mCount += count;
1964 return true;
1965 }
1966
1967 JS_ALWAYS_INLINE unsigned count() {
1968 return mCount;
1969 }
1970
1971 JS_ALWAYS_INLINE bool stopped() {
1972 return mDone;
1973 }
1974 };
1975
1976 /*
1977 * Calculate the total number of native frame slots we need from this frame all
1978 * the way back to the entry frame, including the current stack usage.
1979 */
1980 JS_REQUIRES_STACK unsigned
1981 NativeStackSlots(JSContext *cx, unsigned callDepth)
1982 {
1983 JSStackFrame* fp = cx->fp;
1984 unsigned slots = 0;
1985 unsigned depth = callDepth;
1986 for (;;) {
1987 /*
1988 * Duplicate native stack layout computation: see VisitFrameSlots
1989 * header comment.
1990 */
1991 unsigned operands = fp->regs->sp - StackBase(fp);
1992 slots += operands;
1993 if (fp->argv)
1994 slots += fp->script->nfixed + 1 /*argsobj*/;
1995 if (depth-- == 0) {
1996 if (fp->argv)
1997 slots += 2/*callee,this*/ + argSlots(fp);
1998 #ifdef DEBUG
1999 CountSlotsVisitor visitor;
2000 VisitStackSlots(visitor, cx, callDepth);
2001 JS_ASSERT(visitor.count() == slots && !visitor.stopped());
2002 #endif
2003 return slots;
2004 }
2005 JSStackFrame* fp2 = fp;
2006 fp = fp->down;
2007 int missing = fp2->fun->nargs - fp2->argc;
2008 if (missing > 0)
2009 slots += missing;
2010 }
2011 JS_NOT_REACHED("NativeStackSlots");
2012 }
2013
2014 class CaptureTypesVisitor : public SlotVisitorBase
2015 {
2016 JSContext* mCx;
2017 JSTraceType* mTypeMap;
2018 JSTraceType* mPtr;
2019
2020 public:
2021 JS_ALWAYS_INLINE CaptureTypesVisitor(JSContext* cx, JSTraceType* typeMap) :
2022 mCx(cx),
2023 mTypeMap(typeMap),
2024 mPtr(typeMap)
2025 {}
2026
2027 JS_REQUIRES_STACK JS_ALWAYS_INLINE void
2028 visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) {
2029 JSTraceType type = getCoercedType(*vp);
2030 if (type == TT_INT32 &&
2031 oracle.isGlobalSlotUndemotable(mCx, slot))
2032 type = TT_DOUBLE;
2033 JS_ASSERT(type != TT_JSVAL);
2034 debug_only_printf(LC_TMTracer,
2035 "capture type global%d: %d=%c\n",
2036 n, type, typeChar[type]);
2037 *mPtr++ = type;
2038 }
2039
2040 JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
2041 visitStackSlots(jsval *vp, int count, JSStackFrame* fp) {
2042 for (int i = 0; i < count; ++i) {
2043 JSTraceType type = getCoercedType(vp[i]);
2044 if (type == TT_INT32 &&
2045 oracle.isStackSlotUndemotable(mCx, length()))
2046 type = TT_DOUBLE;
2047 JS_ASSERT(type != TT_JSVAL);
2048 debug_only_printf(LC_TMTracer,
2049 "capture type %s%d: %d=%c\n",
2050 stackSlotKind(), i, type, typeChar[type]);
2051 *mPtr++ = type;
2052 }
2053 return true;
2054 }
2055
2056 JS_ALWAYS_INLINE uintptr_t length() {
2057 return mPtr - mTypeMap;
2058 }
2059 };
2060
2061 /*
2062 * Capture the type map for the selected slots of the global object and currently pending
2063 * stack frames.
2064 */
2065 JS_REQUIRES_STACK void
2066 TypeMap::captureTypes(JSContext* cx, JSObject* globalObj, SlotList& slots, unsigned callDepth)
2067 {
2068 setLength(NativeStackSlots(cx, callDepth) + slots.length());
2069 CaptureTypesVisitor visitor(cx, data());
2070 VisitSlots(visitor, cx, globalObj, callDepth, slots);
2071 JS_ASSERT(visitor.length() == length());
2072 }
2073
2074 JS_REQUIRES_STACK void
2075 TypeMap::captureMissingGlobalTypes(JSContext* cx, JSObject* globalObj, SlotList& slots, unsigned stackSlots)
2076 {
2077 unsigned oldSlots = length() - stackSlots;
2078 int diff = slots.length() - oldSlots;
2079 JS_ASSERT(diff >= 0);
2080 setLength(length() + diff);
2081 CaptureTypesVisitor visitor(cx, data() + stackSlots + oldSlots);
2082 VisitGlobalSlots(visitor, cx, globalObj, diff, slots.data() + oldSlots);
2083 }
2084
2085 /* Compare this type map to another one and see whether they match. */
2086 bool
2087 TypeMap::matches(TypeMap& other) const
2088 {
2089 if (length() != other.length())
2090 return false;
2091 return !memcmp(data(), other.data(), length());
2092 }
2093
2094 void
2095 TypeMap::fromRaw(JSTraceType* other, unsigned numSlots)
2096 {
2097 unsigned oldLength = length();
2098 setLength(length() + numSlots);
2099 for (unsigned i = 0; i < numSlots; i++)
2100 get(oldLength + i) = other[i];
2101 }
2102
2103 /*
2104 * Use the provided storage area to create a new type map that contains the
2105 * partial type map with the rest of it filled up from the complete type
2106 * map.
2107 */
2108 static void
2109 MergeTypeMaps(JSTraceType** partial, unsigned* plength, JSTraceType* complete, unsigned clength, JSTraceType* mem)
2110 {
2111 unsigned l = *plength;
2112 JS_ASSERT(l < clength);
2113 memcpy(mem, *partial, l * sizeof(JSTraceType));
2114 memcpy(mem + l, complete + l, (clength - l) * sizeof(JSTraceType));
2115 *partial = mem;
2116 *plength = clength;
2117 }
2118
2119 /*
2120 * Specializes a tree to any specifically missing globals, including any
2121 * dependent trees.
2122 */
2123 static JS_REQUIRES_STACK void
2124 SpecializeTreesToLateGlobals(JSContext* cx, TreeInfo* root, JSTraceType* globalTypeMap,
2125 unsigned numGlobalSlots)
2126 {
2127 TreeInfo* ti = root;
2128
2129 for (unsigned i = ti->nGlobalTypes(); i < numGlobalSlots; i++)
2130 ti->typeMap.add(globalTypeMap[i]);
2131
2132 JS_ASSERT(ti->nGlobalTypes() == numGlobalSlots);
2133
2134 for (unsigned i = 0; i < root->dependentTrees.length(); i++) {
2135 ti = (TreeInfo*)root->dependentTrees[i]->vmprivate;
2136
2137 /* ti can be NULL if we hit the recording tree in emitTreeCall; this is harmless. */
2138 if (ti && ti->nGlobalTypes() < numGlobalSlots)
2139 SpecializeTreesToLateGlobals(cx, ti, globalTypeMap, numGlobalSlots);
2140 }
2141 for (unsigned i = 0; i < root->linkedTrees.length(); i++) {
2142 ti = (TreeInfo*)root->linkedTrees[i]->vmprivate;
2143 if (ti && ti->nGlobalTypes() < numGlobalSlots)
2144 SpecializeTreesToLateGlobals(cx, ti, globalTypeMap, numGlobalSlots);
2145 }
2146 }
2147
2148 /* Specializes a tree to any missing globals, including any dependent trees. */
2149 static JS_REQUIRES_STACK void
2150 SpecializeTreesToMissingGlobals(JSContext* cx, JSObject* globalObj, TreeInfo* root)
2151 {
2152 TreeInfo* ti = root;
2153
2154 ti->typeMap.captureMissingGlobalTypes(cx, globalObj, *ti->globalSlots, ti->nStackTypes);
2155 JS_ASSERT(ti->globalSlots->length() == ti->typeMap.length() - ti->nStackTypes);
2156
2157 SpecializeTreesToLateGlobals(cx, ti, ti->globalTypeMap(), ti->nGlobalTypes());
2158 }
2159
2160 static void
2161 TrashTree(JSContext* cx, Fragment* f);
2162
2163 JS_REQUIRES_STACK
2164 TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* _anchor, Fragment* _fragment,
2165 TreeInfo* ti, unsigned stackSlots, unsigned ngslots, JSTraceType* typeMap,
2166 VMSideExit* innermostNestedGuard, jsbytecode* outer, uint32 outerArgc)
2167 : tempAlloc(*JS_TRACE_MONITOR(cx).tempAlloc),
2168 whichTreesToTrash(&tempAlloc),
2169 cfgMerges(&tempAlloc),
2170 tempTypeMap(cx)
2171 {
2172 JS_ASSERT(!_fragment->vmprivate && ti && cx->fp->regs->pc == (jsbytecode*)_fragment->ip);
2173 /* Reset the fragment state we care about in case we got a recycled fragment.
2174 This includes resetting any profiling data we might have accumulated. */
2175 _fragment->lastIns = NULL;
2176 verbose_only( _fragment->profCount = 0; )
2177 verbose_only( _fragment->nStaticExits = 0; )
2178 verbose_only( _fragment->nCodeBytes = 0; )
2179 verbose_only( _fragment->nExitBytes = 0; )
2180 verbose_only( _fragment->guardNumberer = 1; )
2181 verbose_only( _fragment->guardsForFrag = NULL; )
2182 verbose_only( _fragment->loopLabel = NULL; )
2183 // don't change _fragment->profFragID, though. Once the identity of
2184 // the Fragment is set up (for profiling purposes), we can't change it.
2185 this->cx = cx;
2186 this->traceMonitor = &JS_TRACE_MONITOR(cx);
2187 this->globalObj = JS_GetGlobalForObject(cx, cx->fp->scopeChain);
2188 this->lexicalBlock = cx->fp->blockChain;
2189 this->anchor = _anchor;
2190 this->fragment = _fragment;
2191 this->lirbuf = _fragment->lirbuf;
2192 this->treeInfo = ti;
2193 this->callDepth = _anchor ? _anchor->calldepth : 0;
2194 this->atoms = FrameAtomBase(cx, cx->fp);
2195 this->trashSelf = false;
2196 this->global_dslots = this->globalObj->dslots;
2197 this->loop = true; /* default assumption is we are compiling a loop */
2198 this->outer = outer;
2199 this->outerArgc = outerArgc;
2200 this->pendingSpecializedNative = NULL;
2201 this->newobj_ins = NULL;
2202 this->loopLabel = NULL;
2203
2204 #ifdef JS_JIT_SPEW
2205 debug_only_print0(LC_TMMinimal, "\n");
2206 debug_only_printf(LC_TMMinimal, "Recording starting from %s:%u@%u (FragID=%06u)\n",
2207 ti->treeFileName, ti->treeLineNumber, ti->treePCOffset,
2208 _fragment->profFragID);
2209
2210 debug_only_printf(LC_TMTracer, "globalObj=%p, shape=%d\n",
2211 (void*)this->globalObj, OBJ_SHAPE(this->globalObj));
2212 debug_only_printf(LC_TMTreeVis, "TREEVIS RECORD FRAG=%p ANCHOR=%p\n", (void*)fragment,
2213 (void*)anchor);
2214 #endif
2215
2216 lir = lir_buf_writer = new LirBufWriter(lirbuf);
2217 #ifdef DEBUG
2218 lir = sanity_filter_1 = new SanityFilter(lir);
2219 #endif
2220 debug_only_stmt(
2221 if (js_LogController.lcbits & LC_TMRecorder) {
2222 lir = verbose_filter
2223 = new VerboseWriter (tempAlloc, lir, lirbuf->names,
2224 &js_LogController);
2225 }
2226 )
2227 // CseFilter must be downstream of SoftFloatFilter (see bug 527754 for why).
2228 lir = cse_filter = new CseFilter(lir, tempAlloc);
2229 if (nanojit::AvmCore::config.soft_float)
2230 lir = float_filter = new SoftFloatFilter(lir);
2231 lir = expr_filter = new ExprFilter(lir);
2232 lir = func_filter = new FuncFilter(lir);
2233 #ifdef DEBUG
2234 lir = sanity_filter_2 = new SanityFilter(lir);
2235 #endif
2236 lir->ins0(LIR_start);
2237
2238 for (int i = 0; i < NumSavedRegs; ++i)
2239 lir->insParam(i, 1);
2240 #ifdef DEBUG
2241 for (int i = 0; i < NumSavedRegs; ++i)
2242 addName(lirbuf->savedRegs[i], regNames[Assembler::savedRegs[i]]);
2243 #endif
2244
2245 lirbuf->state = addName(lir->insParam(0, 0), "state");
2246
2247 if (fragment == fragment->root)
2248 loopLabel = lir->ins0(LIR_label);
2249
2250 // if profiling, drop a label, so the assembler knows to put a
2251 // frag-entry-counter increment at this point. If there's a
2252 // loopLabel, use that; else we'll have to make a dummy label
2253 // especially for this purpose.
2254 verbose_only( if (js_LogController.lcbits & LC_FragProfile) {
2255 LIns* entryLabel = NULL;
2256 if (fragment == fragment->root) {
2257 entryLabel = loopLabel;
2258 } else {
2259 entryLabel = lir->ins0(LIR_label);
2260 }
2261 NanoAssert(entryLabel);
2262 NanoAssert(!fragment->loopLabel);
2263 fragment->loopLabel = entryLabel;
2264 })
2265
2266 lirbuf->sp = addName(lir->insLoad(LIR_ldp, lirbuf->state, (int)offsetof(InterpState, sp)), "sp");
2267 lirbuf->rp = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, rp)), "rp");
2268 cx_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, cx)), "cx");
2269 eos_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, eos)), "eos");
2270 eor_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, eor)), "eor");
2271
2272 /* If we came from exit, we might not have enough global types. */
2273 if (ti->globalSlots->length() > ti->nGlobalTypes())
2274 SpecializeTreesToMissingGlobals(cx, globalObj, ti);
2275
2276 /* read into registers all values on the stack and all globals we know so far */
2277 import(treeInfo, lirbuf->sp, stackSlots, ngslots, callDepth, typeMap);
2278
2279 if (fragment == fragment->root) {
2280 /*
2281 * We poll the operation callback request flag. It is updated asynchronously whenever
2282 * the callback is to be invoked.
2283 */
2284 LIns* x = lir->insLoad(LIR_ld, cx_ins, offsetof(JSContext, operationCallbackFlag));
2285 guard(true, lir->ins_eq0(x), snapshot(TIMEOUT_EXIT));
2286 }
2287
2288 /*
2289 * If we are attached to a tree call guard, make sure the guard the inner
2290 * tree exited from is what we expect it to be.
2291 */
2292 if (_anchor && _anchor->exitType == NESTED_EXIT) {
2293 LIns* nested_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state,
2294 offsetof(InterpState, lastTreeExitGuard)),
2295 "lastTreeExitGuard");
2296 guard(true, lir->ins2(LIR_peq, nested_ins, INS_CONSTPTR(innermostNestedGuard)), NESTED_EXIT);
2297 }
2298 }
2299
2300 TraceRecorder::~TraceRecorder()
2301 {
2302 JS_ASSERT(treeInfo && fragment);
2303
2304 if (trashSelf)
2305 TrashTree(cx, fragment->root);
2306
2307 for (unsigned int i = 0; i < whichTreesToTrash.length(); i++)
2308 TrashTree(cx, whichTreesToTrash[i]);
2309
2310 /* Purge the tempAlloc used during recording. */
2311 tempAlloc.reset();
2312 traceMonitor->lirbuf->clear();
2313
2314 #ifdef DEBUG
2315 debug_only_stmt( delete verbose_filter; )
2316 delete sanity_filter_1;
2317 delete sanity_filter_2;
2318 #endif
2319 delete cse_filter;
2320 delete expr_filter;
2321 delete func_filter;
2322 delete float_filter;
2323 delete lir_buf_writer;
2324 }
2325
2326 bool
2327 TraceRecorder::outOfMemory()
2328 {
2329 return traceMonitor->dataAlloc->outOfMemory() || tempAlloc.outOfMemory();
2330 }
2331
2332 /* Add debug information to a LIR instruction as we emit it. */
2333 inline LIns*
2334 TraceRecorder::addName(LIns* ins, const char* name)
2335 {
2336 #ifdef JS_JIT_SPEW
2337 /*
2338 * We'll only ask for verbose Nanojit when .lcbits > 0, so there's no point
2339 * in adding names otherwise.
2340 */
2341 if (js_LogController.lcbits > 0)
2342 lirbuf->names->addName(ins, name);
2343 #endif
2344 return ins;
2345 }
2346
2347 inline LIns*
2348 TraceRecorder::insImmVal(jsval val)
2349 {
2350 if (JSVAL_IS_TRACEABLE(val))
2351 treeInfo->gcthings.addUnique(val);
2352 return lir->insImmWord(val);
2353 }
2354
2355 inline LIns*
2356 TraceRecorder::insImmObj(JSObject* obj)
2357 {
2358 treeInfo->gcthings.addUnique(OBJECT_TO_JSVAL(obj));
2359 return lir->insImmPtr((void*)obj);
2360 }
2361
2362 inline LIns*
2363 TraceRecorder::insImmFun(JSFunction* fun)
2364 {
2365 treeInfo->gcthings.addUnique(OBJECT_TO_JSVAL(FUN_OBJECT(fun)));
2366 return lir->insImmPtr((void*)fun);
2367 }
2368
2369 inline LIns*
2370 TraceRecorder::insImmStr(JSString* str)
2371 {
2372 treeInfo->gcthings.addUnique(STRING_TO_JSVAL(str));
2373 return lir->insImmPtr((void*)str);
2374 }
2375
2376 inline LIns*
2377 TraceRecorder::insImmSprop(JSScopeProperty* sprop)
2378 {
2379 treeInfo->sprops.addUnique(sprop);
2380 return lir->insImmPtr((void*)sprop);
2381 }
2382
2383 inline LIns*
2384 TraceRecorder::p2i(nanojit::LIns* ins)
2385 {
2386 #ifdef NANOJIT_64BIT
2387 return lir->ins1(LIR_qlo, ins);
2388 #else
2389 return ins;
2390 #endif
2391 }
2392
2393 /* Determine the current call depth (starting with the entry frame.) */
2394 unsigned
2395 TraceRecorder::getCallDepth() const
2396 {
2397 return callDepth;
2398 }
2399
2400 /* Determine the offset in the native global frame for a jsval we track. */
2401 ptrdiff_t
2402 TraceRecorder::nativeGlobalOffset(jsval* p) const
2403 {
2404 JS_ASSERT(isGlobal(p));
2405 if (size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS)
2406 return sizeof(InterpState) + size_t(p - globalObj->fslots) * sizeof(double);
2407 return sizeof(InterpState) + ((p - globalObj->dslots) + JS_INITIAL_NSLOTS) * sizeof(double);
2408 }
2409
2410 /* Determine whether a value is a global stack slot. */
2411 bool
2412 TraceRecorder::isGlobal(jsval* p) const
2413 {
2414 return ((size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS) ||
2415 (size_t(p - globalObj->dslots) < (STOBJ_NSLOTS(globalObj) - JS_INITIAL_NSLOTS)));
2416 }
2417
2418 /*
2419 * Return the offset in the native stack for the given jsval. More formally,
2420 * |p| must be the address of a jsval that is represented in the native stack
2421 * area. The return value is the offset, from InterpState::stackBase, in bytes,
2422 * where the native representation of |*p| is stored. To get the offset
2423 * relative to InterpState::sp, subtract TreeInfo::nativeStackBase.
2424 */
2425 JS_REQUIRES_STACK ptrdiff_t
2426 TraceRecorder::nativeStackOffset(jsval* p) const
2427 {
2428 CountSlotsVisitor visitor(p);
2429 VisitStackSlots(visitor, cx, callDepth);
2430 size_t offset = visitor.count() * sizeof(double);
2431
2432 /*
2433 * If it's not in a pending frame, it must be on the stack of the current
2434 * frame above sp but below fp->slots + script->nslots.
2435 */
2436 if (!visitor.stopped()) {
2437 JS_ASSERT(size_t(p - cx->fp->slots) < cx->fp->script->nslots);
2438 offset += size_t(p - cx->fp->regs->sp) * sizeof(double);
2439 }
2440 return offset;
2441 }
2442
2443 /* Track the maximum number of native frame slots we need during execution. */
2444 void
2445 TraceRecorder::trackNativeStackUse(unsigned slots)
2446 {
2447 if (slots > treeInfo->maxNativeStackSlots)
2448 treeInfo->maxNativeStackSlots = slots;
2449 }
2450
2451 /*
2452 * Unbox a jsval into a slot. Slots are wide enough to hold double values
2453 * directly (instead of storing a pointer to them). We assert instead of
2454 * type checking. The caller must ensure the types are compatible.
2455 */
2456 static void
2457 ValueToNative(JSContext* cx, jsval v, JSTraceType type, double* slot)
2458 {
2459 uint8_t tag = JSVAL_TAG(v);
2460 switch (type) {
2461 case TT_OBJECT:
2462 JS_ASSERT(tag == JSVAL_OBJECT);
2463 JS_ASSERT(!JSVAL_IS_NULL(v) && !HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(v)));
2464 *(JSObject**)slot = JSVAL_TO_OBJECT(v);
2465 debug_only_printf(LC_TMTracer,
2466 "object<%p:%s> ", (void*)JSVAL_TO_OBJECT(v),
2467 JSVAL_IS_NULL(v)
2468 ? "null"
2469 : STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v))->name);
2470 return;
2471
2472 case TT_INT32:
2473 jsint i;
2474 if (JSVAL_IS_INT(v))
2475 *(jsint*)slot = JSVAL_TO_INT(v);
2476 else if (tag == JSVAL_DOUBLE && JSDOUBLE_IS_INT(*JSVAL_TO_DOUBLE(v), i))
2477 *(jsint*)slot = i;
2478 else
2479 JS_ASSERT(JSVAL_IS_INT(v));
2480 debug_only_printf(LC_TMTracer, "int<%d> ", *(jsint*)slot);
2481 return;
2482
2483 case TT_DOUBLE:
2484 jsdouble d;
2485 if (JSVAL_IS_INT(v))
2486 d = JSVAL_TO_INT(v);
2487 else
2488 d = *JSVAL_TO_DOUBLE(v);
2489 JS_ASSERT(JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v));
2490 *(jsdouble*)slot = d;
2491 debug_only_printf(LC_TMTracer, "double<%g> ", d);
2492 return;
2493
2494 case TT_JSVAL:
2495 JS_NOT_REACHED("found jsval type in an entry type map");
2496 return;
2497
2498 case TT_STRING:
2499 JS_ASSERT(tag == JSVAL_STRING);
2500 *(JSString**)slot = JSVAL_TO_STRING(v);
2501 debug_only_printf(LC_TMTracer, "string<%p> ", (void*)(*(JSString**)slot));
2502 return;
2503
2504 case TT_NULL:
2505 JS_ASSERT(tag == JSVAL_OBJECT);
2506 *(JSObject**)slot = NULL;
2507 debug_only_print0(LC_TMTracer, "null ");
2508 return;
2509
2510 case TT_PSEUDOBOOLEAN:
2511 /* Watch out for pseudo-booleans. */
2512 JS_ASSERT(tag == JSVAL_SPECIAL);
2513 *(JSBool*)slot = JSVAL_TO_SPECIAL(v);
2514 debug_only_printf(LC_TMTracer, "pseudoboolean<%d> ", *(JSBool*)slot);
2515 return;
2516
2517 case TT_FUNCTION: {
2518 JS_ASSERT(tag == JSVAL_OBJECT);
2519 JSObject* obj = JSVAL_TO_OBJECT(v);
2520 *(JSObject**)slot = obj;
2521 #ifdef DEBUG
2522 JSFunction* fun = GET_FUNCTION_PRIVATE(cx, obj);
2523 debug_only_printf(LC_TMTracer,
2524 "function<%p:%s> ", (void*) obj,
2525 fun->atom
2526 ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom))
2527 : "unnamed");
2528 #endif
2529 return;
2530 }
2531 }
2532
2533 JS_NOT_REACHED("unexpected type");
2534 }
2535
2536 /*
2537 * We maintain an emergency pool of doubles so we can recover safely if a trace
2538 * runs out of memory (doubles or objects).
2539 */
2540 static jsval
2541 AllocateDoubleFromReservedPool(JSContext* cx)
2542 {
2543 JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
2544 JS_ASSERT(tm->reservedDoublePoolPtr > tm->reservedDoublePool);
2545 return *--tm->reservedDoublePoolPtr;
2546 }
2547
2548 static bool
2549 ReplenishReservedPool(JSContext* cx, JSTraceMonitor* tm)
2550 {
2551 /* We should not be called with a full pool. */
2552 JS_ASSERT((size_t) (tm->reservedDoublePoolPtr - tm->reservedDoublePool) <
2553 MAX_NATIVE_STACK_SLOTS);
2554
2555 /*
2556 * When the GC runs in js_NewDoubleInRootedValue, it resets
2557 * tm->reservedDoublePoolPtr back to tm->reservedDoublePool.
2558 */
2559 JSRuntime* rt = cx->runtime;
2560 uintN gcNumber = rt->gcNumber;
2561 uintN lastgcNumber = gcNumber;
2562 jsval* ptr = tm->reservedDoublePoolPtr;
2563 while (ptr < tm->reservedDoublePool + MAX_NATIVE_STACK_SLOTS) {
2564 if (!js_NewDoubleInRootedValue(cx, 0.0, ptr))
2565 goto oom;
2566
2567 /* Check if the last call to js_NewDoubleInRootedValue GC'd. */
2568 if (rt->gcNumber != lastgcNumber) {
2569 lastgcNumber = rt->gcNumber;
2570 ptr = tm->reservedDoublePool;
2571
2572 /*
2573 * Have we GC'd more than once? We're probably running really
2574 * low on memory, bail now.
2575 */
2576 if (uintN(rt->gcNumber - gcNumber) > uintN(1))
2577 goto oom;
2578 continue;
2579 }
2580 ++ptr;
2581 }
2582 tm->reservedDoublePoolPtr = ptr;
2583 return true;
2584
2585 oom:
2586 /*
2587 * Already massive GC pressure, no need to hold doubles back.
2588 * We won't run any native code anyway.
2589 */
2590 tm->reservedDoublePoolPtr = tm->reservedDoublePool;
2591 return false;
2592 }
2593
2594 void
2595 JSTraceMonitor::flush()
2596 {
2597 AUDIT(cacheFlushed);
2598
2599 // recover profiling data from expiring Fragments
2600 verbose_only(
2601 for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) {
2602 for (VMFragment *f = vmfragments[i]; f; f = f->next) {
2603 JS_ASSERT(f->root == f);
2604 for (VMFragment *p = f; p; p = p->peer)
2605 js_FragProfiling_FragFinalizer(p, this);
2606 }
2607 }
2608 )
2609
2610 verbose_only(
2611 for (Seq<Fragment*>* f = branches; f; f = f->tail)
2612 js_FragProfiling_FragFinalizer(f->head, this);
2613 )
2614
2615 dataAlloc->reset();
2616 codeAlloc->reset();
2617 tempAlloc->reset();
2618 reTempAlloc->reset();
2619
2620 Allocator& alloc = *dataAlloc;
2621
2622 for (size_t i = 0; i < MONITOR_N_GLOBAL_STATES; ++i) {
2623 globalStates[i].globalShape = -1;
2624 globalStates[i].globalSlots = new (alloc) SlotList(&alloc);
2625 }
2626
2627 assembler = new (alloc) Assembler(*codeAlloc, alloc, core, &js_LogController);
2628 lirbuf = new (alloc) LirBuffer(*tempAlloc);
2629 reLirBuf = new (alloc) LirBuffer(*reTempAlloc);
2630 verbose_only( branches = NULL; )
2631
2632 #ifdef DEBUG
2633 labels = new (alloc) LabelMap(alloc, &js_LogController);
2634 reLirBuf->names =
2635 lirbuf->names = new (alloc) LirNameMap(alloc, labels);
2636 #endif
2637
2638 memset(&vmfragments[0], 0, FRAGMENT_TABLE_SIZE * sizeof(VMFragment*));
2639 reFragments = new (alloc) REHashMap(alloc);
2640
2641 needFlush = JS_FALSE;
2642 }
2643
2644 static inline void
2645 MarkTreeInfo(JSTracer* trc, TreeInfo *ti)
2646 {
2647 jsval* vp = ti->gcthings.data();
2648 unsigned len = ti->gcthings.length();
2649 while (len--) {
2650 jsval v = *vp++;
2651 JS_SET_TRACING_NAME(trc, "jitgcthing");
2652 JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v));
2653 }
2654 JSScopeProperty** spropp = ti->sprops.data();
2655 len = ti->sprops.length();
2656 while (len--) {
2657 JSScopeProperty* sprop = *spropp++;
2658 sprop->trace(trc);
2659 }
2660 }
2661
2662 void
2663 JSTraceMonitor::mark(JSTracer* trc)
2664 {
2665 if (!trc->context->runtime->gcFlushCodeCaches) {
2666 for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) {
2667 VMFragment* f = vmfragments[i];
2668 while (f) {
2669 if (TreeInfo* ti = (TreeInfo*)f->vmprivate)
2670 MarkTreeInfo(trc, ti);
2671 VMFragment* peer = (VMFragment*)f->peer;
2672 while (peer) {
2673 if (TreeInfo* ti = (TreeInfo*)peer->vmprivate)
2674 MarkTreeInfo(trc, ti);
2675 peer = (VMFragment*)peer->peer;
2676 }
2677 f = f->next;
2678 }
2679 }
2680 if (recorder)
2681 MarkTreeInfo(trc, recorder->getTreeInfo());
2682 }
2683 }
2684
2685 /*
2686 * Box a value from the native stack back into the jsval format. Integers that
2687 * are too large to fit into a jsval are automatically boxed into
2688 * heap-allocated doubles.
2689 */
2690 template <typename E>
2691 static inline bool
2692 NativeToValueBase(JSContext* cx, jsval& v, JSTraceType type, double* slot)
2693 {
2694 jsint i;
2695 jsdouble d;
2696 switch (type) {
2697 case TT_OBJECT:
2698 v = OBJECT_TO_JSVAL(*(JSObject**)slot);
2699 JS_ASSERT(v != JSVAL_ERROR_COOKIE); /* don't leak JSVAL_ERROR_COOKIE */
2700 debug_only_printf(LC_TMTracer,
2701 "object<%p:%s> ", (void*)JSVAL_TO_OBJECT(v),
2702 JSVAL_IS_NULL(v)
2703 ? "null"
2704 : STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v))->name);
2705 break;
2706
2707 case TT_INT32:
2708 i = *(jsint*)slot;
2709 debug_only_printf(LC_TMTracer, "int<%d> ", i);
2710 store_int:
2711 if (INT_FITS_IN_JSVAL(i)) {
2712 v = INT_TO_JSVAL(i);
2713 break;
2714 }
2715 d = (jsdouble)i;
2716 goto store_double;
2717 case TT_DOUBLE:
2718 d = *slot;
2719 debug_only_printf(LC_TMTracer, "double<%g> ", d);
2720 if (JSDOUBLE_IS_INT(d, i))
2721 goto store_int;
2722 store_double: {
2723 /*
2724 * It's not safe to trigger the GC here, so use an emergency heap if we
2725 * are out of double boxes.
2726 */
2727 if (cx->doubleFreeList) {
2728 #ifdef DEBUG
2729 JSBool ok =
2730 #endif
2731 js_NewDoubleInRootedValue(cx, d, &v);
2732 JS_ASSERT(ok);
2733 return true;
2734 }
2735 return E::handleDoubleOOM(cx, d, v);
2736 }
2737
2738 case TT_JSVAL:
2739 v = *(jsval*)slot;
2740 JS_ASSERT(v != JSVAL_ERROR_COOKIE); /* don't leak JSVAL_ERROR_COOKIE */
2741 debug_only_printf(LC_TMTracer, "box<%p> ", (void*)v);
2742 break;
2743
2744 case TT_STRING:
2745 v = STRING_TO_JSVAL(*(JSString**)slot);
2746 debug_only_printf(LC_TMTracer, "string<%p> ", (void*)(*(JSString**)slot));
2747 break;
2748
2749 case TT_NULL:
2750 JS_ASSERT(*(JSObject**)slot == NULL);
2751 v = JSVAL_NULL;
2752 debug_only_printf(LC_TMTracer, "null<%p> ", (void*)(*(JSObject**)slot));
2753 break;
2754
2755 case TT_PSEUDOBOOLEAN:
2756 /* Watch out for pseudo-booleans. */
2757 v = SPECIAL_TO_JSVAL(*(JSBool*)slot);
2758 debug_only_printf(LC_TMTracer, "boolean<%d> ", *(JSBool*)slot);
2759 break;
2760
2761 case TT_FUNCTION: {
2762 JS_ASSERT(HAS_FUNCTION_CLASS(*(JSObject**)slot));
2763 v = OBJECT_TO_JSVAL(*(JSObject**)slot);
2764 #ifdef DEBUG
2765 JSFunction* fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v));
2766 debug_only_printf(LC_TMTracer,
2767 "function<%p:%s> ", (void*)JSVAL_TO_OBJECT(v),
2768 fun->atom
2769 ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom))
2770 : "unnamed");
2771 #endif
2772 break;
2773 }
2774 }
2775 return true;
2776 }
2777
2778 struct ReserveDoubleOOMHandler {
2779 static bool handleDoubleOOM(JSContext *cx, double d, jsval& v) {
2780 v = AllocateDoubleFromReservedPool(cx);
2781 JS_ASSERT(JSVAL_IS_DOUBLE(v) && *JSVAL_TO_DOUBLE(v) == 0.0);
2782 *JSVAL_TO_DOUBLE(v) = d;
2783 return true;
2784 }
2785 };
2786
2787 static void
2788 NativeToValue(JSContext* cx, jsval& v, JSTraceType type, double* slot)
2789 {
2790 #ifdef DEBUG
2791 bool ok =
2792 #endif
2793 NativeToValueBase<ReserveDoubleOOMHandler>(cx, v, type, slot);
2794 JS_ASSERT(ok);
2795 }
2796
2797 struct FailDoubleOOMHandler {
2798 static bool handleDoubleOOM(JSContext *cx, double d, jsval& v) {
2799 return false;
2800 }
2801 };
2802
2803 bool
2804 js_NativeToValue(JSContext* cx, jsval& v, JSTraceType type, double* slot)
2805 {
2806 return NativeToValueBase<FailDoubleOOMHandler>(cx, v, type, slot);
2807 }
2808
2809 class BuildNativeFrameVisitor : public SlotVisitorBase
2810 {
2811 JSContext *mCx;
2812 JSTraceType *mTypeMap;
2813 double *mGlobal;
2814 double *mStack;
2815 public:
2816 BuildNativeFrameVisitor(JSContext *cx,
2817 JSTraceType *typemap,
2818 double *global,
2819 double *stack) :
2820 mCx(cx),
2821 mTypeMap(typemap),
2822 mGlobal(global),
2823 mStack(stack)
2824 {}
2825
2826 JS_REQUIRES_STACK JS_ALWAYS_INLINE void
2827 visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) {
2828 debug_only_printf(LC_TMTracer, "global%d: ", n);
2829 ValueToNative(mCx, *vp, *mTypeMap++, &mGlobal[slot]);
2830 }
2831
2832 JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
2833 visitStackSlots(jsval *vp, int count, JSStackFrame* fp) {
2834 for (int i = 0; i < count; ++i) {
2835 debug_only_printf(LC_TMTracer, "%s%d: ", stackSlotKind(), i);
2836 ValueToNative(mCx, *vp++, *mTypeMap++, mStack++);
2837 }
2838 return true;
2839 }
2840 };
2841
2842 static JS_REQUIRES_STACK void
2843 BuildNativeFrame(JSContext *cx, JSObject *globalObj, unsigned callDepth,
2844 unsigned ngslots, uint16 *gslots,
2845 JSTraceType *typeMap, double *global, double *stack)
2846 {
2847 BuildNativeFrameVisitor visitor(cx, typeMap, global, stack);
2848 VisitSlots(visitor, cx, globalObj, callDepth, ngslots, gslots);
2849 debug_only_print0(LC_TMTracer, "\n");
2850 }
2851
2852 class FlushNativeGlobalFrameVisitor : public SlotVisitorBase
2853 {
2854 JSContext *mCx;
2855 JSTraceType *mTypeMap;
2856 double *mGlobal;
2857 public:
2858 FlushNativeGlobalFrameVisitor(JSContext *cx,
2859 JSTraceType *typeMap,
2860 double *global) :
2861 mCx(cx),
2862 mTypeMap(typeMap),
2863 mGlobal(global)
2864 {}
2865
2866 JS_REQUIRES_STACK JS_ALWAYS_INLINE void
2867 visitGlobalSlot(jsval *vp, unsigned n, unsigned slot) {
2868 debug_only_printf(LC_TMTracer, "global%d=", n);
2869 NativeToValue(mCx, *vp, *mTypeMap++, &mGlobal[slot]);
2870 }
2871 };
2872
2873 class FlushNativeStackFrameVisitor : public SlotVisitorBase
2874 {
2875 JSContext *mCx;
2876 JSTraceType *mTypeMap;
2877 double *mStack;
2878 jsval *mStop;
2879 public:
2880 FlushNativeStackFrameVisitor(JSContext *cx,
2881 JSTraceType *typeMap,
2882 double *stack,
2883 jsval *stop) :
2884 mCx(cx),
2885 mTypeMap(typeMap),
2886 mStack(stack),
2887 mStop(stop)
2888 {}
2889
2890 JSTraceType* getTypeMap()
2891 {
2892 return mTypeMap;
2893 }
2894
2895 JS_REQUIRES_STACK JS_ALWAYS_INLINE bool
2896 visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) {
2897 for (size_t i = 0; i < count; ++i) {
2898 if (vp == mStop)
2899 return false;
2900 debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), unsigned(i));
2901 NativeToValue(mCx, *vp++, *mTypeMap++, mStack++);
2902 }
2903 return true;
2904 }
2905 };
2906
2907 /* Box the given native frame into a JS frame. This is infallible. */
2908 static JS_REQUIRES_STACK void
2909 FlushNativeGlobalFrame(JSContext *cx, double *global, unsigned ngslots,
2910 uint16 *gslots, JSTraceType *typemap)
2911 {
2912 FlushNativeGlobalFrameVisitor visitor(cx, typemap, global);
2913 JSObject *globalObj = JS_GetGlobalForObject(cx, cx->fp->scopeChain);
2914 VisitGlobalSlots(visitor, cx, globalObj, ngslots, gslots);
2915 debug_only_print0(LC_TMTracer, "\n");
2916 }
2917
2918 /*
2919 * Returns the number of values on the native stack, excluding the innermost
2920 * frame. This walks all FrameInfos on the native frame stack and sums the
2921 * slot usage of each frame.
2922 */
2923 static int32
2924 StackDepthFromCallStack(InterpState* state, uint32 callDepth)
2925 {
2926 int32 nativeStackFramePos = 0;
2927
2928 // Duplicate native stack layout computation: see VisitFrameSlots header comment.
2929 for (FrameInfo** fip = state->callstackBase; fip < state->rp + callDepth; fip++)
2930 nativeStackFramePos += (*fip)->callerHeight;
2931 return nativeStackFramePos;
2932 }
2933
2934 /*
2935 * Generic function to read upvars on trace from slots of active frames.
2936 * T Traits type parameter. Must provide static functions:
2937 * interp_get(fp, slot) Read the value out of an interpreter frame.
2938 * native_slot(argc, slot) Return the position of the desired value in the on-trace
2939 * stack frame (with position 0 being callee).
2940 *
2941 * upvarLevel Static level of the function containing the upvar definition
2942 * slot Identifies the value to get. The meaning is defined by the traits type.
2943 * callDepth Call depth of current point relative to trace entry
2944 */
2945 template<typename T>
2946 inline JSTraceType
2947 GetUpvarOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDepth, double* result)
2948 {
2949 InterpState* state = cx->interpState;
2950 FrameInfo** fip = state->rp + callDepth;
2951
2952 /*
2953 * First search the FrameInfo call stack for an entry containing our
2954 * upvar, namely one with level == upvarLevel. The first FrameInfo is a
2955 * transition from the entry frame to some callee. However, it is not
2956 * known (from looking at the FrameInfo) whether the entry frame had a
2957 * callee. Rather than special-case this or insert more logic into the
2958 * loop, instead just stop before that FrameInfo (i.e. |> base| instead of
2959 * |>= base|), and let the code after the loop handle it.
2960 */
2961 int32 stackOffset = StackDepthFromCallStack(state, callDepth);
2962 while (--fip > state->callstackBase) {
2963 FrameInfo* fi = *fip;
2964
2965 /*
2966 * The loop starts aligned to the top of the stack, so move down to the first meaningful
2967 * callee. Then read the callee directly from the frame.
2968 */
2969 stackOffset -= fi->callerHeight;
2970 JSObject* callee = *(JSObject**)(&state->stackBase[stackOffset]);
2971 JSFunction* fun = GET_FUNCTION_PRIVATE(cx, callee);
2972 uintN calleeLevel = fun->u.i.script->staticLevel;
2973 if (calleeLevel == upvarLevel) {
2974 /*
2975 * Now find the upvar's value in the native stack. stackOffset is
2976 * the offset of the start of the activation record corresponding
2977 * to *fip in the native stack.
2978 */
2979 uint32 native_slot = T::native_slot(fi->callerArgc, slot);
2980 *result = state->stackBase[stackOffset + native_slot];
2981 return fi->get_typemap()[native_slot];
2982 }
2983 }
2984
2985 // Next search the trace entry frame, which is not in the FrameInfo stack.
2986 if (state->outermostTree->script->staticLevel == upvarLevel) {
2987 uint32 argc = ((VMFragment*) state->outermostTree->fragment)->argc;
2988 uint32 native_slot = T::native_slot(argc, slot);
2989 *result = state->stackBase[native_slot];
2990 return state->callstackBase[0]->get_typemap()[native_slot];
2991 }
2992
2993 /*
2994 * If we did not find the upvar in the frames for the active traces,
2995 * then we simply get the value from the interpreter state.
2996 */
2997 JS_ASSERT(upvarLevel < JS_DISPLAY_SIZE);
2998 JSStackFrame* fp = cx->display[upvarLevel];
2999 jsval v = T::interp_get(fp, slot);
3000 JSTraceType type = getCoercedType(v);
3001 ValueToNative(cx, v, type, result);
3002 return type;
3003 }
3004
3005 // For this traits type, 'slot' is the argument index, which may be -2 for callee.
3006 struct UpvarArgTraits {
3007 static jsval interp_get(JSStackFrame* fp, int32 slot) {
3008 return fp->argv[slot];
3009 }
3010
3011 static uint32 native_slot(uint32 argc, int32 slot) {
3012 return 2 /*callee,this*/ + slot;
3013 }
3014 };
3015
3016 uint32 JS_FASTCALL
3017 GetUpvarArgOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDepth, double* result)
3018 {
3019 return GetUpvarOnTrace<UpvarArgTraits>(cx, upvarLevel, slot, callDepth, result);
3020 }
3021
3022 // For this traits type, 'slot' is an index into the local slots array.
3023 struct UpvarVarTraits {
3024 static jsval interp_get(JSStackFrame* fp, int32 slot) {
3025 return fp->slots[slot];
3026 }
3027
3028 static uint32 native_slot(uint32 argc, int32 slot) {
3029 return 3 /*callee,this,arguments*/ + argc + slot;
3030 }
3031 };
3032
3033 uint32 JS_FASTCALL
3034 GetUpvarVarOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDepth, double* result)
3035 {
3036 return GetUpvarOnTrace<UpvarVarTraits>(cx, upvarLevel, slot, callDepth, result);
3037 }
3038
3039 /*
3040 * For this traits type, 'slot' is an index into the stack area (within slots,
3041 * after nfixed) of a frame with no function. (On trace, the top-level frame is
3042 * the only one that can have no function.)
3043 */
3044 struct UpvarStackTraits {
3045 static jsval interp_get(JSStackFrame* fp, int32 slot) {
3046 return fp->slots[slot + fp->script->nfixed];
3047 }
3048
3049 static uint32 native_slot(uint32 argc, int32 slot) {
3050 /*
3051 * Locals are not imported by the tracer when the frame has no
3052 * function, so we do not add fp->script->nfixed.
3053 */
3054 JS_ASSERT(argc == 0);
3055 return slot;
3056 }
3057 };
3058
3059 uint32 JS_FASTCALL
3060 GetUpvarStackOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDepth,
3061 double* result)
3062 {
3063 return GetUpvarOnTrace<UpvarStackTraits>(cx, upvarLevel, slot, callDepth, result);
3064 }
3065
3066 // Parameters needed to access a value from a closure on trace.
3067 struct ClosureVarInfo
3068 {
3069 jsid id;
3070 uint32 slot;
3071 uint32 callDepth;
3072 uint32 resolveFlags;
3073 };
3074
3075 /*
3076 * Generic function to read upvars from Call objects of active heavyweight functions.
3077 * call Callee Function object in which the upvar is accessed.
3078 */
3079 template<typename T>
3080 inline uint32
3081 GetFromClosure(JSContext* cx, JSObject* call, const ClosureVarInfo* cv, double* result)
3082 {
3083 JS_ASSERT(OBJ_GET_CLASS(cx, call) == &js_CallClass);
3084
3085 InterpState* state = cx->interpState;
3086
3087 #ifdef DEBUG
3088 int32 stackOffset = StackDepthFromCallStack(state, cv->callDepth);
3089 FrameInfo** fip = state->rp + cv->callDepth;
3090 while (--fip > state->callstackBase) {
3091 FrameInfo* fi = *fip;
3092 JSObject* callee = *(JSObject**)(&state->stackBase[stackOffset]);
3093 if (callee == call) {
3094 // This is not reachable as long as JSOP_LAMBDA is not traced:
3095 // - The upvar is found at this point only if the upvar was defined on a frame that was
3096 // entered on this trace.
3097 // - The upvar definition must be (dynamically, and thus on trace) before the definition
3098 // of the function that uses the upvar.
3099 // - Therefore, if the upvar is found at this point, the function definition JSOP_LAMBDA
3100 // is on the trace.
3101 JS_NOT_REACHED("JSOP_NAME variable found in outer trace");
3102 }
3103 stackOffset -= fi->callerHeight;
3104 }
3105 #endif
3106
3107 /*
3108 * Here we specifically want to check the call object of the trace entry frame.
3109 */
3110 uint32 slot = cv->slot;
3111 VOUCH_DOES_NOT_REQUIRE_STACK();
3112 if (cx->fp->callobj == call) {
3113 slot = T::adj_slot(cx->fp, slot);
3114 *result = state->stackBase[slot];
3115 return state->callstackBase[0]->get_typemap()[slot];
3116 }
3117
3118 JSStackFrame* fp = (JSStackFrame*) call->getPrivate();
3119 jsval v;
3120 if (fp) {
3121 v = T::slots(fp)[slot];
3122 } else {
3123 JS_ASSERT(cv->resolveFlags != JSRESOLVE_INFER);
3124 JSAutoResolveFlags rf(cx, cv->resolveFlags);
3125 #ifdef DEBUG
3126 JSBool rv =
3127 #endif
3128 js_GetPropertyHelper(cx, call, cv->id, JS_FALSE, &v);
3129 JS_ASSERT(rv);
3130 }
3131 JSTraceType type = getCoercedType(v);
3132 ValueToNative(cx, v, type, result);
3133 return type;
3134 }
3135
3136 struct ArgClosureTraits
3137 {
3138 // See also UpvarArgTraits.
3139 static inline uint32 adj_slot(JSStackFrame* fp, uint32 slot) { return 2 + slot; }
3140
3141 // Generate the adj_slot computation in LIR.
3142 static inline LIns* adj_slot_lir(LirWriter* lir, LIns* fp_ins, unsigned slot) {
3143 return lir->insImm(2 + slot);
3144 }
3145
3146 // See also UpvarArgTraits.
3147 static inline jsval* slots(JSStackFrame* fp) { return fp->argv; }
3148 private:
3149 ArgClosureTraits();
3150 };
3151
3152 uint32 JS_FASTCALL
3153 GetClosureArg(JSContext* cx, JSObject* callee, const ClosureVarInfo* cv, double* result)
3154 {
3155 return GetFromClosure<ArgClosureTraits>(cx, callee, cv, result);
3156 }
3157
3158 struct VarClosureTraits
3159 {
3160 // See also UpvarVarTraits.
3161 static inline uint32 adj_slot(JSStackFrame* fp, uint32 slot) { return 3 + fp->argc + slot; }
3162
3163 // See also UpvarVarTraits.
3164 static inline LIns* adj_slot_lir(LirWriter* lir, LIns* fp_ins, unsigned slot) {
3165 LIns *argc_ins = lir->insLoad(LIR_ld, fp_ins, offsetof(JSStackFrame, argc));
3166 return lir->ins2(LIR_add, lir->insImm(3 + slot), argc_ins);
3167 }
3168
3169 // See also UpvarVarTraits.
3170 static inline jsval* slots(JSStackFrame* fp) { return fp->slots; }
3171 private:
3172 VarClosureTraits();
3173 };
3174
3175 uint32 JS_FASTCALL
3176 GetClosureVar(JSContext* cx, JSObject* callee, const ClosureVarInfo* cv, double* result)
3177 {
3178 return GetFromClosure<VarClosureTraits>(cx, callee, cv, result);
3179 }
3180
3181 /**
3182 * Box the given native stack frame into the virtual machine stack. This
3183 * is infallible.
3184 *
3185 * @param callDepth the distance between the entry frame into our trace and
3186 * cx->fp when we make this call. If this is not called as a
3187 * result of a nested exit, callDepth is 0.
3188 * @param mp an array of JSTraceTypes that indicate what the types of the things
3189 * on the stack are.
3190 * @param np pointer to the native stack. We want to copy values from here to
3191 * the JS stack as needed.
3192 * @param stopFrame if non-null, this frame and everything above it should not
3193 * be restored.
3194 * @return the number of things we popped off of np.
3195 */
3196 static JS_REQUIRES_STACK int
3197 FlushNativeStackFrame(JSContext* cx, unsigned callDepth, JSTraceType* mp, double* np,
3198 JSStackFrame* stopFrame)
3199 {
3200 jsval* stopAt = stopFrame ? &stopFrame->argv[-2] : NULL;
3201
3202 /* Root all string and object references first (we don't need to call the GC for this). */
3203 FlushNativeStackFrameVisitor visitor(cx, mp, np, stopAt);
3204 VisitStackSlots(visitor, cx,