From 7e754c5e8c2a787c37d9b04f5f7a96bdf992a895 Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Sat, 5 Dec 2009 23:35:06 -0800 Subject: [PATCH 01/82] See bug 533138. --- js/src/tests/e4x/Regress/jstests.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/tests/e4x/Regress/jstests.list b/js/src/tests/e4x/Regress/jstests.list index 331a3861fdb..5a5793a1afb 100644 --- a/js/src/tests/e4x/Regress/jstests.list +++ b/js/src/tests/e4x/Regress/jstests.list @@ -52,7 +52,7 @@ script regress-354145-03.js script regress-354145-04.js script regress-354145-05.js script regress-354145-07.js -script regress-354998.js +skip-if(!xulRuntime.shell&&isDebugBuild) script regress-354998.js # very slow; test needs revising script regress-355474-02.js script regress-355478.js script regress-355569.js From e93e6c4af560aafbe099d2d17b3151751b9e356f Mon Sep 17 00:00:00 2001 From: Leon Sha Date: Thu, 26 Nov 2009 09:21:05 +0800 Subject: [PATCH 02/82] Bug 530979 - NJ: Build errors for lirasm.cpp on solaris with sun studio. r=graydon --HG-- extra : convert_revision : 69ef86e45856c850accc542111053c4387a8b804 --- js/src/lirasm/lirasm.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/js/src/lirasm/lirasm.cpp b/js/src/lirasm/lirasm.cpp index f70415042c6..1478a7bbb63 100644 --- a/js/src/lirasm/lirasm.cpp +++ b/js/src/lirasm/lirasm.cpp @@ -380,7 +380,7 @@ imm(const string &s) } uint64_t -quad(const string &s) +lquad(const string &s) { stringstream tmp(s); uint64_t ret; @@ -571,7 +571,11 @@ FragmentAssembler::assemble_jump(bool isCond) return mLir->insBranch(mOpcode, condition, target); } else { LIns *ins = mLir->insBranch(mOpcode, condition, NULL); +#ifdef __SUNPRO_CC + mFwdJumps.insert(make_pair(name, ins)); +#else mFwdJumps.insert(make_pair(name, ins)); +#endif return ins; } } @@ -842,7 +846,11 @@ FragmentAssembler::assembleFragment(LirTokenStream &in, bool implicitBegin, cons if (!lab.empty()) { ins = mLir->ins0(LIR_label); typedef multimap mulmap; +#ifdef __SUNPRO_CC + typedef mulmap::iterator ci; +#else typedef mulmap::const_iterator ci; +#endif pair range = mFwdJumps.equal_range(lab); for (ci i = range.first; i != range.second; ++i) { i->second->setTarget(ins); @@ -968,7 +976,7 @@ FragmentAssembler::assembleFragment(LirTokenStream &in, bool implicitBegin, cons case LIR_quad: need(1); - ins = mLir->insImmq(quad(mTokens[0])); + ins = mLir->insImmq(lquad(mTokens[0])); break; case LIR_float: From e913eb4c6a6e6d22d80ffa81aa5d5c0d3c802cb2 Mon Sep 17 00:00:00 2001 From: Leon Sha Date: Mon, 30 Nov 2009 09:24:10 +0800 Subject: [PATCH 03/82] Bug 531218 - NJ: Remove unused _getfp for solaris from nanojit. r=stejohns. --HG-- extra : convert_revision : 2d22cabbe865243948ddcad4bc73dba4fc0c5aef --- js/src/nanojit/VMPI.cpp | 5 ----- js/src/nanojit/avmplus.cpp | 5 ----- 2 files changed, 10 deletions(-) diff --git a/js/src/nanojit/VMPI.cpp b/js/src/nanojit/VMPI.cpp index 79993e7f649..b7727960d52 100644 --- a/js/src/nanojit/VMPI.cpp +++ b/js/src/nanojit/VMPI.cpp @@ -35,11 +35,6 @@ #include "nanojit.h" #ifdef SOLARIS - #include - #include - #include - #include - extern "C" caddr_t _getfp(void); typedef caddr_t maddr_ptr; #else typedef void *maddr_ptr; diff --git a/js/src/nanojit/avmplus.cpp b/js/src/nanojit/avmplus.cpp index 94bbc604606..ce21b9bf616 100644 --- a/js/src/nanojit/avmplus.cpp +++ b/js/src/nanojit/avmplus.cpp @@ -35,11 +35,6 @@ #include "nanojit.h" #ifdef SOLARIS - #include - #include - #include - #include - extern "C" caddr_t _getfp(void); typedef caddr_t maddr_ptr; #else typedef void *maddr_ptr; From b7cf8c804f92e12cd1b6d726593f8841026729b7 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 30 Nov 2009 17:26:15 +1100 Subject: [PATCH 04/82] Bug 531324 - nanojit: improve int32 constant generation in i386 backend. r=graydon. --HG-- extra : convert_revision : 3b04b5fdd47f0469f040fb147d60ed4cd30d8c8e --- js/src/nanojit/Nativei386.cpp | 20 ++++++++++++-------- js/src/nanojit/Nativei386.h | 1 + 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/js/src/nanojit/Nativei386.cpp b/js/src/nanojit/Nativei386.cpp index 1dbae288aaa..f9ba44486c2 100644 --- a/js/src/nanojit/Nativei386.cpp +++ b/js/src/nanojit/Nativei386.cpp @@ -154,7 +154,7 @@ namespace nanojit MR(SP,FP); // return value is GuardRecord* - LDi(EAX, int(lr)); + asm_int(EAX, int(lr), /*canClobberCCs*/true); } NIns *Assembler::genEpilogue() @@ -407,7 +407,7 @@ namespace nanojit if (!i->getArIndex()) { i->markAsClear(); } - LDi(r, i->imm32()); + asm_int(r, i->imm32(), /*canClobberCCs*/false); } else if (i->isop(LIR_param) && i->paramKind() == 0 && (arg = i->paramArg()) >= (abi_regcount = max_abi_regs[_thisfrag->lirbuf->abi])) { @@ -1150,11 +1150,15 @@ namespace nanojit void Assembler::asm_int(LInsp ins) { Register rr = prepResultReg(ins, GpRegs); - int32_t val = ins->imm32(); - if (val == 0) - XOR(rr,rr); + asm_int(rr, ins->imm32(), /*canClobberCCs*/true); + } + + void Assembler::asm_int(Register r, int32_t val, bool canClobberCCs) + { + if (val == 0 && canClobberCCs) + XOR(r, r); else - LDi(rr, val); + LDi(r, val); } void Assembler::asm_quad(LInsp ins) @@ -1182,7 +1186,7 @@ namespace nanojit Register gr = registerAllocTmp(GpRegs); SSE_CVTSI2SD(rr, gr); SSE_XORPDr(rr,rr); // zero rr to ensure no dependency stalls - LDi(gr, (int)d); + asm_int(gr, (int)d, /*canClobberCCs*/true); } else { findMemFor(ins); const int d = disp(ins); @@ -1329,7 +1333,7 @@ namespace nanojit if (isKnownReg(r)) { // arg goes in specific register if (p->isconst()) { - LDi(r, p->imm32()); + asm_int(r, p->imm32(), /*canClobberCCs*/true); } else { if (p->isUsed()) { if (!p->hasKnownReg()) { diff --git a/js/src/nanojit/Nativei386.h b/js/src/nanojit/Nativei386.h index 10c255ba46c..759f2eabbe9 100644 --- a/js/src/nanojit/Nativei386.h +++ b/js/src/nanojit/Nativei386.h @@ -178,6 +178,7 @@ namespace nanojit void nativePageReset();\ void nativePageSetup();\ void underrunProtect(int);\ + void asm_int(Register r, int32_t val, bool canClobberCCs);\ void asm_stkarg(LInsp p, int32_t& stkd);\ void asm_farg(LInsp, int32_t& stkd);\ void asm_arg(ArgSize sz, LInsp p, Register r, int32_t& stkd);\ From e3584b76b36e8a0c87f04b69fc7a3ecc0dca5226 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Dec 2009 14:56:44 +1100 Subject: [PATCH 05/82] Bug 527754 - CseFilter not able to handle downstream modification of instructions. r=rreitmai,gal. --HG-- extra : convert_revision : 8d7d6dcce7ebda66b1aab48f40b46c1e7df2d91d --- js/src/nanojit/LIR.cpp | 66 ++++++++++++++++++++++++++++-------------- js/src/nanojit/LIR.h | 2 +- 2 files changed, 45 insertions(+), 23 deletions(-) diff --git a/js/src/nanojit/LIR.cpp b/js/src/nanojit/LIR.cpp index 15a4e1cf1f0..73b450d8d09 100644 --- a/js/src/nanojit/LIR.cpp +++ b/js/src/nanojit/LIR.cpp @@ -1205,22 +1205,15 @@ namespace nanojit return k; } - LInsp LInsHashSet::findImmf(double a, uint32_t &k) + LInsp LInsHashSet::findImmf(uint64_t a, uint32_t &k) { - // We must pun 'a' as a uint64_t otherwise 0 and -0 will be treated as - // equal, which breaks things (see bug 527288). - union { - double d; - uint64_t u64; - } u; - u.d = a; LInsHashKind kind = LInsImmf; const uint32_t bitmask = (m_cap[kind] - 1) & ~0x1; uint32_t hash = hashImmf(a) & bitmask; uint32_t n = 7 << 1; LInsp ins; while ((ins = m_list[kind][hash]) != NULL && - (ins->imm64() != u.u64)) + (ins->imm64() != a)) { NanoAssert(ins->isconstf()); hash = (hash + (n += 2)) & bitmask; // quadratic probe @@ -1232,7 +1225,7 @@ namespace nanojit uint32_t LInsHashSet::findImmf(LInsp ins) { uint32_t k; - findImmf(ins->imm64f(), k); + findImmf(ins->imm64(), k); return k; } @@ -1930,7 +1923,11 @@ namespace nanojit LInsp ins = exprs->findImm(imm, k); if (ins) return ins; - return exprs->add(LInsImm, out->insImm(imm), k); + ins = out->insImm(imm); + // We assume that downstream stages do not modify the instruction, so + // that we can insert 'ins' into slot 'k'. Check this. + NanoAssert(ins->opcode() == LIR_int && ins->imm32() == imm); + return exprs->add(LInsImm, ins, k); } LIns* CseFilter::insImmq(uint64_t q) @@ -1939,16 +1936,27 @@ namespace nanojit LInsp ins = exprs->findImmq(q, k); if (ins) return ins; - return exprs->add(LInsImmq, out->insImmq(q), k); + ins = out->insImmq(q); + NanoAssert(ins->opcode() == LIR_quad && ins->imm64() == q); + return exprs->add(LInsImmq, ins, k); } LIns* CseFilter::insImmf(double d) { uint32_t k; - LInsp ins = exprs->findImmf(d, k); + // We must pun 'd' as a uint64_t otherwise 0 and -0 will be treated as + // equal, which breaks things (see bug 527288). + union { + double d; + uint64_t u64; + } u; + u.d = d; + LInsp ins = exprs->findImmf(u.u64, k); if (ins) return ins; - return exprs->add(LInsImmf, out->insImmf(d), k); + ins = out->insImmf(d); + NanoAssert(ins->opcode() == LIR_float && ins->imm64() == u.u64); + return exprs->add(LInsImmf, ins, k); } LIns* CseFilter::ins0(LOpcode v) @@ -1965,7 +1973,9 @@ namespace nanojit LInsp ins = exprs->find1(v, a, k); if (ins) return ins; - return exprs->add(LIns1, out->ins1(v,a), k); + ins = out->ins1(v, a); + NanoAssert(ins->opcode() == v && ins->oprnd1() == a); + return exprs->add(LIns1, ins, k); } return out->ins1(v,a); } @@ -1977,7 +1987,9 @@ namespace nanojit LInsp ins = exprs->find2(v, a, b, k); if (ins) return ins; - return exprs->add(LIns2, out->ins2(v,a,b), k); + ins = out->ins2(v, a, b); + NanoAssert(ins->opcode() == v && ins->oprnd1() == a && ins->oprnd2() == b); + return exprs->add(LIns2, ins, k); } return out->ins2(v,a,b); } @@ -1989,7 +2001,10 @@ namespace nanojit LInsp ins = exprs->find3(v, a, b, c, k); if (ins) return ins; - return exprs->add(LIns3, out->ins3(v,a,b,c), k); + ins = out->ins3(v, a, b, c); + NanoAssert(ins->opcode() == v && ins->oprnd1() == a && ins->oprnd2() == b && + ins->oprnd3() == c); + return exprs->add(LIns3, ins, k); } LIns* CseFilter::insLoad(LOpcode v, LInsp base, int32_t disp) @@ -1999,9 +2014,11 @@ namespace nanojit LInsp ins = exprs->findLoad(v, base, disp, k); if (ins) return ins; - return exprs->add(LInsLoad, out->insLoad(v,base,disp), k); + ins = out->insLoad(v, base, disp); + NanoAssert(ins->opcode() == v && ins->oprnd1() == base && ins->disp() == disp); + return exprs->add(LInsLoad, ins, k); } - return out->insLoad(v,base,disp); + return out->insLoad(v, base, disp); } LInsp CseFilter::insGuard(LOpcode v, LInsp c, GuardRecord *gr) @@ -2029,7 +2046,9 @@ namespace nanojit LInsp ins = exprs->find1(v, c, k); if (ins) return 0; - return exprs->add(LIns1, out->insGuard(v,c,gr), k); + ins = out->insGuard(v, c, gr); + NanoAssert(ins->opcode() == v && ins->oprnd1() == c); + return exprs->add(LIns1, ins, k); } return out->insGuard(v, c, gr); } @@ -2042,7 +2061,9 @@ namespace nanojit LInsp ins = exprs->findCall(ci, argc, args, k); if (ins) return ins; - return exprs->add(LInsCall, out->insCall(ci, args), k); + ins = out->insCall(ci, args); + NanoAssert(ins->isCall() && ins->callInfo() == ci && argsmatch(ins, argc, args)); + return exprs->add(LInsCall, ins, k); } return out->insCall(ci, args); } @@ -2192,7 +2213,8 @@ namespace nanojit LInsp ins = exprs->findLoad(v, base, disp, k); if (ins) return ins; - return exprs->add(LInsLoad, out->insLoad(v,base,disp), k); + ins = out->insLoad(v, base, disp); + return exprs->add(LInsLoad, ins, k); } return out->insLoad(v, base, disp); } diff --git a/js/src/nanojit/LIR.h b/js/src/nanojit/LIR.h index a6a4de9146a..d8afa9a084f 100644 --- a/js/src/nanojit/LIR.h +++ b/js/src/nanojit/LIR.h @@ -1289,7 +1289,7 @@ namespace nanojit // These public versions are used before an LIns has been created. LInsp findImm(int32_t a, uint32_t &k); LInsp findImmq(uint64_t a, uint32_t &k); - LInsp findImmf(double d, uint32_t &k); + LInsp findImmf(uint64_t d, uint32_t &k); LInsp find1(LOpcode v, LInsp a, uint32_t &k); LInsp find2(LOpcode v, LInsp a, LInsp b, uint32_t &k); LInsp find3(LOpcode v, LInsp a, LInsp b, LInsp c, uint32_t &k); From ed2756a1e0ecddd6647f5cce740dfad65d833e63 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Dec 2009 15:16:57 +1100 Subject: [PATCH 06/82] Backed out changeset 8d7d6dcce7eb (due to ARM and WinNT bustage). --HG-- extra : convert_revision : 2d17a9b2e8daa8f3e89cd756a7eb23a41579f7e1 --- js/src/nanojit/LIR.cpp | 66 ++++++++++++++---------------------------- js/src/nanojit/LIR.h | 2 +- 2 files changed, 23 insertions(+), 45 deletions(-) diff --git a/js/src/nanojit/LIR.cpp b/js/src/nanojit/LIR.cpp index 73b450d8d09..15a4e1cf1f0 100644 --- a/js/src/nanojit/LIR.cpp +++ b/js/src/nanojit/LIR.cpp @@ -1205,15 +1205,22 @@ namespace nanojit return k; } - LInsp LInsHashSet::findImmf(uint64_t a, uint32_t &k) + LInsp LInsHashSet::findImmf(double a, uint32_t &k) { + // We must pun 'a' as a uint64_t otherwise 0 and -0 will be treated as + // equal, which breaks things (see bug 527288). + union { + double d; + uint64_t u64; + } u; + u.d = a; LInsHashKind kind = LInsImmf; const uint32_t bitmask = (m_cap[kind] - 1) & ~0x1; uint32_t hash = hashImmf(a) & bitmask; uint32_t n = 7 << 1; LInsp ins; while ((ins = m_list[kind][hash]) != NULL && - (ins->imm64() != a)) + (ins->imm64() != u.u64)) { NanoAssert(ins->isconstf()); hash = (hash + (n += 2)) & bitmask; // quadratic probe @@ -1225,7 +1232,7 @@ namespace nanojit uint32_t LInsHashSet::findImmf(LInsp ins) { uint32_t k; - findImmf(ins->imm64(), k); + findImmf(ins->imm64f(), k); return k; } @@ -1923,11 +1930,7 @@ namespace nanojit LInsp ins = exprs->findImm(imm, k); if (ins) return ins; - ins = out->insImm(imm); - // We assume that downstream stages do not modify the instruction, so - // that we can insert 'ins' into slot 'k'. Check this. - NanoAssert(ins->opcode() == LIR_int && ins->imm32() == imm); - return exprs->add(LInsImm, ins, k); + return exprs->add(LInsImm, out->insImm(imm), k); } LIns* CseFilter::insImmq(uint64_t q) @@ -1936,27 +1939,16 @@ namespace nanojit LInsp ins = exprs->findImmq(q, k); if (ins) return ins; - ins = out->insImmq(q); - NanoAssert(ins->opcode() == LIR_quad && ins->imm64() == q); - return exprs->add(LInsImmq, ins, k); + return exprs->add(LInsImmq, out->insImmq(q), k); } LIns* CseFilter::insImmf(double d) { uint32_t k; - // We must pun 'd' as a uint64_t otherwise 0 and -0 will be treated as - // equal, which breaks things (see bug 527288). - union { - double d; - uint64_t u64; - } u; - u.d = d; - LInsp ins = exprs->findImmf(u.u64, k); + LInsp ins = exprs->findImmf(d, k); if (ins) return ins; - ins = out->insImmf(d); - NanoAssert(ins->opcode() == LIR_float && ins->imm64() == u.u64); - return exprs->add(LInsImmf, ins, k); + return exprs->add(LInsImmf, out->insImmf(d), k); } LIns* CseFilter::ins0(LOpcode v) @@ -1973,9 +1965,7 @@ namespace nanojit LInsp ins = exprs->find1(v, a, k); if (ins) return ins; - ins = out->ins1(v, a); - NanoAssert(ins->opcode() == v && ins->oprnd1() == a); - return exprs->add(LIns1, ins, k); + return exprs->add(LIns1, out->ins1(v,a), k); } return out->ins1(v,a); } @@ -1987,9 +1977,7 @@ namespace nanojit LInsp ins = exprs->find2(v, a, b, k); if (ins) return ins; - ins = out->ins2(v, a, b); - NanoAssert(ins->opcode() == v && ins->oprnd1() == a && ins->oprnd2() == b); - return exprs->add(LIns2, ins, k); + return exprs->add(LIns2, out->ins2(v,a,b), k); } return out->ins2(v,a,b); } @@ -2001,10 +1989,7 @@ namespace nanojit LInsp ins = exprs->find3(v, a, b, c, k); if (ins) return ins; - ins = out->ins3(v, a, b, c); - NanoAssert(ins->opcode() == v && ins->oprnd1() == a && ins->oprnd2() == b && - ins->oprnd3() == c); - return exprs->add(LIns3, ins, k); + return exprs->add(LIns3, out->ins3(v,a,b,c), k); } LIns* CseFilter::insLoad(LOpcode v, LInsp base, int32_t disp) @@ -2014,11 +1999,9 @@ namespace nanojit LInsp ins = exprs->findLoad(v, base, disp, k); if (ins) return ins; - ins = out->insLoad(v, base, disp); - NanoAssert(ins->opcode() == v && ins->oprnd1() == base && ins->disp() == disp); - return exprs->add(LInsLoad, ins, k); + return exprs->add(LInsLoad, out->insLoad(v,base,disp), k); } - return out->insLoad(v, base, disp); + return out->insLoad(v,base,disp); } LInsp CseFilter::insGuard(LOpcode v, LInsp c, GuardRecord *gr) @@ -2046,9 +2029,7 @@ namespace nanojit LInsp ins = exprs->find1(v, c, k); if (ins) return 0; - ins = out->insGuard(v, c, gr); - NanoAssert(ins->opcode() == v && ins->oprnd1() == c); - return exprs->add(LIns1, ins, k); + return exprs->add(LIns1, out->insGuard(v,c,gr), k); } return out->insGuard(v, c, gr); } @@ -2061,9 +2042,7 @@ namespace nanojit LInsp ins = exprs->findCall(ci, argc, args, k); if (ins) return ins; - ins = out->insCall(ci, args); - NanoAssert(ins->isCall() && ins->callInfo() == ci && argsmatch(ins, argc, args)); - return exprs->add(LInsCall, ins, k); + return exprs->add(LInsCall, out->insCall(ci, args), k); } return out->insCall(ci, args); } @@ -2213,8 +2192,7 @@ namespace nanojit LInsp ins = exprs->findLoad(v, base, disp, k); if (ins) return ins; - ins = out->insLoad(v, base, disp); - return exprs->add(LInsLoad, ins, k); + return exprs->add(LInsLoad, out->insLoad(v,base,disp), k); } return out->insLoad(v, base, disp); } diff --git a/js/src/nanojit/LIR.h b/js/src/nanojit/LIR.h index d8afa9a084f..a6a4de9146a 100644 --- a/js/src/nanojit/LIR.h +++ b/js/src/nanojit/LIR.h @@ -1289,7 +1289,7 @@ namespace nanojit // These public versions are used before an LIns has been created. LInsp findImm(int32_t a, uint32_t &k); LInsp findImmq(uint64_t a, uint32_t &k); - LInsp findImmf(uint64_t d, uint32_t &k); + LInsp findImmf(double d, uint32_t &k); LInsp find1(LOpcode v, LInsp a, uint32_t &k); LInsp find2(LOpcode v, LInsp a, LInsp b, uint32_t &k); LInsp find3(LOpcode v, LInsp a, LInsp b, LInsp c, uint32_t &k); From 5be99c7ad23cad4417a98e3d9621add2973a2825 Mon Sep 17 00:00:00 2001 From: Rick Reitmaier Date: Tue, 1 Dec 2009 11:36:07 -0800 Subject: [PATCH 07/82] Bug 500466 - avmshell crashes on P3 cpu machine with JIT enabled (r+nnethercote,edwsmith) Copy x86 CPU detection code from jstracer into Nativei386; allowing hosts to share the common code. One potential issue with this patch is that the detection code is executed each time an Assembler object is created. If it becomes an issue, it might be worthwhile to add a tri-state to config in order to capture the value post-test. --HG-- extra : convert_revision : ab0a893ffe833f1562216186567d4e7798be45aa --- js/src/nanojit/Nativei386.cpp | 38 +++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/js/src/nanojit/Nativei386.cpp b/js/src/nanojit/Nativei386.cpp index f9ba44486c2..c595423d13f 100644 --- a/js/src/nanojit/Nativei386.cpp +++ b/js/src/nanojit/Nativei386.cpp @@ -70,11 +70,45 @@ namespace nanojit 0 /* ABI_CDECL */ }; + static bool CheckForSSE2() + { + int features = 0; + #if defined _MSC_VER + __asm + { + pushad + mov eax, 1 + cpuid + mov features, edx + popad + } + #elif defined __GNUC__ + asm("xchg %%esi, %%ebx\n" /* we can't clobber ebx on gcc (PIC register) */ + "mov $0x01, %%eax\n" + "cpuid\n" + "mov %%edx, %0\n" + "xchg %%esi, %%ebx\n" + : "=m" (features) + : /* We have no inputs */ + : "%eax", "%esi", "%ecx", "%edx" + ); + #elif defined __SUNPRO_C || defined __SUNPRO_CC + asm("push %%ebx\n" + "mov $0x01, %%eax\n" + "cpuid\n" + "pop %%ebx\n" + : "=d" (features) + : /* We have no inputs */ + : "%eax", "%ecx" + ); + #endif + return (features & (1<<26)) != 0; + } void Assembler::nInit(AvmCore* core) { (void) core; - VMPI_getDate(); + config.sse2 = config.sse2 && CheckForSSE2(); } void Assembler::nBeginAssembly() { @@ -1880,6 +1914,6 @@ namespace nanojit SWAP(NIns*, codeEnd, exitEnd); verbose_only( SWAP(size_t, codeBytes, exitBytes); ) } - + #endif /* FEATURE_NANOJIT */ } From 45a6270f4c6946c17332ac573ce8336487c5b841 Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Tue, 1 Dec 2009 16:50:03 -0800 Subject: [PATCH 08/82] Add new opcodes to allow load/store of 8-bit int, 16-bit int, and 32-bit floats. Initially implemented only for i386, other backend implementations to follow. See https://bugzilla.mozilla.org/show_bug.cgi?id=527083 for more detail. (r=rreitmai, nnethercote) --HG-- extra : convert_revision : 7c5395e67078266797de2e2de6555974ee61acf7 --- js/src/lirasm/lirasm.cpp | 30 +++- js/src/nanojit/Assembler.cpp | 34 +++-- js/src/nanojit/Assembler.h | 7 +- js/src/nanojit/LIR.cpp | 24 +++- js/src/nanojit/LIR.h | 21 ++- js/src/nanojit/LIRopcode.tbl | 46 +++---- js/src/nanojit/Native.h | 4 + js/src/nanojit/NativeARM.cpp | 87 +++++++++--- js/src/nanojit/NativeARM.h | 1 + js/src/nanojit/NativePPC.cpp | 101 +++++++++++--- js/src/nanojit/NativePPC.h | 7 + js/src/nanojit/NativeSparc.cpp | 73 ++++++++-- js/src/nanojit/NativeSparc.h | 6 +- js/src/nanojit/NativeX64.cpp | 74 +++++++++- js/src/nanojit/NativeX64.h | 1 + js/src/nanojit/Nativei386.cpp | 245 +++++++++++++++++++++++++++------ js/src/nanojit/Nativei386.h | 114 +++++++++++++-- 17 files changed, 715 insertions(+), 160 deletions(-) diff --git a/js/src/lirasm/lirasm.cpp b/js/src/lirasm/lirasm.cpp index 1478a7bbb63..fa10bb3808c 100644 --- a/js/src/lirasm/lirasm.cpp +++ b/js/src/lirasm/lirasm.cpp @@ -984,14 +984,29 @@ FragmentAssembler::assembleFragment(LirTokenStream &in, bool implicitBegin, cons ins = mLir->insImmf(immf(mTokens[0])); break; +#if NJ_EXPANDED_LOADSTORE_SUPPORTED + case LIR_stb: + case LIR_sts: + case LIR_st32f: +#endif case LIR_sti: case LIR_stqi: need(3); - ins = mLir->insStorei(ref(mTokens[0]), + ins = mLir->insStore(mOpcode, ref(mTokens[0]), ref(mTokens[1]), imm(mTokens[2])); break; +#if NJ_EXPANDED_LOADSTORE_SUPPORTED + case LIR_ldzb: + case LIR_ldzs: + case LIR_ldsb: + case LIR_ldss: + case LIR_ldcsb: + case LIR_ldcss: + case LIR_ld32f: + case LIR_ldc32f: +#endif case LIR_ld: case LIR_ldc: case LIR_ldq: @@ -1348,11 +1363,24 @@ FragmentAssembler::assembleRandomFragment(int nIns) I_loads.push_back(LIR_ldc); I_loads.push_back(LIR_ldcb); I_loads.push_back(LIR_ldcs); +#if NJ_EXPANDED_LOADSTORE_SUPPORTED + I_loads.push_back(LIR_ldzb); + I_loads.push_back(LIR_ldzs); + I_loads.push_back(LIR_ldsb); + I_loads.push_back(LIR_ldss); + I_loads.push_back(LIR_ldcsb); + I_loads.push_back(LIR_ldcss); +#endif vector QorF_loads; QorF_loads.push_back(LIR_ldq); // weight LIR_ldq the heaviest QorF_loads.push_back(LIR_ldq); QorF_loads.push_back(LIR_ldqc); +#if NJ_EXPANDED_LOADSTORE_SUPPORTED + // this loads a 32-bit float and expands to 64-bit float + QorF_loads.push_back(LIR_ld32f); + QorF_loads.push_back(LIR_ldc32f); +#endif enum LInsClass { #define CLASS(name, only64bit, relFreq) name, diff --git a/js/src/nanojit/Assembler.cpp b/js/src/nanojit/Assembler.cpp index 0bd4ccd8d4f..0efb9079ebf 100755 --- a/js/src/nanojit/Assembler.cpp +++ b/js/src/nanojit/Assembler.cpp @@ -1063,15 +1063,24 @@ namespace nanojit asm_cmov(ins); break; } + case LIR_ldzb: + case LIR_ldzs: + case LIR_ldsb: + case LIR_ldss: + case LIR_ldcsb: + case LIR_ldcss: case LIR_ld: case LIR_ldc: case LIR_ldcb: case LIR_ldcs: { countlir_ld(); - asm_ld(ins); + asm_load32(ins); break; } + + case LIR_ld32f: + case LIR_ldc32f: case LIR_ldq: case LIR_ldqc: { @@ -1159,27 +1168,30 @@ namespace nanojit asm_promote(ins); break; } + case LIR_stb: + case LIR_sts: case LIR_sti: { countlir_st(); - asm_store32(ins->oprnd1(), ins->disp(), ins->oprnd2()); + asm_store32(op, ins->oprnd1(), ins->disp(), ins->oprnd2()); break; } + case LIR_st32f: case LIR_stqi: { countlir_stq(); LIns* value = ins->oprnd1(); LIns* base = ins->oprnd2(); int dr = ins->disp(); - if (value->isop(LIR_qjoin)) + if (value->isop(LIR_qjoin) && op != LIR_st32f) { // this is correct for little-endian only - asm_store32(value->oprnd1(), dr, base); - asm_store32(value->oprnd2(), dr+4, base); + asm_store32(op, value->oprnd1(), dr, base); + asm_store32(op, value->oprnd2(), dr+4, base); } else { - asm_store64(value, dr, base); + asm_store64(op, value, dr, base); } break; } @@ -1805,7 +1817,7 @@ namespace nanojit } } } - + /** * Merge the current state of the registers with a previously stored version * current == saved skip @@ -1825,9 +1837,13 @@ namespace nanojit // of load/store multiple instructions. Hence iterate the loop the // other way. The "r <= LastReg" guards against wraparound in // the case where Register is treated as unsigned and FirstReg is zero. - for (Register r=LastReg; r >= FirstReg && r <= LastReg; - r = prevreg(r)) + // + // Note, the loop var is deliberately typed as int (*not* Register) + // to outsmart compilers that will otherwise report + // "error: comparison is always true due to limited range of data type". + for (int ri=LastReg; ri >= FirstReg && ri <= LastReg; ri = int(prevreg(Register(ri)))) { + Register const r = Register(ri); LIns * curins = _allocator.getActive(r); LIns * savedins = saved.getActive(r); if (curins == savedins) diff --git a/js/src/nanojit/Assembler.h b/js/src/nanojit/Assembler.h index b738bb94a47..ca73cf5e331 100644 --- a/js/src/nanojit/Assembler.h +++ b/js/src/nanojit/Assembler.h @@ -341,10 +341,9 @@ namespace nanojit NIns* asm_exit(LInsp guard); NIns* asm_leave_trace(LInsp guard); void asm_qjoin(LIns *ins); - void asm_store32(LIns *val, int d, LIns *base); - void asm_store64(LIns *val, int d, LIns *base); + void asm_store32(LOpcode op, LIns *val, int d, LIns *base); + void asm_store64(LOpcode op, LIns *val, int d, LIns *base); void asm_restore(LInsp, Register); - void asm_load(int d, Register r); void asm_spilli(LInsp i, bool pop); void asm_spill(Register rr, int d, bool pop, bool quad); void asm_load64(LInsp i); @@ -354,7 +353,7 @@ namespace nanojit void asm_cond(LInsp i); void asm_arith(LInsp i); void asm_neg_not(LInsp i); - void asm_ld(LInsp i); + void asm_load32(LInsp i); void asm_cmov(LInsp i); void asm_param(LInsp i); void asm_int(LInsp i); diff --git a/js/src/nanojit/LIR.cpp b/js/src/nanojit/LIR.cpp index 15a4e1cf1f0..715f5ed32ee 100644 --- a/js/src/nanojit/LIR.cpp +++ b/js/src/nanojit/LIR.cpp @@ -223,9 +223,8 @@ namespace nanojit return startOfRoom; } - LInsp LirBufWriter::insStorei(LInsp val, LInsp base, int32_t d) + LInsp LirBufWriter::insStore(LOpcode op, LInsp val, LInsp base, int32_t d) { - LOpcode op = val->isQuad() ? LIR_stqi : LIR_sti; base = insDisp(op, base, d); LInsSti* insSti = (LInsSti*)_buf->makeRoom(sizeof(LInsSti)); LIns* ins = insSti->getLIns(); @@ -859,6 +858,12 @@ namespace nanojit #endif } + LIns* LirWriter::insStorei(LIns* value, LIns* base, int32_t d) + { + LOpcode op = value->isQuad() ? LIR_stqi : LIR_sti; + return insStore(op, value, base, d); + } + LIns* LirWriter::qjoin(LInsp lo, LInsp hi) { return ins2(LIR_qjoin, lo, hi); @@ -1884,8 +1889,16 @@ namespace nanojit case LIR_ldc: case LIR_ldq: case LIR_ldqc: + case LIR_ldzb: + case LIR_ldzs: case LIR_ldcb: case LIR_ldcs: + case LIR_ldsb: + case LIR_ldss: + case LIR_ldcsb: + case LIR_ldcss: + case LIR_ld32f: + case LIR_ldc32f: VMPI_sprintf(s, "%s = %s %s[%d]", formatRef(i), lirNames[op], formatRef(i->oprnd1()), i->disp()); @@ -1893,6 +1906,9 @@ namespace nanojit case LIR_sti: case LIR_stqi: + case LIR_stb: + case LIR_sts: + case LIR_st32f: VMPI_sprintf(s, "%s %s[%d] = %s", lirNames[op], formatRef(i->oprnd2()), i->disp(), @@ -2203,10 +2219,10 @@ namespace nanojit exprs->clear(); } - LInsp LoadFilter::insStorei(LInsp v, LInsp b, int32_t d) + LInsp LoadFilter::insStore(LOpcode op, LInsp v, LInsp b, int32_t d) { clear(b); - return out->insStorei(v, b, d); + return out->insStore(op, v, b, d); } LInsp LoadFilter::insCall(const CallInfo *ci, LInsp args[]) diff --git a/js/src/nanojit/LIR.h b/js/src/nanojit/LIR.h index a6a4de9146a..8fa998f5b5d 100644 --- a/js/src/nanojit/LIR.h +++ b/js/src/nanojit/LIR.h @@ -462,13 +462,10 @@ namespace nanojit return (op & ~LIR64) == LIR_icall || op == LIR_qcall; } bool isStore() const { - LOpcode op = LOpcode(opcode() & ~LIR64); - return op == LIR_sti; + return isLInsSti(); } bool isLoad() const { - LOpcode op = opcode(); - return op == LIR_ldq || op == LIR_ld || op == LIR_ldc || - op == LIR_ldqc || op == LIR_ldcs || op == LIR_ldcb; + return isLInsLd(); } bool isGuard() const { LOpcode op = opcode(); @@ -1006,8 +1003,8 @@ namespace nanojit virtual LInsp insLoad(LOpcode op, LIns* base, int32_t d) { return out->insLoad(op, base, d); } - virtual LInsp insStorei(LIns* value, LIns* base, int32_t d) { - return out->insStorei(value, base, d); + virtual LInsp insStore(LOpcode op, LIns* value, LIns* base, int32_t d) { + return out->insStore(op, value, base, d); } // args[] is in reverse order, ie. args[0] holds the rightmost arg. virtual LInsp insCall(const CallInfo *call, LInsp args[]) { @@ -1039,6 +1036,8 @@ namespace nanojit // Sign or zero extend integers to native integers. On 32-bit this is a no-op. LIns* ins_i2p(LIns* intIns); LIns* ins_u2p(LIns* uintIns); + // choose LIR_sti or LIR_stqi based on size of value + LIns* insStorei(LIns* value, LIns* base, int32_t d); }; @@ -1192,8 +1191,8 @@ namespace nanojit LIns* insLoad(LOpcode v, LInsp base, int32_t disp) { return add(out->insLoad(v, base, disp)); } - LIns* insStorei(LInsp v, LInsp b, int32_t d) { - return add(out->insStorei(v, b, d)); + LIns* insStore(LOpcode op, LInsp v, LInsp b, int32_t d) { + return add(out->insStore(op, v, b, d)); } LIns* insAlloc(int32_t size) { return add(out->insAlloc(size)); @@ -1374,7 +1373,7 @@ namespace nanojit // LirWriter interface LInsp insLoad(LOpcode op, LInsp base, int32_t disp); - LInsp insStorei(LInsp o1, LInsp o2, int32_t disp); + LInsp insStore(LOpcode op, LInsp o1, LInsp o2, int32_t disp); LInsp ins0(LOpcode op); LInsp ins1(LOpcode op, LInsp o1); LInsp ins2(LOpcode op, LInsp o1, LInsp o2); @@ -1483,7 +1482,7 @@ namespace nanojit LInsp ins0(LOpcode); LInsp insLoad(LOpcode, LInsp base, int32_t disp); - LInsp insStorei(LInsp v, LInsp b, int32_t d); + LInsp insStore(LOpcode op, LInsp v, LInsp b, int32_t d); LInsp insCall(const CallInfo *call, LInsp args[]); }; diff --git a/js/src/nanojit/LIRopcode.tbl b/js/src/nanojit/LIRopcode.tbl index 52fbe1fba5c..9928d246f26 100644 --- a/js/src/nanojit/LIRopcode.tbl +++ b/js/src/nanojit/LIRopcode.tbl @@ -66,23 +66,23 @@ OPDEF(start, 0, Op0) // start of a fragment OPDEF(regfence, 1, Op0) // register fence, no register allocation is allowed across this meta instruction OPDEF(skip, 2, Sk) // holds blobs ("payloads") of data; also links pages -OPDEF(__3, 3, None) -OPDEF(__4, 4, None) -OPDEF(__5, 5, None) -OPDEF(__6, 6, None) /* non-pure operations */ -OPDEF(iaddp, 7, Op2) // integer addition for temporary pointer calculations (32bit only) -OPDEF(iparam, 8, P) // load a parameter (32bit register or stk location) -OPDEF(__9, 9, None) -OPDEF(ld, 10, Ld) // 32-bit load -OPDEF(ialloc, 11, I) // alloc some stack space (value is 32bit address) -OPDEF(sti, 12, Sti) // 32-bit store -OPDEF(ret, 13, Op1) // return a word-sized value -OPDEF(live, 14, Op1) // extend live range of reference -OPDEF(flive, 15, Op1) // extend live range of a floating point value reference -OPDEF(icall, 16, C) // subroutine call returning a 32-bit value -OPDEF(__17, 17, None) +OPDEF(ldsb, 3, Ld) // 8-bit integer load, sign-extend to 32-bit +OPDEF(ldss, 4, Ld) // 16-bit integer load, sign-extend to 32-bit +OPDEF(ldzb, 5, Ld) // 8-bit integer load, zero extend to 32-bit +OPDEF(ldzs, 6, Ld) // 16-bit integer load, zero extend to 32-bit +OPDEF(iaddp, 7, Op2) // integer addition for temporary pointer calculations (32bit only) +OPDEF(iparam, 8, P) // load a parameter (32bit register or stk location) +OPDEF(stb, 9, Sti) // 8-bit integer store +OPDEF(ld, 10, Ld) // 32-bit integer load +OPDEF(ialloc, 11, I) // alloc some stack space (value is 32bit address) +OPDEF(sti, 12, Sti) // 32-bit integer store +OPDEF(ret, 13, Op1) // return a word-sized value +OPDEF(live, 14, Op1) // extend live range of reference +OPDEF(flive, 15, Op1) // extend live range of a floating point value reference +OPDEF(icall, 16, C) // subroutine call returning a 32-bit value +OPDEF(sts, 17, Sti) // 16-bit integer store /* guards */ OPDEF(x, 18, Op2) // exit always @@ -120,9 +120,9 @@ OPDEF(fgt, 29, Op2) // floating-point greater-than OPDEF(fle, 30, Op2) // floating-point less-than-or-equal OPDEF(fge, 31, Op2) // floating-point greater-than-or-equal -OPDEF(ldcb, 32, Ld) // non-volatile 8-bit load -OPDEF(ldcs, 33, Ld) // non-volatile 16-bit load -OPDEF(ldc, 34, Ld) // non-volatile 32-bit load +OPDEF(ldcb, 32, Ld) // non-volatile 8-bit integer load, zero-extend to 32-bit +OPDEF(ldcs, 33, Ld) // non-volatile 16-bit integer load, zero-extend to 32-bit +OPDEF(ldc, 34, Ld) // non-volatile 32-bit integer load OPDEF(neg, 35, Op1) // integer negation OPDEF(add, 36, Op2) // integer addition @@ -147,8 +147,8 @@ OPDEF(xf, 49, Op2) // exit if false (0x31 0011 0001) OPDEF(qlo, 50, Op1) // get the low 32 bits of a 64-bit value OPDEF(qhi, 51, Op1) // get the high 32 bits of a 64-bit value -OPDEF(__52, 52, None) -OPDEF(__53, 53, None) +OPDEF(ldcsb, 52, Ld) // non-volatile 8-bit integer load (sign-extend to 32-bit) +OPDEF(ldcss, 53, Ld) // non-volatile 16-bit integer load (sign-extend to 32-bit) // This must be right before LIR_eq, so (op&~LIR64 - LIR_ov) can be indexed // into a convenient table. @@ -191,8 +191,8 @@ OPD64(qalloc, LIR_ialloc, I) // allocate some stack space (value is 64bit add OPD64(stqi, LIR_sti, Sti) // 64-bit (quad) store OPD64(fret, LIR_ret, Op1) -OPD64(__14_64, 14, None) -OPD64(__15_64, 15, None) +OPD64(st32f, 14, Sti) // store 64-bit float as a 32-bit float (dropping precision) +OPD64(ld32f, 15, Ld) // load 32-bit float and widen to 64-bit float OPD64(fcall, LIR_icall, C) // subroutine call returning 64-bit (quad) double value OPD64(qcall, 17, C) // subroutine call returning 64-bit (quad) integer value @@ -239,7 +239,7 @@ OPD64(qirsh, 46, Op2) // 64-bit signed right shift OPD64(qursh, 47, Op2) // 64-bit unsigned right shift OPD64(qiadd, 48, Op2) // 64-bit bitwise ADD -OPD64(__49_64, 49, None) +OPD64(ldc32f, 49, Ld) // non-volatile load 32-bit float and widen to 64-bit float OPD64(qjoin, 50, Op2) // join two 32-bit values (1st arg is low bits, 2nd is high) OPD64(__51_64, 51, None) OPD64(__52_64, 52, None) diff --git a/js/src/nanojit/Native.h b/js/src/nanojit/Native.h index 8085c076b6c..d6268a36bdb 100644 --- a/js/src/nanojit/Native.h +++ b/js/src/nanojit/Native.h @@ -125,6 +125,10 @@ namespace nanojit { # define NJ_JTBL_SUPPORTED 0 #endif +#ifndef NJ_EXPANDED_LOADSTORE_SUPPORTED +# define NJ_EXPANDED_LOADSTORE_SUPPORTED 0 +#endif + namespace nanojit { inline Register nextreg(Register r) { diff --git a/js/src/nanojit/NativeARM.cpp b/js/src/nanojit/NativeARM.cpp index dc9f43f1e5a..56568fbebaa 100644 --- a/js/src/nanojit/NativeARM.cpp +++ b/js/src/nanojit/NativeARM.cpp @@ -1172,8 +1172,21 @@ Assembler::asm_qjoin(LIns *ins) } void -Assembler::asm_store32(LIns *value, int dr, LIns *base) +Assembler::asm_store32(LOpcode op, LIns *value, int dr, LIns *base) { + switch (op) { + case LIR_sti: + // handled by mainline code below for now + break; + case LIR_stb: + case LIR_sts: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode"); + return; + } + Register ra, rb; if (base->isop(LIR_alloc)) { rb = FP; @@ -1268,6 +1281,20 @@ Assembler::asm_load64(LInsp ins) { //asm_output("<<< load64"); + switch (ins->opcode()) { + case LIR_ldq: + case LIR_ldqc: + // handled by mainline code below for now + break; + case LIR_ld32f: + case LIR_ldc32f: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_load64 should never receive this LIR opcode"); + return; + } + NanoAssert(ins->isQuad()); LIns* base = ins->oprnd1(); @@ -1310,10 +1337,22 @@ Assembler::asm_load64(LInsp ins) } void -Assembler::asm_store64(LInsp value, int dr, LInsp base) +Assembler::asm_store64(LOpcode op, LInsp value, int dr, LInsp base) { //asm_output("<<< store64 (dr: %d)", dr); + switch (op) { + case LIR_stqi: + // handled by mainline code below for now + break; + case LIR_st32f: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_store64 should never receive this LIR opcode"); + return; + } + if (ARM_VFP) { Register rb = findRegFor(base, GpRegs); @@ -2440,7 +2479,7 @@ Assembler::asm_neg_not(LInsp ins) } void -Assembler::asm_ld(LInsp ins) +Assembler::asm_load32(LInsp ins) { LOpcode op = ins->opcode(); LIns* base = ins->oprnd1(); @@ -2449,25 +2488,31 @@ Assembler::asm_ld(LInsp ins) Register rr = prepResultReg(ins, GpRegs); Register ra = getBaseReg(op, base, d, GpRegs); - // these will always be 4-byte aligned - if (op == LIR_ld || op == LIR_ldc) { - LDR(rr, ra, d); - return; + switch(op) { + case LIR_ldzb: + case LIR_ldcb: + LDRB(rr, ra, d); + return; + case LIR_ldzs: + case LIR_ldcs: + // these are expected to be 2 or 4-byte aligned + LDRH(rr, ra, d); + return; + case LIR_ld: + case LIR_ldc: + // these are expected to be 4-byte aligned + LDR(rr, ra, d); + return; + case LIR_ldsb: + case LIR_ldss: + case LIR_ldcsb: + case LIR_ldcss: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); + return; } - - // these will be 2 or 4-byte aligned - if (op == LIR_ldcs) { - LDRH(rr, ra, d); - return; - } - - // aaand this is just any byte. - if (op == LIR_ldcb) { - LDRB(rr, ra, d); - return; - } - - NanoAssertMsg(0, "Unsupported instruction in asm_ld"); } void diff --git a/js/src/nanojit/NativeARM.h b/js/src/nanojit/NativeARM.h index c00083a912a..a53f12afe32 100644 --- a/js/src/nanojit/NativeARM.h +++ b/js/src/nanojit/NativeARM.h @@ -79,6 +79,7 @@ namespace nanojit #define NJ_MAX_PARAMETERS 16 #define NJ_ALIGN_STACK 8 #define NJ_JTBL_SUPPORTED 1 +#define NJ_EXPANDED_LOADSTORE_SUPPORTED 0 #define NJ_CONSTANT_POOLS const int NJ_MAX_CPOOL_OFFSET = 4096; diff --git a/js/src/nanojit/NativePPC.cpp b/js/src/nanojit/NativePPC.cpp index 0d8340c58c3..9c17de475e8 100644 --- a/js/src/nanojit/NativePPC.cpp +++ b/js/src/nanojit/NativePPC.cpp @@ -140,30 +140,69 @@ namespace nanojit freeRsrcOf(ins, false); // if we had a reg in use, emit a ST to flush it to mem } - void Assembler::asm_ld(LIns *ins) { + void Assembler::asm_load32(LIns *ins) { LIns* base = ins->oprnd1(); int d = ins->disp(); Register rr = prepResultReg(ins, GpRegs); Register ra = getBaseReg(ins->opcode(), base, d, GpRegs); - #if !PEDANTIC - if (isS16(d)) { - if (ins->isop(LIR_ldcb)) { - LBZ(rr, d, ra); - } else { - LWZ(rr, d, ra); - } - return; + switch(ins->opcode()) { + case LIR_ldzb: + case LIR_ldcb: + if (isS16(d)) { + LBZ(rr, d, ra); + } else { + LBZX(rr, ra, R0); // rr = [ra+R0] + asm_li(R0,d); + } + return; + case LIR_ldzs: + case LIR_ldcs: + // these are expected to be 2 or 4-byte aligned + if (isS16(d)) { + LHZ(rr, d, ra); + } else { + LHZX(rr, ra, R0); // rr = [ra+R0] + asm_li(R0,d); + } + return; + case LIR_ld: + case LIR_ldc: + // these are expected to be 4-byte aligned + if (isS16(d)) { + LWZ(rr, d, ra); + } else { + LWZX(rr, ra, R0); // rr = [ra+R0] + asm_li(R0,d); + } + return; + case LIR_ldsb: + case LIR_ldss: + case LIR_ldcsb: + case LIR_ldcss: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); + return; } - #endif - - // general case - underrunProtect(12); - LWZX(rr, ra, R0); // rr = [ra+R0] - asm_li(R0,d); } - void Assembler::asm_store32(LIns *value, int32_t dr, LIns *base) { + void Assembler::asm_store32(LOpcode op, LIns *value, int32_t dr, LIns *base) { + + switch (op) { + case LIR_sti: + // handled by mainline code below for now + break; + case LIR_stb: + case LIR_sts: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode"); + return; + } + Register rs = findRegFor(value, GpRegs); Register ra = value == base ? rs : getBaseReg(LIR_sti, base, dr, GpRegs & ~rmask(rs)); @@ -180,6 +219,21 @@ namespace nanojit } void Assembler::asm_load64(LIns *ins) { + + switch (ins->opcode()) { + case LIR_ldq: + case LIR_ldqc: + // handled by mainline code below for now + break; + case LIR_ld32f: + case LIR_ldc32f: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_load64 should never receive this LIR opcode"); + return; + } + LIns* base = ins->oprnd1(); #ifdef NANOJIT_64BIT Register rr = ins->getReg(); @@ -256,8 +310,21 @@ namespace nanojit asm_li32(r, int32_t(imm>>32)); // r[0:31] = imm[32:63] } - void Assembler::asm_store64(LIns *value, int32_t dr, LIns *base) { + void Assembler::asm_store64(LOpcode op, LIns *value, int32_t dr, LIns *base) { NanoAssert(value->isQuad()); + + switch (op) { + case LIR_stqi: + // handled by mainline code below for now + break; + case LIR_st32f: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_store64 should never receive this LIR opcode"); + return; + } + Register ra = getBaseReg(LIR_stqi, base, dr, GpRegs); #if !PEDANTIC && !defined NANOJIT_64BIT diff --git a/js/src/nanojit/NativePPC.h b/js/src/nanojit/NativePPC.h index f34736547d3..1225c5bad23 100644 --- a/js/src/nanojit/NativePPC.h +++ b/js/src/nanojit/NativePPC.h @@ -57,6 +57,7 @@ namespace nanojit #define NJ_MAX_STACK_ENTRY 256 #define NJ_ALIGN_STACK 16 #define NJ_JTBL_SUPPORTED 1 +#define NJ_EXPANDED_LOADSTORE_SUPPORTED 0 enum ConditionRegister { CR0 = 0, @@ -191,10 +192,13 @@ namespace nanojit PPC_fneg = 0xFC000050, // floating negate PPC_fsub = 0xFC000028, // floating subtract (double precision) PPC_lbz = 0x88000000, // load byte and zero + PPC_lbzx = 0x7C0000AE, // load byte and zero indexed PPC_ld = 0xE8000000, // load doubleword PPC_ldx = 0x7C00002A, // load doubleword indexed PPC_lfd = 0xC8000000, // load floating point double PPC_lfdx = 0x7C0004AE, // load floating-point double indexed + PPC_lhz = 0xA0000000, // load halfword and zero + PPC_lhzx = 0x7C00022E, // load halfword and zero indexed PPC_lwz = 0x80000000, // load word and zero PPC_lwzx = 0x7C00002E, // load word and zero indexed PPC_mfcr = 0x7C000026, // move from condition register @@ -448,8 +452,11 @@ namespace nanojit "%s %s,%s,%s", #op, gpn(rs), gpn(ra), gpn(rb)) #define LBZ(r, d, b) MEMd(lbz, r, d, b) + #define LHZ(r, d, b) MEMd(lhz, r, d, b) #define LWZ(r, d, b) MEMd(lwz, r, d, b) #define LD(r, d, b) MEMd(ld, r, d, b) + #define LBZX(r, a, b) MEMx(lbzx, r, a, b) + #define LHZX(r, a, b) MEMx(lhzx, r, a, b) #define LWZX(r, a, b) MEMx(lwzx, r, a, b) #define LDX(r, a, b) MEMx(ldx, r, a, b) diff --git a/js/src/nanojit/NativeSparc.cpp b/js/src/nanojit/NativeSparc.cpp index 63768e4f3cf..81c0fa0f731 100644 --- a/js/src/nanojit/NativeSparc.cpp +++ b/js/src/nanojit/NativeSparc.cpp @@ -299,8 +299,21 @@ namespace nanojit } } - void Assembler::asm_store32(LIns *value, int dr, LIns *base) + void Assembler::asm_store32(LOpcode op, LIns *value, int dr, LIns *base) { + switch (op) { + case LIR_sti: + // handled by mainline code below for now + break; + case LIR_stb: + case LIR_sts: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode"); + return; + } + underrunProtect(20); if (value->isconst()) { @@ -344,6 +357,20 @@ namespace nanojit void Assembler::asm_load64(LInsp ins) { + switch (ins->opcode()) { + case LIR_ldq: + case LIR_ldqc: + // handled by mainline code below for now + break; + case LIR_ld32f: + case LIR_ldc32f: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_load64 should never receive this LIR opcode"); + return; + } + underrunProtect(72); LIns* base = ins->oprnd1(); int db = ins->disp(); @@ -373,8 +400,20 @@ namespace nanojit } } - void Assembler::asm_store64(LInsp value, int dr, LInsp base) + void Assembler::asm_store64(LOpcode op, LInsp value, int dr, LInsp base) { + switch (op) { + case LIR_stqi: + // handled by mainline code below for now + break; + case LIR_st32f: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_store64 should never receive this LIR opcode"); + return; + } + underrunProtect(48); if (value->isconstq()) { @@ -716,7 +755,7 @@ namespace nanojit ORI(ra, 0, rr); } - void Assembler::asm_ld(LInsp ins) + void Assembler::asm_load32(LInsp ins) { underrunProtect(12); LOpcode op = ins->opcode(); @@ -724,12 +763,28 @@ namespace nanojit int d = ins->disp(); Register rr = prepResultReg(ins, GpRegs); Register ra = getBaseReg(ins->opcode(), base, d, GpRegs); - if (op == LIR_ldcb) { - LDUB32(ra, d, rr); - } else if (op == LIR_ldcs) { - LDUH32(ra, d, rr); - } else { - LDSW32(ra, d, rr); + switch(op) { + case LIR_ldzb: + case LIR_ldcb: + LDUB32(ra, d, rr); + break; + case LIR_ldzs: + case LIR_ldcs: + LDUH32(ra, d, rr); + break; + case LIR_ld: + case LIR_ldc: + LDSW32(ra, d, rr); + break; + case LIR_ldsb: + case LIR_ldss: + case LIR_ldcsb: + case LIR_ldcss: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); + return; } } diff --git a/js/src/nanojit/NativeSparc.h b/js/src/nanojit/NativeSparc.h index fe0c3d97664..d12c6a6993b 100644 --- a/js/src/nanojit/NativeSparc.h +++ b/js/src/nanojit/NativeSparc.h @@ -71,8 +71,10 @@ namespace nanojit const int LARGEST_UNDERRUN_PROT = 32; // largest value passed to underrunProtect -#define NJ_MAX_STACK_ENTRY 256 -#define NJ_MAX_PARAMETERS 1 +#define NJ_MAX_STACK_ENTRY 256 +#define NJ_MAX_PARAMETERS 1 +#define NJ_JTBL_SUPPORTED 0 +#define NJ_EXPANDED_LOADSTORE_SUPPORTED 0 const int NJ_ALIGN_STACK = 16; diff --git a/js/src/nanojit/NativeX64.cpp b/js/src/nanojit/NativeX64.cpp index 40c476599c6..c32428b68e1 100644 --- a/js/src/nanojit/NativeX64.cpp +++ b/js/src/nanojit/NativeX64.cpp @@ -1347,6 +1347,21 @@ namespace nanojit } void Assembler::asm_load64(LIns *ins) { + + switch (ins->opcode()) { + case LIR_ldq: + case LIR_ldqc: + // handled by mainline code below for now + break; + case LIR_ld32f: + case LIR_ldc32f: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_load64 should never receive this LIR opcode"); + return; + } + Register rr, rb; int32_t dr; regalloc_load(ins, rr, dr, rb); @@ -1359,21 +1374,52 @@ namespace nanojit } } - void Assembler::asm_ld(LIns *ins) { + void Assembler::asm_load32(LIns *ins) { NanoAssert(!ins->isQuad()); Register r, b; int32_t d; regalloc_load(ins, r, d, b); LOpcode op = ins->opcode(); - switch (op) { - case LIR_ldcb: MOVZX8M( r, d, b); break; - case LIR_ldcs: MOVZX16M(r, d, b); break; - default: MOVLRM( r, d, b); break; + switch(op) { + case LIR_ldzb: + case LIR_ldcb: + MOVZX8M( r, d, b); + break; + case LIR_ldzs: + case LIR_ldcs: + MOVZX16M(r, d, b); + break; + case LIR_ld: + case LIR_ldc: + MOVLRM( r, d, b); + break; + case LIR_ldsb: + case LIR_ldss: + case LIR_ldcsb: + case LIR_ldcss: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); + return; } } - void Assembler::asm_store64(LIns *value, int d, LIns *base) { + void Assembler::asm_store64(LOpcode op, LIns *value, int d, LIns *base) { NanoAssert(value->isQuad()); + + switch (op) { + case LIR_stqi: + // handled by mainline code below for now + break; + case LIR_st32f: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_store64 should never receive this LIR opcode"); + return; + } + Register b = getBaseReg(LIR_stqi, base, d, BaseRegs); // if we have to choose a register, use a GPR, but not the base reg @@ -1401,7 +1447,21 @@ namespace nanojit } } - void Assembler::asm_store32(LIns *value, int d, LIns *base) { + void Assembler::asm_store32(LOpcode op, LIns *value, int d, LIns *base) { + + switch (op) { + case LIR_sti: + // handled by mainline code below for now + break; + case LIR_stb: + case LIR_sts: + NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); + return; + default: + NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode"); + return; + } + NanoAssert(!value->isQuad()); Register b = getBaseReg(LIR_sti, base, d, BaseRegs); Register r = findRegFor(value, GpRegs & ~rmask(b)); diff --git a/js/src/nanojit/NativeX64.h b/js/src/nanojit/NativeX64.h index 66e488e3447..b2e10a3de1b 100644 --- a/js/src/nanojit/NativeX64.h +++ b/js/src/nanojit/NativeX64.h @@ -61,6 +61,7 @@ namespace nanojit #define NJ_MAX_STACK_ENTRY 256 #define NJ_ALIGN_STACK 16 #define NJ_JTBL_SUPPORTED 1 +#define NJ_EXPANDED_LOADSTORE_SUPPORTED 0 enum Register { RAX = 0, // 1st int return, # of sse varargs diff --git a/js/src/nanojit/Nativei386.cpp b/js/src/nanojit/Nativei386.cpp index c595423d13f..f6fa5b72555 100644 --- a/js/src/nanojit/Nativei386.cpp +++ b/js/src/nanojit/Nativei386.cpp @@ -464,31 +464,62 @@ namespace nanojit } } - void Assembler::asm_store32(LIns *value, int dr, LIns *base) + void Assembler::asm_store32(LOpcode op, LIns* value, int dr, LIns* base) { if (value->isconst()) { Register rb = getBaseReg(LIR_sti, base, dr, GpRegs); int c = value->imm32(); - STi(rb, dr, c); + switch(op) { + case LIR_stb: + ST8i(rb, dr, c); + break; + case LIR_sts: + ST16i(rb, dr, c); + break; + case LIR_sti: + STi(rb, dr, c); + break; + default: + NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode"); + break; + } } else { + // quirk of x86-32: reg must be a/b/c/d for single-byte stores + const RegisterMask SrcRegs = (op == LIR_stb) ? + (1<isop(LIR_alloc)) { rb = FP; dr += findMemFor(base); - ra = findRegFor(value, GpRegs); + ra = findRegFor(value, SrcRegs); } else if (base->isconst()) { // absolute address dr += base->imm32(); - ra = findRegFor(value, GpRegs); + ra = findRegFor(value, SrcRegs); rb = UnknownReg; } else { - findRegFor2(GpRegs, value, ra, base, rb); + findRegFor2(SrcRegs, value, ra, base, rb); + } + switch(op) { + case LIR_stb: + ST8(rb, dr, ra); + break; + case LIR_sts: + ST16(rb, dr, ra); + break; + case LIR_sti: + ST(rb, dr, ra); + break; + default: + NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode"); + break; } - ST(rb, dr, ra); } } @@ -528,10 +559,25 @@ namespace nanojit { freeRsrcOf(ins, false); Register rb = getBaseReg(ins->opcode(), base, db, GpRegs); - SSE_LDQ(rr, db, rb); + switch (ins->opcode()) { + case LIR_ldq: + case LIR_ldqc: + SSE_LDQ(rr, db, rb); + break; + case LIR_ld32f: + case LIR_ldc32f: + SSE_CVTSS2SD(rr, rr); + SSE_LDSS(rr, db, rb); + SSE_XORPDr(rr,rr); + break; + default: + NanoAssertMsg(0, "asm_load64 should never receive this LIR opcode"); + break; + } } else { + int dr = disp(ins); Register rb; if (base->isop(LIR_alloc)) { @@ -542,23 +588,79 @@ namespace nanojit } ins->setReg(UnknownReg); - // don't use an fpu reg to simply load & store the value. - if (dr) - asm_mmq(FP, dr, rb, db); - - freeRsrcOf(ins, false); - - if (isKnownReg(rr)) - { - NanoAssert(rmask(rr)&FpRegs); - _allocator.retire(rr); - FLDQ(db, rb); + switch (ins->opcode()) { + case LIR_ldq: + case LIR_ldqc: + // don't use an fpu reg to simply load & store the value. + if (dr) + asm_mmq(FP, dr, rb, db); + freeRsrcOf(ins, false); + if (isKnownReg(rr)) + { + NanoAssert(rmask(rr)&x87Regs); + _allocator.retire(rr); + FLDQ(db, rb); + } + break; + case LIR_ld32f: + case LIR_ldc32f: + freeRsrcOf(ins, false); + if (isKnownReg(rr)) + { + NanoAssert(rmask(rr)&x87Regs); + _allocator.retire(rr); + FLD32(db, rb); + } + else + { + // need to use fpu to expand 32->64 + NanoAssert(dr != 0); + FSTPQ(dr, FP); + FLD32(db, rb); + } + break; + default: + NanoAssertMsg(0, "asm_load64 should never receive this LIR opcode"); + break; } } } - void Assembler::asm_store64(LInsp value, int dr, LInsp base) + void Assembler::asm_store64(LOpcode op, LInsp value, int dr, LInsp base) { + if (op == LIR_st32f) + { + Register rb; + if (base->isop(LIR_alloc)) { + rb = FP; + dr += findMemFor(base); + } else { + rb = findRegFor(base, GpRegs); + } + + // if value already in a reg, use that, otherwise + // try to get it into XMM regs before FPU regs. + bool pop = value->isUnusedOrHasUnknownReg(); + Register rv = findRegFor(value, config.sse2 ? XmmRegs : FpRegs); + if (rmask(rv) & XmmRegs) + { + // need a scratch reg + Register t = registerAllocTmp(XmmRegs); + + // cvt to single-precision and store + SSE_STSS(dr, rb, t); + SSE_CVTSD2SS(t, rv); + SSE_XORPDr(t,t); // zero dest to ensure no dependency stalls + } + else + { + FST32(pop?1:0, dr, rb); + } + return; + } + + NanoAssertMsg(op == LIR_stqi, "asm_store64 should never receive this LIR opcode"); + if (value->isconstq()) { // if a constant 64-bit value just store it now rather than @@ -626,9 +728,12 @@ namespace nanojit ? findRegFor(value, config.sse2 ? XmmRegs : FpRegs) : value->getReg() ); - if (rmask(rv) & XmmRegs) { + if (rmask(rv) & XmmRegs) + { SSE_STQ(dr, rb, rv); - } else { + } + else + { FSTQ(pop?1:0, dr, rb); } } @@ -1044,7 +1149,7 @@ namespace nanojit MR(rr,ra); } - void Assembler::asm_ld(LInsp ins) + void Assembler::asm_load32(LInsp ins) { LOpcode op = ins->opcode(); LIns* base = ins->oprnd1(); @@ -1054,13 +1159,31 @@ namespace nanojit if (base->isconst()) { intptr_t addr = base->imm32(); addr += d; - if (op == LIR_ldcb) - LD8Zdm(rr, addr); - else if (op == LIR_ldcs) - LD16Zdm(rr, addr); - else - LDdm(rr, addr); - return; + switch(op) { + case LIR_ldzb: + case LIR_ldcb: + LD8Zdm(rr, addr); + return; + case LIR_ldsb: + case LIR_ldcsb: + LD8Sdm(rr, addr); + return; + case LIR_ldzs: + case LIR_ldcs: + LD16Zdm(rr, addr); + return; + case LIR_ldss: + case LIR_ldcss: + LD16Sdm(rr, addr); + return; + case LIR_ld: + case LIR_ldc: + LDdm(rr, addr); + return; + default: + NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); + return; + } } /* Search for add(X,Y) */ @@ -1092,23 +1215,59 @@ namespace nanojit ? findSpecificRegForUnallocated(rhs, rr) : findRegFor(rhs, GpRegs & ~(rmask(rleft))) ); - if (op == LIR_ldcb) - LD8Zsib(rr, d, rleft, rright, scale); - else if (op == LIR_ldcs) - LD16Zsib(rr, d, rleft, rright, scale); - else - LDsib(rr, d, rleft, rright, scale); - - return; + switch(op) { + case LIR_ldzb: + case LIR_ldcb: + LD8Zsib(rr, d, rleft, rright, scale); + return; + case LIR_ldsb: + case LIR_ldcsb: + LD8Ssib(rr, d, rleft, rright, scale); + return; + case LIR_ldzs: + case LIR_ldcs: + LD16Zsib(rr, d, rleft, rright, scale); + return; + case LIR_ldss: + case LIR_ldcss: + LD16Ssib(rr, d, rleft, rright, scale); + return; + case LIR_ld: + case LIR_ldc: + LDsib(rr, d, rleft, rright, scale); + return; + default: + NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); + return; + } } Register ra = getBaseReg(op, base, d, GpRegs); - if (op == LIR_ldcb) - LD8Z(rr, d, ra); - else if (op == LIR_ldcs) - LD16Z(rr, d, ra); - else - LD(rr, d, ra); + switch(op) { + case LIR_ldzb: + case LIR_ldcb: + LD8Z(rr, d, ra); + return; + case LIR_ldsb: + case LIR_ldcsb: + LD8S(rr, d, ra); + return; + case LIR_ldzs: + case LIR_ldcs: + LD16Z(rr, d, ra); + return; + case LIR_ldss: + case LIR_ldcss: + LD16S(rr, d, ra); + return; + case LIR_ld: + case LIR_ldc: + LD(rr, d, ra); + return; + default: + NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); + return; + } } void Assembler::asm_cmov(LInsp ins) diff --git a/js/src/nanojit/Nativei386.h b/js/src/nanojit/Nativei386.h index 759f2eabbe9..f9c18eeeb9d 100644 --- a/js/src/nanojit/Nativei386.h +++ b/js/src/nanojit/Nativei386.h @@ -96,6 +96,7 @@ namespace nanojit #define NJ_MAX_STACK_ENTRY 256 #define NJ_MAX_PARAMETERS 1 #define NJ_JTBL_SUPPORTED 1 + #define NJ_EXPANDED_LOADSTORE_SUPPORTED 1 // Preserve a 16-byte stack alignment, to support the use of // SSE instructions like MOVDQA (if not by Tamarin itself, @@ -186,14 +187,24 @@ namespace nanojit void asm_fcmp(LIns *cond);\ NIns* asm_fbranch(bool, LIns*, NIns*);\ void asm_cmp(LIns *cond); \ - void asm_div_mod(LIns *cond); + void asm_div_mod(LIns *cond); \ + void asm_load(int d, Register r); + #define IMM8(i) \ + _nIns -= 1; \ + *((int8_t*)_nIns) = (int8_t)(i) + + #define IMM16(i) \ + _nIns -= 2; \ + *((int16_t*)_nIns) = (int16_t)(i) + #define IMM32(i) \ _nIns -= 4; \ *((int32_t*)_nIns) = (int32_t)(i) // XXX rearrange NanoAssert() expression to workaround apparent gcc 4.3 bug: // XXX "error: logical && with non-zero constant will always evaluate as true" +// underrunProtect(6) is necessary for worst-case #define MODRMs(r,d,b,l,i) \ NanoAssert(unsigned(i)<8 && unsigned(b)<8 && unsigned(r)<8); \ if ((d) == 0 && (b) != EBP) { \ @@ -211,6 +222,7 @@ namespace nanojit *(--_nIns) = (uint8_t) ( 2<<6 | (r)<<3 | 4 ); \ } +// underrunProtect(6) is necessary for worst-case #define MODRMm(r,d,b) \ NanoAssert(unsigned(r)<8 && ((b)==UnknownReg || unsigned(b)<8)); \ if ((b) == UnknownReg) {\ @@ -441,37 +453,66 @@ namespace nanojit asm_output("mov %s,%d(%s+%s*%c)",gpn(reg),disp,gpn(base),gpn(index),SIBIDX(scale)); \ } while (0) +// note: movzx/movsx are being output with an 8/16 suffix to indicate the size +// being loaded. this doesn't really match standard intel format (though is arguably +// terser and more obvious in this case) and would probably be nice to fix. +// (likewise, the 8/16 bit stores being output as "mov8" and "mov16" respectively.) + // load 16-bit, sign extend -#define LD16S(r,d,b) do { count_ld(); ALU2m(0x0fbf,r,d,b); asm_output("movsx %s,%d(%s)", gpn(r),d,gpn(b)); } while(0) +#define LD16S(r,d,b) do { count_ld(); ALU2m(0x0fbf,r,d,b); asm_output("movsx16 %s,%d(%s)", gpn(r),d,gpn(b)); } while(0) + +#define LD16Sdm(r,addr) do { count_ld(); ALU2dm(0x0fbf,r,addr); asm_output("movsx16 %s,0(%lx)", gpn(r),(unsigned long)addr); } while (0) + +#define LD16Ssib(r,disp,base,index,scale) do { \ + count_ld(); \ + ALU2sib(0x0fbf,r,base,index,scale,disp); \ + asm_output("movsx16 %s,%d(%s+%s*%c)",gpn(r),disp,gpn(base),gpn(index),SIBIDX(scale)); \ + } while (0) // load 16-bit, zero extend -#define LD16Z(r,d,b) do { count_ld(); ALU2m(0x0fb7,r,d,b); asm_output("movsz %s,%d(%s)", gpn(r),d,gpn(b)); } while(0) +#define LD16Z(r,d,b) do { count_ld(); ALU2m(0x0fb7,r,d,b); asm_output("movzx16 %s,%d(%s)", gpn(r),d,gpn(b)); } while(0) -#define LD16Zdm(r,addr) do { count_ld(); ALU2dm(0x0fb7,r,addr); asm_output("movsz %s,0(%lx)", gpn(r),(unsigned long)addr); } while (0) +#define LD16Zdm(r,addr) do { count_ld(); ALU2dm(0x0fb7,r,addr); asm_output("movzx16 %s,0(%lx)", gpn(r),(unsigned long)addr); } while (0) #define LD16Zsib(r,disp,base,index,scale) do { \ count_ld(); \ ALU2sib(0x0fb7,r,base,index,scale,disp); \ - asm_output("movsz %s,%d(%s+%s*%c)",gpn(r),disp,gpn(base),gpn(index),SIBIDX(scale)); \ + asm_output("movzx16 %s,%d(%s+%s*%c)",gpn(r),disp,gpn(base),gpn(index),SIBIDX(scale)); \ } while (0) // load 8-bit, zero extend -#define LD8Z(r,d,b) do { count_ld(); ALU2m(0x0fb6,r,d,b); asm_output("movzx %s,%d(%s)", gpn(r),d,gpn(b)); } while(0) +#define LD8Z(r,d,b) do { count_ld(); ALU2m(0x0fb6,r,d,b); asm_output("movzx8 %s,%d(%s)", gpn(r),d,gpn(b)); } while(0) #define LD8Zdm(r,addr) do { \ count_ld(); \ NanoAssert((d)>=0&&(d)<=31); \ ALU2dm(0x0fb6,r,addr); \ - asm_output("movzx %s,0(%lx)", gpn(r),(long unsigned)addr); \ + asm_output("movzx8 %s,0(%lx)", gpn(r),(long unsigned)addr); \ } while(0) #define LD8Zsib(r,disp,base,index,scale) do { \ count_ld(); \ NanoAssert((d)>=0&&(d)<=31); \ ALU2sib(0x0fb6,r,base,index,scale,disp); \ - asm_output("movzx %s,%d(%s+%s*%c)",gpn(r),disp,gpn(base),gpn(index),SIBIDX(scale)); \ + asm_output("movzx8 %s,%d(%s+%s*%c)",gpn(r),disp,gpn(base),gpn(index),SIBIDX(scale)); \ } while(0) +// load 8-bit, sign extend +#define LD8S(r,d,b) do { count_ld(); ALU2m(0x0fbe,r,d,b); asm_output("movsx8 %s,%d(%s)", gpn(r),d,gpn(b)); } while(0) + +#define LD8Sdm(r,addr) do { \ + count_ld(); \ + NanoAssert((d)>=0&&(d)<=31); \ + ALU2dm(0x0fbe,r,addr); \ + asm_output("movsx8 %s,0(%lx)", gpn(r),(long unsigned)addr); \ + } while(0) + +#define LD8Ssib(r,disp,base,index,scale) do { \ + count_ld(); \ + NanoAssert((d)>=0&&(d)<=31); \ + ALU2sib(0x0fbe,r,base,index,scale,disp); \ + asm_output("movsx8 %s,%d(%s+%s*%c)",gpn(r),disp,gpn(base),gpn(index),SIBIDX(scale)); \ + } while(0) #define LDi(r,i) do { \ count_ld();\ @@ -481,14 +522,43 @@ namespace nanojit *(--_nIns) = (uint8_t) (0xb8 | (r) ); \ asm_output("mov %s,%d",gpn(r),i); } while(0) +// quirk of x86-32: reg must be a/b/c/d for byte stores here +#define ST8(base,disp,reg) do { \ + count_st();\ + NanoAssert(((unsigned)reg)<4); \ + ALUm(0x88,reg,disp,base); \ + asm_output("mov8 %d(%s),%s",disp,base==UnknownReg?"0":gpn(base),gpn(reg)); } while(0) + +#define ST16(base,disp,reg) do { \ + count_st();\ + ALUm16(0x89,reg,disp,base); \ + asm_output("mov16 %d(%s),%s",disp,base==UnknownReg?"0":gpn(base),gpn(reg)); } while(0) + #define ST(base,disp,reg) do { \ count_st();\ ALUm(0x89,reg,disp,base); \ asm_output("mov %d(%s),%s",disp,base==UnknownReg?"0":gpn(base),gpn(reg)); } while(0) +#define ST8i(base,disp,imm) do { \ + count_st();\ + underrunProtect(8); \ + IMM8(imm); \ + MODRMm(0, disp, base); \ + *(--_nIns) = 0xc6; \ + asm_output("mov8 %d(%s),%d",disp,gpn(base),imm); } while(0) + +#define ST16i(base,disp,imm) do { \ + count_st();\ + underrunProtect(10); \ + IMM16(imm); \ + MODRMm(0, disp, base); \ + *(--_nIns) = 0xc7; \ + *(--_nIns) = 0x66; \ + asm_output("mov16 %d(%s),%d",disp,gpn(base),imm); } while(0) + #define STi(base,disp,imm) do { \ count_st();\ - underrunProtect(12); \ + underrunProtect(11); \ IMM32(imm); \ MODRMm(0, disp, base); \ *(--_nIns) = 0xc7; \ @@ -681,12 +751,36 @@ namespace nanojit asm_output("movq %d(%s),%s",(d),gpn(b),gpn(r)); \ } while(0) +#define SSE_LDSS(r,d,b)do { \ + count_ld();\ + SSEm(0xf30f10, (r)&7, (d), (b)); \ + asm_output("movss %s,%d(%s)",gpn(r),d,gpn(b)); \ + } while(0) + +#define SSE_STSS(d,b,r)do { \ + count_st();\ + SSEm(0xf30f11, (r)&7, (d), (b)); \ + asm_output("movss %d(%s),%s",(d),gpn(b),gpn(r)); \ + } while(0) + #define SSE_CVTSI2SD(xr,gr) do{ \ count_fpu();\ SSE(0xf20f2a, (xr)&7, (gr)&7); \ asm_output("cvtsi2sd %s,%s",gpn(xr),gpn(gr)); \ } while(0) +#define SSE_CVTSD2SS(xr,gr) do{ \ + count_fpu();\ + SSE(0xf20f5a, (xr)&7, (gr)&7); \ + asm_output("cvtsd2ss %s,%s",gpn(xr),gpn(gr)); \ + } while(0) + +#define SSE_CVTSS2SD(xr,gr) do{ \ + count_fpu();\ + SSE(0xf30f5a, (xr)&7, (gr)&7); \ + asm_output("cvtss2sd %s,%s",gpn(xr),gpn(gr)); \ + } while(0) + #define CVTDQ2PD(dstr,srcr) do{ \ count_fpu();\ SSE(0xf30fe6, (dstr)&7, (srcr)&7); \ @@ -829,9 +923,11 @@ namespace nanojit #define FLD1() do { count_fpu(); FPUc(0xd9e8); asm_output("fld1"); fpu_push(); } while(0) #define FLDZ() do { count_fpu(); FPUc(0xd9ee); asm_output("fldz"); fpu_push(); } while(0) #define FFREE(r) do { count_fpu(); FPU(0xddc0, r); asm_output("ffree %s",fpn(r)); } while(0) +#define FST32(p,d,b) do { count_stq(); FPUm(0xd902|(p), d, b); asm_output("fst%s32 %d(%s)",((p)?"p":""),d,gpn(b)); if (p) fpu_pop(); } while(0) #define FSTQ(p,d,b) do { count_stq(); FPUm(0xdd02|(p), d, b); asm_output("fst%sq %d(%s)",((p)?"p":""),d,gpn(b)); if (p) fpu_pop(); } while(0) #define FSTPQ(d,b) FSTQ(1,d,b) #define FCOM(p,d,b) do { count_fpuld(); FPUm(0xdc02|(p), d, b); asm_output("fcom%s %d(%s)",((p)?"p":""),d,gpn(b)); if (p) fpu_pop(); } while(0) +#define FLD32(d,b) do { count_ldq(); FPUm(0xd900, d, b); asm_output("fld32 %d(%s)",d,gpn(b)); fpu_push();} while(0) #define FLDQ(d,b) do { count_ldq(); FPUm(0xdd00, d, b); asm_output("fldq %d(%s)",d,gpn(b)); fpu_push();} while(0) #define FILDQ(d,b) do { count_fpuld(); FPUm(0xdf05, d, b); asm_output("fildq %d(%s)",d,gpn(b)); fpu_push(); } while(0) #define FILD(d,b) do { count_fpuld(); FPUm(0xdb00, d, b); asm_output("fild %d(%s)",d,gpn(b)); fpu_push(); } while(0) From 4732e1b68216a67e9774fcc4efba91646d5dc5f9 Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Tue, 1 Dec 2009 17:40:25 -0800 Subject: [PATCH 09/82] nanojit/Assembler.cpp: when LIR_stqi calls asm_store32 twice, pass LIR_sti for the opcode, rather than op. (r=me) --HG-- extra : convert_revision : 0da91726eca5ccbfed98d340e428d3303d5708f0 --- js/src/nanojit/Assembler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/nanojit/Assembler.cpp b/js/src/nanojit/Assembler.cpp index 0efb9079ebf..2758533915e 100755 --- a/js/src/nanojit/Assembler.cpp +++ b/js/src/nanojit/Assembler.cpp @@ -1186,8 +1186,8 @@ namespace nanojit if (value->isop(LIR_qjoin) && op != LIR_st32f) { // this is correct for little-endian only - asm_store32(op, value->oprnd1(), dr, base); - asm_store32(op, value->oprnd2(), dr+4, base); + asm_store32(LIR_sti, value->oprnd1(), dr, base); + asm_store32(LIR_sti, value->oprnd2(), dr+4, base); } else { From a4408cec60aafb80837eb2331b7fc9d19ad1afbe Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 1 Dec 2009 18:50:07 -0800 Subject: [PATCH 10/82] Bug 531347 - nanojit: rejig asm_call(). r=edwsmith. --HG-- extra : convert_revision : 1c68f440a757a8cb002c8da9118d034a9c7d4fab --- js/src/nanojit/Assembler.cpp | 17 ---------- js/src/nanojit/Assembler.h | 1 - js/src/nanojit/NativeARM.cpp | 62 ++++++++++++++++++---------------- js/src/nanojit/NativePPC.cpp | 12 ++++--- js/src/nanojit/NativeSparc.cpp | 13 ++++--- js/src/nanojit/NativeX64.cpp | 12 ++++--- js/src/nanojit/Nativei386.cpp | 13 ++++--- 7 files changed, 65 insertions(+), 65 deletions(-) diff --git a/js/src/nanojit/Assembler.cpp b/js/src/nanojit/Assembler.cpp index 2758533915e..28576521d08 100755 --- a/js/src/nanojit/Assembler.cpp +++ b/js/src/nanojit/Assembler.cpp @@ -1419,23 +1419,6 @@ namespace nanojit case LIR_icall: { countlir_call(); - Register rr = UnknownReg; - if (ARM_VFP && op == LIR_fcall) - { - // fcall - rr = asm_prep_fcall(ins); - } - else - { - rr = retRegs[0]; - prepResultReg(ins, rmask(rr)); - } - - // do this after we've handled the call result, so we dont - // force the call result to be spilled unnecessarily. - - evictScratchRegs(); - asm_call(ins); break; } diff --git a/js/src/nanojit/Assembler.h b/js/src/nanojit/Assembler.h index ca73cf5e331..29154469cd0 100644 --- a/js/src/nanojit/Assembler.h +++ b/js/src/nanojit/Assembler.h @@ -364,7 +364,6 @@ namespace nanojit void asm_i2f(LInsp ins); void asm_u2f(LInsp ins); void asm_promote(LIns *ins); - Register asm_prep_fcall(LInsp ins); void asm_nongp_copy(Register r, Register s); void asm_call(LInsp); Register asm_binop_rhs_reg(LInsp ins); diff --git a/js/src/nanojit/NativeARM.cpp b/js/src/nanojit/NativeARM.cpp index 56568fbebaa..f0e726bae66 100644 --- a/js/src/nanojit/NativeARM.cpp +++ b/js/src/nanojit/NativeARM.cpp @@ -824,6 +824,37 @@ Assembler::asm_stkarg(LInsp arg, int stkd) void Assembler::asm_call(LInsp ins) { + if (ARM_VFP && ins->isop(LIR_fcall)) { + /* Because ARM actually returns the result in (R0,R1), and not in a + * floating point register, the code to move the result into a correct + * register is below. We do nothing here. + * + * The reason being that if we did something here, the final code + * sequence we'd get would be something like: + * MOV {R0-R3},params [from below] + * BL function [from below] + * MOV {R0-R3},spilled data [from evictScratchRegs()] + * MOV Dx,{R0,R1} [from here] + * which is clearly broken. + * + * This is not a problem for non-floating point calls, because the + * restoring of spilled data into R0 is done via a call to + * prepResultReg(R0) in the other branch of this if-then-else, + * meaning that evictScratchRegs() will not modify R0. However, + * prepResultReg is not aware of the concept of using a register pair + * (R0,R1) for the result of a single operation, so it can only be + * used here with the ultimate VFP register, and not R0/R1, which + * potentially allows for R0/R1 to get corrupted as described. + */ + } else { + prepResultReg(ins, rmask(retRegs[0])); + } + + // Do this after we've handled the call result, so we don't + // force the call result to be spilled unnecessarily. + + evictScratchRegs(); + const CallInfo* call = ins->callInfo(); ArgSize sizes[MAXARGS]; uint32_t argc = call->get_sizes(sizes); @@ -835,8 +866,8 @@ Assembler::asm_call(LInsp ins) // If we're using VFP, and the return type is a double, it'll come back in // R0/R1. We need to either place it in the result fp reg, or store it. - // See comments in asm_prep_fcall() for more details as to why this is - // necessary here for floating point calls, but not for integer calls. + // See comments above for more details as to why this is necessary here + // for floating point calls, but not for integer calls. if (ARM_VFP && ins->isUsed()) { // Determine the size (and type) of the instruction result. ArgSize rsize = (ArgSize)(call->_argtypes & ARGSIZE_MASK_ANY); @@ -2067,33 +2098,6 @@ Assembler::asm_fcmp(LInsp ins) FCMPD(ra, rb, e_bit); } -Register -Assembler::asm_prep_fcall(LInsp) -{ - /* Because ARM actually returns the result in (R0,R1), and not in a - * floating point register, the code to move the result into a correct - * register is at the beginning of asm_call(). This function does - * nothing. - * - * The reason being that if this function did something, the final code - * sequence we'd get would be something like: - * MOV {R0-R3},params [from asm_call()] - * BL function [from asm_call()] - * MOV {R0-R3},spilled data [from evictScratchRegs()] - * MOV Dx,{R0,R1} [from this function] - * which is clearly broken. - * - * This is not a problem for non-floating point calls, because the - * restoring of spilled data into R0 is done via a call to prepResultReg(R0) - * at the same point in the sequence as this function is called, meaning that - * evictScratchRegs() will not modify R0. However, prepResultReg is not aware - * of the concept of using a register pair (R0,R1) for the result of a single - * operation, so it can only be used here with the ultimate VFP register, and - * not R0/R1, which potentially allows for R0/R1 to get corrupted as described. - */ - return UnknownReg; -} - /* Call this with targ set to 0 if the target is not yet known and the branch * will be patched up later. */ diff --git a/js/src/nanojit/NativePPC.cpp b/js/src/nanojit/NativePPC.cpp index 9c17de475e8..a9bb88e1efa 100644 --- a/js/src/nanojit/NativePPC.cpp +++ b/js/src/nanojit/NativePPC.cpp @@ -662,10 +662,6 @@ namespace nanojit } } - Register Assembler::asm_prep_fcall(LIns *ins) { - return prepResultReg(ins, rmask(F1)); - } - void Assembler::asm_int(LIns *ins) { Register rr = prepResultReg(ins, GpRegs); asm_li(rr, ins->imm32()); @@ -699,6 +695,14 @@ namespace nanojit } void Assembler::asm_call(LIns *ins) { + Register retReg = ( ins->isop(LIR_fcall) ? F1 : retRegs[0] ); + prepResultReg(ins, rmask(retReg)); + + // Do this after we've handled the call result, so we don't + // force the call result to be spilled unnecessarily. + + evictScratchRegs(); + const CallInfo* call = ins->callInfo(); ArgSize sizes[MAXARGS]; uint32_t argc = call->get_sizes(sizes); diff --git a/js/src/nanojit/NativeSparc.cpp b/js/src/nanojit/NativeSparc.cpp index 81c0fa0f731..2e03ac564c3 100644 --- a/js/src/nanojit/NativeSparc.cpp +++ b/js/src/nanojit/NativeSparc.cpp @@ -153,6 +153,14 @@ namespace nanojit void Assembler::asm_call(LInsp ins) { + Register retReg = ( ins->isop(LIR_fcall) ? F0 : retRegs[0] ); + prepResultReg(ins, rmask(retReg)); + + // Do this after we've handled the call result, so we don't + // force the call result to be spilled unnecessarily. + + evictScratchRegs(); + const CallInfo* call = ins->callInfo(); underrunProtect(8); @@ -934,11 +942,6 @@ namespace nanojit LDDF32(FP, d, rr); } - Register Assembler::asm_prep_fcall(LInsp ins) - { - return prepResultReg(ins, rmask(F0)); - } - void Assembler::asm_u2f(LInsp ins) { underrunProtect(72); diff --git a/js/src/nanojit/NativeX64.cpp b/js/src/nanojit/NativeX64.cpp index c32428b68e1..ea9f873e4ad 100644 --- a/js/src/nanojit/NativeX64.cpp +++ b/js/src/nanojit/NativeX64.cpp @@ -832,6 +832,14 @@ namespace nanojit } void Assembler::asm_call(LIns *ins) { + Register retReg = ( ins->isop(LIR_fcall) ? XMM0 : retRegs[0] ); + prepResultReg(ins, rmask(retReg)); + + // Do this after we've handled the call result, so we don't + // force the call result to be spilled unnecessarily. + + evictScratchRegs(); + const CallInfo *call = ins->callInfo(); ArgSize sizes[MAXARGS]; int argc = call->get_sizes(sizes); @@ -1519,10 +1527,6 @@ namespace nanojit TODO(asm_qjoin); } - Register Assembler::asm_prep_fcall(LIns *ins) { - return prepResultReg(ins, rmask(XMM0)); - } - void Assembler::asm_param(LIns *ins) { uint32_t a = ins->paramArg(); uint32_t kind = ins->paramKind(); diff --git a/js/src/nanojit/Nativei386.cpp b/js/src/nanojit/Nativei386.cpp index f6fa5b72555..4d86d808528 100644 --- a/js/src/nanojit/Nativei386.cpp +++ b/js/src/nanojit/Nativei386.cpp @@ -201,6 +201,14 @@ namespace nanojit void Assembler::asm_call(LInsp ins) { + Register retReg = ( ins->isop(LIR_fcall) ? FST0 : retRegs[0] ); + prepResultReg(ins, rmask(retReg)); + + // Do this after we've handled the call result, so we don't + // force the call result to be spilled unnecessarily. + + evictScratchRegs(); + const CallInfo* call = ins->callInfo(); // must be signed, not unsigned uint32_t iargs = call->count_iargs(); @@ -1730,11 +1738,6 @@ namespace nanojit } } - Register Assembler::asm_prep_fcall(LInsp ins) - { - return prepResultReg(ins, rmask(FST0)); - } - void Assembler::asm_u2f(LInsp ins) { // where our result goes From fd6f74bc98f9569e29fdd23fc9ffcf5284ca3313 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Fri, 4 Dec 2009 10:46:09 -0800 Subject: [PATCH 11/82] Add regress-532491.js to jstests.list (follow-up for bug 532491). --- js/src/tests/js1_8/regress/jstests.list | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/tests/js1_8/regress/jstests.list b/js/src/tests/js1_8/regress/jstests.list index bafa09715ff..19467388c68 100644 --- a/js/src/tests/js1_8/regress/jstests.list +++ b/js/src/tests/js1_8/regress/jstests.list @@ -82,3 +82,4 @@ script regress-479740.js script regress-481800.js script regress-483749.js script regress-499524.js +script regress-532491.js From ac9a848ac50b6db536d1630fb10209f3e14ec465 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Fri, 4 Dec 2009 21:54:43 +0300 Subject: [PATCH 12/82] bug 531037 - patching js1_4/Eval/jstests.list to include bug's test --- js/src/tests/js1_4/Eval/jstests.list | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/tests/js1_4/Eval/jstests.list b/js/src/tests/js1_4/Eval/jstests.list index ccbfba82138..1f09aec5e68 100644 --- a/js/src/tests/js1_4/Eval/jstests.list +++ b/js/src/tests/js1_4/Eval/jstests.list @@ -2,3 +2,4 @@ url-prefix ../../jsreftest.html?test=js1_4/Eval/ script eval-001.js script eval-002.js script eval-003.js +script regress-531037.js From dfbcf19f408084ce8fe06dd0637e258a7e56f552 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Fri, 4 Dec 2009 21:57:51 +0300 Subject: [PATCH 13/82] bug 531682 - patching js1_4/Eval/jstests.list to include bug's test --- js/src/tests/js1_4/Eval/jstests.list | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/tests/js1_4/Eval/jstests.list b/js/src/tests/js1_4/Eval/jstests.list index 1f09aec5e68..add94deee18 100644 --- a/js/src/tests/js1_4/Eval/jstests.list +++ b/js/src/tests/js1_4/Eval/jstests.list @@ -3,3 +3,4 @@ script eval-001.js script eval-002.js script eval-003.js script regress-531037.js +script regress-531682.js From 318753041664e258ba3113f6d6caaa0b7465121a Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sat, 5 Dec 2009 06:58:42 +1100 Subject: [PATCH 14/82] Bug 528857 - nanojit: mismanagement of name lifetimes with TMFLAGS=assembly? (NJ-only part). r=graydon. --HG-- extra : convert_revision : cb855a65f046a59c28277766aa5d320df33159c2 --- js/src/nanojit/LIR.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/nanojit/LIR.h b/js/src/nanojit/LIR.h index 8fa998f5b5d..53df9bcaa60 100644 --- a/js/src/nanojit/LIR.h +++ b/js/src/nanojit/LIR.h @@ -1097,10 +1097,10 @@ namespace nanojit char* name; }; HashMap names; - LabelMap *labels; void formatImm(int32_t c, char *buf); - public: + public: + LabelMap *labels; LirNameMap(Allocator& alloc, LabelMap *lm) : alloc(alloc), lircounts(alloc), From ad0e2876154fdbfd7b326f75f984d6ac34d8bc20 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Fri, 4 Dec 2009 15:38:25 -0600 Subject: [PATCH 15/82] Bug 532363 - TM: "Assertion failure: IsDenseArrayId(cx, obj, (jsid) prop), at ../jsarray.cpp". r=bzbarsky. --- js/src/jstracer.cpp | 2 +- js/src/trace-test/tests/basic/testBug532363.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 js/src/trace-test/tests/basic/testBug532363.js diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 2d98bce1404..a310fb58179 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -11626,7 +11626,7 @@ GetPropertyWithNativeGetter(JSContext* cx, JSObject* obj, JSScopeProperty* sprop JSObject* pobj; JS_ASSERT(obj->lookupProperty(cx, sprop->id, &pobj, &prop)); JS_ASSERT(prop == (JSProperty*) sprop); - obj->dropProperty(cx, prop); + pobj->dropProperty(cx, prop); #endif // JSScopeProperty::get contains a special case for With objects. We can diff --git a/js/src/trace-test/tests/basic/testBug532363.js b/js/src/trace-test/tests/basic/testBug532363.js new file mode 100644 index 00000000000..c329014cd4f --- /dev/null +++ b/js/src/trace-test/tests/basic/testBug532363.js @@ -0,0 +1,2 @@ +for (var i = 0; i < 9; i++) + ({__parent__: []} = []); From 8fb72a7a1227d148db1d1e07f0664440fd522d53 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 4 Dec 2009 19:59:55 -0800 Subject: [PATCH 16/82] Get rid of TreeInfo and inline its members into TreeFragment (bug 525371, r=gal,lw) --- js/src/jscntxt.h | 5 +- js/src/jsrecursion.cpp | 48 ++-- js/src/jstracer.cpp | 636 +++++++++++++++++++---------------------- js/src/jstracer.h | 161 +++++------ 4 files changed, 394 insertions(+), 456 deletions(-) diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 978f8723873..5618301244d 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -113,7 +113,6 @@ static const size_t MAX_GLOBAL_SLOTS = 4096; static const size_t GLOBAL_SLOTS_BUFFER_SIZE = MAX_GLOBAL_SLOTS + 1; /* Forward declarations of tracer types. */ -class TreeInfo; class VMAllocator; class TraceRecorder; class FrameInfoCache; @@ -149,7 +148,7 @@ struct InterpState // call exit guard mismatched void* rpAtLastTreeCall; // value of rp at innermost tree call guard VMSideExit* outermostTreeExitGuard; // the last side exit returned by js_CallTree - TreeInfo* outermostTree; // the outermost tree we initially invoked + TreeFragment* outermostTree; // the outermost tree we initially invoked uintN* inlineCallCountp; // inline call count counter VMSideExit** innermostNestedGuardp; VMSideExit* innermost; @@ -168,7 +167,7 @@ struct InterpState uintN nativeVpLen; jsval* nativeVp; - InterpState(JSContext *cx, JSTraceMonitor *tm, TreeInfo *ti, + InterpState(JSContext *cx, JSTraceMonitor *tm, TreeFragment *ti, uintN &inlineCallCountp, VMSideExit** innermostNestedGuardp); ~InterpState(); }; diff --git a/js/src/jsrecursion.cpp b/js/src/jsrecursion.cpp index d06430e094a..2ad62fdc407 100644 --- a/js/src/jsrecursion.cpp +++ b/js/src/jsrecursion.cpp @@ -70,7 +70,7 @@ class RecursiveSlotMap : public SlotMap * Store at exit->sp_adj - sizeof(double) */ ptrdiff_t retOffset = downPostSlots * sizeof(double) - - mRecorder.treeInfo->nativeStackBase; + mRecorder.tree->nativeStackBase; mRecorder.lir->insStorei(mRecorder.addName(rval_ins, "rval_ins"), mRecorder.lirbuf->sp, retOffset); } @@ -93,7 +93,7 @@ class UpRecursiveSlotMap : public RecursiveSlotMap /* * The native stack offset of the return value once this frame has * returned, is: - * -treeInfo->nativeStackBase + downPostSlots * sizeof(double) + * -tree->nativeStackBase + downPostSlots * sizeof(double) * * Note, not +1, since the offset is 0-based. * @@ -101,15 +101,15 @@ class UpRecursiveSlotMap : public RecursiveSlotMap * be the amount down recursion added, which was just guarded as * |downPostSlots|. So the offset is: * - * -treeInfo->nativeStackBase + downPostSlots * sizeof(double) - + * -tree->nativeStackBase + downPostSlots * sizeof(double) - * downPostSlots * sizeof(double) * Or: - * -treeInfo->nativeStackBase + * -tree->nativeStackBase * * This makes sense because this slot is just above the highest sp for * the down frame. */ - lir->insStorei(rval_ins, lirbuf->sp, -mRecorder.treeInfo->nativeStackBase); + lir->insStorei(rval_ins, lirbuf->sp, -mRecorder.tree->nativeStackBase); lirbuf->sp = lir->ins2(LIR_piadd, lirbuf->sp, lir->insImmWord(-int(downPostSlots) * sizeof(double))); @@ -152,7 +152,7 @@ TraceRecorder::downSnapshot(FrameInfo* downFrame) /* Build the typemap the exit will have. Note extra stack slot for return value. */ unsigned downPostSlots = downFrame->callerHeight; - unsigned ngslots = treeInfo->globalSlots->length(); + unsigned ngslots = tree->globalSlots->length(); unsigned exitTypeMapLen = downPostSlots + 1 + ngslots; JSTraceType* exitTypeMap = (JSTraceType*)alloca(sizeof(JSTraceType) * exitTypeMapLen); JSTraceType* typeMap = downFrame->get_typemap(); @@ -176,7 +176,7 @@ TraceRecorder::downSnapshot(FrameInfo* downFrame) exit->block = cx->fp->down->blockChain; exit->pc = downFrame->pc + JSOP_CALL_LENGTH; exit->imacpc = NULL; - exit->sp_adj = ((downPostSlots + 1) * sizeof(double)) - treeInfo->nativeStackBase; + exit->sp_adj = ((downPostSlots + 1) * sizeof(double)) - tree->nativeStackBase; exit->rp_adj = exit->calldepth * sizeof(FrameInfo*); exit->nativeCalleeWord = 0; exit->lookupFlags = js_InferFlags(cx, 0); @@ -257,11 +257,11 @@ TraceRecorder::upRecursion() */ js_CaptureStackTypes(cx, 1, fi->get_typemap()); } else { - /* Case 2: Guess that up-recursion is backing out, infer types from our TreeInfo. */ - JS_ASSERT(treeInfo->nStackTypes == downPostSlots + 1); + /* Case 2: Guess that up-recursion is backing out, infer types from our Tree. */ + JS_ASSERT(tree->nStackTypes == downPostSlots + 1); JSTraceType* typeMap = fi->get_typemap(); for (unsigned i = 0; i < downPostSlots; i++) - typeMap[i] = treeInfo->typeMap[i]; + typeMap[i] = tree->typeMap[i]; } fi = traceMonitor->frameCache->memoize(fi); @@ -311,7 +311,7 @@ TraceRecorder::upRecursion() for (unsigned i = 0; i < downPostSlots; i++) slotMap.addSlot(exit->stackType(i)); slotMap.addSlot(&stackval(-1)); - VisitGlobalSlots(slotMap, cx, *treeInfo->globalSlots); + VisitGlobalSlots(slotMap, cx, *tree->globalSlots); if (recursive_pc == (jsbytecode*)fragment->root->ip) { debug_only_print0(LC_TMTracer, "Compiling up-recursive loop...\n"); } else { @@ -319,9 +319,9 @@ TraceRecorder::upRecursion() exit->exitType = RECURSIVE_UNLINKED_EXIT; exit->recursive_pc = recursive_pc; } - JS_ASSERT(treeInfo->recursion != Recursion_Disallowed); - if (treeInfo->recursion != Recursion_Detected) - treeInfo->recursion = Recursion_Unwinds; + JS_ASSERT(tree->recursion != Recursion_Disallowed); + if (tree->recursion != Recursion_Detected) + tree->recursion = Recursion_Unwinds; return closeLoop(slotMap, exit); } @@ -424,7 +424,7 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc) * value. The slurpSlot variable keeps track of the last slot that has been * unboxed, as to avoid re-unboxing when taking a SLURP_FAIL exit. */ - unsigned numGlobalSlots = treeInfo->globalSlots->length(); + unsigned numGlobalSlots = tree->globalSlots->length(); unsigned safeSlots = NativeStackSlots(cx, frameDepth) + 1 + numGlobalSlots; jsbytecode* recursive_pc = return_pc + JSOP_CALL_LENGTH; VMSideExit* exit = (VMSideExit*) @@ -435,7 +435,7 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc) exit->exitType = RECURSIVE_SLURP_FAIL_EXIT; exit->numStackSlots = downPostSlots + 1; exit->numGlobalSlots = numGlobalSlots; - exit->sp_adj = ((downPostSlots + 1) * sizeof(double)) - treeInfo->nativeStackBase; + exit->sp_adj = ((downPostSlots + 1) * sizeof(double)) - tree->nativeStackBase; exit->recursive_pc = recursive_pc; /* @@ -557,7 +557,7 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc) for (unsigned i = 0; i < downPostSlots; i++) slotMap.addSlot(typeMap[i]); slotMap.addSlot(&stackval(-1), typeMap[downPostSlots]); - VisitGlobalSlots(slotMap, cx, *treeInfo->globalSlots); + VisitGlobalSlots(slotMap, cx, *tree->globalSlots); debug_only_print0(LC_TMTracer, "Compiling up-recursive slurp...\n"); exit = copy(exit); if (exit->recursive_pc == fragment->root->ip) @@ -566,7 +566,7 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc) exit->exitType = RECURSIVE_UNLINKED_EXIT; debug_only_printf(LC_TMTreeVis, "TREEVIS CHANGEEXIT EXIT=%p TYPE=%s\n", (void*)exit, getExitName(exit->exitType)); - JS_ASSERT(treeInfo->recursion >= Recursion_Unwinds); + JS_ASSERT(tree->recursion >= Recursion_Unwinds); return closeLoop(slotMap, exit); } @@ -584,9 +584,9 @@ TraceRecorder::downRecursion() JS_ASSERT(unsigned(slots) == NativeStackSlots(cx, 1) - fp->argc - 2 - fp->script->nfixed - 1); /* Guard that there is enough stack space. */ - JS_ASSERT(treeInfo->maxNativeStackSlots >= treeInfo->nativeStackBase / sizeof(double)); - int guardSlots = slots + treeInfo->maxNativeStackSlots - - treeInfo->nativeStackBase / sizeof(double); + JS_ASSERT(tree->maxNativeStackSlots >= tree->nativeStackBase / sizeof(double)); + int guardSlots = slots + tree->maxNativeStackSlots - + tree->nativeStackBase / sizeof(double); LIns* sp_top = lir->ins2(LIR_piadd, lirbuf->sp, lir->insImmWord(guardSlots * sizeof(double))); guard(true, lir->ins2(LIR_plt, sp_top, eos_ins), OOM_EXIT); @@ -618,8 +618,8 @@ TraceRecorder::downRecursion() exit = snapshot(RECURSIVE_UNLINKED_EXIT); exit->recursive_pc = fp->script->code; debug_only_print0(LC_TMTracer, "Compiling down-recursive function call.\n"); - JS_ASSERT(treeInfo->recursion != Recursion_Disallowed); - treeInfo->recursion = Recursion_Detected; + JS_ASSERT(tree->recursion != Recursion_Disallowed); + tree->recursion = Recursion_Detected; return closeLoop(exit); } @@ -783,7 +783,7 @@ TraceRecorder::slurpSlot(LIns* val_ins, jsval* vp, SlurpInfo* info) LIns* val = slurpSlot(val_ins, vp, exit); lir->insStorei(val, lirbuf->sp, - -treeInfo->nativeStackBase + ptrdiff_t(info->curSlot) * sizeof(double)); + -tree->nativeStackBase + ptrdiff_t(info->curSlot) * sizeof(double)); info->curSlot++; } diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index a310fb58179..f9b11cb9b2c 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -1153,43 +1153,43 @@ Oracle::clearDemotability() } JS_REQUIRES_STACK static JS_INLINE void -MarkSlotUndemotable(JSContext* cx, TreeInfo* ti, unsigned slot) +MarkSlotUndemotable(JSContext* cx, LinkableFragment* f, unsigned slot) { - if (slot < ti->nStackTypes) { + if (slot < f->nStackTypes) { oracle.markStackSlotUndemotable(cx, slot); return; } - uint16* gslots = ti->globalSlots->data(); - oracle.markGlobalSlotUndemotable(cx, gslots[slot - ti->nStackTypes]); + uint16* gslots = f->globalSlots->data(); + oracle.markGlobalSlotUndemotable(cx, gslots[slot - f->nStackTypes]); } JS_REQUIRES_STACK static JS_INLINE void -MarkSlotUndemotable(JSContext* cx, TreeInfo* ti, unsigned slot, const void* pc) +MarkSlotUndemotable(JSContext* cx, LinkableFragment* f, unsigned slot, const void* pc) { - if (slot < ti->nStackTypes) { + if (slot < f->nStackTypes) { oracle.markStackSlotUndemotable(cx, slot, pc); return; } - uint16* gslots = ti->globalSlots->data(); - oracle.markGlobalSlotUndemotable(cx, gslots[slot - ti->nStackTypes]); + uint16* gslots = f->globalSlots->data(); + oracle.markGlobalSlotUndemotable(cx, gslots[slot - f->nStackTypes]); } static JS_REQUIRES_STACK inline bool -IsSlotUndemotable(JSContext* cx, TreeInfo* ti, unsigned slot, const void* ip) +IsSlotUndemotable(JSContext* cx, LinkableFragment* f, unsigned slot, const void* ip) { - if (slot < ti->nStackTypes) + if (slot < f->nStackTypes) return oracle.isStackSlotUndemotable(cx, slot, ip); - uint16* gslots = ti->globalSlots->data(); - return oracle.isGlobalSlotUndemotable(cx, gslots[slot - ti->nStackTypes]); + uint16* gslots = f->globalSlots->data(); + return oracle.isGlobalSlotUndemotable(cx, gslots[slot - f->nStackTypes]); } static JS_REQUIRES_STACK inline bool -IsSlotUndemotable(JSContext* cx, TreeInfo* ti, unsigned slot) +IsSlotUndemotable(JSContext* cx, LinkableFragment* f, unsigned slot) { - return IsSlotUndemotable(cx, ti, slot, cx->fp->regs->pc); + return IsSlotUndemotable(cx, f, slot, cx->fp->regs->pc); } class FrameInfoCache @@ -1417,8 +1417,8 @@ LookupOrAddLoop(JSTraceMonitor* tm, const void *ip, JSObject* globalObj, uint32_t profFragID = (js_LogController.lcbits & LC_FragProfile) ? (++(tm->lastFragID)) : 0; ) - TreeFragment* f = new (*tm->dataAlloc) TreeFragment(ip, globalObj, globalShape, argc - verbose_only(, profFragID)); + TreeFragment* f = new (*tm->dataAlloc) TreeFragment(ip, tm->dataAlloc, globalObj, globalShape, + argc verbose_only(, profFragID)); f->root = f; /* f is the root of a new tree */ *prevTreeNextp = f; /* insert f at the end of the vmfragments bucket-list */ f->next = NULL; @@ -1435,9 +1435,9 @@ AddNewPeerToPeerList(JSTraceMonitor* tm, TreeFragment* peer) uint32_t profFragID = (js_LogController.lcbits & LC_FragProfile) ? (++(tm->lastFragID)) : 0; ) - TreeFragment* f = new (*tm->dataAlloc) TreeFragment(peer->ip, peer->globalObj, - peer->globalShape, peer->argc - verbose_only(, profFragID)); + TreeFragment* f = new (*tm->dataAlloc) TreeFragment(peer->ip, tm->dataAlloc, peer->globalObj, + peer->globalShape, peer->argc + verbose_only(, profFragID)); f->root = f; /* f is the root of a new tree */ f->first = peer->first; /* add f to peer list */ f->peer = peer->peer; @@ -1447,9 +1447,55 @@ AddNewPeerToPeerList(JSTraceMonitor* tm, TreeFragment* peer) return f; } +JS_REQUIRES_STACK void +TreeFragment::initialize(JSContext* cx, SlotList *globalSlots) +{ + this->dependentTrees.clear(); + this->linkedTrees.clear(); + this->globalSlots = globalSlots; + + /* Capture the coerced type of each active slot in the type map. */ + this->typeMap.captureTypes(cx, globalObj, *globalSlots, 0 /* callDepth */); + this->nStackTypes = this->typeMap.length() - globalSlots->length(); + +#ifdef DEBUG + this->treeFileName = cx->fp->script->filename; + this->treeLineNumber = js_FramePCToLineNumber(cx, cx->fp); + this->treePCOffset = FramePCOffset(cx->fp); +#endif + this->script = cx->fp->script; + this->recursion = Recursion_None; + this->gcthings.clear(); + this->sprops.clear(); + this->unstableExits = NULL; + this->sideExits.clear(); + + /* Determine the native frame layout at the entry point. */ + this->nativeStackBase = (nStackTypes - (cx->fp->regs->sp - StackBase(cx->fp))) * + sizeof(double); + this->maxNativeStackSlots = nStackTypes; + this->maxCallDepth = 0; +} + +UnstableExit* +TreeFragment::removeUnstableExit(VMSideExit* exit) +{ + /* Now erase this exit from the unstable exit list. */ + UnstableExit** tail = &this->unstableExits; + for (UnstableExit* uexit = this->unstableExits; uexit != NULL; uexit = uexit->next) { + if (uexit->exit == exit) { + *tail = uexit->next; + return *tail; + } + tail = &uexit->next; + } + JS_NOT_REACHED("exit not in unstable exit list"); + return NULL; +} + #ifdef DEBUG static void -AssertTreeIsUnique(JSTraceMonitor* tm, TreeFragment* f, TreeInfo* ti) +AssertTreeIsUnique(JSTraceMonitor* tm, TreeFragment* f) { JS_ASSERT(f->root == f); @@ -1458,15 +1504,12 @@ AssertTreeIsUnique(JSTraceMonitor* tm, TreeFragment* f, TreeInfo* ti) * trace explosion since we are trying to stabilize something without * properly connecting peer edges. */ - TreeInfo* ti_other; for (TreeFragment* peer = LookupLoop(tm, f->ip, f->globalObj, f->globalShape, f->argc); peer != NULL; peer = peer->peer) { if (!peer->code() || peer == f) continue; - ti_other = peer->treeInfo; - JS_ASSERT(ti_other); - JS_ASSERT(!ti->typeMap.matches(ti_other->typeMap)); + JS_ASSERT(!f->typeMap.matches(peer->typeMap)); } } #endif @@ -2002,10 +2045,10 @@ VisitGlobalSlots(Visitor &visitor, JSContext *cx, JSObject *globalObj, template static JS_REQUIRES_STACK JS_ALWAYS_INLINE void -VisitGlobalSlots(Visitor &visitor, JSContext *cx, TreeInfo *ti) +VisitGlobalSlots(Visitor &visitor, JSContext *cx, TreeFragment *f) { - JSObject* globalObj = ti->globalObj(); - SlotList& gslots = *ti->globalSlots; + JSObject* globalObj = f->globalObj(); + SlotList& gslots = *f->globalSlots; VisitGlobalSlots(visitor, cx, globalObj, gslots.length(), gslots.data()); } @@ -2253,24 +2296,22 @@ MergeTypeMaps(JSTraceType** partial, unsigned* plength, JSTraceType* complete, u /* Specializes a tree to any missing globals, including any dependent trees. */ static JS_REQUIRES_STACK void -SpecializeTreesToMissingGlobals(JSContext* cx, JSObject* globalObj, TreeInfo* root) +SpecializeTreesToMissingGlobals(JSContext* cx, JSObject* globalObj, TreeFragment* root) { - TreeInfo* ti = root; - - ti->typeMap.captureMissingGlobalTypes(cx, globalObj, *ti->globalSlots, ti->nStackTypes); - JS_ASSERT(ti->globalSlots->length() == ti->typeMap.length() - ti->nStackTypes); + root->typeMap.captureMissingGlobalTypes(cx, globalObj, *root->globalSlots, root->nStackTypes); + JS_ASSERT(root->globalSlots->length() == root->typeMap.length() - root->nStackTypes); for (unsigned i = 0; i < root->dependentTrees.length(); i++) { - ti = root->dependentTrees[i]->treeInfo; + TreeFragment* f = root->dependentTrees[i]; - /* ti can be NULL if we hit the recording tree in emitTreeCall; this is harmless. */ - if (ti && ti->nGlobalTypes() < ti->globalSlots->length()) - SpecializeTreesToMissingGlobals(cx, globalObj, ti); + /* code() can be NULL if we hit the recording tree in emitTreeCall; this is harmless. */ + if (f->code() && f->nGlobalTypes() < f->globalSlots->length()) + SpecializeTreesToMissingGlobals(cx, globalObj, f); } for (unsigned i = 0; i < root->linkedTrees.length(); i++) { - ti = root->linkedTrees[i]->treeInfo; - if (ti && ti->nGlobalTypes() < ti->globalSlots->length()) - SpecializeTreesToMissingGlobals(cx, globalObj, ti); + TreeFragment* f = root->linkedTrees[i]; + if (f->code() && f->nGlobalTypes() < f->globalSlots->length()) + SpecializeTreesToMissingGlobals(cx, globalObj, f); } } @@ -2300,15 +2341,15 @@ InitConst(const T &t) JS_REQUIRES_STACK TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* fragment, - TreeInfo* ti, unsigned stackSlots, unsigned ngslots, JSTraceType* typeMap, + unsigned stackSlots, unsigned ngslots, JSTraceType* typeMap, VMSideExit* innermost, jsbytecode* outer, uint32 outerArgc, RecordReason recordReason) : cx(cx), traceMonitor(&JS_TRACE_MONITOR(cx)), fragment(fragment), - treeInfo(ti), + tree(fragment->root), recordReason(recordReason), - globalObj(ti->globalObj()), + globalObj(tree->globalObj), outer(outer), outerArgc(outerArgc), lexicalBlock(cx->fp->blockChain), @@ -2320,7 +2361,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag loopLabel(NULL), lirbuf(traceMonitor->lirbuf), mark(*traceMonitor->traceAlloc), - numSideExitsBefore(treeInfo->sideExits.length()), + numSideExitsBefore(tree->sideExits.length()), tracker(), nativeFrameTracker(), global_dslots(NULL), @@ -2341,9 +2382,6 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag { JS_ASSERT(globalObj == JS_GetGlobalForObject(cx, cx->fp->scopeChain)); JS_ASSERT(cx->fp->regs->pc == (jsbytecode*)fragment->ip); - JS_ASSERT(fragment->root == treeInfo->rootFragment); - JS_ASSERT_IF(fragment->root == fragment, !fragment->root->treeInfo); - JS_ASSERT(ti); /* * Reset the fragment state we care about in case we got a recycled @@ -2371,7 +2409,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag #ifdef JS_JIT_SPEW debug_only_print0(LC_TMMinimal, "\n"); debug_only_printf(LC_TMMinimal, "Recording starting from %s:%u@%u (FragID=%06u)\n", - ti->treeFileName, ti->treeLineNumber, ti->treePCOffset, + tree->treeFileName, tree->treeLineNumber, tree->treePCOffset, fragment->profFragID); debug_only_printf(LC_TMTracer, "globalObj=%p, shape=%d\n", @@ -2437,11 +2475,11 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag InitConst(eor_ins) = addName(lir->insLoad(LIR_ldp, lirbuf->state, offsetof(InterpState, eor)), "eor"); /* If we came from exit, we might not have enough global types. */ - if (ti->globalSlots->length() > ti->nGlobalTypes()) - SpecializeTreesToMissingGlobals(cx, globalObj, ti); + if (tree->globalSlots->length() > tree->nGlobalTypes()) + SpecializeTreesToMissingGlobals(cx, globalObj, tree); /* read into registers all values on the stack and all globals we know so far */ - import(treeInfo, lirbuf->sp, stackSlots, ngslots, callDepth, typeMap); + import(tree, lirbuf->sp, stackSlots, ngslots, callDepth, typeMap); /* Finish handling RECURSIVE_SLURP_FAIL_EXIT in startRecorder. */ if (anchor && anchor->exitType == RECURSIVE_SLURP_FAIL_EXIT) @@ -2472,7 +2510,6 @@ TraceRecorder::~TraceRecorder() { /* Should already have been adjusted by callers before calling delete. */ JS_ASSERT(traceMonitor->recorder != this); - JS_ASSERT(fragment->root == treeInfo->rootFragment); if (trashSelf) TrashTree(cx, fragment->root); @@ -2504,7 +2541,6 @@ TraceRecorder::finishSuccessfully() { JS_ASSERT(traceMonitor->recorder == this); JS_ASSERT(fragment->lastIns && fragment->code()); - JS_ASSERT_IF(fragment == fragment->root, fragment->toTreeFragment()->treeInfo); AUDIT(traceCompleted); mark.commit(); @@ -2530,15 +2566,14 @@ TraceRecorder::finishAbort(const char* reason) { JS_ASSERT(traceMonitor->recorder == this); JS_ASSERT(!fragment->code()); - JS_ASSERT_IF(fragment == fragment->root, !fragment->toTreeFragment()->treeInfo); AUDIT(recorderAborted); #ifdef DEBUG debug_only_printf(LC_TMAbort, "Abort recording of tree %s:%d@%d at %s:%d@%d: %s.\n", - treeInfo->treeFileName, - treeInfo->treeLineNumber, - treeInfo->treePCOffset, + tree->treeFileName, + tree->treeLineNumber, + tree->treePCOffset, cx->fp->script->filename, js_FramePCToLineNumber(cx, cx->fp), FramePCOffset(cx->fp), @@ -2548,8 +2583,8 @@ TraceRecorder::finishAbort(const char* reason) /* * If this is the primary trace and we didn't succeed compiling, trash the - * TreeInfo object. Otherwise, remove the VMSideExits we added while - * recording, which are about to be invalid. + * tree. Otherwise, remove the VMSideExits we added while recording, which + * are about to be invalid. * * BIG FAT WARNING: resetting the length is only a valid strategy as long as * there may be only one recorder active for a single TreeInfo at a time. @@ -2558,8 +2593,8 @@ TraceRecorder::finishAbort(const char* reason) if (fragment->root == fragment) { TrashTree(cx, fragment->toTreeFragment()); } else { - JS_ASSERT(numSideExitsBefore <= fragment->root->treeInfo->sideExits.length()); - fragment->root->treeInfo->sideExits.setLength(numSideExitsBefore); + JS_ASSERT(numSideExitsBefore <= fragment->root->sideExits.length()); + fragment->root->sideExits.setLength(numSideExitsBefore); } /* Grab local copies of members needed after |delete this|. */ @@ -2592,35 +2627,35 @@ inline LIns* TraceRecorder::insImmVal(jsval val) { if (JSVAL_IS_TRACEABLE(val)) - treeInfo->gcthings.addUnique(val); + tree->gcthings.addUnique(val); return lir->insImmWord(val); } inline LIns* TraceRecorder::insImmObj(JSObject* obj) { - treeInfo->gcthings.addUnique(OBJECT_TO_JSVAL(obj)); + tree->gcthings.addUnique(OBJECT_TO_JSVAL(obj)); return lir->insImmPtr((void*)obj); } inline LIns* TraceRecorder::insImmFun(JSFunction* fun) { - treeInfo->gcthings.addUnique(OBJECT_TO_JSVAL(FUN_OBJECT(fun))); + tree->gcthings.addUnique(OBJECT_TO_JSVAL(FUN_OBJECT(fun))); return lir->insImmPtr((void*)fun); } inline LIns* TraceRecorder::insImmStr(JSString* str) { - treeInfo->gcthings.addUnique(STRING_TO_JSVAL(str)); + tree->gcthings.addUnique(STRING_TO_JSVAL(str)); return lir->insImmPtr((void*)str); } inline LIns* TraceRecorder::insImmSprop(JSScopeProperty* sprop) { - treeInfo->sprops.addUnique(sprop); + tree->sprops.addUnique(sprop); return lir->insImmPtr((void*)sprop); } @@ -2657,7 +2692,7 @@ TraceRecorder::isGlobal(jsval* p) const * |p| must be the address of a jsval that is represented in the native stack * area. The return value is the offset, from InterpState::stackBase, in bytes, * where the native representation of |*p| is stored. To get the offset - * relative to InterpState::sp, subtract TreeInfo::nativeStackBase. + * relative to InterpState::sp, subtract TreeFragment::nativeStackBase. */ JS_REQUIRES_STACK ptrdiff_t TraceRecorder::nativeStackOffset(jsval* p) const @@ -2678,20 +2713,20 @@ TraceRecorder::nativeStackOffset(jsval* p) const } /* * Return the offset, from InterpState:sp, for the given jsval. Shorthand for: - * -TreeInfo::nativeStackBase + nativeStackOffset(p). + * -TreeFragment::nativeStackBase + nativeStackOffset(p). */ inline JS_REQUIRES_STACK ptrdiff_t TraceRecorder::nativespOffset(jsval* p) const { - return -treeInfo->nativeStackBase + nativeStackOffset(p); + return -tree->nativeStackBase + nativeStackOffset(p); } /* Track the maximum number of native frame slots we need during execution. */ inline void TraceRecorder::trackNativeStackUse(unsigned slots) { - if (slots > treeInfo->maxNativeStackSlots) - treeInfo->maxNativeStackSlots = slots; + if (slots > tree->maxNativeStackSlots) + tree->maxNativeStackSlots = slots; } /* @@ -2834,17 +2869,17 @@ JSTraceMonitor::flush() } static inline void -MarkTreeInfo(JSTracer* trc, TreeInfo *ti) +MarkTree(JSTracer* trc, TreeFragment *f) { - jsval* vp = ti->gcthings.data(); - unsigned len = ti->gcthings.length(); + jsval* vp = f->gcthings.data(); + unsigned len = f->gcthings.length(); while (len--) { jsval v = *vp++; JS_SET_TRACING_NAME(trc, "jitgcthing"); JS_CallTracer(trc, JSVAL_TO_TRACEABLE(v), JSVAL_TRACE_KIND(v)); } - JSScopeProperty** spropp = ti->sprops.data(); - len = ti->sprops.length(); + JSScopeProperty** spropp = f->sprops.data(); + len = f->sprops.length(); while (len--) { JSScopeProperty* sprop = *spropp++; sprop->trace(trc); @@ -2858,19 +2893,19 @@ JSTraceMonitor::mark(JSTracer* trc) for (size_t i = 0; i < FRAGMENT_TABLE_SIZE; ++i) { TreeFragment* f = vmfragments[i]; while (f) { - if (TreeInfo* ti = f->treeInfo) - MarkTreeInfo(trc, ti); + if (f->code()) + MarkTree(trc, f); TreeFragment* peer = f->peer; while (peer) { - if (TreeInfo* ti = peer->treeInfo) - MarkTreeInfo(trc, ti); + if (peer->code()) + MarkTree(trc, peer); peer = peer->peer; } f = f->next; } } if (recorder) - MarkTreeInfo(trc, recorder->getTreeInfo()); + MarkTree(trc, recorder->getTree()); } } @@ -3150,7 +3185,7 @@ GetUpvarOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDepth, // Next search the trace entry frame, which is not in the FrameInfo stack. if (state->outermostTree->script->staticLevel == upvarLevel) { - uint32 argc = state->outermostTree->rootFragment->argc; + uint32 argc = state->outermostTree->argc; uint32 native_slot = T::native_slot(argc, slot); *result = state->stackBase[native_slot]; return state->callstackBase[0]->get_typemap()[native_slot]; @@ -3615,7 +3650,7 @@ public: }; JS_REQUIRES_STACK void -TraceRecorder::import(TreeInfo* treeInfo, LIns* sp, unsigned stackSlots, unsigned ngslots, +TraceRecorder::import(TreeFragment* tree, LIns* sp, unsigned stackSlots, unsigned ngslots, unsigned callDepth, JSTraceType* typeMap) { /* @@ -3634,7 +3669,7 @@ TraceRecorder::import(TreeInfo* treeInfo, LIns* sp, unsigned stackSlots, unsigne */ JSTraceType* globalTypeMap = typeMap + stackSlots; - unsigned length = treeInfo->nGlobalTypes(); + unsigned length = tree->nGlobalTypes(); /* * This is potentially the typemap of the side exit and thus shorter than @@ -3642,11 +3677,11 @@ TraceRecorder::import(TreeInfo* treeInfo, LIns* sp, unsigned stackSlots, unsigne */ if (ngslots < length) { MergeTypeMaps(&globalTypeMap /* out param */, &ngslots /* out param */, - treeInfo->globalTypeMap(), length, + tree->globalTypeMap(), length, (JSTraceType*)alloca(sizeof(JSTraceType) * length)); } - JS_ASSERT(ngslots == treeInfo->nGlobalTypes()); - ptrdiff_t offset = -treeInfo->nativeStackBase; + JS_ASSERT(ngslots == tree->nGlobalTypes()); + ptrdiff_t offset = -tree->nativeStackBase; /* * Check whether there are any values on the stack we have to unbox and do @@ -3659,7 +3694,7 @@ TraceRecorder::import(TreeInfo* treeInfo, LIns* sp, unsigned stackSlots, unsigne ImportGlobalSlotVisitor globalVisitor(*this, eos_ins, globalTypeMap); VisitGlobalSlots(globalVisitor, cx, globalObj, ngslots, - treeInfo->globalSlots->data()); + tree->globalSlots->data()); if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { ImportUnboxedStackSlotVisitor unboxedStackVisitor(*this, sp, offset, @@ -3708,17 +3743,17 @@ TraceRecorder::lazilyImportGlobalSlot(unsigned slot) jsval* vp = &STOBJ_GET_SLOT(globalObj, slot); if (known(vp)) return true; /* we already have it */ - unsigned index = treeInfo->globalSlots->length(); + unsigned index = tree->globalSlots->length(); /* Add the slot to the list of interned global slots. */ - JS_ASSERT(treeInfo->nGlobalTypes() == treeInfo->globalSlots->length()); - treeInfo->globalSlots->add(slot); + JS_ASSERT(tree->nGlobalTypes() == tree->globalSlots->length()); + tree->globalSlots->add(slot); JSTraceType type = getCoercedType(*vp); if (type == TT_INT32 && oracle.isGlobalSlotUndemotable(cx, slot)) type = TT_DOUBLE; - treeInfo->typeMap.add(type); + tree->typeMap.add(type); import(eos_ins, slot*sizeof(double), vp, type, "global", index, NULL); - SpecializeTreesToMissingGlobals(cx, globalObj, treeInfo); + SpecializeTreesToMissingGlobals(cx, globalObj, tree); return true; } @@ -3944,12 +3979,10 @@ public: JS_REQUIRES_STACK void TraceRecorder::adjustCallerTypes(TreeFragment* f) { - TreeInfo* ti = f->treeInfo; + AdjustCallerGlobalTypesVisitor globalVisitor(*this, f->globalTypeMap()); + VisitGlobalSlots(globalVisitor, cx, *tree->globalSlots); - AdjustCallerGlobalTypesVisitor globalVisitor(*this, ti->globalTypeMap()); - VisitGlobalSlots(globalVisitor, cx, *treeInfo->globalSlots); - - AdjustCallerStackTypesVisitor stackVisitor(*this, ti->stackTypeMap()); + AdjustCallerStackTypesVisitor stackVisitor(*this, f->stackTypeMap()); VisitStackSlots(stackVisitor, cx, 0); JS_ASSERT(f == f->root); @@ -4067,7 +4100,7 @@ TraceRecorder::snapshot(ExitType exitType) trackNativeStackUse(stackSlots + 1); /* Capture the type map into a temporary location. */ - unsigned ngslots = treeInfo->globalSlots->length(); + unsigned ngslots = tree->globalSlots->length(); unsigned typemap_size = (stackSlots + ngslots) * sizeof(JSTraceType); /* Use the recorder-local temporary type map. */ @@ -4083,7 +4116,7 @@ TraceRecorder::snapshot(ExitType exitType) */ DetermineTypesVisitor detVisitor(*this, typemap); VisitSlots(detVisitor, cx, callDepth, ngslots, - treeInfo->globalSlots->data()); + tree->globalSlots->data()); JS_ASSERT(unsigned(detVisitor.getTypeMap() - typemap) == ngslots + stackSlots); @@ -4122,8 +4155,8 @@ TraceRecorder::snapshot(ExitType exitType) * Check if we already have a matching side exit; if so we can return that * side exit instead of creating a new one. */ - VMSideExit** exits = treeInfo->sideExits.data(); - unsigned nexits = treeInfo->sideExits.length(); + VMSideExit** exits = tree->sideExits.data(); + unsigned nexits = tree->sideExits.length(); if (exitType == LOOP_EXIT) { for (unsigned n = 0; n < nexits; ++n) { VMSideExit* e = exits[n]; @@ -4154,10 +4187,10 @@ TraceRecorder::snapshot(ExitType exitType) exit->exitType = exitType; exit->block = fp->blockChain; if (fp->blockChain) - treeInfo->gcthings.addUnique(OBJECT_TO_JSVAL(fp->blockChain)); + tree->gcthings.addUnique(OBJECT_TO_JSVAL(fp->blockChain)); exit->pc = pc; exit->imacpc = fp->imacpc; - exit->sp_adj = (stackSlots * sizeof(double)) - treeInfo->nativeStackBase; + exit->sp_adj = (stackSlots * sizeof(double)) - tree->nativeStackBase; exit->rp_adj = exit->calldepth * sizeof(FrameInfo*); exit->nativeCalleeWord = 0; exit->lookupFlags = js_InferFlags(cx, 0); @@ -4202,7 +4235,7 @@ TraceRecorder::guard(bool expected, LIns* cond, VMSideExit* exit) GuardRecord* guardRec = createGuardRecord(exit); if (exit->exitType == LOOP_EXIT) - treeInfo->sideExits.add(exit); + tree->sideExits.add(exit); if (!cond->isCond()) { expected = !expected; @@ -4231,7 +4264,7 @@ TraceRecorder::copy(VMSideExit* copy) exit->target = NULL; if (exit->exitType == LOOP_EXIT) - treeInfo->sideExits.add(exit); + tree->sideExits.add(exit); #if defined JS_JIT_SPEW TreevisLogExit(cx, exit); #endif @@ -4310,7 +4343,7 @@ TraceRecorder::compile() ResetJIT(cx, FR_DEEP_BAIL); return ARECORD_ABORTED; } - if (treeInfo->maxNativeStackSlots >= MAX_NATIVE_STACK_SLOTS) { + if (tree->maxNativeStackSlots >= MAX_NATIVE_STACK_SLOTS) { debug_only_print0(LC_TMTracer, "Blacklist: excessive stack use.\n"); Blacklist((jsbytecode*) fragment->root->ip); return ARECORD_STOP; @@ -4355,9 +4388,7 @@ TraceRecorder::compile() assm->patch(anchor); } JS_ASSERT(fragment->code()); - JS_ASSERT_IF(fragment == fragment->root, !fragment->root->treeInfo); - if (fragment == fragment->root) - fragment->root->treeInfo = treeInfo; + JS_ASSERT_IF(fragment == fragment->root, fragment->root == tree); return ARECORD_CONTINUE; } @@ -4373,8 +4404,8 @@ JoinPeers(Assembler* assm, VMSideExit* exit, TreeFragment* target) if (exit->root() == target) return; - target->treeInfo->dependentTrees.addUnique(exit->root()); - exit->root()->treeInfo->linkedTrees.addUnique(target); + target->dependentTrees.addUnique(exit->root()); + exit->root()->linkedTrees.addUnique(target); } /* Results of trying to connect an arbitrary type A with arbitrary type B */ @@ -4449,14 +4480,14 @@ class SlotMap : public SlotVisitorBase * such slots as undemotable. */ JS_REQUIRES_STACK TypeConsensus - checkTypes(TreeInfo* ti) + checkTypes(LinkableFragment* f) { - if (length() != ti->typeMap.length()) + if (length() != f->typeMap.length()) return TypeConsensus_Bad; bool has_undemotes = false; for (unsigned i = 0; i < length(); i++) { - TypeCheckResult result = checkType(i, ti->typeMap[i]); + TypeCheckResult result = checkType(i, f->typeMap[i]); if (result == TypeCheck_Bad) return TypeConsensus_Bad; if (result == TypeCheck_Undemote) @@ -4491,7 +4522,7 @@ class SlotMap : public SlotVisitorBase { for (unsigned i = 0; i < length(); i++) { if (get(i).lastCheck == TypeCheck_Undemote) - MarkSlotUndemotable(mRecorder.cx, mRecorder.treeInfo, i); + MarkSlotUndemotable(mRecorder.cx, mRecorder.tree, i); } } @@ -4581,7 +4612,7 @@ JS_REQUIRES_STACK TypeConsensus TraceRecorder::selfTypeStability(SlotMap& slotMap) { debug_only_printf(LC_TMTracer, "Checking type stability against self=%p\n", (void*)fragment); - TypeConsensus consensus = slotMap.checkTypes(treeInfo); + TypeConsensus consensus = slotMap.checkTypes(tree); /* Best case: loop jumps back to its own header */ if (consensus == TypeConsensus_Okay) @@ -4609,10 +4640,10 @@ TraceRecorder::peerTypeStability(SlotMap& slotMap, const void* ip, TreeFragment* return TypeConsensus_Bad; bool onlyUndemotes = false; for (; peer != NULL; peer = peer->peer) { - if (!peer->treeInfo || peer == fragment) + if (!peer->code() || peer == fragment) continue; debug_only_printf(LC_TMTracer, "Checking type stability against peer=%p\n", (void*)peer); - TypeConsensus consensus = slotMap.checkTypes(peer->treeInfo); + TypeConsensus consensus = slotMap.checkTypes(peer); if (consensus == TypeConsensus_Okay) { *pPeer = peer; /* Return this even though there will be linkage; the trace itself is not stable. @@ -4637,7 +4668,7 @@ JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::closeLoop(VMSideExit* exit) { DefaultSlotMap slotMap(*this); - VisitSlots(slotMap, cx, 0, *treeInfo->globalSlots); + VisitSlots(slotMap, cx, 0, *tree->globalSlots); return closeLoop(slotMap, exit); } @@ -4666,7 +4697,7 @@ TraceRecorder::closeLoop(SlotMap& slotMap, VMSideExit* exit) } JS_ASSERT_IF(exit->exitType == UNSTABLE_LOOP_EXIT, - exit->numStackSlots == treeInfo->nStackTypes); + exit->numStackSlots == tree->nStackTypes); JS_ASSERT_IF(exit->exitType != UNSTABLE_LOOP_EXIT, exit->exitType == RECURSIVE_UNLINKED_EXIT); JS_ASSERT_IF(exit->exitType == RECURSIVE_UNLINKED_EXIT, exit->recursive_pc != fragment->root->ip); @@ -4723,16 +4754,16 @@ TraceRecorder::closeLoop(SlotMap& slotMap, VMSideExit* exit) UnstableExit* uexit = new (traceAlloc()) UnstableExit; uexit->fragment = fragment; uexit->exit = exit; - uexit->next = treeInfo->unstableExits; - treeInfo->unstableExits = uexit; + uexit->next = tree->unstableExits; + tree->unstableExits = uexit; } else { JS_ASSERT(peer->code()); exit->target = peer; debug_only_printf(LC_TMTracer, "Joining type-unstable trace to target fragment %p.\n", (void*)peer); - peer->treeInfo->dependentTrees.addUnique(fragment->root); - treeInfo->linkedTrees.addUnique(peer); + peer->dependentTrees.addUnique(fragment->root); + tree->linkedTrees.addUnique(peer); } } else { exit->exitType = LOOP_EXIT; @@ -4762,8 +4793,8 @@ TraceRecorder::closeLoop(SlotMap& slotMap, VMSideExit* exit) debug_only_print0(LC_TMTracer, "updating specializations on dependent and linked trees\n"); - if (fragment->root->treeInfo) - SpecializeTreesToMissingGlobals(cx, globalObj, fragment->root->treeInfo); + if (fragment->root->code()) + SpecializeTreesToMissingGlobals(cx, globalObj, fragment->root); /* * If this is a newly formed tree, and the outer tree has not been compiled yet, we @@ -4791,23 +4822,23 @@ FullMapFromExit(TypeMap& typeMap, VMSideExit* exit) typeMap.fromRaw(exit->stackTypeMap(), exit->numStackSlots); typeMap.fromRaw(exit->globalTypeMap(), exit->numGlobalSlots); /* Include globals that were later specialized at the root of the tree. */ - if (exit->numGlobalSlots < exit->root()->treeInfo->nGlobalTypes()) { - typeMap.fromRaw(exit->root()->treeInfo->globalTypeMap() + exit->numGlobalSlots, - exit->root()->treeInfo->nGlobalTypes() - exit->numGlobalSlots); + if (exit->numGlobalSlots < exit->root()->nGlobalTypes()) { + typeMap.fromRaw(exit->root()->globalTypeMap() + exit->numGlobalSlots, + exit->root()->nGlobalTypes() - exit->numGlobalSlots); } } static JS_REQUIRES_STACK TypeConsensus TypeMapLinkability(JSContext* cx, const TypeMap& typeMap, TreeFragment* peer) { - const TypeMap& peerMap = peer->treeInfo->typeMap; + const TypeMap& peerMap = peer->typeMap; unsigned minSlots = JS_MIN(typeMap.length(), peerMap.length()); TypeConsensus consensus = TypeConsensus_Okay; for (unsigned i = 0; i < minSlots; i++) { if (typeMap[i] == peerMap[i]) continue; if (typeMap[i] == TT_INT32 && peerMap[i] == TT_DOUBLE && - IsSlotUndemotable(cx, peer->treeInfo, i, peer->ip)) { + IsSlotUndemotable(cx, peer, i, peer->ip)) { consensus = TypeConsensus_Undemotes; } else { return TypeConsensus_Bad; @@ -4817,20 +4848,20 @@ TypeMapLinkability(JSContext* cx, const TypeMap& typeMap, TreeFragment* peer) } static JS_REQUIRES_STACK unsigned -FindUndemotesInTypemaps(JSContext* cx, const TypeMap& typeMap, TreeInfo* treeInfo, +FindUndemotesInTypemaps(JSContext* cx, const TypeMap& typeMap, LinkableFragment* f, Queue& undemotes) { undemotes.setLength(0); - unsigned minSlots = JS_MIN(typeMap.length(), treeInfo->typeMap.length()); + unsigned minSlots = JS_MIN(typeMap.length(), f->typeMap.length()); for (unsigned i = 0; i < minSlots; i++) { - if (typeMap[i] == TT_INT32 && treeInfo->typeMap[i] == TT_DOUBLE) { + if (typeMap[i] == TT_INT32 && f->typeMap[i] == TT_DOUBLE) { undemotes.add(i); - } else if (typeMap[i] != treeInfo->typeMap[i]) { + } else if (typeMap[i] != f->typeMap[i]) { return 0; } } for (unsigned i = 0; i < undemotes.length(); i++) - MarkSlotUndemotable(cx, treeInfo, undemotes[i]); + MarkSlotUndemotable(cx, f, undemotes[i]); return undemotes.length(); } @@ -4844,10 +4875,9 @@ TraceRecorder::joinEdgesToEntry(TreeFragment* peer_root) Queue undemotes(NULL); for (TreeFragment* peer = peer_root; peer; peer = peer->peer) { - TreeInfo* ti = peer->treeInfo; - if (!ti) + if (!peer->code()) continue; - UnstableExit* uexit = ti->unstableExits; + UnstableExit* uexit = peer->unstableExits; while (uexit != NULL) { /* :TODO: these exits go somewhere else. */ if (uexit->exit->exitType == RECURSIVE_UNLINKED_EXIT) { @@ -4865,10 +4895,10 @@ TraceRecorder::joinEdgesToEntry(TreeFragment* peer_root) (void*)uexit->fragment, (void*)uexit->exit); /* It's okay! Link together and remove the unstable exit. */ JoinPeers(traceMonitor->assembler, uexit->exit, (TreeFragment*)fragment); - uexit = ti->removeUnstableExit(uexit->exit); + uexit = peer->removeUnstableExit(uexit->exit); } else { /* Check for int32->double slots that suggest trashing. */ - if (FindUndemotesInTypemaps(cx, typeMap, treeInfo, undemotes)) { + if (FindUndemotesInTypemaps(cx, typeMap, tree, undemotes)) { JS_ASSERT(peer == uexit->fragment->root); if (fragment == peer) trashSelf = true; @@ -4921,8 +4951,8 @@ TraceRecorder::endLoop(VMSideExit* exit) */ debug_only_print0(LC_TMTracer, "updating specializations on dependent and linked trees\n"); - if (fragment->root->treeInfo) - SpecializeTreesToMissingGlobals(cx, globalObj, fragment->root->treeInfo); + if (fragment->root->code()) + SpecializeTreesToMissingGlobals(cx, globalObj, fragment->root); /* * If this is a newly formed tree, and the outer tree has not been compiled @@ -4947,7 +4977,6 @@ TraceRecorder::endLoop(VMSideExit* exit) JS_REQUIRES_STACK void TraceRecorder::prepareTreeCall(TreeFragment* inner, LIns*& inner_sp_ins) { - TreeInfo* ti = inner->treeInfo; inner_sp_ins = lirbuf->sp; VMSideExit* exit = snapshot(OOM_EXIT); @@ -4976,24 +5005,24 @@ TraceRecorder::prepareTreeCall(TreeFragment* inner, LIns*& inner_sp_ins) debug_only_printf(LC_TMTracer, "sp_adj=%lld outer=%lld inner=%lld\n", (long long int)sp_adj, - (long long int)treeInfo->nativeStackBase, - (long long int)ti->nativeStackBase); + (long long int)tree->nativeStackBase, + (long long int)inner->nativeStackBase); ptrdiff_t sp_offset = - - treeInfo->nativeStackBase /* rebase sp to beginning of outer tree's stack */ + - tree->nativeStackBase /* rebase sp to beginning of outer tree's stack */ + sp_adj /* adjust for stack in outer frame inner tree can't see */ - + ti->maxNativeStackSlots * sizeof(double); /* plus the inner tree's stack */ + + inner->maxNativeStackSlots * sizeof(double); /* plus the inner tree's stack */ LIns* sp_top = lir->ins2(LIR_piadd, lirbuf->sp, INS_CONSTWORD(sp_offset)); guard(true, lir->ins2(LIR_plt, sp_top, eos_ins), exit); /* Guard that we have enough call stack space. */ - ptrdiff_t rp_offset = rp_adj + ti->maxCallDepth * sizeof(FrameInfo*); + ptrdiff_t rp_offset = rp_adj + inner->maxCallDepth * sizeof(FrameInfo*); LIns* rp_top = lir->ins2(LIR_piadd, lirbuf->rp, INS_CONSTWORD(rp_offset)); guard(true, lir->ins2(LIR_plt, rp_top, eor_ins), exit); sp_offset = - - treeInfo->nativeStackBase /* rebase sp to beginning of outer tree's stack */ + - tree->nativeStackBase /* rebase sp to beginning of outer tree's stack */ + sp_adj /* adjust for stack in outer frame inner tree can't see */ - + ti->nativeStackBase; /* plus the inner tree's stack base */ + + inner->nativeStackBase; /* plus the inner tree's stack base */ /* We have enough space, so adjust sp and rp to their new level. */ lir->insStorei(inner_sp_ins = lir->ins2(LIR_piadd, lirbuf->sp, INS_CONSTWORD(sp_offset)), lirbuf->state, offsetof(InterpState, sp)); @@ -5021,11 +5050,11 @@ BuildGlobalTypeMapFromInnerTree(Queue& typeMap, VMSideExit* inner) typeMap.add(inner->globalTypeMap(), inner->numGlobalSlots); /* Add missing global types from the innermost exit's tree. */ - TreeInfo* innerTree = inner->root()->treeInfo; + TreeFragment* innerFrag = inner->root(); unsigned slots = inner->numGlobalSlots; - if (slots < innerTree->nGlobalTypes()) { - typeMap.add(innerTree->globalTypeMap() + slots, innerTree->nGlobalTypes() - slots); - slots = innerTree->nGlobalTypes(); + if (slots < innerFrag->nGlobalTypes()) { + typeMap.add(innerFrag->globalTypeMap() + slots, innerFrag->nGlobalTypes() - slots); + slots = innerFrag->nGlobalTypes(); } JS_ASSERT(typeMap.length() - initialSlots == slots); return slots; @@ -5112,8 +5141,7 @@ TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit, LIns* inner_s fullMap.add(exit->stackTypeMap(), exit->numStackSlots); BuildGlobalTypeMapFromInnerTree(fullMap, exit); - TreeInfo* ti = inner->treeInfo; - import(ti, inner_sp_ins, exit->numStackSlots, fullMap.length() - exit->numStackSlots, + import(inner, inner_sp_ins, exit->numStackSlots, fullMap.length() - exit->numStackSlots, exit->calldepth, fullMap.data()); /* Restore sp and rp to their original values (we still have them in a register). */ @@ -5133,8 +5161,8 @@ TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit, LIns* inner_s (void*)nestedExit, (void*)exit); /* Register us as a dependent tree of the inner tree. */ - inner->treeInfo->dependentTrees.addUnique(fragment->root); - treeInfo->linkedTrees.addUnique(inner); + inner->dependentTrees.addUnique(fragment->root); + tree->linkedTrees.addUnique(inner); } /* Add a if/if-else control-flow merge point to the list of known merge points. */ @@ -5328,7 +5356,6 @@ CheckGlobalObjectShape(JSContext* cx, JSTraceMonitor* tm, JSObject* globalObj, if (tm->recorder) { TreeFragment* root = tm->recorder->getFragment()->root; - TreeInfo* ti = tm->recorder->getTreeInfo(); /* Check the global shape matches the recorder's treeinfo's shape. */ if (globalObj != root->globalObj || globalShape != root->globalShape) { @@ -5344,7 +5371,7 @@ CheckGlobalObjectShape(JSContext* cx, JSTraceMonitor* tm, JSObject* globalObj, if (shape) *shape = globalShape; if (slots) - *slots = ti->globalSlots; + *slots = root->globalSlots; return true; } @@ -5383,7 +5410,7 @@ CheckGlobalObjectShape(JSContext* cx, JSTraceMonitor* tm, JSObject* globalObj, */ bool JS_REQUIRES_STACK TraceRecorder::startRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* f, - TreeInfo* ti, unsigned stackSlots, unsigned ngslots, + unsigned stackSlots, unsigned ngslots, JSTraceType* typeMap, VMSideExit* expectedInnerExit, jsbytecode* outer, uint32 outerArgc, RecordReason recordReason) { @@ -5391,9 +5418,8 @@ TraceRecorder::startRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* f, JS_ASSERT(!tm->needFlush); JS_ASSERT_IF(cx->fp->imacpc, f->root != f); - tm->recorder = new TraceRecorder(cx, anchor, f, ti, stackSlots, - ngslots, typeMap, expectedInnerExit, - outer, outerArgc, recordReason); + tm->recorder = new TraceRecorder(cx, anchor, f, stackSlots, ngslots, typeMap, + expectedInnerExit, outer, outerArgc, recordReason); if (!tm->recorder || tm->outOfMemory() || js_OverfullJITCache(tm)) { ResetJIT(cx, FR_OOM); @@ -5417,7 +5443,6 @@ TraceRecorder::startRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* f, static void TrashTree(JSContext* cx, TreeFragment* f) { - JS_ASSERT((!f->code()) == (!f->treeInfo)); JS_ASSERT(f == f->root); debug_only_printf(LC_TMTreeVis, "TREEVIS TRASH FRAG=%p\n", (void*)f); @@ -5425,15 +5450,13 @@ TrashTree(JSContext* cx, TreeFragment* f) return; AUDIT(treesTrashed); debug_only_print0(LC_TMTracer, "Trashing tree info.\n"); - TreeInfo* ti = f->treeInfo; - f->treeInfo = NULL; f->setCode(NULL); - TreeFragment** data = ti->dependentTrees.data(); - unsigned length = ti->dependentTrees.length(); + TreeFragment** data = f->dependentTrees.data(); + unsigned length = f->dependentTrees.length(); for (unsigned n = 0; n < length; ++n) TrashTree(cx, data[n]); - data = ti->linkedTrees.data(); - length = ti->linkedTrees.length(); + data = f->linkedTrees.data(); + length = f->linkedTrees.length(); for (unsigned n = 0; n < length; ++n) TrashTree(cx, data[n]); } @@ -5635,53 +5658,6 @@ SynthesizeSlowNativeFrame(InterpState& state, JSContext *cx, VMSideExit *exit) cx->fp = fp; } -/* - * Create a TreeInfo in preparation for starting a recorder. If one cannot be - * allocated, reset the JIT and return NULL. - */ -static JS_REQUIRES_STACK TreeInfo* -CreateTreeInfo(JSContext* cx, TreeFragment* f, JSObject* globalObj, SlotList* globalSlots) -{ - JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); - - /* Set up the VM-private treeInfo structure for this fragment. */ - TreeInfo* ti = new (*tm->traceAlloc) TreeInfo(tm->dataAlloc, f, globalSlots); - - /* Capture the coerced type of each active slot in the type map. */ - ti->typeMap.captureTypes(cx, globalObj, *globalSlots, 0 /* callDepth */); - ti->nStackTypes = ti->typeMap.length() - globalSlots->length(); - -#ifdef DEBUG - AssertTreeIsUnique(tm, f, ti); - ti->treeFileName = cx->fp->script->filename; - ti->treeLineNumber = js_FramePCToLineNumber(cx, cx->fp); - ti->treePCOffset = FramePCOffset(cx->fp); -#endif -#ifdef JS_JIT_SPEW - debug_only_printf(LC_TMTreeVis, "TREEVIS CREATETREE ROOT=%p PC=%p FILE=\"%s\" LINE=%d OFFS=%d", - (void*)f, f->ip, ti->treeFileName, ti->treeLineNumber, - FramePCOffset(cx->fp)); - debug_only_print0(LC_TMTreeVis, " STACK=\""); - for (unsigned i = 0; i < ti->nStackTypes; i++) - debug_only_printf(LC_TMTreeVis, "%c", typeChar[ti->typeMap[i]]); - debug_only_print0(LC_TMTreeVis, "\" GLOBALS=\""); - for (unsigned i = 0; i < ti->nGlobalTypes(); i++) - debug_only_printf(LC_TMTreeVis, "%c", typeChar[ti->typeMap[ti->nStackTypes + i]]); - debug_only_print0(LC_TMTreeVis, "\"\n"); -#endif - - /* Determine the native frame layout at the entry point. */ - unsigned entryNativeStackSlots = ti->nStackTypes; - JS_ASSERT(entryNativeStackSlots == NativeStackSlots(cx, 0 /* callDepth */)); - ti->nativeStackBase = (entryNativeStackSlots - - (cx->fp->regs->sp - StackBase(cx->fp))) * sizeof(double); - ti->maxNativeStackSlots = entryNativeStackSlots; - ti->maxCallDepth = 0; - ti->script = cx->fp->script; - - return ti; -} - static JS_REQUIRES_STACK bool RecordTree(JSContext* cx, JSTraceMonitor* tm, TreeFragment* peer, jsbytecode* outer, uint32 outerArgc, JSObject* globalObj, uint32 globalShape, @@ -5714,17 +5690,30 @@ RecordTree(JSContext* cx, JSTraceMonitor* tm, TreeFragment* peer, jsbytecode* ou return false; } - JS_ASSERT(!f->code() && !f->treeInfo); + JS_ASSERT(!f->code()); - TreeInfo* ti = CreateTreeInfo(cx, f, globalObj, globalSlots); - if (!ti) - return false; + f->initialize(cx, globalSlots); +#ifdef DEBUG + AssertTreeIsUnique(tm, f); +#endif +#ifdef JS_JIT_SPEW + debug_only_printf(LC_TMTreeVis, "TREEVIS CREATETREE ROOT=%p PC=%p FILE=\"%s\" LINE=%d OFFS=%d", + (void*)f, f->ip, f->treeFileName, f->treeLineNumber, + FramePCOffset(cx->fp)); + debug_only_print0(LC_TMTreeVis, " STACK=\""); + for (unsigned i = 0; i < f->nStackTypes; i++) + debug_only_printf(LC_TMTreeVis, "%c", typeChar[f->typeMap[i]]); + debug_only_print0(LC_TMTreeVis, "\" GLOBALS=\""); + for (unsigned i = 0; i < f->nGlobalTypes(); i++) + debug_only_printf(LC_TMTreeVis, "%c", typeChar[f->typeMap[f->nStackTypes + i]]); + debug_only_print0(LC_TMTreeVis, "\"\n"); +#endif + /* Recording primary trace. */ - return TraceRecorder::startRecorder(cx, NULL, f, ti, - ti->nStackTypes, - ti->globalSlots->length(), - ti->typeMap.data(), NULL, + return TraceRecorder::startRecorder(cx, NULL, f, f->nStackTypes, + f->globalSlots->length(), + f->typeMap.data(), NULL, outer, outerArgc, reason); } @@ -5732,16 +5721,15 @@ static JS_REQUIRES_STACK TypeConsensus FindLoopEdgeTarget(JSContext* cx, VMSideExit* exit, TreeFragment** peerp) { TreeFragment* from = exit->root(); - TreeInfo* from_ti = from->treeInfo; JS_ASSERT(from->code()); TypeMap typeMap(NULL); FullMapFromExit(typeMap, exit); - JS_ASSERT(typeMap.length() - exit->numStackSlots == from_ti->nGlobalTypes()); + JS_ASSERT(typeMap.length() - exit->numStackSlots == from->nGlobalTypes()); /* Mark all double slots as undemotable */ - uint16* gslots = from_ti->globalSlots->data(); + uint16* gslots = from->globalSlots->data(); for (unsigned i = 0; i < typeMap.length(); i++) { if (typeMap[i] == TT_DOUBLE) { if (exit->exitType == RECURSIVE_UNLINKED_EXIT) { @@ -5750,7 +5738,7 @@ FindLoopEdgeTarget(JSContext* cx, VMSideExit* exit, TreeFragment** peerp) else oracle.markGlobalSlotUndemotable(cx, gslots[i - exit->numStackSlots]); } - if (i < from_ti->nStackTypes) + if (i < from->nStackTypes) oracle.markStackSlotUndemotable(cx, i, from->ip); else if (i >= exit->numStackSlots) oracle.markGlobalSlotUndemotable(cx, gslots[i - exit->numStackSlots]); @@ -5769,11 +5757,10 @@ FindLoopEdgeTarget(JSContext* cx, VMSideExit* exit, TreeFragment** peerp) } for (TreeFragment* peer = firstPeer; peer; peer = peer->peer) { - TreeInfo* peer_ti = peer->treeInfo; - if (!peer_ti) + if (!peer->code()) continue; JS_ASSERT(peer->argc == from->argc); - JS_ASSERT(exit->numStackSlots == peer_ti->nStackTypes); + JS_ASSERT(exit->numStackSlots == peer->nStackTypes); TypeConsensus consensus = TypeMapLinkability(cx, typeMap, peer); if (consensus == TypeConsensus_Okay || consensus == TypeConsensus_Undemotes) { *peerp = peer; @@ -5784,22 +5771,6 @@ FindLoopEdgeTarget(JSContext* cx, VMSideExit* exit, TreeFragment** peerp) return TypeConsensus_Bad; } -UnstableExit* -TreeInfo::removeUnstableExit(VMSideExit* exit) -{ - /* Now erase this exit from the unstable exit list. */ - UnstableExit** tail = &this->unstableExits; - for (UnstableExit* uexit = this->unstableExits; uexit != NULL; uexit = uexit->next) { - if (uexit->exit == exit) { - *tail = uexit->next; - return *tail; - } - tail = &uexit->next; - } - JS_NOT_REACHED("exit not in unstable exit list"); - return NULL; -} - static JS_REQUIRES_STACK bool AttemptToStabilizeTree(JSContext* cx, JSObject* globalObj, VMSideExit* exit, jsbytecode* outer, uint32 outerArgc) @@ -5811,29 +5782,27 @@ AttemptToStabilizeTree(JSContext* cx, JSObject* globalObj, VMSideExit* exit, jsb } TreeFragment* from = exit->root(); - TreeInfo* from_ti = from->treeInfo; TreeFragment* peer = NULL; TypeConsensus consensus = FindLoopEdgeTarget(cx, exit, &peer); if (consensus == TypeConsensus_Okay) { - TreeInfo* peer_ti = peer->treeInfo; - JS_ASSERT(from_ti->globalSlots == peer_ti->globalSlots); + JS_ASSERT(from->globalSlots == peer->globalSlots); JS_ASSERT_IF(exit->exitType == UNSTABLE_LOOP_EXIT, - from_ti->nStackTypes == peer_ti->nStackTypes); - JS_ASSERT(exit->numStackSlots == peer_ti->nStackTypes); + from->nStackTypes == peer->nStackTypes); + JS_ASSERT(exit->numStackSlots == peer->nStackTypes); /* Patch this exit to its peer */ JoinPeers(tm->assembler, exit, peer); /* * Update peer global types. The |from| fragment should already be updated because it on * the execution path, and somehow connected to the entry trace. */ - if (peer_ti->nGlobalTypes() < peer_ti->globalSlots->length()) - SpecializeTreesToMissingGlobals(cx, globalObj, peer_ti); - JS_ASSERT(from_ti->nGlobalTypes() == from_ti->globalSlots->length()); + if (peer->nGlobalTypes() < peer->globalSlots->length()) + SpecializeTreesToMissingGlobals(cx, globalObj, peer); + JS_ASSERT(from->nGlobalTypes() == from->globalSlots->length()); /* This exit is no longer unstable, so remove it. */ if (exit->exitType == UNSTABLE_LOOP_EXIT) - from_ti->removeUnstableExit(exit); - debug_only_stmt(DumpPeerStability(tm, peer->ip, from->globalObj, from->globalShape, from->argc);) + from->removeUnstableExit(exit); + debug_only_stmt(DumpPeerStability(tm, peer->ip, globalObj, from->globalShape, from->argc);) return false; } else if (consensus == TypeConsensus_Undemotes) { /* The original tree is unconnectable, so trash it. */ @@ -5841,6 +5810,9 @@ AttemptToStabilizeTree(JSContext* cx, JSObject* globalObj, VMSideExit* exit, jsb return false; } + uint32 globalShape = from->globalShape; + SlotList *globalSlots = from->globalSlots; + /* Don't bother recording if the exit doesn't expect this PC */ if (exit->exitType == RECURSIVE_UNLINKED_EXIT) { if (++exit->hitcount >= MAX_RECURSIVE_UNLINK_HITS) { @@ -5850,7 +5822,7 @@ AttemptToStabilizeTree(JSContext* cx, JSObject* globalObj, VMSideExit* exit, jsb } if (exit->recursive_pc != cx->fp->regs->pc) return false; - from = LookupLoop(tm, exit->recursive_pc, from->globalObj, from->globalShape, cx->fp->argc); + from = LookupLoop(tm, exit->recursive_pc, globalObj, globalShape, cx->fp->argc); if (!from) return false; /* use stale TI for RecordTree - since from might not have one anymore. */ @@ -5862,8 +5834,8 @@ AttemptToStabilizeTree(JSContext* cx, JSObject* globalObj, VMSideExit* exit, jsb if (*(jsbytecode*)from->ip == JSOP_NOP) return false; - return RecordTree(cx, tm, from->first, outer, outerArgc, from->globalObj, - from->globalShape, from_ti->globalSlots, cx->fp->argc, + return RecordTree(cx, tm, from->first, outer, outerArgc, globalObj, + globalShape, globalSlots, cx->fp->argc, Record_Branch); } @@ -5911,7 +5883,7 @@ AttemptToExtendTree(JSContext* cx, VMSideExit* anchor, VMSideExit* exitedFrom, j } TreeFragment* f = anchor->root(); - JS_ASSERT(f->treeInfo); + JS_ASSERT(f->code()); /* * Don't grow trees above a certain size to avoid code explosion due to @@ -5975,9 +5947,8 @@ AttemptToExtendTree(JSContext* cx, VMSideExit* anchor, VMSideExit* exitedFrom, j typeMap = fullMap.data(); } JS_ASSERT(ngslots >= anchor->numGlobalSlots); - bool rv = TraceRecorder::startRecorder(cx, anchor, c, f->treeInfo, - stackSlots, ngslots, typeMap, exitedFrom, - outer, cx->fp->argc, Record_Branch); + bool rv = TraceRecorder::startRecorder(cx, anchor, c, stackSlots, ngslots, typeMap, + exitedFrom, outer, cx->fp->argc, Record_Branch); #ifdef MOZ_TRACEVIS if (!rv && tvso) tvso->r = R_FAIL_EXTEND_START; @@ -6066,13 +6037,13 @@ TraceRecorder::attemptTreeCall(TreeFragment* f, uintN& inlineCallCount) * In the interim, just do tree calls knowing that they won't go into * recursive trees that can pop parent frames. */ - if (f->treeInfo->script == cx->fp->script) { - if (f->treeInfo->recursion >= Recursion_Unwinds) { + if (f->script == cx->fp->script) { + if (f->recursion >= Recursion_Unwinds) { Blacklist(cx->fp->script->code); js_AbortRecording(cx, "Inner tree is an unsupported type of recursion"); return ARECORD_ABORTED; } else { - f->treeInfo->recursion = Recursion_Disallowed; + f->recursion = Recursion_Disallowed; } } @@ -6267,19 +6238,16 @@ TraceRecorder::findNestedCompatiblePeer(TreeFragment* f) JSTraceMonitor* tm; tm = &JS_TRACE_MONITOR(cx); - unsigned int ngslots = treeInfo->globalSlots->length(); + unsigned int ngslots = tree->globalSlots->length(); - TreeInfo* ti; for (; f != NULL; f = f->peer) { if (!f->code()) continue; - ti = f->treeInfo; - debug_only_printf(LC_TMTracer, "checking nested types %p: ", (void*)f); - if (ngslots > ti->nGlobalTypes()) - SpecializeTreesToMissingGlobals(cx, globalObj, ti); + if (ngslots > f->nGlobalTypes()) + SpecializeTreesToMissingGlobals(cx, globalObj, f); /* * Determine whether the typemap of the inner tree matches the outer @@ -6290,8 +6258,8 @@ TraceRecorder::findNestedCompatiblePeer(TreeFragment* f) * expects a double, but the outer tree has an integer, we can proceed, * but we mark the location undemotable. */ - TypeCompatibilityVisitor visitor(*this, ti->typeMap.data()); - VisitSlots(visitor, cx, 0, *treeInfo->globalSlots); + TypeCompatibilityVisitor visitor(*this, f->typeMap.data()); + VisitSlots(visitor, cx, 0, *tree->globalSlots); debug_only_printf(LC_TMTracer, " %s\n", visitor.isOk() ? "match" : ""); if (visitor.isOk()) @@ -6342,25 +6310,25 @@ public: * Check if types are usable for trace execution. * * @param cx Context. - * @param ti Tree info of peer we're testing. + * @param f Tree of peer we're testing. * @return True if compatible (with or without demotions), false otherwise. */ static JS_REQUIRES_STACK bool -CheckEntryTypes(JSContext* cx, JSObject* globalObj, TreeInfo* ti) +CheckEntryTypes(JSContext* cx, JSObject* globalObj, TreeFragment* f) { - unsigned int ngslots = ti->globalSlots->length(); + unsigned int ngslots = f->globalSlots->length(); - JS_ASSERT(ti->nStackTypes == NativeStackSlots(cx, 0)); + JS_ASSERT(f->nStackTypes == NativeStackSlots(cx, 0)); - if (ngslots > ti->nGlobalTypes()) - SpecializeTreesToMissingGlobals(cx, globalObj, ti); + if (ngslots > f->nGlobalTypes()) + SpecializeTreesToMissingGlobals(cx, globalObj, f); - JS_ASSERT(ti->typeMap.length() == NativeStackSlots(cx, 0) + ngslots); - JS_ASSERT(ti->typeMap.length() == ti->nStackTypes + ngslots); - JS_ASSERT(ti->nGlobalTypes() == ngslots); + JS_ASSERT(f->typeMap.length() == NativeStackSlots(cx, 0) + ngslots); + JS_ASSERT(f->typeMap.length() == f->nStackTypes + ngslots); + JS_ASSERT(f->nGlobalTypes() == ngslots); - CheckEntryTypeVisitor visitor(ti->typeMap.data()); - VisitSlots(visitor, cx, 0, *ti->globalSlots); + CheckEntryTypeVisitor visitor(f->typeMap.data()); + VisitSlots(visitor, cx, 0, *f->globalSlots); debug_only_print0(LC_TMTracer, "\n"); return visitor.isOk(); @@ -6380,11 +6348,11 @@ FindVMCompatiblePeer(JSContext* cx, JSObject* globalObj, TreeFragment* f, uintN& { count = 0; for (; f != NULL; f = f->peer) { - if (!f->treeInfo) + if (!f->code()) continue; debug_only_printf(LC_TMTracer, "checking vm types %p (ip: %p): ", (void*)f, f->ip); - if (CheckEntryTypes(cx, globalObj, f->treeInfo)) + if (CheckEntryTypes(cx, globalObj, f)) return f; ++count; } @@ -6398,11 +6366,11 @@ FindVMCompatiblePeer(JSContext* cx, JSObject* globalObj, TreeFragment* f, uintN& * |deepBailSp| in js_DeepBail. */ JS_ALWAYS_INLINE -InterpState::InterpState(JSContext* cx, JSTraceMonitor* tm, TreeInfo* ti, +InterpState::InterpState(JSContext* cx, JSTraceMonitor* tm, TreeFragment* f, uintN& inlineCallCount, VMSideExit** innermostNestedGuardp) : cx(cx), stackBase(tm->storage.stack()), - sp(stackBase + ti->nativeStackBase / sizeof(double)), + sp(stackBase + f->nativeStackBase / sizeof(double)), eos(tm->storage.global()), callstackBase(tm->storage.callstack()), sor(callstackBase), @@ -6412,7 +6380,7 @@ InterpState::InterpState(JSContext* cx, JSTraceMonitor* tm, TreeInfo* ti, lastTreeExitGuard(NULL), lastTreeCallGuard(NULL), rpAtLastTreeCall(NULL), - outermostTree(ti), + outermostTree(f), inlineCallCountp(&inlineCallCount), innermostNestedGuardp(innermostNestedGuardp), #ifdef EXECUTE_TREE_TIMER @@ -6474,10 +6442,9 @@ ExecuteTrace(JSContext* cx, Fragment* f, InterpState& state) /* Check whether our assumptions about the incoming scope-chain are upheld. */ static JS_REQUIRES_STACK JS_ALWAYS_INLINE bool -ScopeChainCheck(JSContext* cx, TreeInfo* ti, TreeFragment* f) +ScopeChainCheck(JSContext* cx, TreeFragment* f) { - JS_ASSERT(ti->globalObj() == f->globalObj); - JS_ASSERT(ti->globalObj() == JS_GetGlobalForObject(cx, cx->fp->scopeChain)); + JS_ASSERT(f->globalObj == JS_GetGlobalForObject(cx, cx->fp->scopeChain)); /* * The JIT records and expects to execute with two scope-chain @@ -6514,8 +6481,8 @@ ScopeChainCheck(JSContext* cx, TreeInfo* ti, TreeFragment* f) /* Make sure the global object is sane. */ JS_ASSERT(STOBJ_NSLOTS(f->globalObj) <= MAX_GLOBAL_SLOTS); - JS_ASSERT(ti->nGlobalTypes() == ti->globalSlots->length()); - JS_ASSERT_IF(ti->globalSlots->length() != 0, + JS_ASSERT(f->nGlobalTypes() == f->globalSlots->length()); + JS_ASSERT_IF(f->globalSlots->length() != 0, OBJ_SHAPE(f->globalObj) == f->globalShape); return true; } @@ -6530,23 +6497,22 @@ ExecuteTree(JSContext* cx, TreeFragment* f, uintN& inlineCallCount, #ifdef MOZ_TRACEVIS TraceVisStateObj tvso(cx, S_EXECUTE); #endif - JS_ASSERT(f->root == f && f->code() && f->treeInfo); + JS_ASSERT(f->root == f && f->code()); JSTraceMonitor* tm = &JS_TRACE_MONITOR(cx); - TreeInfo* ti = f->treeInfo; - if (!ScopeChainCheck(cx, ti, f)) + if (!ScopeChainCheck(cx, f)) return NULL; /* Initialize trace state. */ - InterpState state(cx, tm, ti, inlineCallCount, innermostNestedGuardp); + InterpState state(cx, tm, f, inlineCallCount, innermostNestedGuardp); double* stack = tm->storage.stack(); double* global = tm->storage.global(); JSObject* globalObj = f->globalObj; - unsigned ngslots = ti->globalSlots->length(); - uint16* gslots = ti->globalSlots->data(); + unsigned ngslots = f->globalSlots->length(); + uint16* gslots = f->globalSlots->data(); BuildNativeFrame(cx, globalObj, 0 /* callDepth */, ngslots, gslots, - ti->typeMap.data(), global, stack); + f->typeMap.data(), global, stack); AUDIT(traceTriggered); debug_only_printf(LC_TMTracer, @@ -6554,7 +6520,7 @@ ExecuteTree(JSContext* cx, TreeFragment* f, uintN& inlineCallCount, cx->fp->script->filename, js_FramePCToLineNumber(cx, cx->fp), FramePCOffset(cx->fp), - ti->maxNativeStackSlots, + f->maxNativeStackSlots, f->code()); debug_only_stmt(uint32 globalSlots = STOBJ_NSLOTS(globalObj);) @@ -6867,7 +6833,7 @@ LeaveTree(InterpState& state, VMSideExit* lr) * type-map. See also the comment in the constructor of TraceRecorder * regarding why this is always safe to do. */ - TreeInfo* outermostTree = state.outermostTree; + TreeFragment* outermostTree = state.outermostTree; uint16* gslots = outermostTree->globalSlots->data(); unsigned ngslots = outermostTree->globalSlots->length(); JS_ASSERT(ngslots == outermostTree->nGlobalTypes()); @@ -6886,8 +6852,8 @@ LeaveTree(InterpState& state, VMSideExit* lr) * is lazily added into a tree, all dependent and linked trees are * immediately specialized (see bug 476653). */ - JS_ASSERT(innermost->root()->treeInfo->nGlobalTypes() == ngslots); - JS_ASSERT(innermost->root()->treeInfo->nGlobalTypes() > innermost->numGlobalSlots); + JS_ASSERT(innermost->root()->nGlobalTypes() == ngslots); + JS_ASSERT(innermost->root()->nGlobalTypes() > innermost->numGlobalSlots); typeMap.ensure(ngslots); #ifdef DEBUG unsigned check_ngslots = @@ -6913,7 +6879,7 @@ LeaveTree(InterpState& state, VMSideExit* lr) /* Write back interned globals. */ JS_ASSERT(state.eos == state.stackBase + MAX_NATIVE_STACK_SLOTS); - JSObject* globalObj = outermostTree->globalObj(); + JSObject* globalObj = outermostTree->globalObj; FlushNativeGlobalFrame(cx, globalObj, state.eos, ngslots, gslots, globalTypeMap); #ifdef DEBUG /* Verify that our state restoration worked. */ @@ -7048,7 +7014,7 @@ js_MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, RecordReason reason) * most time will be spent entering and exiting ExecuteTree(). There's no * benefit to doing this until the down-recursive side completes. */ - if (match->treeInfo->recursion == Recursion_Unwinds) + if (match->recursion == Recursion_Unwinds) return false; VMSideExit* lr = NULL; @@ -9813,10 +9779,10 @@ TraceRecorder::record_EnterFrame(uintN& inlineCallCount) * concerned about. That should pass through below to not regress pre-recursion * functionality. */ - if (IsTraceableRecursion(cx) && treeInfo->script == cx->fp->script) { - if (treeInfo->recursion == Recursion_Disallowed) + if (IsTraceableRecursion(cx) && tree->script == cx->fp->script) { + if (tree->recursion == Recursion_Disallowed) RETURN_STOP_A("recursion not allowed in this tree"); - if (treeInfo->script != cx->fp->script) + if (tree->script != cx->fp->script) RETURN_STOP_A("recursion does not match original tree"); return InjectStatus(downRecursion()); } @@ -9840,14 +9806,14 @@ TraceRecorder::record_EnterFrame(uintN& inlineCallCount) * recursive. */ for (f = first; f; f = f->peer) { - if (f->treeInfo && f->treeInfo->recursion == Recursion_Detected) { + if (f->code() && f->recursion == Recursion_Detected) { /* Since this recorder is about to die, save its values. */ if (++first->hits() <= HOTLOOP) return ARECORD_STOP; if (IsBlacklisted((jsbytecode*)f->ip)) RETURN_STOP_A("inner recursive tree is blacklisted"); JSContext* _cx = cx; - SlotList* globalSlots = treeInfo->globalSlots; + SlotList* globalSlots = tree->globalSlots; JSTraceMonitor* tm = traceMonitor; js_AbortRecording(cx, "trying to compile inner recursive tree"); if (RecordTree(_cx, tm, first, NULL, 0, first->globalObj, first->globalShape, @@ -9936,8 +9902,8 @@ TraceRecorder::record_JSOP_RETURN() { /* A return from callDepth 0 terminates the current loop, except for recursion. */ if (callDepth == 0) { - if (IsTraceableRecursion(cx) && treeInfo->recursion != Recursion_Disallowed && - treeInfo->script == cx->fp->script) { + if (IsTraceableRecursion(cx) && tree->recursion != Recursion_Disallowed && + tree->script == cx->fp->script) { return InjectStatus(upRecursion()); } else { AUDIT(returnLoopExits); @@ -10576,7 +10542,7 @@ TraceRecorder::emitNativeCall(JSSpecializedNative* sn, uintN argc, LIns* args[], JSObject* funobj = JSVAL_TO_OBJECT(stackval(0 - (2 + argc))); if (FUN_SLOW_NATIVE(GET_FUNCTION_PRIVATE(cx, funobj))) { exit->setNativeCallee(funobj, constructing); - treeInfo->gcthings.addUnique(OBJECT_TO_JSVAL(funobj)); + tree->gcthings.addUnique(OBJECT_TO_JSVAL(funobj)); } } @@ -12168,7 +12134,7 @@ TraceRecorder::guardCallee(jsval& callee) JSObject* callee_obj = JSVAL_TO_OBJECT(callee); LIns* callee_ins = get(&callee); - treeInfo->gcthings.addUnique(callee); + tree->gcthings.addUnique(callee); guard(true, lir->ins2(LIR_peq, stobj_get_private(callee_ins), @@ -12255,10 +12221,10 @@ TraceRecorder::interpretedFunctionCall(jsval& fval, JSFunction* fun, uintN argc, JS_ASSERT(argc < FrameInfo::CONSTRUCTING_FLAG); - treeInfo->gcthings.addUnique(fval); + tree->gcthings.addUnique(fval); fi->block = fp->blockChain; if (fp->blockChain) - treeInfo->gcthings.addUnique(OBJECT_TO_JSVAL(fp->blockChain)); + tree->gcthings.addUnique(OBJECT_TO_JSVAL(fp->blockChain)); fi->pc = fp->regs->pc; fi->imacpc = fp->imacpc; fi->spdist = fp->regs->sp - fp->slots; @@ -12266,8 +12232,8 @@ TraceRecorder::interpretedFunctionCall(jsval& fval, JSFunction* fun, uintN argc, fi->callerHeight = stackSlots - (2 + argc); fi->callerArgc = fp->argc; - if (callDepth >= treeInfo->maxCallDepth) - treeInfo->maxCallDepth = callDepth + 1; + if (callDepth >= tree->maxCallDepth) + tree->maxCallDepth = callDepth + 1; fi = traceMonitor->frameCache->memoize(fi); if (!fi) @@ -14804,24 +14770,22 @@ DumpPeerStability(JSTraceMonitor* tm, const void* ip, JSObject* globalObj, uint3 uint32 argc) { TreeFragment* f; - TreeInfo* ti; bool looped = false; unsigned length = 0; for (f = LookupLoop(tm, ip, globalObj, globalShape, argc); f != NULL; f = f->peer) { - if (!f->treeInfo) + if (!f->code()) continue; debug_only_printf(LC_TMRecorder, "Stability of fragment %p:\nENTRY STACK=", (void*)f); - ti = f->treeInfo; if (looped) - JS_ASSERT(ti->nStackTypes == length); - for (unsigned i = 0; i < ti->nStackTypes; i++) - debug_only_printf(LC_TMRecorder, "%c", typeChar[ti->stackTypeMap()[i]]); + JS_ASSERT(f->nStackTypes == length); + for (unsigned i = 0; i < f->nStackTypes; i++) + debug_only_printf(LC_TMRecorder, "%c", typeChar[f->stackTypeMap()[i]]); debug_only_print0(LC_TMRecorder, " GLOBALS="); - for (unsigned i = 0; i < ti->nGlobalTypes(); i++) - debug_only_printf(LC_TMRecorder, "%c", typeChar[ti->globalTypeMap()[i]]); + for (unsigned i = 0; i < f->nGlobalTypes(); i++) + debug_only_printf(LC_TMRecorder, "%c", typeChar[f->globalTypeMap()[i]]); debug_only_print0(LC_TMRecorder, "\n"); - UnstableExit* uexit = ti->unstableExits; + UnstableExit* uexit = f->unstableExits; while (uexit != NULL) { debug_only_print0(LC_TMRecorder, "EXIT "); JSTraceType* m = uexit->exit->fullTypeMap(); @@ -14836,7 +14800,7 @@ DumpPeerStability(JSTraceMonitor* tm, const void* ip, JSObject* globalObj, uint3 debug_only_print0(LC_TMRecorder, "\n"); uexit = uexit->next; } - length = ti->nStackTypes; + length = f->nStackTypes; looped = true; } } @@ -14928,7 +14892,7 @@ JS_REQUIRES_STACK void TraceRecorder::determineGlobalTypes(JSTraceType* typeMap) { DetermineTypesVisitor detVisitor(*this, typeMap); - VisitGlobalSlots(detVisitor, cx, *treeInfo->globalSlots); + VisitGlobalSlots(detVisitor, cx, *tree->globalSlots); } #include "jsrecursion.cpp" diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 830bf8338d8..04e6ee648bb 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -217,54 +217,6 @@ public: TreeFragment* toTreeFragment(); }; -struct LinkableFragment : public VMFragment -{ - LinkableFragment(const void* _ip verbose_only(, uint32_t profFragID)) - : VMFragment(_ip verbose_only(, profFragID)) - { } - - uint32 branchCount; -}; - -/* - * argc is cx->fp->argc at the trace loop header, i.e., the number of arguments - * pushed for the innermost JS frame. This is required as part of the fragment - * key because the fragment will write those arguments back to the interpreter - * stack when it exits, using its typemap, which implicitly incorporates a - * given value of argc. Without this feature, a fragment could be called as an - * inner tree with two different values of argc, and entry type checking or - * exit frame synthesis could crash. - */ -struct TreeFragment : public LinkableFragment -{ - TreeFragment(const void* _ip, JSObject* _globalObj, uint32 _globalShape, uint32 _argc - verbose_only(, uint32_t profFragID)) : - LinkableFragment(_ip verbose_only(, profFragID)), - treeInfo(NULL), - first(NULL), - next(NULL), - peer(NULL), - globalObj(_globalObj), - globalShape(_globalShape), - argc(_argc) - { } - - TreeInfo *treeInfo; - TreeFragment* first; - TreeFragment* next; - TreeFragment* peer; - JSObject* globalObj; - uint32 globalShape; - uint32 argc; -}; - -inline TreeFragment* -VMFragment::toTreeFragment() -{ - JS_ASSERT(root == this); - return static_cast(this); -} - #if defined(JS_JIT_SPEW) || defined(NJ_NO_VARIADIC_MACROS) enum LC_TMBits { @@ -633,8 +585,6 @@ struct REHashFn { } }; -class TreeInfo; - struct FrameInfo { JSObject* block; // caller block chain head jsbytecode* pc; // caller fp->regs->pc @@ -695,51 +645,71 @@ enum RecursionStatus Recursion_Detected /* Tree has down recursion and maybe up recursion. */ }; -class TreeInfo { -public: - TreeFragment* const rootFragment; - JSScript* script; - unsigned maxNativeStackSlots; - ptrdiff_t nativeStackBase; - unsigned maxCallDepth; +struct LinkableFragment : public VMFragment +{ + LinkableFragment(const void* _ip, nanojit::Allocator* alloc + verbose_only(, uint32_t profFragID)) + : VMFragment(_ip verbose_only(, profFragID)), typeMap(alloc), nStackTypes(0) + { } + + uint32 branchCount; TypeMap typeMap; unsigned nStackTypes; SlotList* globalSlots; +}; + +/* + * argc is cx->fp->argc at the trace loop header, i.e., the number of arguments + * pushed for the innermost JS frame. This is required as part of the fragment + * key because the fragment will write those arguments back to the interpreter + * stack when it exits, using its typemap, which implicitly incorporates a + * given value of argc. Without this feature, a fragment could be called as an + * inner tree with two different values of argc, and entry type checking or + * exit frame synthesis could crash. + */ +struct TreeFragment : public LinkableFragment +{ + TreeFragment(const void* _ip, nanojit::Allocator* alloc, JSObject* _globalObj, + uint32 _globalShape, uint32 _argc verbose_only(, uint32_t profFragID)): + LinkableFragment(_ip, alloc verbose_only(, profFragID)), + first(NULL), + next(NULL), + peer(NULL), + globalObj(_globalObj), + globalShape(_globalShape), + argc(_argc), + dependentTrees(alloc), + linkedTrees(alloc), + sideExits(alloc), + gcthings(alloc), + sprops(alloc) + { } + + TreeFragment* first; + TreeFragment* next; + TreeFragment* peer; + JSObject* globalObj; + uint32 globalShape; + uint32 argc; /* Dependent trees must be trashed if this tree dies, and updated on missing global types */ - Queue dependentTrees; + Queue dependentTrees; /* Linked trees must be updated on missing global types, but are not dependent */ - Queue linkedTrees; - Queue sideExits; - UnstableExit* unstableExits; - /* All embedded GC things are registered here so the GC can scan them. */ - Queue gcthings; - Queue sprops; + Queue linkedTrees; #ifdef DEBUG const char* treeFileName; uintN treeLineNumber; uintN treePCOffset; #endif + JSScript* script; RecursionStatus recursion; - - TreeInfo(nanojit::Allocator* alloc, - TreeFragment* fragment, - SlotList* globalSlots) - : rootFragment(fragment), - script(NULL), - maxNativeStackSlots(0), - nativeStackBase(0), - maxCallDepth(0), - typeMap(alloc), - nStackTypes(0), - globalSlots(globalSlots), - dependentTrees(alloc), - linkedTrees(alloc), - sideExits(alloc), - unstableExits(NULL), - gcthings(alloc), - sprops(alloc), - recursion(Recursion_None) - {} + UnstableExit* unstableExits; + Queue sideExits; + ptrdiff_t nativeStackBase; + unsigned maxCallDepth; + /* All embedded GC things are registered here so the GC can scan them. */ + Queue gcthings; + Queue sprops; + unsigned maxNativeStackSlots; inline unsigned nGlobalTypes() { return typeMap.length() - nStackTypes; @@ -750,13 +720,18 @@ public: inline JSTraceType* stackTypeMap() { return typeMap.data(); } - inline JSObject* globalObj() { - return rootFragment->globalObj; - } + JS_REQUIRES_STACK void initialize(JSContext* cx, SlotList *globalSlots); UnstableExit* removeUnstableExit(VMSideExit* exit); }; +inline TreeFragment* +VMFragment::toTreeFragment() +{ + JS_ASSERT(root == this); + return static_cast(this); +} + typedef enum JSBuiltinStatus { JSBUILTIN_BAILED = 1, JSBUILTIN_ERROR = 2 @@ -935,8 +910,8 @@ class TraceRecorder /* The Fragment being recorded by this recording session. */ VMFragment* const fragment; - /* The tree to which this |fragment| will belong when finished. */ - TreeInfo* const treeInfo; + /* The root fragment representing the tree. */ + TreeFragment* const tree; /* The reason we started recording. */ RecordReason const recordReason; @@ -1069,7 +1044,7 @@ class TraceRecorder JS_REQUIRES_STACK ptrdiff_t nativespOffset(jsval* p) const; JS_REQUIRES_STACK void import(nanojit::LIns* base, ptrdiff_t offset, jsval* p, JSTraceType t, const char *prefix, uintN index, JSStackFrame *fp); - JS_REQUIRES_STACK void import(TreeInfo* treeInfo, nanojit::LIns* sp, unsigned stackSlots, + JS_REQUIRES_STACK void import(TreeFragment* tree, nanojit::LIns* sp, unsigned stackSlots, unsigned callDepth, unsigned ngslots, JSTraceType* typeMap); void trackNativeStackUse(unsigned slots); @@ -1365,7 +1340,7 @@ class TraceRecorder inline void operator delete(void *p) { free(p); } JS_REQUIRES_STACK - TraceRecorder(JSContext* cx, VMSideExit*, VMFragment*, TreeInfo*, + TraceRecorder(JSContext* cx, VMSideExit*, VMFragment*, unsigned stackSlots, unsigned ngslots, JSTraceType* typeMap, VMSideExit* expectedInnerExit, jsbytecode* outerTree, uint32 outerArgc, RecordReason reason); @@ -1392,14 +1367,14 @@ class TraceRecorder public: static bool JS_REQUIRES_STACK - startRecorder(JSContext*, VMSideExit*, VMFragment*, TreeInfo*, + startRecorder(JSContext*, VMSideExit*, VMFragment*, unsigned stackSlots, unsigned ngslots, JSTraceType* typeMap, VMSideExit* expectedInnerExit, jsbytecode* outerTree, uint32 outerArgc, RecordReason reason); /* Accessors. */ VMFragment* getFragment() const { return fragment; } - TreeInfo* getTreeInfo() const { return treeInfo; } + TreeFragment* getTree() const { return tree; } bool outOfMemory() const { return traceMonitor->outOfMemory(); } /* Entry points / callbacks from the interpreter. */ From 51a679002f6fe805eebef32113805d7889f9e6f1 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 4 Dec 2009 23:05:47 -0800 Subject: [PATCH 17/82] Fix scope chain traversal with let blocks (bug 523793, r=dmandelin,mrbkap). --- js/src/jstracer.cpp | 73 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index f9b11cb9b2c..54d164cf227 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -13188,18 +13188,73 @@ TraceRecorder::record_JSOP_POPN() } /* - * Generate LIR to reach |obj2| from |obj| by traversing the scope chain. The generated code - * also ensures that any call objects found have not changed shape. + * Generate LIR to reach |obj2| from |obj| by traversing the scope chain. The + * generated code also ensures that any call objects found have not changed shape. * * obj starting object * obj_ins LIR instruction representing obj - * obj2 end object for traversal - * obj2_ins [out] LIR instruction representing obj2 + * targetObj end object for traversal + * targetIns [out] LIR instruction representing obj2 */ JS_REQUIRES_STACK RecordingStatus -TraceRecorder::traverseScopeChain(JSObject *obj, LIns *obj_ins, JSObject *obj2, LIns *&obj2_ins) +TraceRecorder::traverseScopeChain(JSObject *obj, LIns *obj_ins, JSObject *targetObj, + LIns *&targetIns) { VMSideExit* exit = NULL; + + /* + * Scope chains are often left "incomplete", and reified lazily when + * necessary, since doing so is expensive. When creating null and flat + * closures on trace (the only kinds supported), the global object is + * hardcoded as the parent, since reifying the scope chain on trace + * would be extremely difficult. This is because block objects need frame + * pointers, which do not exist on trace, and thus would require magic + * similar to arguments objects or reification of stack frames. Luckily, + * for null and flat closures, these blocks are unnecessary. + * + * The problem, as exposed by bug 523793, is that this means creating a + * fixed traversal on trace can be inconsistent with the shorter scope + * chain used when executing a trace. To address this, perform an initial + * sweep of the scope chain to make sure that if there is a heavyweight + * function with a call object, and there is also a block object, the + * trace is safely aborted. + * + * If there is no call object, we must have arrived at the global object, + * and can bypass the scope chain traversal completely. + */ + bool foundCallObj = false; + bool foundBlockObj = false; + JSObject* searchObj = obj; + + for (;;) { + if (searchObj != globalObj) { + JSClass* cls = STOBJ_GET_CLASS(searchObj); + if (cls == &js_BlockClass) { + foundBlockObj = true; + } else if (cls == &js_CallClass && + JSFUN_HEAVYWEIGHT_TEST(js_GetCallObjectFunction(searchObj)->flags)) { + foundCallObj = true; + } + } + + if (searchObj == targetObj) + break; + + searchObj = STOBJ_GET_PARENT(searchObj); + if (!searchObj) + RETURN_STOP("cannot traverse this scope chain on trace"); + } + + if (!foundCallObj) { + JS_ASSERT(targetObj == globalObj); + targetIns = INS_CONSTPTR(globalObj); + return RECORD_CONTINUE; + } + + if (foundBlockObj) + RETURN_STOP("cannot traverse this scope chain on trace"); + + /* There was a call object, or should be a call object now. */ for (;;) { if (obj != globalObj) { if (!js_IsCacheableNonGlobalScope(obj)) @@ -13221,16 +13276,16 @@ TraceRecorder::traverseScopeChain(JSObject *obj, LIns *obj_ins, JSObject *obj2, } } - if (obj == obj2) + JS_ASSERT(STOBJ_GET_CLASS(obj) != &js_BlockClass); + + if (obj == targetObj) break; obj = STOBJ_GET_PARENT(obj); - if (!obj) - RETURN_STOP("target object not reached on scope chain"); obj_ins = stobj_get_parent(obj_ins); } - obj2_ins = obj_ins; + targetIns = obj_ins; return RECORD_CONTINUE; } From 3ded1d4c80f6a44aca880082a4b20cc7e5409b27 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sat, 5 Dec 2009 12:46:46 -0800 Subject: [PATCH 18/82] Efficient Implementation of JSDOUBLE_IS_INT using SSE2 (original patch by Moh Haghighat/Intel, bug 530896, r=dvander). --- js/src/configure.in | 36 ++++++++++++++++++++++++++++++++++ js/src/jsapi.cpp | 3 +++ js/src/jsnum.h | 32 +++++++++++++++++++++++++++++- js/src/jstracer.cpp | 47 ++------------------------------------------- js/src/jsutil.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++++ js/src/jsutil.h | 11 +++++++++++ 6 files changed, 130 insertions(+), 46 deletions(-) diff --git a/js/src/configure.in b/js/src/configure.in index 3ffead04a3d..772573ce84a 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -2476,6 +2476,42 @@ if test -z "$COMPILE_ENVIRONMENT"; then SKIP_LIBRARY_CHECKS=1 fi +dnl Configure SSE2 support + +dnl Enable SSE2 support in the compiler if available. +dnl ======================================================== +case "$target" in +i?86-*|x86_64*-*) + if test "$GNU_CC"; then + CFLAGS="$CFLAGS -msse2" + fi + if test "$GNU_CXX"; then + CXXFLAGS="$CXXFLAGS -msse2" + fi + ;; +esac + +dnl If we have the proper include file, enable SSE2 support. +dnl ======================================================== +HAVE_SSE2= +AC_CHECK_HEADER(xmmintrin.h, HAVE_SSE2=1) +AC_CHECK_HEADER(emmintrin.h, HAVE_SSE2=1 && AC_DEFINE(USE_EMM_INTRIN)) + +if test "$HAVE_SSE2"; then + AC_DEFINE(HAVE_SSE2) + case "$target_os" in + darwin*) + ;; + *) + case "$target" in + i?86-*) + AC_DEFINE(MUST_DETECT_SSE2) + ;; + esac + ;; + esac +fi + dnl Configure JIT support case "$target" in diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 00de732cc37..8fc2d6c3eff 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -825,6 +825,9 @@ JSRuntime::~JSRuntime() JS_PUBLIC_API(JSRuntime *) JS_NewRuntime(uint32 maxbytes) { +#ifdef MUST_DETECT_SSE2 + js_use_SSE2 = js_DetectSSE2(); +#endif #ifdef DEBUG if (!js_NewRuntimeWasCalled) { /* diff --git a/js/src/jsnum.h b/js/src/jsnum.h index f3e7a253dfe..e64da4c6cea 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -47,6 +47,13 @@ #ifdef SOLARIS #include #endif +#ifdef HAVE_SSE2 +#ifdef USE_EMM_INTRIN +#include +#else +#include +#endif +#endif /* * JS number (IEEE double) interface. @@ -131,9 +138,32 @@ JSDOUBLE_IS_NEGZERO(jsdouble d) #define JSDOUBLE_HI32_EXPMASK 0x7ff00000 #define JSDOUBLE_HI32_MANTMASK 0x000fffff -static inline int +/* Older versions of MSVC don't have _mm_castpd_si128. */ +#if defined(HAVE_SSE2) && defined (_MSC_VER) && (_MSC_VER < 1500) +static inline __m128i +_mm_castpd_si128(__m128d v) { + return *(__m128i *)&v; +} +#endif + +static inline bool JSDOUBLE_IS_INT(jsdouble d, jsint& i) { +#ifdef HAVE_SSE2 + if (js_use_SSE2) { + __m128d xd = _mm_set_sd(d); /* load double into an XMM register */ + int ii = _mm_cvtsd_si32(xd); /* Inf/NaN & large |d| convert to -2^31 */ + __m128d xdi = _mm_setzero_pd(); /* kill dependencies for the top half of xdi */ + xdi = _mm_cvtsi32_sd(xdi, ii); /* convert the result back to double */ + __m128i xcmp = _mm_cmpeq_epi32(_mm_castpd_si128(xd), /* 32-bit integer bit-to-bit */ + _mm_castpd_si128(xdi)); /* comparison */ + int m = _mm_movemask_epi8(xcmp); /* extract significant bits of compare */ + if ((m & 0xff) != 0xff) /* result is non-integer? */ + return false; + i = ii; + return true; + } +#endif if (JSDOUBLE_IS_NEGZERO(d)) return false; return d == (i = jsint(d)); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 54d164cf227..5515a60a137 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -74,6 +74,7 @@ #include "jsscript.h" #include "jsstaticcheck.h" #include "jstracer.h" +#include "jsutil.h" #include "jsxml.h" #include "jsatominlines.h" @@ -7200,50 +7201,6 @@ js_AbortRecording(JSContext* cx, const char* reason) #endif } -#if defined NANOJIT_IA32 -static bool -CheckForSSE2() -{ - char *c = getenv("X86_FORCE_SSE2"); - if (c) - return (!strcmp(c, "true") || - !strcmp(c, "1") || - !strcmp(c, "yes")); - - int features = 0; -#if defined _MSC_VER - __asm - { - pushad - mov eax, 1 - cpuid - mov features, edx - popad - } -#elif defined __GNUC__ - asm("xchg %%esi, %%ebx\n" /* we can't clobber ebx on gcc (PIC register) */ - "mov $0x01, %%eax\n" - "cpuid\n" - "mov %%edx, %0\n" - "xchg %%esi, %%ebx\n" - : "=m" (features) - : /* We have no inputs */ - : "%eax", "%esi", "%ecx", "%edx" - ); -#elif defined __SUNPRO_C || defined __SUNPRO_CC - asm("push %%ebx\n" - "mov $0x01, %%eax\n" - "cpuid\n" - "pop %%ebx\n" - : "=d" (features) - : /* We have no inputs */ - : "%eax", "%ecx" - ); -#endif - return (features & (1<<26)) != 0; -} -#endif - #if defined(NANOJIT_ARM) #if defined(_MSC_VER) && defined(WINCE) @@ -7505,7 +7462,7 @@ js_InitJIT(JSTraceMonitor *tm) if (!did_we_check_processor_features) { #if defined NANOJIT_IA32 avmplus::AvmCore::config.use_cmov = - avmplus::AvmCore::config.sse2 = CheckForSSE2(); + avmplus::AvmCore::config.sse2 = js_use_SSE2; avmplus::AvmCore::config.fixed_esp = true; #endif #if defined NANOJIT_ARM diff --git a/js/src/jsutil.cpp b/js/src/jsutil.cpp index 0394996eb0c..774522c8bad 100644 --- a/js/src/jsutil.cpp +++ b/js/src/jsutil.cpp @@ -43,6 +43,7 @@ */ #include #include +#include #include "jstypes.h" #include "jsstdint.h" #include "jsutil.h" @@ -71,6 +72,52 @@ JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) #endif } +#ifdef MUST_DETECT_SSE2 +bool js_use_SSE2; + +bool +js_DetectSSE2() +{ + char *c = getenv("X86_FORCE_SSE2"); + if (c) + return (!strcmp(c, "true") || + !strcmp(c, "1") || + !strcmp(c, "yes")); + + int features = 0; +#if defined _MSC_VER + __asm + { + pushad + mov eax, 1 + cpuid + mov features, edx + popad + } +#elif defined __GNUC__ + asm("xchg %%esi, %%ebx\n" /* we can't clobber ebx on gcc (PIC register) */ + "mov $0x01, %%eax\n" + "cpuid\n" + "mov %%edx, %0\n" + "xchg %%esi, %%ebx\n" + : "=m" (features) + : /* We have no inputs */ + : "%eax", "%esi", "%ecx", "%edx" + ); +#elif defined __SUNPRO_C || defined __SUNPRO_CC + asm("push %%ebx\n" + "mov $0x01, %%eax\n" + "cpuid\n" + "pop %%ebx\n" + : "=d" (features) + : /* We have no inputs */ + : "%eax", "%ecx" + ); +#endif + return (features & (1<<26)) != 0; +} +#endif + #ifdef JS_BASIC_STATS #include diff --git a/js/src/jsutil.h b/js/src/jsutil.h index 08a31eee4bf..ad8fa2e4eb2 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -74,6 +74,17 @@ JS_Assert(const char *s, const char *file, JSIntn ln); #endif /* defined(DEBUG) */ +#ifdef HAVE_SSE2 +#ifdef MUST_DETECT_SSE2 +extern bool js_use_SSE2; + +bool +js_DetectSSE2(); +#else +static const bool js_use_SSE2 = true; +#endif +#endif + /* * Compile-time assert. "cond" must be a constant expression. * The macro can be used only in places where an "extern" declaration is From 97dc7a91741a59e18ddf75e3489cd459d8f3e120 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Sat, 5 Dec 2009 16:53:16 -0800 Subject: [PATCH 19/82] Try to fix autoconf madness for bug 530896. --- js/src/configure.in | 47 +++++++++++++++++++++------------------------ js/src/jsnum.h | 6 +++--- 2 files changed, 25 insertions(+), 28 deletions(-) diff --git a/js/src/configure.in b/js/src/configure.in index 772573ce84a..16d911af87a 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -2478,38 +2478,35 @@ fi dnl Configure SSE2 support -dnl Enable SSE2 support in the compiler if available. -dnl ======================================================== +HAVE_SSE2= +MUST_DETECT_SSE2= case "$target" in -i?86-*|x86_64*-*) +i?86-*) + HAVE_SSE2=1 + case "$target_os" in + darwin*) + ;; + *) + MUST_DETECT_SSE2=1 + ;; + esac + ;; +x86_64*-*)e + HAVE_SSE2=1 + ;; +esac + +if test "$HAVE_SSE2"; then + AC_DEFINE(HAVE_SSE2) if test "$GNU_CC"; then CFLAGS="$CFLAGS -msse2" fi if test "$GNU_CXX"; then CXXFLAGS="$CXXFLAGS -msse2" fi - ;; -esac - -dnl If we have the proper include file, enable SSE2 support. -dnl ======================================================== -HAVE_SSE2= -AC_CHECK_HEADER(xmmintrin.h, HAVE_SSE2=1) -AC_CHECK_HEADER(emmintrin.h, HAVE_SSE2=1 && AC_DEFINE(USE_EMM_INTRIN)) - -if test "$HAVE_SSE2"; then - AC_DEFINE(HAVE_SSE2) - case "$target_os" in - darwin*) - ;; - *) - case "$target" in - i?86-*) - AC_DEFINE(MUST_DETECT_SSE2) - ;; - esac - ;; - esac + if test "$MUST_DETECT_SSE2"; then + AC_DEFINE(MUST_DETECT_SSE2) + fi fi dnl Configure JIT support diff --git a/js/src/jsnum.h b/js/src/jsnum.h index e64da4c6cea..69f621e3c80 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -48,10 +48,10 @@ #include #endif #ifdef HAVE_SSE2 -#ifdef USE_EMM_INTRIN -#include -#else +#ifdef __GNUC__ #include +#else +#include #endif #endif From 15703de7bb6552ba4405e6bb63651b56bb205c76 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 6 Dec 2009 15:36:37 -0800 Subject: [PATCH 20/82] Update nanojit-import-rev stamp. --- js/src/nanojit-import-rev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/nanojit-import-rev b/js/src/nanojit-import-rev index bb6cc345464..7c484cf176e 100644 --- a/js/src/nanojit-import-rev +++ b/js/src/nanojit-import-rev @@ -1 +1 @@ -23ed78f42df2b7b1a590fc7e986e6d446ef4d3d4 +cb855a65f046a59c28277766aa5d320df33159c2 From 03809b83a99e5785f5379a6191f0511183d97955 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 7 Dec 2009 21:06:41 -0800 Subject: [PATCH 21/82] Bug 528857 - nanojit: mismanagement of name lifetimes with TMFLAGS=assembly? r=graydon. --- js/src/jscntxt.h | 55 ++++++++++++++++++++++----------------------- js/src/jsregexp.cpp | 16 +++++++++---- js/src/jstracer.cpp | 21 ++++++++--------- 3 files changed, 48 insertions(+), 44 deletions(-) diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 5618301244d..df2d322c5d1 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -95,10 +95,6 @@ namespace nanojit class Assembler; class CodeAlloc; class Fragment; - class LirBuffer; -#ifdef DEBUG - class LabelMap; -#endif template struct DefaultHash; template class HashMap; template class Seq; @@ -221,32 +217,40 @@ struct JSTraceMonitor { TraceNativeStorage storage; /* - * There are 3 allocators here. This might seem like overkill, but they + * There are 5 allocators here. This might seem like overkill, but they * have different lifecycles, and by keeping them separate we keep the - * amount of retained memory down significantly. + * amount of retained memory down significantly. They are flushed (ie. + * all the allocated memory is freed) periodically. * - * The dataAlloc has the lifecycle of the monitor. It's flushed only - * when the monitor is flushed. + * - dataAlloc has the lifecycle of the monitor. It's flushed only when + * the monitor is flushed. It's used for fragments. * - * The traceAlloc has the same flush lifecycle as the dataAlloc, but - * it is also *marked* when a recording starts and rewinds to the mark - * point if recording aborts. So you can put things in it that are only - * reachable on a successful record/compile cycle. + * - traceAlloc has the same flush lifecycle as the dataAlloc, but it is + * also *marked* when a recording starts and rewinds to the mark point + * if recording aborts. So you can put things in it that are only + * reachable on a successful record/compile cycle like GuardRecords and + * SideExits. * - * The tempAlloc is flushed after each recording, successful or not. + * - tempAlloc is flushed after each recording, successful or not. It's + * used to store LIR code and for all other elements in the LIR + * pipeline. + * + * - reTempAlloc is just like tempAlloc, but is used for regexp + * compilation in RegExpNativeCompiler rather than normal compilation in + * TraceRecorder. + * + * - codeAlloc has the same lifetime as dataAlloc, but its API is + * different (CodeAlloc vs. VMAllocator). It's used for native code. + * It's also a good idea to keep code and data separate to avoid I-cache + * vs. D-cache issues. */ - - VMAllocator* dataAlloc; /* A chunk allocator for fragments. */ - VMAllocator* traceAlloc; /* An allocator for trace metadata. */ - VMAllocator* tempAlloc; /* A temporary chunk allocator. */ - nanojit::CodeAlloc* codeAlloc; /* An allocator for native code. */ + VMAllocator* dataAlloc; + VMAllocator* traceAlloc; + VMAllocator* tempAlloc; + VMAllocator* reTempAlloc; + nanojit::CodeAlloc* codeAlloc; nanojit::Assembler* assembler; - nanojit::LirBuffer* lirbuf; - nanojit::LirBuffer* reLirBuf; FrameInfoCache* frameCache; -#ifdef DEBUG - nanojit::LabelMap* labels; -#endif TraceRecorder* recorder; @@ -279,11 +283,6 @@ struct JSTraceMonitor { */ REHashMap* reFragments; - /* - * A temporary allocator for RE recording. - */ - VMAllocator* reTempAlloc; - #ifdef DEBUG /* Fields needed for fragment/guard profiling. */ nanojit::Seq* branches; diff --git a/js/src/jsregexp.cpp b/js/src/jsregexp.cpp index 57a1ad98544..f641792e886 100644 --- a/js/src/jsregexp.cpp +++ b/js/src/jsregexp.cpp @@ -2026,7 +2026,6 @@ LookupNativeRegExp(JSContext* cx, uint16 re_flags, ? (++(tm->lastFragID)) : 0; ) frag = new (alloc) REFragment(0 verbose_only(, profFragID)); - frag->lirbuf = tm->reLirBuf; /* * Copy the re_chars portion of the hash key into the Allocator, so * its lifecycle is disconnected from the lifecycle of the @@ -2304,6 +2303,8 @@ class RegExpNativeCompiler { LIns* start; LIns* cpend; + LirBuffer* const lirbuf; + bool outOfMemory() { return tempAlloc.outOfMemory() || JS_TRACE_MONITOR(cx).dataAlloc->outOfMemory(); } @@ -3134,12 +3135,19 @@ class RegExpNativeCompiler { public: RegExpNativeCompiler(JSContext* cx, JSRegExp* re, CompilerState* cs, Fragment* fragment) : tempAlloc(*JS_TRACE_MONITOR(cx).reTempAlloc), cx(cx), - re(re), cs(cs), fragment(fragment), lir(NULL), lirBufWriter(NULL) { } + re(re), cs(cs), fragment(fragment), lir(NULL), lirBufWriter(NULL), + lirbuf(new (tempAlloc) LirBuffer(tempAlloc)) + { + fragment->lirbuf = lirbuf; +#ifdef DEBUG + LabelMap* labels = new (tempAlloc) LabelMap(tempAlloc, &js_LogController); + lirbuf->names = new (tempAlloc) LirNameMap(tempAlloc, labels); +#endif + } ~RegExpNativeCompiler() { /* Purge the tempAlloc used during recording. */ tempAlloc.reset(); - JS_TRACE_MONITOR(cx).reLirBuf->clear(); } JSBool compile() @@ -3235,7 +3243,7 @@ class RegExpNativeCompiler { */ JS_ASSERT(!lirbuf->sp && !lirbuf->rp); - ::compile(assm, fragment, tempAlloc verbose_only(, tm->labels)); + ::compile(assm, fragment, tempAlloc verbose_only(, lirbuf->names->labels)); if (assm->error() != nanojit::None) goto fail; diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 5515a60a137..992900df000 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -2360,7 +2360,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag eos_ins(NULL), eor_ins(NULL), loopLabel(NULL), - lirbuf(traceMonitor->lirbuf), + lirbuf(new (tempAlloc()) LirBuffer(tempAlloc())), mark(*traceMonitor->traceAlloc), numSideExitsBefore(tree->sideExits.length()), tracker(), @@ -2384,6 +2384,12 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag JS_ASSERT(globalObj == JS_GetGlobalForObject(cx, cx->fp->scopeChain)); JS_ASSERT(cx->fp->regs->pc == (jsbytecode*)fragment->ip); + fragment->lirbuf = lirbuf; +#ifdef DEBUG + LabelMap* labels = new (tempAlloc()) LabelMap(tempAlloc(), &js_LogController); + lirbuf->names = new (tempAlloc()) LirNameMap(tempAlloc(), labels); +#endif + /* * Reset the fragment state we care about in case we got a recycled * fragment. This includes resetting any profiling data we might have @@ -2520,7 +2526,6 @@ TraceRecorder::~TraceRecorder() /* Purge the tempAlloc used during recording. */ tempAlloc().reset(); - traceMonitor->lirbuf->clear(); forgetGuardedShapes(); } @@ -2853,16 +2858,8 @@ JSTraceMonitor::flush() } assembler = new (alloc) Assembler(*codeAlloc, alloc, alloc, core, &js_LogController); - lirbuf = new (alloc) LirBuffer(*tempAlloc); - reLirBuf = new (alloc) LirBuffer(*reTempAlloc); verbose_only( branches = NULL; ) -#ifdef DEBUG - labels = new (alloc) LabelMap(alloc, &js_LogController); - reLirBuf->names = - lirbuf->names = new (alloc) LirNameMap(alloc, labels); -#endif - memset(&vmfragments[0], 0, FRAGMENT_TABLE_SIZE * sizeof(TreeFragment*)); reFragments = new (alloc) REHashMap(alloc); @@ -4361,13 +4358,13 @@ TraceRecorder::compile() char* label = (char*)js_malloc((filename ? strlen(filename) : 7) + 16); sprintf(label, "%s:%u", filename ? filename : "", js_FramePCToLineNumber(cx, cx->fp)); - traceMonitor->labels->add(fragment, sizeof(Fragment), 0, label); + lirbuf->names->labels->add(fragment, sizeof(Fragment), 0, label); js_free(label); #endif Assembler *assm = traceMonitor->assembler; JS_ASSERT(assm->error() == nanojit::None); - nanojit::compile(assm, fragment, tempAlloc() verbose_only(, traceMonitor->labels)); + nanojit::compile(assm, fragment, tempAlloc() verbose_only(, lirbuf->names->labels)); if (assm->error() != nanojit::None) { assm->setError(nanojit::None); From e32598ecebf2bc7c5a8ae01c919915a5dc1b61af Mon Sep 17 00:00:00 2001 From: Jacob Bramley Date: Tue, 8 Dec 2009 13:15:50 +0000 Subject: [PATCH 22/82] Bug 521161: Re-enable LIR_mul speculation for ARM. (r=gal) --- js/src/jstracer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 992900df000..ed5f79f316e 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -8107,13 +8107,11 @@ TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1) case LIR_fsub: r = v0 - v1; break; -#if !defined NANOJIT_ARM case LIR_fmul: r = v0 * v1; if (r == 0.0) goto out; break; -#endif #if defined NANOJIT_IA32 || defined NANOJIT_X64 case LIR_fdiv: if (v1 == 0) From 771c4639e97033041bf02fe308c73b49dccc67f8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 8 Dec 2009 08:04:57 -0800 Subject: [PATCH 23/82] Bug 533233 - nanojit: get rid of LC_NoCodeAddrs (TM-specific part). r=edwsmith. --- js/src/jstracer.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index ed5f79f316e..7a901088a9b 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -383,7 +383,7 @@ InitJITLogController() " help show this message\n" " ------ options for jstracer & jsregexp ------\n" " minimal ultra-minimalist output; try this first\n" - " full everything except 'treevis' and 'nocodeaddrs'\n" + " full everything except 'treevis' and 'fragprofile'\n" " tracer tracer lifetime (FIXME:better description)\n" " recorder trace recording stuff (FIXME:better description)\n" " abort show trace recording aborts\n" @@ -398,7 +398,6 @@ InitJITLogController() " assembly show final aggregated assembly code\n" " regalloc show regalloc state in 'assembly' output\n" " activation show activation state in 'assembly' output\n" - " nocodeaddrs omit code addresses in 'assembly' output\n" "\n" ); exit(0); @@ -424,7 +423,6 @@ InitJITLogController() if (strstr(tmf, "aftersf") || strstr(tmf, "full")) bits |= LC_AfterSF; if (strstr(tmf, "regalloc") || strstr(tmf, "full")) bits |= LC_RegAlloc; if (strstr(tmf, "assembly") || strstr(tmf, "full")) bits |= LC_Assembly; - if (strstr(tmf, "nocodeaddrs")) bits |= LC_NoCodeAddrs; js_LogController.lcbits = bits; return; From d8eaacf13ff1ebf7eb463471ee0531fa5b510a0c Mon Sep 17 00:00:00 2001 From: David Anderson Date: Tue, 8 Dec 2009 11:15:34 -0800 Subject: [PATCH 24/82] Fixed loops ending in JSOP_GOTO not compiling properly (bug 533042, r=gal). --- js/src/jstracer.cpp | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 7a901088a9b..7a154d8188c 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -6904,28 +6904,31 @@ js_MonitorLoopEdge(JSContext* cx, uintN& inlineCallCount, RecordReason reason) /* Is the recorder currently active? */ if (tm->recorder) { - jsbytecode* innerLoopHeaderPC = cx->fp->regs->pc; + jsbytecode* pc = cx->fp->regs->pc; + if (pc == tm->recorder->tree->ip) { + tm->recorder->closeLoop(); + } else { + if (TraceRecorder::recordLoopEdge(cx, tm->recorder, inlineCallCount)) + return true; - if (TraceRecorder::recordLoopEdge(cx, tm->recorder, inlineCallCount)) - return true; - - /* - * recordLoopEdge will invoke an inner tree if we have a matching - * one. If we arrive here, that tree didn't run to completion and - * instead we mis-matched or the inner tree took a side exit other than - * the loop exit. We are thus no longer guaranteed to be parked on the - * same loop header js_MonitorLoopEdge was called for. In fact, this - * might not even be a loop header at all. Hence if the program counter - * no longer hovers over the inner loop header, return to the - * interpreter and do not attempt to trigger or record a new tree at - * this location. - */ - if (innerLoopHeaderPC != cx->fp->regs->pc) { + /* + * recordLoopEdge will invoke an inner tree if we have a matching + * one. If we arrive here, that tree didn't run to completion and + * instead we mis-matched or the inner tree took a side exit other than + * the loop exit. We are thus no longer guaranteed to be parked on the + * same loop header js_MonitorLoopEdge was called for. In fact, this + * might not even be a loop header at all. Hence if the program counter + * no longer hovers over the inner loop header, return to the + * interpreter and do not attempt to trigger or record a new tree at + * this location. + */ + if (pc != cx->fp->regs->pc) { #ifdef MOZ_TRACEVIS - tvso.r = R_INNER_SIDE_EXIT; + tvso.r = R_INNER_SIDE_EXIT; #endif - return false; - } + return false; + } + } } JS_ASSERT(!tm->recorder); From c8424c2c679b92d53e46c13176f9d63d230d215f Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Tue, 8 Dec 2009 16:20:00 -0800 Subject: [PATCH 25/82] Fix JSOP_INITMETHOD to use correct JSScope method (addProperty, not addDataProperty; 533254, r=jorendorff). --- js/src/jsops.cpp | 3 +- js/src/tests/ecma_3/FunExpr/jstests.list | 1 + js/src/tests/ecma_3/FunExpr/regress-533254.js | 29 +++++++++++++++++++ 3 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 js/src/tests/ecma_3/FunExpr/regress-533254.js diff --git a/js/src/jsops.cpp b/js/src/jsops.cpp index 997bc9e542c..89790c1fb8f 100644 --- a/js/src/jsops.cpp +++ b/js/src/jsops.cpp @@ -3566,7 +3566,8 @@ BEGIN_CASE(JSOP_INITMETHOD) scope->shape == scope->lastProperty()->shape); if (scope->table) { JSScopeProperty *sprop2 = - scope->addDataProperty(cx, sprop->id, slot, sprop->attrs); + scope->addProperty(cx, sprop->id, sprop->getter, sprop->setter, slot, + sprop->attrs, sprop->flags, sprop->shortid); if (!sprop2) { js_FreeSlot(cx, obj, slot); JS_UNLOCK_SCOPE(cx, scope); diff --git a/js/src/tests/ecma_3/FunExpr/jstests.list b/js/src/tests/ecma_3/FunExpr/jstests.list index 7ca17f92819..f1b78e5e0d1 100644 --- a/js/src/tests/ecma_3/FunExpr/jstests.list +++ b/js/src/tests/ecma_3/FunExpr/jstests.list @@ -5,3 +5,4 @@ script fe-002.js script regress-518103.js script regress-524826.js script regress-528082.js +script regress-533254.js diff --git a/js/src/tests/ecma_3/FunExpr/regress-533254.js b/js/src/tests/ecma_3/FunExpr/regress-533254.js new file mode 100644 index 00000000000..801c6841f57 --- /dev/null +++ b/js/src/tests/ecma_3/FunExpr/regress-533254.js @@ -0,0 +1,29 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +var gTestfile = 'regress-533254.js'; +var BUGNUMBER = 533254; +var summary = 'init-method late in table-big initialiser screwup'; + +printBugNumber(BUGNUMBER); +printStatus(summary); + +function f() { + var proto = {p8:8}; + var obj = { + p0:0, p1:1, p2:2, p3:3, p4:4, p5:5, p6:6, p7:7, p8:8, p9:9, + p10:0, p11:1, p12:2, p13:3, p14:4, p15:5, p16:6, p17:7, p18:8, p19:9, + m: function() { return 42; } + }; + return obj; +} +var expect = f(), + actual = f(); + +expect += ''; +actual += ''; +reportCompare(expect, actual, summary); + +printStatus("All tests passed!"); From 6af77f03b533c11c9809ab5ce95091f685bb1dd7 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 8 Dec 2009 21:56:54 -0800 Subject: [PATCH 26/82] Bug 533148. Go back to the MAX_LENGTH we used to have. r=brendan --- js/src/jsstr.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/js/src/jsstr.h b/js/src/jsstr.h index f031a3d87e3..f674dda164b 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -133,8 +133,11 @@ struct JSString { } public: - /* Generous but sane length bound. */ - static const size_t MAX_LENGTH = (1 << 28); + /* + * Generous but sane length bound; the "-1" is there for comptibility with + * OOM tests. + */ + static const size_t MAX_LENGTH = (1 << 28) - 1; bool isDependent() const { return hasFlag(DEPENDENT); From f9b73e0ae4aa5f772171708d8a71bdabc886e46b Mon Sep 17 00:00:00 2001 From: Bob Clary Date: Wed, 9 Dec 2009 14:41:34 -0800 Subject: [PATCH 27/82] bug 496127 - relax timing comparison to eliminate random failures in js1_8_1/trace/regress-452498-01.js. --- js/src/tests/js1_8_1/trace/regress-452498-01.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/tests/js1_8_1/trace/regress-452498-01.js b/js/src/tests/js1_8_1/trace/regress-452498-01.js index 59a04c8434c..862c3dc004b 100644 --- a/js/src/tests/js1_8_1/trace/regress-452498-01.js +++ b/js/src/tests/js1_8_1/trace/regress-452498-01.js @@ -99,7 +99,7 @@ var timenonjit = f(false); var timejit = f(true); expect = true; -actual = timejit < timenonjit/2; +actual = timejit < timenonjit; print('time nonjit: ' + timenonjit + ', time jit: ' + timejit); From 91997361f19e795342a2d4c1ba0e255472df04e3 Mon Sep 17 00:00:00 2001 From: Bob Clary Date: Wed, 9 Dec 2009 14:41:34 -0800 Subject: [PATCH 28/82] bug 528717 - relax timing comparison to eliminate random failures in js1_8_1/trace/regress-470739.js. --- js/src/tests/js1_8_1/trace/regress-470739.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/tests/js1_8_1/trace/regress-470739.js b/js/src/tests/js1_8_1/trace/regress-470739.js index 0ad2b9a2f5b..f48c66c1ae1 100644 --- a/js/src/tests/js1_8_1/trace/regress-470739.js +++ b/js/src/tests/js1_8_1/trace/regress-470739.js @@ -73,7 +73,7 @@ function test() print('time: nonjit = ' + timenonjit + ', jit = ' + timejit); expect = true; - actual = timejit < timenonjit/2; + actual = timejit < timenonjit; reportCompare(expect, actual, summary); From 1c605bec56e024c029ba1f61cc262358defd8e0c Mon Sep 17 00:00:00 2001 From: Bob Clary Date: Wed, 9 Dec 2009 14:41:35 -0800 Subject: [PATCH 29/82] bug 531325 - relax timing comparison to eliminate random failures in js1_8_1/trace/regress-469927.js. --- js/src/tests/js1_8_1/trace/regress-469927.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/tests/js1_8_1/trace/regress-469927.js b/js/src/tests/js1_8_1/trace/regress-469927.js index 1c3579a2bac..4d0651f0dbd 100644 --- a/js/src/tests/js1_8_1/trace/regress-469927.js +++ b/js/src/tests/js1_8_1/trace/regress-469927.js @@ -71,7 +71,7 @@ function test() print('time: nonjit = ' + timenonjit + ', jit = ' + timejit); expect = true; - actual = timejit < timenonjit/2; + actual = timejit < timenonjit; reportCompare(expect, actual, summary); From f8930f33b8a3cd9c0ee90a4d5e8ba7a18f832839 Mon Sep 17 00:00:00 2001 From: Bob Clary Date: Wed, 9 Dec 2009 14:41:35 -0800 Subject: [PATCH 30/82] bug 533027 - skip js1_5/Regress/regress-3649-n.js due to random oom related failures. --- js/src/tests/js1_5/Regress/jstests.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/tests/js1_5/Regress/jstests.list b/js/src/tests/js1_5/Regress/jstests.list index 5e967d21d29..29208f6c5fc 100644 --- a/js/src/tests/js1_5/Regress/jstests.list +++ b/js/src/tests/js1_5/Regress/jstests.list @@ -209,7 +209,7 @@ script regress-360969-06.js script regress-361467.js script regress-361617.js skip script regress-362583.js # obsolete test -fails-if(xulRuntime.OS=="WINNT") random-if(xulRuntime.OS=="Linux"&&!xulRuntime.XPCOMABI.match(/x86_64/)) skip-if(xulRuntime.OS=="Linux"&&xulRuntime.XPCOMABI.match(/x86_64/)) script regress-3649-n.js # No test results on windows, sometimes no test results on 32 bit linux, hangs os/consumes ram/swap on 64bit linux. +skip script regress-3649-n.js # skip test due to random oom related errors. script regress-366122.js script regress-366468.js script regress-366601.js From 814fcf00f1ec0489f73119667e39f7ede77e0ec8 Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Mon, 7 Dec 2009 11:19:24 -0800 Subject: [PATCH 31/82] nanojit/LIR.cpp: LoadFilter should handle the new load instructions (r=rreitmai, r=nnethercote, bug=533015) --HG-- extra : convert_revision : 6069a35f1fcfcc3bcaf15ddaece24b80d8955db4 --- js/src/nanojit/LIR.cpp | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/js/src/nanojit/LIR.cpp b/js/src/nanojit/LIR.cpp index 715f5ed32ee..7b0fc894e24 100644 --- a/js/src/nanojit/LIR.cpp +++ b/js/src/nanojit/LIR.cpp @@ -2203,12 +2203,28 @@ namespace nanojit LInsp LoadFilter::insLoad(LOpcode v, LInsp base, int32_t disp) { - if (base != sp && base != rp && (v == LIR_ld || v == LIR_ldq)) { - uint32_t k; - LInsp ins = exprs->findLoad(v, base, disp, k); - if (ins) - return ins; - return exprs->add(LInsLoad, out->insLoad(v,base,disp), k); + if (base != sp && base != rp) + { + switch (v) + { + case LIR_ld: + case LIR_ldq: + case LIR_ld32f: + case LIR_ldsb: + case LIR_ldss: + case LIR_ldzb: + case LIR_ldzs: + { + uint32_t k; + LInsp ins = exprs->findLoad(v, base, disp, k); + if (ins) + return ins; + return exprs->add(LInsLoad, out->insLoad(v,base,disp), k); + } + default: + // fall thru + break; + } } return out->insLoad(v, base, disp); } From 51c20028df243f3e964f3f4bcbd7ea55bdb61990 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 8 Dec 2009 07:57:17 -0800 Subject: [PATCH 32/82] Bug 533233 - nanojit: get rid of LC_NoCodeAddrs. r=edwsmith. --HG-- extra : convert_revision : 5edcef2131dfd0f521b306aee9a40a51b487efb9 --- js/src/nanojit/Assembler.cpp | 5 +---- js/src/nanojit/Assembler.h | 3 --- js/src/nanojit/LIR.cpp | 10 ++-------- js/src/nanojit/Native.h | 9 ++------- js/src/nanojit/NativeX64.cpp | 2 +- js/src/nanojit/Nativei386.cpp | 2 +- js/src/nanojit/nanojit.h | 15 +++++++-------- 7 files changed, 14 insertions(+), 32 deletions(-) diff --git a/js/src/nanojit/Assembler.cpp b/js/src/nanojit/Assembler.cpp index 28576521d08..b40389e6588 100755 --- a/js/src/nanojit/Assembler.cpp +++ b/js/src/nanojit/Assembler.cpp @@ -83,7 +83,6 @@ namespace nanojit verbose_only( _outputCache = 0; ) verbose_only( outline[0] = '\0'; ) verbose_only( outlineEOL[0] = '\0'; ) - verbose_only( outputAddr = false; ) reset(); } @@ -777,7 +776,6 @@ namespace nanojit } NIns* fragEntry = genPrologue(); - verbose_only( outputAddr=true; ) verbose_only( asm_output("[prologue]"); ) // check for resource leaks @@ -1335,8 +1333,7 @@ namespace nanojit label->addr = _nIns; } verbose_only( if (_logc->lcbits & LC_Assembly) { - outputAddr=true; asm_output("[%s]", - _thisfrag->lirbuf->names->formatRef(ins)); + asm_output("[%s]", _thisfrag->lirbuf->names->formatRef(ins)); }) break; } diff --git a/js/src/nanojit/Assembler.h b/js/src/nanojit/Assembler.h index 29154469cd0..c6cbeb9df95 100644 --- a/js/src/nanojit/Assembler.h +++ b/js/src/nanojit/Assembler.h @@ -207,9 +207,6 @@ namespace nanojit // Buffer used to hold extra text to be printed at the end of some // lines. static char outlineEOL[512]; - // If outputAddr=true the next asm instruction output will - // be prepended with its address. - bool outputAddr, vpad[3]; // Outputs 'outline' and 'outlineEOL', and resets them both. // Output goes to '_outputCache' if it's non-NULL, or is printed diff --git a/js/src/nanojit/LIR.cpp b/js/src/nanojit/LIR.cpp index 7b0fc894e24..64e96b3c122 100644 --- a/js/src/nanojit/LIR.cpp +++ b/js/src/nanojit/LIR.cpp @@ -2282,18 +2282,12 @@ namespace nanojit const void *end = (const char*)start + e->size; const char *name = e->name; if (p == start) { - if (!(logc->lcbits & LC_NoCodeAddrs)) - VMPI_sprintf(b,"%p %s",p,name); - else - VMPI_strcpy(b, name); + VMPI_sprintf(b,"%p %s",p,name); return dup(b); } else if (p > start && p < end) { int32_t d = int32_t(intptr_t(p)-intptr_t(start)) >> e->align; - if (!(logc->lcbits & LC_NoCodeAddrs)) - VMPI_sprintf(b, "%p %s+%d", p, name, d); - else - VMPI_sprintf(b,"%s+%d", name, d); + VMPI_sprintf(b, "%p %s+%d", p, name, d); return dup(b); } else { diff --git a/js/src/nanojit/Native.h b/js/src/nanojit/Native.h index d6268a36bdb..39712bd5efc 100644 --- a/js/src/nanojit/Native.h +++ b/js/src/nanojit/Native.h @@ -187,19 +187,14 @@ namespace nanojit { #elif defined(NJ_VERBOSE) // Used for printing native instructions. Like Assembler::outputf(), // but only outputs if LC_Assembly is set. Also prepends the output - // with the address of the current native instruction if - // LC_NoCodeAddrs is not set. + // with the address of the current native instruction. #define asm_output(...) do { \ counter_increment(native); \ if (_logc->lcbits & LC_Assembly) { \ outline[0]='\0'; \ - if (outputAddr) \ - VMPI_sprintf(outline, "%010lx ", (unsigned long)_nIns); \ - else \ - VMPI_memset(outline, (int)' ', 10+3); \ + VMPI_sprintf(outline, "%010lx ", (unsigned long)_nIns); \ sprintf(&outline[13], ##__VA_ARGS__); \ output(); \ - outputAddr=(_logc->lcbits & LC_NoCodeAddrs) ? false : true; \ } \ } while (0) /* no semi */ #define gpn(r) regNames[(r)] diff --git a/js/src/nanojit/NativeX64.cpp b/js/src/nanojit/NativeX64.cpp index ea9f873e4ad..4f3219c8998 100644 --- a/js/src/nanojit/NativeX64.cpp +++ b/js/src/nanojit/NativeX64.cpp @@ -1634,7 +1634,7 @@ namespace nanojit SUBQRI(RSP, amt); } - verbose_only( outputAddr=true; asm_output("[patch entry]"); ) + verbose_only( asm_output("[patch entry]"); ) NIns *patchEntry = _nIns; MR(FP, RSP); // Establish our own FP. PUSHR(FP); // Save caller's FP. diff --git a/js/src/nanojit/Nativei386.cpp b/js/src/nanojit/Nativei386.cpp index 4d86d808528..fd358a33e9f 100644 --- a/js/src/nanojit/Nativei386.cpp +++ b/js/src/nanojit/Nativei386.cpp @@ -136,7 +136,7 @@ namespace nanojit SUBi(SP, amt); } - verbose_only( outputAddr=true; asm_output("[frag entry]"); ) + verbose_only( asm_output("[frag entry]"); ) NIns *fragEntry = _nIns; MR(FP, SP); // Establish our own FP. PUSHr(FP); // Save caller's FP. diff --git a/js/src/nanojit/nanojit.h b/js/src/nanojit/nanojit.h index 1a259a98f50..983cb03e21b 100644 --- a/js/src/nanojit/nanojit.h +++ b/js/src/nanojit/nanojit.h @@ -249,14 +249,13 @@ namespace nanojit { and below, so that callers can use bits 16 and above for themselves. */ // TODO: add entries for the writer pipeline - LC_FragProfile = 1<<7, // collect per-frag usage counts - LC_Activation = 1<<6, // enable printActivationState - LC_Liveness = 1<<5, // (show LIR liveness analysis) - LC_ReadLIR = 1<<4, // As read from LirBuffer - LC_AfterSF = 1<<3, // After StackFilter - LC_RegAlloc = 1<<2, // stuff to do with reg alloc - LC_Assembly = 1<<1, // final assembly - LC_NoCodeAddrs = 1<<0 // (don't show code addresses on asm output) + LC_FragProfile = 1<<6, // collect per-frag usage counts + LC_Activation = 1<<5, // enable printActivationState + LC_Liveness = 1<<4, // (show LIR liveness analysis) + LC_ReadLIR = 1<<3, // As read from LirBuffer + LC_AfterSF = 1<<2, // After StackFilter + LC_RegAlloc = 1<<1, // stuff to do with reg alloc + LC_Assembly = 1<<0, // final assembly }; class LogControl From 7de7cc4825d288d9ae95f0c6125b913424e84ca1 Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Tue, 8 Dec 2009 14:19:50 -0800 Subject: [PATCH 33/82] nanojit/LIR.cpp: add new load/store ops to liveness filter (r=reitmai,r=nnethercote) nanojit/Nativei386.h: remove bogus assertions from some i386 instructions (r=reitmai,r=nnethercote) --HG-- extra : convert_revision : 279dc4a9597527626dc0e2b2deceed148f9b61e4 --- js/src/nanojit/LIR.cpp | 10 ++++++++++ js/src/nanojit/Nativei386.h | 4 ---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/js/src/nanojit/LIR.cpp b/js/src/nanojit/LIR.cpp index 64e96b3c122..8aadc450726 100644 --- a/js/src/nanojit/LIR.cpp +++ b/js/src/nanojit/LIR.cpp @@ -1488,8 +1488,16 @@ namespace nanojit case LIR_ldc: case LIR_ldq: case LIR_ldqc: + case LIR_ldzb: + case LIR_ldzs: case LIR_ldcb: case LIR_ldcs: + case LIR_ldsb: + case LIR_ldss: + case LIR_ldcsb: + case LIR_ldcss: + case LIR_ld32f: + case LIR_ldc32f: case LIR_ret: case LIR_fret: case LIR_live: @@ -1515,6 +1523,8 @@ namespace nanojit case LIR_sti: case LIR_stqi: + case LIR_stb: + case LIR_sts: case LIR_eq: case LIR_lt: case LIR_gt: diff --git a/js/src/nanojit/Nativei386.h b/js/src/nanojit/Nativei386.h index f9c18eeeb9d..66899ea9240 100644 --- a/js/src/nanojit/Nativei386.h +++ b/js/src/nanojit/Nativei386.h @@ -485,14 +485,12 @@ namespace nanojit #define LD8Zdm(r,addr) do { \ count_ld(); \ - NanoAssert((d)>=0&&(d)<=31); \ ALU2dm(0x0fb6,r,addr); \ asm_output("movzx8 %s,0(%lx)", gpn(r),(long unsigned)addr); \ } while(0) #define LD8Zsib(r,disp,base,index,scale) do { \ count_ld(); \ - NanoAssert((d)>=0&&(d)<=31); \ ALU2sib(0x0fb6,r,base,index,scale,disp); \ asm_output("movzx8 %s,%d(%s+%s*%c)",gpn(r),disp,gpn(base),gpn(index),SIBIDX(scale)); \ } while(0) @@ -502,14 +500,12 @@ namespace nanojit #define LD8Sdm(r,addr) do { \ count_ld(); \ - NanoAssert((d)>=0&&(d)<=31); \ ALU2dm(0x0fbe,r,addr); \ asm_output("movsx8 %s,0(%lx)", gpn(r),(long unsigned)addr); \ } while(0) #define LD8Ssib(r,disp,base,index,scale) do { \ count_ld(); \ - NanoAssert((d)>=0&&(d)<=31); \ ALU2sib(0x0fbe,r,base,index,scale,disp); \ asm_output("movsx8 %s,%d(%s+%s*%c)",gpn(r),disp,gpn(base),gpn(index),SIBIDX(scale)); \ } while(0) From 2c6fd7810451f9fe621bf32a634ede2c4304095c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 9 Dec 2009 15:49:15 -0800 Subject: [PATCH 34/82] Bug 504507 - nanojit: kill LIR64 (NJ-only part). r=gal. --HG-- extra : convert_revision : 2271d9cb2d87970d20befb51dbc7092c830a4e10 --- js/src/lirasm/lirasm.cpp | 5 +- js/src/nanojit/Assembler.cpp | 2 +- js/src/nanojit/LIR.cpp | 70 ++++++--- js/src/nanojit/LIR.h | 90 +++++------ js/src/nanojit/LIRopcode.tbl | 294 +++++++++++++++++------------------ js/src/nanojit/Native.h | 8 +- js/src/nanojit/NativeX64.cpp | 179 ++++++++++----------- 7 files changed, 333 insertions(+), 315 deletions(-) diff --git a/js/src/lirasm/lirasm.cpp b/js/src/lirasm/lirasm.cpp index fa10bb3808c..b658f72a648 100644 --- a/js/src/lirasm/lirasm.cpp +++ b/js/src/lirasm/lirasm.cpp @@ -1784,13 +1784,10 @@ Lirasm::Lirasm(bool verbose) : #endif // Populate the mOpMap table. -#define OPDEF(op, number, repkind) \ - mOpMap[#op] = LIR_##op; -#define OPD64(op, number, repkind) \ +#define OPDEF(op, number, repKind, retType) \ mOpMap[#op] = LIR_##op; #include "nanojit/LIRopcode.tbl" #undef OPDEF -#undef OPD64 // TODO - These should alias to the appropriate platform-specific LIR opcode. mOpMap["alloc"] = mOpMap["ialloc"]; diff --git a/js/src/nanojit/Assembler.cpp b/js/src/nanojit/Assembler.cpp index b40389e6588..43b381eb305 100755 --- a/js/src/nanojit/Assembler.cpp +++ b/js/src/nanojit/Assembler.cpp @@ -966,7 +966,7 @@ namespace nanojit switch(op) { default: - NanoAssertMsgf(false, "unsupported LIR instruction: %d (~0x40: %d)\n", op, op&~LIR64); + NanoAssertMsgf(false, "unsupported LIR instruction: %d\n", op); break; case LIR_regfence: diff --git a/js/src/nanojit/LIR.cpp b/js/src/nanojit/LIR.cpp index 8aadc450726..ab191fba02f 100644 --- a/js/src/nanojit/LIR.cpp +++ b/js/src/nanojit/LIR.cpp @@ -45,27 +45,29 @@ namespace nanojit #ifdef FEATURE_NANOJIT const uint8_t repKinds[] = { -#define OPDEF(op, number, repkind) \ - LRK_##repkind, -#define OPD64(op, number, repkind) \ - LRK_##repkind, +#define OPDEF(op, number, repKind, retType) \ + LRK_##repKind, #include "LIRopcode.tbl" #undef OPDEF -#undef OPD64 0 }; + const LTy retTypes[] = { +#define OPDEF(op, number, repKind, retType) \ + LTy_##retType, +#include "LIRopcode.tbl" +#undef OPDEF + LTy_Void + }; + // LIR verbose specific #ifdef NJ_VERBOSE const char* lirNames[] = { -#define OPDEF(op, number, repkind) \ - #op, -#define OPD64(op, number, repkind) \ +#define OPDEF(op, number, repKind, retType) \ #op, #include "LIRopcode.tbl" #undef OPDEF -#undef OPD64 NULL }; @@ -327,22 +329,22 @@ namespace nanojit LInsp LirBufWriter::insImmq(uint64_t imm) { - LInsI64* insI64 = (LInsI64*)_buf->makeRoom(sizeof(LInsI64)); - LIns* ins = insI64->getLIns(); - ins->initLInsI64(LIR_quad, imm); + LInsN64* insN64 = (LInsN64*)_buf->makeRoom(sizeof(LInsN64)); + LIns* ins = insN64->getLIns(); + ins->initLInsN64(LIR_quad, imm); return ins; } LInsp LirBufWriter::insImmf(double d) { - LInsI64* insI64 = (LInsI64*)_buf->makeRoom(sizeof(LInsI64)); - LIns* ins = insI64->getLIns(); + LInsN64* insN64 = (LInsN64*)_buf->makeRoom(sizeof(LInsN64)); + LIns* ins = insN64->getLIns(); union { double d; uint64_t q; } u; u.d = d; - ins->initLInsI64(LIR_float, u.q); + ins->initLInsN64(LIR_float, u.q); return ins; } @@ -351,13 +353,10 @@ namespace nanojit { static const uint8_t insSizes[] = { // LIR_start is treated specially -- see below. -#define OPDEF(op, number, repkind) \ - ((number) == LIR_start ? 0 : sizeof(LIns##repkind)), -#define OPD64(op, number, repkind) \ - OPDEF(op, number, repkind) +#define OPDEF(op, number, repKind, retType) \ + ((number) == LIR_start ? 0 : sizeof(LIns##repKind)), #include "LIRopcode.tbl" #undef OPDEF -#undef OPD64 0 }; @@ -380,6 +379,33 @@ namespace nanojit return ret; } + LOpcode f64arith_to_i32arith(LOpcode op) + { + switch (op) { + case LIR_fneg: return LIR_neg; + case LIR_fadd: return LIR_add; + case LIR_fsub: return LIR_sub; + case LIR_fmul: return LIR_mul; + default: NanoAssert(0); return LIR_skip; + } + } + + LOpcode i32cmp_to_i64cmp(LOpcode op) + { + switch (op) { + case LIR_eq: return LIR_qeq; + case LIR_lt: return LIR_qlt; + case LIR_gt: return LIR_qgt; + case LIR_le: return LIR_qle; + case LIR_ge: return LIR_qge; + case LIR_ult: return LIR_qult; + case LIR_ugt: return LIR_qugt; + case LIR_ule: return LIR_qule; + case LIR_uge: return LIR_quge; + default: NanoAssert(0); return LIR_skip; + } + } + // This is never called, but that's ok because it contains only static // assertions. void LIns::staticSanityCheck() @@ -399,9 +425,9 @@ namespace nanojit NanoStaticAssert(sizeof(LInsP) == 2*sizeof(void*)); NanoStaticAssert(sizeof(LInsI) == 2*sizeof(void*)); #if defined NANOJIT_64BIT - NanoStaticAssert(sizeof(LInsI64) == 2*sizeof(void*)); + NanoStaticAssert(sizeof(LInsN64) == 2*sizeof(void*)); #else - NanoStaticAssert(sizeof(LInsI64) == 3*sizeof(void*)); + NanoStaticAssert(sizeof(LInsN64) == 3*sizeof(void*)); #endif // oprnd_1 must be in the same position in LIns{Op1,Op2,Op3,Ld,Sti} diff --git a/js/src/nanojit/LIR.h b/js/src/nanojit/LIR.h index 53df9bcaa60..15b576a2aa8 100644 --- a/js/src/nanojit/LIR.h +++ b/js/src/nanojit/LIR.h @@ -120,16 +120,28 @@ namespace nanojit }; inline bool isCseOpcode(LOpcode op) { - op = LOpcode(op & ~LIR64); - return op >= LIR_int && op <= LIR_uge; + return (op >= LIR_int && op <= LIR_uge) || + (op >= LIR_quad && op <= LIR_quge); } inline bool isRetOpcode(LOpcode op) { - return (op & ~LIR64) == LIR_ret; + return op == LIR_ret || op == LIR_fret; } + LOpcode f64arith_to_i32arith(LOpcode op); + LOpcode i32cmp_to_i64cmp(LOpcode op); - // Array holding the 'repkind' field from LIRopcode.tbl. + // Array holding the 'repKind' field from LIRopcode.tbl. extern const uint8_t repKinds[]; + enum LTy { + LTy_Void, // no value/no type + LTy_I32, // 32-bit integer + LTy_I64, // 64-bit integer + LTy_F64 // 64-bit float + }; + + // Array holding the 'retType' field from LIRopcode.tbl. + extern const LTy retTypes[]; + //----------------------------------------------------------------------- // Low-level instructions. This is a bit complicated, because we have a // variable-width representation to minimise space usage. @@ -213,7 +225,7 @@ namespace nanojit LRK_C, LRK_P, LRK_I, - LRK_I64, + LRK_N64, LRK_Jtbl, LRK_None // this one is used for unused opcode numbers }; @@ -228,7 +240,7 @@ namespace nanojit class LInsC; class LInsP; class LInsI; - class LInsI64; + class LInsN64; class LInsJtbl; class LIns @@ -265,7 +277,7 @@ namespace nanojit inline LInsC* toLInsC() const; inline LInsP* toLInsP() const; inline LInsI* toLInsI() const; - inline LInsI64* toLInsI64() const; + inline LInsN64* toLInsN64() const; inline LInsJtbl*toLInsJtbl()const; void staticSanityCheck(); @@ -284,7 +296,7 @@ namespace nanojit inline void initLInsC(LOpcode opcode, LIns** args, const CallInfo* ci); inline void initLInsP(int32_t arg, int32_t kind); inline void initLInsI(LOpcode opcode, int32_t imm32); - inline void initLInsI64(LOpcode opcode, int64_t imm64); + inline void initLInsN64(LOpcode opcode, int64_t imm64); inline void initLInsJtbl(LIns* index, uint32_t size, LIns** table); LOpcode opcode() const { return lastWord.opcode; } @@ -349,7 +361,7 @@ namespace nanojit // For LInsI. inline int32_t imm32() const; - // For LInsI64. + // For LInsN64. inline int32_t imm64_0() const; inline int32_t imm64_1() const; inline uint64_t imm64() const; @@ -416,9 +428,9 @@ namespace nanojit NanoAssert(LRK_None != repKinds[opcode()]); return LRK_I == repKinds[opcode()]; } - bool isLInsI64() const { + bool isLInsN64() const { NanoAssert(LRK_None != repKinds[opcode()]); - return LRK_I64 == repKinds[opcode()]; + return LRK_N64 == repKinds[opcode()]; } bool isLInsJtbl() const { NanoAssert(LRK_None != repKinds[opcode()]); @@ -436,30 +448,21 @@ namespace nanojit return opcode() == o; } bool isQuad() const { - LOpcode op = opcode(); -#ifdef NANOJIT_64BIT - // callh in 64bit cpu's means a call that returns an int64 in a single register - return (!(op >= LIR_qeq && op <= LIR_quge) && (op & LIR64) != 0) || - op == LIR_callh; -#else - // callh in 32bit cpu's means the 32bit MSW of an int64 result in 2 registers - return (op & LIR64) != 0; -#endif + LTy ty = retTypes[opcode()]; + return ty == LTy_I64 || ty == LTy_F64; } bool isCond() const { - LOpcode op = opcode(); - return (op == LIR_ov) || isCmp(); + return (isop(LIR_ov)) || isCmp(); } bool isFloat() const; // not inlined because it contains a switch bool isCmp() const { LOpcode op = opcode(); - return (op >= LIR_eq && op <= LIR_uge) || + return (op >= LIR_eq && op <= LIR_uge) || (op >= LIR_qeq && op <= LIR_quge) || (op >= LIR_feq && op <= LIR_fge); } bool isCall() const { - LOpcode op = opcode(); - return (op & ~LIR64) == LIR_icall || op == LIR_qcall; + return isop(LIR_icall) || isop(LIR_fcall) || isop(LIR_qcall); } bool isStore() const { return isLInsSti(); @@ -468,13 +471,12 @@ namespace nanojit return isLInsLd(); } bool isGuard() const { - LOpcode op = opcode(); - return op == LIR_x || op == LIR_xf || op == LIR_xt || - op == LIR_xbarrier || op == LIR_xtbl; + return isop(LIR_x) || isop(LIR_xf) || isop(LIR_xt) || + isop(LIR_xbarrier) || isop(LIR_xtbl); } // True if the instruction is a 32-bit or smaller constant integer. bool isconst() const { - return opcode() == LIR_int; + return isop(LIR_int); } // True if the instruction is a 32-bit or smaller constant integer and // has the value val when treated as a 32-bit signed integer. @@ -483,7 +485,7 @@ namespace nanojit } // True if the instruction is a constant quad value. bool isconstq() const { - return opcode() == LIR_quad || opcode() == LIR_float; + return isop(LIR_quad) || isop(LIR_float); } // True if the instruction is a constant pointer value. bool isconstp() const @@ -496,7 +498,7 @@ namespace nanojit } // True if the instruction is a constant float value. bool isconstf() const { - return opcode() == LIR_float; + return isop(LIR_float); } bool isBranch() const { @@ -505,16 +507,16 @@ namespace nanojit bool isPtr() { #ifdef NANOJIT_64BIT - return isQuad(); + return retTypes[opcode()] == LTy_I64; #else - return !isQuad(); + return retTypes[opcode()] == LTy_I32; #endif } // Return true if removal of 'ins' from a LIR fragment could // possibly change the behaviour of that fragment, even if any // value computed by 'ins' is not used later in the fragment. - // In other words, can 'ins' possible alter control flow or memory? + // In other words, can 'ins' possibly alter control flow or memory? // Note, this assumes that loads will never fault and hence cannot // affect the control flow. bool isStmt() { @@ -698,8 +700,8 @@ namespace nanojit LIns* getLIns() { return &ins; }; }; - // Used for LIR_quad. - class LInsI64 + // Used for LIR_quad and LIR_float. + class LInsN64 { private: friend class LIns; @@ -747,7 +749,7 @@ namespace nanojit LInsC* LIns::toLInsC() const { return (LInsC* )( uintptr_t(this+1) - sizeof(LInsC ) ); } LInsP* LIns::toLInsP() const { return (LInsP* )( uintptr_t(this+1) - sizeof(LInsP ) ); } LInsI* LIns::toLInsI() const { return (LInsI* )( uintptr_t(this+1) - sizeof(LInsI ) ); } - LInsI64* LIns::toLInsI64() const { return (LInsI64*)( uintptr_t(this+1) - sizeof(LInsI64) ); } + LInsN64* LIns::toLInsN64() const { return (LInsN64*)( uintptr_t(this+1) - sizeof(LInsN64) ); } LInsJtbl*LIns::toLInsJtbl()const { return (LInsJtbl*)(uintptr_t(this+1) - sizeof(LInsJtbl)); } void LIns::initLInsOp0(LOpcode opcode) { @@ -818,12 +820,12 @@ namespace nanojit toLInsI()->imm32 = imm32; NanoAssert(isLInsI()); } - void LIns::initLInsI64(LOpcode opcode, int64_t imm64) { + void LIns::initLInsN64(LOpcode opcode, int64_t imm64) { markAsClear(); lastWord.opcode = opcode; - toLInsI64()->imm64_0 = int32_t(imm64); - toLInsI64()->imm64_1 = int32_t(imm64 >> 32); - NanoAssert(isLInsI64()); + toLInsN64()->imm64_0 = int32_t(imm64); + toLInsN64()->imm64_1 = int32_t(imm64 >> 32); + NanoAssert(isLInsN64()); } void LIns::initLInsJtbl(LIns* index, uint32_t size, LIns** table) { markAsClear(); @@ -895,11 +897,11 @@ namespace nanojit inline int32_t LIns::imm32() const { NanoAssert(isconst()); return toLInsI()->imm32; } - inline int32_t LIns::imm64_0() const { NanoAssert(isconstq()); return toLInsI64()->imm64_0; } - inline int32_t LIns::imm64_1() const { NanoAssert(isconstq()); return toLInsI64()->imm64_1; } + inline int32_t LIns::imm64_0() const { NanoAssert(isconstq()); return toLInsN64()->imm64_0; } + inline int32_t LIns::imm64_1() const { NanoAssert(isconstq()); return toLInsN64()->imm64_1; } uint64_t LIns::imm64() const { NanoAssert(isconstq()); - return (uint64_t(toLInsI64()->imm64_1) << 32) | uint32_t(toLInsI64()->imm64_0); + return (uint64_t(toLInsN64()->imm64_1) << 32) | uint32_t(toLInsN64()->imm64_0); } double LIns::imm64f() const { union { diff --git a/js/src/nanojit/LIRopcode.tbl b/js/src/nanojit/LIRopcode.tbl index 9928d246f26..f73ddf9287a 100644 --- a/js/src/nanojit/LIRopcode.tbl +++ b/js/src/nanojit/LIRopcode.tbl @@ -42,16 +42,16 @@ * Definitions of LIR opcodes. If you need to allocate an opcode, look * for a name beginning with "__" and claim it. * - * Includers must define OPDEF and OPD64 macros of the following forms: + * Includers must define an OPDEF macro of the following form: * - * #define OPDEF(op,val,repkind) ... - * #define OPD64(op,val,repkind) ... + * #define OPDEF(op, val, repKind, retType) ... * * Selected arguments can then be used within the macro expansions. * - op Bytecode name, token-pasted after "LIR_" to form an LOpcode. * - val Bytecode value, which is the LOpcode enumerator value. - * - repkind Indicates how the instruction is represented in memory; XYZ + * - repKind Indicates how the instruction is represented in memory; XYZ * corresponds to LInsXYZ and LRK_XYZ. + * - retType Type (LTy) of the value returned by the instruction. * * This file is best viewed with 128 columns: 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678 @@ -63,36 +63,36 @@ /* op val name operands */ /* special operations (must be 0..N) */ -OPDEF(start, 0, Op0) // start of a fragment -OPDEF(regfence, 1, Op0) // register fence, no register allocation is allowed across this meta instruction -OPDEF(skip, 2, Sk) // holds blobs ("payloads") of data; also links pages +OPDEF(start, 0, Op0, Void) // start of a fragment +OPDEF(regfence, 1, Op0, Void) // register fence, no register allocation is allowed across this meta instruction +OPDEF(skip, 2, Sk, Void) // used to link code chunks /* non-pure operations */ -OPDEF(ldsb, 3, Ld) // 8-bit integer load, sign-extend to 32-bit -OPDEF(ldss, 4, Ld) // 16-bit integer load, sign-extend to 32-bit -OPDEF(ldzb, 5, Ld) // 8-bit integer load, zero extend to 32-bit -OPDEF(ldzs, 6, Ld) // 16-bit integer load, zero extend to 32-bit -OPDEF(iaddp, 7, Op2) // integer addition for temporary pointer calculations (32bit only) -OPDEF(iparam, 8, P) // load a parameter (32bit register or stk location) -OPDEF(stb, 9, Sti) // 8-bit integer store -OPDEF(ld, 10, Ld) // 32-bit integer load -OPDEF(ialloc, 11, I) // alloc some stack space (value is 32bit address) -OPDEF(sti, 12, Sti) // 32-bit integer store -OPDEF(ret, 13, Op1) // return a word-sized value -OPDEF(live, 14, Op1) // extend live range of reference -OPDEF(flive, 15, Op1) // extend live range of a floating point value reference -OPDEF(icall, 16, C) // subroutine call returning a 32-bit value -OPDEF(sts, 17, Sti) // 16-bit integer store +OPDEF(ldsb, 3, Ld, I32) // 8-bit integer load, sign-extend to 32-bit +OPDEF(ldss, 4, Ld, I32) // 16-bit integer load, sign-extend to 32-bit +OPDEF(ldzb, 5, Ld, I32) // 8-bit integer load, zero extend to 32-bit +OPDEF(ldzs, 6, Ld, I32) // 16-bit integer load, zero extend to 32-bit +OPDEF(iaddp, 7, Op2, I32) // integer addition for temporary pointer calculations (32bit only) +OPDEF(iparam, 8, P, I32) // load a parameter (32bit register or stk location) +OPDEF(stb, 9, Sti, Void) // 8-bit integer store +OPDEF(ld, 10, Ld, I32) // 32-bit integer load +OPDEF(ialloc, 11, I, I32) // alloc some stack space (value is 32bit address) +OPDEF(sti, 12, Sti, Void) // 32-bit integer store +OPDEF(ret, 13, Op1, Void) // return a word-sized value +OPDEF(live, 14, Op1, Void) // extend live range of reference +OPDEF(flive, 15, Op1, Void) // extend live range of a floating point value reference +OPDEF(icall, 16, C, I32) // subroutine call returning a 32-bit value +OPDEF(sts, 17, Sti, Void) // 16-bit integer store /* guards */ -OPDEF(x, 18, Op2) // exit always +OPDEF(x, 18, Op2, Void) // exit always /* branches */ -OPDEF(j, 19, Op2) // jump always -OPDEF(jt, 20, Op2) // jump if true -OPDEF(jf, 21, Op2) // jump if false -OPDEF(label, 22, Op0) // a jump target (no machine code is emitted for this) -OPDEF(jtbl, 23, Jtbl) // jump to address in table +OPDEF(j, 19, Op2, Void) // jump always +OPDEF(jt, 20, Op2, Void) // jump if true +OPDEF(jf, 21, Op2, Void) // jump if false +OPDEF(label, 22, Op0, Void) // a jump target (no machine code is emitted for this) +OPDEF(jtbl, 23, Jtbl, Void) // jump to address in table /* operators */ @@ -101,158 +101,154 @@ OPDEF(jtbl, 23, Jtbl) // jump to address in table * common-subexpression-elimination detection code. */ -OPDEF(int, 24, I) // constant 32-bit integer -OPDEF(cmov, 25, Op3) // conditional move -OPDEF(callh, 26, Op1) // get the high 32 bits of a call returning a 64-bit value in two 32bit registers +OPDEF(int, 24, I, I32) // constant 32-bit integer +OPDEF(cmov, 25, Op3, I32) // conditional move +OPDEF(callh, 26, Op1, I32) // get the high 32 bits of a call returning a 64-bit value in two 32bit registers -/* - * feq though fge must only be used on float arguments. They return integers. - * For all except feq, (op ^ 1) is the op which flips the - * left and right sides of the comparison, so (lt ^ 1) == gt, or the operator - * "<" is xored with 1 to get ">". Similarly, (op ^ 3) is the complement of - * op, so (lt ^ 1) == ge, or the complement of the operator "<" is ">=" xored - * with 3. NB: These opcodes must remain continuous so that comparison-opcode - * detection works correctly. - */ -OPDEF(feq, 27, Op2) // floating-point equality -OPDEF(flt, 28, Op2) // floating-point less-than -OPDEF(fgt, 29, Op2) // floating-point greater-than -OPDEF(fle, 30, Op2) // floating-point less-than-or-equal -OPDEF(fge, 31, Op2) // floating-point greater-than-or-equal +// feq though fge must only be used on float arguments. They return integers. +// For all except feq, (op ^ 1) is the op which flips the +// left and right sides of the comparison, so (lt ^ 1) == gt, or the operator +// "<" is xored with 1 to get ">". Similarly, (op ^ 3) is the complement of +// op, so (lt ^ 1) == ge, or the complement of the operator "<" is ">=" xored +// with 3. NB: These opcodes must remain continuous so that comparison-opcode +// detection works correctly. +OPDEF(feq, 27, Op2, I32) // floating-point equality +OPDEF(flt, 28, Op2, I32) // floating-point less-than +OPDEF(fgt, 29, Op2, I32) // floating-point greater-than +OPDEF(fle, 30, Op2, I32) // floating-point less-than-or-equal +OPDEF(fge, 31, Op2, I32) // floating-point greater-than-or-equal -OPDEF(ldcb, 32, Ld) // non-volatile 8-bit integer load, zero-extend to 32-bit -OPDEF(ldcs, 33, Ld) // non-volatile 16-bit integer load, zero-extend to 32-bit -OPDEF(ldc, 34, Ld) // non-volatile 32-bit integer load +OPDEF(ldcb, 32, Ld, I32) // non-volatile 8-bit integer load, zero-extended to 32-bit +OPDEF(ldcs, 33, Ld, I32) // non-volatile 16-bit integer load, zero-extended to 32-bit +OPDEF(ldc, 34, Ld, I32) // non-volatile 32-bit integer load, zero-extended to 32-bit -OPDEF(neg, 35, Op1) // integer negation -OPDEF(add, 36, Op2) // integer addition -OPDEF(sub, 37, Op2) // integer subtraction -OPDEF(mul, 38, Op2) // integer multiplication -OPDEF(div, 39, Op2) // integer division -OPDEF(mod, 40, Op1) // hack: get the modulus from a LIR_div result, for x86 only +OPDEF(neg, 35, Op1, I32) // integer negation +OPDEF(add, 36, Op2, I32) // integer addition +OPDEF(sub, 37, Op2, I32) // integer subtraction +OPDEF(mul, 38, Op2, I32) // integer multiplication +OPDEF(div, 39, Op2, I32) // integer division +OPDEF(mod, 40, Op1, I32) // hack: get the modulus from a LIR_div result, for x86 only -OPDEF(and, 41, Op2) // 32-bit bitwise AND -OPDEF(or, 42, Op2) // 32-bit bitwise OR -OPDEF(xor, 43, Op2) // 32-bit bitwise XOR -OPDEF(not, 44, Op1) // 32-bit bitwise NOT -OPDEF(lsh, 45, Op2) // 32-bit left shift -OPDEF(rsh, 46, Op2) // 32-bit right shift with sign-extend (>>) -OPDEF(ush, 47, Op2) // 32-bit unsigned right shift (>>>) +OPDEF(and, 41, Op2, I32) // 32-bit bitwise AND +OPDEF(or, 42, Op2, I32) // 32-bit bitwise OR +OPDEF(xor, 43, Op2, I32) // 32-bit bitwise XOR +OPDEF(not, 44, Op1, I32) // 32-bit bitwise NOT +OPDEF(lsh, 45, Op2, I32) // 32-bit left shift +OPDEF(rsh, 46, Op2, I32) // 32-bit right shift with sign-extend (>>) +OPDEF(ush, 47, Op2, I32) // 32-bit unsigned right shift (>>>) -// conditional guards, op^1 to complement. Only things that are +// Conditional guards, op^1 to complement. Only things that are // isCond() can be passed to these. -OPDEF(xt, 48, Op2) // exit if true (0x30 0011 0000) -OPDEF(xf, 49, Op2) // exit if false (0x31 0011 0001) +OPDEF(xt, 48, Op2, Void) // exit if true (0x30 0011 0000) +OPDEF(xf, 49, Op2, Void) // exit if false (0x31 0011 0001) -OPDEF(qlo, 50, Op1) // get the low 32 bits of a 64-bit value -OPDEF(qhi, 51, Op1) // get the high 32 bits of a 64-bit value +OPDEF(qlo, 50, Op1, I32) // get the low 32 bits of a 64-bit value +OPDEF(qhi, 51, Op1, I32) // get the high 32 bits of a 64-bit value -OPDEF(ldcsb, 52, Ld) // non-volatile 8-bit integer load (sign-extend to 32-bit) -OPDEF(ldcss, 53, Ld) // non-volatile 16-bit integer load (sign-extend to 32-bit) +OPDEF(ldcsb, 52, Ld, Void) // non-volatile 8-bit integer load, sign-extednded to 32-bit +OPDEF(ldcss, 53, Ld, Void) // non-volatile 16-bit integer load, sign-extednded to 32-bit -// This must be right before LIR_eq, so (op&~LIR64 - LIR_ov) can be indexed -// into a convenient table. -OPDEF(ov, 54, Op1) // test for overflow; value must have just been computed +OPDEF(ov, 54, Op1, I32) // test for overflow; value must have just been computed -// Integer (32 bit) relational operators. (op ^ 1) is the op which flips the +// Integer (32-bit) relational operators. (op ^ 1) is the op which flips the // left and right sides of the comparison, so (lt ^ 1) == gt, or the operator // "<" is xored with 1 to get ">". Similarly, (op ^ 3) is the complement of // op, so (lt ^ 1) == ge, or the complement of the operator "<" is ">=" xored // with 3. 'u' prefix indicates the unsigned integer variant. // NB: These opcodes must remain continuous so that comparison-opcode detection // works correctly. -OPDEF(eq, 55, Op2) // integer equality -OPDEF(lt, 56, Op2) // signed integer less-than (0x38 0011 1000) -OPDEF(gt, 57, Op2) // signed integer greater-than (0x39 0011 1001) -OPDEF(le, 58, Op2) // signed integer less-than-or-equal (0x3A 0011 1010) -OPDEF(ge, 59, Op2) // signed integer greater-than-or-equal (0x3B 0011 1011) -OPDEF(ult, 60, Op2) // unsigned integer less-than (0x3C 0011 1100) -OPDEF(ugt, 61, Op2) // unsigned integer greater-than (0x3D 0011 1101) -OPDEF(ule, 62, Op2) // unsigned integer less-than-or-equal (0x3E 0011 1110) -OPDEF(uge, 63, Op2) // unsigned integer greater-than-or-equal (0x3F 0011 1111) +OPDEF(eq, 55, Op2, I32) // integer equality +OPDEF(lt, 56, Op2, I32) // signed integer less-than (0x38 0011 1000) +OPDEF(gt, 57, Op2, I32) // signed integer greater-than (0x39 0011 1001) +OPDEF(le, 58, Op2, I32) // signed integer less-than-or-equal (0x3A 0011 1010) +OPDEF(ge, 59, Op2, I32) // signed integer greater-than-or-equal (0x3B 0011 1011) +OPDEF(ult, 60, Op2, I32) // unsigned integer less-than (0x3C 0011 1100) +OPDEF(ugt, 61, Op2, I32) // unsigned integer greater-than (0x3D 0011 1101) +OPDEF(ule, 62, Op2, I32) // unsigned integer less-than-or-equal (0x3E 0011 1110) +OPDEF(uge, 63, Op2, I32) // unsigned integer greater-than-or-equal (0x3F 0011 1111) -OPD64(__0_64, 0, None) +OPDEF(__64, 64, None, Void) -OPD64(file, 1, Op1) // source filename for debug symbols -OPD64(line, 2, Op1) // source line number for debug symbols -OPD64(xbarrier, 3, Op2) // memory barrier; doesn't exit, but flushes all values to the stack -OPD64(xtbl, 4, Op2) // exit via indirect jump +OPDEF(file, 65, Op1, Void) // source filename for debug symbols +OPDEF(line, 66, Op1, Void) // source line number for debug symbols +OPDEF(xbarrier, 67, Op2, Void) // memory barrier; doesn't exit, but flushes all values to the stack +OPDEF(xtbl, 68, Op2, Void) // exit via indirect jump -OPD64(__5_64, 5, None) -OPD64(__6_64, 6, None) -OPD64(qaddp, LIR_iaddp, Op2) // integer addition for temp pointer calculations (64bit only) -OPD64(qparam, LIR_iparam, P) // load a parameter (64bit register or stk location) -OPD64(__9_64, 9, None) +OPDEF(__69, 69, None, Void) +OPDEF(__70, 70, None, Void) +OPDEF(qaddp, 71, Op2, I64) // integer addition for temp pointer calculations (64bit only) +OPDEF(qparam, 72, P, I64) // load a parameter (64bit register or stk location) +OPDEF(__73, 73, None, Void) -OPD64(ldq, LIR_ld, Ld) // 64-bit (quad) load +OPDEF(ldq, 74, Ld, I64) // 64-bit (quad) load -OPD64(qalloc, LIR_ialloc, I) // allocate some stack space (value is 64bit address) +OPDEF(qalloc, 75, I, I64) // allocate some stack space (value is 64bit address) -OPD64(stqi, LIR_sti, Sti) // 64-bit (quad) store -OPD64(fret, LIR_ret, Op1) +OPDEF(stqi, 76, Sti, Void) // 64-bit (quad) store +OPDEF(fret, 77, Op1, Void) -OPD64(st32f, 14, Sti) // store 64-bit float as a 32-bit float (dropping precision) -OPD64(ld32f, 15, Ld) // load 32-bit float and widen to 64-bit float +OPDEF(st32f, 78, Sti, Void) // store 64-bit float as a 32-bit float (dropping precision) +OPDEF(ld32f, 79, Ld, F64) // load 32-bit float and widen to 64-bit float -OPD64(fcall, LIR_icall, C) // subroutine call returning 64-bit (quad) double value -OPD64(qcall, 17, C) // subroutine call returning 64-bit (quad) integer value +OPDEF(fcall, 80, C, F64) // subroutine call returning 64-bit (quad) double value +OPDEF(qcall, 81, C, I64) // subroutine call returning 64-bit (quad) integer value -OPD64(__18_64, 18, None) -OPD64(__19_64, 19, None) -OPD64(__20_64, 20, None) -OPD64(__21_64, 21, None) -OPD64(__22_64, 22, None) -OPD64(__23_64, 23, None) +OPDEF(__82, 82, None, Void) +OPDEF(__83, 83, None, Void) +OPDEF(__84, 84, None, Void) +OPDEF(__85, 85, None, Void) +OPDEF(__86, 86, None, Void) +OPDEF(__87, 87, None, Void) -// We strip off the 64 bit flag and compare that the opcode is between LIR_int -// and LIR_uge to decide whether we can CSE the opcode. All opcodes below -// this marker are subject to CSE. +// All opcodes below this marker are subject to CSE. -OPD64(quad, LIR_int, I64) // 64-bit (quad) constant value -OPD64(qcmov, LIR_cmov, Op3) // 64-bit conditional move +OPDEF(quad, 88, N64, I64) // 64-bit (quad) constant value +OPDEF(qcmov, 89, Op3, I64) // 64-bit conditional move -OPD64(i2q, 26, Op1) // sign-extend i32 to i64 -OPD64(u2q, 27, Op1) // zero-extend u32 to u64 -OPD64(i2f, 28, Op1) // convert a signed 32-bit integer to a float -OPD64(u2f, 29, Op1) // convert an unsigned 32-bit integer to a float +OPDEF(i2q, 90, Op1, I64) // sign-extend i32 to i64 +OPDEF(u2q, 91, Op1, I64) // zero-extend u32 to u64 +OPDEF(i2f, 92, Op1, F64) // convert a signed 32-bit integer to a float +OPDEF(u2f, 93, Op1, F64) // convert an unsigned 32-bit integer to a float -OPD64(__30_64, 30, None) -OPD64(__31_64, 31, None) -OPD64(__32_64, 32, None) -OPD64(__33_64, 33, None) +OPDEF(__94, 94, None, Void) +OPDEF(__95, 95, None, Void) +OPDEF(__96, 96, None, Void) +OPDEF(__97, 97, None, Void) -OPD64(ldqc, LIR_ldc, Ld) // non-volatile 64-bit load +OPDEF(ldqc, 98, Ld, I64) // non-volatile 64-bit load -OPD64(fneg, LIR_neg, Op1) // floating-point negation -OPD64(fadd, LIR_add, Op2) // floating-point addition -OPD64(fsub, LIR_sub, Op2) // floating-point subtraction -OPD64(fmul, LIR_mul, Op2) // floating-point multiplication -OPD64(fdiv, LIR_div, Op2) // floating-point division -OPD64(fmod, LIR_mod, Op2) // floating-point modulus(?) +OPDEF(fneg, 99, Op1, F64) // floating-point negation +OPDEF(fadd, 100, Op2, F64) // floating-point addition +OPDEF(fsub, 101, Op2, F64) // floating-point subtraction +OPDEF(fmul, 102, Op2, F64) // floating-point multiplication +OPDEF(fdiv, 103, Op2, F64) // floating-point division +OPDEF(fmod, 104, Op2, F64) // floating-point modulus(?) -OPD64(qiand, 41, Op2) // 64-bit bitwise AND -OPD64(qior, 42, Op2) // 64-bit bitwise OR -OPD64(qxor, 43, Op2) // 64-bit bitwise XOR -OPD64(__44_64, 44, None) -OPD64(qilsh, 45, Op2) // 64-bit left shift -OPD64(qirsh, 46, Op2) // 64-bit signed right shift -OPD64(qursh, 47, Op2) // 64-bit unsigned right shift -OPD64(qiadd, 48, Op2) // 64-bit bitwise ADD +OPDEF(qiand, 105, Op2, I64) // 64-bit bitwise AND +OPDEF(qior, 106, Op2, I64) // 64-bit bitwise OR +OPDEF(qxor, 107, Op2, I64) // 64-bit bitwise XOR +OPDEF(__108, 108, None, Void) +OPDEF(qilsh, 109, Op2, I64) // 64-bit left shift +OPDEF(qirsh, 110, Op2, I64) // 64-bit signed right shift +OPDEF(qursh, 111, Op2, I64) // 64-bit unsigned right shift +OPDEF(qiadd, 112, Op2, I64) // 64-bit bitwise ADD -OPD64(ldc32f, 49, Ld) // non-volatile load 32-bit float and widen to 64-bit float -OPD64(qjoin, 50, Op2) // join two 32-bit values (1st arg is low bits, 2nd is high) -OPD64(__51_64, 51, None) -OPD64(__52_64, 52, None) -OPD64(__53_64, 53, None) -OPD64(float, 54, I64) +OPDEF(ldc32f, 113, Ld, F64) // non-volatile load 32-bit float and widen to 64-bit float +OPDEF(qjoin, 114, Op2, F64) // join two 32-bit values (1st arg is low bits, 2nd is high) +OPDEF(__115, 115, None, Void) +OPDEF(__116, 116, None, Void) +OPDEF(__117, 117, None, Void) +OPDEF(float, 118, N64, F64) -// 64bit equivalents for integer comparisons -OPD64(qeq, LIR_eq, Op2) // integer equality -OPD64(qlt, LIR_lt, Op2) // signed integer less-than (0x78 0111 1000) -OPD64(qgt, LIR_gt, Op2) // signed integer greater-than (0x79 0111 1001) -OPD64(qle, LIR_le, Op2) // signed integer less-than-or-equal (0x7A 0111 1010) -OPD64(qge, LIR_ge, Op2) // signed integer greater-than-or-equal (0x7B 0111 1011) -OPD64(qult, LIR_ult, Op2) // unsigned integer less-than (0x7C 0111 1100) -OPD64(qugt, LIR_ugt, Op2) // unsigned integer greater-than (0x7D 0111 1101) -OPD64(qule, LIR_ule, Op2) // unsigned integer less-than-or-equal (0x7E 0111 1110) -OPD64(quge, LIR_uge, Op2) // unsigned integer greater-than-or-equal (0x7F 0111 1111) +// Integer (64-bit) relational operators. +// NB: These opcodes must remain continuous so that comparison-opcode detection +// works correctly. +OPDEF(qeq, 119, Op2, I32) // integer equality +OPDEF(qlt, 120, Op2, I32) // signed integer less-than (0x78 0111 1000) +OPDEF(qgt, 121, Op2, I32) // signed integer greater-than (0x79 0111 1001) +OPDEF(qle, 122, Op2, I32) // signed integer less-than-or-equal (0x7A 0111 1010) +OPDEF(qge, 123, Op2, I32) // signed integer greater-than-or-equal (0x7B 0111 1011) +OPDEF(qult, 124, Op2, I32) // unsigned integer less-than (0x7C 0111 1100) +OPDEF(qugt, 125, Op2, I32) // unsigned integer greater-than (0x7D 0111 1101) +OPDEF(qule, 126, Op2, I32) // unsigned integer less-than-or-equal (0x7E 0111 1110) +OPDEF(quge, 127, Op2, I32) // unsigned integer greater-than-or-equal (0x7F 0111 1111) diff --git a/js/src/nanojit/Native.h b/js/src/nanojit/Native.h index 39712bd5efc..a1a52aad3e7 100644 --- a/js/src/nanojit/Native.h +++ b/js/src/nanojit/Native.h @@ -61,17 +61,11 @@ namespace nanojit { : unsigned #endif { - // flags; upper bits reserved - LIR64 = 0x40, // result is double or quad - -#define OPDEF(op, number, repkind) \ +#define OPDEF(op, number, repKind, retType) \ LIR_##op = (number), -#define OPD64(op, number, repkind) \ - LIR_##op = ((number) | LIR64), #include "LIRopcode.tbl" LIR_sentinel, #undef OPDEF -#undef OPD64 #ifdef NANOJIT_64BIT # define PTR_SIZE(a,b) b diff --git a/js/src/nanojit/NativeX64.cpp b/js/src/nanojit/NativeX64.cpp index 4f3219c8998..9d8021a66e5 100644 --- a/js/src/nanojit/NativeX64.cpp +++ b/js/src/nanojit/NativeX64.cpp @@ -764,10 +764,10 @@ namespace nanojit void Assembler::asm_arith(LIns *ins) { Register rr, ra, rb; - switch (ins->opcode() & ~LIR64) { - case LIR_lsh: - case LIR_rsh: - case LIR_ush: + switch (ins->opcode()) { + case LIR_lsh: case LIR_qilsh: + case LIR_rsh: case LIR_qirsh: + case LIR_ush: case LIR_qursh: asm_shift(ins); return; case LIR_mod: @@ -999,32 +999,32 @@ namespace nanojit LOpcode condop = cond->opcode(); if (ins->opcode() == LIR_cmov) { - switch (condop & ~LIR64) { - case LIR_ov: CMOVNO( rr, rf); break; - case LIR_eq: CMOVNE( rr, rf); break; - case LIR_lt: CMOVNL( rr, rf); break; - case LIR_gt: CMOVNG( rr, rf); break; - case LIR_le: CMOVNLE(rr, rf); break; - case LIR_ge: CMOVNGE(rr, rf); break; - case LIR_ult: CMOVNB( rr, rf); break; - case LIR_ugt: CMOVNA( rr, rf); break; - case LIR_ule: CMOVNBE(rr, rf); break; - case LIR_uge: CMOVNAE(rr, rf); break; - default: NanoAssert(0); break; + switch (condop) { + case LIR_ov: CMOVNO( rr, rf); break; + case LIR_eq: case LIR_qeq: CMOVNE( rr, rf); break; + case LIR_lt: case LIR_qlt: CMOVNL( rr, rf); break; + case LIR_gt: case LIR_qgt: CMOVNG( rr, rf); break; + case LIR_le: case LIR_qle: CMOVNLE(rr, rf); break; + case LIR_ge: case LIR_qge: CMOVNGE(rr, rf); break; + case LIR_ult: case LIR_qult: CMOVNB( rr, rf); break; + case LIR_ugt: case LIR_qugt: CMOVNA( rr, rf); break; + case LIR_ule: case LIR_qule: CMOVNBE(rr, rf); break; + case LIR_uge: case LIR_quge: CMOVNAE(rr, rf); break; + default: NanoAssert(0); break; } } else { - switch (condop & ~LIR64) { - case LIR_ov: CMOVQNO( rr, rf); break; - case LIR_eq: CMOVQNE( rr, rf); break; - case LIR_lt: CMOVQNL( rr, rf); break; - case LIR_gt: CMOVQNG( rr, rf); break; - case LIR_le: CMOVQNLE(rr, rf); break; - case LIR_ge: CMOVQNGE(rr, rf); break; - case LIR_ult: CMOVQNB( rr, rf); break; - case LIR_ugt: CMOVQNA( rr, rf); break; - case LIR_ule: CMOVQNBE(rr, rf); break; - case LIR_uge: CMOVQNAE(rr, rf); break; - default: NanoAssert(0); break; + switch (condop) { + case LIR_ov: CMOVQNO( rr, rf); break; + case LIR_eq: case LIR_qeq: CMOVQNE( rr, rf); break; + case LIR_lt: case LIR_qlt: CMOVQNL( rr, rf); break; + case LIR_gt: case LIR_qgt: CMOVQNG( rr, rf); break; + case LIR_le: case LIR_qle: CMOVQNLE(rr, rf); break; + case LIR_ge: case LIR_qge: CMOVQNGE(rr, rf); break; + case LIR_ult: case LIR_qult: CMOVQNB( rr, rf); break; + case LIR_ugt: case LIR_qugt: CMOVQNA( rr, rf); break; + case LIR_ule: case LIR_qule: CMOVQNBE(rr, rf); break; + case LIR_uge: case LIR_quge: CMOVQNAE(rr, rf); break; + default: NanoAssert(0); break; } } /*const Register rt =*/ findSpecificRegFor(iftrue, rr); @@ -1032,72 +1032,71 @@ namespace nanojit } NIns* Assembler::asm_branch(bool onFalse, LIns *cond, NIns *target) { + NanoAssert(cond->isCond()); LOpcode condop = cond->opcode(); if (condop >= LIR_feq && condop <= LIR_fge) return asm_fbranch(onFalse, cond, target); - // we must ensure there's room for the instr before calculating - // the offset. and the offset, determines the opcode (8bit or 32bit) - NanoAssert((condop & ~LIR64) >= LIR_ov); - NanoAssert((condop & ~LIR64) <= LIR_uge); + // We must ensure there's room for the instr before calculating + // the offset. And the offset determines the opcode (8bit or 32bit). if (target && isTargetWithinS8(target)) { if (onFalse) { - switch (condop & ~LIR64) { - case LIR_ov: JNO8( 8, target); break; - case LIR_eq: JNE8( 8, target); break; - case LIR_lt: JNL8( 8, target); break; - case LIR_gt: JNG8( 8, target); break; - case LIR_le: JNLE8(8, target); break; - case LIR_ge: JNGE8(8, target); break; - case LIR_ult: JNB8( 8, target); break; - case LIR_ugt: JNA8( 8, target); break; - case LIR_ule: JNBE8(8, target); break; - case LIR_uge: JNAE8(8, target); break; - default: NanoAssert(0); break; + switch (condop) { + case LIR_ov: JNO8( 8, target); break; + case LIR_eq: case LIR_qeq: JNE8( 8, target); break; + case LIR_lt: case LIR_qlt: JNL8( 8, target); break; + case LIR_gt: case LIR_qgt: JNG8( 8, target); break; + case LIR_le: case LIR_qle: JNLE8(8, target); break; + case LIR_ge: case LIR_qge: JNGE8(8, target); break; + case LIR_ult: case LIR_qult: JNB8( 8, target); break; + case LIR_ugt: case LIR_qugt: JNA8( 8, target); break; + case LIR_ule: case LIR_qule: JNBE8(8, target); break; + case LIR_uge: case LIR_quge: JNAE8(8, target); break; + default: NanoAssert(0); break; } } else { - switch (condop & ~LIR64) { - case LIR_ov: JO8( 8, target); break; - case LIR_eq: JE8( 8, target); break; - case LIR_lt: JL8( 8, target); break; - case LIR_gt: JG8( 8, target); break; - case LIR_le: JLE8(8, target); break; - case LIR_ge: JGE8(8, target); break; - case LIR_ult: JB8( 8, target); break; - case LIR_ugt: JA8( 8, target); break; - case LIR_ule: JBE8(8, target); break; - case LIR_uge: JAE8(8, target); break; - default: NanoAssert(0); break; + switch (condop) { + case LIR_ov: JO8( 8, target); break; + case LIR_eq: case LIR_qeq: JE8( 8, target); break; + case LIR_lt: case LIR_qlt: JL8( 8, target); break; + case LIR_gt: case LIR_qgt: JG8( 8, target); break; + case LIR_le: case LIR_qle: JLE8(8, target); break; + case LIR_ge: case LIR_qge: JGE8(8, target); break; + case LIR_ult: case LIR_qult: JB8( 8, target); break; + case LIR_ugt: case LIR_qugt: JA8( 8, target); break; + case LIR_ule: case LIR_qule: JBE8(8, target); break; + case LIR_uge: case LIR_quge: JAE8(8, target); break; + default: NanoAssert(0); break; } } } else { if (onFalse) { - switch (condop & ~LIR64) { - case LIR_ov: JNO( 8, target); break; - case LIR_eq: JNE( 8, target); break; - case LIR_lt: JNL( 8, target); break; - case LIR_gt: JNG( 8, target); break; - case LIR_le: JNLE(8, target); break; - case LIR_ge: JNGE(8, target); break; - case LIR_ult: JNB( 8, target); break; - case LIR_ugt: JNA( 8, target); break; - case LIR_ule: JNBE(8, target); break; - case LIR_uge: JNAE(8, target); break; - default: NanoAssert(0); break; + switch (condop) { + case LIR_ov: JNO( 8, target); break; + case LIR_eq: case LIR_qeq: JNE( 8, target); break; + case LIR_lt: case LIR_qlt: JNL( 8, target); break; + case LIR_gt: case LIR_qgt: JNG( 8, target); break; + case LIR_le: case LIR_qle: JNLE(8, target); break; + case LIR_ge: case LIR_qge: JNGE(8, target); break; + case LIR_ult: case LIR_qult: JNB( 8, target); break; + case LIR_ugt: case LIR_qugt: JNA( 8, target); break; + case LIR_ule: case LIR_qule: JNBE(8, target); break; + case LIR_uge: case LIR_quge: JNAE(8, target); break; + default: NanoAssert(0); break; } } else { - switch (condop & ~LIR64) { - case LIR_ov: JO( 8, target); break; - case LIR_eq: JE( 8, target); break; - case LIR_lt: JL( 8, target); break; - case LIR_gt: JG( 8, target); break; - case LIR_le: JLE(8, target); break; - case LIR_ge: JGE(8, target); break; - case LIR_ult: JB( 8, target); break; - case LIR_ugt: JA( 8, target); break; - case LIR_ule: JBE(8, target); break; - case LIR_uge: JAE(8, target); break; - default: NanoAssert(0); break; + switch (condop) { + case LIR_ov: JO( 8, target); break; + case LIR_eq: case LIR_qeq: JE( 8, target); break; + case LIR_lt: case LIR_qlt: JL( 8, target); break; + case LIR_gt: case LIR_qgt: JG( 8, target); break; + case LIR_le: case LIR_qle: JLE(8, target); break; + case LIR_ge: case LIR_qge: JGE(8, target); break; + case LIR_ult: case LIR_qult: JB( 8, target); break; + case LIR_ugt: case LIR_qugt: JA( 8, target); break; + case LIR_ule: case LIR_qule: JBE(8, target); break; + case LIR_uge: case LIR_quge: JAE(8, target); break; + default: NanoAssert(0); break; } } } @@ -1125,25 +1124,29 @@ namespace nanojit } LOpcode condop = cond->opcode(); - if (condop & LIR64) + if (LIR_qeq <= condop && condop <= LIR_quge) { CMPQR(ra, rb); - else + } else { + NanoAssert(LIR_eq <= condop && condop <= LIR_uge); CMPLR(ra, rb); + } } void Assembler::asm_cmp_imm(LIns *cond) { + LOpcode condop = cond->opcode(); LIns *a = cond->oprnd1(); LIns *b = cond->oprnd2(); Register ra = findRegFor(a, GpRegs); int32_t imm = getImm32(b); - if (isS8(imm)) { - if (cond->opcode() & LIR64) + if (LIR_qeq <= condop && condop <= LIR_quge) { + if (isS8(imm)) CMPQR8(ra, imm); - else - CMPLR8(ra, imm); - } else { - if (cond->opcode() & LIR64) + else CMPQRI(ra, imm); + } else { + NanoAssert(LIR_eq <= condop && condop <= LIR_uge); + if (isS8(imm)) + CMPLR8(ra, imm); else CMPLRI(ra, imm); } From 41cabae63625d42e72b4cf038562654a4355561d Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 9 Dec 2009 15:51:43 -0800 Subject: [PATCH 35/82] Update nanojit-import-rev stamp. --- js/src/nanojit-import-rev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/nanojit-import-rev b/js/src/nanojit-import-rev index 7c484cf176e..243276769e9 100644 --- a/js/src/nanojit-import-rev +++ b/js/src/nanojit-import-rev @@ -1 +1 @@ -cb855a65f046a59c28277766aa5d320df33159c2 +2271d9cb2d87970d20befb51dbc7092c830a4e10 From efbef8869083df7a619dc9dae074d3d2b558be40 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 9 Dec 2009 15:57:12 -0800 Subject: [PATCH 36/82] Bug 504507 - nanojit: kill LIR64 (TM-only part). r=gal. --- js/src/jstracer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 7a154d8188c..e13fbb8294e 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -1931,7 +1931,7 @@ public: LIns* lhs = s0->oprnd1(); LIns* rhs = s0->oprnd2(); if (isPromote(lhs) && isPromote(rhs)) { - LOpcode op = LOpcode(s0->opcode() & ~LIR64); + LOpcode op = f64arith_to_i32arith(s0->opcode()); return out->ins2(op, demote(out, lhs), demote(out, rhs)); } } @@ -8203,7 +8203,7 @@ TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1) #endif default: - v = (LOpcode)((int)v & ~LIR64); + v = f64arith_to_i32arith(v); result = lir->ins2(v, d0, d1); /* @@ -8890,7 +8890,7 @@ JS_REQUIRES_STACK RecordingStatus TraceRecorder::unary(LOpcode op) { jsval& v = stackval(-1); - bool intop = !(op & LIR64); + bool intop = retTypes[op] == LTy_I32; if (isNumber(v)) { LIns* a = get(&v); if (intop) @@ -8923,7 +8923,7 @@ TraceRecorder::binary(LOpcode op) return call_imacro(binary_imacros.any_obj); } - bool intop = !(op & LIR64); + bool intop = retTypes[op] == LTy_I32; LIns* a = get(&l); LIns* b = get(&r); From c60a9d49a08a629954ad85e78c079d40a3b328f1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 9 Dec 2009 17:21:46 -0800 Subject: [PATCH 37/82] Bustage fix for 504507 on Linux. --HG-- extra : convert_revision : 18cf6386858be93609ffcc0912e1c169b81e6531 --- js/src/nanojit/nanojit.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/nanojit/nanojit.h b/js/src/nanojit/nanojit.h index 983cb03e21b..11031fcc860 100644 --- a/js/src/nanojit/nanojit.h +++ b/js/src/nanojit/nanojit.h @@ -255,7 +255,7 @@ namespace nanojit { LC_ReadLIR = 1<<3, // As read from LirBuffer LC_AfterSF = 1<<2, // After StackFilter LC_RegAlloc = 1<<1, // stuff to do with reg alloc - LC_Assembly = 1<<0, // final assembly + LC_Assembly = 1<<0 // final assembly }; class LogControl From 4bd18613b744ff7c8ad042f57b57c38ca1055925 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Wed, 9 Dec 2009 17:24:08 -0800 Subject: [PATCH 38/82] Update nanojit-import-rev stamp. --- js/src/nanojit-import-rev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/nanojit-import-rev b/js/src/nanojit-import-rev index 243276769e9..181156ad407 100644 --- a/js/src/nanojit-import-rev +++ b/js/src/nanojit-import-rev @@ -1 +1 @@ -2271d9cb2d87970d20befb51dbc7092c830a4e10 +18cf6386858be93609ffcc0912e1c169b81e6531 From d9a16641ee406580ea373cb2583d9dab0009e342 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 11 Dec 2009 12:10:36 -0800 Subject: [PATCH 39/82] Fixed CASE_EXITs being limited to about 4 entries (bug 533521, r=gal). --- js/src/jstracer.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index e13fbb8294e..e565b215ef1 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -5910,7 +5910,10 @@ AttemptToExtendTree(JSContext* cx, VMSideExit* anchor, VMSideExit* exitedFrom, j "trying to attach another branch to the tree (hits = %d)\n", c->hits()); int32_t& hits = c->hits(); - if (outer || (hits++ >= HOTEXIT && hits <= HOTEXIT+MAXEXIT)) { + int32_t maxHits = HOTEXIT + MAXEXIT; + if (anchor->exitType == CASE_EXIT) + maxHits *= anchor->switchInfo->count; + if (outer || (hits++ >= HOTEXIT && hits <= maxHits)) { /* start tracing secondary trace from this point */ unsigned stackSlots; unsigned ngslots; From 6b623c429295dda25ffa0e434e01544bbfdb91b4 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 11 Dec 2009 12:17:58 -0800 Subject: [PATCH 40/82] Fixed recursion not tracing when hitting JSOP_STOP instead of JSOP_RETURN (bug 530900, r=gal). --- js/src/jsrecursion.cpp | 118 ++++++++++++++++++++++++++--------------- js/src/jstracer.cpp | 8 ++- 2 files changed, 82 insertions(+), 44 deletions(-) diff --git a/js/src/jsrecursion.cpp b/js/src/jsrecursion.cpp index 2ad62fdc407..d5f28d41bf7 100644 --- a/js/src/jsrecursion.cpp +++ b/js/src/jsrecursion.cpp @@ -156,9 +156,19 @@ TraceRecorder::downSnapshot(FrameInfo* downFrame) unsigned exitTypeMapLen = downPostSlots + 1 + ngslots; JSTraceType* exitTypeMap = (JSTraceType*)alloca(sizeof(JSTraceType) * exitTypeMapLen); JSTraceType* typeMap = downFrame->get_typemap(); + + /* Add stack slots. */ for (unsigned i = 0; i < downPostSlots; i++) exitTypeMap[i] = typeMap[i]; - exitTypeMap[downPostSlots] = determineSlotType(&stackval(-1)); + + /* Add the return type. */ + JS_ASSERT_IF(*cx->fp->regs->pc != JSOP_RETURN, *cx->fp->regs->pc == JSOP_STOP); + if (*cx->fp->regs->pc == JSOP_RETURN) + exitTypeMap[downPostSlots] = determineSlotType(&stackval(-1)); + else + exitTypeMap[downPostSlots] = TT_PSEUDOBOOLEAN; + + /* Add global types. */ determineGlobalTypes(&exitTypeMap[downPostSlots + 1]); VMSideExit* exit = (VMSideExit*) @@ -246,9 +256,9 @@ TraceRecorder::upRecursion() * This is always safe because this point is only reached on simple "call myself" * recursive functions. */ - #if defined DEBUG +#if defined DEBUG AssertDownFrameIsConsistent(cx, anchor, fi); - #endif +#endif fi = anchor->recursive_down; } else if (recursive_pc != fragment->root->ip) { /* @@ -296,12 +306,19 @@ TraceRecorder::upRecursion() */ exit = downSnapshot(fi); - LIns* rval_ins = (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) ? - get(&stackval(-1)) : - NULL; - JS_ASSERT(rval_ins != NULL); + LIns* rval_ins; + if (*cx->fp->regs->pc == JSOP_RETURN) { + rval_ins = (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) ? + get(&stackval(-1)) : + NULL; + JS_ASSERT(rval_ins); + } else { + rval_ins = INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID)); + } + JSTraceType returnType = exit->stackTypeMap()[downPostSlots]; if (returnType == TT_INT32) { + JS_ASSERT(*cx->fp->regs->pc == JSOP_RETURN); JS_ASSERT(determineSlotType(&stackval(-1)) == TT_INT32); JS_ASSERT(isPromoteInt(rval_ins)); rval_ins = ::demote(lir, rval_ins); @@ -310,7 +327,10 @@ TraceRecorder::upRecursion() UpRecursiveSlotMap slotMap(*this, downPostSlots, rval_ins); for (unsigned i = 0; i < downPostSlots; i++) slotMap.addSlot(exit->stackType(i)); - slotMap.addSlot(&stackval(-1)); + if (*cx->fp->regs->pc == JSOP_RETURN) + slotMap.addSlot(&stackval(-1)); + else + slotMap.addSlot(TT_PSEUDOBOOLEAN); VisitGlobalSlots(slotMap, cx, *tree->globalSlots); if (recursive_pc == (jsbytecode*)fragment->root->ip) { debug_only_print0(LC_TMTracer, "Compiling up-recursive loop...\n"); @@ -447,10 +467,15 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc) cx->fp->regs->pc = exit->pc; js_CaptureStackTypes(cx, frameDepth, typeMap); cx->fp->regs->pc = oldpc; - if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) - typeMap[downPostSlots] = determineSlotType(&stackval(-1)); - else + if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { + JS_ASSERT_IF(*cx->fp->regs->pc != JSOP_RETURN, *cx->fp->regs->pc == JSOP_STOP); + if (*cx->fp->regs->pc == JSOP_RETURN) + typeMap[downPostSlots] = determineSlotType(&stackval(-1)); + else + typeMap[downPostSlots] = TT_PSEUDOBOOLEAN; + } else { typeMap[downPostSlots] = anchor->stackTypeMap()[anchor->numStackSlots - 1]; + } determineGlobalTypes(&typeMap[exit->numStackSlots]); #if defined JS_JIT_SPEW TreevisLogExit(cx, exit); @@ -466,39 +491,43 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc) * grabbed safely. */ LIns* rval_ins; - JSTraceType returnType = exit->stackTypeMap()[downPostSlots]; - if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { - rval_ins = get(&stackval(-1)); - if (returnType == TT_INT32) { - JS_ASSERT(determineSlotType(&stackval(-1)) == TT_INT32); - JS_ASSERT(isPromoteInt(rval_ins)); - rval_ins = ::demote(lir, rval_ins); + if (*cx->fp->regs->pc == JSOP_RETURN) { + JSTraceType returnType = exit->stackTypeMap()[downPostSlots]; + if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { + rval_ins = get(&stackval(-1)); + if (returnType == TT_INT32) { + JS_ASSERT(determineSlotType(&stackval(-1)) == TT_INT32); + JS_ASSERT(isPromoteInt(rval_ins)); + rval_ins = ::demote(lir, rval_ins); + } + /* + * The return value must be written out early, before slurping can fail, + * otherwise it will not be available when there's a type mismatch. + */ + lir->insStorei(rval_ins, lirbuf->sp, exit->sp_adj - sizeof(double)); + } else { + switch (returnType) + { + case TT_PSEUDOBOOLEAN: + case TT_INT32: + rval_ins = lir->insLoad(LIR_ld, lirbuf->sp, exit->sp_adj - sizeof(double)); + break; + case TT_DOUBLE: + rval_ins = lir->insLoad(LIR_ldq, lirbuf->sp, exit->sp_adj - sizeof(double)); + break; + case TT_FUNCTION: + case TT_OBJECT: + case TT_STRING: + case TT_NULL: + rval_ins = lir->insLoad(LIR_ldp, lirbuf->sp, exit->sp_adj - sizeof(double)); + break; + default: + JS_NOT_REACHED("unknown type"); + RETURN_STOP_A("unknown type"); + } } - /* - * The return value must be written out early, before slurping can fail, - * otherwise it will not be available when there's a type mismatch. - */ - lir->insStorei(rval_ins, lirbuf->sp, exit->sp_adj - sizeof(double)); } else { - switch (returnType) - { - case TT_PSEUDOBOOLEAN: - case TT_INT32: - rval_ins = lir->insLoad(LIR_ld, lirbuf->sp, exit->sp_adj - sizeof(double)); - break; - case TT_DOUBLE: - rval_ins = lir->insLoad(LIR_ldq, lirbuf->sp, exit->sp_adj - sizeof(double)); - break; - case TT_FUNCTION: - case TT_OBJECT: - case TT_STRING: - case TT_NULL: - rval_ins = lir->insLoad(LIR_ldp, lirbuf->sp, exit->sp_adj - sizeof(double)); - break; - default: - JS_NOT_REACHED("unknown type"); - RETURN_STOP_A("unknown type"); - } + rval_ins = INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID)); } /* Slurp */ @@ -556,7 +585,10 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc) RecursiveSlotMap slotMap(*this, downPostSlots, rval_ins); for (unsigned i = 0; i < downPostSlots; i++) slotMap.addSlot(typeMap[i]); - slotMap.addSlot(&stackval(-1), typeMap[downPostSlots]); + if (*cx->fp->regs->pc == JSOP_RETURN) + slotMap.addSlot(&stackval(-1), typeMap[downPostSlots]); + else + slotMap.addSlot(TT_PSEUDOBOOLEAN); VisitGlobalSlots(slotMap, cx, *tree->globalSlots); debug_only_print0(LC_TMTracer, "Compiling up-recursive slurp...\n"); exit = copy(exit); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index e565b215ef1..37baaad9c5f 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -4682,7 +4682,8 @@ TraceRecorder::closeLoop(SlotMap& slotMap, VMSideExit* exit) * case this loop was blacklisted in the meantime, JSOP_NOP. */ JS_ASSERT((*cx->fp->regs->pc == JSOP_TRACE || *cx->fp->regs->pc == JSOP_NOP || - *cx->fp->regs->pc == JSOP_RETURN) && !cx->fp->imacpc); + *cx->fp->regs->pc == JSOP_RETURN || *cx->fp->regs->pc == JSOP_STOP) && + !cx->fp->imacpc); if (callDepth != 0) { debug_only_print0(LC_TMTracer, @@ -14204,6 +14205,11 @@ TraceRecorder::record_JSOP_CALLELEM() JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_STOP() { + if (callDepth == 0 && IsTraceableRecursion(cx) && + tree->recursion != Recursion_Disallowed && + tree->script == cx->fp->script) { + return InjectStatus(upRecursion()); + } JSStackFrame *fp = cx->fp; if (fp->imacpc) { From 7ef11f37b897f86103163c6c0a8409ef547561bb Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 11 Dec 2009 12:26:08 -0800 Subject: [PATCH 41/82] Fixed rare case of not connecting trees properly when missing globals are involved (bug 531513, r=gal). --- js/src/jstracer.cpp | 51 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 12 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 37baaad9c5f..7fbf89b877b 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -2293,6 +2293,31 @@ MergeTypeMaps(JSTraceType** partial, unsigned* plength, JSTraceType* complete, u *plength = clength; } +/* + * Specializes a tree to any specifically missing globals, including any + * dependent trees. + */ +static JS_REQUIRES_STACK void +SpecializeTreesToLateGlobals(JSContext* cx, TreeFragment* root, JSTraceType* globalTypeMap, + unsigned numGlobalSlots) +{ + for (unsigned i = root->nGlobalTypes(); i < numGlobalSlots; i++) + root->typeMap.add(globalTypeMap[i]); + + JS_ASSERT(root->nGlobalTypes() == numGlobalSlots); + + for (unsigned i = 0; i < root->dependentTrees.length(); i++) { + TreeFragment* tree = root->dependentTrees[i]; + if (tree->code() && tree->nGlobalTypes() < numGlobalSlots) + SpecializeTreesToLateGlobals(cx, tree, globalTypeMap, numGlobalSlots); + } + for (unsigned i = 0; i < root->linkedTrees.length(); i++) { + TreeFragment* tree = root->linkedTrees[i]; + if (tree->code() && tree->nGlobalTypes() < numGlobalSlots) + SpecializeTreesToLateGlobals(cx, tree, globalTypeMap, numGlobalSlots); + } +} + /* Specializes a tree to any missing globals, including any dependent trees. */ static JS_REQUIRES_STACK void SpecializeTreesToMissingGlobals(JSContext* cx, JSObject* globalObj, TreeFragment* root) @@ -2300,18 +2325,8 @@ SpecializeTreesToMissingGlobals(JSContext* cx, JSObject* globalObj, TreeFragment root->typeMap.captureMissingGlobalTypes(cx, globalObj, *root->globalSlots, root->nStackTypes); JS_ASSERT(root->globalSlots->length() == root->typeMap.length() - root->nStackTypes); - for (unsigned i = 0; i < root->dependentTrees.length(); i++) { - TreeFragment* f = root->dependentTrees[i]; - /* code() can be NULL if we hit the recording tree in emitTreeCall; this is harmless. */ - if (f->code() && f->nGlobalTypes() < f->globalSlots->length()) - SpecializeTreesToMissingGlobals(cx, globalObj, f); - } - for (unsigned i = 0; i < root->linkedTrees.length(); i++) { - TreeFragment* f = root->linkedTrees[i]; - if (f->code() && f->nGlobalTypes() < f->globalSlots->length()) - SpecializeTreesToMissingGlobals(cx, globalObj, f); - } + SpecializeTreesToLateGlobals(cx, root, root->globalTypeMap(), root->nGlobalTypes()); } static JS_REQUIRES_STACK void @@ -4890,8 +4905,20 @@ TraceRecorder::joinEdgesToEntry(TreeFragment* peer_root) debug_only_printf(LC_TMTracer, "Joining type-stable trace to target exit %p->%p.\n", (void*)uexit->fragment, (void*)uexit->exit); + + /* + * See bug 531513. Before linking these trees, make sure the + * peer's dependency graph is up to date. + */ + TreeFragment* from = uexit->exit->root(); + if (from->nGlobalTypes() < tree->nGlobalTypes()) { + SpecializeTreesToLateGlobals(cx, from, tree->globalTypeMap(), + tree->nGlobalTypes()); + } + /* It's okay! Link together and remove the unstable exit. */ - JoinPeers(traceMonitor->assembler, uexit->exit, (TreeFragment*)fragment); + JS_ASSERT(tree == fragment); + JoinPeers(traceMonitor->assembler, uexit->exit, tree); uexit = peer->removeUnstableExit(uexit->exit); } else { /* Check for int32->double slots that suggest trashing. */ From 142909c3ddb74f236e5c2f4b186d31439dba2fc0 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Fri, 11 Dec 2009 16:12:48 -0800 Subject: [PATCH 42/82] Avoid slow linear growth of Queues used in the JIT (534168, r=dvander). --- js/src/jstracer.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 04e6ee648bb..8d439de564c 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -66,10 +66,11 @@ class Queue { public: void ensure(unsigned size) { + if (_max > size) + return; if (!_max) - _max = 16; - while (_max < size) - _max <<= 1; + _max = 8; + _max = JS_MAX(_max * 2, size); if (alloc) { T* tmp = new (*alloc) T[_max]; memcpy(tmp, _data, _len * sizeof(T)); From 92b5f8535146c24d2e8b377e7ea6ab8e3bdb998f Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 11 Dec 2009 17:40:06 -0800 Subject: [PATCH 43/82] Backed out changeset 783ce7ce6ed7 (possible orange). --- js/src/jsrecursion.cpp | 118 +++++++++++++++-------------------------- js/src/jstracer.cpp | 8 +-- 2 files changed, 44 insertions(+), 82 deletions(-) diff --git a/js/src/jsrecursion.cpp b/js/src/jsrecursion.cpp index d5f28d41bf7..2ad62fdc407 100644 --- a/js/src/jsrecursion.cpp +++ b/js/src/jsrecursion.cpp @@ -156,19 +156,9 @@ TraceRecorder::downSnapshot(FrameInfo* downFrame) unsigned exitTypeMapLen = downPostSlots + 1 + ngslots; JSTraceType* exitTypeMap = (JSTraceType*)alloca(sizeof(JSTraceType) * exitTypeMapLen); JSTraceType* typeMap = downFrame->get_typemap(); - - /* Add stack slots. */ for (unsigned i = 0; i < downPostSlots; i++) exitTypeMap[i] = typeMap[i]; - - /* Add the return type. */ - JS_ASSERT_IF(*cx->fp->regs->pc != JSOP_RETURN, *cx->fp->regs->pc == JSOP_STOP); - if (*cx->fp->regs->pc == JSOP_RETURN) - exitTypeMap[downPostSlots] = determineSlotType(&stackval(-1)); - else - exitTypeMap[downPostSlots] = TT_PSEUDOBOOLEAN; - - /* Add global types. */ + exitTypeMap[downPostSlots] = determineSlotType(&stackval(-1)); determineGlobalTypes(&exitTypeMap[downPostSlots + 1]); VMSideExit* exit = (VMSideExit*) @@ -256,9 +246,9 @@ TraceRecorder::upRecursion() * This is always safe because this point is only reached on simple "call myself" * recursive functions. */ -#if defined DEBUG + #if defined DEBUG AssertDownFrameIsConsistent(cx, anchor, fi); -#endif + #endif fi = anchor->recursive_down; } else if (recursive_pc != fragment->root->ip) { /* @@ -306,19 +296,12 @@ TraceRecorder::upRecursion() */ exit = downSnapshot(fi); - LIns* rval_ins; - if (*cx->fp->regs->pc == JSOP_RETURN) { - rval_ins = (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) ? - get(&stackval(-1)) : - NULL; - JS_ASSERT(rval_ins); - } else { - rval_ins = INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID)); - } - + LIns* rval_ins = (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) ? + get(&stackval(-1)) : + NULL; + JS_ASSERT(rval_ins != NULL); JSTraceType returnType = exit->stackTypeMap()[downPostSlots]; if (returnType == TT_INT32) { - JS_ASSERT(*cx->fp->regs->pc == JSOP_RETURN); JS_ASSERT(determineSlotType(&stackval(-1)) == TT_INT32); JS_ASSERT(isPromoteInt(rval_ins)); rval_ins = ::demote(lir, rval_ins); @@ -327,10 +310,7 @@ TraceRecorder::upRecursion() UpRecursiveSlotMap slotMap(*this, downPostSlots, rval_ins); for (unsigned i = 0; i < downPostSlots; i++) slotMap.addSlot(exit->stackType(i)); - if (*cx->fp->regs->pc == JSOP_RETURN) - slotMap.addSlot(&stackval(-1)); - else - slotMap.addSlot(TT_PSEUDOBOOLEAN); + slotMap.addSlot(&stackval(-1)); VisitGlobalSlots(slotMap, cx, *tree->globalSlots); if (recursive_pc == (jsbytecode*)fragment->root->ip) { debug_only_print0(LC_TMTracer, "Compiling up-recursive loop...\n"); @@ -467,15 +447,10 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc) cx->fp->regs->pc = exit->pc; js_CaptureStackTypes(cx, frameDepth, typeMap); cx->fp->regs->pc = oldpc; - if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { - JS_ASSERT_IF(*cx->fp->regs->pc != JSOP_RETURN, *cx->fp->regs->pc == JSOP_STOP); - if (*cx->fp->regs->pc == JSOP_RETURN) - typeMap[downPostSlots] = determineSlotType(&stackval(-1)); - else - typeMap[downPostSlots] = TT_PSEUDOBOOLEAN; - } else { + if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) + typeMap[downPostSlots] = determineSlotType(&stackval(-1)); + else typeMap[downPostSlots] = anchor->stackTypeMap()[anchor->numStackSlots - 1]; - } determineGlobalTypes(&typeMap[exit->numStackSlots]); #if defined JS_JIT_SPEW TreevisLogExit(cx, exit); @@ -491,43 +466,39 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc) * grabbed safely. */ LIns* rval_ins; - if (*cx->fp->regs->pc == JSOP_RETURN) { - JSTraceType returnType = exit->stackTypeMap()[downPostSlots]; - if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { - rval_ins = get(&stackval(-1)); - if (returnType == TT_INT32) { - JS_ASSERT(determineSlotType(&stackval(-1)) == TT_INT32); - JS_ASSERT(isPromoteInt(rval_ins)); - rval_ins = ::demote(lir, rval_ins); - } - /* - * The return value must be written out early, before slurping can fail, - * otherwise it will not be available when there's a type mismatch. - */ - lir->insStorei(rval_ins, lirbuf->sp, exit->sp_adj - sizeof(double)); - } else { - switch (returnType) - { - case TT_PSEUDOBOOLEAN: - case TT_INT32: - rval_ins = lir->insLoad(LIR_ld, lirbuf->sp, exit->sp_adj - sizeof(double)); - break; - case TT_DOUBLE: - rval_ins = lir->insLoad(LIR_ldq, lirbuf->sp, exit->sp_adj - sizeof(double)); - break; - case TT_FUNCTION: - case TT_OBJECT: - case TT_STRING: - case TT_NULL: - rval_ins = lir->insLoad(LIR_ldp, lirbuf->sp, exit->sp_adj - sizeof(double)); - break; - default: - JS_NOT_REACHED("unknown type"); - RETURN_STOP_A("unknown type"); - } + JSTraceType returnType = exit->stackTypeMap()[downPostSlots]; + if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { + rval_ins = get(&stackval(-1)); + if (returnType == TT_INT32) { + JS_ASSERT(determineSlotType(&stackval(-1)) == TT_INT32); + JS_ASSERT(isPromoteInt(rval_ins)); + rval_ins = ::demote(lir, rval_ins); } + /* + * The return value must be written out early, before slurping can fail, + * otherwise it will not be available when there's a type mismatch. + */ + lir->insStorei(rval_ins, lirbuf->sp, exit->sp_adj - sizeof(double)); } else { - rval_ins = INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID)); + switch (returnType) + { + case TT_PSEUDOBOOLEAN: + case TT_INT32: + rval_ins = lir->insLoad(LIR_ld, lirbuf->sp, exit->sp_adj - sizeof(double)); + break; + case TT_DOUBLE: + rval_ins = lir->insLoad(LIR_ldq, lirbuf->sp, exit->sp_adj - sizeof(double)); + break; + case TT_FUNCTION: + case TT_OBJECT: + case TT_STRING: + case TT_NULL: + rval_ins = lir->insLoad(LIR_ldp, lirbuf->sp, exit->sp_adj - sizeof(double)); + break; + default: + JS_NOT_REACHED("unknown type"); + RETURN_STOP_A("unknown type"); + } } /* Slurp */ @@ -585,10 +556,7 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc) RecursiveSlotMap slotMap(*this, downPostSlots, rval_ins); for (unsigned i = 0; i < downPostSlots; i++) slotMap.addSlot(typeMap[i]); - if (*cx->fp->regs->pc == JSOP_RETURN) - slotMap.addSlot(&stackval(-1), typeMap[downPostSlots]); - else - slotMap.addSlot(TT_PSEUDOBOOLEAN); + slotMap.addSlot(&stackval(-1), typeMap[downPostSlots]); VisitGlobalSlots(slotMap, cx, *tree->globalSlots); debug_only_print0(LC_TMTracer, "Compiling up-recursive slurp...\n"); exit = copy(exit); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 37baaad9c5f..e565b215ef1 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -4682,8 +4682,7 @@ TraceRecorder::closeLoop(SlotMap& slotMap, VMSideExit* exit) * case this loop was blacklisted in the meantime, JSOP_NOP. */ JS_ASSERT((*cx->fp->regs->pc == JSOP_TRACE || *cx->fp->regs->pc == JSOP_NOP || - *cx->fp->regs->pc == JSOP_RETURN || *cx->fp->regs->pc == JSOP_STOP) && - !cx->fp->imacpc); + *cx->fp->regs->pc == JSOP_RETURN) && !cx->fp->imacpc); if (callDepth != 0) { debug_only_print0(LC_TMTracer, @@ -14205,11 +14204,6 @@ TraceRecorder::record_JSOP_CALLELEM() JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::record_JSOP_STOP() { - if (callDepth == 0 && IsTraceableRecursion(cx) && - tree->recursion != Recursion_Disallowed && - tree->script == cx->fp->script) { - return InjectStatus(upRecursion()); - } JSStackFrame *fp = cx->fp; if (fp->imacpc) { From e72392a6679670fc8b4158f0844ccb2fbe8c9ac6 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 11 Dec 2009 19:10:36 -0800 Subject: [PATCH 44/82] Lazily import stack and global slots (bug 515749, original patch and r=gal). --- js/src/jsrecursion.cpp | 2 +- js/src/jstracer.cpp | 248 ++++++++++++++++++++++++++++------------- js/src/jstracer.h | 25 ++++- 3 files changed, 196 insertions(+), 79 deletions(-) diff --git a/js/src/jsrecursion.cpp b/js/src/jsrecursion.cpp index 2ad62fdc407..2ea643d9c3c 100644 --- a/js/src/jsrecursion.cpp +++ b/js/src/jsrecursion.cpp @@ -600,7 +600,7 @@ TraceRecorder::downRecursion() lirbuf->rp = lir->ins2(LIR_piadd, lirbuf->rp, lir->insImmWord(sizeof(FrameInfo*))); lir->insStorei(lirbuf->rp, lirbuf->state, offsetof(InterpState, rp)); --callDepth; - clearFrameSlotsFromCache(); + clearFrameSlotsFromTracker(nativeFrameTracker); /* * If the callee and caller have identical call sites, this is a down- diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 3f605744405..e31909da657 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -2235,6 +2235,15 @@ public: } }; +void +TypeMap::set(unsigned stackSlots, unsigned ngslots, + const JSTraceType* stackTypeMap, const JSTraceType* globalTypeMap) +{ + setLength(ngslots + stackSlots); + memcpy(data(), stackTypeMap, stackSlots * sizeof(JSTraceType)); + memcpy(data() + stackSlots, globalTypeMap, ngslots * sizeof(JSTraceType)); +} + /* * Capture the type map for the selected slots of the global object and currently pending * stack frames. @@ -2373,6 +2382,7 @@ TraceRecorder::TraceRecorder(JSContext* cx, VMSideExit* anchor, VMFragment* frag eos_ins(NULL), eor_ins(NULL), loopLabel(NULL), + importTypeMap(&tempAlloc()), lirbuf(new (tempAlloc()) LirBuffer(tempAlloc())), mark(*traceMonitor->traceAlloc), numSideExitsBefore(tree->sideExits.length()), @@ -2688,14 +2698,20 @@ TraceRecorder::p2i(nanojit::LIns* ins) #endif } +ptrdiff_t +TraceRecorder::nativeGlobalSlot(jsval* p) const +{ + JS_ASSERT(isGlobal(p)); + if (size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS) + return ptrdiff_t(p - globalObj->fslots); + return ptrdiff_t((p - globalObj->dslots) + JS_INITIAL_NSLOTS); +} + /* Determine the offset in the native global frame for a jsval we track. */ ptrdiff_t TraceRecorder::nativeGlobalOffset(jsval* p) const { - JS_ASSERT(isGlobal(p)); - if (size_t(p - globalObj->fslots) < JS_INITIAL_NSLOTS) - return size_t(p - globalObj->fslots) * sizeof(double); - return ((p - globalObj->dslots) + JS_INITIAL_NSLOTS) * sizeof(double); + return nativeGlobalSlot(p) * sizeof(double); } /* Determine whether a value is a global stack slot. */ @@ -2730,6 +2746,13 @@ TraceRecorder::nativeStackOffset(jsval* p) const } return offset; } + +JS_REQUIRES_STACK ptrdiff_t +TraceRecorder::nativeStackSlot(jsval* p) const +{ + return nativeStackOffset(p) / sizeof(double); +} + /* * Return the offset, from InterpState:sp, for the given jsval. Shorthand for: * -TreeFragment::nativeStackBase + nativeStackOffset(p). @@ -2828,9 +2851,10 @@ ValueToNative(JSContext* cx, jsval v, JSTraceType type, double* slot) #endif return; } + default: + JS_NOT_REACHED("unexpected type"); + break; } - - JS_NOT_REACHED("unexpected type"); } void @@ -3001,6 +3025,9 @@ js_NativeToValue(JSContext* cx, jsval& v, JSTraceType type, double* slot) #endif break; } + default: + JS_NOT_REACHED("unexpected type"); + break; } return true; } @@ -3628,38 +3655,6 @@ public: } }; -class ImportUnboxedStackSlotVisitor : public SlotVisitorBase -{ - TraceRecorder &mRecorder; - LIns *mBase; - ptrdiff_t mStackOffset; - JSTraceType *mTypemap; - JSStackFrame *mFp; -public: - ImportUnboxedStackSlotVisitor(TraceRecorder &recorder, - LIns *base, - ptrdiff_t stackOffset, - JSTraceType *typemap) : - mRecorder(recorder), - mBase(base), - mStackOffset(stackOffset), - mTypemap(typemap) - {} - - JS_REQUIRES_STACK JS_ALWAYS_INLINE bool - visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) { - for (size_t i = 0; i < count; ++i) { - if (*mTypemap != TT_JSVAL) { - mRecorder.import(mBase, mStackOffset, vp++, *mTypemap, - stackSlotKind(), i, fp); - } - mTypemap++; - mStackOffset += sizeof(double); - } - return true; - } -}; - JS_REQUIRES_STACK void TraceRecorder::import(TreeFragment* tree, LIns* sp, unsigned stackSlots, unsigned ngslots, unsigned callDepth, JSTraceType* typeMap) @@ -3692,26 +3687,24 @@ TraceRecorder::import(TreeFragment* tree, LIns* sp, unsigned stackSlots, unsigne (JSTraceType*)alloca(sizeof(JSTraceType) * length)); } JS_ASSERT(ngslots == tree->nGlobalTypes()); - ptrdiff_t offset = -tree->nativeStackBase; /* * Check whether there are any values on the stack we have to unbox and do * that first before we waste any time fetching the state from the stack. */ if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { - ImportBoxedStackSlotVisitor boxedStackVisitor(*this, sp, offset, typeMap); + ImportBoxedStackSlotVisitor boxedStackVisitor(*this, sp, -tree->nativeStackBase, typeMap); VisitStackSlots(boxedStackVisitor, cx, callDepth); } - ImportGlobalSlotVisitor globalVisitor(*this, eos_ins, globalTypeMap); - VisitGlobalSlots(globalVisitor, cx, globalObj, ngslots, - tree->globalSlots->data()); - if (!anchor || anchor->exitType != RECURSIVE_SLURP_FAIL_EXIT) { - ImportUnboxedStackSlotVisitor unboxedStackVisitor(*this, sp, offset, - typeMap); - VisitStackSlots(unboxedStackVisitor, cx, callDepth); - } + /* + * Remember the import type map so we can lazily import later whatever + * we need. + */ + importTypeMap.set(importStackSlots = stackSlots, + importGlobalSlots = ngslots, + typeMap, globalTypeMap); } JS_REQUIRES_STACK bool @@ -3738,13 +3731,41 @@ TraceRecorder::isValidSlot(JSScope* scope, JSScopeProperty* sprop) return true; } +/* Lazily import a global slot if we don't already have it in the tracker. */ +JS_REQUIRES_STACK void +TraceRecorder::importGlobalSlot(unsigned slot) +{ + JS_ASSERT(slot == uint16(slot)); + JS_ASSERT(STOBJ_NSLOTS(globalObj) <= MAX_GLOBAL_SLOTS); + + jsval* vp = &STOBJ_GET_SLOT(globalObj, slot); + JS_ASSERT(!known(vp)); + + /* Add the slot to the list of interned global slots. */ + JSTraceType type; + int index = tree->globalSlots->offsetOf(slot); + if (index == -1) { + type = getCoercedType(*vp); + if (type == TT_INT32 && oracle.isGlobalSlotUndemotable(cx, slot)) + type = TT_DOUBLE; + index = (int)tree->globalSlots->length(); + tree->globalSlots->add(slot); + tree->typeMap.add(type); + SpecializeTreesToMissingGlobals(cx, globalObj, tree); + JS_ASSERT(tree->nGlobalTypes() == tree->globalSlots->length()); + } else { + type = importTypeMap[importStackSlots + index]; + JS_ASSERT(type != TT_IGNORE); + } + import(eos_ins, slot * sizeof(double), vp, type, "global", index, NULL); +} + /* Lazily import a global slot if we don't already have it in the tracker. */ JS_REQUIRES_STACK bool TraceRecorder::lazilyImportGlobalSlot(unsigned slot) { if (slot != uint16(slot)) /* we use a table of 16-bit ints, bail out if that's not enough */ return false; - /* * If the global object grows too large, alloca in ExecuteTree might fail, * so abort tracing on global objects with unreasonably many slots. @@ -3754,17 +3775,7 @@ TraceRecorder::lazilyImportGlobalSlot(unsigned slot) jsval* vp = &STOBJ_GET_SLOT(globalObj, slot); if (known(vp)) return true; /* we already have it */ - unsigned index = tree->globalSlots->length(); - - /* Add the slot to the list of interned global slots. */ - JS_ASSERT(tree->nGlobalTypes() == tree->globalSlots->length()); - tree->globalSlots->add(slot); - JSTraceType type = getCoercedType(*vp); - if (type == TT_INT32 && oracle.isGlobalSlotUndemotable(cx, slot)) - type = TT_DOUBLE; - tree->typeMap.add(type); - import(eos_ins, slot*sizeof(double), vp, type, "global", index, NULL); - SpecializeTreesToMissingGlobals(cx, globalObj, tree); + importGlobalSlot(slot); return true; } @@ -3787,7 +3798,6 @@ JS_REQUIRES_STACK void TraceRecorder::set(jsval* p, LIns* i, bool initializing, bool demote) { JS_ASSERT(i != NULL); - JS_ASSERT(initializing || known(p)); checkForGlobalObjectReallocation(); tracker.set(p, i); @@ -3829,8 +3839,22 @@ TraceRecorder::set(jsval* p, LIns* i, bool initializing, bool demote) JS_REQUIRES_STACK LIns* TraceRecorder::get(jsval* p) { - JS_ASSERT(known(p)); checkForGlobalObjectReallocation(); + LIns* x = tracker.get(p); + if (x) + return x; + if (isGlobal(p)) { + unsigned slot = nativeGlobalSlot(p); + JS_ASSERT(tree->globalSlots->offsetOf(slot) != -1); + importGlobalSlot(slot); + } else { + unsigned slot = nativeStackSlot(p); + JSTraceType type = importTypeMap[slot]; + JS_ASSERT(type != TT_IGNORE); + import(lirbuf->sp, -tree->nativeStackBase + slot * sizeof(jsdouble), + p, type, "stack", slot, cx->fp); + } + JS_ASSERT(known(p)); return tracker.get(p); } @@ -4003,9 +4027,17 @@ JS_REQUIRES_STACK JSTraceType TraceRecorder::determineSlotType(jsval* vp) { JSTraceType m; - LIns* i = get(vp); if (isNumber(*vp)) { - m = isPromoteInt(i) ? TT_INT32 : TT_DOUBLE; + LIns* i = tracker.get(vp); + if (i) { + m = isPromoteInt(i) ? TT_INT32 : TT_DOUBLE; + } else if (isGlobal(vp)) { + int offset = tree->globalSlots->offsetOf(nativeGlobalSlot(vp)); + JS_ASSERT(offset != -1); + m = importTypeMap[importStackSlots + offset]; + } else { + m = importTypeMap[nativeStackSlot(vp)]; + } } else if (JSVAL_IS_OBJECT(*vp)) { if (JSVAL_IS_NULL(*vp)) m = TT_NULL; @@ -4513,7 +4545,21 @@ class SlotMap : public SlotVisitorBase JS_REQUIRES_STACK JS_ALWAYS_INLINE void addSlot(jsval* vp) { - slots.add(SlotInfo(vp, isPromoteInt(mRecorder.get(vp)))); + bool promoteInt = false; + if (isNumber(*vp)) { + if (LIns* i = mRecorder.tracker.get(vp)) { + promoteInt = isPromoteInt(i); + } else if (mRecorder.isGlobal(vp)) { + int offset = mRecorder.tree->globalSlots->offsetOf(mRecorder.nativeGlobalSlot(vp)); + JS_ASSERT(offset != -1); + promoteInt = mRecorder.importTypeMap[mRecorder.importStackSlots + offset] == + TT_INT32; + } else { + promoteInt = mRecorder.importTypeMap[mRecorder.nativeStackSlot(vp)] == + TT_INT32; + } + } + slots.add(SlotInfo(vp, promoteInt)); } JS_REQUIRES_STACK JS_ALWAYS_INLINE void @@ -5156,16 +5202,66 @@ TraceRecorder::emitTreeCall(TreeFragment* inner, VMSideExit* exit, LIns* inner_s JS_ASSERT(map[i] != TT_JSVAL); #endif + /* + * Flush values from the tracker which could have been invalidated by the + * inner tree. This means variables local to this frame and global slots. + * It's safe to keep the offset cache around, we just want to force reloads. + */ + clearFrameSlotsFromTracker(tracker); + SlotList& gslots = *tree->globalSlots; + for (unsigned i = 0; i < gslots.length(); i++) { + unsigned slot = gslots[i]; + jsval* vp = &STOBJ_GET_SLOT(globalObj, slot); + tracker.set(vp, NULL); + } + + /* + * Begin the complicated process of building a usable typemap by looking + * at all stack slots. + */ + TypeMap stackTypeMap(NULL); + stackTypeMap.setLength(NativeStackSlots(cx, callDepth)); + CaptureTypesVisitor visitor(cx, stackTypeMap.data()); + VisitStackSlots(visitor, cx, callDepth); + JS_ASSERT(stackTypeMap.length() >= exit->numStackSlots); + + /* + * The import map layout looks like: + * ------------- + * Initial Frame (callDepth=0) + * ... + * ... + * ... + * Current Frame (callDepth=N) + * ------------- + * + * Anything in between the innermost and outermost frames must be in the + * tracker at this point, so it's kind of pointless to make sure it's + * in the typemap. It'd be a pain to have separate typemaps for each + * "hole" though, so make one big one knowing that the holes won't be + * read. + * + * NOTE: It's important that setLength() doesn't mess up stuff below the + * current frame. It has to shrink safely. + */ + importTypeMap.setLength(stackTypeMap.length()); + unsigned startOfInnerFrame = stackTypeMap.length() - exit->numStackSlots; +#ifdef DEBUG + for (unsigned i = importStackSlots; i < startOfInnerFrame; i++) + stackTypeMap[i] = TT_IGNORE; +#endif + for (unsigned i = 0; i < exit->numStackSlots; i++) + importTypeMap[startOfInnerFrame + i] = exit->stackTypeMap()[i]; + /* * Bug 502604 - It is illegal to extend from the outer typemap without * first extending from the inner. Make a new typemap here. */ - TypeMap fullMap(NULL); - fullMap.add(exit->stackTypeMap(), exit->numStackSlots); - BuildGlobalTypeMapFromInnerTree(fullMap, exit); + BuildGlobalTypeMapFromInnerTree(importTypeMap, exit); - import(inner, inner_sp_ins, exit->numStackSlots, fullMap.length() - exit->numStackSlots, - exit->calldepth, fullMap.data()); + importStackSlots = stackTypeMap.length(); + importGlobalSlots = importTypeMap.length() - importStackSlots; + JS_ASSERT(importGlobalSlots == tree->globalSlots->length()); /* Restore sp and rp to their original values (we still have them in a register). */ if (callDepth > 0) { @@ -9646,7 +9742,7 @@ TraceRecorder::guardNotGlobalObject(JSObject* obj, LIns* obj_ins) } JS_REQUIRES_STACK void -TraceRecorder::clearFrameSlotsFromCache() +TraceRecorder::clearFrameSlotsFromTracker(Tracker& which) { /* * Clear out all slots of this frame in the nativeFrameTracker. Different @@ -9668,13 +9764,13 @@ TraceRecorder::clearFrameSlotsFromCache() vp = &fp->argv[-2]; vpstop = &fp->argv[argSlots(fp)]; while (vp < vpstop) - nativeFrameTracker.set(vp++, (LIns*)0); - nativeFrameTracker.set(&fp->argsobj, (LIns*)0); + which.set(vp++, (LIns*)0); + which.set(&fp->argsobj, (LIns*)0); } vp = &fp->slots[0]; vpstop = &fp->slots[fp->script->nslots]; while (vp < vpstop) - nativeFrameTracker.set(vp++, (LIns*)0); + which.set(vp++, (LIns*)0); } /* @@ -9908,7 +10004,7 @@ TraceRecorder::record_JSOP_RETURN() debug_only_printf(LC_TMTracer, "returning from %s\n", js_AtomToPrintableString(cx, cx->fp->fun->atom)); - clearFrameSlotsFromCache(); + clearFrameSlotsFromTracker(nativeFrameTracker); return ARECORD_CONTINUE; } @@ -14259,7 +14355,7 @@ TraceRecorder::record_JSOP_STOP() } else { rval_ins = INS_CONST(JSVAL_TO_SPECIAL(JSVAL_VOID)); } - clearFrameSlotsFromCache(); + clearFrameSlotsFromTracker(nativeFrameTracker); return ARECORD_CONTINUE; } diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 8d439de564c..1986641ab57 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -156,6 +156,16 @@ public: T* data() const { return _data; } + + int offsetOf(T slot) { + T* p = _data; + unsigned n = 0; + for (n = 0; n < _len; ++n) + if (*p++ == slot) + return n; + return -1; + } + }; /* @@ -337,7 +347,8 @@ enum JSTraceType_ TT_STRING = 4, /* pointer to JSString */ TT_NULL = 5, /* null */ TT_PSEUDOBOOLEAN = 6, /* true, false, or undefined (0, 1, or 2) */ - TT_FUNCTION = 7 /* pointer to JSObject whose class is js_FunctionClass */ + TT_FUNCTION = 7, /* pointer to JSObject whose class is js_FunctionClass */ + TT_IGNORE = 8 } #if defined(__GNUC__) && defined(USE_TRACE_TYPE_ENUM) __attribute__((packed)) @@ -362,6 +373,8 @@ typedef Queue SlotList; class TypeMap : public Queue { public: TypeMap(nanojit::Allocator* alloc) : Queue(alloc) {} + void set(unsigned stackSlots, unsigned ngslots, + const JSTraceType* stackTypeMap, const JSTraceType* globalTypeMap); JS_REQUIRES_STACK void captureTypes(JSContext* cx, JSObject* globalObj, SlotList& slots, unsigned callDepth); JS_REQUIRES_STACK void captureMissingGlobalTypes(JSContext* cx, JSObject* globalObj, SlotList& slots, unsigned stackSlots); @@ -941,6 +954,11 @@ class TraceRecorder nanojit::LIns* const eor_ins; nanojit::LIns* const loopLabel; + /* Lazy slot import state. */ + unsigned importStackSlots; + unsigned importGlobalSlots; + TypeMap importTypeMap; + /* * The LirBuffer used to supply memory to our LirWriter pipeline. Also contains the most recent * instruction for {sp, rp, state}. Also contains names for debug JIT spew. Should be split. @@ -1040,8 +1058,10 @@ class TraceRecorder JS_REQUIRES_STACK nanojit::GuardRecord* createGuardRecord(VMSideExit* exit); bool isGlobal(jsval* p) const; + ptrdiff_t nativeGlobalSlot(jsval *p) const; ptrdiff_t nativeGlobalOffset(jsval* p) const; JS_REQUIRES_STACK ptrdiff_t nativeStackOffset(jsval* p) const; + JS_REQUIRES_STACK ptrdiff_t nativeStackSlot(jsval* p) const; JS_REQUIRES_STACK ptrdiff_t nativespOffset(jsval* p) const; JS_REQUIRES_STACK void import(nanojit::LIns* base, ptrdiff_t offset, jsval* p, JSTraceType t, const char *prefix, uintN index, JSStackFrame *fp); @@ -1051,6 +1071,7 @@ class TraceRecorder JS_REQUIRES_STACK bool isValidSlot(JSScope* scope, JSScopeProperty* sprop); JS_REQUIRES_STACK bool lazilyImportGlobalSlot(unsigned slot); + JS_REQUIRES_STACK void importGlobalSlot(unsigned slot); JS_REQUIRES_STACK void guard(bool expected, nanojit::LIns* cond, ExitType exitType); JS_REQUIRES_STACK void guard(bool expected, nanojit::LIns* cond, VMSideExit* exit); @@ -1266,7 +1287,7 @@ class TraceRecorder ExitType exitType); JS_REQUIRES_STACK RecordingStatus guardNotGlobalObject(JSObject* obj, nanojit::LIns* obj_ins); - void clearFrameSlotsFromCache(); + void clearFrameSlotsFromTracker(Tracker& which); JS_REQUIRES_STACK void putArguments(); JS_REQUIRES_STACK RecordingStatus guardCallee(jsval& callee); JS_REQUIRES_STACK JSStackFrame *guardArguments(JSObject *obj, nanojit::LIns* obj_ins, From 79103c3e597f9c236a195820764abea111ff2c26 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Fri, 11 Dec 2009 19:48:14 -0800 Subject: [PATCH 45/82] Eliminate call snooping in the FuncFilter (534364, r=dvander). --- js/src/jstracer.cpp | 429 ++++++++++++++++++++++---------------------- js/src/jstracer.h | 1 + 2 files changed, 212 insertions(+), 218 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index e31909da657..d41c79719d0 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -1544,128 +1544,6 @@ AttemptCompilation(JSContext *cx, JSTraceMonitor* tm, JSObject* globalObj, jsbyt } } -// Forward declarations. -JS_DEFINE_CALLINFO_1(static, DOUBLE, i2f, INT32, 1, 1) -JS_DEFINE_CALLINFO_1(static, DOUBLE, u2f, UINT32, 1, 1) - -static bool -isi2f(LIns* i) -{ - if (i->isop(LIR_i2f)) - return true; - - if (nanojit::AvmCore::config.soft_float && - i->isop(LIR_qjoin) && - i->oprnd1()->isop(LIR_pcall) && - i->oprnd2()->isop(LIR_callh)) { - if (i->oprnd1()->callInfo() == &i2f_ci) - return true; - } - - return false; -} - -static bool -isu2f(LIns* i) -{ - if (i->isop(LIR_u2f)) - return true; - - if (nanojit::AvmCore::config.soft_float && - i->isop(LIR_qjoin) && - i->oprnd1()->isop(LIR_pcall) && - i->oprnd2()->isop(LIR_callh)) { - if (i->oprnd1()->callInfo() == &u2f_ci) - return true; - } - - return false; -} - -static LIns* -iu2fArg(LIns* i) -{ - if (nanojit::AvmCore::config.soft_float && - i->isop(LIR_qjoin)) { - return i->oprnd1()->arg(0); - } - - return i->oprnd1(); -} - -static LIns* -demote(LirWriter *out, LIns* i) -{ - if (i->isCall()) - return i->callArgN(0); - if (isi2f(i) || isu2f(i)) - return iu2fArg(i); - if (i->isconst()) - return i; - JS_ASSERT(i->isconstf()); - double cf = i->imm64f(); - int32_t ci = cf > 0x7fffffff ? uint32_t(cf) : int32_t(cf); - return out->insImm(ci); -} - -static bool -isPromoteInt(LIns* i) -{ - if (isi2f(i) || i->isconst()) - return true; - if (!i->isconstf()) - return false; - jsdouble d = i->imm64f(); - return d == jsdouble(jsint(d)) && !JSDOUBLE_IS_NEGZERO(d); -} - -static bool -isPromoteUint(LIns* i) -{ - if (isu2f(i) || i->isconst()) - return true; - if (!i->isconstf()) - return false; - jsdouble d = i->imm64f(); - return d == jsdouble(jsuint(d)) && !JSDOUBLE_IS_NEGZERO(d); -} - -static bool -isPromote(LIns* i) -{ - return isPromoteInt(i) || isPromoteUint(i); -} - -static bool -IsConst(LIns* i, int32_t c) -{ - return i->isconst() && i->imm32() == c; -} - -/* - * Determine whether this operand is guaranteed to not overflow the specified - * integer operation. - */ -static bool -IsOverflowSafe(LOpcode op, LIns* i) -{ - LIns* c; - switch (op) { - case LIR_add: - case LIR_sub: - return (i->isop(LIR_and) && ((c = i->oprnd2())->isconst()) && - ((c->imm32() & 0xc0000000) == 0)) || - (i->isop(LIR_rsh) && ((c = i->oprnd2())->isconst()) && - ((c->imm32() > 0))); - default: - JS_ASSERT(op == LIR_mul); - } - return (i->isop(LIR_and) && ((c = i->oprnd2())->isconst()) && - ((c->imm32() & 0xffff0000) == 0)) || - (i->isop(LIR_ush) && ((c = i->oprnd2())->isconst()) && - ((c->imm32() >= 16))); -} - /* soft float support */ static jsdouble FASTCALL @@ -1680,12 +1558,14 @@ i2f(int32 i) { return i; } +JS_DEFINE_CALLINFO_1(static, DOUBLE, i2f, INT32, 1, 1) static jsdouble FASTCALL u2f(jsuint u) { return u; } +JS_DEFINE_CALLINFO_1(static, DOUBLE, u2f, UINT32, 1, 1) static int32 FASTCALL fcmpeq(jsdouble x, jsdouble y) @@ -1750,6 +1630,27 @@ fsub(jsdouble x, jsdouble y) } JS_DEFINE_CALLINFO_2(static, DOUBLE, fsub, DOUBLE, DOUBLE, 1, 1) +static struct SoftFloatOps +{ + const CallInfo *map[LIR_sentinel]; + + SoftFloatOps() { + memset(map, 0, sizeof map); + map[LIR_i2f] = &i2f_ci; + map[LIR_u2f] = &u2f_ci; + map[LIR_fneg] = &fneg_ci; + map[LIR_fadd] = &fadd_ci; + map[LIR_fsub] = &fsub_ci; + map[LIR_fmul] = &fmul_ci; + map[LIR_fdiv] = &fdiv_ci; + map[LIR_feq] = &fcmpeq_ci; + map[LIR_flt] = &fcmplt_ci; + map[LIR_fgt] = &fcmpgt_ci; + map[LIR_fle] = &fcmple_ci; + map[LIR_fge] = &fcmpge_ci; + } +} softFloatOps; + // replace fpu ops with function calls class SoftFloatFilter: public LirWriter { @@ -1794,42 +1695,20 @@ public: } LIns *ins1(LOpcode op, LIns *a) { - switch (op) { - case LIR_i2f: - return fcall1(&i2f_ci, a); - case LIR_u2f: - return fcall1(&u2f_ci, a); - case LIR_fneg: - return fcall1(&fneg_ci, a); - case LIR_fret: + const CallInfo *ci = softFloatOps.map[op]; + if (ci) + return fcall1(ci, a); + if (op == LIR_fret) return out->ins1(op, split(a)); - default: - return out->ins1(op, a); - } + return out->ins1(op, a); } LIns *ins2(LOpcode op, LIns *a, LIns *b) { - switch (op) { - case LIR_fadd: - return fcall2(&fadd_ci, a, b); - case LIR_fsub: - return fcall2(&fsub_ci, a, b); - case LIR_fmul: - return fcall2(&fmul_ci, a, b); - case LIR_fdiv: - return fcall2(&fdiv_ci, a, b); - case LIR_feq: - return fcmp(&fcmpeq_ci, a, b); - case LIR_flt: - return fcmp(&fcmplt_ci, a, b); - case LIR_fgt: - return fcmp(&fcmpgt_ci, a, b); - case LIR_fle: - return fcmp(&fcmple_ci, a, b); - case LIR_fge: - return fcmp(&fcmpge_ci, a, b); - default: - ; + const CallInfo *ci = softFloatOps.map[op]; + if (ci) { + if ((op >= LIR_feq && op <= LIR_fge)) + return fcmp(ci, a, b); + return fcall2(ci, a, b); } return out->ins2(op, a, b); } @@ -1850,6 +1729,132 @@ public: } }; +static bool +isfop(LIns* i, LOpcode op) +{ + if (i->isop(op)) + return true; + if (nanojit::AvmCore::config.soft_float && + i->isop(LIR_qjoin) && + i->oprnd1()->isop(LIR_icall) && + i->oprnd2()->isop(LIR_callh)) { + return i->oprnd1()->callInfo() == softFloatOps.map[op]; + } + return false; +} + +static const CallInfo * +fcallinfo(LIns *i) +{ + if (nanojit::AvmCore::config.soft_float) { + if (i->isop(LIR_qjoin)) + return NULL; + i = i->oprnd1(); + return i->isop(LIR_icall) ? i->callInfo() : NULL; + } + return i->isop(LIR_fcall) ? i->callInfo() : NULL; +} + +static LIns* +fcallarg(LIns* i, int n) +{ + if (nanojit::AvmCore::config.soft_float) { + NanoAssert(i->isop(LIR_qjoin)); + return i->oprnd1()->callArgN(n); + } + NanoAssert(i->isop(LIR_fcall)); + return i->callArgN(n); +} + +static LIns* +foprnd1(LIns* i) +{ + if (nanojit::AvmCore::config.soft_float) + return fcallarg(i, 0); + return i->oprnd1(); +} + +static LIns* +foprnd2(LIns* i) +{ + if (nanojit::AvmCore::config.soft_float) + return fcallarg(i, 1); + return i->oprnd2(); +} + +static LIns* +demote(LirWriter *out, LIns* i) +{ + if (i->isCall()) + return i->callArgN(0); + if (isfop(i, LIR_i2f) || isfop(i, LIR_u2f)) + return foprnd1(i); + if (i->isconst()) + return i; + JS_ASSERT(i->isconstf()); + double cf = i->imm64f(); + int32_t ci = cf > 0x7fffffff ? uint32_t(cf) : int32_t(cf); + return out->insImm(ci); +} + +static bool +isPromoteInt(LIns* i) +{ + if (isfop(i, LIR_i2f) || i->isconst()) + return true; + if (!i->isconstf()) + return false; + jsdouble d = i->imm64f(); + return d == jsdouble(jsint(d)) && !JSDOUBLE_IS_NEGZERO(d); +} + +static bool +isPromoteUint(LIns* i) +{ + if (isfop(i, LIR_u2f) || i->isconst()) + return true; + if (!i->isconstf()) + return false; + jsdouble d = i->imm64f(); + return d == jsdouble(jsuint(d)) && !JSDOUBLE_IS_NEGZERO(d); +} + +static bool +isPromote(LIns* i) +{ + return isPromoteInt(i) || isPromoteUint(i); +} + +static bool +IsConst(LIns* i, int32_t c) +{ + return i->isconst() && i->imm32() == c; +} + +/* + * Determine whether this operand is guaranteed to not overflow the specified + * integer operation. + */ +static bool +IsOverflowSafe(LOpcode op, LIns* i) +{ + LIns* c; + switch (op) { + case LIR_add: + case LIR_sub: + return (i->isop(LIR_and) && ((c = i->oprnd2())->isconst()) && + ((c->imm32() & 0xc0000000) == 0)) || + (i->isop(LIR_rsh) && ((c = i->oprnd2())->isconst()) && + ((c->imm32() > 0))); + default: + JS_ASSERT(op == LIR_mul); + } + return (i->isop(LIR_and) && ((c = i->oprnd2())->isconst()) && + ((c->imm32() & 0xffff0000) == 0)) || + (i->isop(LIR_ush) && ((c = i->oprnd2())->isconst()) && + ((c->imm32() >= 16))); +} + class FuncFilter: public LirWriter { public: @@ -1911,70 +1916,8 @@ public: return out->ins2(LIR_add, x, y); } } - return out->ins2(v, s0, s1); } - - LIns* insCall(const CallInfo *ci, LIns* args[]) - { - if (ci == &js_DoubleToUint32_ci) { - LIns* s0 = args[0]; - if (s0->isconstf()) - return out->insImm(js_DoubleToECMAUint32(s0->imm64f())); - if (isi2f(s0) || isu2f(s0)) - return iu2fArg(s0); - } else if (ci == &js_DoubleToInt32_ci) { - LIns* s0 = args[0]; - if (s0->isconstf()) - return out->insImm(js_DoubleToECMAInt32(s0->imm64f())); - if (s0->isop(LIR_fadd) || s0->isop(LIR_fsub)) { - LIns* lhs = s0->oprnd1(); - LIns* rhs = s0->oprnd2(); - if (isPromote(lhs) && isPromote(rhs)) { - LOpcode op = f64arith_to_i32arith(s0->opcode()); - return out->ins2(op, demote(out, lhs), demote(out, rhs)); - } - } - if (isi2f(s0) || isu2f(s0)) - return iu2fArg(s0); - - // XXX ARM -- check for qjoin(call(UnboxDouble),call(UnboxDouble)) - if (s0->isCall()) { - const CallInfo* ci2 = s0->callInfo(); - if (ci2 == &js_UnboxDouble_ci) { - LIns* args2[] = { s0->callArgN(0) }; - return out->insCall(&js_UnboxInt32_ci, args2); - } else if (ci2 == &js_StringToNumber_ci) { - // callArgN's ordering is that as seen by the builtin, not as stored in - // args here. True story! - LIns* args2[] = { s0->callArgN(1), s0->callArgN(0) }; - return out->insCall(&js_StringToInt32_ci, args2); - } else if (ci2 == &js_String_p_charCodeAt0_ci) { - // Use a fast path builtin for a charCodeAt that converts to an int right away. - LIns* args2[] = { s0->callArgN(0) }; - return out->insCall(&js_String_p_charCodeAt0_int_ci, args2); - } else if (ci2 == &js_String_p_charCodeAt_ci) { - LIns* idx = s0->callArgN(1); - // If the index is not already an integer, force it to be an integer. - idx = isPromote(idx) - ? demote(out, idx) - : out->insCall(&js_DoubleToInt32_ci, &idx); - LIns* args2[] = { idx, s0->callArgN(0) }; - return out->insCall(&js_String_p_charCodeAt_int_ci, args2); - } - } - } else if (ci == &js_BoxDouble_ci) { - LIns* s0 = args[0]; - JS_ASSERT(s0->isQuad()); - if (isPromoteInt(s0)) { - LIns* args2[] = { demote(out, s0), args[1] }; - return out->insCall(&js_BoxInt32_ci, args2); - } - if (s0->isCall() && s0->callInfo() == &js_UnboxDouble_ci) - return s0->callArgN(0); - } - return out->insCall(ci, args); - } }; /* @@ -8355,9 +8298,53 @@ TraceRecorder::alu(LOpcode v, jsdouble v0, jsdouble v1, LIns* s0, LIns* s1) LIns* TraceRecorder::f2i(LIns* f) { + if (f->isconstf()) + return lir->insImm(js_DoubleToECMAInt32(f->imm64f())); + if (isfop(f, LIR_i2f) || isfop(f, LIR_u2f)) + return foprnd1(f); + if (isfop(f, LIR_fadd) || isfop(f, LIR_fsub)) { + LIns* lhs = foprnd1(f); + LIns* rhs = foprnd2(f); + if (isPromote(lhs) && isPromote(rhs)) { + LOpcode op = f64arith_to_i32arith(f->opcode()); + return lir->ins2(op, demote(lir, lhs), demote(lir, rhs)); + } + } + if (f->isCall()) { + const CallInfo* ci = f->callInfo(); + if (ci == &js_UnboxDouble_ci) { + LIns* args[] = { fcallarg(f, 0) }; + return lir->insCall(&js_UnboxInt32_ci, args); + } else if (ci == &js_StringToNumber_ci) { + LIns* args[] = { fcallarg(f, 1), fcallarg(f, 0) }; + return lir->insCall(&js_StringToInt32_ci, args); + } else if (ci == &js_String_p_charCodeAt0_ci) { + // Use a fast path builtin for a charCodeAt that converts to an int right away. + LIns* args[] = { fcallarg(f, 1) }; + return lir->insCall(&js_String_p_charCodeAt0_int_ci, args); + } else if (ci == &js_String_p_charCodeAt_ci) { + LIns* idx = fcallarg(f, 1); + // If the index is not already an integer, force it to be an integer. + idx = isPromote(idx) + ? demote(lir, idx) + : lir->insCall(&js_DoubleToInt32_ci, &idx); + LIns* args[] = { idx, fcallarg(f, 0) }; + return lir->insCall(&js_String_p_charCodeAt_int_ci, args); + } + } return lir->insCall(&js_DoubleToInt32_ci, &f); } +LIns* +TraceRecorder::f2u(LIns* f) +{ + if (f->isconstf()) + return lir->insImm(js_DoubleToECMAUint32(f->imm64f())); + if (isfop(f, LIR_i2f) || isfop(f, LIR_u2f)) + return foprnd1(f); + return lir->insCall(&js_DoubleToUint32_ci, &f); +} + JS_REQUIRES_STACK LIns* TraceRecorder::makeNumberInt32(LIns* f) { @@ -9091,8 +9078,7 @@ TraceRecorder::binary(LOpcode op) } if (leftIsNumber && rightIsNumber) { if (intop) { - LIns *args[] = { a }; - a = lir->insCall(op == LIR_ush ? &js_DoubleToUint32_ci : &js_DoubleToInt32_ci, args); + a = (op == LIR_ush) ? f2u(a) : f2i(a); b = f2i(b); } a = alu(op, lnum, rnum, a, b); @@ -9521,6 +9507,13 @@ JS_REQUIRES_STACK LIns* TraceRecorder::box_jsval(jsval v, LIns* v_ins) { if (isNumber(v)) { + JS_ASSERT(v_ins->isQuad()); + if (fcallinfo(v_ins) == &js_UnboxDouble_ci) + return fcallarg(v_ins, 0); + if (isPromoteInt(v_ins)) { + LIns* args[] = { demote(lir, v_ins), cx_ins }; + return lir->insCall(&js_BoxInt32_ci, args); + } LIns* args[] = { v_ins, cx_ins }; v_ins = lir->insCall(&js_BoxDouble_ci, args); guard(false, lir->ins2(LIR_peq, v_ins, INS_CONSTWORD(JSVAL_ERROR_COOKIE)), diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 1986641ab57..02cb4b176a0 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1145,6 +1145,7 @@ class TraceRecorder JS_REQUIRES_STACK nanojit::LIns* alu(nanojit::LOpcode op, jsdouble v0, jsdouble v1, nanojit::LIns* s0, nanojit::LIns* s1); nanojit::LIns* f2i(nanojit::LIns* f); + nanojit::LIns* f2u(nanojit::LIns* f); JS_REQUIRES_STACK nanojit::LIns* makeNumberInt32(nanojit::LIns* f); JS_REQUIRES_STACK nanojit::LIns* stringify(jsval& v); From be1555ec1c7e01a535969777be5ccbc22b9c89ae Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Sun, 13 Dec 2009 09:04:46 -0800 Subject: [PATCH 46/82] Remove sporadic else after return (534364 followup). --- js/src/jstracer.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index d41c79719d0..7624ecf44bb 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -1723,9 +1723,8 @@ public: // this function returns a double as two 32bit values, so replace // call with qjoin(qhi(call), call) return split(ci, args); - } else { - return out->insCall(ci, args); } + return out->insCall(ci, args); } }; @@ -8315,14 +8314,17 @@ TraceRecorder::f2i(LIns* f) if (ci == &js_UnboxDouble_ci) { LIns* args[] = { fcallarg(f, 0) }; return lir->insCall(&js_UnboxInt32_ci, args); - } else if (ci == &js_StringToNumber_ci) { + } + if (ci == &js_StringToNumber_ci) { LIns* args[] = { fcallarg(f, 1), fcallarg(f, 0) }; return lir->insCall(&js_StringToInt32_ci, args); - } else if (ci == &js_String_p_charCodeAt0_ci) { + } + if (ci == &js_String_p_charCodeAt0_ci) { // Use a fast path builtin for a charCodeAt that converts to an int right away. LIns* args[] = { fcallarg(f, 1) }; return lir->insCall(&js_String_p_charCodeAt0_int_ci, args); - } else if (ci == &js_String_p_charCodeAt_ci) { + } + if (ci == &js_String_p_charCodeAt_ci) { LIns* idx = fcallarg(f, 1); // If the index is not already an integer, force it to be an integer. idx = isPromote(idx) From b684411ba9f076f496b05d6a06a0625fecfe31f3 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Sun, 13 Dec 2009 21:46:37 +0300 Subject: [PATCH 47/82] bug 534493 - avoiding too much debug checks in JSScope::removeProperty. r=brendan --- js/src/jsscope.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index ad39c6f5878..ea7281b8b60 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -1594,7 +1594,13 @@ JSScope::removeProperty(JSContext *cx, jsid id) if (table) { *spp = NULL; #ifdef DEBUG - for (JSScopeProperty *aprop = lastProp; aprop; aprop = aprop->parent) + /* + * Check the consistency of the table but limit the number of + * checks not to alter significantly the complexity of the delete + * in debug builds, see bug 534493. + */ + JSScopeProperty *aprop = lastProp; + for (unsigned n = 50; aprop && n != 0; aprop = aprop->parent, --n) JS_ASSERT_IF(aprop != sprop, hasProperty(aprop)); #endif } From 60821e2d43f00007d44b7aefaa2c86db65e0f7b3 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Sun, 13 Dec 2009 21:54:46 +0300 Subject: [PATCH 48/82] restoring the test for bug 354998 as the bug 534493 is fixed now --- js/src/tests/e4x/Regress/jstests.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/tests/e4x/Regress/jstests.list b/js/src/tests/e4x/Regress/jstests.list index 5a5793a1afb..331a3861fdb 100644 --- a/js/src/tests/e4x/Regress/jstests.list +++ b/js/src/tests/e4x/Regress/jstests.list @@ -52,7 +52,7 @@ script regress-354145-03.js script regress-354145-04.js script regress-354145-05.js script regress-354145-07.js -skip-if(!xulRuntime.shell&&isDebugBuild) script regress-354998.js # very slow; test needs revising +script regress-354998.js script regress-355474-02.js script regress-355478.js script regress-355569.js From dd52a42965690dbb179874301226422308f17c2c Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Sun, 13 Dec 2009 22:06:04 +0300 Subject: [PATCH 49/82] bug 526449 - marking the slow array as HAS_PRIVATE for simpler handling of JSSLOT_ARRAY_LENGTH. r=brendan --- js/src/jsarray.cpp | 26 +++++++------------------- js/src/jsscript.cpp | 34 +++++++++++++--------------------- 2 files changed, 20 insertions(+), 40 deletions(-) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 93542388656..de475fbf1c5 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -822,22 +822,6 @@ slowarray_addProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) return JS_TRUE; } -static void -slowarray_trace(JSTracer *trc, JSObject *obj) -{ - uint32 length = obj->fslots[JSSLOT_ARRAY_LENGTH]; - - JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_SlowArrayClass); - - /* - * Move JSSLOT_ARRAY_LENGTH aside to prevent the GC from treating - * untagged integer values as objects or strings. - */ - obj->fslots[JSSLOT_ARRAY_LENGTH] = JSVAL_VOID; - js_TraceObject(trc, obj); - obj->fslots[JSSLOT_ARRAY_LENGTH] = length; -} - static JSObjectOps js_SlowArrayObjectOps; static JSObjectOps * @@ -1271,7 +1255,7 @@ JSClass js_ArrayClass = { JSClass js_SlowArrayClass = { "Array", - JSCLASS_HAS_RESERVED_SLOTS(1) | + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Array), slowarray_addProperty, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, js_TryValueOf, NULL, @@ -1338,9 +1322,14 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj) * a jsval, set our slow/sparse COUNT to the current length as a jsval, so * we can tell when only named properties have been added to a dense array * to make it slow-but-not-sparse. + * + * We do not need to make the length slot GC-safe as this slot is private + * where the implementation can store an arbitrary value. */ { - uint32 length = obj->fslots[JSSLOT_ARRAY_LENGTH]; + JS_STATIC_ASSERT(JSSLOT_ARRAY_LENGTH == JSSLOT_PRIVATE); + JS_ASSERT(js_SlowArrayClass.flags & JSCLASS_HAS_PRIVATE); + uint32 length = uint32(obj->fslots[JSSLOT_ARRAY_LENGTH]); obj->fslots[JSSLOT_ARRAY_COUNT] = INT_FITS_IN_JSVAL(length) ? INT_TO_JSVAL(length) : JSVAL_VOID; @@ -3464,7 +3453,6 @@ js_InitArrayClass(JSContext *cx, JSObject *obj) /* Initialize the ops structure used by slow arrays */ memcpy(&js_SlowArrayObjectOps, &js_ObjectOps, sizeof(JSObjectOps)); - js_SlowArrayObjectOps.trace = slowarray_trace; js_SlowArrayObjectOps.enumerate = slowarray_enumerate; js_SlowArrayObjectOps.call = NULL; diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 9b7d2ba3e0e..b785887fdf2 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -67,34 +67,26 @@ #include "jsscriptinlines.h" +const uint32 JSSLOT_EXEC_DEPTH = JSSLOT_PRIVATE + 1; +const uint32 JSSCRIPT_RESERVED_SLOTS = 1; + #if JS_HAS_SCRIPT_OBJECT static const char js_script_exec_str[] = "Script.prototype.exec"; static const char js_script_compile_str[] = "Script.prototype.compile"; -/* - * This routine requires that obj has been locked previously. - */ static jsint -GetScriptExecDepth(JSContext *cx, JSObject *obj) +GetScriptExecDepth(JSObject *obj) { - jsval v; - - JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj)); - v = LOCKED_OBJ_GET_SLOT(obj, JSSLOT_START(&js_ScriptClass)); + jsval v = obj->fslots[JSSLOT_EXEC_DEPTH]; return JSVAL_IS_VOID(v) ? 0 : JSVAL_TO_INT(v); } static void -AdjustScriptExecDepth(JSContext *cx, JSObject *obj, jsint delta) +AdjustScriptExecDepth(JSObject *obj, jsint delta) { - jsint execDepth; - - JS_LOCK_OBJ(cx, obj); - execDepth = GetScriptExecDepth(cx, obj); - LOCKED_OBJ_SET_SLOT(obj, JSSLOT_START(&js_ScriptClass), - INT_TO_JSVAL(execDepth + delta)); - JS_UNLOCK_OBJ(cx, obj); + jsint execDepth = GetScriptExecDepth(obj); + obj->fslots[JSSLOT_EXEC_DEPTH] = INT_TO_JSVAL(execDepth + delta); } #if JS_HAS_TOSOURCE @@ -272,7 +264,7 @@ script_compile_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, return JS_FALSE; JS_LOCK_OBJ(cx, obj); - execDepth = GetScriptExecDepth(cx, obj); + execDepth = GetScriptExecDepth(obj); /* * execDepth must be 0 to allow compilation here, otherwise the JSScript @@ -378,7 +370,7 @@ script_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, return JS_FALSE; /* Keep track of nesting depth for the script. */ - AdjustScriptExecDepth(cx, obj, 1); + AdjustScriptExecDepth(obj, 1); /* Must get to out label after this */ script = (JSScript *) obj->getPrivate(); @@ -397,7 +389,7 @@ script_exec_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval); out: - AdjustScriptExecDepth(cx, obj, -1); + AdjustScriptExecDepth(obj, -1); return ok; } @@ -855,7 +847,7 @@ script_thaw(JSContext *cx, uintN argc, jsval *vp) } JS_LOCK_OBJ(cx, obj); - execDepth = GetScriptExecDepth(cx, obj); + execDepth = GetScriptExecDepth(obj); /* * execDepth must be 0 to allow compilation here, otherwise the JSScript @@ -948,7 +940,7 @@ script_trace(JSTracer *trc, JSObject *obj) JS_FRIEND_DATA(JSClass) js_ScriptClass = { js_Script_str, - JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(JSSCRIPT_RESERVED_SLOTS) | JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Script), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, script_finalize, From 9ef15a98d6cdc5ea5da9dad466d3629f70ca8881 Mon Sep 17 00:00:00 2001 From: Bob Clary Date: Sun, 13 Dec 2009 16:09:11 -0800 Subject: [PATCH 50/82] bug 496127 - mark js1_8_1/trace/regress-452498-01.js as random. --- js/src/tests/js1_8_1/trace/jstests.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/tests/js1_8_1/trace/jstests.list b/js/src/tests/js1_8_1/trace/jstests.list index fdcf1184113..ece7830577b 100644 --- a/js/src/tests/js1_8_1/trace/jstests.list +++ b/js/src/tests/js1_8_1/trace/jstests.list @@ -3,7 +3,7 @@ script math-trace-tests.js script regress-451673.js # slow script regress-451974-01.js random script regress-451974-02.js # bug 524734 -fails script regress-452498-01.js +random script regress-452498-01.js script regress-458838.js script regress-462459-01.js script regress-462459-02.js From 9c736ec009b209df53338f7123fe479acdc58076 Mon Sep 17 00:00:00 2001 From: Bob Clary Date: Sun, 13 Dec 2009 16:09:12 -0800 Subject: [PATCH 51/82] bug 524731 - skip js1_5/Regress/regress-303213.js due to random timeouts. --- js/src/tests/js1_5/Regress/jstests.list | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/tests/js1_5/Regress/jstests.list b/js/src/tests/js1_5/Regress/jstests.list index 29208f6c5fc..4face6cea9f 100644 --- a/js/src/tests/js1_5/Regress/jstests.list +++ b/js/src/tests/js1_5/Regress/jstests.list @@ -114,7 +114,7 @@ script regress-295052.js script regress-295666.js script regress-299209.js script regress-299641.js -skip-if(xulRuntime.XPCOMABI.match(/x86_64/)||xulRuntime.OS=="WINNT") script regress-303213.js # bug 524731 +skip-if(!xulRuntime.shell) script regress-303213.js # bug 524731 script regress-306633.js script regress-306727.js script regress-306794.js From 4d092aea985867339c88190fec4418bbbae9cc0c Mon Sep 17 00:00:00 2001 From: Andrew Paprocki Date: Mon, 14 Dec 2009 10:08:55 +0300 Subject: [PATCH 52/82] bug 486779 - silencing SunPro CC compiler complains about js_MapKeywords. r=brendan --- js/src/jsscan.cpp | 2 +- js/src/jsscan.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/js/src/jsscan.cpp b/js/src/jsscan.cpp index 3d7ed726deb..0e305497703 100644 --- a/js/src/jsscan.cpp +++ b/js/src/jsscan.cpp @@ -146,7 +146,7 @@ js_CheckKeyword(const jschar *str, size_t length) } JS_FRIEND_API(void) -js_MapKeywords(void (*mapfun)(const char *)) +js_MapKeywords(JSMapKeywordFun mapfun) { size_t i; diff --git a/js/src/jsscan.h b/js/src/jsscan.h index 511af74fd37..e2cd10c5fb9 100644 --- a/js/src/jsscan.h +++ b/js/src/jsscan.h @@ -369,8 +369,10 @@ js_CheckKeyword(const jschar *chars, size_t length); * Friend-exported API entry point to call a mapping function on each reserved * identifier in the scanner's keyword table. */ +typedef void (*JSMapKeywordFun)(const char *); + extern JS_FRIEND_API(void) -js_MapKeywords(void (*mapfun)(const char *)); +js_MapKeywords(JSMapKeywordFun mapfun); /* * Check that str forms a valid JS identifier name. The function does not From da4f40e4dc2e757921a84ebe098877729cc48e71 Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Mon, 14 Dec 2009 10:55:17 +0300 Subject: [PATCH 53/82] bug 528486 - eliminating GCF_CHILDREN. r=brendan --- js/src/js.msg | 2 +- js/src/jsapi.cpp | 13 +- js/src/jsatom.cpp | 9 +- js/src/jscntxt.h | 10 +- js/src/jsdbgapi.cpp | 2 +- js/src/jsgc.cpp | 370 ++++++++++++++++++++++++-------------------- js/src/jsgc.h | 10 +- 7 files changed, 217 insertions(+), 199 deletions(-) diff --git a/js/src/js.msg b/js/src/js.msg index bff0d089d31..40fa9ee2cf7 100644 --- a/js/src/js.msg +++ b/js/src/js.msg @@ -86,7 +86,7 @@ MSG_DEF(JSMSG_MORE_ARGS_NEEDED, 3, 3, JSEXN_TYPEERR, "{0} requires more MSG_DEF(JSMSG_BAD_CHAR, 4, 1, JSEXN_INTERNALERR, "invalid format character {0}") MSG_DEF(JSMSG_BAD_TYPE, 5, 1, JSEXN_TYPEERR, "unknown type {0}") MSG_DEF(JSMSG_ALLOC_OVERFLOW, 6, 0, JSEXN_INTERNALERR, "allocation size overflow") -MSG_DEF(JSMSG_CANT_UNLOCK, 7, 0, JSEXN_INTERNALERR, "can't unlock memory") +MSG_DEF(JSMSG_UNUSED7, 7, 0, JSEXN_NONE, "") MSG_DEF(JSMSG_INCOMPATIBLE_PROTO, 8, 3, JSEXN_TYPEERR, "{0}.prototype.{1} called on incompatible {2}") MSG_DEF(JSMSG_NO_CONSTRUCTOR, 9, 1, JSEXN_TYPEERR, "{0} has no constructor") MSG_DEF(JSMSG_CANT_ALIAS, 10, 3, JSEXN_TYPEERR, "can't alias {0} to {1} in class {2}") diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 8fc2d6c3eff..630bb59806e 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2010,19 +2010,16 @@ JS_LockGCThingRT(JSRuntime *rt, void *thing) JS_PUBLIC_API(JSBool) JS_UnlockGCThing(JSContext *cx, void *thing) { - JSBool ok; - CHECK_REQUEST(cx); - ok = js_UnlockGCThingRT(cx->runtime, thing); - if (!ok) - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_UNLOCK); - return ok; + js_UnlockGCThingRT(cx->runtime, thing); + return true; } JS_PUBLIC_API(JSBool) JS_UnlockGCThingRT(JSRuntime *rt, void *thing) { - return js_UnlockGCThingRT(rt, thing); + js_UnlockGCThingRT(rt, thing); + return true; } JS_PUBLIC_API(void) @@ -2545,7 +2542,7 @@ JS_PUBLIC_API(JSBool) JS_IsAboutToBeFinalized(JSContext *cx, void *thing) { JS_ASSERT(thing); - return js_IsAboutToBeFinalized(cx, thing); + return js_IsAboutToBeFinalized(thing); } JS_PUBLIC_API(void) diff --git a/js/src/jsatom.cpp b/js/src/jsatom.cpp index 8ccbfde0a8e..10f7fb19fed 100644 --- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -581,7 +581,6 @@ js_atom_sweeper(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 number, void *arg) { JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); - JSContext *cx = (JSContext *)arg; /* Remove uninitialized entries. */ if (entry->keyAndFlags == 0) @@ -589,8 +588,8 @@ js_atom_sweeper(JSDHashTable *table, JSDHashEntryHdr *hdr, if (ATOM_ENTRY_FLAGS(entry) & (ATOM_PINNED | ATOM_INTERNED)) { /* Pinned or interned key cannot be finalized. */ - JS_ASSERT(!js_IsAboutToBeFinalized(cx, ATOM_ENTRY_KEY(entry))); - } else if (js_IsAboutToBeFinalized(cx, ATOM_ENTRY_KEY(entry))) { + JS_ASSERT(!js_IsAboutToBeFinalized(ATOM_ENTRY_KEY(entry))); + } else if (js_IsAboutToBeFinalized(ATOM_ENTRY_KEY(entry))) { /* Remove entries with things about to be GC'ed. */ return JS_DHASH_REMOVE; } @@ -602,8 +601,8 @@ js_SweepAtomState(JSContext *cx) { JSAtomState *state = &cx->runtime->atomState; - JS_DHashTableEnumerate(&state->doubleAtoms, js_atom_sweeper, cx); - JS_DHashTableEnumerate(&state->stringAtoms, js_atom_sweeper, cx); + JS_DHashTableEnumerate(&state->doubleAtoms, js_atom_sweeper, NULL); + JS_DHashTableEnumerate(&state->stringAtoms, js_atom_sweeper, NULL); /* * Optimize for simplicity and mutate table generation numbers even if the diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index df2d322c5d1..fec909bebfe 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -606,14 +606,10 @@ struct JSRuntime { */ ptrdiff_t gcMallocBytes; - /* - * Stack of GC arenas containing things that the GC marked, where children - * reached from those things have not yet been marked. This helps avoid - * using too much native stack during recursive GC marking. - */ - JSGCArenaInfo *gcUntracedArenaStackTop; + /* See comments before DelayMarkingChildren is jsgc.cpp. */ + JSGCArenaInfo *gcUnmarkedArenaStackTop; #ifdef DEBUG - size_t gcTraceLaterCount; + size_t gcMarkLaterCount; #endif /* diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index bb25c449274..bfc96e69982 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -526,7 +526,7 @@ js_SweepWatchPoints(JSContext *cx) &wp->links != &rt->watchPointList; wp = next) { next = (JSWatchPoint *)wp->links.next; - if (js_IsAboutToBeFinalized(cx, wp->object)) { + if (js_IsAboutToBeFinalized(wp->object)) { sample = rt->debuggerMutations; /* Ignore failures. */ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index eb891eca82b..7bcc3606d80 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -209,11 +209,13 @@ struct JSGCArenaInfo { JSGCArenaInfo *prev; /* - * A link field for the list of arenas with marked but not yet traced - * things. The field is encoded as arena's page to share the space with - * firstArena and arenaIndex fields. + * A link field for the list of arenas with marked things that haven't yet + * been scanned for live children. The field is encoded as arena's page to + * to hold only the high-order arena-counting bits to share the space with + * firstArena and arenaIndex fields. For details see comments before + * DelayMarkingChildren. */ - jsuword prevUntracedPage : JS_BITS_PER_WORD - GC_ARENA_SHIFT; + jsuword prevUnmarkedPage : JS_BITS_PER_WORD - GC_ARENA_SHIFT; /* * When firstArena is false, the index of arena in the chunk. When @@ -228,22 +230,20 @@ struct JSGCArenaInfo { /* Flag indicating if the arena is the first in the chunk. */ jsuword firstArena : 1; - union { - struct { - JSGCThing *freeList; - jsuword untracedThings; /* bitset for fast search of marked - but not yet traced things */ - } finalizable; + JSGCThing *freeList; - bool hasMarkedDoubles; /* the arena has marked doubles */ + union { + /* See comments before DelayMarkingChildren. */ + jsuword unmarkedChildren; + + /* The arena has marked doubles. */ + bool hasMarkedDoubles; }; }; /* GC flag definitions, must fit in 8 bits. */ const uint8 GCF_MARK = JS_BIT(0); const uint8 GCF_LOCK = JS_BIT(1); /* lock request bit in API */ -const uint8 GCF_CHILDREN = JS_BIT(2); /* GC things with children to be - marked later. */ /* * The private JSGCThing struct, which describes a JSRuntime.gcFreeList element. @@ -693,7 +693,7 @@ NewGCArena(JSContext *cx) } rt->gcBytes += GC_ARENA_SIZE; - a->prevUntracedPage = 0; + a->prevUnmarkedPage = 0; return a; } @@ -894,8 +894,8 @@ js_GetGCStringRuntime(JSString *str) offsetof(JSRuntime, gcArenaList)); } -JSBool -js_IsAboutToBeFinalized(JSContext *cx, void *thing) +bool +js_IsAboutToBeFinalized(void *thing) { JSGCArenaInfo *a; uint32 index, flags; @@ -1095,9 +1095,9 @@ js_DumpGCStats(JSRuntime *rt, FILE *fp) fprintf(fp, " maximum mark recursion: %lu\n", ULSTAT(maxdepth)); fprintf(fp, " mark C recursion depth: %lu\n", ULSTAT(cdepth)); fprintf(fp, " maximum mark C recursion: %lu\n", ULSTAT(maxcdepth)); - fprintf(fp, " delayed tracing calls: %lu\n", ULSTAT(untraced)); + fprintf(fp, " delayed tracing calls: %lu\n", ULSTAT(unmarked)); #ifdef DEBUG - fprintf(fp, " max trace later count: %lu\n", ULSTAT(maxuntraced)); + fprintf(fp, " max trace later count: %lu\n", ULSTAT(maxunmarked)); #endif fprintf(fp, " maximum GC nesting level: %lu\n", ULSTAT(maxlevel)); fprintf(fp, "potentially useful GC calls: %lu\n", ULSTAT(poke)); @@ -1346,7 +1346,7 @@ CloseNativeIterators(JSContext *cx) size_t newLength = 0; for (size_t i = 0; i < length; ++i) { JSObject *obj = array[i]; - if (js_IsAboutToBeFinalized(cx, obj)) + if (js_IsAboutToBeFinalized(obj)) js_CloseNativeIterator(cx, obj); else array[newLength++] = obj; @@ -1384,8 +1384,8 @@ JSGCFreeLists::purge() JSGCThing *freeListHead = *p; if (freeListHead) { JSGCArenaInfo *a = THING_TO_ARENA(freeListHead); - JS_ASSERT(!a->finalizable.freeList); - a->finalizable.freeList = freeListHead; + JS_ASSERT(!a->freeList); + a->freeList = freeListHead; *p = NULL; } } @@ -1473,9 +1473,9 @@ RefillFinalizableFreeList(JSContext *cx, unsigned thingKind) while ((a = arenaList->cursor) != NULL) { arenaList->cursor = a->prev; - JSGCThing *freeList = a->finalizable.freeList; + JSGCThing *freeList = a->freeList; if (freeList) { - a->finalizable.freeList = NULL; + a->freeList = NULL; JS_UNLOCK_GC(rt); return freeList; } @@ -1499,9 +1499,9 @@ RefillFinalizableFreeList(JSContext *cx, unsigned thingKind) */ a->list = arenaList; a->prev = arenaList->head; - a->prevUntracedPage = 0; - a->finalizable.untracedThings = 0; - a->finalizable.freeList = NULL; + a->prevUnmarkedPage = 0; + a->freeList = NULL; + a->unmarkedChildren = 0; arenaList->head = a; JS_UNLOCK_GC(rt); @@ -1720,6 +1720,7 @@ RefillDoubleFreeList(JSContext *cx) } a->list = NULL; + a->freeList = NULL; a->hasMarkedDoubles = false; a->prev = rt->gcDoubleArenaList.head; rt->gcDoubleArenaList.head = a; @@ -1876,11 +1877,11 @@ js_LockGCThingRT(JSRuntime *rt, void *thing) return ok; } -JSBool +void js_UnlockGCThingRT(JSRuntime *rt, void *thing) { if (!thing) - return JS_TRUE; + return; JS_LOCK_GC(rt); @@ -1908,7 +1909,6 @@ js_UnlockGCThingRT(JSRuntime *rt, void *thing) METER(rt->gcStats.unlock++); out: JS_UNLOCK_GC(rt); - return JS_TRUE; } JS_PUBLIC_API(void) @@ -1940,110 +1940,146 @@ JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind) } /* - * Number of things covered by a single bit of JSGCArenaInfo.untracedThings. + * When the native stack is low, the GC does not call JS_TraceChildren to mark + * the reachable "children" of the thing. Rather the thing is put aside and + * JS_TraceChildren is called later with more space on the C stack. + * + * To implement such delayed marking of the children with minimal overhead for + * the normal case of sufficient native stack, the code adds two fields to + * JSGCArenaInfo. The first field, JSGCArenaInfo::prevUnmarkedPage, links all + * arenas with delayed things into a stack list with the pointer to stack top + * in JSRuntime::gcUnmarkedArenaStackTop. DelayMarkingChildren adds arenas to + * the stack as necessary while MarkDelayedChildren pops the arenas from the + * stack until it empties. + * + * The second field, JSGCArenaInfo::unmarkedChildren, is a bitmap that tells + * for which things the GC should call JS_TraceChildren later. The bitmap is + * a single word. As such it does not pinpoint the delayed things in the arena + * but rather tells the intervals containing ThingsPerUnmarkedBit(thingSize) + * things. Later the code in MarkDelayedChildren discovers such intervals + * and calls JS_TraceChildren on any marked thing in the interval. This + * implies that JS_TraceChildren can be called many times for a single thing + * if the thing shares the same interval with some delayed things. This should + * be fine as any GC graph marking/traversing hooks must allow repeated calls + * during the same GC cycle. In particular, xpcom cycle collector relies on + * this. + * + * Note that such repeated scanning may slow down the GC. In particular, it is + * possible to construct an object graph where the GC calls JS_TraceChildren + * ThingsPerUnmarkedBit(thingSize) for almost all things in the graph. We + * tolerate this as the max value for ThingsPerUnmarkedBit(thingSize) is 4. + * This is archived for JSObject on 32 bit system as it is exactly JSObject + * that has the smallest size among the GC things that can be delayed. On 32 + * bit CPU we have less than 128 objects per 4K GC arena so each bit in + * unmarkedChildren covers 4 objects. */ -#define THINGS_PER_UNTRACED_BIT(thingSize) \ - JS_HOWMANY(THINGS_PER_ARENA(thingSize), JS_BITS_PER_WORD) - -static void -DelayTracingChildren(JSRuntime *rt, uint8 *flagp) +inline unsigned +ThingsPerUnmarkedBit(unsigned thingSize) { - JSGCArenaInfo *a; - uint32 untracedBitIndex; - jsuword bit; - - JS_ASSERT(!(*flagp & GCF_CHILDREN)); - *flagp |= GCF_CHILDREN; - - METER(rt->gcStats.untraced++); -#ifdef DEBUG - ++rt->gcTraceLaterCount; - METER_UPDATE_MAX(rt->gcStats.maxuntraced, rt->gcTraceLaterCount); -#endif - - a = FLAGP_TO_ARENA(flagp); - untracedBitIndex = FLAGP_TO_INDEX(flagp) / - THINGS_PER_UNTRACED_BIT(a->list->thingSize); - JS_ASSERT(untracedBitIndex < JS_BITS_PER_WORD); - bit = (jsuword)1 << untracedBitIndex; - if (a->finalizable.untracedThings != 0) { - JS_ASSERT(rt->gcUntracedArenaStackTop); - if (a->finalizable.untracedThings & bit) { - /* bit already covers things with children to trace later. */ - return; - } - a->finalizable.untracedThings |= bit; - } else { - /* - * The thing is the first thing with not yet traced children in the - * whole arena, so push the arena on the stack of arenas with things - * to be traced later unless the arena has already been pushed. We - * detect that through checking prevUntracedPage as the field is 0 - * only for not yet pushed arenas. To ensure that - * prevUntracedPage != 0 - * even when the stack contains one element, we make prevUntracedPage - * for the arena at the bottom to point to itself. - * - * See comments in TraceDelayedChildren. - */ - a->finalizable.untracedThings = bit; - if (a->prevUntracedPage == 0) { - if (!rt->gcUntracedArenaStackTop) { - /* Stack was empty, mark the arena as the bottom element. */ - a->prevUntracedPage = ARENA_INFO_TO_PAGE(a); - } else { - JS_ASSERT(rt->gcUntracedArenaStackTop->prevUntracedPage != 0); - a->prevUntracedPage = - ARENA_INFO_TO_PAGE(rt->gcUntracedArenaStackTop); - } - rt->gcUntracedArenaStackTop = a; - } - } - JS_ASSERT(rt->gcUntracedArenaStackTop); + return JS_HOWMANY(THINGS_PER_ARENA(thingSize), JS_BITS_PER_WORD); } static void -TraceDelayedChildren(JSTracer *trc) +DelayMarkingChildren(JSRuntime *rt, uint8 *flagp) +{ + JSGCArenaInfo *a; + uint32 unmarkedBitIndex; + jsuword bit; + + JS_ASSERT(*flagp & GCF_MARK); + + METER(rt->gcStats.unmarked++); + a = FLAGP_TO_ARENA(flagp); + unmarkedBitIndex = FLAGP_TO_INDEX(flagp) / + ThingsPerUnmarkedBit(a->list->thingSize); + JS_ASSERT(unmarkedBitIndex < JS_BITS_PER_WORD); + bit = (jsuword)1 << unmarkedBitIndex; + if (a->unmarkedChildren != 0) { + JS_ASSERT(rt->gcUnmarkedArenaStackTop); + if (a->unmarkedChildren & bit) { + /* bit already covers things with children to mark later. */ + return; + } + a->unmarkedChildren |= bit; + } else { + /* + * The thing is the first thing with not yet marked children in the + * whole arena, so push the arena on the stack of arenas with things + * to be marked later unless the arena has already been pushed. We + * detect that through checking prevUnmarkedPage as the field is 0 + * only for not yet pushed arenas. To ensure that + * prevUnmarkedPage != 0 + * even when the stack contains one element, we make prevUnmarkedPage + * for the arena at the bottom to point to itself. + * + * See comments in MarkDelayedChildren. + */ + a->unmarkedChildren = bit; + if (a->prevUnmarkedPage == 0) { + if (!rt->gcUnmarkedArenaStackTop) { + /* Stack was empty, mark the arena as the bottom element. */ + a->prevUnmarkedPage = ARENA_INFO_TO_PAGE(a); + } else { + JS_ASSERT(rt->gcUnmarkedArenaStackTop->prevUnmarkedPage != 0); + a->prevUnmarkedPage = + ARENA_INFO_TO_PAGE(rt->gcUnmarkedArenaStackTop); + } + rt->gcUnmarkedArenaStackTop = a; + } + JS_ASSERT(rt->gcUnmarkedArenaStackTop); + } +#ifdef DEBUG + rt->gcMarkLaterCount += ThingsPerUnmarkedBit(a->list->thingSize); + METER_UPDATE_MAX(rt->gcStats.maxunmarked, rt->gcMarkLaterCount); +#endif +} + +static void +MarkDelayedChildren(JSTracer *trc) { JSRuntime *rt; JSGCArenaInfo *a, *aprev; uint32 thingSize, traceKind; - uint32 thingsPerUntracedBit; - uint32 untracedBitIndex, thingIndex, indexLimit, endIndex; + uint32 thingsPerUnmarkedBit; + uint32 unmarkedBitIndex, thingIndex, indexLimit, endIndex; JSGCThing *thing; uint8 *flagp; rt = trc->context->runtime; - a = rt->gcUntracedArenaStackTop; + a = rt->gcUnmarkedArenaStackTop; if (!a) { - JS_ASSERT(rt->gcTraceLaterCount == 0); + JS_ASSERT(rt->gcMarkLaterCount == 0); return; } for (;;) { /* * The following assert verifies that the current arena belongs to the - * untraced stack, since DelayTracingChildren ensures that even for - * stack's bottom prevUntracedPage != 0 but rather points to itself. + * unmarked stack, since DelayMarkingChildren ensures that even for + * the stack's bottom, prevUnmarkedPage != 0 but rather points to + * itself. */ - JS_ASSERT(a->prevUntracedPage != 0); - JS_ASSERT(rt->gcUntracedArenaStackTop->prevUntracedPage != 0); + JS_ASSERT(a->prevUnmarkedPage != 0); + JS_ASSERT(rt->gcUnmarkedArenaStackTop->prevUnmarkedPage != 0); thingSize = a->list->thingSize; traceKind = GetFinalizableArenaTraceKind(a); indexLimit = THINGS_PER_ARENA(thingSize); - thingsPerUntracedBit = THINGS_PER_UNTRACED_BIT(thingSize); + thingsPerUnmarkedBit = ThingsPerUnmarkedBit(thingSize); /* - * We cannot use do-while loop here as a->untracedThings can be zero + * We cannot use do-while loop here as a->unmarkedChildren can be zero * before the loop as a leftover from the previous iterations. See * comments after the loop. */ - while (a->finalizable.untracedThings != 0) { - untracedBitIndex = JS_FLOOR_LOG2W(a->finalizable.untracedThings); - a->finalizable.untracedThings &= - ~((jsuword)1 << untracedBitIndex); - thingIndex = untracedBitIndex * thingsPerUntracedBit; - endIndex = thingIndex + thingsPerUntracedBit; + while (a->unmarkedChildren != 0) { + unmarkedBitIndex = JS_FLOOR_LOG2W(a->unmarkedChildren); + a->unmarkedChildren &= ~((jsuword)1 << unmarkedBitIndex); +#ifdef DEBUG + JS_ASSERT(rt->gcMarkLaterCount >= thingsPerUnmarkedBit); + rt->gcMarkLaterCount -= thingsPerUnmarkedBit; +#endif + thingIndex = unmarkedBitIndex * thingsPerUnmarkedBit; + endIndex = thingIndex + thingsPerUnmarkedBit; /* * endIndex can go beyond the last allocated thing as the real @@ -2052,22 +2088,12 @@ TraceDelayedChildren(JSTracer *trc) if (endIndex > indexLimit) endIndex = indexLimit; JS_ASSERT(thingIndex < indexLimit); - do { - /* - * Skip free or already traced things that share the bit - * with untraced ones. - */ flagp = THING_FLAGP(a, thingIndex); - if (!(*flagp & GCF_CHILDREN)) - continue; - *flagp &= ~GCF_CHILDREN; -#ifdef DEBUG - JS_ASSERT(rt->gcTraceLaterCount != 0); - --rt->gcTraceLaterCount; -#endif - thing = FLAGP_TO_THING(flagp, thingSize); - JS_TraceChildren(trc, thing, traceKind); + if (*flagp & GCF_MARK) { + thing = FLAGP_TO_THING(flagp, thingSize); + JS_TraceChildren(trc, thing, traceKind); + } } while (++thingIndex != endIndex); } @@ -2076,29 +2102,29 @@ TraceDelayedChildren(JSTracer *trc) * pop it from the stack if the arena is the stack's top. * * When JS_TraceChildren from the above calls JS_CallTracer that in - * turn on low C stack calls DelayTracingChildren and the latter - * pushes new arenas to the untraced stack, we have to skip popping + * turn on low C stack calls DelayMarkingChildren and the latter + * pushes new arenas to the unmarked stack, we have to skip popping * of this arena until it becomes the top of the stack again. */ - if (a == rt->gcUntracedArenaStackTop) { - aprev = ARENA_PAGE_TO_INFO(a->prevUntracedPage); - a->prevUntracedPage = 0; + if (a == rt->gcUnmarkedArenaStackTop) { + aprev = ARENA_PAGE_TO_INFO(a->prevUnmarkedPage); + a->prevUnmarkedPage = 0; if (a == aprev) { /* - * prevUntracedPage points to itself and we reached the + * prevUnmarkedPage points to itself and we reached the * bottom of the stack. */ break; } - rt->gcUntracedArenaStackTop = a = aprev; + rt->gcUnmarkedArenaStackTop = a = aprev; } else { - a = rt->gcUntracedArenaStackTop; + a = rt->gcUnmarkedArenaStackTop; } } - JS_ASSERT(rt->gcUntracedArenaStackTop); - JS_ASSERT(rt->gcUntracedArenaStackTop->prevUntracedPage == 0); - rt->gcUntracedArenaStackTop = NULL; - JS_ASSERT(rt->gcTraceLaterCount == 0); + JS_ASSERT(rt->gcUnmarkedArenaStackTop); + JS_ASSERT(rt->gcUnmarkedArenaStackTop->prevUnmarkedPage == 0); + rt->gcUnmarkedArenaStackTop = NULL; + JS_ASSERT(rt->gcMarkLaterCount == 0); } JS_PUBLIC_API(void) @@ -2178,7 +2204,7 @@ JS_CallTracer(JSTracer *trc, void *thing, uint32 kind) # define RECURSION_TOO_DEEP() (!JS_CHECK_STACK_SIZE(cx, stackDummy)) #endif if (RECURSION_TOO_DEEP()) - DelayTracingChildren(rt, flagp); + DelayMarkingChildren(rt, flagp); else JS_TraceChildren(trc, thing, kind); } else { @@ -2190,16 +2216,16 @@ JS_CallTracer(JSTracer *trc, void *thing, uint32 kind) * * Since we do not know which call from inside the callback is the * last, we ensure that children of all marked things are traced and - * call TraceDelayedChildren(trc) after tracing the thing. + * call MarkDelayedChildren(trc) after tracing the thing. * - * As TraceDelayedChildren unconditionally invokes JS_TraceChildren - * for the things with untraced children, calling DelayTracingChildren + * As MarkDelayedChildren unconditionally invokes JS_TraceChildren + * for the things with unmarked children, calling DelayMarkingChildren * is useless here. Hence we always trace thing's children even with a * low native stack. */ cx->insideGCMarkCallback = JS_FALSE; JS_TraceChildren(trc, thing, kind); - TraceDelayedChildren(trc); + MarkDelayedChildren(trc); cx->insideGCMarkCallback = JS_TRUE; } @@ -2220,7 +2246,7 @@ js_CallValueTracerIfGCThing(JSTracer *trc, jsval v) if (JSVAL_IS_DOUBLE(v) || JSVAL_IS_STRING(v)) { thing = JSVAL_TO_TRACEABLE(v); kind = JSVAL_TRACE_KIND(v); - JS_ASSERT(kind == js_GetGCThingTraceKind(JSVAL_TO_GCTHING(v))); + JS_ASSERT(kind == js_GetGCThingTraceKind(thing)); } else if (JSVAL_IS_OBJECT(v) && v != JSVAL_NULL) { /* v can be an arbitrary GC thing reinterpreted as an object. */ thing = JSVAL_TO_OBJECT(v); @@ -2241,41 +2267,43 @@ gc_root_traversal(JSDHashTable *table, JSDHashEntryHdr *hdr, uint32 num, jsval v = *rp; /* Ignore null reference, scalar values, and static strings. */ - if (!JSVAL_IS_NULL(v) && - JSVAL_IS_GCTHING(v) && - !JSString::isStatic(JSVAL_TO_GCTHING(v))) { + if (JSVAL_IS_TRACEABLE(v)) { #ifdef DEBUG - bool root_points_to_gcArenaList = false; - jsuword thing = (jsuword) JSVAL_TO_GCTHING(v); - JSRuntime *rt = trc->context->runtime; - for (unsigned i = 0; i != FINALIZE_LIMIT; i++) { - JSGCArenaList *arenaList = &rt->gcArenaList[i]; - size_t thingSize = arenaList->thingSize; - size_t limit = THINGS_PER_ARENA(thingSize) * thingSize; - for (JSGCArenaInfo *a = arenaList->head; a; a = a->prev) { - if (thing - ARENA_INFO_TO_START(a) < limit) { - root_points_to_gcArenaList = true; - break; + if (!JSString::isStatic(JSVAL_TO_GCTHING(v))) { + bool root_points_to_gcArenaList = false; + jsuword thing = (jsuword) JSVAL_TO_GCTHING(v); + JSRuntime *rt = trc->context->runtime; + for (unsigned i = 0; i != FINALIZE_LIMIT; i++) { + JSGCArenaList *arenaList = &rt->gcArenaList[i]; + size_t thingSize = arenaList->thingSize; + size_t limit = THINGS_PER_ARENA(thingSize) * thingSize; + for (JSGCArenaInfo *a = arenaList->head; a; a = a->prev) { + if (thing - ARENA_INFO_TO_START(a) < limit) { + root_points_to_gcArenaList = true; + break; + } } } - } - if (!root_points_to_gcArenaList) { - for (JSGCArenaInfo *a = rt->gcDoubleArenaList.head; a; a = a->prev) { - if (thing - ARENA_INFO_TO_START(a) < - DOUBLES_PER_ARENA * sizeof(jsdouble)) { - root_points_to_gcArenaList = true; - break; + if (!root_points_to_gcArenaList) { + for (JSGCArenaInfo *a = rt->gcDoubleArenaList.head; + a; + a = a->prev) { + if (thing - ARENA_INFO_TO_START(a) < + DOUBLES_PER_ARENA * sizeof(jsdouble)) { + root_points_to_gcArenaList = true; + break; + } } } - } - if (!root_points_to_gcArenaList && rhe->name) { - fprintf(stderr, + if (!root_points_to_gcArenaList && rhe->name) { + fprintf(stderr, "JS API usage error: the address passed to JS_AddNamedRoot currently holds an\n" "invalid jsval. This is usually caused by a missing call to JS_RemoveRoot.\n" "The root's name is \"%s\".\n", - rhe->name); + rhe->name); + } + JS_ASSERT(root_points_to_gcArenaList); } - JS_ASSERT(root_points_to_gcArenaList); #endif JS_SET_TRACING_NAME(trc, rhe->name ? rhe->name : "root"); js_CallValueTracerIfGCThing(trc, v); @@ -2765,8 +2793,8 @@ FinalizeArenaList(JSContext *cx, unsigned thingKind, #endif for (;;) { JS_ASSERT(a->list == arenaList); - JS_ASSERT(a->prevUntracedPage == 0); - JS_ASSERT(a->finalizable.untracedThings == 0); + JS_ASSERT(a->prevUnmarkedPage == 0); + JS_ASSERT(a->unmarkedChildren == 0); JSGCThing *freeList = NULL; JSGCThing **tailp = &freeList; @@ -2778,9 +2806,7 @@ FinalizeArenaList(JSContext *cx, unsigned thingKind, reinterpret_cast(ARENA_INFO_TO_START(a) + THINGS_PER_ARENA(sizeof(T)) * sizeof(T)); - JSGCThing* nextFree = a->finalizable.freeList - ? a->finalizable.freeList - : thingsEnd; + JSGCThing* nextFree = a->freeList ? a->freeList : thingsEnd; for (;; thing = NextThing(thing, sizeof(T)), --flagp) { if (thing == nextFree) { if (thing == thingsEnd) @@ -2844,7 +2870,7 @@ FinalizeArenaList(JSContext *cx, unsigned thingKind, } else { JS_ASSERT(nfree < THINGS_PER_ARENA(sizeof(T))); *tailp = NULL; - a->finalizable.freeList = freeList; + a->freeList = freeList; ap = &a->prev; METER(nlivearenas++); } @@ -3072,8 +3098,8 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) restart: rt->gcNumber++; - JS_ASSERT(!rt->gcUntracedArenaStackTop); - JS_ASSERT(rt->gcTraceLaterCount == 0); + JS_ASSERT(!rt->gcUnmarkedArenaStackTop); + JS_ASSERT(rt->gcMarkLaterCount == 0); /* * Reset the property cache's type id generator so we can compress ids. @@ -3118,7 +3144,7 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) * Mark children of things that caused too deep recursion during the above * tracing. */ - TraceDelayedChildren(&trc); + MarkDelayedChildren(&trc); JS_ASSERT(!cx->insideGCMarkCallback); if (rt->gcCallback) { @@ -3127,7 +3153,7 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) JS_ASSERT(cx->insideGCMarkCallback); cx->insideGCMarkCallback = JS_FALSE; } - JS_ASSERT(rt->gcTraceLaterCount == 0); + JS_ASSERT(rt->gcMarkLaterCount == 0); rt->gcMarkingTracer = NULL; diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 0ec449df9f8..dd6cdf3d263 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -148,11 +148,11 @@ js_ReserveObjects(JSContext *cx, size_t nobjects); extern JSBool js_LockGCThingRT(JSRuntime *rt, void *thing); -extern JSBool +extern void js_UnlockGCThingRT(JSRuntime *rt, void *thing); -extern JSBool -js_IsAboutToBeFinalized(JSContext *cx, void *thing); +extern bool +js_IsAboutToBeFinalized(void *thing); /* * Macro to test if a traversal is the marking phase of GC to avoid exposing @@ -414,10 +414,10 @@ typedef struct JSGCStats { uint32 maxdepth; /* maximum mark tail recursion depth */ uint32 cdepth; /* mark recursion depth of C functions */ uint32 maxcdepth; /* maximum mark recursion depth of C functions */ - uint32 untraced; /* number of times tracing of GC thing's children were + uint32 unmarked; /* number of times marking of GC thing's children were delayed due to a low C stack */ #ifdef DEBUG - uint32 maxuntraced;/* maximum number of things with children to trace + uint32 maxunmarked;/* maximum number of things with children to mark later */ #endif uint32 maxlevel; /* maximum GC nesting (indirect recursion) level */ From 609a5b5c8458d88ae6a68cfe39fac9aea1acfc4e Mon Sep 17 00:00:00 2001 From: Jacob Bramley Date: Mon, 14 Dec 2009 09:39:42 +0000 Subject: [PATCH 54/82] Bug 491682: Remove the LDMIA and STMIA macros from NativeARM.h, as they are not used. (r=vlad) --- js/src/nanojit/NativeARM.h | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/js/src/nanojit/NativeARM.h b/js/src/nanojit/NativeARM.h index a53f12afe32..3f730700566 100644 --- a/js/src/nanojit/NativeARM.h +++ b/js/src/nanojit/NativeARM.h @@ -800,22 +800,6 @@ enum { } \ } while(0) -#define STMIA(_b, _mask) do { \ - underrunProtect(4); \ - NanoAssert(IsGpReg(_b)); \ - NanoAssert(((_mask)&rmask(_b))==0 && isU8(_mask)); \ - *(--_nIns) = (NIns)(COND_AL | (0x8A<<20) | ((_b)<<16) | (_mask)&0xFF); \ - asm_output("stmia %s!,{0x%x}", gpn(_b), _mask); \ - } while (0) - -#define LDMIA(_b, _mask) do { \ - underrunProtect(4); \ - NanoAssert(IsGpReg(_b)); \ - NanoAssert(((_mask)&rmask(_b))==0 && isU8(_mask)); \ - *(--_nIns) = (NIns)(COND_AL | (0x8B<<20) | ((_b)<<16) | (_mask)&0xFF); \ - asm_output("ldmia %s!,{0x%x}", gpn(_b), (_mask)); \ - } while (0) - /* * VFP */ From 708810413f4c99d3b574f5b81d5dd5cc815abfd4 Mon Sep 17 00:00:00 2001 From: Jacob Bramley Date: Mon, 14 Dec 2009 10:34:45 +0000 Subject: [PATCH 55/82] Backing out my previous commit; this should have gone into nanojit-central. --- js/src/jstracer.cpp | 5 +++++ js/src/nanojit/NativeARM.h | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 7624ecf44bb..3e86cee51bd 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -7535,6 +7535,11 @@ js_InitJIT(JSTraceMonitor *tm) js_disable_debugger_exceptions(); + // Get the pre-defined values (if they are set). If the values aren't + // pre-defined using NJ_FORCE_*, they will be overwritten anyway. + bool arm_vfp = avmplus::AvmCore::config.vfp; + + bool arm_vfp = js_arm_check_vfp(); bool arm_thumb = js_arm_check_thumb(); bool arm_thumb2 = js_arm_check_thumb2(); diff --git a/js/src/nanojit/NativeARM.h b/js/src/nanojit/NativeARM.h index 3f730700566..a53f12afe32 100644 --- a/js/src/nanojit/NativeARM.h +++ b/js/src/nanojit/NativeARM.h @@ -800,6 +800,22 @@ enum { } \ } while(0) +#define STMIA(_b, _mask) do { \ + underrunProtect(4); \ + NanoAssert(IsGpReg(_b)); \ + NanoAssert(((_mask)&rmask(_b))==0 && isU8(_mask)); \ + *(--_nIns) = (NIns)(COND_AL | (0x8A<<20) | ((_b)<<16) | (_mask)&0xFF); \ + asm_output("stmia %s!,{0x%x}", gpn(_b), _mask); \ + } while (0) + +#define LDMIA(_b, _mask) do { \ + underrunProtect(4); \ + NanoAssert(IsGpReg(_b)); \ + NanoAssert(((_mask)&rmask(_b))==0 && isU8(_mask)); \ + *(--_nIns) = (NIns)(COND_AL | (0x8B<<20) | ((_b)<<16) | (_mask)&0xFF); \ + asm_output("ldmia %s!,{0x%x}", gpn(_b), (_mask)); \ + } while (0) + /* * VFP */ From 05b93868ea29b3a14f65ffa46d77900e4efcf19f Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Fri, 11 Dec 2009 14:12:55 -0800 Subject: [PATCH 56/82] XORPS was wrong on x64, didn't allow for regs xmm8..xmm15 (r=edwsmith,r=nnethercote,bug=533854) --HG-- extra : convert_revision : 49246c195323d333eb2d106539c48c03c4a4d5f6 --- js/src/nanojit/NativeX64.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/js/src/nanojit/NativeX64.cpp b/js/src/nanojit/NativeX64.cpp index 9d8021a66e5..0a8caacd79d 100644 --- a/js/src/nanojit/NativeX64.cpp +++ b/js/src/nanojit/NativeX64.cpp @@ -431,7 +431,8 @@ namespace nanojit // XORPS is a 4x32f vector operation, we use it instead of the more obvious // XORPD because it's one byte shorter. This is ok because it's only used for // zeroing an XMM register; hence the single argument. - void Assembler::XORPS( R r) { emitprr(X64_xorps, r,r); asm_output("xorps %s, %s", RQ(r),RQ(r)); } +// Also note that (unlike most SSE2 instructions) XORPS does not have a prefix, thus emitrr() should be used. + void Assembler::XORPS( R r) { emitrr(X64_xorps, r,r); asm_output("xorps %s, %s", RQ(r),RQ(r)); } void Assembler::DIVSD( R l, R r) { emitprr(X64_divsd, l,r); asm_output("divsd %s, %s", RQ(l),RQ(r)); } void Assembler::MULSD( R l, R r) { emitprr(X64_mulsd, l,r); asm_output("mulsd %s, %s", RQ(l),RQ(r)); } void Assembler::ADDSD( R l, R r) { emitprr(X64_addsd, l,r); asm_output("addsd %s, %s", RQ(l),RQ(r)); } From fb8fe39b225726894049bf551cb81c108db7d659 Mon Sep 17 00:00:00 2001 From: Steven Johnson Date: Fri, 11 Dec 2009 16:22:23 -0800 Subject: [PATCH 57/82] implement NJ_EXPANDED_LOADSTORE_SUPPORTED for x64 backend (r=edwsmith,r=nnethercote,bug=532240) --HG-- extra : convert_revision : cd0b46495c9520568c6507766dfdcb5fbf24d482 --- js/src/nanojit/NativeX64.cpp | 173 +++++++++++++++++++++++------------ js/src/nanojit/NativeX64.h | 21 ++++- 2 files changed, 133 insertions(+), 61 deletions(-) diff --git a/js/src/nanojit/NativeX64.cpp b/js/src/nanojit/NativeX64.cpp index 0a8caacd79d..298f984e9ef 100644 --- a/js/src/nanojit/NativeX64.cpp +++ b/js/src/nanojit/NativeX64.cpp @@ -99,6 +99,11 @@ namespace nanojit "ah", "ch", "dh", "bh" }; + const char *gpRegNames16[] = { + "ax", "cx", "dx", "bx", "spx", "bpx", "six", "dix", + "r8x", "r9x", "r10x", "r11x", "r12x", "r13x", "r14x", "r15x" + }; + #ifdef _DEBUG #define TODO(x) todo(#x) static void todo(const char *s) { @@ -240,6 +245,11 @@ namespace nanojit emit(rexprb(mod_rr(op, r, b), r, b)); } + // disp32 modrm8 form, when the disp fits in the instruction (opcode is 1-3 bytes) + void Assembler::emitrm8(uint64_t op, Register r, int32_t d, Register b) { + emit(rexrb8(mod_disp32(op, r, b, d), r, b)); + } + // disp32 modrm form, when the disp fits in the instruction (opcode is 1-3 bytes) void Assembler::emitrm(uint64_t op, Register r, int32_t d, Register b) { emit(rexrb(mod_disp32(op, r, b, d), r, b)); @@ -343,6 +353,7 @@ namespace nanojit } #define RB(r) gpRegNames8[(r)] +#define RS(r) gpRegNames16[(r)] #define RBhi(r) gpRegNames8hi[(r)] #define RL(r) gpRegNames32[(r)] #define RQ(r) gpn(r) @@ -439,6 +450,8 @@ namespace nanojit void Assembler::SUBSD( R l, R r) { emitprr(X64_subsd, l,r); asm_output("subsd %s, %s", RQ(l),RQ(r)); } void Assembler::CVTSQ2SD(R l, R r) { emitprr(X64_cvtsq2sd,l,r); asm_output("cvtsq2sd %s, %s",RQ(l),RQ(r)); } void Assembler::CVTSI2SD(R l, R r) { emitprr(X64_cvtsi2sd,l,r); asm_output("cvtsi2sd %s, %s",RQ(l),RL(r)); } + void Assembler::CVTSS2SD(R l, R r) { emitprr(X64_cvtss2sd,l,r); asm_output("cvtss2sd %s, %s",RQ(l),RL(r)); } + void Assembler::CVTSD2SS(R l, R r) { emitprr(X64_cvtsd2ss,l,r); asm_output("cvtsd2ss %s, %s",RL(l),RQ(r)); } void Assembler::UCOMISD( R l, R r) { emitprr(X64_ucomisd, l,r); asm_output("ucomisd %s, %s", RQ(l),RQ(r)); } void Assembler::MOVQRX( R l, R r) { emitprr(X64_movqrx, r,l); asm_output("movq %s, %s", RQ(l),RQ(r)); } // Nb: r and l are deliberately reversed within the emitprr() call. void Assembler::MOVQXR( R l, R r) { emitprr(X64_movqxr, l,r); asm_output("movq %s, %s", RQ(l),RQ(r)); } @@ -483,14 +496,21 @@ namespace nanojit void Assembler::LEAQRM(R r1, I d, R r2) { emitrm(X64_leaqrm,r1,d,r2); asm_output("leaq %s, %d(%s)",RQ(r1),d,RQ(r2)); } void Assembler::MOVLRM(R r1, I d, R r2) { emitrm(X64_movlrm,r1,d,r2); asm_output("movl %s, %d(%s)",RL(r1),d,RQ(r2)); } void Assembler::MOVQRM(R r1, I d, R r2) { emitrm(X64_movqrm,r1,d,r2); asm_output("movq %s, %d(%s)",RQ(r1),d,RQ(r2)); } + void Assembler::MOVBMR(R r1, I d, R r2) { emitrm8(X64_movbmr,r1,d,r2); asm_output("movb %d(%s), %s",d,RQ(r1),RB(r2)); } + void Assembler::MOVSMR(R r1, I d, R r2) { emitprm(X64_movsmr,r1,d,r2); asm_output("movs %d(%s), %s",d,RQ(r1),RS(r2)); } void Assembler::MOVLMR(R r1, I d, R r2) { emitrm(X64_movlmr,r1,d,r2); asm_output("movl %d(%s), %s",d,RQ(r1),RL(r2)); } void Assembler::MOVQMR(R r1, I d, R r2) { emitrm(X64_movqmr,r1,d,r2); asm_output("movq %d(%s), %s",d,RQ(r1),RQ(r2)); } void Assembler::MOVZX8M( R r1, I d, R r2) { emitrm_wide(X64_movzx8m, r1,d,r2); asm_output("movzxb %s, %d(%s)",RQ(r1),d,RQ(r2)); } void Assembler::MOVZX16M(R r1, I d, R r2) { emitrm_wide(X64_movzx16m,r1,d,r2); asm_output("movzxs %s, %d(%s)",RQ(r1),d,RQ(r2)); } + void Assembler::MOVSX8M( R r1, I d, R r2) { emitrm_wide(X64_movsx8m, r1,d,r2); asm_output("movsxb %s, %d(%s)",RQ(r1),d,RQ(r2)); } + void Assembler::MOVSX16M(R r1, I d, R r2) { emitrm_wide(X64_movsx16m,r1,d,r2); asm_output("movsxs %s, %d(%s)",RQ(r1),d,RQ(r2)); } + void Assembler::MOVSDRM(R r1, I d, R r2) { emitprm(X64_movsdrm,r1,d,r2); asm_output("movsd %s, %d(%s)",RQ(r1),d,RQ(r2)); } void Assembler::MOVSDMR(R r1, I d, R r2) { emitprm(X64_movsdmr,r1,d,r2); asm_output("movsd %d(%s), %s",d,RQ(r1),RQ(r2)); } + void Assembler::MOVSSRM(R r1, I d, R r2) { emitprm(X64_movssrm,r1,d,r2); asm_output("movss %s, %d(%s)",RQ(r1),d,RQ(r2)); } + void Assembler::MOVSSMR(R r1, I d, R r2) { emitprm(X64_movssmr,r1,d,r2); asm_output("movss %d(%s), %s",d,RQ(r1),RQ(r2)); } void Assembler::JMP8( S n, NIns* t) { emit_target8(n, X64_jmp8,t); asm_output("jmp %p", t); } @@ -1339,58 +1359,62 @@ namespace nanojit // xmm <- xmm: use movaps. movsd r,r causes partial register stall MOVAPSR(d, s); } else { + NanoAssert(IsFpReg(d) && !IsFpReg(s)); // xmm <- gpr: use movq xmm, r/m64 (66 REX.W 0F 6E /r) MOVQXR(d, s); } } - void Assembler::regalloc_load(LIns *ins, Register &rr, int32_t &dr, Register &rb) { + void Assembler::regalloc_load(LIns *ins, RegisterMask allow, Register &rr, int32_t &dr, Register &rb) { dr = ins->disp(); LIns *base = ins->oprnd1(); rb = getBaseReg(ins->opcode(), base, dr, BaseRegs); - if (ins->isUnusedOrHasUnknownReg()) { - // use a gpr in case we're copying a non-double - rr = prepResultReg(ins, GpRegs & ~rmask(rb)); + if (ins->isUnusedOrHasUnknownReg() || !(allow & rmask(ins->getReg()))) { + rr = prepResultReg(ins, allow & ~rmask(rb)); } else { // keep already assigned register rr = ins->getReg(); + NanoAssert(allow & rmask(rr)); freeRsrcOf(ins, false); } } void Assembler::asm_load64(LIns *ins) { + Register rr, rb; + int32_t dr; switch (ins->opcode()) { case LIR_ldq: case LIR_ldqc: - // handled by mainline code below for now + regalloc_load(ins, GpRegs, rr, dr, rb); + if (IsGpReg(rr)) { + // general 64bit load, 32bit const displacement + MOVQRM(rr, dr, rb); + } else { + NanoAssert(IsFpReg(rr)); + // load 64bits into XMM. don't know if double or int64, assume double. + MOVSDRM(rr, dr, rb); + } break; case LIR_ld32f: case LIR_ldc32f: - NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); - return; + regalloc_load(ins, FpRegs, rr, dr, rb); + NanoAssert(IsFpReg(rr)); + CVTSS2SD(rr, rr); + MOVSSRM(rr, dr, rb); + break; default: NanoAssertMsg(0, "asm_load64 should never receive this LIR opcode"); - return; + break; } - Register rr, rb; - int32_t dr; - regalloc_load(ins, rr, dr, rb); - if (IsGpReg(rr)) { - // general 64bit load, 32bit const displacement - MOVQRM(rr, dr, rb); - } else { - // load 64bits into XMM. don't know if double or int64, assume double. - MOVSDRM(rr, dr, rb); - } } void Assembler::asm_load32(LIns *ins) { NanoAssert(!ins->isQuad()); Register r, b; int32_t d; - regalloc_load(ins, r, d, b); + regalloc_load(ins, GpRegs, r, d, b); LOpcode op = ins->opcode(); switch(op) { case LIR_ldzb: @@ -1406,40 +1430,32 @@ namespace nanojit MOVLRM( r, d, b); break; case LIR_ldsb: - case LIR_ldss: case LIR_ldcsb: + MOVSX8M( r, d, b); + break; + case LIR_ldss: case LIR_ldcss: - NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); - return; + MOVSX16M( r, d, b); + break; default: NanoAssertMsg(0, "asm_load32 should never receive this LIR opcode"); - return; + break; } } void Assembler::asm_store64(LOpcode op, LIns *value, int d, LIns *base) { NanoAssert(value->isQuad()); - switch (op) { - case LIR_stqi: - // handled by mainline code below for now - break; - case LIR_st32f: - NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); - return; - default: - NanoAssertMsg(0, "asm_store64 should never receive this LIR opcode"); - return; - } - Register b = getBaseReg(LIR_stqi, base, d, BaseRegs); + Register r; // if we have to choose a register, use a GPR, but not the base reg - Register r; if (value->isUnusedOrHasUnknownReg()) { RegisterMask allow; + // If op is LIR_st32f and we have no reg, prefer FPR over GPR: saves an instruction later, + // and the value is almost certainly going to operated on as FP later anyway. // XXX: isFloat doesn't cover float/fmod! see bug 520208. - if (value->isFloat() || value->isop(LIR_float) || value->isop(LIR_fmod)) { + if (op == LIR_st32f || value->isFloat() || value->isop(LIR_float) || value->isop(LIR_fmod)) { allow = FpRegs; } else { allow = GpRegs; @@ -1449,37 +1465,76 @@ namespace nanojit r = value->getReg(); } - if (IsGpReg(r)) { - // gpr store - MOVQMR(r, d, b); - } - else { - // xmm store - MOVSDMR(r, d, b); + switch (op) { + case LIR_stqi: + { + if (IsGpReg(r)) { + // gpr store + MOVQMR(r, d, b); + } + else { + // xmm store + MOVSDMR(r, d, b); + } + break; + } + case LIR_st32f: + { + // need a scratch FPR reg + Register t = registerAllocTmp(FpRegs & ~rmask(r)); + + // store + MOVSSMR(t, d, b); + + // cvt to single-precision + if (IsGpReg(r)) + { + CVTSD2SS(t, t); + MOVQXR(t, r); // xmm <- gpr: use movq xmm, r/m64 (66 REX.W 0F 6E /r) + } + else + { + NanoAssert(IsFpReg(r)); + CVTSD2SS(t, r); + } + XORPS(t); // break dependency chains + break; + } + default: + NanoAssertMsg(0, "asm_store64 should never receive this LIR opcode"); + break; } } void Assembler::asm_store32(LOpcode op, LIns *value, int d, LIns *base) { - switch (op) { - case LIR_sti: - // handled by mainline code below for now - break; - case LIR_stb: - case LIR_sts: - NanoAssertMsg(0, "NJ_EXPANDED_LOADSTORE_SUPPORTED not yet supported for this architecture"); - return; - default: - NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode"); - return; - } + // quirk of x86-64: reg cannot appear to be ah/bh/ch/dh + // for single-byte stores with REX prefix + const RegisterMask SrcRegs = + (op == LIR_stb) ? + (GpRegs & ~(1<isQuad()); Register b = getBaseReg(LIR_sti, base, d, BaseRegs); - Register r = findRegFor(value, GpRegs & ~rmask(b)); + Register r = findRegFor(value, SrcRegs & ~rmask(b)); + + switch (op) { + case LIR_stb: + MOVBMR(r, d, b); + break; + case LIR_sts: + MOVSMR(r, d, b); + break; + case LIR_sti: + MOVLMR(r, d, b); + break; + default: + NanoAssertMsg(0, "asm_store32 should never receive this LIR opcode"); + break; + } + - // store 32bits to 64bit addr. use rex so we can use all 16 regs - MOVLMR(r, d, b); } // generate a 64bit constant, must not affect condition codes! diff --git a/js/src/nanojit/NativeX64.h b/js/src/nanojit/NativeX64.h index b2e10a3de1b..8bbd7041d9c 100644 --- a/js/src/nanojit/NativeX64.h +++ b/js/src/nanojit/NativeX64.h @@ -61,7 +61,7 @@ namespace nanojit #define NJ_MAX_STACK_ENTRY 256 #define NJ_ALIGN_STACK 16 #define NJ_JTBL_SUPPORTED 1 -#define NJ_EXPANDED_LOADSTORE_SUPPORTED 0 +#define NJ_EXPANDED_LOADSTORE_SUPPORTED 1 enum Register { RAX = 0, // 1st int return, # of sse varargs @@ -191,6 +191,8 @@ namespace nanojit X64_cmpqr8 = 0x00F8834800000004LL, // 64bit compare r,int64(imm8) X64_cvtsi2sd= 0xC02A0F40F2000005LL, // convert int32 to double r = (double) b X64_cvtsq2sd= 0xC02A0F48F2000005LL, // convert int64 to double r = (double) b + X64_cvtss2sd= 0xC05A0F40F3000005LL, // convert float to double r = (double) b + X64_cvtsd2ss= 0xC05A0F40F2000005LL, // convert double to float r = (float) b X64_divsd = 0xC05E0F40F2000005LL, // divide scalar double r /= b X64_mulsd = 0xC0590F40F2000005LL, // multiply scalar double r *= b X64_addsd = 0xC0580F40F2000005LL, // add scalar double r += b @@ -230,6 +232,8 @@ namespace nanojit X64_learm = 0x00000000808D4007LL, // 32bit load effective addr reg <- disp32+base X64_learip = 0x00000000058D4807LL, // 64bit RIP-relative lea. reg <- disp32+rip (modrm = 00rrr101 = 05) X64_movlr = 0xC08B400000000003LL, // 32bit mov r <- b + X64_movbmr = 0x0000000080884007LL, // 8bit store r -> [b+d32] + X64_movsmr = 0x8089406600000004LL, // 16bit store r -> [b+d32] X64_movlmr = 0x0000000080894007LL, // 32bit store r -> [b+d32] X64_movlrm = 0x00000000808B4007LL, // 32bit load r <- [b+d32] X64_movqmr = 0x0000000080894807LL, // 64bit store gpr -> [b+d32] @@ -245,10 +249,14 @@ namespace nanojit X64_movsdrr = 0xC0100F40F2000005LL, // 64bit mov xmm-r <- xmm-b (upper 64bits unchanged) X64_movsdrm = 0x80100F40F2000005LL, // 64bit load xmm-r <- [b+d32] (upper 64 cleared) X64_movsdmr = 0x80110F40F2000005LL, // 64bit store xmm-r -> [b+d32] + X64_movssrm = 0x80100F40F3000005LL, // 32bit load xmm-r <- [b+d32] (upper 96 cleared) + X64_movssmr = 0x80110F40F3000005LL, // 32bit store xmm-r -> [b+d32] X64_movsxdr = 0xC063480000000003LL, // sign extend i32 to i64 r = (int64)(int32) b X64_movzx8 = 0xC0B60F4000000004LL, // zero extend i8 to i64 r = (uint64)(uint8) b X64_movzx8m = 0x80B60F4000000004LL, // zero extend i8 load to i32 r <- [b+d32] X64_movzx16m= 0x80B70F4000000004LL, // zero extend i16 load to i32 r <- [b+d32] + X64_movsx8m = 0x80BE0F4000000004LL, // sign extend i8 load to i32 r <- [b+d32] + X64_movsx16m= 0x80BF0F4000000004LL, // sign extend i16 load to i32 r <- [b+d32] X64_neg = 0xD8F7400000000003LL, // 32bit two's compliment b = -b X64_nop1 = 0x9000000000000001LL, // one byte NOP X64_nop2 = 0x9066000000000002LL, // two byte NOP @@ -359,6 +367,7 @@ namespace nanojit void emitr(uint64_t op, Register b) { emitrr(op, (Register)0, b); }\ void emitr8(uint64_t op, Register b) { emitrr8(op, (Register)0, b); }\ void emitprr(uint64_t op, Register r, Register b);\ + void emitrm8(uint64_t op, Register r, int32_t d, Register b);\ void emitrm(uint64_t op, Register r, int32_t d, Register b);\ void emitrm_wide(uint64_t op, Register r, int32_t d, Register b);\ uint64_t emit_disp32(uint64_t op, int32_t d);\ @@ -380,7 +389,7 @@ namespace nanojit void asm_arith_imm(LIns*);\ void regalloc_unary(LIns *ins, RegisterMask allow, Register &rr, Register &ra);\ void regalloc_binary(LIns *ins, RegisterMask allow, Register &rr, Register &ra, Register &rb);\ - void regalloc_load(LIns *ins, Register &rr, int32_t &d, Register &rb);\ + void regalloc_load(LIns *ins, RegisterMask allow, Register &rr, int32_t &d, Register &rb);\ void dis(NIns *p, int bytes);\ void asm_cmp(LIns*);\ void asm_cmp_imm(LIns*);\ @@ -460,6 +469,8 @@ namespace nanojit void SUBSD(Register l, Register r);\ void CVTSQ2SD(Register l, Register r);\ void CVTSI2SD(Register l, Register r);\ + void CVTSS2SD(Register l, Register r);\ + void CVTSD2SS(Register l, Register r);\ void UCOMISD(Register l, Register r);\ void MOVQRX(Register l, Register r);\ void MOVQXR(Register l, Register r);\ @@ -495,12 +506,18 @@ namespace nanojit void LEAQRM(Register r1, int d, Register r2);\ void MOVLRM(Register r1, int d, Register r2);\ void MOVQRM(Register r1, int d, Register r2);\ + void MOVBMR(Register r1, int d, Register r2);\ + void MOVSMR(Register r1, int d, Register r2);\ void MOVLMR(Register r1, int d, Register r2);\ void MOVQMR(Register r1, int d, Register r2);\ void MOVZX8M(Register r1, int d, Register r2);\ void MOVZX16M(Register r1, int d, Register r2);\ + void MOVSX8M(Register r1, int d, Register r2);\ + void MOVSX16M(Register r1, int d, Register r2);\ void MOVSDRM(Register r1, int d, Register r2);\ void MOVSDMR(Register r1, int d, Register r2);\ + void MOVSSMR(Register r1, int d, Register r2);\ + void MOVSSRM(Register r1, int d, Register r2);\ void JMP8(size_t n, NIns* t);\ void JMP32(size_t n, NIns* t);\ void JMPX(Register indexreg, NIns** table);\ From f0ca3397b156e9dbd6a6ba77359a17a008643aa3 Mon Sep 17 00:00:00 2001 From: Jacob Bramley Date: Mon, 14 Dec 2009 11:13:24 +0000 Subject: [PATCH 58/82] Bug 491682: Remove the LDMIA and STMIA macros from NativeARM.h, as they are not used. (r=vlad) --HG-- extra : convert_revision : a6a96927117a1e462a04784e1b621a3d85f61099 --- js/src/nanojit/NativeARM.h | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/js/src/nanojit/NativeARM.h b/js/src/nanojit/NativeARM.h index a53f12afe32..3f730700566 100644 --- a/js/src/nanojit/NativeARM.h +++ b/js/src/nanojit/NativeARM.h @@ -800,22 +800,6 @@ enum { } \ } while(0) -#define STMIA(_b, _mask) do { \ - underrunProtect(4); \ - NanoAssert(IsGpReg(_b)); \ - NanoAssert(((_mask)&rmask(_b))==0 && isU8(_mask)); \ - *(--_nIns) = (NIns)(COND_AL | (0x8A<<20) | ((_b)<<16) | (_mask)&0xFF); \ - asm_output("stmia %s!,{0x%x}", gpn(_b), _mask); \ - } while (0) - -#define LDMIA(_b, _mask) do { \ - underrunProtect(4); \ - NanoAssert(IsGpReg(_b)); \ - NanoAssert(((_mask)&rmask(_b))==0 && isU8(_mask)); \ - *(--_nIns) = (NIns)(COND_AL | (0x8B<<20) | ((_b)<<16) | (_mask)&0xFF); \ - asm_output("ldmia %s!,{0x%x}", gpn(_b), (_mask)); \ - } while (0) - /* * VFP */ From 37965c89df109718bacf179df2a779dcecd29ff5 Mon Sep 17 00:00:00 2001 From: Jacob Bramley Date: Mon, 14 Dec 2009 11:18:43 +0000 Subject: [PATCH 59/82] Update nanojit-import-rev stamp. --- js/src/nanojit-import-rev | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/nanojit-import-rev b/js/src/nanojit-import-rev index 181156ad407..1481f47f021 100644 --- a/js/src/nanojit-import-rev +++ b/js/src/nanojit-import-rev @@ -1 +1 @@ -18cf6386858be93609ffcc0912e1c169b81e6531 +a6a96927117a1e462a04784e1b621a3d85f61099 From ce3c3571e21746e668f18d78b3f3ff15ed8c07c3 Mon Sep 17 00:00:00 2001 From: Jacob Bramley Date: Mon, 14 Dec 2009 11:29:31 +0000 Subject: [PATCH 60/82] Cleaning out the rubbish I left when I backed out a previous patch. --- js/src/jstracer.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 3e86cee51bd..7624ecf44bb 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -7535,11 +7535,6 @@ js_InitJIT(JSTraceMonitor *tm) js_disable_debugger_exceptions(); - // Get the pre-defined values (if they are set). If the values aren't - // pre-defined using NJ_FORCE_*, they will be overwritten anyway. - bool arm_vfp = avmplus::AvmCore::config.vfp; - - bool arm_vfp = js_arm_check_vfp(); bool arm_thumb = js_arm_check_thumb(); bool arm_thumb2 = js_arm_check_thumb2(); From 5d706bfc9a837bb5a1c529af54cf005db5f553d7 Mon Sep 17 00:00:00 2001 From: Gregor Wagner Date: Mon, 14 Dec 2009 18:36:58 -0800 Subject: [PATCH 61/82] fix JS_SCOPE_DEPTH_METER and enable in DEBUG mode bug #528674, r=gal --- js/src/jsemit.cpp | 2 +- js/src/jsemit.h | 16 +++++++++------- js/src/jsobj.cpp | 2 +- js/src/jsutil.h | 2 +- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/js/src/jsemit.cpp b/js/src/jsemit.cpp index f8b482f23c1..2b198acc300 100644 --- a/js/src/jsemit.cpp +++ b/js/src/jsemit.cpp @@ -4384,7 +4384,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) cg2->staticLevel = cg->staticLevel + 1; /* We measured the max scope depth when we parsed the function. */ - JS_SCOPE_DEPTH_METERING(cg2->maxScopeDepth = (uintN) -1); + JS_SCOPE_DEPTH_METERING(cg2->maxScopeDepth = uint16(-1)); if (!js_EmitFunctionScript(cx, cg2, pn->pn_body)) pn = NULL; diff --git a/js/src/jsemit.h b/js/src/jsemit.h index 5593ce0e1a3..e9d1bdb24bb 100644 --- a/js/src/jsemit.h +++ b/js/src/jsemit.h @@ -157,8 +157,10 @@ struct JSStmtInfo { #ifdef JS_SCOPE_DEPTH_METER # define JS_SCOPE_DEPTH_METERING(code) ((void) (code)) +# define JS_SCOPE_DEPTH_METERING_IF(cond, code) ((cond) ? (void) (code) : (void) 0) #else # define JS_SCOPE_DEPTH_METERING(code) ((void) 0) +# define JS_SCOPE_DEPTH_METERING_IF(code, x) ((void) 0) #endif struct JSTreeContext { /* tree context for semantic checks */ @@ -208,15 +210,15 @@ struct JSTreeContext { /* tree context for semantic checks */ /* * For functions the tree context is constructed and destructed a second * time during code generation. To avoid a redundant stats update in such - * cases, we store (uintN) -1 in maxScopeDepth. + * cases, we store uint16(-1) in maxScopeDepth. */ ~JSTreeContext() { - JS_SCOPE_DEPTH_METERING(maxScopeDepth == (uintN) -1 || - JS_BASIC_STATS_ACCUM(&compiler - ->context - ->runtime - ->lexicalScopeDepthStats, - maxScopeDepth)); + JS_SCOPE_DEPTH_METERING_IF((maxScopeDepth != uint16(-1)), + JS_BASIC_STATS_ACCUM(&compiler + ->context + ->runtime + ->lexicalScopeDepthStats, + maxScopeDepth)); } uintN blockid() { return topStmt ? topStmt->blockid : bodyid; } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 8ef1433cf9e..60dcd98b29f 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4175,7 +4175,7 @@ js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult, scopeIndex, protoIndex, pobj, (JSScopeProperty *) prop, false); } - SCOPE_DEPTH_ACCUM(&rt->scopeSearchDepthStats, scopeIndex); + SCOPE_DEPTH_ACCUM(&cx->runtime->scopeSearchDepthStats, scopeIndex); goto out; } diff --git a/js/src/jsutil.h b/js/src/jsutil.h index ad8fa2e4eb2..52eab96f6a4 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -119,7 +119,7 @@ static const bool js_use_SSE2 = true; */ extern JS_PUBLIC_API(void) JS_Abort(void); -#if 0 +#ifdef DEBUG # define JS_BASIC_STATS 1 # define JS_SCOPE_DEPTH_METER 1 #endif From 881436060a7ee0eb08f146c351345b4ff40f1137 Mon Sep 17 00:00:00 2001 From: Gregor Wagner Date: Mon, 14 Dec 2009 19:01:37 -0800 Subject: [PATCH 62/82] fix JS_GCMETER r=gal bug #504581 --- js/src/jsgc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 7bcc3606d80..c31da3026fb 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -3224,7 +3224,7 @@ js_GC(JSContext *cx, JSGCInvocationKind gckind) ap = &a->prev; #ifdef JS_GCMETER for (size_t i = 0; i != DOUBLES_PER_ARENA; ++i) { - if (IsMarkedDouble(a, index)) + if (IsMarkedDouble(a, i)) METER(nthings++); } METER(nlivearenas++); From 689d4154a49166d8f5b652dc95a2f4296919306f Mon Sep 17 00:00:00 2001 From: Gregor Wagner Date: Mon, 14 Dec 2009 22:12:42 -0800 Subject: [PATCH 63/82] inline several string functions r=gal, bug #507531 --- js/src/jsstr.h | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/js/src/jsstr.h b/js/src/jsstr.h index f674dda164b..cb43683858b 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -128,7 +128,7 @@ struct JSString { static const size_t ATOMIZED = JSSTRING_BIT(3); static const size_t DEFLATED = JSSTRING_BIT(4); - bool hasFlag(size_t flag) const { + inline bool hasFlag(size_t flag) const { return (mFlags & flag) != 0; } @@ -139,53 +139,53 @@ struct JSString { */ static const size_t MAX_LENGTH = (1 << 28) - 1; - bool isDependent() const { + inline bool isDependent() const { return hasFlag(DEPENDENT); } - bool isFlat() const { + inline bool isFlat() const { return !isDependent(); } - bool isDeflated() const { + inline bool isDeflated() const { return hasFlag(DEFLATED); } - void setDeflated() { + inline void setDeflated() { JS_ATOMIC_SET_MASK(&mFlags, DEFLATED); } - bool isMutable() const { + inline bool isMutable() const { return !isDependent() && hasFlag(MUTABLE); } - bool isAtomized() const { + inline bool isAtomized() const { return !isDependent() && hasFlag(ATOMIZED); } - JS_ALWAYS_INLINE jschar *chars() { + inline jschar *chars() { return isDependent() ? dependentChars() : flatChars(); } - JS_ALWAYS_INLINE size_t length() const { + inline size_t length() const { return mLength; } - JS_ALWAYS_INLINE bool empty() const { + inline bool empty() const { return length() == 0; } - JS_ALWAYS_INLINE void getCharsAndLength(const jschar *&chars, size_t &length) { + inline void getCharsAndLength(const jschar *&chars, size_t &length) { chars = this->chars(); length = this->length(); } - JS_ALWAYS_INLINE void getCharsAndEnd(const jschar *&chars, const jschar *&end) { + inline void getCharsAndEnd(const jschar *&chars, const jschar *&end) { end = length() + (chars = this->chars()); } /* Specific flat string initializer and accessor methods. */ - void initFlat(jschar *chars, size_t length) { + inline void initFlat(jschar *chars, size_t length) { JS_ASSERT(length <= MAX_LENGTH); mLength = length; mOffset = 0; @@ -193,12 +193,12 @@ struct JSString { mChars = chars; } - jschar *flatChars() const { + inline jschar *flatChars() const { JS_ASSERT(isFlat()); return mChars; } - JS_ALWAYS_INLINE size_t flatLength() const { + inline size_t flatLength() const { JS_ASSERT(isFlat()); return length(); } @@ -242,23 +242,23 @@ struct JSString { * js_AtomizeString. This function would find that the string was already * hashed and return it with the atomized bit set. */ - void flatSetAtomized() { + inline void flatSetAtomized() { JS_ASSERT(isFlat() && !isMutable()); JS_ATOMIC_SET_MASK(&mFlags, ATOMIZED); } - void flatSetMutable() { + inline void flatSetMutable() { JS_ASSERT(isFlat() && !isAtomized()); mFlags |= MUTABLE; } - void flatClearMutable() { + inline void flatClearMutable() { JS_ASSERT(isFlat()); if (hasFlag(MUTABLE)) mFlags &= ~MUTABLE; } - void initDependent(JSString *bstr, size_t off, size_t len) { + inline void initDependent(JSString *bstr, size_t off, size_t len) { JS_ASSERT(len <= MAX_LENGTH); mLength = len; mOffset = off; @@ -267,7 +267,7 @@ struct JSString { } /* See JSString::reinitFlat. */ - void reinitDependent(JSString *bstr, size_t off, size_t len) { + inline void reinitDependent(JSString *bstr, size_t off, size_t len) { JS_ASSERT(len <= MAX_LENGTH); mLength = len; mOffset = off; @@ -275,22 +275,22 @@ struct JSString { mBase = bstr; } - JSString *dependentBase() const { + inline JSString *dependentBase() const { JS_ASSERT(isDependent()); return mBase; } - JS_ALWAYS_INLINE jschar *dependentChars() { + inline jschar *dependentChars() { return dependentBase()->isDependent() ? js_GetDependentStringChars(this) : dependentBase()->flatChars() + dependentStart(); } - JS_ALWAYS_INLINE size_t dependentStart() const { + inline size_t dependentStart() const { return mOffset; } - JS_ALWAYS_INLINE size_t dependentLength() const { + inline size_t dependentLength() const { JS_ASSERT(isDependent()); return length(); } From d58ef2604a23877cf4ac323709cbe142582515a4 Mon Sep 17 00:00:00 2001 From: Saint Wesonga Date: Tue, 15 Dec 2009 21:40:33 -0500 Subject: [PATCH 64/82] Bug 514808 - Regex whitespace character class deficiencies. r=dmandelin --- js/src/jsstr.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index d7534fdb289..6d3966f105d 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -4284,8 +4284,8 @@ const uint8 js_Y[] = { 60, 15, 60, 60, 60, 60, 46, 46, /* 27 */ 9, 9, 9, 9, 9, 9, 9, 9, /* 27 */ 9, 9, 46, 46, 46, 46, 46, 46, /* 27 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 2, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 2, 46, /* 28 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ @@ -4597,17 +4597,17 @@ const uint8 js_Y[] = { 46, 46, 16, 94, 16, 46, 16, 16, /* 66 */ 102, 102, 103, 103, 96, 11, 11, 46, /* 66 */ 2, 2, 2, 2, 2, 2, 2, 2, /* 67 */ - 2, 2, 2, 2, 104, 104, 104, 104, /* 67 */ + 2, 2, 2, 104, 104, 104, 104, 104, /* 67 */ 8, 8, 8, 8, 8, 8, 3, 3, /* 67 */ 5, 6, 5, 5, 5, 6, 5, 5, /* 67 */ 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ -105, 106, 104, 104, 104, 104, 104, 46, /* 67 */ +105, 106, 104, 104, 104, 104, 104, 2, /* 67 */ 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ 3, 5, 6, 3, 3, 3, 3, 12, /* 67 */ 12, 3, 3, 3, 7, 5, 6, 46, /* 68 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ - 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ + 46, 46, 46, 46, 46, 46, 46, 2, /* 68 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ 46, 46, 104, 104, 104, 104, 104, 104, /* 68 */ 17, 46, 46, 46, 17, 17, 17, 17, /* 68 */ From 7156489d4a340c0ef3fe81ee7b0a29e0100b754a Mon Sep 17 00:00:00 2001 From: David Mandelin Date: Tue, 15 Dec 2009 21:40:39 -0500 Subject: [PATCH 65/82] Bug 514808 - Regex whitespace character class deficiencies. Add a test. --- js/src/tests/js1_5/Regress/jstests.list | 1 + js/tests/js1_5/Regress/regress-514808.js | 80 ++++++++++++++++++++++++ 2 files changed, 81 insertions(+) create mode 100644 js/tests/js1_5/Regress/regress-514808.js diff --git a/js/src/tests/js1_5/Regress/jstests.list b/js/src/tests/js1_5/Regress/jstests.list index 4face6cea9f..18a1a21898b 100644 --- a/js/src/tests/js1_5/Regress/jstests.list +++ b/js/src/tests/js1_5/Regress/jstests.list @@ -343,6 +343,7 @@ script regress-501124.js script regress-504078.js script regress-506567.js script regress-511859.js +script regress-514808.js script regress-57043.js script regress-58116.js script regress-68498-001.js diff --git a/js/tests/js1_5/Regress/regress-514808.js b/js/tests/js1_5/Regress/regress-514808.js new file mode 100644 index 00000000000..9243f57d306 --- /dev/null +++ b/js/tests/js1_5/Regress/regress-514808.js @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is JavaScript Engine testing utilities. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): Dave Mandelin + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +var gTestfile = 'regress-514808.js'; +//----------------------------------------------------------------------------- +var BUGNUMBER = 514808; +var summary = 'Correct space character class in regexes'; +var actual = ''; +var expect = ''; + + +//----------------------------------------------------------------------------- +test(); +//----------------------------------------------------------------------------- + +function test() +{ + enterFunc ('test'); + printBugNumber(BUGNUMBER); + printStatus (summary); + + var spaces = [ "\u0009", "\u000b", "\u000c", "\u0020", "\u00a0", "\u1680", + "\u180e", "\u2000", "\u2001", "\u2002", "\u2003", "\u2004", + "\u2005", "\u2006", "\u2007", "\u2008", "\u2009", "\u200a", + "\u202f", "\u205f", "\u3000" ]; + var line_terminators = [ "\u2028", "\u2029", "\u000a", "\u000d" ]; + var space_chars = [].concat(spaces, line_terminators); + + var non_space_chars = [ "\u200b", "\u200c", "\u200d", + "\ufeff" ]; + + var chars = [].concat(space_chars, non_space_chars); + var is_space = [].concat(space_chars.map(function(ch) { return true; }), + non_space_chars.map(function(ch) { return false; })); + var expect = is_space.join(','); + + var actual = chars.map(function(ch) { return /\s/.test(ch); }).join(','); + reportCompare(expect, actual, summary); + + jit(true); + var actual = chars.map(function(ch) { return /\s/.test(ch); }).join(','); + reportCompare(expect, actual, summary); + jit(false); + + exitFunc ('test'); +} From aedf90bff25520badad9cc1d60beb3096dada55f Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Tue, 15 Dec 2009 22:30:25 -0500 Subject: [PATCH 66/82] =?UTF-8?q?Backed=20out=20Andreas=20Gal=20=E2=80=94?= =?UTF-8?q?=20Try=20to=20fix=20autoconf=20madness=20for=20bug=20530896.=20?= =?UTF-8?q?changeset=20289c9c3c9195?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/src/configure.in | 47 ++++++++++++++++++++++++--------------------- js/src/jsnum.h | 6 +++--- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/js/src/configure.in b/js/src/configure.in index 16d911af87a..772573ce84a 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -2478,35 +2478,38 @@ fi dnl Configure SSE2 support -HAVE_SSE2= -MUST_DETECT_SSE2= +dnl Enable SSE2 support in the compiler if available. +dnl ======================================================== case "$target" in -i?86-*) - HAVE_SSE2=1 - case "$target_os" in - darwin*) - ;; - *) - MUST_DETECT_SSE2=1 - ;; - esac - ;; -x86_64*-*)e - HAVE_SSE2=1 - ;; -esac - -if test "$HAVE_SSE2"; then - AC_DEFINE(HAVE_SSE2) +i?86-*|x86_64*-*) if test "$GNU_CC"; then CFLAGS="$CFLAGS -msse2" fi if test "$GNU_CXX"; then CXXFLAGS="$CXXFLAGS -msse2" fi - if test "$MUST_DETECT_SSE2"; then - AC_DEFINE(MUST_DETECT_SSE2) - fi + ;; +esac + +dnl If we have the proper include file, enable SSE2 support. +dnl ======================================================== +HAVE_SSE2= +AC_CHECK_HEADER(xmmintrin.h, HAVE_SSE2=1) +AC_CHECK_HEADER(emmintrin.h, HAVE_SSE2=1 && AC_DEFINE(USE_EMM_INTRIN)) + +if test "$HAVE_SSE2"; then + AC_DEFINE(HAVE_SSE2) + case "$target_os" in + darwin*) + ;; + *) + case "$target" in + i?86-*) + AC_DEFINE(MUST_DETECT_SSE2) + ;; + esac + ;; + esac fi dnl Configure JIT support diff --git a/js/src/jsnum.h b/js/src/jsnum.h index 69f621e3c80..e64da4c6cea 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -48,10 +48,10 @@ #include #endif #ifdef HAVE_SSE2 -#ifdef __GNUC__ -#include -#else +#ifdef USE_EMM_INTRIN #include +#else +#include #endif #endif From fa15ebcadcf179808ef94baecf2c123ae84b3489 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Tue, 15 Dec 2009 22:34:35 -0500 Subject: [PATCH 67/82] =?UTF-8?q?Backed=20out=20changeset=20eae07941000e.?= =?UTF-8?q?=20Andreas=20Gal=20=E2=80=94=20Efficient=20Implementation=20of?= =?UTF-8?q?=20JSDOUBLE=5FIS=5FINT=20using=20SSE2=20(original=20patch=20by?= =?UTF-8?q?=20Moh=20Haghighat/Intel,=20bug=20530896,=20r=3Ddvander).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/src/configure.in | 36 ---------------------------------- js/src/jsapi.cpp | 3 --- js/src/jsnum.h | 32 +----------------------------- js/src/jstracer.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++-- js/src/jsutil.cpp | 47 --------------------------------------------- js/src/jsutil.h | 11 ----------- 6 files changed, 46 insertions(+), 130 deletions(-) diff --git a/js/src/configure.in b/js/src/configure.in index 772573ce84a..3ffead04a3d 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -2476,42 +2476,6 @@ if test -z "$COMPILE_ENVIRONMENT"; then SKIP_LIBRARY_CHECKS=1 fi -dnl Configure SSE2 support - -dnl Enable SSE2 support in the compiler if available. -dnl ======================================================== -case "$target" in -i?86-*|x86_64*-*) - if test "$GNU_CC"; then - CFLAGS="$CFLAGS -msse2" - fi - if test "$GNU_CXX"; then - CXXFLAGS="$CXXFLAGS -msse2" - fi - ;; -esac - -dnl If we have the proper include file, enable SSE2 support. -dnl ======================================================== -HAVE_SSE2= -AC_CHECK_HEADER(xmmintrin.h, HAVE_SSE2=1) -AC_CHECK_HEADER(emmintrin.h, HAVE_SSE2=1 && AC_DEFINE(USE_EMM_INTRIN)) - -if test "$HAVE_SSE2"; then - AC_DEFINE(HAVE_SSE2) - case "$target_os" in - darwin*) - ;; - *) - case "$target" in - i?86-*) - AC_DEFINE(MUST_DETECT_SSE2) - ;; - esac - ;; - esac -fi - dnl Configure JIT support case "$target" in diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 8fc2d6c3eff..00de732cc37 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -825,9 +825,6 @@ JSRuntime::~JSRuntime() JS_PUBLIC_API(JSRuntime *) JS_NewRuntime(uint32 maxbytes) { -#ifdef MUST_DETECT_SSE2 - js_use_SSE2 = js_DetectSSE2(); -#endif #ifdef DEBUG if (!js_NewRuntimeWasCalled) { /* diff --git a/js/src/jsnum.h b/js/src/jsnum.h index e64da4c6cea..f3e7a253dfe 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -47,13 +47,6 @@ #ifdef SOLARIS #include #endif -#ifdef HAVE_SSE2 -#ifdef USE_EMM_INTRIN -#include -#else -#include -#endif -#endif /* * JS number (IEEE double) interface. @@ -138,32 +131,9 @@ JSDOUBLE_IS_NEGZERO(jsdouble d) #define JSDOUBLE_HI32_EXPMASK 0x7ff00000 #define JSDOUBLE_HI32_MANTMASK 0x000fffff -/* Older versions of MSVC don't have _mm_castpd_si128. */ -#if defined(HAVE_SSE2) && defined (_MSC_VER) && (_MSC_VER < 1500) -static inline __m128i -_mm_castpd_si128(__m128d v) { - return *(__m128i *)&v; -} -#endif - -static inline bool +static inline int JSDOUBLE_IS_INT(jsdouble d, jsint& i) { -#ifdef HAVE_SSE2 - if (js_use_SSE2) { - __m128d xd = _mm_set_sd(d); /* load double into an XMM register */ - int ii = _mm_cvtsd_si32(xd); /* Inf/NaN & large |d| convert to -2^31 */ - __m128d xdi = _mm_setzero_pd(); /* kill dependencies for the top half of xdi */ - xdi = _mm_cvtsi32_sd(xdi, ii); /* convert the result back to double */ - __m128i xcmp = _mm_cmpeq_epi32(_mm_castpd_si128(xd), /* 32-bit integer bit-to-bit */ - _mm_castpd_si128(xdi)); /* comparison */ - int m = _mm_movemask_epi8(xcmp); /* extract significant bits of compare */ - if ((m & 0xff) != 0xff) /* result is non-integer? */ - return false; - i = ii; - return true; - } -#endif if (JSDOUBLE_IS_NEGZERO(d)) return false; return d == (i = jsint(d)); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 5515a60a137..54d164cf227 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -74,7 +74,6 @@ #include "jsscript.h" #include "jsstaticcheck.h" #include "jstracer.h" -#include "jsutil.h" #include "jsxml.h" #include "jsatominlines.h" @@ -7201,6 +7200,50 @@ js_AbortRecording(JSContext* cx, const char* reason) #endif } +#if defined NANOJIT_IA32 +static bool +CheckForSSE2() +{ + char *c = getenv("X86_FORCE_SSE2"); + if (c) + return (!strcmp(c, "true") || + !strcmp(c, "1") || + !strcmp(c, "yes")); + + int features = 0; +#if defined _MSC_VER + __asm + { + pushad + mov eax, 1 + cpuid + mov features, edx + popad + } +#elif defined __GNUC__ + asm("xchg %%esi, %%ebx\n" /* we can't clobber ebx on gcc (PIC register) */ + "mov $0x01, %%eax\n" + "cpuid\n" + "mov %%edx, %0\n" + "xchg %%esi, %%ebx\n" + : "=m" (features) + : /* We have no inputs */ + : "%eax", "%esi", "%ecx", "%edx" + ); +#elif defined __SUNPRO_C || defined __SUNPRO_CC + asm("push %%ebx\n" + "mov $0x01, %%eax\n" + "cpuid\n" + "pop %%ebx\n" + : "=d" (features) + : /* We have no inputs */ + : "%eax", "%ecx" + ); +#endif + return (features & (1<<26)) != 0; +} +#endif + #if defined(NANOJIT_ARM) #if defined(_MSC_VER) && defined(WINCE) @@ -7462,7 +7505,7 @@ js_InitJIT(JSTraceMonitor *tm) if (!did_we_check_processor_features) { #if defined NANOJIT_IA32 avmplus::AvmCore::config.use_cmov = - avmplus::AvmCore::config.sse2 = js_use_SSE2; + avmplus::AvmCore::config.sse2 = CheckForSSE2(); avmplus::AvmCore::config.fixed_esp = true; #endif #if defined NANOJIT_ARM diff --git a/js/src/jsutil.cpp b/js/src/jsutil.cpp index 774522c8bad..0394996eb0c 100644 --- a/js/src/jsutil.cpp +++ b/js/src/jsutil.cpp @@ -43,7 +43,6 @@ */ #include #include -#include #include "jstypes.h" #include "jsstdint.h" #include "jsutil.h" @@ -72,52 +71,6 @@ JS_PUBLIC_API(void) JS_Assert(const char *s, const char *file, JSIntn ln) #endif } -#ifdef MUST_DETECT_SSE2 -bool js_use_SSE2; - -bool -js_DetectSSE2() -{ - char *c = getenv("X86_FORCE_SSE2"); - if (c) - return (!strcmp(c, "true") || - !strcmp(c, "1") || - !strcmp(c, "yes")); - - int features = 0; -#if defined _MSC_VER - __asm - { - pushad - mov eax, 1 - cpuid - mov features, edx - popad - } -#elif defined __GNUC__ - asm("xchg %%esi, %%ebx\n" /* we can't clobber ebx on gcc (PIC register) */ - "mov $0x01, %%eax\n" - "cpuid\n" - "mov %%edx, %0\n" - "xchg %%esi, %%ebx\n" - : "=m" (features) - : /* We have no inputs */ - : "%eax", "%esi", "%ecx", "%edx" - ); -#elif defined __SUNPRO_C || defined __SUNPRO_CC - asm("push %%ebx\n" - "mov $0x01, %%eax\n" - "cpuid\n" - "pop %%ebx\n" - : "=d" (features) - : /* We have no inputs */ - : "%eax", "%ecx" - ); -#endif - return (features & (1<<26)) != 0; -} -#endif - #ifdef JS_BASIC_STATS #include diff --git a/js/src/jsutil.h b/js/src/jsutil.h index ad8fa2e4eb2..08a31eee4bf 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -74,17 +74,6 @@ JS_Assert(const char *s, const char *file, JSIntn ln); #endif /* defined(DEBUG) */ -#ifdef HAVE_SSE2 -#ifdef MUST_DETECT_SSE2 -extern bool js_use_SSE2; - -bool -js_DetectSSE2(); -#else -static const bool js_use_SSE2 = true; -#endif -#endif - /* * Compile-time assert. "cond" must be a constant expression. * The macro can be used only in places where an "extern" declaration is From 90dbf27bde0acab8c7355d523c8441876ec8b105 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Wed, 16 Dec 2009 03:18:10 -0500 Subject: [PATCH 68/82] Backed out changeset 787d5fbc1f80 --- js/src/tests/js1_5/Regress/jstests.list | 1 - js/tests/js1_5/Regress/regress-514808.js | 80 ------------------------ 2 files changed, 81 deletions(-) delete mode 100644 js/tests/js1_5/Regress/regress-514808.js diff --git a/js/src/tests/js1_5/Regress/jstests.list b/js/src/tests/js1_5/Regress/jstests.list index 18a1a21898b..4face6cea9f 100644 --- a/js/src/tests/js1_5/Regress/jstests.list +++ b/js/src/tests/js1_5/Regress/jstests.list @@ -343,7 +343,6 @@ script regress-501124.js script regress-504078.js script regress-506567.js script regress-511859.js -script regress-514808.js script regress-57043.js script regress-58116.js script regress-68498-001.js diff --git a/js/tests/js1_5/Regress/regress-514808.js b/js/tests/js1_5/Regress/regress-514808.js deleted file mode 100644 index 9243f57d306..00000000000 --- a/js/tests/js1_5/Regress/regress-514808.js +++ /dev/null @@ -1,80 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is JavaScript Engine testing utilities. - * - * The Initial Developer of the Original Code is - * Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2008 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): Dave Mandelin - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -var gTestfile = 'regress-514808.js'; -//----------------------------------------------------------------------------- -var BUGNUMBER = 514808; -var summary = 'Correct space character class in regexes'; -var actual = ''; -var expect = ''; - - -//----------------------------------------------------------------------------- -test(); -//----------------------------------------------------------------------------- - -function test() -{ - enterFunc ('test'); - printBugNumber(BUGNUMBER); - printStatus (summary); - - var spaces = [ "\u0009", "\u000b", "\u000c", "\u0020", "\u00a0", "\u1680", - "\u180e", "\u2000", "\u2001", "\u2002", "\u2003", "\u2004", - "\u2005", "\u2006", "\u2007", "\u2008", "\u2009", "\u200a", - "\u202f", "\u205f", "\u3000" ]; - var line_terminators = [ "\u2028", "\u2029", "\u000a", "\u000d" ]; - var space_chars = [].concat(spaces, line_terminators); - - var non_space_chars = [ "\u200b", "\u200c", "\u200d", - "\ufeff" ]; - - var chars = [].concat(space_chars, non_space_chars); - var is_space = [].concat(space_chars.map(function(ch) { return true; }), - non_space_chars.map(function(ch) { return false; })); - var expect = is_space.join(','); - - var actual = chars.map(function(ch) { return /\s/.test(ch); }).join(','); - reportCompare(expect, actual, summary); - - jit(true); - var actual = chars.map(function(ch) { return /\s/.test(ch); }).join(','); - reportCompare(expect, actual, summary); - jit(false); - - exitFunc ('test'); -} From 91d87dc586dbe05ab39726d27a92ba35e878cb76 Mon Sep 17 00:00:00 2001 From: Robert Sayre Date: Wed, 16 Dec 2009 03:18:59 -0500 Subject: [PATCH 69/82] Backed out changeset 8a44551a28c1 --- js/src/jsstr.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 6d3966f105d..d7534fdb289 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -4284,8 +4284,8 @@ const uint8 js_Y[] = { 60, 15, 60, 60, 60, 60, 46, 46, /* 27 */ 9, 9, 9, 9, 9, 9, 9, 9, /* 27 */ 9, 9, 46, 46, 46, 46, 46, 46, /* 27 */ - 2, 46, 46, 46, 46, 46, 46, 46, /* 28 */ - 46, 46, 46, 46, 46, 46, 2, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 28 */ @@ -4597,17 +4597,17 @@ const uint8 js_Y[] = { 46, 46, 16, 94, 16, 46, 16, 16, /* 66 */ 102, 102, 103, 103, 96, 11, 11, 46, /* 66 */ 2, 2, 2, 2, 2, 2, 2, 2, /* 67 */ - 2, 2, 2, 104, 104, 104, 104, 104, /* 67 */ + 2, 2, 2, 2, 104, 104, 104, 104, /* 67 */ 8, 8, 8, 8, 8, 8, 3, 3, /* 67 */ 5, 6, 5, 5, 5, 6, 5, 5, /* 67 */ 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ -105, 106, 104, 104, 104, 104, 104, 2, /* 67 */ +105, 106, 104, 104, 104, 104, 104, 46, /* 67 */ 3, 3, 3, 3, 3, 3, 3, 3, /* 67 */ 3, 5, 6, 3, 3, 3, 3, 12, /* 67 */ 12, 3, 3, 3, 7, 5, 6, 46, /* 68 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ - 46, 46, 46, 46, 46, 46, 46, 2, /* 68 */ + 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ 46, 46, 46, 46, 46, 46, 46, 46, /* 68 */ 46, 46, 104, 104, 104, 104, 104, 104, /* 68 */ 17, 46, 46, 46, 17, 17, 17, 17, /* 68 */ From 5ad8dc39c33f500d1526422f1819ccab5c8013cf Mon Sep 17 00:00:00 2001 From: Jeff Wang Date: Tue, 15 Dec 2009 18:16:12 -0500 Subject: [PATCH 70/82] 64-bit Mac OS X bustage fix for font code. b=532346 r=jkew r=joshmoz --- gfx/thebes/src/gfxMacPlatformFontList.h | 4 ++++ gfx/thebes/src/gfxMacPlatformFontList.mm | 16 ++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/gfx/thebes/src/gfxMacPlatformFontList.h b/gfx/thebes/src/gfxMacPlatformFontList.h index a7e824ee2f3..0098d0a02fe 100644 --- a/gfx/thebes/src/gfxMacPlatformFontList.h +++ b/gfx/thebes/src/gfxMacPlatformFontList.h @@ -70,7 +70,9 @@ public: ATSFontRef GetFontRef(); nsresult ReadCMAP(); +#ifndef __LP64__ PRBool UseLiGothicAtsuiHack() { return mUseLiGothicAtsuiHack; } +#endif protected: // for use with data fonts @@ -82,7 +84,9 @@ protected: ATSFontRef mATSFontRef; PRPackedBool mATSFontRefInitialized; +#ifndef __LP64__ PRPackedBool mUseLiGothicAtsuiHack; +#endif }; class gfxMacPlatformFontList : public gfxPlatformFontList { diff --git a/gfx/thebes/src/gfxMacPlatformFontList.mm b/gfx/thebes/src/gfxMacPlatformFontList.mm index 6aa5abcb7e3..8f02e59529e 100644 --- a/gfx/thebes/src/gfxMacPlatformFontList.mm +++ b/gfx/thebes/src/gfxMacPlatformFontList.mm @@ -117,8 +117,10 @@ MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName, PRBool aIsStandardFace) : gfxFontEntry(aPostscriptName, aFamily, aIsStandardFace), mATSFontRef(0), - mATSFontRefInitialized(PR_FALSE), - mUseLiGothicAtsuiHack(PR_FALSE) + mATSFontRefInitialized(PR_FALSE) +#ifndef __LP64__ + , mUseLiGothicAtsuiHack(PR_FALSE) +#endif { mWeight = aWeight; } @@ -128,8 +130,10 @@ MacOSFontEntry::MacOSFontEntry(const nsAString& aPostscriptName, ATSFontRef aFon gfxUserFontData *aUserFontData) : gfxFontEntry(aPostscriptName), mATSFontRef(aFontRef), - mATSFontRefInitialized(PR_TRUE), - mUseLiGothicAtsuiHack(PR_FALSE) + mATSFontRefInitialized(PR_TRUE) +#ifndef __LP64__ + , mUseLiGothicAtsuiHack(PR_FALSE) +#endif { // xxx - stretch is basically ignored for now @@ -265,6 +269,8 @@ MacOSFontEntry::ReadCMAP() } } +#ifndef __LP64__ /* ATSUI not available on 64-bit */ + if ((gfxPlatformMac::GetPlatform()->OSXVersion() & MAC_OS_X_MAJOR_VERSION_MASK) == MAC_OS_X_VERSION_10_6_HEX) { // even ruder hack - LiGothic font on 10.6 has a bad glyph for U+775B @@ -281,6 +287,8 @@ MacOSFontEntry::ReadCMAP() } } +#endif /* not __LP64__ */ + PR_LOG(gFontInfoLog, PR_LOG_DEBUG, ("(fontinit-cmap) psname: %s, size: %d\n", NS_ConvertUTF16toUTF8(mName).get(), mCharacterMap.GetSize())); From b9caea2cb66fc4c101c3a54a1ea6573a0c51023c Mon Sep 17 00:00:00 2001 From: Ted Mielczarek Date: Tue, 15 Dec 2009 01:52:57 -0500 Subject: [PATCH 71/82] Rename sqlite3 library to mozsqlite3, fixes compile problems and crashes on Mac OS X. b=513747 r=sdwilsh --- browser/installer/package-manifest.in | 2 +- browser/installer/removed-files.in | 1 + configure.in | 2 +- db/sqlite3/src/Makefile.in | 2 +- db/sqlite3/src/sqlite.def | 2 +- security/manager/Makefile.in | 5 ++++- xpcom/stub/Makefile.in | 2 +- 7 files changed, 10 insertions(+), 6 deletions(-) diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 7cfc4b4604a..de97cd41cb4 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -87,7 +87,7 @@ #elifdef XP_OS2 @BINPATH@/plugins/npnulos2.dll #endif -@BINPATH@/@DLL_PREFIX@sqlite3@DLL_SUFFIX@ +@BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@ @BINPATH@/README.txt @BINPATH@/LICENSE @BINPATH@/blocklist.xml diff --git a/browser/installer/removed-files.in b/browser/installer/removed-files.in index 496a7e25044..e09d1376018 100644 --- a/browser/installer/removed-files.in +++ b/browser/installer/removed-files.in @@ -843,4 +843,5 @@ components/brwsrcmp.dll components/nsUpdateService.js components/nsUpdateServiceStub.js #endif +@DLL_PREFIX@sqlite3@DLL_SUFFIX@ old-homepage-default.properties diff --git a/configure.in b/configure.in index ea750e00412..5d76ffc0103 100644 --- a/configure.in +++ b/configure.in @@ -6142,7 +6142,7 @@ MOZ_NATIVE_SQLITE= ) if test -z "$MOZ_NATIVE_SQLITE" then SQLITE_CFLAGS= - SQLITE_LIBS='$(call EXPAND_LIBNAME_PATH,sqlite3,$(DIST)/lib)' + SQLITE_LIBS='$(call EXPAND_LIBNAME_PATH,mozsqlite3,$(DIST)/lib)' else PKG_CHECK_MODULES(SQLITE, sqlite3 >= $SQLITE_VERSION) fi diff --git a/db/sqlite3/src/Makefile.in b/db/sqlite3/src/Makefile.in index 9cbc0af2296..ab019627616 100644 --- a/db/sqlite3/src/Makefile.in +++ b/db/sqlite3/src/Makefile.in @@ -46,7 +46,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk MODULE = sqlite3 -LIBRARY_NAME = sqlite3 +LIBRARY_NAME = mozsqlite3 FORCE_SHARED_LIB = 1 VISIBILITY_FLAGS = LIB_IS_C_ONLY = 1 diff --git a/db/sqlite3/src/sqlite.def b/db/sqlite3/src/sqlite.def index 7b56079713f..26e9571fd36 100644 --- a/db/sqlite3/src/sqlite.def +++ b/db/sqlite3/src/sqlite.def @@ -33,7 +33,7 @@ ; ; ***** END LICENSE BLOCK ***** -LIBRARY sqlite3.dll +LIBRARY mozsqlite3.dll EXPORTS sqlite3_aggregate_context diff --git a/security/manager/Makefile.in b/security/manager/Makefile.in index 5b2eabd6081..19efa4e9dff 100644 --- a/security/manager/Makefile.in +++ b/security/manager/Makefile.in @@ -183,6 +183,9 @@ DEFAULT_GMAKE_FLAGS += MOZILLA_CLIENT=1 DEFAULT_GMAKE_FLAGS += NO_MDUPDATE=1 DEFAULT_GMAKE_FLAGS += NSS_ENABLE_ECC=1 DEFAULT_GMAKE_FLAGS += IMPORT_LIB_SUFFIX=".$(IMPORT_LIB_SUFFIX)" +ifndef MOZ_NATIVE_SQLITE +DEFAULT_GMAKE_FLAGS += SQLITE=$(call EXPAND_LIBNAME,mozsqlite3) +endif ifdef NSS_DISABLE_DBM DEFAULT_GMAKE_FLAGS += NSS_DISABLE_DBM=1 endif @@ -320,7 +323,7 @@ ifndef NSS_DISABLE_DBM endif $(NSSMAKE) -C $(NSS_SRCDIR)/security/nss/lib $(DEFAULT_GMAKE_FLAGS) ifdef ENABLE_TESTS - # Need certutil binary for mochitest certificates generation +# Need certutil binary for mochitest certificates generation $(NSSMAKE) -C $(NSS_SRCDIR)/security/nss/cmd/lib $(DEFAULT_GMAKE_FLAGS) $(NSSMAKE) -C $(NSS_SRCDIR)/security/nss/cmd/certutil $(DEFAULT_GMAKE_FLAGS) $(NSSMAKE) -C $(NSS_SRCDIR)/security/nss/cmd/pk12util $(DEFAULT_GMAKE_FLAGS) diff --git a/xpcom/stub/Makefile.in b/xpcom/stub/Makefile.in index 843677daa5e..f4f9ee88afc 100644 --- a/xpcom/stub/Makefile.in +++ b/xpcom/stub/Makefile.in @@ -83,7 +83,7 @@ DEPENDENT_LIBS_LIST += \ ifdef MOZ_ENABLE_LIBXUL DEPENDENT_LIBS_LIST += \ - $(LIB_PREFIX)sqlite3$(DLL_SUFFIX) \ + $(LIB_PREFIX)mozsqlite3$(DLL_SUFFIX) \ $(LIB_PREFIX)nssutil3$(DLL_SUFFIX) \ $(LIB_PREFIX)softokn3$(DLL_SUFFIX) \ $(LIB_PREFIX)nss3$(DLL_SUFFIX) \ From e3b2653adee50064c0250196f66477af47b4346e Mon Sep 17 00:00:00 2001 From: Brendan Eich Date: Wed, 16 Dec 2009 18:11:19 -0600 Subject: [PATCH 72/82] Bug 533705 - Lock proto-scope around canProvideEmptyScope/getEmptyScope; check for slot mismatch in js_AddProperty; don't optimize for slot mismatch in JSOP_SETPROP. r=jorendorff. --HG-- extra : rebase_source : 4d49e9ef150286db93f2e5e1c77b217832263f83 --- js/src/jsbuiltins.cpp | 3 +- js/src/jsinterp.cpp | 1 - js/src/jsinterp.h | 2 -- js/src/jsobj.cpp | 39 ++++++++++++++-------- js/src/jsops.cpp | 15 +++------ js/src/jsscope.cpp | 6 ++++ js/src/trace-test/tests/basic/bug533705.js | 26 +++++++++++++++ 7 files changed, 64 insertions(+), 28 deletions(-) create mode 100644 js/src/trace-test/tests/basic/bug533705.js diff --git a/js/src/jsbuiltins.cpp b/js/src/jsbuiltins.cpp index 717aac0120c..3338d90d45d 100644 --- a/js/src/jsbuiltins.cpp +++ b/js/src/jsbuiltins.cpp @@ -235,7 +235,8 @@ js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop) uint32 slot = sprop->slot; JSScope* scope = OBJ_SCOPE(obj); - JS_ASSERT(slot == scope->freeslot); + if (slot != scope->freeslot) + goto exit_trace; JS_ASSERT(sprop->parent == scope->lastProperty()); if (scope->owned()) { diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index a87fdef75a0..e60629c5a7a 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -535,7 +535,6 @@ js_PurgePropertyCache(JSContext *cx, JSPropertyCache *cache) P(addpchits); P(setpchits); P(setpcmisses); - P(slotchanges); P(setmisses); P(idmisses); P(komisses); diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 86c75bc28c1..07b707778d1 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -313,8 +313,6 @@ typedef struct JSPropertyCache { uint32 addpchits; /* adding next property pchit case */ uint32 setpchits; /* setting existing property pchit */ uint32 setpcmisses; /* setting/adding property pc misses */ - uint32 slotchanges; /* clasp->reserveSlots result variance- - induced slot changes */ uint32 setmisses; /* JSOP_SET{NAME,PROP} total misses */ uint32 idmisses; /* slow-path key id == atom misses */ uint32 komisses; /* slow-path key object misses */ diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 60dcd98b29f..6832f8d3e0a 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -2206,13 +2206,24 @@ InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, JSObjectOps* o /* Share proto's emptyScope only if obj is similar to proto. */ JSClass *clasp = OBJ_GET_CLASS(cx, obj); - JSScope *scope; - if (proto && OBJ_IS_NATIVE(proto) && - (scope = OBJ_SCOPE(proto))->canProvideEmptyScope(ops, clasp)) { - scope = scope->getEmptyScope(cx, clasp); - if (!scope) - goto bad; - } else { + JSScope *scope = NULL; + + if (proto && OBJ_IS_NATIVE(proto)) { + JS_LOCK_OBJ(cx, proto); + scope = OBJ_SCOPE(proto); + if (scope->canProvideEmptyScope(ops, clasp)) { + JSScope *emptyScope = scope->getEmptyScope(cx, clasp); + JS_UNLOCK_SCOPE(cx, scope); + if (!emptyScope) + goto bad; + scope = emptyScope; + } else { + JS_UNLOCK_SCOPE(cx, scope); + scope = NULL; + } + } + + if (!scope) { scope = JSScope::create(cx, ops, clasp, obj, js_GenerateShape(cx, false)); if (!scope) goto bad; @@ -2225,6 +2236,7 @@ InitScopeForObject(JSContext* cx, JSObject* obj, JSObject* proto, JSObjectOps* o goto bad; } } + obj->map = scope; return true; @@ -3549,14 +3561,16 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, return obj; } -/* XXXbe if one adds props, deletes earlier props, adds more, the last added - won't recycle the deleted props' slots. */ +/* + * If one adds props, deletes earlier props, adds more, the last added won't + * recycle the deleted props' slots. FIXME: bug NNNNNN + */ JSBool js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) { - JS_ASSERT(OBJ_IS_NATIVE(obj)); - JSScope *scope = OBJ_SCOPE(obj); + JS_ASSERT(scope->object == obj); + JSClass *clasp = obj->getClass(); if (scope->freeslot == JSSLOT_FREE(clasp) && clasp->reserveSlots) { /* Adjust scope->freeslot to include computed reserved slots, if any. */ @@ -3577,9 +3591,8 @@ js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp) void js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot) { - JS_ASSERT(OBJ_IS_NATIVE(obj)); - JSScope *scope = OBJ_SCOPE(obj); + JS_ASSERT(scope->object == obj); LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_VOID); if (scope->freeslot == slot + 1) scope->freeslot = slot; diff --git a/js/src/jsops.cpp b/js/src/jsops.cpp index 89790c1fb8f..aea0786a42b 100644 --- a/js/src/jsops.cpp +++ b/js/src/jsops.cpp @@ -1815,10 +1815,10 @@ BEGIN_CASE(JSOP_SETMETHOD) * if something created a hash table for scope, we must * pay the price of JSScope::putProperty. * - * If slot does not match the cached sprop's slot, - * update the cache entry in the hope that obj and - * other instances with the same number of reserved - * slots are now "hot". + * (A reserveSlots hook can cause scopes of the same + * shape to have different freeslot values. This is + * what causes the slot != sprop->slot case. See + * js_GetMutableScope.) */ if (slot != sprop->slot || scope->table) { JSScopeProperty *sprop2 = @@ -1831,13 +1831,6 @@ BEGIN_CASE(JSOP_SETMETHOD) JS_UNLOCK_SCOPE(cx, scope); goto error; } - if (sprop2 != sprop) { - PCMETER(cache->slotchanges++); - JS_ASSERT(slot != sprop->slot && - slot == sprop2->slot && - sprop2->id == sprop->id); - entry->vword = SPROP_TO_PCVAL(sprop2); - } sprop = sprop2; } else { scope->extend(cx, sprop); diff --git a/js/src/jsscope.cpp b/js/src/jsscope.cpp index ea7281b8b60..316087ea1ec 100644 --- a/js/src/jsscope.cpp +++ b/js/src/jsscope.cpp @@ -112,6 +112,12 @@ js_GetMutableScope(JSContext *cx, JSObject *obj) JS_ASSERT(newscope->freeslot == JSSLOT_FREE(STOBJ_GET_CLASS(obj))); clasp = STOBJ_GET_CLASS(obj); if (clasp->reserveSlots) { + /* + * FIXME: Here we change OBJ_SCOPE(obj)->freeslot without changing + * OBJ_SHAPE(obj). If we strengthen the shape guarantees to cover + * freeslot, we can eliminate a check in JSOP_SETPROP and in + * js_AddProperty. See bug 535416. + */ freeslot = JSSLOT_FREE(clasp) + clasp->reserveSlots(cx, obj); if (freeslot > STOBJ_NSLOTS(obj)) freeslot = STOBJ_NSLOTS(obj); diff --git a/js/src/trace-test/tests/basic/bug533705.js b/js/src/trace-test/tests/basic/bug533705.js new file mode 100644 index 00000000000..c1dbb3f7551 --- /dev/null +++ b/js/src/trace-test/tests/basic/bug533705.js @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +function mk() { + return (function () {}); +} + +function f() { + var j = 55; + + var f = function () { + return j; + }; + + var g = function() {}; + + var a = [ mk(), f, g, mk(), mk() ]; + + for (var i = 0; i < 5; ++i) { + a[i].p = 99; + } +} + +f(); + +for (var i = 0; i < 9; i++) + ({__parent__: []} = []); From 6791c09f56eeb86f77409f22ef9e09c36974ef2a Mon Sep 17 00:00:00 2001 From: Ted Mielczarek Date: Tue, 15 Dec 2009 01:52:21 -0500 Subject: [PATCH 73/82] NSS: Allow the specification of an alternate library for SQLite. b=519550 r=rrelyea --- security/nss/cmd/platlibs.mk | 3 ++- security/nss/lib/softoken/config.mk | 9 ++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/security/nss/cmd/platlibs.mk b/security/nss/cmd/platlibs.mk index ab8024386bd..d81670da2c8 100644 --- a/security/nss/cmd/platlibs.mk +++ b/security/nss/cmd/platlibs.mk @@ -92,6 +92,7 @@ ifdef USE_STATIC_LIBS # can't do this in manifest.mn because OS_ARCH isn't defined there. ifeq (,$(filter-out WINNT WINCE,$(OS_ARCH))) +SQLITE = $(LIB_PREFIX)sqlite3.$(LIB_SUFFIX) DEFINES += -DNSS_USE_STATIC_LIBS # $(PROGRAM) has explicit dependencies on $(EXTRA_LIBS) @@ -129,7 +130,7 @@ EXTRA_LIBS += \ $(DIST)/lib/$(LIB_PREFIX)nssb.$(LIB_SUFFIX) \ $(PKIXLIB) \ $(DBMLIB) \ - $(DIST)/lib/$(LIB_PREFIX)sqlite3.$(LIB_SUFFIX) \ + $(DIST)/lib/$(SQLITE) \ $(DIST)/lib/$(LIB_PREFIX)nssutil3.$(LIB_SUFFIX) \ $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plc4.$(LIB_SUFFIX) \ $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plds4.$(LIB_SUFFIX) \ diff --git a/security/nss/lib/softoken/config.mk b/security/nss/lib/softoken/config.mk index c434af286ba..18ec1d8e05e 100644 --- a/security/nss/lib/softoken/config.mk +++ b/security/nss/lib/softoken/config.mk @@ -53,11 +53,12 @@ RES = $(OBJDIR)/$(LIBRARY_NAME).res RESNAME = $(LIBRARY_NAME).rc ifdef NS_USE_GCC +SQLITE = -lsqlite3 EXTRA_SHARED_LIBS += \ -L$(DIST)/lib \ -L$(NSSUTIL_LIB_DIR) \ -lnssutil3 \ - -lsqlite3 \ + $(SQLITE) \ -L$(NSPR_LIB_DIR) \ -lplc4 \ -lplds4 \ @@ -65,8 +66,9 @@ EXTRA_SHARED_LIBS += \ $(NULL) else # ! NS_USE_GCC +SQLITE = sqlite3.lib EXTRA_SHARED_LIBS += \ - $(DIST)/lib/sqlite3.lib \ + $(DIST)/lib/$(SQLITE) \ $(DIST)/lib/nssutil3.lib \ $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plc4.lib \ $(NSPR_LIB_DIR)/$(NSPR31_LIB_PREFIX)plds4.lib \ @@ -79,11 +81,12 @@ else # $(PROGRAM) has NO explicit dependencies on $(EXTRA_SHARED_LIBS) # $(EXTRA_SHARED_LIBS) come before $(OS_LIBS), except on AIX. +SQLITE = -lsqlite3 EXTRA_SHARED_LIBS += \ -L$(DIST)/lib \ -L$(NSSUTIL_LIB_DIR) \ -lnssutil3 \ - -lsqlite3 \ + $(SQLITE) \ -L$(NSPR_LIB_DIR) \ -lplc4 \ -lplds4 \ From 2098f14d9fa9694bdb71c0f9ad12a044cd8c8edf Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 16 Dec 2009 18:58:47 -0800 Subject: [PATCH 74/82] Fixed regression where lazy imports could happen in JSOP_ARGUMENTS conditional branch (bug 535474, r=gal). --- js/src/jstracer.cpp | 8 ++++---- js/src/jstracer.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index 401c70670bf..00e00a24e39 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -10078,11 +10078,10 @@ TraceRecorder::record_JSOP_IFNE() } LIns* -TraceRecorder::newArguments() +TraceRecorder::newArguments(LIns* callee_ins) { LIns* global_ins = INS_CONSTOBJ(globalObj); LIns* argc_ins = INS_CONST(cx->fp->argc); - LIns* callee_ins = get(&cx->fp->argv[-2]); LIns* argv_ins = cx->fp->argc ? lir->ins2(LIR_piadd, lirbuf->sp, lir->insImmWord(nativespOffset(&cx->fp->argv[0]))) @@ -10106,9 +10105,10 @@ TraceRecorder::record_JSOP_ARGUMENTS() LIns* a_ins = get(&cx->fp->argsobj); LIns* args_ins; + LIns* callee_ins = get(&cx->fp->argv[-2]); if (a_ins->opcode() == LIR_int) { // |arguments| is set to 0 by EnterFrame on this trace, so call to create it. - args_ins = newArguments(); + args_ins = newArguments(callee_ins); } else { // Generate LIR to create arguments only if it has not already been created. @@ -10121,7 +10121,7 @@ TraceRecorder::record_JSOP_ARGUMENTS() LIns* label1 = lir->ins0(LIR_label); br1->setTarget(label1); - LIns* call_ins = newArguments(); + LIns* call_ins = newArguments(callee_ins); lir->insStorei(call_ins, mem_ins, 0); LIns* label2 = lir->ins0(LIR_label); diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 02cb4b176a0..eb9ac9567d9 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1149,7 +1149,7 @@ class TraceRecorder JS_REQUIRES_STACK nanojit::LIns* makeNumberInt32(nanojit::LIns* f); JS_REQUIRES_STACK nanojit::LIns* stringify(jsval& v); - JS_REQUIRES_STACK nanojit::LIns* newArguments(); + JS_REQUIRES_STACK nanojit::LIns* newArguments(nanojit::LIns* callee_ins); JS_REQUIRES_STACK RecordingStatus call_imacro(jsbytecode* imacro); From 2b9de94325af506489241594f4f771d46e215f2b Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 16 Dec 2009 19:22:04 -0800 Subject: [PATCH 75/82] Added test case for bug 535474. --- js/src/trace-test/tests/basic/bug535474.js | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 js/src/trace-test/tests/basic/bug535474.js diff --git a/js/src/trace-test/tests/basic/bug535474.js b/js/src/trace-test/tests/basic/bug535474.js new file mode 100644 index 00000000000..6e34298a2b8 --- /dev/null +++ b/js/src/trace-test/tests/basic/bug535474.js @@ -0,0 +1,27 @@ +function f() { + var _76 = {}; + for (var i = 0; i < arguments.length; i++) { + var typ = arguments[i]; + _76[typ] = typ; + } + return function () { + for (var i = 0; i < arguments.length; i++) { + if (!(typeof (arguments[i]) in _76)) { + return false; + } + } + return true; + } +} + +g = f("number", "boolean", "object"); + +g("a", "b", "c", "d", "e", "f", 2); +g(2, "a", "b", "c", "d", "e", "f", 2); + +/* + * Don't assert -- + * Assertion failed: frame entry -4 wasn't freed + * : _activation.entry[i] == 0 (../nanojit/Assembler.cpp:786) + */ + From 66687af7abba0cfa17b358bb4852d5bb3aa753c8 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 16 Dec 2009 22:06:07 -0800 Subject: [PATCH 76/82] Fixed lazy import regression where down recursion could build wrong exit typemaps (bug 534745, r=gal). --- js/src/jsrecursion.cpp | 25 +++++++++++++++++++++++++ js/src/jstracer.h | 1 + 2 files changed, 26 insertions(+) diff --git a/js/src/jsrecursion.cpp b/js/src/jsrecursion.cpp index 2ea643d9c3c..1acc4bc1883 100644 --- a/js/src/jsrecursion.cpp +++ b/js/src/jsrecursion.cpp @@ -570,6 +570,21 @@ TraceRecorder::slurpDownFrames(jsbytecode* return_pc) return closeLoop(slotMap, exit); } +class ImportFrameSlotsVisitor : public SlotVisitorBase +{ + TraceRecorder &mRecorder; +public: + ImportFrameSlotsVisitor(TraceRecorder &recorder) : mRecorder(recorder) + {} + + JS_REQUIRES_STACK JS_ALWAYS_INLINE bool + visitStackSlots(jsval *vp, size_t count, JSStackFrame* fp) { + for (size_t i = 0; i < count; ++i) + mRecorder.get(vp++); + return true; + } +}; + JS_REQUIRES_STACK AbortableRecordingStatus TraceRecorder::downRecursion() { @@ -594,6 +609,16 @@ TraceRecorder::downRecursion() LIns* rp_top = lir->ins2(LIR_piadd, lirbuf->rp, lir->insImmWord(sizeof(FrameInfo*))); guard(true, lir->ins2(LIR_plt, rp_top, eor_ins), OOM_EXIT); + /* + * For every slot in the new frame that is not in the tracker, create a load + * in the tracker. This is necessary because otherwise snapshot() will see + * missing imports and use the down frame, rather than the new frame. + * This won't affect performance because the loads will be killed if not + * used. + */ + ImportFrameSlotsVisitor visitor(*this); + VisitStackSlots(visitor, cx, callDepth); + /* Add space for a new JIT frame. */ lirbuf->sp = lir->ins2(LIR_piadd, lirbuf->sp, lir->insImmWord(slots * sizeof(double))); lir->insStorei(lirbuf->sp, lirbuf->state, offsetof(InterpState, sp)); diff --git a/js/src/jstracer.h b/js/src/jstracer.h index eb9ac9567d9..c593e3bfb6b 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1379,6 +1379,7 @@ class TraceRecorder friend class AdjustCallerGlobalTypesVisitor; friend class AdjustCallerStackTypesVisitor; friend class TypeCompatibilityVisitor; + friend class ImportFrameSlotsVisitor; friend class SlotMap; friend class DefaultSlotMap; friend class DetermineTypesVisitor; From 4f96be79de18c737422854764dd766c8e32fe733 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Thu, 17 Dec 2009 00:21:48 -0800 Subject: [PATCH 77/82] Extensions/libraries/plugins might enable FPU/SSE2 exceptions, causing floating-point operations to crash (533035, r=robarnold). --- toolkit/xre/Makefile.in | 5 +- toolkit/xre/nsAppRunner.cpp | 8 +- toolkit/xre/nsSigHandlers.cpp | 160 +++++++++++++++++++++++++++++++++- toolkit/xre/nsSigHandlers.h | 72 +++++++++++++++ 4 files changed, 232 insertions(+), 13 deletions(-) create mode 100644 toolkit/xre/nsSigHandlers.h diff --git a/toolkit/xre/Makefile.in b/toolkit/xre/Makefile.in index d0d8e5588c0..0968e83bd2e 100644 --- a/toolkit/xre/Makefile.in +++ b/toolkit/xre/Makefile.in @@ -67,6 +67,7 @@ CPPSRCS = \ nsXREDirProvider.cpp \ nsNativeAppSupportBase.cpp \ nsAppData.cpp \ + nsSigHandlers.cpp \ $(NULL) ifdef MOZ_SPLASHSCREEN @@ -120,10 +121,6 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa) CMMSRCS += MacApplicationDelegate.mm endif -ifneq (,$(filter-out OS2 WINNT,$(OS_ARCH))) -CPPSRCS += nsSigHandlers.cpp -endif - SHARED_LIBRARY_LIBS += ../profile/src/$(LIB_PREFIX)profile_s.$(LIB_SUFFIX) ifdef MOZ_ENABLE_XREMOTE diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index f385d48f97c..efdadad2b83 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -235,9 +235,7 @@ protected: }; #endif -#if defined(XP_UNIX) || defined(XP_BEOS) - extern void InstallUnixSignalHandlers(const char *ProgramName); -#endif +extern void InstallSignalHandlers(const char *ProgramName); #define FILE_COMPATIBILITY_INFO NS_LITERAL_CSTRING("compatibility.ini") @@ -2651,9 +2649,7 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) #endif #endif -#if defined(XP_UNIX) || defined(XP_BEOS) - InstallUnixSignalHandlers(argv[0]); -#endif + InstallSignalHandlers(argv[0]); #ifdef MOZ_ACCESSIBILITY_ATK // Reset GTK_MODULES, strip atk-bridge if exists diff --git a/toolkit/xre/nsSigHandlers.cpp b/toolkit/xre/nsSigHandlers.cpp index 05386a185b5..8c7bfb2c202 100644 --- a/toolkit/xre/nsSigHandlers.cpp +++ b/toolkit/xre/nsSigHandlers.cpp @@ -42,6 +42,10 @@ * platforms that do not support it. */ +#include "nsSigHandlers.h" + +#if !defined(XP_WIN) && !defined(XP_OS2) + #include #include #include @@ -55,6 +59,7 @@ #include #include #include // atoi +#include #endif #if defined(SOLARIS) @@ -204,7 +209,53 @@ my_glib_log_func(const gchar *log_domain, GLogLevelFlags log_level, #endif -void InstallUnixSignalHandlers(const char *ProgramName) +static void fpehandler(int signum, siginfo_t *si, void *context) +{ +#ifdef XP_MACOSX + ucontext_t *uc = (ucontext_t *)context; + +#if defined(__i386__) || defined(__amd64__) + _STRUCT_FP_CONTROL *ctrl = &uc->uc_mcontext->__fs.__fpu_fcw; + ctrl->__invalid = ctrl->__denorm = ctrl->__zdiv = ctrl->__ovrfl = ctrl->__undfl = ctrl->__precis = 1; + + _STRUCT_FP_STATUS *status = &uc->uc_mcontext->__fs.__fpu_fsw; + status->__invalid = status->__denorm = status->__zdiv = status->__ovrfl = status->__undfl = + status->__precis = status->__stkflt = status->__errsumm = 0; + + __uint32_t *mxcsr = &uc->uc_mcontext->__fs.__fpu_mxcsr; + *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */ + *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */ +#endif +#endif +#ifdef LINUX + ucontext_t *uc = (ucontext_t *)context; + +#if defined(__i386__) + /* + * It seems that we have no access to mxcsr on Linux. libc + * seems to be translating cw/sw to mxcsr. + */ + unsigned long int *cw = &uc->uc_mcontext.fpregs->cw; + *cw |= FPU_EXCEPTION_MASK; + + unsigned long int *sw = &uc->uc_mcontext.fpregs->sw; + *sw &= ~FPU_STATUS_FLAGS; +#endif +#if defined(__amd64__) + uint16_t *cw = &uc->uc_mcontext.fpregs->cwd; + *cwd |= FPU_EXCEPTION_MASK; + + uint16_t *sw = &uc->uc_mcontext.fpregs->swd; + *swd &= ~FPU_STATUS_FLAGS; + + __uint32_t *mxcsr = &uc->uc_mcontext->fpregs->mxcsr; + *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */ + *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */ +#endif +#endif +} + +void InstallSignalHandlers(const char *ProgramName) { PL_strncpy(_progname,ProgramName, (sizeof(_progname)-1) ); @@ -226,15 +277,19 @@ void InstallUnixSignalHandlers(const char *ProgramName) signal(SIGSEGV, abnormal_exit_handler); signal(SIGILL, abnormal_exit_handler); signal(SIGABRT, abnormal_exit_handler); - signal(SIGFPE, abnormal_exit_handler); #elif defined(CRAWL_STACK_ON_SIGSEGV) signal(SIGSEGV, ah_crap_handler); signal(SIGILL, ah_crap_handler); signal(SIGABRT, ah_crap_handler); - signal(SIGFPE, ah_crap_handler); #endif // CRAWL_STACK_ON_SIGSEGV + /* Install a handler for floating point exceptions and disable them if they occur. */ + struct sigaction sa, osa; + sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; + sa.sa_sigaction = fpehandler; + sigaction(SIGFPE, &sa, &osa); + #if defined(DEBUG) && defined(LINUX) const char *memLimit = PR_GetEnv("MOZ_MEM_LIMIT"); if (memLimit && *memLimit) @@ -289,3 +344,102 @@ void InstallUnixSignalHandlers(const char *ProgramName) } #endif } + +#else + +#include + +#ifdef _M_IX86 + +#if WINVER < 0x0601 +typedef struct DECLSPEC_ALIGN(16) _M128A { + ULONGLONG Low; + LONGLONG High; +} M128A, *PM128A; +#endif + +/* + * The ExtendedRegisters field of the x86.32 CONTEXT structure uses this layout; however, + * this structure is only made available from winnt.h on x86.64 + */ +typedef struct _XMM_SAVE_AREA32 { + WORD ControlWord; /* 000 */ + WORD StatusWord; /* 002 */ + BYTE TagWord; /* 004 */ + BYTE Reserved1; /* 005 */ + WORD ErrorOpcode; /* 006 */ + DWORD ErrorOffset; /* 008 */ + WORD ErrorSelector; /* 00c */ + WORD Reserved2; /* 00e */ + DWORD DataOffset; /* 010 */ + WORD DataSelector; /* 014 */ + WORD Reserved3; /* 016 */ + DWORD MxCsr; /* 018 */ + DWORD MxCsr_Mask; /* 01c */ + M128A FloatRegisters[8]; /* 020 */ + M128A XmmRegisters[16]; /* 0a0 */ + BYTE Reserved4[96]; /* 1a0 */ +} XMM_SAVE_AREA32, *PXMM_SAVE_AREA32; + +#define MXCSR(ctx) ((XMM_SAVE_AREA32*)((ctx)->ExtendedRegisters))->MxCsr +#endif + +#ifdef _M_X64 +#define MXCSR(ctx) (ctx)->MxCsr +#endif + +#if defined(_M_IA32) || define(_M_X64) + +#define X87CW(ctx) (ctx)->FloatSave.ControlWord +#define X87SW(ctx) (ctx)->FloatSave.StatusWord + +/* + * SSE traps raise these exception codes, which are defined in internal NT headers + * but not winbase.h + */ +#define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4 +#define STATUS_FLOAT_MULTIPLE_TRAPS 0xC00002B5 + +LONG __stdcall FpeHandler(PEXCEPTION_POINTERS pe) +{ + PEXCEPTION_RECORD e = (PEXCEPTION_RECORD)pe->ExceptionRecord; + CONTEXT *c = (CONTEXT*)pe->ContextRecord; + + switch (e->ExceptionCode) { + case STATUS_FLOAT_DENORMAL_OPERAND: + case STATUS_FLOAT_DIVIDE_BY_ZERO: + case STATUS_FLOAT_INEXACT_RESULT: + case STATUS_FLOAT_INVALID_OPERATION: + case STATUS_FLOAT_OVERFLOW: + case STATUS_FLOAT_STACK_CHECK: + case STATUS_FLOAT_UNDERFLOW: + case STATUS_FLOAT_MULTIPLE_FAULTS: + case STATUS_FLOAT_MULTIPLE_TRAPS: + X87CW(c) |= FPU_EXCEPTION_MASK; /* disable all FPU exceptions */ + X86SW(c) &= ~FPU_STATUS_FLAGS; /* clear all pending FPU exceptions */ +#ifdef _M_IA32 + if (c->ContextFlags & CONTEXT_EXTENDED_REGISTERS) { +#endif + MXCSR(c) |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */ + MXCSR(c) &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */ +#ifdef _M_IA32 + } +#endif + return EXCEPTION_CONTINUE_EXECUTION; + } + return EXCEPTION_CONTINUE_SEARCH; +} + +void InstallSignalHandlers(const char *ProgramName) +{ + SetUnhandledExceptionFilter(FpeHandler); +} +#else +void InstallSignalHandlers(const char *ProgramName) +{ +} +#endif + +#endif + + diff --git a/toolkit/xre/nsSigHandlers.h b/toolkit/xre/nsSigHandlers.h new file mode 100644 index 00000000000..fb2c577c067 --- /dev/null +++ b/toolkit/xre/nsSigHandlers.h @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Communicator client code. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Andreas Gal + * + * Alternatively, the contents of this file may be used under the terms of + * either of the GNU General Public License Version 2 or later (the "GPL"), + * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#if defined(_M_IA32) || defined(_M_X86) || defined(__i386__) || defined(__amd64__) + +/* + * x87 FPU Control Word: + * + * 0 -> IM Invalid Operation + * 1 -> DM Denormalized Operand + * 2 -> ZM Zero Divide + * 3 -> OM Overflow + * 4 -> UM Underflow + * 5 -> PM Precision + */ +#define FPU_EXCEPTION_MASK 0x3f + +/* + * x86 FPU Status Word: + * + * 0..5 -> Exception flags (see x86 FPU Control Word) + * 6 -> SF Stack Fault + * 7 -> ES Error Summary Status + */ +#define FPU_STATUS_FLAGS 0xff + +/* + * MXCSR Control and Status Register: + * + * 0..5 -> Exception flags (see x86 FPU Control Word) + * 6 -> DAZ Denormals Are Zero + * 7..12 -> Exception mask (see x86 FPU Control Word) + */ +#define SSE_STATUS_FLAGS FPU_EXCEPTION_MASK +#define SSE_EXCEPTION_MASK (FPU_EXCEPTION_MASK << 7) + +#endif From 24877c4a9b79d93b63d64fda124a60198b4b7ba4 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Thu, 17 Dec 2009 00:29:32 -0800 Subject: [PATCH 78/82] Backed out try-landing of bug 533035. --- toolkit/xre/Makefile.in | 5 +- toolkit/xre/nsAppRunner.cpp | 8 +- toolkit/xre/nsSigHandlers.cpp | 160 +--------------------------------- 3 files changed, 13 insertions(+), 160 deletions(-) diff --git a/toolkit/xre/Makefile.in b/toolkit/xre/Makefile.in index 0968e83bd2e..d0d8e5588c0 100644 --- a/toolkit/xre/Makefile.in +++ b/toolkit/xre/Makefile.in @@ -67,7 +67,6 @@ CPPSRCS = \ nsXREDirProvider.cpp \ nsNativeAppSupportBase.cpp \ nsAppData.cpp \ - nsSigHandlers.cpp \ $(NULL) ifdef MOZ_SPLASHSCREEN @@ -121,6 +120,10 @@ ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa) CMMSRCS += MacApplicationDelegate.mm endif +ifneq (,$(filter-out OS2 WINNT,$(OS_ARCH))) +CPPSRCS += nsSigHandlers.cpp +endif + SHARED_LIBRARY_LIBS += ../profile/src/$(LIB_PREFIX)profile_s.$(LIB_SUFFIX) ifdef MOZ_ENABLE_XREMOTE diff --git a/toolkit/xre/nsAppRunner.cpp b/toolkit/xre/nsAppRunner.cpp index efdadad2b83..f385d48f97c 100644 --- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -235,7 +235,9 @@ protected: }; #endif -extern void InstallSignalHandlers(const char *ProgramName); +#if defined(XP_UNIX) || defined(XP_BEOS) + extern void InstallUnixSignalHandlers(const char *ProgramName); +#endif #define FILE_COMPATIBILITY_INFO NS_LITERAL_CSTRING("compatibility.ini") @@ -2649,7 +2651,9 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData) #endif #endif - InstallSignalHandlers(argv[0]); +#if defined(XP_UNIX) || defined(XP_BEOS) + InstallUnixSignalHandlers(argv[0]); +#endif #ifdef MOZ_ACCESSIBILITY_ATK // Reset GTK_MODULES, strip atk-bridge if exists diff --git a/toolkit/xre/nsSigHandlers.cpp b/toolkit/xre/nsSigHandlers.cpp index 8c7bfb2c202..05386a185b5 100644 --- a/toolkit/xre/nsSigHandlers.cpp +++ b/toolkit/xre/nsSigHandlers.cpp @@ -42,10 +42,6 @@ * platforms that do not support it. */ -#include "nsSigHandlers.h" - -#if !defined(XP_WIN) && !defined(XP_OS2) - #include #include #include @@ -59,7 +55,6 @@ #include #include #include // atoi -#include #endif #if defined(SOLARIS) @@ -209,53 +204,7 @@ my_glib_log_func(const gchar *log_domain, GLogLevelFlags log_level, #endif -static void fpehandler(int signum, siginfo_t *si, void *context) -{ -#ifdef XP_MACOSX - ucontext_t *uc = (ucontext_t *)context; - -#if defined(__i386__) || defined(__amd64__) - _STRUCT_FP_CONTROL *ctrl = &uc->uc_mcontext->__fs.__fpu_fcw; - ctrl->__invalid = ctrl->__denorm = ctrl->__zdiv = ctrl->__ovrfl = ctrl->__undfl = ctrl->__precis = 1; - - _STRUCT_FP_STATUS *status = &uc->uc_mcontext->__fs.__fpu_fsw; - status->__invalid = status->__denorm = status->__zdiv = status->__ovrfl = status->__undfl = - status->__precis = status->__stkflt = status->__errsumm = 0; - - __uint32_t *mxcsr = &uc->uc_mcontext->__fs.__fpu_mxcsr; - *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */ - *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */ -#endif -#endif -#ifdef LINUX - ucontext_t *uc = (ucontext_t *)context; - -#if defined(__i386__) - /* - * It seems that we have no access to mxcsr on Linux. libc - * seems to be translating cw/sw to mxcsr. - */ - unsigned long int *cw = &uc->uc_mcontext.fpregs->cw; - *cw |= FPU_EXCEPTION_MASK; - - unsigned long int *sw = &uc->uc_mcontext.fpregs->sw; - *sw &= ~FPU_STATUS_FLAGS; -#endif -#if defined(__amd64__) - uint16_t *cw = &uc->uc_mcontext.fpregs->cwd; - *cwd |= FPU_EXCEPTION_MASK; - - uint16_t *sw = &uc->uc_mcontext.fpregs->swd; - *swd &= ~FPU_STATUS_FLAGS; - - __uint32_t *mxcsr = &uc->uc_mcontext->fpregs->mxcsr; - *mxcsr |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */ - *mxcsr &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */ -#endif -#endif -} - -void InstallSignalHandlers(const char *ProgramName) +void InstallUnixSignalHandlers(const char *ProgramName) { PL_strncpy(_progname,ProgramName, (sizeof(_progname)-1) ); @@ -277,19 +226,15 @@ void InstallSignalHandlers(const char *ProgramName) signal(SIGSEGV, abnormal_exit_handler); signal(SIGILL, abnormal_exit_handler); signal(SIGABRT, abnormal_exit_handler); + signal(SIGFPE, abnormal_exit_handler); #elif defined(CRAWL_STACK_ON_SIGSEGV) signal(SIGSEGV, ah_crap_handler); signal(SIGILL, ah_crap_handler); signal(SIGABRT, ah_crap_handler); + signal(SIGFPE, ah_crap_handler); #endif // CRAWL_STACK_ON_SIGSEGV - /* Install a handler for floating point exceptions and disable them if they occur. */ - struct sigaction sa, osa; - sa.sa_flags = SA_ONSTACK | SA_RESTART | SA_SIGINFO; - sa.sa_sigaction = fpehandler; - sigaction(SIGFPE, &sa, &osa); - #if defined(DEBUG) && defined(LINUX) const char *memLimit = PR_GetEnv("MOZ_MEM_LIMIT"); if (memLimit && *memLimit) @@ -344,102 +289,3 @@ void InstallSignalHandlers(const char *ProgramName) } #endif } - -#else - -#include - -#ifdef _M_IX86 - -#if WINVER < 0x0601 -typedef struct DECLSPEC_ALIGN(16) _M128A { - ULONGLONG Low; - LONGLONG High; -} M128A, *PM128A; -#endif - -/* - * The ExtendedRegisters field of the x86.32 CONTEXT structure uses this layout; however, - * this structure is only made available from winnt.h on x86.64 - */ -typedef struct _XMM_SAVE_AREA32 { - WORD ControlWord; /* 000 */ - WORD StatusWord; /* 002 */ - BYTE TagWord; /* 004 */ - BYTE Reserved1; /* 005 */ - WORD ErrorOpcode; /* 006 */ - DWORD ErrorOffset; /* 008 */ - WORD ErrorSelector; /* 00c */ - WORD Reserved2; /* 00e */ - DWORD DataOffset; /* 010 */ - WORD DataSelector; /* 014 */ - WORD Reserved3; /* 016 */ - DWORD MxCsr; /* 018 */ - DWORD MxCsr_Mask; /* 01c */ - M128A FloatRegisters[8]; /* 020 */ - M128A XmmRegisters[16]; /* 0a0 */ - BYTE Reserved4[96]; /* 1a0 */ -} XMM_SAVE_AREA32, *PXMM_SAVE_AREA32; - -#define MXCSR(ctx) ((XMM_SAVE_AREA32*)((ctx)->ExtendedRegisters))->MxCsr -#endif - -#ifdef _M_X64 -#define MXCSR(ctx) (ctx)->MxCsr -#endif - -#if defined(_M_IA32) || define(_M_X64) - -#define X87CW(ctx) (ctx)->FloatSave.ControlWord -#define X87SW(ctx) (ctx)->FloatSave.StatusWord - -/* - * SSE traps raise these exception codes, which are defined in internal NT headers - * but not winbase.h - */ -#define STATUS_FLOAT_MULTIPLE_FAULTS 0xC00002B4 -#define STATUS_FLOAT_MULTIPLE_TRAPS 0xC00002B5 - -LONG __stdcall FpeHandler(PEXCEPTION_POINTERS pe) -{ - PEXCEPTION_RECORD e = (PEXCEPTION_RECORD)pe->ExceptionRecord; - CONTEXT *c = (CONTEXT*)pe->ContextRecord; - - switch (e->ExceptionCode) { - case STATUS_FLOAT_DENORMAL_OPERAND: - case STATUS_FLOAT_DIVIDE_BY_ZERO: - case STATUS_FLOAT_INEXACT_RESULT: - case STATUS_FLOAT_INVALID_OPERATION: - case STATUS_FLOAT_OVERFLOW: - case STATUS_FLOAT_STACK_CHECK: - case STATUS_FLOAT_UNDERFLOW: - case STATUS_FLOAT_MULTIPLE_FAULTS: - case STATUS_FLOAT_MULTIPLE_TRAPS: - X87CW(c) |= FPU_EXCEPTION_MASK; /* disable all FPU exceptions */ - X86SW(c) &= ~FPU_STATUS_FLAGS; /* clear all pending FPU exceptions */ -#ifdef _M_IA32 - if (c->ContextFlags & CONTEXT_EXTENDED_REGISTERS) { -#endif - MXCSR(c) |= SSE_EXCEPTION_MASK; /* disable all SSE exceptions */ - MXCSR(c) &= ~SSE_STATUS_FLAGS; /* clear all pending SSE exceptions */ -#ifdef _M_IA32 - } -#endif - return EXCEPTION_CONTINUE_EXECUTION; - } - return EXCEPTION_CONTINUE_SEARCH; -} - -void InstallSignalHandlers(const char *ProgramName) -{ - SetUnhandledExceptionFilter(FpeHandler); -} -#else -void InstallSignalHandlers(const char *ProgramName) -{ -} -#endif - -#endif - - From 8df1e64371928fdfd35a22d5bc72c48416d6d6b8 Mon Sep 17 00:00:00 2001 From: Andreas Gal Date: Thu, 17 Dec 2009 00:38:04 -0800 Subject: [PATCH 79/82] Remove leftover bits from 533035. --- toolkit/xre/nsSigHandlers.h | 72 ------------------------------------- 1 file changed, 72 deletions(-) delete mode 100644 toolkit/xre/nsSigHandlers.h diff --git a/toolkit/xre/nsSigHandlers.h b/toolkit/xre/nsSigHandlers.h deleted file mode 100644 index fb2c577c067..00000000000 --- a/toolkit/xre/nsSigHandlers.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is Mozilla Communicator client code. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1998 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Andreas Gal - * - * Alternatively, the contents of this file may be used under the terms of - * either of the GNU General Public License Version 2 or later (the "GPL"), - * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#if defined(_M_IA32) || defined(_M_X86) || defined(__i386__) || defined(__amd64__) - -/* - * x87 FPU Control Word: - * - * 0 -> IM Invalid Operation - * 1 -> DM Denormalized Operand - * 2 -> ZM Zero Divide - * 3 -> OM Overflow - * 4 -> UM Underflow - * 5 -> PM Precision - */ -#define FPU_EXCEPTION_MASK 0x3f - -/* - * x86 FPU Status Word: - * - * 0..5 -> Exception flags (see x86 FPU Control Word) - * 6 -> SF Stack Fault - * 7 -> ES Error Summary Status - */ -#define FPU_STATUS_FLAGS 0xff - -/* - * MXCSR Control and Status Register: - * - * 0..5 -> Exception flags (see x86 FPU Control Word) - * 6 -> DAZ Denormals Are Zero - * 7..12 -> Exception mask (see x86 FPU Control Word) - */ -#define SSE_STATUS_FLAGS FPU_EXCEPTION_MASK -#define SSE_EXCEPTION_MASK (FPU_EXCEPTION_MASK << 7) - -#endif From 51bdff8212d131d613cd7881929b88b5f88ef52d Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Thu, 17 Dec 2009 11:30:14 -0600 Subject: [PATCH 80/82] Fix memory leak in a jsapi-test. No bug, r=Waldo. --HG-- extra : rebase_source : c9cdf4fc2391f812a3decb645900ac1ec4ffa118 --- js/src/jsapi-tests/testXDR.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/js/src/jsapi-tests/testXDR.cpp b/js/src/jsapi-tests/testXDR.cpp index bd0dc840793..4989ee30217 100644 --- a/js/src/jsapi-tests/testXDR.cpp +++ b/js/src/jsapi-tests/testXDR.cpp @@ -119,6 +119,7 @@ BEGIN_TEST(testXDR_bug525481) JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE); JS_XDRMemSetData(r, frozen, nbytes); CHECK(JS_XDRScript(r, &script)); + JS_DestroyScript(cx, script); JS_XDRDestroy(r); // this frees `frozen` return true; } From 102ed4de5a7b0ef16c32b928a726c367049bd7d8 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Thu, 17 Dec 2009 11:37:25 -0600 Subject: [PATCH 81/82] Do not secretly write to /tmp in DEBUG builds unless your name is brendan. (Leave the basic stats code ifdef'd on for everyone though, to help avoid bitrot.) No bug, r=Waldo. --- js/src/jscntxt.h | 2 +- js/src/jsutil.h | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index fec909bebfe..4f1915e368d 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -317,7 +317,7 @@ typedef struct InterpStruct InterpStruct; # define JS_ON_TRACE(cx) JS_FALSE #endif -#ifdef DEBUG +#ifdef DEBUG_brendan # define JS_EVAL_CACHE_METERING 1 # define JS_FUNCTION_METERING 1 #endif diff --git a/js/src/jsutil.h b/js/src/jsutil.h index 4c7eb167bae..56029438cc7 100644 --- a/js/src/jsutil.h +++ b/js/src/jsutil.h @@ -110,11 +110,10 @@ extern JS_PUBLIC_API(void) JS_Abort(void); #ifdef DEBUG # define JS_BASIC_STATS 1 -# define JS_SCOPE_DEPTH_METER 1 #endif -#if defined DEBUG && !defined JS_BASIC_STATS -# define JS_BASIC_STATS 1 +#ifdef DEBUG_brendan +# define JS_SCOPE_DEPTH_METER 1 #endif #ifdef JS_BASIC_STATS From 8b449748c73693350d403517d9f9c14ac32bc455 Mon Sep 17 00:00:00 2001 From: Jason Orendorff Date: Thu, 17 Dec 2009 14:24:39 -0600 Subject: [PATCH 82/82] Fix "bug NNNNNN" in a comment to point to the actual bugzilla bug. r=nobody. --- js/src/jsobj.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 6832f8d3e0a..bb15cc765a6 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -3562,8 +3562,8 @@ js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto, } /* - * If one adds props, deletes earlier props, adds more, the last added won't - * recycle the deleted props' slots. FIXME: bug NNNNNN + * FIXME bug 535629: If one adds props, deletes earlier props, adds more, the + * last added won't recycle the deleted props' slots. */ JSBool js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)