Import method JIT 'outer' framework.

This commit is contained in:
David Anderson 2010-05-22 17:09:52 -07:00
Родитель d2e09dddb2
Коммит 044faf8261
16 изменённых файлов: 2007 добавлений и 15 удалений

Просмотреть файл

@ -296,14 +296,14 @@ CPPSRCS += Assertions.cpp \
ExecutableAllocator.cpp \ ExecutableAllocator.cpp \
ARMAssembler.cpp \ ARMAssembler.cpp \
MacroAssemblerARM.cpp \ MacroAssemblerARM.cpp \
$(NULL)
# Compiler.cpp \
CodeGenerator.cpp \
PICStubCompiler.cpp \
MethodJIT.cpp \ MethodJIT.cpp \
Stubs.cpp \
BytecodeAnalyzer.cpp \ BytecodeAnalyzer.cpp \
Logging.cpp Logging.cpp \
Stubs.cpp \
Compiler.cpp \
$(NULL)
# CodeGenerator.cpp \
# PICStubCompiler.cpp \
ifeq (86, $(findstring 86,$(TARGET_CPU))) ifeq (86, $(findstring 86,$(TARGET_CPU)))
ifeq (x86_64, $(TARGET_CPU)) ifeq (x86_64, $(TARGET_CPU))

Просмотреть файл

@ -802,6 +802,8 @@ JS_StringToVersion(const char *string);
leaving that up to the leaving that up to the
embedding. */ embedding. */
#define JSOPTION_METHODJIT JS_BIT(14) /* Whole-method JIT. */
extern JS_PUBLIC_API(uint32) extern JS_PUBLIC_API(uint32)
JS_GetOptions(JSContext *cx); JS_GetOptions(JSContext *cx);

Просмотреть файл

@ -523,6 +523,9 @@ JSThreadData::init()
return false; return false;
#ifdef JS_TRACER #ifdef JS_TRACER
InitJIT(&traceMonitor); InitJIT(&traceMonitor);
#endif
#ifdef JS_METHODJIT
jmData.Initialize();
#endif #endif
dtoaState = js_NewDtoaState(); dtoaState = js_NewDtoaState();
if (!dtoaState) { if (!dtoaState) {
@ -550,6 +553,9 @@ JSThreadData::finish()
propertyCache.~PropertyCache(); propertyCache.~PropertyCache();
#if defined JS_TRACER #if defined JS_TRACER
FinishJIT(&traceMonitor); FinishJIT(&traceMonitor);
#endif
#if defined JS_METHODJIT
jmData.Finish();
#endif #endif
stackSpace.finish(); stackSpace.finish();
} }

Просмотреть файл

@ -113,6 +113,10 @@ template<typename T> class Seq;
} /* namespace nanojit */ } /* namespace nanojit */
namespace JSC {
class ExecutableAllocator;
}
namespace js { namespace js {
/* Tracer constants. */ /* Tracer constants. */
@ -209,6 +213,23 @@ struct TracerState
~TracerState(); ~TracerState();
}; };
namespace mjit {
struct ThreadData
{
JSC::ExecutableAllocator *execPool;
// Scripts that have had PICs patched or PIC stubs generated.
typedef js::HashSet<JSScript*, DefaultHasher<JSScript*>, js::SystemAllocPolicy> ScriptSet;
ScriptSet picScripts;
bool Initialize();
void Finish();
bool addScript(JSScript *script);
void removeScript(JSScript *script);
};
}
/* /*
* Storage for the execution state and store during trace execution. Generated * Storage for the execution state and store during trace execution. Generated
* code depends on the fact that the globals begin |MAX_NATIVE_STACK_SLOTS| * code depends on the fact that the globals begin |MAX_NATIVE_STACK_SLOTS|
@ -1006,6 +1027,10 @@ struct JSThreadData {
js::TraceMonitor traceMonitor; js::TraceMonitor traceMonitor;
#endif #endif
#ifdef JS_METHODJIT
js::mjit::ThreadData jmData;
#endif
/* Lock-free hashed lists of scripts created by eval to garbage-collect. */ /* Lock-free hashed lists of scripts created by eval to garbage-collect. */
JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE]; JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE];
@ -1562,6 +1587,7 @@ struct JSRuntime {
#define JS_GSN_CACHE(cx) (JS_THREAD_DATA(cx)->gsnCache) #define JS_GSN_CACHE(cx) (JS_THREAD_DATA(cx)->gsnCache)
#define JS_PROPERTY_CACHE(cx) (JS_THREAD_DATA(cx)->propertyCache) #define JS_PROPERTY_CACHE(cx) (JS_THREAD_DATA(cx)->propertyCache)
#define JS_TRACE_MONITOR(cx) (JS_THREAD_DATA(cx)->traceMonitor) #define JS_TRACE_MONITOR(cx) (JS_THREAD_DATA(cx)->traceMonitor)
#define JS_METHODJIT_DATA(cx) (JS_THREAD_DATA(cx)->jmData)
#define JS_SCRIPTS_TO_GC(cx) (JS_THREAD_DATA(cx)->scriptsToGC) #define JS_SCRIPTS_TO_GC(cx) (JS_THREAD_DATA(cx)->scriptsToGC)
#ifdef JS_EVAL_CACHE_METERING #ifdef JS_EVAL_CACHE_METERING
@ -1722,7 +1748,7 @@ struct JSContext
JS_REQUIRES_STACK JS_REQUIRES_STACK
JSFrameRegs *regs; JSFrameRegs *regs;
private: public:
friend class js::StackSpace; friend class js::StackSpace;
friend bool js::Interpret(JSContext *); friend bool js::Interpret(JSContext *);
@ -1735,7 +1761,6 @@ struct JSContext
this->regs = regs; this->regs = regs;
} }
public:
/* Temporary arena pool used while compiling and decompiling. */ /* Temporary arena pool used while compiling and decompiling. */
JSArenaPool tempPool; JSArenaPool tempPool;

Просмотреть файл

