PageRenderTime 100ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 1ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jstracer.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 1877 lines | 1476 code | 187 blank | 214 comment | 360 complexity | c3178098a8b605185c2e4b7b07c99e8c MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. /* -*- Mode: C++; tab-width: 8; 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. #include "jsstddef.h" // always first
  42. #include "jsbit.h" // low-level (NSPR-based) headers next
  43. #include "jsprf.h"
  44. #include <math.h> // standard headers next
  45. #ifdef _MSC_VER
  46. #include <malloc.h>
  47. #define alloca _alloca
  48. #endif
  49. #ifdef SOLARIS
  50. #include <alloca.h>
  51. #endif
  52. #include "nanojit/nanojit.h"
  53. #include "jsarray.h" // higher-level library and API headers
  54. #include "jsbool.h"
  55. #include "jscntxt.h"
  56. #include "jsdbgapi.h"
  57. #include "jsemit.h"
  58. #include "jsfun.h"
  59. #include "jsinterp.h"
  60. #include "jsiter.h"
  61. #include "jsobj.h"
  62. #include "jsopcode.h"
  63. #include "jsregexp.h"
  64. #include "jsscope.h"
  65. #include "jsscript.h"
  66. #include "jsdate.h"
  67. #include "jsstaticcheck.h"
  68. #include "jstracer.h"
  69. #include "jsautooplen.h" // generated headers last
  70. /* Never use JSVAL_IS_BOOLEAN because it restricts the value (true, false) and
  71. the type. What you want to use is JSVAL_TAG(x) == JSVAL_BOOLEAN and then
  72. handle the undefined case properly (bug 457363). */
  73. #undef JSVAL_IS_BOOLEAN
  74. #define JSVAL_IS_BOOLEAN(x) JS_STATIC_ASSERT(0)
  75. /* Use a fake tag to represent boxed values, borrowing from the integer tag
  76. range since we only use JSVAL_INT to indicate integers. */
  77. #define JSVAL_BOXED 3
  78. /* Map to translate a type tag into a printable representation. */
  79. static const char typeChar[] = "OIDVS?B?";
  80. /* Number of iterations of a loop where we start tracing. That is, we don't
  81. start tracing until the beginning of the HOTLOOP-th iteration. */
  82. #define HOTLOOP 2
  83. /* Number of times we wait to exit on a side exit before we try to extend the tree. */
  84. #define HOTEXIT 1
  85. /* Max call depths for inlining. */
  86. #define MAX_CALLDEPTH 10
  87. /* Max number of type mismatchs before we trash the tree. */
  88. #define MAX_MISMATCH 20
  89. /* Max blacklist level of inner tree immediate recompiling */
  90. #define MAX_INNER_RECORD_BLACKLIST -16
  91. /* Max native stack size. */
  92. #define MAX_NATIVE_STACK_SLOTS 1024
  93. /* Max call stack size. */
  94. #define MAX_CALL_STACK_ENTRIES 64
  95. /* Max number of branches per tree. */
  96. #define MAX_BRANCHES 16
  97. /* Macros for demote slot lists */
  98. #define ALLOCA_UNDEMOTE_SLOTLIST(num) (unsigned*)alloca(((num) + 1) * sizeof(unsigned))
  99. #define ADD_UNDEMOTE_SLOT(list, slot) list[++list[0]] = slot
  100. #define NUM_UNDEMOTE_SLOTS(list) list[0]
  101. #define CLEAR_UNDEMOTE_SLOTLIST(list) list[0] = 0
  102. #ifdef JS_JIT_SPEW
  103. #define ABORT_TRACE(msg) do { debug_only_v(fprintf(stdout, "abort: %d: %s\n", __LINE__, msg);) return false; } while (0)
  104. #else
  105. #define ABORT_TRACE(msg) return false
  106. #endif
  107. #ifdef JS_JIT_SPEW
  108. struct __jitstats {
  109. #define JITSTAT(x) uint64 x;
  110. #include "jitstats.tbl"
  111. #undef JITSTAT
  112. } jitstats = { 0LL, };
  113. JS_STATIC_ASSERT(sizeof(jitstats) % sizeof(uint64) == 0);
  114. enum jitstat_ids {
  115. #define JITSTAT(x) STAT ## x ## ID,
  116. #include "jitstats.tbl"
  117. #undef JITSTAT
  118. STAT_IDS_TOTAL
  119. };
  120. static JSPropertySpec jitstats_props[] = {
  121. #define JITSTAT(x) { #x, STAT ## x ## ID, JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT },
  122. #include "jitstats.tbl"
  123. #undef JITSTAT
  124. { 0 }
  125. };
  126. static JSBool
  127. jitstats_getProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
  128. {
  129. int index = -1;
  130. if (JSVAL_IS_STRING(id)) {
  131. JSString* str = JSVAL_TO_STRING(id);
  132. if (strcmp(JS_GetStringBytes(str), "HOTLOOP") == 0) {
  133. *vp = INT_TO_JSVAL(HOTLOOP);
  134. return JS_TRUE;
  135. }
  136. }
  137. if (JSVAL_IS_INT(id))
  138. index = JSVAL_TO_INT(id);
  139. uint64 result = 0;
  140. switch (index) {
  141. #define JITSTAT(x) case STAT ## x ## ID: result = jitstats.x; break;
  142. #include "jitstats.tbl"
  143. #undef JITSTAT
  144. default:
  145. *vp = JSVAL_VOID;
  146. return JS_TRUE;
  147. }
  148. if (result < JSVAL_INT_MAX) {
  149. *vp = INT_TO_JSVAL(result);
  150. return JS_TRUE;
  151. }
  152. char retstr[64];
  153. JS_snprintf(retstr, sizeof retstr, "%llu", result);
  154. *vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, retstr));
  155. return JS_TRUE;
  156. }
  157. JSClass jitstats_class = {
  158. "jitstats",
  159. JSCLASS_HAS_PRIVATE,
  160. JS_PropertyStub, JS_PropertyStub,
  161. jitstats_getProperty, JS_PropertyStub,
  162. JS_EnumerateStub, JS_ResolveStub,
  163. JS_ConvertStub, JS_FinalizeStub,
  164. JSCLASS_NO_OPTIONAL_MEMBERS
  165. };
  166. void
  167. js_InitJITStatsClass(JSContext *cx, JSObject *glob)
  168. {
  169. JS_InitClass(cx, glob, NULL, &jitstats_class, NULL, 0, jitstats_props, NULL, NULL, NULL);
  170. }
  171. #define AUDIT(x) (jitstats.x++)
  172. #else
  173. #define AUDIT(x) ((void)0)
  174. #endif /* JS_JIT_SPEW */
  175. #define INS_CONST(c) addName(lir->insImm(c), #c)
  176. #define INS_CONSTPTR(p) addName(lir->insImmPtr((void*) (p)), #p)
  177. using namespace avmplus;
  178. using namespace nanojit;
  179. static GC gc = GC();
  180. static avmplus::AvmCore s_core = avmplus::AvmCore();
  181. static avmplus::AvmCore* core = &s_core;
  182. #ifdef JS_JIT_SPEW
  183. void
  184. js_DumpPeerStability(Fragmento* frago, const void* ip);
  185. #endif
  186. /* We really need a better way to configure the JIT. Shaver, where is my fancy JIT object? */
  187. static bool nesting_enabled = true;
  188. #if defined(NANOJIT_IA32)
  189. static bool did_we_check_sse2 = false;
  190. #endif
  191. #ifdef JS_JIT_SPEW
  192. static bool verbose_debug = getenv("TRACEMONKEY") && strstr(getenv("TRACEMONKEY"), "verbose");
  193. #define debug_only_v(x) if (verbose_debug) { x; }
  194. #else
  195. #define debug_only_v(x)
  196. #endif
  197. /* The entire VM shares one oracle. Collisions and concurrent updates are tolerated and worst
  198. case cause performance regressions. */
  199. static Oracle oracle;
  200. /* Blacklists the root peer fragment at a fragment's PC. This is so blacklisting stays at the
  201. top of the peer list and not scattered around. */
  202. void
  203. js_BlacklistPC(Fragmento* frago, Fragment* frag);
  204. Tracker::Tracker()
  205. {
  206. pagelist = 0;
  207. }
  208. Tracker::~Tracker()
  209. {
  210. clear();
  211. }
  212. jsuword
  213. Tracker::getPageBase(const void* v) const
  214. {
  215. return jsuword(v) & ~jsuword(NJ_PAGE_SIZE-1);
  216. }
  217. struct Tracker::Page*
  218. Tracker::findPage(const void* v) const
  219. {
  220. jsuword base = getPageBase(v);
  221. struct Tracker::Page* p = pagelist;
  222. while (p) {
  223. if (p->base == base) {
  224. return p;
  225. }
  226. p = p->next;
  227. }
  228. return 0;
  229. }
  230. struct Tracker::Page*
  231. Tracker::addPage(const void* v) {
  232. jsuword base = getPageBase(v);
  233. struct Tracker::Page* p = (struct Tracker::Page*)
  234. GC::Alloc(sizeof(*p) - sizeof(p->map) + (NJ_PAGE_SIZE >> 2) * sizeof(LIns*));
  235. p->base = base;
  236. p->next = pagelist;
  237. pagelist = p;
  238. return p;
  239. }
  240. void
  241. Tracker::clear()
  242. {
  243. while (pagelist) {
  244. Page* p = pagelist;
  245. pagelist = pagelist->next;
  246. GC::Free(p);
  247. }
  248. }
  249. bool
  250. Tracker::has(const void *v) const
  251. {
  252. return get(v) != NULL;
  253. }
  254. #if defined NANOJIT_64BIT
  255. #define PAGEMASK 0x7ff
  256. #else
  257. #define PAGEMASK 0xfff
  258. #endif
  259. LIns*
  260. Tracker::get(const void* v) const
  261. {
  262. struct Tracker::Page* p = findPage(v);
  263. if (!p)
  264. return NULL;
  265. return p->map[(jsuword(v) & PAGEMASK) >> 2];
  266. }
  267. void
  268. Tracker::set(const void* v, LIns* i)
  269. {
  270. struct Tracker::Page* p = findPage(v);
  271. if (!p)
  272. p = addPage(v);
  273. p->map[(jsuword(v) & PAGEMASK) >> 2] = i;
  274. }
  275. static inline bool isNumber(jsval v)
  276. {
  277. return JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v);
  278. }
  279. static inline jsdouble asNumber(jsval v)
  280. {
  281. JS_ASSERT(isNumber(v));
  282. if (JSVAL_IS_DOUBLE(v))
  283. return *JSVAL_TO_DOUBLE(v);
  284. return (jsdouble)JSVAL_TO_INT(v);
  285. }
  286. static inline bool isInt32(jsval v)
  287. {
  288. if (!isNumber(v))
  289. return false;
  290. jsdouble d = asNumber(v);
  291. jsint i;
  292. return JSDOUBLE_IS_INT(d, i);
  293. }
  294. /* Return JSVAL_DOUBLE for all numbers (int and double) and the tag otherwise. */
  295. static inline uint8 getPromotedType(jsval v)
  296. {
  297. return JSVAL_IS_INT(v) ? JSVAL_DOUBLE : uint8(JSVAL_TAG(v));
  298. }
  299. /* Return JSVAL_INT for all whole numbers that fit into signed 32-bit and the tag otherwise. */
  300. static inline uint8 getCoercedType(jsval v)
  301. {
  302. return isInt32(v) ? JSVAL_INT : (uint8) JSVAL_TAG(v);
  303. }
  304. /* Tell the oracle that a certain global variable should not be demoted. */
  305. void
  306. Oracle::markGlobalSlotUndemotable(JSScript* script, unsigned slot)
  307. {
  308. _dontDemote.set(&gc, (slot % ORACLE_SIZE));
  309. }
  310. /* Consult with the oracle whether we shouldn't demote a certain global variable. */
  311. bool
  312. Oracle::isGlobalSlotUndemotable(JSScript* script, unsigned slot) const
  313. {
  314. return _dontDemote.get(slot % ORACLE_SIZE);
  315. }
  316. /* Tell the oracle that a certain slot at a certain bytecode location should not be demoted. */
  317. void
  318. Oracle::markStackSlotUndemotable(JSScript* script, jsbytecode* ip, unsigned slot)
  319. {
  320. uint32 hash = uint32(intptr_t(ip)) + (slot << 5);
  321. hash %= ORACLE_SIZE;
  322. _dontDemote.set(&gc, hash);
  323. }
  324. /* Consult with the oracle whether we shouldn't demote a certain slot. */
  325. bool
  326. Oracle::isStackSlotUndemotable(JSScript* script, jsbytecode* ip, unsigned slot) const
  327. {
  328. uint32 hash = uint32(intptr_t(ip)) + (slot << 5);
  329. hash %= ORACLE_SIZE;
  330. return _dontDemote.get(hash);
  331. }
  332. /* Clear the oracle. */
  333. void
  334. Oracle::clear()
  335. {
  336. _dontDemote.reset();
  337. }
  338. #if defined(NJ_SOFTFLOAT)
  339. JS_DEFINE_CALLINFO_1(static, DOUBLE, i2f, INT32, 1, 1)
  340. JS_DEFINE_CALLINFO_1(static, DOUBLE, u2f, UINT32, 1, 1)
  341. #endif
  342. static bool isi2f(LInsp i)
  343. {
  344. if (i->isop(LIR_i2f))
  345. return true;
  346. #if defined(NJ_SOFTFLOAT)
  347. if (i->isop(LIR_qjoin) &&
  348. i->oprnd1()->isop(LIR_call) &&
  349. i->oprnd2()->isop(LIR_callh))
  350. {
  351. if (i->oprnd1()->callInfo() == &i2f_ci)
  352. return true;
  353. }
  354. #endif
  355. return false;
  356. }
  357. static bool isu2f(LInsp i)
  358. {
  359. if (i->isop(LIR_u2f))
  360. return true;
  361. #if defined(NJ_SOFTFLOAT)
  362. if (i->isop(LIR_qjoin) &&
  363. i->oprnd1()->isop(LIR_call) &&
  364. i->oprnd2()->isop(LIR_callh))
  365. {
  366. if (i->oprnd1()->callInfo() == &u2f_ci)
  367. return true;
  368. }
  369. #endif
  370. return false;
  371. }
  372. static LInsp iu2fArg(LInsp i)
  373. {
  374. #if defined(NJ_SOFTFLOAT)
  375. if (i->isop(LIR_qjoin))
  376. return i->oprnd1()->arg(0);
  377. #endif
  378. return i->oprnd1();
  379. }
  380. static LIns* demote(LirWriter *out, LInsp i)
  381. {
  382. if (i->isCall())
  383. return callArgN(i, 0);
  384. if (isi2f(i) || isu2f(i))
  385. return iu2fArg(i);
  386. if (i->isconst())
  387. return i;
  388. AvmAssert(i->isconstq());
  389. double cf = i->constvalf();
  390. int32_t ci = cf > 0x7fffffff ? uint32_t(cf) : int32_t(cf);
  391. return out->insImm(ci);
  392. }
  393. static bool isPromoteInt(LIns* i)
  394. {
  395. jsdouble d;
  396. return isi2f(i) || i->isconst() ||
  397. (i->isconstq() && (d = i->constvalf()) == jsdouble(jsint(d)) && !JSDOUBLE_IS_NEGZERO(d));
  398. }
  399. static bool isPromoteUint(LIns* i)
  400. {
  401. jsdouble d;
  402. return isu2f(i) || i->isconst() ||
  403. (i->isconstq() && (d = i->constvalf()) == (jsdouble)(jsuint)d && !JSDOUBLE_IS_NEGZERO(d));
  404. }
  405. static bool isPromote(LIns* i)
  406. {
  407. return isPromoteInt(i) || isPromoteUint(i);
  408. }
  409. static bool isconst(LIns* i, int32_t c)
  410. {
  411. return i->isconst() && i->constval() == c;
  412. }
  413. static bool overflowSafe(LIns* i)
  414. {
  415. LIns* c;
  416. return (i->isop(LIR_and) && ((c = i->oprnd2())->isconst()) &&
  417. ((c->constval() & 0xc0000000) == 0)) ||
  418. (i->isop(LIR_rsh) && ((c = i->oprnd2())->isconst()) &&
  419. ((c->constval() > 0)));
  420. }
  421. #if defined(NJ_SOFTFLOAT)
  422. /* soft float */
  423. JS_DEFINE_CALLINFO_1(static, DOUBLE, fneg, DOUBLE, 1, 1)
  424. JS_DEFINE_CALLINFO_2(static, INT32, fcmpeq, DOUBLE, DOUBLE, 1, 1)
  425. JS_DEFINE_CALLINFO_2(static, INT32, fcmplt, DOUBLE, DOUBLE, 1, 1)
  426. JS_DEFINE_CALLINFO_2(static, INT32, fcmple, DOUBLE, DOUBLE, 1, 1)
  427. JS_DEFINE_CALLINFO_2(static, INT32, fcmpgt, DOUBLE, DOUBLE, 1, 1)
  428. JS_DEFINE_CALLINFO_2(static, INT32, fcmpge, DOUBLE, DOUBLE, 1, 1)
  429. JS_DEFINE_CALLINFO_2(static, DOUBLE, fmul, DOUBLE, DOUBLE, 1, 1)
  430. JS_DEFINE_CALLINFO_2(static, DOUBLE, fadd, DOUBLE, DOUBLE, 1, 1)
  431. JS_DEFINE_CALLINFO_2(static, DOUBLE, fdiv, DOUBLE, DOUBLE, 1, 1)
  432. JS_DEFINE_CALLINFO_2(static, DOUBLE, fsub, DOUBLE, DOUBLE, 1, 1)
  433. jsdouble FASTCALL
  434. fneg(jsdouble x)
  435. {
  436. return -x;
  437. }
  438. jsdouble FASTCALL
  439. i2f(int32 i)
  440. {
  441. return i;
  442. }
  443. jsdouble FASTCALL
  444. u2f(jsuint u)
  445. {
  446. return u;
  447. }
  448. int32 FASTCALL
  449. fcmpeq(jsdouble x, jsdouble y)
  450. {
  451. return x==y;
  452. }
  453. int32 FASTCALL
  454. fcmplt(jsdouble x, jsdouble y)
  455. {
  456. return x < y;
  457. }
  458. int32 FASTCALL
  459. fcmple(jsdouble x, jsdouble y)
  460. {
  461. return x <= y;
  462. }
  463. int32 FASTCALL
  464. fcmpgt(jsdouble x, jsdouble y)
  465. {
  466. return x > y;
  467. }
  468. int32 FASTCALL
  469. fcmpge(jsdouble x, jsdouble y)
  470. {
  471. return x >= y;
  472. }
  473. jsdouble FASTCALL
  474. fmul(jsdouble x, jsdouble y)
  475. {
  476. return x * y;
  477. }
  478. jsdouble FASTCALL
  479. fadd(jsdouble x, jsdouble y)
  480. {
  481. return x + y;
  482. }
  483. jsdouble FASTCALL
  484. fdiv(jsdouble x, jsdouble y)
  485. {
  486. return x / y;
  487. }
  488. jsdouble FASTCALL
  489. fsub(jsdouble x, jsdouble y)
  490. {
  491. return x - y;
  492. }
  493. class SoftFloatFilter: public LirWriter
  494. {
  495. public:
  496. SoftFloatFilter(LirWriter* out):
  497. LirWriter(out)
  498. {
  499. }
  500. LInsp quadCall(const CallInfo *ci, LInsp args[]) {
  501. LInsp qlo, qhi;
  502. qlo = out->insCall(ci, args);
  503. qhi = out->ins1(LIR_callh, qlo);
  504. return out->qjoin(qlo, qhi);
  505. }
  506. LInsp ins1(LOpcode v, LInsp s0)
  507. {
  508. if (v == LIR_fneg)
  509. return quadCall(&fneg_ci, &s0);
  510. if (v == LIR_i2f)
  511. return quadCall(&i2f_ci, &s0);
  512. if (v == LIR_u2f)
  513. return quadCall(&u2f_ci, &s0);
  514. return out->ins1(v, s0);
  515. }
  516. LInsp ins2(LOpcode v, LInsp s0, LInsp s1)
  517. {
  518. LInsp args[2];
  519. LInsp bv;
  520. // change the numeric value and order of these LIR opcodes and die
  521. if (LIR_fadd <= v && v <= LIR_fdiv) {
  522. static const CallInfo *fmap[] = { &fadd_ci, &fsub_ci, &fmul_ci, &fdiv_ci };
  523. args[0] = s1;
  524. args[1] = s0;
  525. return quadCall(fmap[v - LIR_fadd], args);
  526. }
  527. if (LIR_feq <= v && v <= LIR_fge) {
  528. static const CallInfo *fmap[] = { &fcmpeq_ci, &fcmplt_ci, &fcmpgt_ci, &fcmple_ci, &fcmpge_ci };
  529. args[0] = s1;
  530. args[1] = s0;
  531. bv = out->insCall(fmap[v - LIR_feq], args);
  532. return out->ins2(LIR_eq, bv, out->insImm(1));
  533. }
  534. return out->ins2(v, s0, s1);
  535. }
  536. LInsp insCall(const CallInfo *ci, LInsp args[])
  537. {
  538. // if the return type is ARGSIZE_F, we have
  539. // to do a quadCall ( qjoin(call,callh) )
  540. if ((ci->_argtypes & 3) == ARGSIZE_F)
  541. return quadCall(ci, args);
  542. return out->insCall(ci, args);
  543. }
  544. };
  545. #endif // NJ_SOFTFLOAT
  546. class FuncFilter: public LirWriter
  547. {
  548. public:
  549. FuncFilter(LirWriter* out):
  550. LirWriter(out)
  551. {
  552. }
  553. LInsp ins2(LOpcode v, LInsp s0, LInsp s1)
  554. {
  555. if (s0 == s1 && v == LIR_feq) {
  556. if (isPromote(s0)) {
  557. // double(int) and double(uint) cannot be nan
  558. return insImm(1);
  559. }
  560. if (s0->isop(LIR_fmul) || s0->isop(LIR_fsub) || s0->isop(LIR_fadd)) {
  561. LInsp lhs = s0->oprnd1();
  562. LInsp rhs = s0->oprnd2();
  563. if (isPromote(lhs) && isPromote(rhs)) {
  564. // add/sub/mul promoted ints can't be nan
  565. return insImm(1);
  566. }
  567. }
  568. } else if (LIR_feq <= v && v <= LIR_fge) {
  569. if (isPromoteInt(s0) && isPromoteInt(s1)) {
  570. // demote fcmp to cmp
  571. v = LOpcode(v + (LIR_eq - LIR_feq));
  572. return out->ins2(v, demote(out, s0), demote(out, s1));
  573. } else if (isPromoteUint(s0) && isPromoteUint(s1)) {
  574. // uint compare
  575. v = LOpcode(v + (LIR_eq - LIR_feq));
  576. if (v != LIR_eq)
  577. v = LOpcode(v + (LIR_ult - LIR_lt)); // cmp -> ucmp
  578. return out->ins2(v, demote(out, s0), demote(out, s1));
  579. }
  580. } else if (v == LIR_or &&
  581. s0->isop(LIR_lsh) && isconst(s0->oprnd2(), 16) &&
  582. s1->isop(LIR_and) && isconst(s1->oprnd2(), 0xffff)) {
  583. LIns* msw = s0->oprnd1();
  584. LIns* lsw = s1->oprnd1();
  585. LIns* x;
  586. LIns* y;
  587. if (lsw->isop(LIR_add) &&
  588. lsw->oprnd1()->isop(LIR_and) &&
  589. lsw->oprnd2()->isop(LIR_and) &&
  590. isconst(lsw->oprnd1()->oprnd2(), 0xffff) &&
  591. isconst(lsw->oprnd2()->oprnd2(), 0xffff) &&
  592. msw->isop(LIR_add) &&
  593. msw->oprnd1()->isop(LIR_add) &&
  594. msw->oprnd2()->isop(LIR_rsh) &&
  595. msw->oprnd1()->oprnd1()->isop(LIR_rsh) &&
  596. msw->oprnd1()->oprnd2()->isop(LIR_rsh) &&
  597. isconst(msw->oprnd2()->oprnd2(), 16) &&
  598. isconst(msw->oprnd1()->oprnd1()->oprnd2(), 16) &&
  599. isconst(msw->oprnd1()->oprnd2()->oprnd2(), 16) &&
  600. (x = lsw->oprnd1()->oprnd1()) == msw->oprnd1()->oprnd1()->oprnd1() &&
  601. (y = lsw->oprnd2()->oprnd1()) == msw->oprnd1()->oprnd2()->oprnd1() &&
  602. lsw == msw->oprnd2()->oprnd1()) {
  603. return out->ins2(LIR_add, x, y);
  604. }
  605. }
  606. #ifdef NANOJIT_ARM
  607. else if (v == LIR_lsh ||
  608. v == LIR_rsh ||
  609. v == LIR_ush)
  610. {
  611. // needed on ARM -- arm doesn't mask shifts to 31 like x86 does
  612. if (s1->isconst())
  613. s1->setimm16(s1->constval() & 31);
  614. else
  615. s1 = out->ins2(LIR_and, s1, out->insImm(31));
  616. return out->ins2(v, s0, s1);
  617. }
  618. #endif
  619. return out->ins2(v, s0, s1);
  620. }
  621. LInsp insCall(const CallInfo *ci, LInsp args[])
  622. {
  623. LInsp s0 = args[0];
  624. if (ci == &js_DoubleToUint32_ci) {
  625. if (s0->isconstq())
  626. return out->insImm(js_DoubleToECMAUint32(s0->constvalf()));
  627. if (isi2f(s0) || isu2f(s0))
  628. return iu2fArg(s0);
  629. } else if (ci == &js_DoubleToInt32_ci) {
  630. if (s0->isconstq())
  631. return out->insImm(js_DoubleToECMAInt32(s0->constvalf()));
  632. if (s0->isop(LIR_fadd) || s0->isop(LIR_fsub)) {
  633. LInsp lhs = s0->oprnd1();
  634. LInsp rhs = s0->oprnd2();
  635. if (isPromote(lhs) && isPromote(rhs)) {
  636. LOpcode op = LOpcode(s0->opcode() & ~LIR64);
  637. return out->ins2(op, demote(out, lhs), demote(out, rhs));
  638. }
  639. }
  640. if (isi2f(s0) || isu2f(s0))
  641. return iu2fArg(s0);
  642. // XXX ARM -- check for qjoin(call(UnboxDouble),call(UnboxDouble))
  643. if (s0->isCall() && s0->callInfo() == &js_UnboxDouble_ci) {
  644. LIns* args2[] = { callArgN(s0, 0) };
  645. return out->insCall(&js_UnboxInt32_ci, args2);
  646. }
  647. if (s0->isCall() && s0->callInfo() == &js_StringToNumber_ci) {
  648. // callArgN's ordering is that as seen by the builtin, not as stored in args here.
  649. // True story!
  650. LIns* args2[] = { callArgN(s0, 1), callArgN(s0, 0) };
  651. return out->insCall(&js_StringToInt32_ci, args2);
  652. }
  653. } else if (ci == &js_BoxDouble_ci) {
  654. JS_ASSERT(s0->isQuad());
  655. if (s0->isop(LIR_i2f)) {
  656. LIns* args2[] = { s0->oprnd1(), args[1] };
  657. return out->insCall(&js_BoxInt32_ci, args2);
  658. }
  659. if (s0->isCall() && s0->callInfo() == &js_UnboxDouble_ci)
  660. return callArgN(s0, 0);
  661. }
  662. return out->insCall(ci, args);
  663. }
  664. };
  665. /* In debug mode vpname contains a textual description of the type of the
  666. slot during the forall iteration over al slots. */
  667. #ifdef JS_JIT_SPEW
  668. #define DEF_VPNAME const char* vpname; unsigned vpnum
  669. #define SET_VPNAME(name) do { vpname = name; vpnum = 0; } while(0)
  670. #define INC_VPNUM() do { ++vpnum; } while(0)
  671. #else
  672. #define DEF_VPNAME do {} while (0)
  673. #define vpname ""
  674. #define vpnum 0
  675. #define SET_VPNAME(name) ((void)0)
  676. #define INC_VPNUM() ((void)0)
  677. #endif
  678. /* Iterate over all interned global variables. */
  679. #define FORALL_GLOBAL_SLOTS(cx, ngslots, gslots, code) \
  680. JS_BEGIN_MACRO \
  681. DEF_VPNAME; \
  682. JSObject* globalObj = JS_GetGlobalForObject(cx, cx->fp->scopeChain); \
  683. unsigned n; \
  684. jsval* vp; \
  685. SET_VPNAME("global"); \
  686. for (n = 0; n < ngslots; ++n) { \
  687. vp = &STOBJ_GET_SLOT(globalObj, gslots[n]); \
  688. { code; } \
  689. INC_VPNUM(); \
  690. } \
  691. JS_END_MACRO
  692. /* Iterate over all slots in the frame, consisting of args, vars, and stack
  693. (except for the top-level frame which does not have args or vars. */
  694. #define FORALL_FRAME_SLOTS(fp, depth, code) \
  695. JS_BEGIN_MACRO \
  696. jsval* vp; \
  697. jsval* vpstop; \
  698. if (fp->callee) { \
  699. if (depth == 0) { \
  700. SET_VPNAME("callee"); \
  701. vp = &fp->argv[-2]; \
  702. { code; } \
  703. SET_VPNAME("this"); \
  704. vp = &fp->argv[-1]; \
  705. { code; } \
  706. SET_VPNAME("argv"); \
  707. vp = &fp->argv[0]; vpstop = &fp->argv[fp->fun->nargs]; \
  708. while (vp < vpstop) { code; ++vp; INC_VPNUM(); } \
  709. } \
  710. SET_VPNAME("vars"); \
  711. vp = fp->slots; vpstop = &fp->slots[fp->script->nfixed]; \
  712. while (vp < vpstop) { code; ++vp; INC_VPNUM(); } \
  713. } \
  714. SET_VPNAME("stack"); \
  715. vp = StackBase(fp); vpstop = fp->regs->sp; \
  716. while (vp < vpstop) { code; ++vp; INC_VPNUM(); } \
  717. if (fsp < fspstop - 1) { \
  718. JSStackFrame* fp2 = fsp[1]; \
  719. int missing = fp2->fun->nargs - fp2->argc; \
  720. if (missing > 0) { \
  721. SET_VPNAME("missing"); \
  722. vp = fp->regs->sp; \
  723. vpstop = vp + missing; \
  724. while (vp < vpstop) { code; ++vp; INC_VPNUM(); } \
  725. } \
  726. } \
  727. JS_END_MACRO
  728. /* Iterate over all slots in each pending frame. */
  729. #define FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth, code) \
  730. JS_BEGIN_MACRO \
  731. DEF_VPNAME; \
  732. unsigned n; \
  733. JSStackFrame* currentFrame = cx->fp; \
  734. JSStackFrame* entryFrame; \
  735. JSStackFrame* fp = currentFrame; \
  736. for (n = 0; n < callDepth; ++n) { fp = fp->down; } \
  737. entryFrame = fp; \
  738. unsigned frames = callDepth+1; \
  739. JSStackFrame** fstack = \
  740. (JSStackFrame**) alloca(frames * sizeof (JSStackFrame*)); \
  741. JSStackFrame** fspstop = &fstack[frames]; \
  742. JSStackFrame** fsp = fspstop-1; \
  743. fp = currentFrame; \
  744. for (;; fp = fp->down) { *fsp-- = fp; if (fp == entryFrame) break; } \
  745. unsigned depth; \
  746. for (depth = 0, fsp = fstack; fsp < fspstop; ++fsp, ++depth) { \
  747. fp = *fsp; \
  748. FORALL_FRAME_SLOTS(fp, depth, code); \
  749. } \
  750. JS_END_MACRO
  751. #define FORALL_SLOTS(cx, ngslots, gslots, callDepth, code) \
  752. JS_BEGIN_MACRO \
  753. FORALL_GLOBAL_SLOTS(cx, ngslots, gslots, code); \
  754. FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth, code); \
  755. JS_END_MACRO
  756. /* Calculate the total number of native frame slots we need from this frame
  757. all the way back to the entry frame, including the current stack usage. */
  758. unsigned
  759. js_NativeStackSlots(JSContext *cx, unsigned callDepth)
  760. {
  761. JSStackFrame* fp = cx->fp;
  762. unsigned slots = 0;
  763. #if defined _DEBUG
  764. unsigned int origCallDepth = callDepth;
  765. #endif
  766. for (;;) {
  767. unsigned operands = fp->regs->sp - StackBase(fp);
  768. slots += operands;
  769. if (fp->callee)
  770. slots += fp->script->nfixed;
  771. if (callDepth-- == 0) {
  772. if (fp->callee)
  773. slots += 2/*callee,this*/ + fp->fun->nargs;
  774. #if defined _DEBUG
  775. unsigned int m = 0;
  776. FORALL_SLOTS_IN_PENDING_FRAMES(cx, origCallDepth, m++);
  777. JS_ASSERT(m == slots);
  778. #endif
  779. return slots;
  780. }
  781. JSStackFrame* fp2 = fp;
  782. fp = fp->down;
  783. int missing = fp2->fun->nargs - fp2->argc;
  784. if (missing > 0)
  785. slots += missing;
  786. }
  787. JS_NOT_REACHED("js_NativeStackSlots");
  788. }
  789. /* Capture the type map for the selected slots of the global object. */
  790. void
  791. TypeMap::captureGlobalTypes(JSContext* cx, SlotList& slots)
  792. {
  793. unsigned ngslots = slots.length();
  794. uint16* gslots = slots.data();
  795. setLength(ngslots);
  796. uint8* map = data();
  797. uint8* m = map;
  798. FORALL_GLOBAL_SLOTS(cx, ngslots, gslots,
  799. uint8 type = getCoercedType(*vp);
  800. if ((type == JSVAL_INT) && oracle.isGlobalSlotUndemotable(cx->fp->script, gslots[n]))
  801. type = JSVAL_DOUBLE;
  802. JS_ASSERT(type != JSVAL_BOXED);
  803. *m++ = type;
  804. );
  805. }
  806. /* Capture the type map for the currently pending stack frames. */
  807. void
  808. TypeMap::captureStackTypes(JSContext* cx, unsigned callDepth)
  809. {
  810. setLength(js_NativeStackSlots(cx, callDepth));
  811. uint8* map = data();
  812. uint8* m = map;
  813. FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth,
  814. uint8 type = getCoercedType(*vp);
  815. if ((type == JSVAL_INT) &&
  816. oracle.isStackSlotUndemotable(cx->fp->script, cx->fp->regs->pc, unsigned(m - map))) {
  817. type = JSVAL_DOUBLE;
  818. }
  819. debug_only_v(printf("capture %s%d: %d\n", vpname, vpnum, type);)
  820. *m++ = type;
  821. );
  822. }
  823. /* Compare this type map to another one and see whether they match. */
  824. bool
  825. TypeMap::matches(TypeMap& other) const
  826. {
  827. if (length() != other.length())
  828. return false;
  829. return !memcmp(data(), other.data(), length());
  830. }
  831. /* Use the provided storage area to create a new type map that contains the partial type map
  832. with the rest of it filled up from the complete type map. */
  833. static void
  834. mergeTypeMaps(uint8** partial, unsigned* plength, uint8* complete, unsigned clength, uint8* mem)
  835. {
  836. unsigned l = *plength;
  837. JS_ASSERT(l < clength);
  838. memcpy(mem, *partial, l * sizeof(uint8));
  839. memcpy(mem + l, complete + l, (clength - l) * sizeof(uint8));
  840. *partial = mem;
  841. *plength = clength;
  842. }
  843. static void
  844. js_TrashTree(JSContext* cx, Fragment* f);
  845. TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* _anchor, Fragment* _fragment,
  846. TreeInfo* ti, unsigned ngslots, uint8* globalTypeMap, uint8* stackTypeMap,
  847. VMSideExit* innermostNestedGuard, Fragment* outerToBlacklist)
  848. {
  849. JS_ASSERT(!_fragment->vmprivate && ti);
  850. this->cx = cx;
  851. this->traceMonitor = &JS_TRACE_MONITOR(cx);
  852. this->globalObj = JS_GetGlobalForObject(cx, cx->fp->scopeChain);
  853. this->anchor = _anchor;
  854. this->fragment = _fragment;
  855. this->lirbuf = _fragment->lirbuf;
  856. this->treeInfo = ti;
  857. this->callDepth = _anchor ? _anchor->calldepth : 0;
  858. this->atoms = cx->fp->script->atomMap.vector;
  859. this->deepAborted = false;
  860. this->applyingArguments = false;
  861. this->trashTree = false;
  862. this->whichTreeToTrash = _fragment->root;
  863. this->global_dslots = this->globalObj->dslots;
  864. this->terminate = false;
  865. this->outerToBlacklist = outerToBlacklist;
  866. this->wasRootFragment = _fragment == _fragment->root;
  867. debug_only_v(printf("recording starting from %s:%u@%u\n",
  868. cx->fp->script->filename,
  869. js_FramePCToLineNumber(cx, cx->fp),
  870. FramePCOffset(cx->fp));)
  871. debug_only_v(printf("globalObj=%p, shape=%d\n", this->globalObj, OBJ_SHAPE(this->globalObj));)
  872. lir = lir_buf_writer = new (&gc) LirBufWriter(lirbuf);
  873. #ifdef DEBUG
  874. if (verbose_debug)
  875. lir = verbose_filter = new (&gc) VerboseWriter(&gc, lir, lirbuf->names);
  876. #endif
  877. #ifdef NJ_SOFTFLOAT
  878. lir = float_filter = new (&gc) SoftFloatFilter(lir);
  879. #endif
  880. lir = cse_filter = new (&gc) CseFilter(lir, &gc);
  881. lir = expr_filter = new (&gc) ExprFilter(lir);
  882. lir = func_filter = new (&gc) FuncFilter(lir);
  883. lir->ins0(LIR_start);
  884. if (!nanojit::AvmCore::config.tree_opt || fragment->root == fragment)
  885. lirbuf->state = addName(lir->insParam(0, 0), "state");
  886. lirbuf->sp = addName(lir->insLoad(LIR_ldp, lirbuf->state, (int)offsetof(InterpState, sp)), "sp");
  887. lirbuf->rp = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, rp)), "rp");
  888. cx_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, cx)), "cx");
  889. gp_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, gp)), "gp");
  890. eos_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, eos)), "eos");
  891. eor_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, eor)), "eor");
  892. /* read into registers all values on the stack and all globals we know so far */
  893. import(treeInfo, lirbuf->sp, ngslots, callDepth, globalTypeMap, stackTypeMap);
  894. /* If we are attached to a tree call guard, make sure the guard the inner tree exited from
  895. is what we expect it to be. */
  896. if (_anchor && _anchor->exitType == NESTED_EXIT) {
  897. LIns* nested_ins = addName(lir->insLoad(LIR_ldp, lirbuf->state,
  898. offsetof(InterpState, lastTreeExitGuard)),
  899. "lastTreeExitGuard");
  900. guard(true, lir->ins2(LIR_eq, nested_ins, INS_CONSTPTR(innermostNestedGuard)), NESTED_EXIT);
  901. }
  902. }
  903. TreeInfo::~TreeInfo()
  904. {
  905. UnstableExit* temp;
  906. while (unstableExits) {
  907. temp = unstableExits->next;
  908. delete unstableExits;
  909. unstableExits = temp;
  910. }
  911. }
  912. TraceRecorder::~TraceRecorder()
  913. {
  914. JS_ASSERT(nextRecorderToAbort == NULL);
  915. JS_ASSERT(treeInfo && (fragment || wasDeepAborted()));
  916. #ifdef DEBUG
  917. TraceRecorder* tr = JS_TRACE_MONITOR(cx).abortStack;
  918. while (tr != NULL)
  919. {
  920. JS_ASSERT(this != tr);
  921. tr = tr->nextRecorderToAbort;
  922. }
  923. #endif
  924. if (fragment) {
  925. if (wasRootFragment && !fragment->root->code()) {
  926. JS_ASSERT(!fragment->root->vmprivate);
  927. delete treeInfo;
  928. }
  929. if (trashTree)
  930. js_TrashTree(cx, whichTreeToTrash);
  931. } else if (wasRootFragment) {
  932. delete treeInfo;
  933. }
  934. #ifdef DEBUG
  935. delete verbose_filter;
  936. #endif
  937. delete cse_filter;
  938. delete expr_filter;
  939. delete func_filter;
  940. #ifdef NJ_SOFTFLOAT
  941. delete float_filter;
  942. #endif
  943. delete lir_buf_writer;
  944. }
  945. void TraceRecorder::removeFragmentoReferences()
  946. {
  947. fragment = NULL;
  948. }
  949. /* Add debug information to a LIR instruction as we emit it. */
  950. inline LIns*
  951. TraceRecorder::addName(LIns* ins, const char* name)
  952. {
  953. #ifdef DEBUG
  954. lirbuf->names->addName(ins, name);
  955. #endif
  956. return ins;
  957. }
  958. /* Determine the current call depth (starting with the entry frame.) */
  959. unsigned
  960. TraceRecorder::getCallDepth() const
  961. {
  962. return callDepth;
  963. }
  964. /* Determine the offset in the native global frame for a jsval we track */
  965. ptrdiff_t
  966. TraceRecorder::nativeGlobalOffset(jsval* p) const
  967. {
  968. JS_ASSERT(isGlobal(p));
  969. if (size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS)
  970. return size_t(p - globalObj->fslots) * sizeof(double);
  971. return ((p - globalObj->dslots) + JS_INITIAL_NSLOTS) * sizeof(double);
  972. }
  973. /* Determine whether a value is a global stack slot */
  974. bool
  975. TraceRecorder::isGlobal(jsval* p) const
  976. {
  977. return ((size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS) ||
  978. (size_t(p - globalObj->dslots) < (STOBJ_NSLOTS(globalObj) - JS_INITIAL_NSLOTS)));
  979. }
  980. /* Determine the offset in the native stack for a jsval we track */
  981. ptrdiff_t
  982. TraceRecorder::nativeStackOffset(jsval* p) const
  983. {
  984. #ifdef DEBUG
  985. size_t slow_offset = 0;
  986. FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth,
  987. if (vp == p) goto done;
  988. slow_offset += sizeof(double)
  989. );
  990. /*
  991. * If it's not in a pending frame, it must be on the stack of the current frame above
  992. * sp but below fp->slots + script->nslots.
  993. */
  994. JS_ASSERT(size_t(p - cx->fp->slots) < cx->fp->script->nslots);
  995. slow_offset += size_t(p - cx->fp->regs->sp) * sizeof(double);
  996. done:
  997. #define RETURN(offset) { JS_ASSERT((offset) == slow_offset); return offset; }
  998. #else
  999. #define RETURN(offset) { return offset; }
  1000. #endif
  1001. size_t offset = 0;
  1002. JSStackFrame* currentFrame = cx->fp;
  1003. JSStackFrame* entryFrame;
  1004. JSStackFrame* fp = currentFrame;
  1005. for (unsigned n = 0; n < callDepth; ++n) { fp = fp->down; }
  1006. entryFrame = fp;
  1007. unsigned frames = callDepth+1;
  1008. JSStackFrame** fstack = (JSStackFrame **)alloca(frames * sizeof (JSStackFrame *));
  1009. JSStackFrame** fspstop = &fstack[frames];
  1010. JSStackFrame** fsp = fspstop-1;
  1011. fp = currentFrame;
  1012. for (;; fp = fp->down) { *fsp-- = fp; if (fp == entryFrame) break; }
  1013. for (fsp = fstack; fsp < fspstop; ++fsp) {
  1014. fp = *fsp;
  1015. if (fp->callee) {
  1016. if (fsp == fstack) {
  1017. if (size_t(p - &fp->argv[-2]) < size_t(2/*callee,this*/ + fp->fun->nargs))
  1018. RETURN(offset + size_t(p - &fp->argv[-2]) * sizeof(double));
  1019. offset += (2/*callee,this*/ + fp->fun->nargs) * sizeof(double);
  1020. }
  1021. if (size_t(p - &fp->slots[0]) < fp->script->nfixed)
  1022. RETURN(offset + size_t(p - &fp->slots[0]) * sizeof(double));
  1023. offset += fp->script->nfixed * sizeof(double);
  1024. }
  1025. jsval* spbase = StackBase(fp);
  1026. if (size_t(p - spbase) < size_t(fp->regs->sp - spbase))
  1027. RETURN(offset + size_t(p - spbase) * sizeof(double));
  1028. offset += size_t(fp->regs->sp - spbase) * sizeof(double);
  1029. if (fsp < fspstop - 1) {
  1030. JSStackFrame* fp2 = fsp[1];
  1031. int missing = fp2->fun->nargs - fp2->argc;
  1032. if (missing > 0) {
  1033. if (size_t(p - fp->regs->sp) < size_t(missing))
  1034. RETURN(offset + size_t(p - fp->regs->sp) * sizeof(double));
  1035. offset += size_t(missing) * sizeof(double);
  1036. }
  1037. }
  1038. }
  1039. /*
  1040. * If it's not in a pending frame, it must be on the stack of the current frame above
  1041. * sp but below fp->slots + script->nslots.
  1042. */
  1043. JS_ASSERT(size_t(p - currentFrame->slots) < currentFrame->script->nslots);
  1044. offset += size_t(p - currentFrame->regs->sp) * sizeof(double);
  1045. RETURN(offset);
  1046. #undef RETURN
  1047. }
  1048. /* Track the maximum number of native frame slots we need during
  1049. execution. */
  1050. void
  1051. TraceRecorder::trackNativeStackUse(unsigned slots)
  1052. {
  1053. if (slots > treeInfo->maxNativeStackSlots)
  1054. treeInfo->maxNativeStackSlots = slots;
  1055. }
  1056. /* Unbox a jsval into a slot. Slots are wide enough to hold double values directly (instead of
  1057. storing a pointer to them). We now assert instead of type checking, the caller must ensure the
  1058. types are compatible. */
  1059. static void
  1060. ValueToNative(JSContext* cx, jsval v, uint8 type, double* slot)
  1061. {
  1062. unsigned tag = JSVAL_TAG(v);
  1063. switch (type) {
  1064. case JSVAL_INT:
  1065. jsint i;
  1066. if (JSVAL_IS_INT(v))
  1067. *(jsint*)slot = JSVAL_TO_INT(v);
  1068. else if ((tag == JSVAL_DOUBLE) && JSDOUBLE_IS_INT(*JSVAL_TO_DOUBLE(v), i))
  1069. *(jsint*)slot = i;
  1070. else
  1071. JS_ASSERT(JSVAL_IS_INT(v));
  1072. debug_only_v(printf("int<%d> ", *(jsint*)slot);)
  1073. return;
  1074. case JSVAL_DOUBLE:
  1075. jsdouble d;
  1076. if (JSVAL_IS_INT(v))
  1077. d = JSVAL_TO_INT(v);
  1078. else
  1079. d = *JSVAL_TO_DOUBLE(v);
  1080. JS_ASSERT(JSVAL_IS_INT(v) || JSVAL_IS_DOUBLE(v));
  1081. *(jsdouble*)slot = d;
  1082. debug_only_v(printf("double<%g> ", d);)
  1083. return;
  1084. case JSVAL_BOOLEAN:
  1085. JS_ASSERT(tag == JSVAL_BOOLEAN);
  1086. *(JSBool*)slot = JSVAL_TO_BOOLEAN(v);
  1087. debug_only_v(printf("boolean<%d> ", *(JSBool*)slot);)
  1088. return;
  1089. case JSVAL_STRING:
  1090. JS_ASSERT(tag == JSVAL_STRING);
  1091. *(JSString**)slot = JSVAL_TO_STRING(v);
  1092. debug_only_v(printf("string<%p> ", *(JSString**)slot);)
  1093. return;
  1094. default:
  1095. /* Note: we should never see JSVAL_BOXED in an entry type map. */
  1096. JS_ASSERT(type == JSVAL_OBJECT);
  1097. JS_ASSERT(tag == JSVAL_OBJECT);
  1098. *(JSObject**)slot = JSVAL_TO_OBJECT(v);
  1099. debug_only_v(printf("object<%p:%s> ", JSVAL_TO_OBJECT(v),
  1100. JSVAL_IS_NULL(v)
  1101. ? "null"
  1102. : STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v))->name);)
  1103. return;
  1104. }
  1105. }
  1106. /* We maintain an emergency recovery pool of doubles so we can recover safely if a trace runs
  1107. out of memory (doubles or objects). */
  1108. static jsval
  1109. AllocateDoubleFromRecoveryPool(JSContext* cx)
  1110. {
  1111. JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx);
  1112. JS_ASSERT(tm->recoveryDoublePoolPtr > tm->recoveryDoublePool);
  1113. return *--tm->recoveryDoublePoolPtr;
  1114. }
  1115. static bool
  1116. js_ReplenishRecoveryPool(JSContext* cx, JSTraceMonitor* tm)
  1117. {
  1118. /* We should not be called with a full pool. */
  1119. JS_ASSERT((size_t) (tm->recoveryDoublePoolPtr - tm->recoveryDoublePool) <
  1120. MAX_NATIVE_STACK_SLOTS);
  1121. /*
  1122. * When the GC runs in js_NewDoubleInRootedValue, it resets
  1123. * tm->recoveryDoublePoolPtr back to tm->recoveryDoublePool.
  1124. */
  1125. JSRuntime* rt = cx->runtime;
  1126. uintN gcNumber = rt->gcNumber;
  1127. jsval* ptr = tm->recoveryDoublePoolPtr;
  1128. while (ptr < tm->recoveryDoublePool + MAX_NATIVE_STACK_SLOTS) {
  1129. if (!js_NewDoubleInRootedValue(cx, 0.0, ptr))
  1130. goto oom;
  1131. if (rt->gcNumber != gcNumber) {
  1132. JS_ASSERT(tm->recoveryDoublePoolPtr == tm->recoveryDoublePool);
  1133. ptr = tm->recoveryDoublePool;
  1134. if (uintN(rt->gcNumber - gcNumber) > uintN(1))
  1135. goto oom;
  1136. continue;
  1137. }
  1138. ++ptr;
  1139. }
  1140. tm->recoveryDoublePoolPtr = ptr;
  1141. return true;
  1142. oom:
  1143. /*
  1144. * Already massive GC pressure, no need to hold doubles back.
  1145. * We won't run any native code anyway.
  1146. */
  1147. tm->recoveryDoublePoolPtr = tm->recoveryDoublePool;
  1148. return false;
  1149. }
  1150. /* Box a value from the native stack back into the jsval format. Integers
  1151. that are too large to fit into a jsval are automatically boxed into
  1152. heap-allocated doubles. */
  1153. static bool
  1154. NativeToValue(JSContext* cx, jsval& v, uint8 type, double* slot)
  1155. {
  1156. jsint i;
  1157. jsdouble d;
  1158. switch (type) {
  1159. case JSVAL_BOOLEAN:
  1160. v = BOOLEAN_TO_JSVAL(*(JSBool*)slot);
  1161. debug_only_v(printf("boolean<%d> ", *(JSBool*)slot);)
  1162. break;
  1163. case JSVAL_INT:
  1164. i = *(jsint*)slot;
  1165. debug_only_v(printf("int<%d> ", i);)
  1166. store_int:
  1167. if (INT_FITS_IN_JSVAL(i)) {
  1168. v = INT_TO_JSVAL(i);
  1169. break;
  1170. }
  1171. d = (jsdouble)i;
  1172. goto store_double;
  1173. case JSVAL_DOUBLE:
  1174. d = *slot;
  1175. debug_only_v(printf("double<%g> ", d);)
  1176. if (JSDOUBLE_IS_INT(d, i))
  1177. goto store_int;
  1178. store_double: {
  1179. /* Its not safe to trigger the GC here, so use an emergency heap if we are out of
  1180. double boxes. */
  1181. if (cx->doubleFreeList) {
  1182. #ifdef DEBUG
  1183. bool ok =
  1184. #endif
  1185. js_NewDoubleInRootedValue(cx, d, &v);
  1186. JS_ASSERT(ok);
  1187. return true;
  1188. }
  1189. v = AllocateDoubleFromRecoveryPool(cx);
  1190. JS_ASSERT(JSVAL_IS_DOUBLE(v) && *JSVAL_TO_DOUBLE(v) == 0.0);
  1191. *JSVAL_TO_DOUBLE(v) = d;
  1192. return true;
  1193. }
  1194. case JSVAL_STRING:
  1195. v = STRING_TO_JSVAL(*(JSString**)slot);
  1196. JS_ASSERT(JSVAL_TAG(v) == JSVAL_STRING); /* if this fails the pointer was not aligned */
  1197. debug_only_v(printf("string<%p> ", *(JSString**)slot);)
  1198. break;
  1199. case JSVAL_BOXED:
  1200. v = *(jsval*)slot;
  1201. debug_only_v(printf("box<%lx> ", v));
  1202. break;
  1203. default:
  1204. JS_ASSERT(type == JSVAL_OBJECT);
  1205. v = OBJECT_TO_JSVAL(*(JSObject**)slot);
  1206. JS_ASSERT(JSVAL_TAG(v) == JSVAL_OBJECT); /* if this fails the pointer was not aligned */
  1207. debug_only_v(printf("object<%p:%s> ", JSVAL_TO_OBJECT(v),
  1208. JSVAL_IS_NULL(v)
  1209. ? "null"
  1210. : STOBJ_GET_CLASS(JSVAL_TO_OBJECT(v))->name);)
  1211. break;
  1212. }
  1213. return true;
  1214. }
  1215. /* Attempt to unbox the given list of interned globals onto the native global frame. */
  1216. static void
  1217. BuildNativeGlobalFrame(JSContext* cx, unsigned ngslots, uint16* gslots, uint8* mp, double* np)
  1218. {
  1219. debug_only_v(printf("global: ");)
  1220. FORALL_GLOBAL_SLOTS(cx, ngslots, gslots,
  1221. ValueToNative(cx, *vp, *mp, np + gslots[n]);
  1222. ++mp;
  1223. );
  1224. debug_only_v(printf("\n");)
  1225. }
  1226. /* Attempt to unbox the given JS frame onto a native frame. */
  1227. static void
  1228. BuildNativeStackFrame(JSContext* cx, unsigned callDepth, uint8* mp, double* np)
  1229. {
  1230. debug_only_v(printf("stack: ");)
  1231. FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth,
  1232. debug_only_v(printf("%s%u=", vpname, vpnum);)
  1233. ValueToNative(cx, *vp, *mp, np);
  1234. ++mp; ++np;
  1235. );
  1236. debug_only_v(printf("\n");)
  1237. }
  1238. /* Box the given native frame into a JS frame. This only fails due to a hard error
  1239. (out of memory for example). */
  1240. static int
  1241. FlushNativeGlobalFrame(JSContext* cx, unsigned ngslots, uint16* gslots, uint8* mp, double* np)
  1242. {
  1243. uint8* mp_base = mp;
  1244. FORALL_GLOBAL_SLOTS(cx, ngslots, gslots,
  1245. if (!NativeToValue(cx, *vp, *mp, np + gslots[n]))
  1246. return -1;
  1247. ++mp;
  1248. );
  1249. debug_only_v(printf("\n");)
  1250. return mp - mp_base;
  1251. }
  1252. /**
  1253. * Box the given native stack frame into the virtual machine stack. This fails
  1254. * only due to a hard error (out of memory for example).
  1255. *
  1256. * @param callDepth the distance between the entry frame into our trace and
  1257. * cx->fp when we make this call. If this is not called as a
  1258. * result of a nested exit, callDepth is 0.
  1259. * @param mp pointer to an array of type tags (JSVAL_INT, etc.) that indicate
  1260. * what the types of the things on the stack are.
  1261. * @param np pointer to the native stack. We want to copy values from here to
  1262. * the JS stack as needed.
  1263. * @param stopFrame if non-null, this frame and everything above it should not
  1264. * be restored.
  1265. * @return the number of things we popped off of np.
  1266. */
  1267. static int
  1268. FlushNativeStackFrame(JSContext* cx, unsigned callDepth, uint8* mp, double* np,
  1269. JSStackFrame* stopFrame)
  1270. {
  1271. jsval* stopAt = stopFrame ? &stopFrame->argv[-2] : NULL;
  1272. uint8* mp_base = mp;
  1273. /* Root all string and object references first (we don't need to call the GC for this). */
  1274. FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth,
  1275. if (vp == stopAt) goto skip;
  1276. debug_only_v(printf("%s%u=", vpname, vpnum);)
  1277. if (!NativeToValue(cx, *vp, *mp, np))
  1278. return -1;
  1279. ++mp; ++np
  1280. );
  1281. skip:
  1282. // Restore thisp from the now-restored argv[-1] in each pending frame.
  1283. // Keep in mind that we didn't restore frames at stopFrame and above!
  1284. // Scope to keep |fp| from leaking into the macros we're using.
  1285. {
  1286. unsigned n = callDepth+1; // +1 to make sure we restore the entry frame
  1287. JSStackFrame* fp = cx->fp;
  1288. if (stopFrame) {
  1289. for (; fp != stopFrame; fp = fp->down) {
  1290. JS_ASSERT(n != 0);
  1291. --n;
  1292. }
  1293. // Skip over stopFrame itself.
  1294. JS_ASSERT(n != 0);
  1295. --n;
  1296. fp = fp->down;
  1297. }
  1298. for (; n != 0; fp = fp->down) {
  1299. --n;
  1300. if (fp->callee) { // might not have it if the entry frame is global
  1301. JS_ASSERT(JSVAL_IS_OBJECT(fp->argv[-1]));
  1302. fp->thisp = JSVAL_TO_OBJECT(fp->argv[-1]);
  1303. }
  1304. }
  1305. }
  1306. debug_only_v(printf("\n");)
  1307. return mp - mp_base;
  1308. }
  1309. /* Emit load instructions onto the trace that read the initial stack state. */
  1310. void
  1311. TraceRecorder::import(LIns* base, ptrdiff_t offset, jsval* p, uint8& t,
  1312. const char *prefix, uintN index, JSStackFrame *fp)
  1313. {
  1314. LIns* ins;
  1315. if (t == JSVAL_INT) { /* demoted */
  1316. JS_ASSERT(isInt32(*p));
  1317. /* Ok, we have a valid demotion attempt pending, so insert an integer
  1318. read and promote it to double since all arithmetic operations expect
  1319. to see doubles on entry. The first op to use this slot will emit a
  1320. f2i cast which will cancel out the i2f we insert here. */
  1321. ins = lir->insLoadi(base, offset);
  1322. ins = lir->ins1(LIR_i2f, ins);
  1323. } else {
  1324. JS_ASSERT(t == JSVAL_BOXED || isNumber(*p) == (t == JSVAL_DOUBLE));
  1325. if (t == JSVAL_DOUBLE) {
  1326. ins = lir->insLoad(LIR_ldq, base, offset);
  1327. } else if (t == JSVAL_BOOLEAN) {
  1328. ins = lir->insLoad(LIR_ld, base, offset);
  1329. } else {
  1330. ins = lir->insLoad(LIR_ldp, base, offset);
  1331. }
  1332. }
  1333. tracker.set(p, ins);
  1334. #ifdef DEBUG
  1335. char name[64];
  1336. JS_ASSERT(strlen(prefix) < 10);
  1337. void* mark = NULL;
  1338. jsuword* localNames = NULL;
  1339. const char* funName = NULL;
  1340. if (*prefix == 'a' || *prefix == 'v') {
  1341. mark = JS_ARENA_MARK(&cx->tempPool);
  1342. if (JS_GET_LOCAL_NAME_COUNT(fp->fun) != 0)
  1343. localNames = js_GetLocalNameArray(cx, fp->fun, &cx->tempPool);
  1344. funName = fp->fun->atom ? js_AtomToPrintableString(cx, fp->fun->atom) : "<anonymous>";
  1345. }
  1346. if (!strcmp(prefix, "argv")) {
  1347. if (index < fp->fun->nargs) {
  1348. JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(localNames[index]);
  1349. JS_snprintf(name, sizeof name, "$%s.%s", funName, js_AtomToPrintableString(cx, atom));
  1350. } else {
  1351. JS_snprintf(name, sizeof name, "$%s.<arg%d>", funName, index);
  1352. }
  1353. } else if (!strcmp(prefix, "vars")) {
  1354. JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(localNames[fp->fun->nargs + index]);
  1355. JS_snprintf(name, sizeof name, "$%s.%s", funName, js_AtomToPrintableString(cx, atom));
  1356. } else {
  1357. JS_snprintf(name, sizeof name, "$%s%d", prefix, index);
  1358. }
  1359. if (mark)
  1360. JS_ARENA_RELEASE(&cx->tempPool, mark);
  1361. addName(ins, name);
  1362. static const char* typestr[] = {
  1363. "object", "int", "double", "3", "string", "5", "boolean", "any"
  1364. };
  1365. debug_only_v(printf("import vp=%p name=%s type=%s flags=%d\n",
  1366. p, name, typestr[t & 7], t >> 3);)
  1367. #endif
  1368. }
  1369. void
  1370. TraceRecorder::import(TreeInfo* treeInfo, LIns* sp, unsigned ngslots, unsigned callDepth,
  1371. uint8* globalTypeMap, uint8* stackTypeMap)
  1372. {
  1373. /* If we get a partial list that doesn't have all the types (i.e. recording from a side
  1374. exit that was recorded but we added more global slots later), merge the missing types
  1375. from the entry type map. This is safe because at the loop edge we verify that we
  1376. have compatible types for all globals (entry type and loop edge type match). While
  1377. a different trace of the tree might have had a guard with a different type map for
  1378. these slots we just filled in here (the guard we continue from didn't know about them),
  1379. since we didn't take that particular guard the only way we could have ended up here
  1380. is if that other trace had at its end a compatible type distribution with the entry
  1381. map. Since thats exactly what we used to fill in the types our current side exit
  1382. didn't provide, this is always safe to do. */
  1383. unsigned length;
  1384. if (ngslots < (length = traceMonitor->globalTypeMap->length()))
  1385. mergeTypeMaps(&globalTypeMap, &ngslots,
  1386. traceMonitor->globalTypeMap->data(), length,
  1387. (uint8*)alloca(sizeof(uint8) * length));
  1388. JS_ASSERT(ngslots == traceMonitor->globalTypeMap->length());
  1389. /* the first time we compile a tree this will be empty as we add entries lazily */
  1390. uint16* gslots = traceMonitor->globalSlots->data();
  1391. uint8* m = globalTypeMap;
  1392. FORALL_GLOBAL_SLOTS(cx, ngslots, gslots,
  1393. import(gp_ins, nativeGlobalOffset(vp), vp, *m, vpname, vpnum, NULL);
  1394. m++;
  1395. );
  1396. ptrdiff_t offset = -treeInfo->nativeStackBase;
  1397. m = stackTypeMap;
  1398. FORALL_SLOTS_IN_PENDING_FRAMES(cx, callDepth,
  1399. import(sp, offset, vp, *m, vpname, vpnum, fp);
  1400. m++; offset += sizeof(double);
  1401. );
  1402. }
  1403. /* Lazily import a global slot if we don't already have it in the tracker. */
  1404. bool
  1405. TraceRecorder::lazilyImportGlobalSlot(unsigned slot)
  1406. {
  1407. if (slot != uint16(slot)) /* we use a table of 16-bit ints, bail out if that's not enough */
  1408. return false;
  1409. jsval* vp = &STOBJ_GET_SLOT(globalObj, slot);
  1410. if (tracker.has(vp))
  1411. return true; /* we already have it */
  1412. unsigned index = traceMonitor->globalSlots->length();
  1413. /* If this the first global we are adding, remember the shape of the global object. */
  1414. if (index == 0)
  1415. traceMonitor->globalShape = OBJ_SHAPE(JS_GetGlobalForObject(cx, cx->fp->scopeChain));
  1416. /* Add the slot to the list of interned global slots. */
  1417. traceMonitor->globalSlots->add(slot);
  1418. uint8 type = getCoercedType(*vp);
  1419. if ((type == JSVAL_INT) && oracle.isGlobalSlotUndemotable(cx->fp->script, slot))
  1420. type = JSVAL_DOUBLE;
  1421. traceMonitor->globalTypeMap->add(type);
  1422. import(gp_ins, slot*sizeof(double), vp, type, "global", index, NULL);
  1423. return true;
  1424. }
  1425. /* Write back a value onto the stack or global frames. */
  1426. LIns*
  1427. TraceRecorder::writeBack(LIns* i, LIns* base, ptrdiff_t offset)
  1428. {
  1429. /* Sink all type casts targeting the stack into the side exit by simply storing the original
  1430. (uncasted) value. Each guard generates the side exit map based on the types of the
  1431. last stores to every stack location, so its safe to not perform them on-trace. */
  1432. if (isPromoteInt(i))
  1433. i = ::demote(lir, i);
  1434. return lir->insStorei(i, base, offset);
  1435. }
  1436. /* Update the tracker, then issue a write back store. */
  1437. void
  1438. TraceRecorder::set(jsval* p, LIns* i, bool initializing)
  1439. {
  1440. JS_ASSERT(initializing || tracker.has(p));
  1441. tracker.set(p, i);
  1442. /* If we are writing to this location for the first time, calculate the offset into the
  1443. native frame manually, otherwise just look up the last load or store associated with
  1444. the same source address (p) and use the same offset/base. */
  1445. LIns* x = nativeFrameTracker.get(p);
  1446. if (!x) {
  1447. if (isGlobal(p))
  1448. x = writeBack(i, gp_ins, nativeGlobalOffset(p));
  1449. else
  1450. x = writeBack(i, lirbuf->sp, -treeInfo->nativeStackBase + nativeStackOffset(p));
  1451. nativeFrameTracker.set(p, x);
  1452. } else {
  1453. #define ASSERT_VALID_CACHE_HIT(base, offset) \
  1454. JS_ASSERT(base == lirbuf->sp || base == gp_ins); \
  1455. JS_ASSERT(offset == ((base == lirbuf->sp) \
  1456. ? -treeInfo->nativeStackBase + nativeStackOffset(p) \
  1457. : nativeGlobalOffset(p))); \
  1458. if (x->isop(LIR_st) || x->isop(LIR_stq)) {
  1459. ASSERT_VALID_CACHE_HIT(x->oprnd2(), x->oprnd3()->constval());
  1460. writeBack(i, x->oprnd2(), x->oprnd3()->constval());
  1461. } else {
  1462. JS_ASSERT(x->isop(LIR_sti) || x->isop(LIR_stqi));
  1463. ASSERT_VALID_CACHE_HIT(x->oprnd2(), x->immdisp());
  1464. writeBack(i, x->oprnd2(), x->immdisp());
  1465. }
  1466. }
  1467. #undef ASSERT_VALID_CACHE_HIT
  1468. }
  1469. LIns*
  1470. TraceRecorder::get(jsval* p) const
  1471. {
  1472. return tracker.get(p);
  1473. }
  1474. /* Determine whether the current branch instruction terminates the loop. */
  1475. static bool
  1476. js_IsLoopExit(jsbytecode* pc, jsbytecode* header)
  1477. {
  1478. switch (*pc) {
  1479. case JSOP_LT:
  1480. case JSOP_GT:
  1481. case JSOP_LE:
  1482. case JSOP_GE:
  1483. case JSOP_NE:
  1484. case JSOP_EQ:
  1485. /* These ops try to dispatch a JSOP_IFEQ or JSOP_IFNE that follows. */
  1486. JS_ASSERT(js_CodeSpec[*pc].length == 1);
  1487. pc++;
  1488. break;
  1489. default:
  1490. for (;;) {
  1491. if (*pc == JSOP_AND || *pc == JSOP_OR)
  1492. pc += GET_JUMP_OFFSET(pc);
  1493. else if (*pc == JSOP_ANDX || *pc == JSOP_ORX)
  1494. pc += GET_JUMPX_OFFSET(pc);
  1495. else
  1496. break;
  1497. }
  1498. }
  1499. switch (*pc) {
  1500. case JSOP_IFEQ:
  1501. case JSOP_IFNE:
  1502. /*
  1503. * Forward jumps are usually intra-branch, but for-in loops jump to the
  1504. * trailing enditer to clean up, so check for that case here.
  1505. */
  1506. if (pc[GET_JUMP_OFFSET(pc)] == JSOP_ENDITER)
  1507. return true;
  1508. return pc + GET_JUMP_OFFSET(pc) == header;
  1509. case JSOP_IFEQX:
  1510. case JSOP_IFNEX:
  1511. if (pc[GET_JUMPX_OFFSET(pc)] == JSOP_ENDITER)
  1512. return true;
  1513. return pc + GET_JUMPX_OFFSET(pc) == header;
  1514. default:;
  1515. }
  1516. return false;
  1517. }
  1518. /* Determine whether the current branch is a loop edge (taken or not taken). */
  1519. static bool
  1520. js_IsLoopEdge(jsbytecode* pc, jsbytecode* header)
  1521. {
  1522. switch (*pc) {
  1523. case JSOP_IFEQ:
  1524. case JSOP_IFNE:
  1525. return ((pc + GET_JUMP_OFFSET(pc)) == header);
  1526. case JSOP_IFEQX:
  1527. case JSOP_IFNEX:
  1528. return ((pc + GET_JUMPX_OFFSET(pc)) == header);
  1529. default:
  1530. JS_ASSERT((*pc == JSOP_AND) || (*pc == JSOP_ANDX) ||
  1531. (*pc == JSOP_OR) || (*pc == JSOP_ORX));
  1532. }
  1533. return false;
  1534. }
  1535. /* Promote slots if necessary to match the called tree' type map and report error if thats
  1536. impossible. */
  1537. bool
  1538. TraceRecorder::adjustCallerTypes(Fragment* f, unsigned* demote_slots, bool& trash)
  1539. {
  1540. JSTraceMonitor* tm = traceMonitor;
  1541. uint8* m = tm->globalTypeMap->data();
  1542. uint16* gslots = traceMonitor->globalSlots->data();
  1543. unsigned ngslots = traceMonitor->globalSlots->length();
  1544. uint8* map = ((TreeInfo*)f->vmprivate)->stackTypeMap.data();
  1545. bool ok = true;
  1546. trash = false;
  1547. FORALL_GLOBAL_SLOTS(cx, ngslots, gslots,
  1548. LIns* i = get(vp);
  1549. bool isPromote = isPromoteInt(i);
  1550. if (isPromote && *m == JSVAL_DOUBLE)
  1551. lir->insStorei(get(vp), gp_ins, nativeGlobalOffset(vp));
  1552. else if (!isPromote && *m == JSVAL_INT) {
  1553. oracle.markGlobalSlotUndemotable(cx->fp->script, nativeGlobalOffset(vp)/sizeof(double));
  1554. trash = true;
  1555. ok = false;
  1556. }
  1557. ++m;
  1558. );
  1559. m = map;
  1560. FORALL_SLOTS_IN_PENDING_FRAMES(cx, 0,
  1561. LIns* i = get(vp);
  1562. bool isPromote = isPromoteInt(i);
  1563. if (isPromote && *m == JSVAL_DOUBLE) {
  1564. lir->insStorei(get(vp), lirbuf->sp,
  1565. -treeInfo->nativeStackBase + nativeStackOffset(vp));
  1566. /* Aggressively undo speculation so the inner tree will compile if this fails. */
  1567. ADD_UNDEMOTE_SLOT(demote_slots, unsigned(m - map));
  1568. } else if (!isPromote && *m == JSVAL_INT) {
  1569. debug_only_v(printf("adjusting will fail, %s%d, slot %d\n", vpname, vpnum, m - map);)
  1570. ok = false;
  1571. ADD_UNDEMOTE_SLOT(demote_slots, unsigned(m - map));
  1572. } else if (JSVAL_IS_INT(*vp) && *m == JSVAL_DOUBLE) {
  1573. /* Aggressively undo speculation so the inner tree will compile if this fails. */
  1574. ADD_UNDEMOTE_SLOT(demote_slots, unsigned(m - map));
  1575. }
  1576. ++m;
  1577. );
  1578. /* If this isn't okay, tell the oracle. */
  1579. if (!ok) {
  1580. for (unsigned i = 1; i <= NUM_UNDEMOTE_SLOTS(demote_slots); i++)
  1581. oracle.markStackSlotUndemotable(cx->fp->script, cx->fp->regs->pc, demote_slots[i]);
  1582. }
  1583. JS_ASSERT(f == f->root);
  1584. return ok;
  1585. }
  1586. uint8
  1587. TraceRecorder::determineSlotType(jsval* vp) const
  1588. {
  1589. uint8 m;
  1590. LIns* i = get(vp);
  1591. m = isNumber(*vp)
  1592. ? (isPromoteInt(i) ? JSVAL_INT : JSVAL_DOUBLE)
  1593. : JSVAL_TAG(*vp);
  1594. JS_ASSERT((m != JSVAL_INT) || isInt32(*vp));
  1595. return m;
  1596. }
  1597. #define IMACRO_PC_ADJ_BITS 8
  1598. #define SCRIPT_PC_ADJ_BITS (32 - IMACRO_PC_ADJ_BITS)
  1599. // The stored imacro_pc_adj byte offset is biased by 1.
  1600. #define IMACRO_PC_ADJ_LIMIT (JS_BIT(IMACRO_PC_ADJ_BITS) - 1)
  1601. #define SCRIPT_PC_ADJ_LIMIT JS_BIT(SCRIPT_PC_ADJ_BITS)
  1602. #define IMACRO_PC_ADJ(ip) ((uintptr_t)(ip) >> SCRIPT_PC_ADJ_BITS)
  1603. #define SCRIPT_PC_ADJ(ip) ((ip) & JS_BITMASK(SCRIPT_PC_ADJ_BITS))
  1604. #define FI_SCRIPT_PC(fi,fp) ((fp)->script->code + SCRIPT_PC_ADJ((fi).ip_adj))
  1605. #define FI_IMACRO_PC(fi,fp) (IMACRO_PC_ADJ((fi).ip_adj) \
  1606. ? imacro_code[*FI_SCRIPT_PC(fi, fp)] + \
  1607. IMACRO_PC_ADJ((fi).ip_adj) \
  1608. : NULL)
  1609. #define IMACRO_PC_OK(fp,pc) JS_ASSERT(uintN((pc)-imacro_code[*(fp)->imacpc]) \
  1610. < JS_BIT(IMACRO_PC_ADJ_BITS))
  1611. #define ENCODE_IP_ADJ(fp,pc) ((fp)->imacpc \
  1612. ? (IMACRO_PC_OK(fp, pc), \
  1613. (((pc) - imacro_code[*(fp)->imacpc]) \
  1614. << SCRIPT_PC_ADJ_BITS) + \
  1615. (fp)->imacpc - (fp)->script->code) \
  1616. : (pc) - (fp)->script->code)
  1617. #define DECODE_IP_ADJ(ip,fp) (IMACRO_PC_ADJ(ip) \
  1618. ? (fp)->imacpc = (fp)->script->code + \
  1619. SCRIPT_PC_ADJ(ip), \
  1620. (fp)->regs->pc = imacro_code[*(fp)->imacpc] + \
  1621. IMACRO_PC_ADJ(ip) \
  1622. : (fp)->regs->pc = (fp)->script->code + (ip))
  1623. static jsbytecode* imacro_code[JSOP_LIMIT];
  1624. LIns*
  1625. TraceRecorder::snapshot(ExitType exitType)
  1626. {
  1627. JSStackFrame* fp = cx->fp;
  1628. JSFrameRegs* regs = fp->regs;
  1629. jsbytecode* pc = regs->pc;
  1630. if (exitType == BRANCH_EXIT && js_IsLoopExit(pc, (jsbytecode*)fragment->root->ip))
  1631. exitType = LOOP_EXIT;
  1632. /* Check for a return-value opcode that needs to restart at the next instruction. */
  1633. const JSCodeSpec& cs = js_CodeSpec[*pc];
  1634. /* WARNING: don't return before restoring the original pc if (resumeAfter). */
  1635. bool resumeAfter = (pendingTraceableNative &&
  1636. JSTN_ERRTYPE(pendingTraceableNative) == FAIL_JSVAL);
  1637. if (resumeAfter) {
  1638. JS_ASSERT(*pc == JSOP_CALL || *pc == JSOP_APPLY || *pc == JSOP_NEXTITER);
  1639. pc += cs.length;
  1640. regs->pc = pc;
  1641. MUST_FLOW_THROUGH("restore_pc");
  1642. }
  1643. /* Generate the entry map for the (possibly advanced) pc and stash it in the trace. */
  1644. unsigned stackSlots = js_NativeStackSlots(cx, callDepth);
  1645. /* It's sufficient to track the native stack use here since all stores above the
  1646. stack watermark defined by guards are killed. */
  1647. trackNativeStackUse(stackSlots + 1);
  1648. /* Capture the type map into a temporary location. */
  1649. unsigned ngslots = traceMonitor->globalSlots->length();
  1650. unsigned typemap_size = (stackSlots + ngslots) * sizeof(uint8);
  1651. uint8* typemap = (uint8*)alloca(typemap_size);
  1652. uint8* m = typemap;
  1653. /* Determine the type of a store by looking at the current type of the actual value the
  1654. interpreter is using. For numbers we have to check what kind of store we used last
  1655. (integer or double) to figure out what the side exit show reflect in its typemap. */
  1656. FORALL_SLOTS(cx, ngslots, traceMonitor->globalSlots->data(), callDepth,
  1657. *m++ = determineSlotType(vp);
  1658. );
  1659. JS_ASSERT(unsigned(m - typemap) == ngslots + stackSlots);
  1660. /* If we are capturing the stack state on a specific instruction, the value on or near
  1661. the top of the stack is a boxed value. Either pc[-cs.length] is JSOP_NEXTITER and we
  1662. want one below top of stack, or else it's JSOP_CALL and we want top of stack. */
  1663. if (resumeAfter) {
  1664. m[(pc[-cs.length] == JSOP_NEXTITER) ? -2 : -1] = JSVAL_BOXED;
  1665. /* Now restore the the original pc (after which early returns are ok). */
  1666. MUST_FLOW_LABEL(restore_pc);
  1667. regs->pc = pc - cs.length;
  1668. } else {
  1669. /* If we take a snapshot on a goto, advance to the target address. This avoids inner
  1670. trees returning on a break goto, which the outer recorder then would confuse with
  1671. a break in the outer tree. */
  1672. if (*pc == JSOP_GOTO)
  1673. pc += GET_JUMP_OFFSET(pc);
  1674. else if (*pc == JSOP_GOTOX)
  1675. pc += GET_JUMPX_OFFSET(pc);
  1676. }
  1677. intptr_t ip_adj = ENCODE_IP_ADJ(fp, pc);
  1678. /* Check if we already have a matching side exit. If so use that side exit structure,
  1679. otherwise we have to create our own. */
  1680. VMSideExit** exits = treeInfo->sideExits.data();
  1681. unsigned nexits = treeInfo->sideExits.length();
  1682. if (exitType == LOOP_EXIT) {
  1683. for (unsigned n = 0; n < nexits; ++n) {
  1684. VMSideExit* e = exits[n];
  1685. if (e->ip_adj == ip_adj &&
  1686. !memcmp(getTypeMap(exits[n]), typemap, typemap_size)) {
  1687. LIns* data = lir_buf_writer->skip(sizeof(GuardRecord));
  1688. GuardRecord* rec = (GuardRecord*)data->payload();
  1689. /* setup guard record structure with shared side exit */
  1690. memset(rec, 0, sizeof(GuardRecord));