зеркало из 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 \
|
||||
ARMAssembler.cpp \
|
||||
MacroAssemblerARM.cpp \
|
||||
$(NULL)
|
||||
# Compiler.cpp \
|
||||
CodeGenerator.cpp \
|
||||
PICStubCompiler.cpp \
|
||||
MethodJIT.cpp \
|
||||
Stubs.cpp \
|
||||
BytecodeAnalyzer.cpp \
|
||||
Logging.cpp
|
||||
Logging.cpp \
|
||||
Stubs.cpp \
|
||||
Compiler.cpp \
|
||||
$(NULL)
|
||||
# CodeGenerator.cpp \
|
||||
# PICStubCompiler.cpp \
|
||||
|
||||
ifeq (86, $(findstring 86,$(TARGET_CPU)))
|
||||
ifeq (x86_64, $(TARGET_CPU))
|
||||
|
|
|
@ -802,6 +802,8 @@ JS_StringToVersion(const char *string);
|
|||
leaving that up to the
|
||||
embedding. */
|
||||
|
||||
#define JSOPTION_METHODJIT JS_BIT(14) /* Whole-method JIT. */
|
||||
|
||||
extern JS_PUBLIC_API(uint32)
|
||||
JS_GetOptions(JSContext *cx);
|
||||
|
||||
|
|
|
@ -523,6 +523,9 @@ JSThreadData::init()
|
|||
return false;
|
||||
#ifdef JS_TRACER
|
||||
InitJIT(&traceMonitor);
|
||||
#endif
|
||||
#ifdef JS_METHODJIT
|
||||
jmData.Initialize();
|
||||
#endif
|
||||
dtoaState = js_NewDtoaState();
|
||||
if (!dtoaState) {
|
||||
|
@ -550,6 +553,9 @@ JSThreadData::finish()
|
|||
propertyCache.~PropertyCache();
|
||||
#if defined JS_TRACER
|
||||
FinishJIT(&traceMonitor);
|
||||
#endif
|
||||
#if defined JS_METHODJIT
|
||||
jmData.Finish();
|
||||
#endif
|
||||
stackSpace.finish();
|
||||
}
|
||||
|
|
|
@ -113,6 +113,10 @@ template<typename T> class Seq;
|
|||
|
||||
} /* namespace nanojit */
|
||||
|
||||
namespace JSC {
|
||||
class ExecutableAllocator;
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
/* Tracer constants. */
|
||||
|
@ -209,6 +213,23 @@ struct 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
|
||||
* code depends on the fact that the globals begin |MAX_NATIVE_STACK_SLOTS|
|
||||
|
@ -1006,6 +1027,10 @@ struct JSThreadData {
|
|||
js::TraceMonitor traceMonitor;
|
||||
#endif
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
js::mjit::ThreadData jmData;
|
||||
#endif
|
||||
|
||||
/* Lock-free hashed lists of scripts created by eval to garbage-collect. */
|
||||
JSScript *scriptsToGC[JS_EVAL_CACHE_SIZE];
|
||||
|
||||
|
@ -1562,6 +1587,7 @@ struct JSRuntime {
|
|||
#define JS_GSN_CACHE(cx) (JS_THREAD_DATA(cx)->gsnCache)
|
||||
#define JS_PROPERTY_CACHE(cx) (JS_THREAD_DATA(cx)->propertyCache)
|
||||
#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)
|
||||
|
||||
#ifdef JS_EVAL_CACHE_METERING
|
||||
|
@ -1722,7 +1748,7 @@ struct JSContext
|
|||
JS_REQUIRES_STACK
|
||||
JSFrameRegs *regs;
|
||||
|
||||
private:
|
||||
public:
|
||||
friend class js::StackSpace;
|
||||
friend bool js::Interpret(JSContext *);
|
||||
|
||||
|
@ -1735,7 +1761,6 @@ struct JSContext
|
|||
this->regs = regs;
|
||||
}
|
||||
|
||||
public:
|
||||
/* Temporary arena pool used while compiling and decompiling. */
|
||||
JSArenaPool tempPool;
|
||||
|
||||
|
|
|
@ -50,8 +50,8 @@
|
|||
#include "jsscript.h"
|
||||
|
||||
typedef struct JSFrameRegs {
|
||||
jsbytecode *pc; /* program counter */
|
||||
js::Value *sp; /* stack pointer */
|
||||
jsbytecode *pc; /* program counter */
|
||||
} JSFrameRegs;
|
||||
|
||||
/* JS stack frame flags. */
|
||||
|
@ -103,6 +103,12 @@ struct JSStackFrame
|
|||
static jsbytecode *const sInvalidPC;
|
||||
#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
|
||||
* 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 -*-
|
||||
* 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 *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
|
@ -113,6 +113,17 @@ struct GlobalSlotArray {
|
|||
# define CHECK_SCRIPT_OWNER 1
|
||||
#endif
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
namespace JSC {
|
||||
class ExecutablePool;
|
||||
}
|
||||
namespace js {
|
||||
namespace mjit {
|
||||
struct PICInfo;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
struct JSScript {
|
||||
jsbytecode *code; /* bytecodes and their immediate operands */
|
||||
uint32 length; /* length of code vector */
|
||||
|
@ -150,6 +161,28 @@ struct JSScript {
|
|||
#ifdef CHECK_SCRIPT_OWNER
|
||||
JSThread *owner; /* for thread-safe life-cycle assertions */
|
||||
#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. */
|
||||
jssrcnote *notes() { return (jssrcnote *)(code + length); }
|
||||
|
@ -223,6 +256,17 @@ struct JSScript {
|
|||
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:
|
||||
/*
|
||||
* 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 volatile bool gCanceled = false;
|
||||
|
||||
static bool enableJit = false;
|
||||
static bool enableTraceJit = false;
|
||||
static bool enableMethodJit = false;
|
||||
|
||||
static JSBool
|
||||
SetTimeoutValue(JSContext *cx, jsdouble t);
|
||||
|
@ -600,7 +601,8 @@ static const struct {
|
|||
} js_options[] = {
|
||||
{"anonfunfix", JSOPTION_ANONFUNFIX},
|
||||
{"atline", JSOPTION_ATLINE},
|
||||
{"jit", JSOPTION_JIT},
|
||||
{"tracejit", JSOPTION_JIT},
|
||||
{"methodjit", JSOPTION_METHODJIT},
|
||||
{"relimit", JSOPTION_RELIMIT},
|
||||
{"strict", JSOPTION_STRICT},
|
||||
{"werror", JSOPTION_WERROR},
|
||||
|
@ -752,7 +754,7 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
|
|||
break;
|
||||
|
||||
case 'j':
|
||||
enableJit = !enableJit;
|
||||
enableTraceJit = !enableTraceJit;
|
||||
JS_ToggleOptions(cx, JSOPTION_JIT);
|
||||
#if defined(JS_TRACER) && defined(DEBUG)
|
||||
js::InitJITStatsClass(cx, JS_GetGlobalObject(cx));
|
||||
|
@ -761,6 +763,11 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
|
|||
#endif
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
enableMethodJit = !enableMethodJit;
|
||||
JS_ToggleOptions(cx, JSOPTION_METHODJIT);
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
{
|
||||
if (++i == argc)
|
||||
|
@ -4904,8 +4911,10 @@ NewContext(JSRuntime *rt)
|
|||
JS_SetErrorReporter(cx, my_ErrorReporter);
|
||||
JS_SetVersion(cx, JSVERSION_LATEST);
|
||||
SetContextOptions(cx);
|
||||
if (enableJit)
|
||||
if (enableTraceJit)
|
||||
JS_ToggleOptions(cx, JSOPTION_JIT);
|
||||
if (enableMethodJit)
|
||||
JS_ToggleOptions(cx, JSOPTION_METHODJIT);
|
||||
return cx;
|
||||
}
|
||||
|
||||
|
|
|
@ -111,7 +111,11 @@ def get_test_cmd(path, lib_dir):
|
|||
if not libdir_var.endswith('/'):
|
||||
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 ]
|
||||
|
||||
def run_test(test, lib_dir):
|
||||
|
@ -268,6 +272,8 @@ if __name__ == '__main__':
|
|||
help='Run test files listed in [FILE]')
|
||||
op.add_option('-R', '--retest', dest='retest', metavar='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',
|
||||
help='Run test in gdb')
|
||||
op.add_option('--valgrind', dest='valgrind', action='store_true',
|
||||
|
|
Загрузка…
Ссылка в новой задаче