@ -50,8 +50,8 @@
#include "jsscript.h" #include "jsscript.h"
typedef struct JSFrameRegs { typedef struct JSFrameRegs {
jsbytecode *pc; /* program counter */
js::Value *sp; /* stack pointer */ js::Value *sp; /* stack pointer */
jsbytecode *pc; /* program counter */
} JSFrameRegs; } JSFrameRegs;
/* JS stack frame flags. */ /* JS stack frame flags. */
@ -103,6 +103,12 @@ struct JSStackFrame
static jsbytecode *const sInvalidPC; static jsbytecode *const sInvalidPC;
#endif #endif
#if defined(JS_CPU_X86) || defined(JS_CPU_ARM)
void *ncode; /* jit return pc */
/* Guh. Align. */
void *align_[3];
#endif
/* /*
* We can't determine in advance which local variables can live on * We can't determine in advance which local variables can live on
* the stack and be freed when their dynamic scope ends, and which * the stack and be freed when their dynamic scope ends, and which

Просмотреть файл

@ -1,5 +1,5 @@
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=79 ft=cpp: * vim: set ts=4 sw=4 et tw=79 ft=cpp:
* *
* ***** BEGIN LICENSE BLOCK ***** * ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
@ -113,6 +113,17 @@ struct GlobalSlotArray {
# define CHECK_SCRIPT_OWNER 1 # define CHECK_SCRIPT_OWNER 1
#endif #endif
#ifdef JS_METHODJIT
namespace JSC {
class ExecutablePool;
}
namespace js {
namespace mjit {
struct PICInfo;
}
}
#endif
struct JSScript { struct JSScript {
jsbytecode *code; /* bytecodes and their immediate operands */ jsbytecode *code; /* bytecodes and their immediate operands */
uint32 length; /* length of code vector */ uint32 length; /* length of code vector */
@ -150,6 +161,28 @@ struct JSScript {
#ifdef CHECK_SCRIPT_OWNER #ifdef CHECK_SCRIPT_OWNER
JSThread *owner; /* for thread-safe life-cycle assertions */ JSThread *owner; /* for thread-safe life-cycle assertions */
#endif #endif
#ifdef JS_METHODJIT
// Note: the other pointers in this group may be non-NULL only if
// |execPool| is non-NULL.
void *ncode; /* native code compiled by the method JIT */
void **nmap; /* maps PCs to native code */
JSC::ExecutablePool *execPool; /* pool that contains |ncode|; script owns the pool */
unsigned npics; /* Number of PICs in the array |pics| */
js::mjit::PICInfo *pics; /* PICs in this script */
# ifdef DEBUG
size_t jitLength; /* length of JIT'd code */
inline bool isValidJitCode(void *jcode) {
return (char*)jcode >= (char*)ncode &&
(char*)jcode < (char*)ncode + jitLength;
}
# endif
#endif
#ifdef JS_TRACER
js::TraceTreeCache *trees; /* trace tree info. */
uint32 tmGen; /* generation number from the TraceMonitor */
#endif
uint32 tracePoints; /* number of trace points in the script */
/* Script notes are allocated right after the code. */ /* Script notes are allocated right after the code. */
jssrcnote *notes() { return (jssrcnote *)(code + length); } jssrcnote *notes() { return (jssrcnote *)(code + length); }
@ -223,6 +256,17 @@ struct JSScript {
return const_cast<JSScript *>(&emptyScriptConst); return const_cast<JSScript *>(&emptyScriptConst);
} }
#ifdef JS_METHODJIT
/*
* Map the given PC to the corresponding native code address.
*/
void *pcToNative(jsbytecode *pc) {
JS_ASSERT(nmap);
JS_ASSERT(nmap[pc - code]);
return nmap[pc - code];
}
#endif
private: private:
/* /*
* Use const to put this in read-only memory if possible. We are stuck with * Use const to put this in read-only memory if possible. We are stuck with

Просмотреть файл

@ -0,0 +1,340 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* ***** 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 SpiderMonkey JavaScript 1.9 code, released
* May 28, 2008.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>
*
* Contributor(s):
* David Anderson <danderson@mozilla.com>
*
* 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 ***** */
#include "BytecodeAnalyzer.h"
#include "jsautooplen.h"
#include "jsemit.h"
using namespace js;
BytecodeAnalyzer::~BytecodeAnalyzer()
{
cx->free(ops);
}
bool
BytecodeAnalyzer::addEdge(jsbytecode *pc, int32 offset, uint32 stackDepth)
{
uint32 idx = (uint32)((pc + offset) - script->code);
JS_ASSERT_IF(ops[idx].visited || ops[idx].nincoming,
ops[idx].stackDepth == stackDepth);
if (!ops[idx].visited && !doList.append(pc + offset))
return false;
ops[idx].stackDepth = stackDepth;
ops[idx].nincoming++;
return true;
}
bool
BytecodeAnalyzer::analyze(uint32 index)
{
jsbytecode *pc = doList[index];
uint32 stackDepth = ops[pc - script->code].stackDepth;
#ifdef DEBUG
bool canAssert = true;
#endif
for (;;) {
JSOp op = JSOp(pc[0]);
OpcodeStatus &status = ops[pc - script->code];
if (status.visited)
return true;
#ifdef DEBUG
if (assertDepths) {
jssrcnote *sn = js_GetSrcNote(script, pc);
if (sn && SN_TYPE(sn) == SRC_HIDDEN)
canAssert = false;
JS_ASSERT_IF(canAssert && pc > script->main,
js_ReconstructStackDepth(cx, script, pc) == stackDepth);
}
#endif
status.visited = true;
status.stackDepth = stackDepth;
uint32 nuses, ndefs;
if (js_CodeSpec[op].nuses == -1)
nuses = js_GetVariableStackUses(op, pc);
else
nuses = js_CodeSpec[op].nuses;
if (js_CodeSpec[op].ndefs == -1)
ndefs = js_GetEnterBlockStackDefs(cx, script, pc);
else
ndefs = js_CodeSpec[op].ndefs;
JS_ASSERT(nuses <= stackDepth);
stackDepth -= nuses;
stackDepth += ndefs;
uint32 offs;
jsbytecode *newpc;
switch (op) {
case JSOP_TRAP:
return false;
case JSOP_DEFAULT:
case JSOP_GOTO:
offs = (pc + JSOP_GOTO_LENGTH) - script->code;
if (!ops[offs].visited && ops[offs].nincoming && !doList.append(pc + JSOP_GOTO_LENGTH))
return false;
pc += GET_JUMP_OFFSET(pc);
ops[pc - script->code].nincoming++;
continue;
case JSOP_DEFAULTX:
case JSOP_GOTOX:
offs = (pc + JSOP_GOTOX_LENGTH) - script->code;
if (!ops[offs].visited && ops[offs].nincoming && !doList.append(pc + JSOP_GOTOX_LENGTH))
return false;
pc += GET_JUMPX_OFFSET(pc);
ops[pc - script->code].nincoming++;
continue;
case JSOP_IFEQ:
case JSOP_IFNE:
if (!addEdge(pc, GET_JUMP_OFFSET(pc), stackDepth))
return false;
break;
case JSOP_OR:
case JSOP_AND:
/* If the jump is taken, the condition is pushed. */
if (!addEdge(pc, GET_JUMP_OFFSET(pc), stackDepth + 1))
return false;
break;
case JSOP_IFEQX:
case JSOP_IFNEX:
case JSOP_ORX:
case JSOP_ANDX:
if (!addEdge(pc, GET_JUMPX_OFFSET(pc), stackDepth))
return false;
break;
case JSOP_CASE:
/* If the jump is taken, the extra value is not pushed. */
if (!addEdge(pc, GET_JUMP_OFFSET(pc), stackDepth - 1))
return false;
break;
case JSOP_CASEX:
/* If the jump is taken, the extra value is not pushed. */
if (!addEdge(pc, GET_JUMPX_OFFSET(pc), stackDepth - 1))
return false;
break;
case JSOP_GOSUB:
case JSOP_GOSUBX:
case JSOP_IFPRIMTOP:
case JSOP_FILTER:
case JSOP_ENDFILTER:
case JSOP_TABLESWITCHX:
case JSOP_LOOKUPSWITCHX:
return false;
case JSOP_TABLESWITCH:
{
jsint def = GET_JUMP_OFFSET(pc);
if (!addEdge(pc, def, stackDepth))
return false;
newpc = pc + JUMP_OFFSET_LEN;
jsint low = GET_JUMP_OFFSET(newpc);
newpc += JUMP_OFFSET_LEN;
jsint high = GET_JUMP_OFFSET(newpc);
newpc += JUMP_OFFSET_LEN;
uint32 ncases = (uint32)(high - low + 1);
for (uint32 i = 0; i < ncases; i++) {
jsint offs = GET_JUMP_OFFSET(newpc);
newpc += JUMP_OFFSET_LEN;
if (!offs)
offs = def;
if (!addEdge(pc, offs, stackDepth))
return false;
}
pc = newpc + 1;
break;
}
case JSOP_LOOKUPSWITCH:
{
if (!addEdge(pc, GET_JUMP_OFFSET(pc), stackDepth))
return false;
newpc = pc + JUMP_OFFSET_LEN;
uint32 npairs = GET_UINT16(newpc);
newpc += UINT16_LEN;
JS_ASSERT(npairs);
for (uint32 i = 0; i < npairs; i++) {
newpc += INDEX_LEN ;
if (!addEdge(pc, GET_JUMP_OFFSET(newpc), stackDepth))
return false;
newpc += JUMP_OFFSET_LEN;
}
pc = newpc + 1;
break;
}
case JSOP_RETRVAL:
case JSOP_RETURN:
{
/*
* If there is nothing incoming, just leave.
* This is to defeat the emitter doing things like:
* leaveblock 1
* retrval
* leaveblock 1
* (see testNullCallee in trace-tests)
*/
JS_ASSERT(js_CodeSpec[op].length == 1);
uint32 offs = (pc + 1) - script->code;
if (ops[offs].visited || !ops[offs].nincoming)
return true;
/* Otherwise, restore the stack depth and continue. */
stackDepth = ops[offs].stackDepth;
break;
}
case JSOP_THROW:
/* Control flow stops here. */
return true;
case JSOP_STOP:
JS_ASSERT(uint32(pc - script->code) + 1 == script->length);
return true;
default:
#ifdef DEBUG
uint32 type = JOF_TYPE(js_CodeSpec[op].format);
JS_ASSERT(type != JOF_JUMP && type != JOF_JUMPX);
#endif
break;
}
if (js_CodeSpec[op].length != -1)
pc += js_CodeSpec[op].length;
}
}
bool
BytecodeAnalyzer::analyze()
{
ops = (OpcodeStatus *)cx->malloc(sizeof(OpcodeStatus) * script->length);
if (!ops)
return false;
memset(ops, 0, sizeof(OpcodeStatus) * script->length);
if (!doList.append(script->code))
return false;
if (script->trynotesOffset) {
JSTryNoteArray *tnarray = script->trynotes();
for (unsigned i = 0; i < tnarray->length; ++i) {
JSTryNote &tn = tnarray->vector[i];
if (tn.kind == JSTRY_ITER)
continue;
unsigned pcoff = script->main + tn.start + tn.length - script->code;
ops[pcoff].exceptionEntry = true;
ops[pcoff].nincoming = 1;
ops[pcoff].stackDepth = tn.stackDepth;
if (!doList.append(script->code + pcoff))
return false;
}
}
for (size_t i = 0; i < doList.length(); i++) {
if (ops[doList[i] - script->code].visited)
continue;
if (!analyze(i))
return false;
}
#ifdef DEBUG
if (!assertDepths)
return true;
/*
* In debug mode, do an extra pass to make sure each opcode was visited
* and has the correct stack depth.
*/
jsbytecode *pc = script->code;
bool canAssert = true;
for (;;) {
jssrcnote *sn = js_GetSrcNote(script, pc);
if (sn && SN_TYPE(sn) == SRC_HIDDEN)
canAssert = false;
/*
* See comment in JSOP_RETURN/RETRVAL case. Don't assert that
* unreachable code was visited.
*/
JS_ASSERT_IF(!ops[pc - script->code].visited,
!ops[pc - script->code].nincoming);
JS_ASSERT_IF(pc > script->main && canAssert && ops[pc - script->code].visited,
(ops[pc - script->code].stackDepth ==
js_ReconstructStackDepth(cx, script, pc)));
if (js_CodeSpec[JSOp(*pc)].length != -1)
pc += js_CodeSpec[JSOp(*pc)].length;
else
pc += js_GetVariableBytecodeLength(pc);
if (pc[0] == JSOP_STOP)
break;
}
#endif
return true;
}

