Backout merge.

This commit is contained in:
David Mandelin 2010-07-22 17:12:24 -07:00
commit b7fa67c098
34 changed files with 62 additions and 999 deletions

View File

@ -320,8 +320,6 @@ CPPSRCS += Assertions.cpp \
PolyIC.cpp \
ImmutableSync.cpp \
InvokeHelpers.cpp \
Retcon.cpp \
TrampolineCompiler.cpp \
$(NULL)
# PICStubCompiler.cpp \

View File

@ -341,9 +341,6 @@ public:
};
X86Assembler()
#ifdef DEBUG
: isOOLPath(false)
#endif
{
}

View File

@ -128,10 +128,6 @@ namespace JSC {
namespace js {
#ifdef JS_METHODJIT
struct VMFrame;
#endif
/* Tracer constants. */
static const size_t MONITOR_N_GLOBAL_STATES = 4;
static const size_t FRAGMENT_TABLE_SIZE = 512;
@ -228,12 +224,6 @@ struct TracerState
#ifdef JS_METHODJIT
namespace mjit {
struct Trampolines
{
void (* forceReturn)();
JSC::ExecutablePool *forceReturnPool;
};
struct ThreadData
{
JSC::ExecutableAllocator *execPool;
@ -242,11 +232,6 @@ namespace mjit {
typedef js::HashSet<JSScript*, DefaultHasher<JSScript*>, js::SystemAllocPolicy> ScriptSet;
ScriptSet picScripts;
// Trampolines for JIT code.
Trampolines trampolines;
VMFrame *activeFrame;
bool Initialize();
void Finish();
@ -359,7 +344,8 @@ class CallStack
: cx(NULL), previousInContext(NULL), previousInThread(NULL),
initialFrame(NULL), suspendedFrame(NULL),
suspendedRegsAndSaved(NULL, false), initialArgEnd(NULL),
initialVarObj(NULL) { }
initialVarObj(NULL)
{}
/* Safe casts guaranteed by the contiguous-stack layout. */
@ -667,6 +653,9 @@ class StackSpace
inline Value *firstUnused() const;
inline void assertIsCurrent(JSContext *cx) const;
#ifdef DEBUG
CallStack *getCurrentCallStack() const { return currentCallStack; }
#endif
/*
* Allocate nvals on the top of the stack, report error on failure.
@ -793,9 +782,6 @@ class StackSpace
/* Our privates leak into xpconnect, which needs a public symbol. */
JS_REQUIRES_STACK
JS_FRIEND_API(bool) pushInvokeArgsFriendAPI(JSContext *, uintN, InvokeArgsGuard &);
CallStack
*getCurrentCallStack() const { return currentCallStack; }
};
JS_STATIC_ASSERT(StackSpace::CAPACITY_VALS % StackSpace::COMMIT_VALS == 0);

View File

@ -69,11 +69,6 @@
#include "jsautooplen.h"
#ifdef JS_METHODJIT
# include "methodjit/MethodJIT.h"
# include "methodjit/Retcon.h"
#endif
using namespace js;
typedef struct JSTrap {
@ -195,15 +190,6 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
DBG_UNLOCK(rt);
if (junk)
cx->free(junk);
#ifdef JS_METHODJIT
if (script->ncode != NULL && script->ncode != JS_UNJITTABLE_METHOD) {
mjit::Recompiler recompiler(cx, script);
if (!recompiler.recompile())
return JS_FALSE;
}
#endif
return JS_TRUE;
}
@ -248,13 +234,6 @@ JS_ClearTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
DestroyTrapAndUnlock(cx, trap);
else
DBG_UNLOCK(cx->runtime);
#ifdef JS_METHODJIT
if (script->ncode != NULL && script->ncode != JS_UNJITTABLE_METHOD) {
mjit::Recompiler recompiler(cx, script);
recompiler.recompile();
}
#endif
}
JS_PUBLIC_API(void)

View File

@ -169,7 +169,6 @@ namespace ic {
struct MICInfo;
# endif
}
union CallSite;
}
}
#endif
@ -227,14 +226,14 @@ struct JSScript {
# if defined JS_MONOIC
js::mjit::ic::MICInfo *mics; /* MICs in this script. */
# endif
js::mjit::CallSite *callSites;
uint32 inlineLength; /* length of inline JIT'd code */
uint32 outOfLineLength; /* length of out of line JIT'd code */
# ifdef DEBUG
uint32 jitLength; /* length of JIT'd code */
inline bool isValidJitCode(void *jcode) {
return (char*)jcode >= (char*)nmap[-1] &&
(char*)jcode < (char*)nmap[-1] + inlineLength + outOfLineLength;
return (char*)jcode >= (char*)ncode &&
(char*)jcode < (char*)ncode + jitLength;
}
# endif
# if defined JS_POLYIC
inline uint32 numPICs() {

View File

@ -186,7 +186,6 @@ class BaseAssembler : public JSC::MacroAssembler
STUB_CALL_TYPE(JSObjStub);
STUB_CALL_TYPE(VoidPtrStubUInt32);
STUB_CALL_TYPE(VoidStubUInt32);
STUB_CALL_TYPE(VoidStub);
#undef STUB_CALL_TYPE

View File

@ -40,7 +40,6 @@
#include "BytecodeAnalyzer.h"
#include "jsautooplen.h"
#include "jsemit.h"
#include "Retcon.h"
using namespace js;
@ -69,7 +68,6 @@ BytecodeAnalyzer::addEdge(jsbytecode *pc, int32 offset, uint32 stackDepth)
bool
BytecodeAnalyzer::analyze(uint32 index)
{
mjit::AutoScriptRetrapper trapper(cx, script);
jsbytecode *pc = doList[index];
uint32 stackDepth = ops[pc - script->code].stackDepth;
@ -83,13 +81,6 @@ BytecodeAnalyzer::analyze(uint32 index)
status.visited = true;
status.stackDepth = stackDepth;
if (op == JSOP_TRAP) {
status.trap = true;
if (!trapper.untrap(pc))
return false;
op = JSOp(pc[0]);
}
uint32 nuses, ndefs;
if (js_CodeSpec[op].nuses == -1)
nuses = js_GetVariableStackUses(op, pc);

View File

@ -52,7 +52,6 @@ namespace js
bool visited; /* flag for CFG traversal */
bool exceptionEntry; /* true iff this is a catch/finally entry point */
bool safePoint; /* false by default */
bool trap; /* It's a trap! */
bool inTryBlock; /* true if in try block */
uint32 nincoming; /* number of CFG inedges here */
uint32 stackDepth; /* stack depth before this opcode */

View File

@ -44,7 +44,6 @@
#include "Compiler.h"
#include "StubCalls.h"
#include "MonoIC.h"
#include "Retcon.h"
#include "assembler/jit/ExecutableAllocator.h"
#include "assembler/assembler/LinkBuffer.h"
#include "FrameState-inl.h"
@ -55,8 +54,6 @@
using namespace js;
using namespace js::mjit;
#define ADD_CALLSITE(stub) addCallSite(__LINE__, (stub))
#if defined(JS_METHODJIT_SPEW)
static const char *OpcodeNames[] = {
# define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) #name,
@ -89,11 +86,14 @@ mjit::Compiler::Compiler(JSContext *cx, JSScript *script, JSFunction *fun, JSObj
#if defined JS_POLYIC
pics(ContextAllocPolicy(cx)),
#endif
callSites(ContextAllocPolicy(cx)), stubcc(cx, *this, frame, script)
stubcc(cx, *this, frame, script)
#if defined JS_TRACER
,addTraceHints(cx->jitEnabled)
#endif
{
#ifdef DEBUG
masm.setSpewPath(false);
#endif
}
#define CHECK_STATUS(expr) \
@ -349,28 +349,11 @@ mjit::Compiler::finishThisUp()
JSC::ExecutableAllocator::cacheFlush(result, masm.size() + stubcc.size());
script->ncode = (uint8 *)(result + masm.distanceOf(invokeLabel));
script->inlineLength = masm.size();
script->outOfLineLength = stubcc.size();
#ifdef DEBUG
script->jitLength = masm.size() + stubcc.size();
#endif
script->execPool = execPool;
/* Build the table of call sites. */
CallSite *callSiteList = (CallSite *)cx->calloc(sizeof(CallSite) * (callSites.length() + 1));
if (!callSiteList) {
execPool->release();
return Compile_Error;
}
(callSiteList++)->nCallSites = callSites.length();
for (size_t i = 0; i < callSites.length(); i++) {
if (callSites[i].stub)
callSiteList[i].c.codeOffset = masm.size() + stubcc.masm.distanceOf(callSites[i].location);
else
callSiteList[i].c.codeOffset = masm.distanceOf(callSites[i].location);
callSiteList[i].c.pcOffset = callSites[i].pc - script->code;
callSiteList[i].c.id = callSites[i].id;
}
script->callSites = callSiteList;
return Compile_Okay;
}
@ -397,26 +380,19 @@ mjit::Compiler::finishThisUp()
CompileStatus
mjit::Compiler::generateMethod()
{
mjit::AutoScriptRetrapper trapper(cx, script);
PC = script->code;
for (;;) {
JSOp op = JSOp(*PC);
OpcodeStatus &opinfo = analysis[PC];
if (opinfo.nincoming || opinfo.trap) {
if (opinfo.nincoming) {
frame.forgetEverything(opinfo.stackDepth);
opinfo.safePoint = true;
}
frame.setInTryBlock(opinfo.inTryBlock);
jumpMap[uint32(PC - script->code)] = masm.label();
if (opinfo.trap) {
if (!trapper.untrap(PC))
return Compile_Error;
op = JSOp(*PC);
}
if (!opinfo.visited) {
if (op == JSOP_STOP)
break;
@ -430,13 +406,6 @@ mjit::Compiler::generateMethod()
SPEW_OPCODE();
JS_ASSERT(frame.stackDepth() == opinfo.stackDepth);
if (opinfo.trap) {
prepareStubCall(Uses(0));
masm.move(ImmPtr(PC), Registers::ArgReg1);
stubCall(stubs::Trap);
}
ADD_CALLSITE(false);
/**********************
* BEGIN COMPILER OPS *
**********************/
@ -1314,9 +1283,7 @@ mjit::Compiler::generateMethod()
frame.freeReg(cxreg);
stubcc.linkExit(jump, Uses(0));
stubcc.leave();
stubcc.masm.move(ImmPtr(PC), Registers::ArgReg1);
stubcc.call(stubs::Interrupt);
ADD_CALLSITE(true);
stubcc.rejoin(Changes(0));
}
}
@ -1439,28 +1406,6 @@ mjit::Compiler::knownJump(jsbytecode *pc)
return pc < PC;
}
void *
mjit::Compiler::findCallSite(const CallSite &callSite)
{
JS_ASSERT(callSite.c.pcOffset < script->length);
for (uint32 i = 0; i < callSites.length(); i++) {
if (callSites[i].pc == script->code + callSite.c.pcOffset &&
callSites[i].id == callSite.c.id) {
if (callSites[i].stub) {
return (uint8*)script->nmap[-1] + masm.size() +
stubcc.masm.distanceOf(callSites[i].location);
}
return (uint8*)script->nmap[-1] +
stubcc.masm.distanceOf(callSites[i].location);
}
}
/* We have no idea where to patch up to. */
JS_NOT_REACHED("Call site vanished.");
return NULL;
}
void
mjit::Compiler::jumpInScript(Jump j, jsbytecode *pc)
{
@ -1618,7 +1563,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew)
VoidPtrStubUInt32 stub = callingNew ? stubs::SlowNew : stubs::SlowCall;
masm.move(Imm32(argc), Registers::ArgReg1);
masm.stubCall(stub, PC, frame.stackDepth() + script->nfixed);
ADD_CALLSITE(false);
frame.popn(argc + 2);
frame.pushSynced();
return;
@ -1663,7 +1607,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew)
stubcc.leave();
stubcc.masm.move(Imm32(argc), Registers::ArgReg1);
stubcc.call(callingNew ? stubs::SlowNew : stubs::SlowCall);
ADD_CALLSITE(true);
/* Get function private pointer. */
Address funPrivate(data, offsetof(JSObject, fslots) +
@ -1691,7 +1634,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew)
stubcc.leave();
stubcc.masm.move(Imm32(argc), Registers::ArgReg1);
stubcc.call(callingNew ? stubs::SlowNew : stubs::SlowCall);
ADD_CALLSITE(true);
}
}
@ -1740,7 +1682,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew)
masm.addPtr(Imm32(sizeof(void*)), Registers::StackPointer);
#endif
masm.call(Registers::ReturnReg);
ADD_CALLSITE(false);
/*
* The scripted call returns a register triplet, containing the jsval and
@ -1776,22 +1717,6 @@ mjit::Compiler::inlineCallHelper(uint32 argc, bool callingNew)
stubcc.rejoin(Changes(0));
}
/*
* This function must be called immediately after any instruction which could
* cause a new JSStackFrame to be pushed and could lead to a new debug trap
* being set. This includes any API callbacks and any scripted or native call.
*/
void
mjit::Compiler::addCallSite(uint32 id, bool stub)
{
InternalCallSite site;
site.stub = stub;
site.location = stub ? stubcc.masm.label() : masm.label();
site.pc = PC;
site.id = id;
callSites.append(site);
}
void
mjit::Compiler::restoreFrameRegs(Assembler &masm)
{

View File

@ -181,13 +181,6 @@ class Compiler
bool set;
};
struct InternalCallSite {
bool stub;
Label location;
jsbytecode *pc;
uint32 id;
};
JSContext *cx;
JSScript *script;
JSObject *scopeChain;
@ -205,7 +198,6 @@ class Compiler
#if defined JS_POLYIC
js::Vector<PICGenInfo, 64> pics;
#endif
js::Vector<InternalCallSite, 64> callSites;
StubCompiler stubcc;
Label invokeLabel;
bool addTraceHints;
@ -224,7 +216,6 @@ class Compiler
Label getLabel() { return masm.label(); }
bool knownJump(jsbytecode *pc);
Label labelOf(jsbytecode *target);
void *findCallSite(const CallSite &callSite);
private:
CompileStatus generatePrologue();
@ -237,7 +228,6 @@ class Compiler
void jumpInScript(Jump j, jsbytecode *pc);
JSC::ExecutablePool *getExecPool(size_t size);
bool compareTwoValues(JSContext *cx, JSOp op, const Value &lhs, const Value &rhs);
void addCallSite(uint32 id, bool stub);
/* Emitting helpers. */
void restoreFrameRegs(Assembler &masm);
@ -339,7 +329,6 @@ class Compiler
STUB_CALL_TYPE(VoidStubJSObj);
STUB_CALL_TYPE(VoidPtrStubPC);
STUB_CALL_TYPE(VoidVpStub);
STUB_CALL_TYPE(VoidStubPC);
#undef STUB_CALL_TYPE
void prepareStubCall(Uses uses);

View File

@ -74,14 +74,14 @@ using namespace JSC;
#define THROW() \
do { \
void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
*f.returnAddressLocation() = ptr; \
f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
return; \
} while (0)
#define THROWV(v) \
do { \
void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
*f.returnAddressLocation() = ptr; \
f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
return v; \
} while (0)
@ -863,7 +863,7 @@ RunTracer(VMFrame &f)
f.fp = cx->fp;
entryFrame->ncode = f.fp->ncode;
void *retPtr = JS_FUNC_TO_DATA_PTR(void *, JaegerFromTracer);
*f.returnAddressLocation() = retPtr;
f.setReturnAddress(ReturnAddressPtr(retPtr));
return NULL;
}

View File

@ -43,7 +43,6 @@
#include "BaseAssembler.h"
#include "MonoIC.h"
#include "PolyIC.h"
#include "TrampolineCompiler.h"
using namespace js;
using namespace js::mjit;
@ -56,8 +55,6 @@ static uint32 StubCallsForOp[STUB_CALLS_FOR_OP_COUNT];
extern "C" void JS_FASTCALL
SetVMFrameRegs(VMFrame &f)
{
f.previous = JS_METHODJIT_DATA(f.cx).activeFrame;
JS_METHODJIT_DATA(f.cx).activeFrame = &f;
f.oldRegs = f.cx->regs;
f.cx->setCurrentRegs(&f.regs);
}
@ -65,8 +62,6 @@ SetVMFrameRegs(VMFrame &f)
extern "C" void JS_FASTCALL
UnsetVMFrameRegs(VMFrame &f)
{
JS_ASSERT(JS_METHODJIT_DATA(f.cx).activeFrame);
JS_METHODJIT_DATA(f.cx).activeFrame = JS_METHODJIT_DATA(f.cx).activeFrame->previous;
*f.oldRegs = f.regs;
f.cx->setCurrentRegs(f.oldRegs);
}
@ -543,12 +538,6 @@ ThreadData::Initialize()
if (!execPool)
return false;
TrampolineCompiler tc(execPool, &trampolines);
if (!tc.compile()) {
delete execPool;
return false;
}
if (!picScripts.init()) {
delete execPool;
return false;
@ -559,15 +548,12 @@ ThreadData::Initialize()
StubCallsForOp[i] = 0;
#endif
activeFrame = NULL;
return true;
}
void
ThreadData::Finish()
{
TrampolineCompiler::release(&trampolines);
delete execPool;
#ifdef JS_METHODJIT_PROFILE_STUBS
FILE *fp = fopen("/tmp/stub-profiling", "wt");
@ -686,15 +672,13 @@ void
mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
{
if (script->execPool) {
#if defined DEBUG && (defined JS_CPU_X86 || defined JS_CPU_X64)
memset(script->nmap[-1], 0xcc, script->inlineLength + script->outOfLineLength);
#endif
script->execPool->release();
script->execPool = NULL;
// Releasing the execPool takes care of releasing the code.
script->ncode = NULL;
script->inlineLength = 0;
script->outOfLineLength = 0;
#ifdef DEBUG
script->jitLength = 0;
#endif
#if defined JS_POLYIC
if (script->pics) {
@ -713,10 +697,6 @@ mjit::ReleaseScriptCode(JSContext *cx, JSScript *script)
cx->free(script->nmap - 1);
script->nmap = NULL;
}
if (script->callSites) {
cx->free(script->callSites - 1);
script->callSites = NULL;
}
#if defined JS_MONOIC
if (script->mics) {
cx->free(script->mics);

View File

@ -65,6 +65,8 @@ struct VMFrame
void *scriptedReturn;
#if defined(JS_CPU_X86)
uintptr_t padding[2];
#elif defined(JS_CPU_ARM)
uintptr_t padding;
#endif
@ -75,7 +77,6 @@ struct VMFrame
} x;
} u;
VMFrame *previous;
JSFrameRegs *oldRegs;
JSFrameRegs regs;
JSStackFrame *fp;
@ -90,12 +91,20 @@ struct VMFrame
void *savedEIP;
#ifdef JS_NO_FASTCALL
inline void** returnAddressLocation() {
return reinterpret_cast<void**>(this) - 3;
inline void setReturnAddress(JSC::ReturnAddressPtr addr) {
*(reinterpret_cast<JSC::ReturnAddressPtr*>(this)-3) = addr;
}
inline JSC::ReturnAddressPtr getReturnAddress() const {
return *(reinterpret_cast<const JSC::ReturnAddressPtr*>(this)-3);
}
#else
inline void** returnAddressLocation() {
return reinterpret_cast<void**>(this) - 1;
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_X64)
@ -112,12 +121,20 @@ struct VMFrame
void *savedRIP;
#ifdef _MSC_VER
inline void** returnAddressLocation() {
return reinterpret_cast<void**>(this) - 5;
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** returnAddressLocation() {
return reinterpret_cast<void**>(this) - 1;
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
@ -132,9 +149,14 @@ struct VMFrame
void *savedR11;
void *savedLR;
inline void** returnAddressLocation() {
return reinterpret_cast<void**>(&this->veneerReturn);
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
@ -162,7 +184,6 @@ typedef void (JS_FASTCALL *VoidStubAtom)(VMFrame &, JSAtom *);
typedef JSString * (JS_FASTCALL *JSStrStub)(VMFrame &);
typedef JSString * (JS_FASTCALL *JSStrStubUInt32)(VMFrame &, uint32);
typedef void (JS_FASTCALL *VoidStubJSObj)(VMFrame &, JSObject *);
typedef void (JS_FASTCALL *VoidStubPC)(VMFrame &, jsbytecode *);
#define JS_UNJITTABLE_METHOD (reinterpret_cast<void*>(1))
@ -200,16 +221,6 @@ CanMethodJIT(JSContext *cx, JSScript *script, JSFunction *fun, JSObject *scopeCh
void
PurgeShapeDependencies(JSContext *cx);
union CallSite
{
struct {
uint32 codeOffset;
uint32 pcOffset;
uint32 id;
} c;
uint32 nCallSites;
};
} /* namespace mjit */
} /* namespace js */

View File

@ -1,209 +0,0 @@
/* -*- 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 Jaegermonkey.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
*
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Andrew Drake <drakedevel@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#if defined JS_METHODJIT
#include "Retcon.h"
#include "MethodJIT.h"
#include "Compiler.h"
#include "jsdbgapi.h"
#include "jsnum.h"
#include "jscntxtinlines.h"
using namespace js;
using namespace js::mjit;
namespace js {
namespace mjit {
AutoScriptRetrapper::~AutoScriptRetrapper()
{
while (!traps.empty()) {
jsbytecode *pc = traps.back();
traps.popBack();
*pc = JSOP_TRAP;
}
}
bool
AutoScriptRetrapper::untrap(jsbytecode *pc)
{
if (!traps.append(pc))
return false;
*pc = JS_GetTrapOpcode(cx, script, pc);
return true;
}
Recompiler::PatchableAddress
Recompiler::findPatch(void **location)
{
for (uint32 i = 0; i < script->callSites[-1].nCallSites; i++) {
if (script->callSites[i].c.codeOffset + (uint8*)script->nmap[-1] == *location) {
PatchableAddress result;
result.location = location;
result.callSite = script->callSites[i];
return result;
}
}
JS_NOT_REACHED("failed to find call site");
return PatchableAddress();
}
void
Recompiler::applyPatch(Compiler& c, PatchableAddress& toPatch)
{
void *result = c.findCallSite(toPatch.callSite);
JS_ASSERT(result);
*toPatch.location = result;
}
Recompiler::Recompiler(JSContext *cx, JSScript *script) : cx(cx), script(script)
{
}
/*
* The strategy for this goes as follows:
*
* 1) Scan the stack, looking at all return addresses that could go into JIT
* code.
* 2) If an address corresponds to a call site registered by |callSite| during
* the last compilation, remember it.
* 3) Purge the old compiled state and return if there were no active frames of
* this script on the stack.
* 4) Fix up the stack by replacing all saved addresses with the addresses the
* new compiler gives us for the call sites.
*/
bool
Recompiler::recompile()
{
JS_ASSERT(script->ncode && script->ncode != JS_UNJITTABLE_METHOD);
Vector<PatchableAddress> toPatch(cx);
/* Scan the stack, saving the ncode elements of the frames. */
JSStackFrame *firstFrame = NULL;
for (CallStackIterator cs(cx); !cs.done(); ++cs) {
FrameIterator fp = cs.top();
for (FrameIterator fp = cs.top(); fp != cs.bottom(); ++fp) {
if (!firstFrame && fp.fp()->script == script)
firstFrame = fp.fp();
if (script->isValidJitCode(fp.fp()->ncode)) {
if (!toPatch.append(findPatch(&fp.fp()->ncode)))
return false;
}
}
}
/* Iterate over VMFrames saving the machine and scripted return. */
for (VMFrame *f = JS_METHODJIT_DATA(cx).activeFrame;
f != NULL;
f = f->previous) {
if (script->isValidJitCode(f->scriptedReturn)) {
if (!toPatch.append(findPatch(&f->scriptedReturn)))
return false;
}
void **machineReturn = f->returnAddressLocation();
if (script->isValidJitCode(*machineReturn))
if (!toPatch.append(findPatch(machineReturn)))
return false;
}
ReleaseScriptCode(cx, script);
/* No need to actually compile or fixup if no frames on the stack */
if (!firstFrame)
return true;
Compiler c(cx, script, firstFrame->fun, firstFrame->scopeChain);
if (c.Compile() != Compile_Okay)
return false;
/* Perform the earlier scanned patches */
for (uint32 i = 0; i < toPatch.length(); i++)
applyPatch(c, toPatch[i]);
return true;
}
FrameIterator&
FrameIterator::operator++() {
JS_ASSERT(curfp);
curfp = curfp->down;
return *this;
}
bool
FrameIterator::operator==(const FrameIterator& other) const {
return curfp == other.curfp;
}
bool
FrameIterator::operator!=(const FrameIterator& other) const {
return curfp != other.curfp;
}
CallStackIterator&
CallStackIterator::operator++() {
JS_ASSERT(curcs);
curcs = curcs->getPreviousInThread();
return *this;
}
FrameIterator
CallStackIterator::top() const {
JS_ASSERT(curcs);
return FrameIterator(curcs->getCurrentFrame());
}
FrameIterator
CallStackIterator::bottom() const {
JS_ASSERT(curcs);
return FrameIterator(curcs->getInitialFrame()->down);
}
} /* mjit */
} /* js */
#endif /* JS_METHODJIT */

View File

@ -1,143 +0,0 @@
/* -*- 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 Jaegermonkey.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
*
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Andrew Drake <drakedevel@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* Retroactive continuity ("retcon") refers to the retroactive modification
* or reinterpretation of established facts.
*/
#if !defined jsjaeger_retcon_h__ && defined JS_METHODJIT
#define jsjaeger_retcon_h__
#include "jscntxt.h"
#include "jsscript.h"
#include "MethodJIT.h"
#include "Compiler.h"
namespace js {
namespace mjit {
/*
* A problem often arises where, for one reason or another, a piece of code
* wants to touch the script->code, but isn't expecting JSOP_TRAP. This allows
* one to temporarily remove JSOP_TRAPs from the instruction stream (without
* copying) and automatically re-add them on scope exit.
*/
class AutoScriptRetrapper
{
public:
AutoScriptRetrapper(JSContext *cx1, JSScript *script1) :
cx(cx1), script(script1), traps(cx) {};
~AutoScriptRetrapper();
bool untrap(jsbytecode *pc);
private:
JSContext *cx;
JSScript *script;
Vector<jsbytecode*> traps;
};
/*
* This class is responsible for sanely re-JITing a script and fixing up
* the world. If you ever change the code associated with a JSScript, or
* otherwise would cause existing JITed code to be incorrect, you /must/ use
* this to invalidate and potentially re-compile the existing JITed code,
* fixing up the stack in the process.
*/
class Recompiler {
struct PatchableAddress {
void **location;
CallSite callSite;
};
public:
Recompiler(JSContext *cx, JSScript *script);
bool recompile();
private:
JSContext *cx;
JSScript *script;
PatchableAddress findPatch(void **location);
void applyPatch(Compiler& c, PatchableAddress& toPatch);
};
/*
* Utility classes to make iteration over the stack clean.
*/
class FrameIterator
{
public:
FrameIterator(JSStackFrame *frame) : curfp(frame) { };
bool done() const { return curfp == NULL; }
FrameIterator& operator++();
bool operator==(const FrameIterator& other) const;
bool operator!=(const FrameIterator& other) const;
JSStackFrame *fp() const { return curfp; }
private:
JSStackFrame *curfp;
};
class CallStackIterator
{
public:
CallStackIterator(JSContext *cx) : curcs(cx->stack().getCurrentCallStack()) { };
bool done() const { return curcs == NULL; }
CallStackIterator& operator++();
FrameIterator top() const;
FrameIterator bottom() const;
CallStack *cs() const { return curcs; }
private:
CallStack *curcs;
};
} /* mjit */
} /* js */
#endif

View File

@ -47,14 +47,14 @@ namespace mjit {
#define THROW() \
do { \
void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
*f.returnAddressLocation() = ptr; \
f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
return; \
} while (0)
#define THROWV(v) \
do { \
void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
*f.returnAddressLocation() = ptr; \
f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
return v; \
} while (0)

View File

@ -50,7 +50,6 @@
#include "assembler/assembler/MacroAssemblerCodeRef.h"
#include "jsiter.h"
#include "jstypes.h"
#include "methodjit/Compiler.h"
#include "methodjit/StubCalls.h"
#include "jstracer.h"
#include "jspropertycache.h"
@ -1313,36 +1312,10 @@ stubs::NewArray(VMFrame &f, uint32 len)
}
void JS_FASTCALL
stubs::Interrupt(VMFrame &f, jsbytecode *pc)
stubs::Interrupt(VMFrame &f)
{
if (!js_HandleExecutionInterrupt(f.cx))
if (!js_HandleExecutionInterrupt(f.cx)) {
THROW();
}
void JS_FASTCALL
stubs::Trap(VMFrame &f, jsbytecode *pc)
{
Value rval;
switch (JS_HandleTrap(f.cx, f.cx->fp->script, pc, Jsvalify(&rval))) {
case JSTRAP_THROW:
f.cx->throwing = JS_TRUE;
f.cx->exception = rval;
THROW();
case JSTRAP_RETURN:
f.cx->throwing = JS_FALSE;
f.cx->fp->rval = rval;
*f.returnAddressLocation() = JS_FUNC_TO_DATA_PTR(void *,
JS_METHODJIT_DATA(f.cx).trampolines.forceReturn);
break;
case JSTRAP_ERROR:
f.cx->throwing = JS_FALSE;
THROW();
default:
break;
}
}

View File

@ -52,8 +52,7 @@ void JS_FASTCALL ComputeThis(VMFrame &f);
JSObject * JS_FASTCALL NewInitArray(VMFrame &f);
JSObject * JS_FASTCALL NewInitObject(VMFrame &f, uint32 empty);
JSObject * JS_FASTCALL NewArray(VMFrame &f, uint32 len);
void JS_FASTCALL Trap(VMFrame &f, jsbytecode *pc);
void JS_FASTCALL Interrupt(VMFrame &f, jsbytecode *pc);
void JS_FASTCALL Interrupt(VMFrame &f);
void JS_FASTCALL InitElem(VMFrame &f, uint32 last);
void JS_FASTCALL InitProp(VMFrame &f, JSAtom *atom);
void JS_FASTCALL InitMethod(VMFrame &f, JSAtom *atom);

View File

@ -121,7 +121,6 @@ class StubCompiler
STUB_CALL_TYPE(VoidPtrStub);
STUB_CALL_TYPE(BoolStub);
STUB_CALL_TYPE(VoidStubAtom);
STUB_CALL_TYPE(VoidStubPC);
#undef STUB_CALL_TYPE

View File

@ -1,165 +0,0 @@
/* -*- 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 Jaegermonkey.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
*
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Andrew Drake <drakedevel@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#if defined JS_METHODJIT
#include "TrampolineCompiler.h"
#include "StubCalls.h"
#include "assembler/assembler/LinkBuffer.h"
namespace js {
namespace mjit {
#define CHECK_RESULT(x) if (!(x)) return false
#define COMPILE(which, pool, how) CHECK_RESULT(compileTrampoline((void **)(&(which)), &pool, how))
#define RELEASE(which, pool) JS_BEGIN_MACRO \
which = NULL; \
if (pool) \
pool->release(); \
pool = NULL; \
JS_END_MACRO
#ifdef JS_CPU_X86
static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = JSC::X86Registers::ecx;
static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = JSC::X86Registers::edx;
#elif defined(JS_CPU_ARM)
static const JSC::MacroAssembler::RegisterID JSReturnReg_Type = JSC::ARMRegisters::r2;
static const JSC::MacroAssembler::RegisterID JSReturnReg_Data = JSC::ARMRegisters::r1;
#endif
bool
TrampolineCompiler::compile()
{
#ifdef JS_METHODJIT_SPEW
JMCheckLogging();
#endif
COMPILE(trampolines->forceReturn, trampolines->forceReturnPool, generateForceReturn);
return true;
}
void
TrampolineCompiler::release(Trampolines *tramps)
{
RELEASE(tramps->forceReturn, tramps->forceReturnPool);
}
bool
TrampolineCompiler::compileTrampoline(void **where, JSC::ExecutablePool **pool,
TrampolineGenerator generator)
{
Assembler masm;
Label entry = masm.label();
CHECK_RESULT(generator(masm));
JS_ASSERT(entry.isValid());
*pool = execPool->poolForSize(masm.size());
if (!*pool)
return false;
JSC::LinkBuffer buffer(&masm, *pool);
uint8 *result = (uint8*)buffer.finalizeCodeAddendum().dataLocation();
masm.finalize(result);
*where = result + masm.distanceOf(entry);
return true;
}
/*
* This is shamelessly copied from emitReturn, but with several changes:
* - There was always at least one inline call.
* - We don't know if there is a call object, so we always check.
* - We don't know where we came from, so we don't know frame depth or PC.
* - There is no stub buffer.
*/
bool
TrampolineCompiler::generateForceReturn(Assembler &masm)
{
/* if (!callobj) stubs::PutCallObject */
Jump noCallObj = masm.branchPtr(Assembler::Equal,
Address(JSFrameReg, offsetof(JSStackFrame, callobj)),
ImmPtr(0));
masm.stubCall(stubs::PutCallObject, NULL, 0);
noCallObj.linkTo(masm.label(), &masm);
/* if (arguments) stubs::PutArgsObject */
Jump noArgsObj = masm.branchPtr(Assembler::Equal,
Address(JSFrameReg, offsetof(JSStackFrame, argsobj)),
ImmIntPtr(0));
masm.stubCall(stubs::PutArgsObject, NULL, 0);
noArgsObj.linkTo(masm.label(), &masm);
/*
* r = fp->down
* a1 = f.cx
* f.fp = r
* cx->fp = r
*/
masm.loadPtr(Address(JSFrameReg, offsetof(JSStackFrame, down)), Registers::ReturnReg);
masm.loadPtr(FrameAddress(offsetof(VMFrame, cx)), Registers::ArgReg1);
masm.storePtr(Registers::ReturnReg, FrameAddress(offsetof(VMFrame, fp)));
masm.storePtr(Registers::ReturnReg, Address(Registers::ArgReg1, offsetof(JSContext, fp)));
masm.subPtr(ImmIntPtr(1), FrameAddress(offsetof(VMFrame, inlineCallCount)));
Address rval(JSFrameReg, offsetof(JSStackFrame, rval));
masm.load32(masm.payloadOf(rval), JSReturnReg_Data);
masm.load32(masm.tagOf(rval), JSReturnReg_Type);
masm.move(Registers::ReturnReg, JSFrameReg);
masm.loadPtr(Address(JSFrameReg, offsetof(JSStackFrame, ncode)), Registers::ReturnReg);
#ifdef DEBUG
masm.storePtr(ImmPtr(JSStackFrame::sInvalidPC),
Address(JSFrameReg, offsetof(JSStackFrame, savedPC)));
#endif
#if defined(JS_CPU_ARM)
masm.loadPtr(FrameAddress(offsetof(VMFrame, scriptedReturn)), JSC::ARMRegisters::lr);
#endif
masm.ret();
return true;
}
} /* mjit */
} /* js */
#endif /* JS_METHOJIT */

View File

@ -1,81 +0,0 @@
/* -*- 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 Jaegermonkey.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
*
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Andrew Drake <drakedevel@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#if !defined jsjaeger_trampolinecompiler_h__ && defined JS_METHODJIT
#define jsjaeger_trampolinecompiler_h__
#include "assembler/jit/ExecutableAllocator.h"
#include "CodeGenIncludes.h"
namespace js {
namespace mjit {
class TrampolineCompiler
{
typedef Assembler::Label Label;
typedef Assembler::Jump Jump;
typedef Assembler::ImmPtr ImmPtr;
typedef Assembler::Address Address;
typedef bool (*TrampolineGenerator)(Assembler &masm);
public:
TrampolineCompiler(JSC::ExecutableAllocator *pool, Trampolines *tramps)
: execPool(pool), trampolines(tramps)
{ }
bool compile();
static void release(Trampolines *tramps);
private:
bool compileTrampoline(void **where, JSC::ExecutablePool **pool,
TrampolineGenerator generator);
/* Generators for trampolines. */
static bool generateForceReturn(Assembler &masm);
JSC::ExecutableAllocator *execPool;
Trampolines *trampolines;
};
} /* mjit */
} /* js */
#endif

