зеркало из https://github.com/mozilla/pjs.git
Import method JIT 'outer' framework.
This commit is contained in:
Родитель
d2e09dddb2
Коммит
044faf8261
|
@ -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
|
||||||
|
|
|
@ -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__ */
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
|
@ -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',
|
||||||
|
|
Загрузка…
Ссылка в новой задаче