Просмотреть файл

@ -0,0 +1,123 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* ***** 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 SpiderMonkey JavaScript 1.9 code, released
* May 28, 2008.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>
*
* Contributor(s):
* David Anderson <danderson@mozilla.com>
*
* 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 jsjaeger_bytecodeAnalyzer_h__ && defined JS_METHODJIT
#define jsjaeger_bytecodeAnalyzer_h__
#include "jsapi.h"
#include "jscntxt.h"
#include "jsscript.h"
#include "jsopcode.h"
namespace js
{
struct OpcodeStatus
{
bool visited; /* flag for CFG traversal */
bool exceptionEntry; /* true iff this is a catch/finally entry point */
bool safePoint; /* false by default */
uint32 nincoming; /* number of CFG inedges here */
uint32 stackDepth; /* stack depth before this opcode */
};
class BytecodeAnalyzer
{
JSContext *cx;
JSScript *script;
OpcodeStatus *ops;
Vector<jsbytecode *, 16, ContextAllocPolicy> doList;
#ifdef DEBUG
bool assertDepths;
#endif
public:
BytecodeAnalyzer(JSContext *cx, JSScript *script)
: cx(cx), script(script), ops(NULL),
doList(ContextAllocPolicy(cx))
{
#ifdef DEBUG
// This takes a very long time with SunSpider's string-tagcloud.
if (script->filename) {
const char *filename = script->filename;
size_t flen = strlen(filename);
const char *pat = "string-tagcloud.js";
size_t plen = strlen(pat);
bool endswith = (plen < flen &&
memcmp(filename + flen - plen, pat, plen) == 0);
assertDepths = !endswith;
}
#endif
}
~BytecodeAnalyzer();
bool analyze(uint32 offs);
bool addEdge(jsbytecode *pc, int32 offset, uint32 stackDepth);
public:
inline const OpcodeStatus & operator [](uint32 offs) const
{
JS_ASSERT(offs < script->length);
return ops[offs];
}
inline OpcodeStatus & operator [](uint32 offs)
{
JS_ASSERT(offs < script->length);
return ops[offs];
}
inline const OpcodeStatus & operator [](jsbytecode *pc) const
{
JS_ASSERT(pc < script->code + script->length);
return ops[pc - script->code];
}
inline OpcodeStatus & operator [](jsbytecode *pc)
{
JS_ASSERT(pc < script->code + script->length);
return ops[pc - script->code];
}
bool analyze();
};
}
#endif /* jsjaeger_bytecodeAnalyzer_h__ */

Просмотреть файл

@ -0,0 +1,140 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* ***** 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 SpiderMonkey JavaScript 1.9 code, released
* May 28, 2008.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>
*
* Contributor(s):
* David Anderson <danderson@mozilla.com>
* Julian Seward <jseward@acm.org>
*
* 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 ***** */
#include <stdarg.h>
#include <string.h>
#include "MethodJIT.h"
#include "Logging.h"
#if defined(JS_METHODJIT_SPEW)
static bool LoggingChecked = false;
static uint32 LoggingBits = 0;
static const char *ChannelNames[] =
{
#define _(name) #name,
JSPEW_CHAN_MAP(_)
#undef _
};
void
js::JMCheckLogging()
{
/* Not MT safe; races on Logging{Checked,Bits}. */
if (LoggingChecked)
return;
LoggingChecked = true;
const char *env = getenv("JMFLAGS");
if (!env)
return;
if (strstr(env, "help")) {
fflush(NULL);
printf(
"\n"
"usage: JMFLAGS=option,option,option,... where options can be:\n"
"\n"
" help show this message\n"
" abort/aborts ???\n"
" scripts ???\n"
" profile ???\n"
#ifdef DEBUG
" jsops JS opcodes\n"
#endif
" insns JS opcodes and generated insns\n"
" vmframe VMFrame contents\n"
" pics PIC patching activity\n"
" slowcalls Calls to slow path functions\n"
" full everything\n"
" notrace disable trace hints\n"
"\n"
);
exit(0);
/*NOTREACHED*/
}
if (strstr(env, "abort") || strstr(env, "aborts"))
LoggingBits |= (1 << uint32(JSpew_Abort));
if (strstr(env, "scripts"))
LoggingBits |= (1 << uint32(JSpew_Scripts));
if (strstr(env, "profile"))
LoggingBits |= (1 << uint32(JSpew_Prof));
#ifdef DEBUG
if (strstr(env, "jsops"))
LoggingBits |= (1 << uint32(JSpew_JSOps));
#endif
if (strstr(env, "insns"))
LoggingBits |= (1 << uint32(JSpew_Insns) | (1 << uint32(JSpew_JSOps)));
if (strstr(env, "vmframe"))
LoggingBits |= (1 << uint32(JSpew_VMFrame));
if (strstr(env, "pics"))
LoggingBits |= (1 << uint32(JSpew_PICs));
if (strstr(env, "slowcalls"))
LoggingBits |= (1 << uint32(JSpew_SlowCalls));
if (strstr(env, "full"))
LoggingBits |= 0xFFFFFFFF;
}
bool
js::IsJaegerSpewChannelActive(JaegerSpewChannel channel)
{
JS_ASSERT(LoggingChecked);
return !!(LoggingBits & (1 << uint32(channel)));
}
void
js::JaegerSpew(JaegerSpewChannel channel, const char *fmt, ...)
{
JS_ASSERT(LoggingChecked);
if (!(LoggingBits & (1 << uint32(channel))))
return;
fprintf(stdout, "[jaeger] %-7s ", ChannelNames[channel]);
va_list ap;
va_start(ap, fmt);
vfprintf(stdout, fmt, ap);
va_end(ap);
/* fprintf(stdout, "\n"); */
}
#endif