View File

@ -1,8 +0,0 @@
var x = "failure";
function main() { x = "success"; }
/* The JSOP_STOP in a. */
trap(main, 8, "");
main();
assertEq(x, "success");

View File

@ -1,9 +0,0 @@
var x = "notset";
function main() { x = "failure"; }
function success() { x = "success"; }
/* The JSOP_STOP in a. */
trap(main, 8, "success()");
main();
assertEq(x, "success");

View File

@ -1,10 +0,0 @@
var x = "notset";
function main() { x = "success"; }
function failure() { x = "failure"; }
/* The JSOP_STOP in a. */
trap(main, 8, "failure()");
untrap(main, 8);
main();
assertEq(x, "success");

View File

@ -1,6 +0,0 @@
function main() {
return "failure";
}
/* JSOP_RETURN in main. */
trap(main, 4, "'success'");
assertEq(main(), "success");

View File

@ -1,6 +0,0 @@
function main() {
return 1;
}
/* JSOP_RETURN in main. */
trap(main, 2, "0");
assertEq(main(), 0);

View File

@ -1,14 +0,0 @@
x = "notset";
function myparent(nested) {
if (nested) {
/* myparent call in myparent. */
trap(myparent, 40, "failure()");
} else {
x = "success";
myparent(true);
}
}
function failure() { x = "failure"; }
myparent(false);
assertEq(x, "success");