112
js/src/methodjit/Logging.h Normal file
Просмотреть файл

@ -0,0 +1,112 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* ***** 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 SpiderMonkey JavaScript 1.9 code, released
* May 28, 2008.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>
*
* Contributor(s):
* David Anderson <danderson@mozilla.com>
* Julian Seward <jseward@acm.org>
*
* 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 jsjaeger_logging_h__ && defined JS_METHODJIT
#define jsjaeger_logging_h__
#include "prmjtime.h"
namespace js {
#define JSPEW_CHAN_MAP(_) \
_(Abort) \
_(Scripts) \
_(Prof) \
_(JSOps) \
_(Insns) \
_(VMFrame) \
_(PICs) \
_(SlowCalls)
enum JaegerSpewChannel {
#define _(name) JSpew_##name,
JSPEW_CHAN_MAP(_)
#undef _
JSpew_Terminator
};
#if defined(DEBUG) && !defined(JS_METHODJIT_SPEW)
# define JS_METHODJIT_SPEW
#endif
#if defined(JS_METHODJIT_SPEW)
void JMCheckLogging();
bool IsJaegerSpewChannelActive(JaegerSpewChannel channel);
void JaegerSpew(JaegerSpewChannel channel, const char *fmt, ...);
struct Profiler {
JSInt64 t_start;
JSInt64 t_stop;
static inline JSInt64 now() {
return PRMJ_Now();
}
inline void start() {
t_start = now();
}
inline void stop() {
t_stop = now();
}
inline uint32 time_ms() {
return uint32((t_stop - t_start) / PRMJ_USEC_PER_MSEC);
}
inline uint32 time_us() {
return uint32(t_stop - t_start);
}
};
#else
static inline void JaegerSpew(JaegerSpewChannel channel, const char *fmt, ...)
{
}
#endif
}
#endif

Просмотреть файл

@ -0,0 +1,618 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* ***** 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 SpiderMonkey JavaScript 1.9 code, released
* May 28, 2008.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>
*
* Contributor(s):
*
* 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 ***** */
#include "MethodJIT.h"
#include "Logging.h"
#include "assembler/jit/ExecutableAllocator.h"
#include "jstracer.h"
using namespace js;
using namespace js::mjit;
extern "C" void JS_FASTCALL
SetVMFrameRegs(VMFrame &f)
{
f.oldRegs = f.cx->regs;
f.cx->setCurrentRegs(&f.regs);
}
extern "C" void JS_FASTCALL
UnsetVMFrameRegs(VMFrame &f)
{
*f.oldRegs = f.regs;
f.cx->setCurrentRegs(f.oldRegs);
}
#if defined(__APPLE__) || defined(XP_WIN)
# define SYMBOL_STRING(name) "_" #name
#else
# define SYMBOL_STRING(name) #name
#endif
JS_STATIC_ASSERT(offsetof(JSFrameRegs, sp) == 0);
#if defined(__linux__) && defined(JS_CPU_X64)
# define SYMBOL_STRING_RELOC(name) #name "@plt"
#else
# define SYMBOL_STRING_RELOC(name) SYMBOL_STRING(name)
#endif
#if defined(XP_MACOSX)
# define HIDE_SYMBOL(name) ".private_extern _" #name
#elif defined(__linux__)
# define HIDE_SYMBOL(name) ".hidden" #name
#else
# define HIDE_SYMBOL(name)
#endif
#if defined(__GNUC__)
/* If this assert fails, you need to realign VMFrame to 16 bytes. */
#ifdef JS_CPU_ARM
JS_STATIC_ASSERT(sizeof(VMFrame) % 8 == 0);
#else
JS_STATIC_ASSERT(sizeof(VMFrame) % 16 == 0);
#endif
# if defined(JS_CPU_X64)
/*
* *** DANGER ***
* If these assertions break, update the constants below.
* *** DANGER ***
*/
JS_STATIC_ASSERT(offsetof(VMFrame, savedRBX) == 0x48);
JS_STATIC_ASSERT(offsetof(VMFrame, fp) == 0x30);
asm volatile (
".text\n"
".globl " SYMBOL_STRING(JaegerTrampoline) "\n"
SYMBOL_STRING(JaegerTrampoline) ":" "\n"
/* Prologue. */
"pushq %rbp" "\n"
"movq %rsp, %rbp" "\n"
/* Save non-volatile registers. */
"pushq %r12" "\n"
"pushq %r13" "\n"
"pushq %r14" "\n"
"pushq %r15" "\n"
"pushq %rbx" "\n"
/* Build the JIT frame.
* rdi = cx
* rsi = fp
* rcx = inlineCallCount
* fp must go into rbx
*/
"pushq %rcx" "\n"
"pushq %rdi" "\n"
"pushq %rsi" "\n"
"movq %rsi, %rbx" "\n"
/* Space for the rest of the VMFrame. */
"subq $0x28, %rsp" "\n"
/* Set cx->regs (requires saving rdx). */
"pushq %rdx" "\n"
"movq %rsp, %rdi" "\n"
"call " SYMBOL_STRING_RELOC(SetVMFrameRegs) "\n"
"popq %rdx" "\n"
/*
* Jump into into the JIT'd code. The call implicitly fills in
* the precious f.scriptedReturn member of VMFrame.
*/
"call *%rdx" "\n"
"leaq -8(%rsp), %rdi" "\n"
"call " SYMBOL_STRING_RELOC(UnsetVMFrameRegs) "\n"
"addq $0x40, %rsp" "\n"
"popq %rbx" "\n"
"popq %r15" "\n"
"popq %r14" "\n"
"popq %r13" "\n"
"popq %r12" "\n"
"popq %rbp" "\n"
"movq $1, %rax" "\n"
"ret" "\n"
);
asm volatile (
".text\n"
".globl " SYMBOL_STRING(JaegerThrowpoline) "\n"
SYMBOL_STRING(JaegerThrowpoline) ":" "\n"
"movq %rsp, %rdi" "\n"
"call " SYMBOL_STRING_RELOC(js_InternalThrow) "\n"
"testq %rax, %rax" "\n"
"je throwpoline_exit" "\n"
"jmp *%rax" "\n"
"throwpoline_exit:" "\n"
"addq $0x48, %rsp" "\n"
"popq %rbx" "\n"
"popq %r15" "\n"
"popq %r14" "\n"
"popq %r13" "\n"
"popq %r12" "\n"
"popq %rbp" "\n"
"ret" "\n"
);
asm volatile (
".text\n"
".globl " SYMBOL_STRING(JaegerFromTracer) "\n"
SYMBOL_STRING(JaegerFromTracer) ":" "\n"
/* Restore fp reg. */
"movq 0x30(%rsp), %rbx" "\n"
"jmp *%rax" "\n"
);
# elif defined(JS_CPU_X86)
/*
* *** DANGER ***
* If these assertions break, update the constants below. The throwpoline
* should have the offset of savedEBX plus 4, because it needs to clean
* up the argument.
* *** DANGER ***
*/
JS_STATIC_ASSERT(offsetof(VMFrame, savedEBX) == 0x2c);
JS_STATIC_ASSERT(offsetof(VMFrame, fp) == 0x20);
asm volatile (
".text\n"
".globl " SYMBOL_STRING(JaegerTrampoline) "\n"
SYMBOL_STRING(JaegerTrampoline) ":" "\n"
/* Prologue. */
"pushl %ebp" "\n"
"movl %esp, %ebp" "\n"
/* Save non-volatile registers. */
"pushl %esi" "\n"
"pushl %edi" "\n"
"pushl %ebx" "\n"
/* Build the JIT frame. Push fields in order,
* then align the stack to form esp == VMFrame. */
"pushl 20(%ebp)" "\n"
"pushl 8(%ebp)" "\n"
"pushl 12(%ebp)" "\n"
"movl 12(%ebp), %ebx" "\n"
"subl $0x1c, %esp" "\n"
/* Jump into the JIT'd code. */
"pushl 16(%ebp)" "\n"
"movl %esp, %ecx" "\n"
"call " SYMBOL_STRING_RELOC(SetVMFrameRegs) "\n"
"popl %edx" "\n"
"call *%edx" "\n"
"leal -4(%esp), %ecx" "\n"
"call " SYMBOL_STRING_RELOC(UnsetVMFrameRegs) "\n"
"addl $0x28, %esp" "\n"
"popl %ebx" "\n"
"popl %edi" "\n"
"popl %esi" "\n"
"popl %ebp" "\n"
"movl $1, %eax" "\n"
"ret" "\n"
);
asm volatile (
".text\n"
".globl " SYMBOL_STRING(JaegerThrowpoline) "\n"
SYMBOL_STRING(JaegerThrowpoline) ":" "\n"
/* Align the stack to 16 bytes. */
"pushl %esp" "\n"
"pushl (%esp)" "\n"
"pushl (%esp)" "\n"
"pushl (%esp)" "\n"
"call " SYMBOL_STRING_RELOC(js_InternalThrow) "\n"
/* Bump the stack by 0x2c, as in the basic trampoline, but
* also one more word to clean up the stack for js_InternalThrow,
* and another to balance the alignment above. */
"addl $0x10, %esp" "\n"
"testl %eax, %eax" "\n"
"je throwpoline_exit" "\n"
"jmp *%eax" "\n"
"throwpoline_exit:" "\n"
"addl $0x2c, %esp" "\n"
"popl %ebx" "\n"
"popl %edi" "\n"
"popl %esi" "\n"
"popl %ebp" "\n"
"xorl %eax, %eax" "\n"
"ret" "\n"
);
asm volatile (
".text\n"
".globl " SYMBOL_STRING(JaegerFromTracer) "\n"
SYMBOL_STRING(JaegerFromTracer) ":" "\n"
/* Restore frame regs. */
"movl 0x20(%esp), %ebx" "\n"
"jmp *%eax" "\n"
);
# elif defined(JS_CPU_ARM)
JS_STATIC_ASSERT(offsetof(VMFrame, savedLR) == 76);
JS_STATIC_ASSERT(offsetof(VMFrame, fp) == 32);
asm volatile (
".text\n"
".globl " SYMBOL_STRING(JaegerFromTracer) "\n"
SYMBOL_STRING(JaegerFromTracer) ":" "\n"
/* Restore frame regs. */
"ldr r11, [sp, #32]" "\n"
"bx r0" "\n"
);
asm volatile (
".text\n"
".globl " SYMBOL_STRING(JaegerTrampoline) "\n"
SYMBOL_STRING(JaegerTrampoline) ":" "\n"
/* The trampoline for ARM looks like this:
* [ lr ] \
* [ r11 ] |
* [ r10 ] |
* [ r9 ] | Callee-saved registers.
* [ r8 ] | VFP registers d8-d15 may be required here too, but
* [ r7 ] | unconditionally preserving them might be expensive
* [ r6 ] | considering that we might not use them anyway.
* [ r5 ] |
* [ r4 ] /
* [ regs ] \
* [ ICallCnt ] | Parameters for the compiled code (and subsequently-called routines).
* [ cx ] |
* [ fp ] |
* [ sp ] /
* [ args ]
* [ ... ]
* [ padding ]
* [ exc. ret ] <- sp
*/
/* We push these in short groups (rather than using one big push operation) because it
* supposedly benefits Cortex-A9. TODO: Check that this is actually a benefit. */
" push {r10,r11,lr}" "\n"
" push {r7-r9}" "\n"
" push {r4-r6}" "\n"
" mov r11, #0" "\n" /* r11 = inlineCallCount */
" push {r11}" "\n" /* inlineCallCount */
" push {r0}" "\n" /* cx */
" push {r1}" "\n" /* fp */
" mov r11, r1" "\n" /* FpReg */
/* Leave space for the VMFrame arguments. The largest slot appears to be 8 bytes for 32-bit
* architectures, though hard-coding this doesn't seem sensible. TODO: Use sizeof here and for
* the other targets. */
" sub sp, sp, #(8*4)" "\n"
" mov r0, sp" "\n"
" push {r2}" "\n"
" blx " SYMBOL_STRING_RELOC(SetVMFrameRegs) "\n"
" pop {r2}" "\n"
/* Call the compiled JavaScript function using r2 ('code'). */
" bl " SYMBOL_STRING_RELOC(JaegerTrampVeneer) "\n"
/* --------
* Tidy up: Unwind the stack pointer, restore the callee-saved registers and then return.
*/
" mov r0, sp" "\n"
" blx " SYMBOL_STRING_RELOC(UnsetVMFrameRegs) "\n"
/* Skip past the parameters we pushed (such as cx and the like). */
" add sp, sp, #(11*4)" "\n"
/* Set a 'true' return value to indicate successful completion. */
" mov r0, #1" "\n"
/* We pop these in short groups (rather than using one big push operation) because it
* supposedly benefits Cortex-A9. TODO: Check that this is actually a benefit. */
" pop {r4-r6}" "\n"
" pop {r7-r9}" "\n"
" pop {r10,r11,pc}" "\n" /* Pop lr directly into the pc to return quickly. */
);
asm volatile (
".text\n"
".globl " SYMBOL_STRING(JaegerThrowpoline) "\n"
SYMBOL_STRING(JaegerThrowpoline) ":" "\n"
/* Restore 'f', as it will have been clobbered. */
" mov r0, sp" "\n"
/* Call the utility function that sets up the internal throw routine. */
" blx " SYMBOL_STRING_RELOC(js_InternalThrow) "\n"
/* If 0 was returned, just bail out as normal. Otherwise, we have a 'catch' or 'finally' clause
* to execute. */
" cmp r0, #0" "\n"
" bxne r0" "\n"
/* Skip past the parameters we pushed (such as cx and the like). */
" add sp, sp, #(11*4)" "\n"
/* We pop these in short groups (rather than using one big push operation) because it
* supposedly benefits Cortex-A9. TODO: Check that this is actually a benefit. */
" pop {r4-r6}" "\n"
" pop {r7-r9}" "\n"
" pop {r10,r11,pc}" "\n" /* Pop lr directly into the pc to return quickly. */
);
asm volatile (
".text\n"
".globl " SYMBOL_STRING(JaegerStubVeneer) "\n"
SYMBOL_STRING(JaegerStubVeneer) ":" "\n"
/* We enter this function as a veneer between a compiled method and one of the js_ stubs. We
* need to store the LR somewhere (so it can be modified in case on an exception) and then
* branch to the js_ stub as if nothing had happened.
* The arguments are identical to those for js_* except that the target function should be in
* 'ip'. TODO: This is not ABI-compliant, though it is convenient for now. I should work out
* which register to use to do this properly; r1 is likely because r0 gets VMFrame &f. */
" str lr, [sp]" "\n" /* VMFrame->veneerReturn */
" blx ip" "\n"
" ldr pc, [sp]" "\n" /* This should stack-predict, but only if 'sp' is used. */
);
asm volatile (
".text\n"
".globl " SYMBOL_STRING(JaegerTrampVeneer) "\n"
SYMBOL_STRING(JaegerTrampVeneer) ":" "\n"
/* This is needed to store the initial scriptedReturn value, which won't
* get stored when invoking JaegerShot() into the middle of methods.
*/
" str lr, [sp, #4]" "\n" /* VMFrame->scriptedReturn */
" bx r2" "\n"
);
# else
# error "Unsupported CPU!"
# endif
#elif defined(_MSC_VER)
#if defined(JS_CPU_X86)
/*
* *** DANGER ***
* If these assertions break, update the constants below. The throwpoline
* should have the offset of savedEBX plus 4, because it needs to clean
* up the argument.
* *** DANGER ***
*/
JS_STATIC_ASSERT(offsetof(VMFrame, savedEBX) == 0x2c);
JS_STATIC_ASSERT(offsetof(VMFrame, fp) == 0x20);
extern "C" {
__declspec(naked) void JaegerFromTracer()
{
__asm {
mov ebx, [esp + 0x20];
jmp eax;
}
}
__declspec(naked) JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code,
uintptr_t inlineCallCount)
{
__asm {
/* Prologue. */
push ebp;
mov ebp, esp;
/* Save non-volatile registers. */
push esi;
push edi;
push ebx;
/* Build the JIT frame. Push fields in order,
* then align the stack to form esp == VMFrame. */
push [ebp+20];
push [ebp+8];
push [ebp+12];
mov ebx, [ebp+12];
sub esp, 0x1c;
/* Jump into into the JIT'd code. */
push [ebp+16];
mov ecx, esp;
call SetVMFrameRegs;
pop edx;
call edx;
lea ecx, [esp-4];
call UnsetVMFrameRegs;
add esp, 0x28
pop ebx;
pop edi;
pop esi;
pop ebp;
mov eax, 1;
ret;
}
}
extern "C" void *js_InternalThrow(js::VMFrame &f);
__declspec(naked) void *JaegerThrowpoline(js::VMFrame *vmFrame) {
__asm {
/* Align the stack to 16 bytes. */
push esp;
push [esp];
push [esp];
push [esp];
call js_InternalThrow;
/* Bump the stack by 0x2c, as in the basic trampoline, but
* also one more word to clean up the stack for js_InternalThrow,
* and another to balance the alignment above. */
add esp, 0x10;
test eax, eax;
je throwpoline_exit;
jmp eax;
throwpoline_exit:
add esp, 0x2c;
pop ebx;
pop edi;
pop esi;
pop ebp;
xor eax, eax
ret;
}
}
}
#elif defined(JS_CPU_X64)
/*
* *** DANGER ***
* If these assertions break, update the constants below.
* *** DANGER ***
*/
JS_STATIC_ASSERT(offsetof(VMFrame, savedRBX) == 0x48);
JS_STATIC_ASSERT(offsetof(VMFrame, fp) == 0x30);
// Windows x64 uses assembler version since compiler doesn't support
// inline assembler
#else
# error "Unsupported CPU!"
#endif
#endif /* _MSC_VER */
bool
ThreadData::Initialize()
{
execPool = new JSC::ExecutableAllocator();
if (!execPool)
return false;
if (!picScripts.init()) {
delete execPool;
return false;
}
return true;
}
void
ThreadData::Finish()
{
delete execPool;
}
bool
ThreadData::addScript(JSScript *script)
{
ScriptSet::AddPtr p = picScripts.lookupForAdd(script);
if (p)
return true;
return picScripts.add(p, script);
}
void
ThreadData::removeScript(JSScript *script)
{
ScriptSet::Ptr p = picScripts.lookup(script);
if (p)
picScripts.remove(p);
}
extern "C" JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code,
uintptr_t inlineCallCount);
JSBool
mjit::JaegerShot(JSContext *cx)
{
JS_ASSERT(cx->regs);
JS_CHECK_RECURSION(cx, return JS_FALSE;);
void *code;
jsbytecode *pc = cx->regs->pc;
JSStackFrame *fp = cx->fp;
JSScript *script = fp->script;
uintptr_t inlineCallCount = 0;
JS_ASSERT(script->ncode && script->ncode != JS_UNJITTABLE_METHOD);
#ifdef JS_TRACER
if (TRACE_RECORDER(cx))
AbortRecording(cx, "attempt to enter method JIT while recording");
#endif
if (pc == script->code)
code = script->ncode;
else
code = script->nmap[pc - script->code];
JS_ASSERT(code);
#ifdef JS_METHODJIT_SPEW
Profiler prof;
JaegerSpew(JSpew_Prof, "entering jaeger script: %s, line %d\n", fp->script->filename,
fp->script->lineno);
prof.start();
#endif
#ifdef DEBUG
JSStackFrame *checkFp = fp;
#endif
#if 0
uintptr_t iCC = inlineCallCount;
while (iCC--)
checkFp = checkFp->down;
#endif
JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
JSBool ok = JaegerTrampoline(cx, fp, code, inlineCallCount);
JS_ASSERT(checkFp == cx->fp);
#ifdef JS_METHODJIT_SPEW
prof.stop();
JaegerSpew(JSpew_Prof, "script run took %d ms\n", prof.time_ms());
#endif
return ok;
}