View File

@ -1,20 +0,0 @@
x = "notset";
function child() {
x = "failure1";
/* JSOP_STOP in parent. */
trap(parent, 11, "success()");
}
function parent() {
x = "failure2";
}
/* First op in parent. */
trap(parent, 1, "child()");
function success() {
x = "success";
}
parent();
assertEq(x, "success");

View File

@ -1,15 +0,0 @@
x = "notset";
function child() {
/* JSOP_STOP in parent. */
trap(parent, 19, "success()");
}
function parent() {
child();
x = "failure";
}
function success() {
x = "success";
}
parent()
assertEq(x, "success");

View File

@ -1,17 +0,0 @@
x = "notset";
function myparent(nested) {
if (nested) {
/* noop call in myparent */
trap(myparent, 48, "success()");
} else {
myparent(true);
x = "failure";
noop();
}
}
function noop() { }
function success() { x = "success"; }
myparent();
assertEq(x, "success");

View File

@ -1,22 +0,0 @@
x = "notset";
function doNothing() { }
function myparent(nested) {
if (nested) {
/* JSOP_CALL to doNothing in myparent with nested = true. */
trap(myparent, 26, "success()");
doNothing();
} else {
doNothing();
}
}
/* JSOP_CALL to doNothing in myparent with nested = false. */
trap(myparent, 37, "myparent(true)");
function success() {
x = "success";
}
myparent(false);
assertEq(x, "success");

View File

@ -1,10 +0,0 @@
x = "notset";
function main() {
/* The JSOP_STOP in a. */
trap(main, 27, "success()");
x = "failure";
}
function success() { x = "success"; }
main();
assertEq(x, "success");

View File

@ -1,14 +0,0 @@
x = "notset";
function child() {
/* JSOP_STOP in parent */
untrap(parent, 11);
x = "success";
}
function parent() {
x = "failure";
}
/* JSOP_STOP in parent */
trap(parent, 11, "child()");
parent();
assertEq(x, "success");

View File

@ -1,12 +0,0 @@
x = "notset";
function main() {
/* JSOP_STOP in main. */
untrap(main, 23);
x = "success";
}
function failure() { x = "failure"; }
/* JSOP_STOP in main. */
trap(main, 23, "failure()");
main();
assertEq(x, "success");