Просмотреть файл

@ -0,0 +1,231 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* ***** 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 SpiderMonkey JavaScript 1.9 code, released
* May 28, 2008.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>
*
* Contributor(s):
*
* 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 jsjaeger_h__ && defined JS_METHODJIT
#define jsjaeger_h__
#include "jscntxt.h"
#include "assembler/assembler/MacroAssemblerCodeRef.h"
#if !defined JS_CPU_X64 && \
!defined JS_CPU_X86 && \
!defined JS_CPU_ARM
# error "Oh no, you should define a platform so this compiles."
#endif
namespace js {
struct VMFrame
{
#if defined(JS_CPU_ARM)
JSC::ReturnAddressPtr veneerReturn;
#endif
/* This must be the first entry on CPUs which push return addresses. */
void *scriptedReturn;
#if defined(JS_CPU_X86)
uintptr_t padding[2];
#elif defined(JS_CPU_ARM)
uintptr_t padding;
#endif
union Arguments {
struct BINDNAME {
uint32 index;
JSObject *obj;
} bindname;
struct DEFVAR {
jsatomid index;
uint32 op;
} defvar;
struct LAMBDA {
uint32 index;
JSOp op;
} lambda;
struct INITPROP {
uint32 index;
unsigned defineHow;
} initprop;
struct DEFLOCALFUN {
uint32 slot;
uint32 index;
} deflocalfun;
struct TRACER {
uint32 traceId;
uint32 offs;
} tracer;
struct SETTER {
jsatomid index;
JSOp op;
} setter;
} u;
JSFrameRegs *oldRegs;
JSFrameRegs regs;
JSStackFrame *fp;
JSContext *cx;
uintptr_t inlineCallCount;
#if defined(JS_CPU_X86)
void *savedEBX;
void *savedEDI;
void *savedESI;
void *savedEBP;
void *savedEIP;
inline void setReturnAddress(JSC::ReturnAddressPtr addr) {
*(reinterpret_cast<JSC::ReturnAddressPtr*>(this)-1) = addr;
}
inline JSC::ReturnAddressPtr getReturnAddress() const {
return *(reinterpret_cast<const JSC::ReturnAddressPtr*>(this)-1);
}
#elif defined(JS_CPU_X64)
void *savedRBX;
#ifdef _MSC_VER
void *savedRSI;
void *savedRDI;
#endif
void *savedR15;
void *savedR14;
void *savedR13;
void *savedR12;
void *savedRBP;
void *savedRIP;
#ifdef _MSC_VER
inline void setReturnAddress(JSC::ReturnAddressPtr addr) {
*(reinterpret_cast<JSC::ReturnAddressPtr*>(this)-5) = addr;
}
inline JSC::ReturnAddressPtr getReturnAddress() const {
return *(reinterpret_cast<const JSC::ReturnAddressPtr*>(this)-5);
}
#else
inline void setReturnAddress(JSC::ReturnAddressPtr addr) {
*(reinterpret_cast<JSC::ReturnAddressPtr*>(this)-1) = addr;
}
inline JSC::ReturnAddressPtr getReturnAddress() const {
return *(reinterpret_cast<const JSC::ReturnAddressPtr*>(this)-1);
}
#endif
#elif defined(JS_CPU_ARM)
void *savedR4;
void *savedR5;
void *savedR6;
void *savedR7;
void *savedR8;
void *savedR9;
void *savedR10;
void *savedR11;
void *savedLR;
inline void setReturnAddress(JSC::ReturnAddressPtr addr) {
this->veneerReturn = addr;
}
inline JSC::ReturnAddressPtr getReturnAddress() {
return this->veneerReturn;
}
#else
# error "The VMFrame layout isn't defined for your processor architecture!"
#endif
JSRuntime *runtime() { return cx->runtime; }
};
#ifdef JS_CPU_ARM
extern "C" void JaegerStubVeneer(void);
#endif
typedef void (JS_FASTCALL *VoidStub)(VMFrame &);
typedef void (JS_FASTCALL *VoidVpStub)(VMFrame &, jsval *);
typedef void (JS_FASTCALL *VoidStubUInt32)(VMFrame &, uint32);
typedef void (JS_FASTCALL *VoidStubInt32)(VMFrame &, int32);
typedef JSBool (JS_FASTCALL *BoolStub)(VMFrame &);
typedef void * (JS_FASTCALL *VoidPtrStub)(VMFrame &);
typedef void * (JS_FASTCALL *VoidPtrStubPC)(VMFrame &, jsbytecode *);
typedef void * (JS_FASTCALL *VoidPtrStubUInt32)(VMFrame &, uint32);
typedef JSObject * (JS_FASTCALL *JSObjStub)(VMFrame &);
typedef JSObject * (JS_FASTCALL *JSObjStubUInt32)(VMFrame &, uint32);
#define JS_UNJITTABLE_METHOD (reinterpret_cast<void*>(-1))
namespace mjit {
JSBool
JaegerShot(JSContext *cx);
enum CompileStatus
{
Compile_Okay,
Compile_Abort,
Compile_Error
};
CompileStatus
TryCompile(JSContext *cx, JSScript *script, JSObject *scopeChain);
static inline CompileStatus
CanMethodJIT(JSContext *cx, JSScript *script, JSObject *scopeChain)
{
if (!(cx->options & JSOPTION_METHODJIT) || script->ncode == JS_UNJITTABLE_METHOD)
return Compile_Abort;
if (script->ncode == NULL)
return TryCompile(cx, script, scopeChain);
return Compile_Okay;
}
} /* namespace mjit */
} /* namespace js */
#ifdef _MSC_VER
extern "C" void *JaegerThrowpoline(js::VMFrame *vmFrame);
#else
extern "C" void JaegerThrowpoline();
#endif
extern "C" void JaegerFromTracer();
#endif /* jsjaeger_h__ */

272
js/src/methodjit/Stubs.cpp Normal file
Просмотреть файл

@ -0,0 +1,272 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* ***** 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 SpiderMonkey JavaScript 1.9 code, released
* May 28, 2008.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>
*
* Contributor(s):
* David Anderson <danderson@mozilla.com>
* David Mandelin <dmandelin@mozilla.com>
*
* 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 ***** */
#define __STDC_LIMIT_MACROS
#include "jscntxt.h"
#include "jsscope.h"
#include "jsobj.h"
#include "jslibmath.h"
#include "jsiter.h"
#include "jsnum.h"
#include "jsxml.h"
#include "jsstaticcheck.h"
#include "jsbool.h"
#include "assembler/assembler/MacroAssemblerCodeRef.h"
#include "jsiter.h"
#include "jstypes.h"
#include "methodjit/Stubs.h"
#include "jstracer.h"
#include "jspropertycache.h"
#include "jspropertycacheinlines.h"
#include "jsscopeinlines.h"
#include "jsscriptinlines.h"
#include "jsstrinlines.h"
#include "jsobjinlines.h"
#include "jscntxtinlines.h"
#include "jsautooplen.h"
using namespace js;
using namespace js::mjit;
using namespace JSC;
#define THROW() \
do { \
void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
return; \
} while (0)
#define THROWV(v) \
do { \
void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
return v; \
} while (0)
static bool
InlineReturn(JSContext *cx)
{
bool ok = true;
JSStackFrame *fp = cx->fp;
JS_ASSERT(!fp->blockChain);
JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
if (fp->script->staticLevel < JS_DISPLAY_SIZE)
cx->display[fp->script->staticLevel] = fp->displaySave;
// Marker for debug support.
void *hookData = fp->hookData;
if (JS_UNLIKELY(hookData != NULL)) {
JSInterpreterHook hook;
JSBool status;
hook = cx->debugHooks->callHook;
if (hook) {
/*
* Do not pass &ok directly as exposing the address inhibits
* optimizations and uninitialised warnings.
*/
status = ok;
hook(cx, fp, JS_FALSE, &status, hookData);
ok = (status == JS_TRUE);
// CHECK_INTERRUPT_HANDLER();
}
}
fp->putActivationObjects(cx);
/* :TODO: version stuff */
if (fp->flags & JSFRAME_CONSTRUCTING && fp->rval.isPrimitive())
fp->rval = fp->thisv;
cx->stack().popInlineFrame(cx, fp, fp->down);
cx->regs->sp[-1] = fp->rval;
return ok;
}
void * JS_FASTCALL
mjit::stubs::Return(VMFrame &f)
{
if (!f.inlineCallCount)
return f.fp->ncode;
JSContext *cx = f.cx;
JS_ASSERT(f.fp == cx->fp);
#ifdef DEBUG
bool wasInterp = f.fp->script->ncode == JS_UNJITTABLE_METHOD;
#endif
bool ok = InlineReturn(cx);
f.inlineCallCount--;
JS_ASSERT(f.regs.sp == cx->regs->sp);
f.fp = cx->fp;
JS_ASSERT_IF(f.inlineCallCount > 1 && !wasInterp,
f.fp->down->script->isValidJitCode(f.fp->ncode));
if (!ok)
THROWV(NULL);
return f.fp->ncode;
}
static jsbytecode *
FindExceptionHandler(JSContext *cx)
{
JSStackFrame *fp = cx->fp;
JSScript *script = fp->script;
top:
if (cx->throwing && script->trynotesOffset) {
// The PC is updated before every stub call, so we can use it here.
unsigned offset = cx->regs->pc - script->main;
JSTryNoteArray *tnarray = script->trynotes();
for (unsigned i = 0; i < tnarray->length; ++i) {
JSTryNote *tn = &tnarray->vector[i];
if (offset - tn->start >= tn->length)
continue;
if (tn->stackDepth > cx->regs->sp - fp->base())
continue;
jsbytecode *pc = script->main + tn->start + tn->length;
JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE);
JS_ASSERT(cx->regs->sp == fp->base() + tn->stackDepth);
switch (tn->kind) {
case JSTRY_CATCH:
JS_ASSERT(js_GetOpcode(cx, fp->script, pc) == JSOP_ENTERBLOCK);
#if JS_HAS_GENERATORS
/* Catch cannot intercept the closing of a generator. */
if (JS_UNLIKELY(cx->exception.isMagic(JS_GENERATOR_CLOSING)))
break;
#endif
/*
* Don't clear cx->throwing to save cx->exception from GC
* until it is pushed to the stack via [exception] in the
* catch block.
*/
return pc;
case JSTRY_FINALLY:
/*
* Push (true, exception) pair for finally to indicate that
* [retsub] should rethrow the exception.
*/
cx->regs->sp[0].setBoolean(true);
cx->regs->sp[1] = cx->exception;
cx->regs->sp += 2;
cx->throwing = JS_FALSE;
return pc;
case JSTRY_ITER:
{
/*
* This is similar to JSOP_ENDITER in the interpreter loop,
* except the code now uses the stack slot normally used by
* JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2
* adjustment and regs.sp[1] after, to save and restore the
* pending exception.
*/
AutoValueRooter tvr(cx, cx->exception);
JS_ASSERT(js_GetOpcode(cx, fp->script, pc) == JSOP_ENDITER);
cx->throwing = JS_FALSE;
ok = !!js_CloseIterator(cx, cx->regs->sp[-1]);
cx->regs->sp -= 1;
if (!ok)
goto top;
cx->throwing = JS_TRUE;
cx->exception = tvr.value();
}
}
}
}
return NULL;
}
extern "C" void *
js_InternalThrow(VMFrame &f)
{
JSContext *cx = f.cx;
// Make sure sp is up to date.
JS_ASSERT(cx->regs == &f.regs);
jsbytecode *pc = NULL;
for (;;) {
pc = FindExceptionHandler(cx);
if (pc)
break;
// If |f.inlineCallCount == 0|, then we are on the 'topmost' frame (where
// topmost means the first frame called into through js_Interpret). In this
// case, we still unwind, but we shouldn't return from a JS function, because
// we're not in a JS function.
bool lastFrame = (f.inlineCallCount == 0);
js_UnwindScope(cx, 0, cx->throwing);
if (lastFrame)
break;
JS_ASSERT(f.regs.sp == cx->regs->sp);
f.scriptedReturn = stubs::Return(f);
}
JS_ASSERT(f.regs.sp == cx->regs->sp);
if (!pc) {
*f.oldRegs = f.regs;
f.cx->setCurrentRegs(f.oldRegs);
return NULL;
}
return cx->fp->script->pcToNative(pc);
}

58
js/src/methodjit/Stubs.h Normal file
Просмотреть файл

@ -0,0 +1,58 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=4 sw=4 et tw=99:
*
* ***** 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 SpiderMonkey JavaScript 1.9 code, released
* May 28, 2008.
*
* The Initial Developer of the Original Code is
* Brendan Eich <brendan@mozilla.org>
*
* Contributor(s):
* David Anderson <danderson@mozilla.com>
* David Mandelin <dmandelin@mozilla.com>
*
* 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 ***** */
#ifndef jslogic_h__
#define jslogic_h__
#include "MethodJIT.h"
namespace js {
namespace mjit {
namespace stubs {
void * JS_FASTCALL Return(VMFrame &f);
}}} /* namespace stubs,mjit,js */
extern "C" void *
js_InternalThrow(js::VMFrame &f);
#endif /* jslogic_h__ */

Просмотреть файл

@ -139,7 +139,8 @@ static jsdouble MAX_TIMEOUT_INTERVAL = 1800.0;
static jsdouble gTimeoutInterval = -1.0; static jsdouble gTimeoutInterval = -1.0;
static volatile bool gCanceled = false; static volatile bool gCanceled = false;
static bool enableJit = false; static bool enableTraceJit = false;
static bool enableMethodJit = false;
static JSBool static JSBool
SetTimeoutValue(JSContext *cx, jsdouble t); SetTimeoutValue(JSContext *cx, jsdouble t);
@ -600,7 +601,8 @@ static const struct {
} js_options[] = { } js_options[] = {
{"anonfunfix", JSOPTION_ANONFUNFIX}, {"anonfunfix", JSOPTION_ANONFUNFIX},
{"atline", JSOPTION_ATLINE}, {"atline", JSOPTION_ATLINE},
{"jit", JSOPTION_JIT}, {"tracejit", JSOPTION_JIT},
{"methodjit", JSOPTION_METHODJIT},
{"relimit", JSOPTION_RELIMIT}, {"relimit", JSOPTION_RELIMIT},
{"strict", JSOPTION_STRICT}, {"strict", JSOPTION_STRICT},
{"werror", JSOPTION_WERROR}, {"werror", JSOPTION_WERROR},
@ -752,7 +754,7 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
break; break;
case 'j': case 'j':
enableJit = !enableJit; enableTraceJit = !enableTraceJit;
JS_ToggleOptions(cx, JSOPTION_JIT); JS_ToggleOptions(cx, JSOPTION_JIT);
#if defined(JS_TRACER) && defined(DEBUG) #if defined(JS_TRACER) && defined(DEBUG)
js::InitJITStatsClass(cx, JS_GetGlobalObject(cx)); js::InitJITStatsClass(cx, JS_GetGlobalObject(cx));
@ -761,6 +763,11 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
#endif #endif
break; break;
case 'm':
enableMethodJit = !enableMethodJit;
JS_ToggleOptions(cx, JSOPTION_METHODJIT);
break;
case 'o': case 'o':
{ {
if (++i == argc) if (++i == argc)
@ -4904,8 +4911,10 @@ NewContext(JSRuntime *rt)
JS_SetErrorReporter(cx, my_ErrorReporter); JS_SetErrorReporter(cx, my_ErrorReporter);
JS_SetVersion(cx, JSVERSION_LATEST); JS_SetVersion(cx, JSVERSION_LATEST);
SetContextOptions(cx); SetContextOptions(cx);
if (enableJit) if (enableTraceJit)
JS_ToggleOptions(cx, JSOPTION_JIT); JS_ToggleOptions(cx, JSOPTION_JIT);
if (enableMethodJit)
JS_ToggleOptions(cx, JSOPTION_METHODJIT);
return cx; return cx;
} }

Просмотреть файл

@ -111,7 +111,11 @@ def get_test_cmd(path, lib_dir):
if not libdir_var.endswith('/'): if not libdir_var.endswith('/'):
libdir_var += '/' libdir_var += '/'
expr = "const platform=%r; const libdir=%r;"%(sys.platform, libdir_var) expr = "const platform=%r; const libdir=%r;"%(sys.platform, libdir_var)
return [ JS, '-j', '-e', expr, '-f', os.path.join(lib_dir, 'prolog.js'), if OPTIONS.methodjit_only:
jit_flags = [ '-m' ]
else:
jit_flags = [ '-m', '-j' ]
return [ JS ] + jit_flags + [ '-e', expr, '-f', os.path.join(lib_dir, 'prolog.js'),
'-f', path ] '-f', path ]
def run_test(test, lib_dir): def run_test(test, lib_dir):
@ -268,6 +272,8 @@ if __name__ == '__main__':
help='Run test files listed in [FILE]') help='Run test files listed in [FILE]')
op.add_option('-R', '--retest', dest='retest', metavar='FILE', op.add_option('-R', '--retest', dest='retest', metavar='FILE',
help='Retest using test list file [FILE]') help='Retest using test list file [FILE]')
op.add_option('--methodjit-only', dest='methodjit_only', action='store_true',
help='Run tests with only method compiler enabled, not tracing')
op.add_option('-g', '--debug', dest='debug', action='store_true', op.add_option('-g', '--debug', dest='debug', action='store_true',
help='Run test in gdb') help='Run test in gdb')
op.add_option('--valgrind', dest='valgrind', action='store_true', op.add_option('--valgrind', dest='valgrind', action='store_true',