/* -*- 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 * * 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 #if !defined(JS_NUNBOX32) && !defined(JS_PUNBOX64) # error "No boxing format selected." #endif namespace js { namespace mjit { struct JITScript; } struct VMFrame { union Arguments { struct { void *ptr; void *ptr2; void *ptr3; } x; struct { uint32 lazyArgsObj; uint32 dynamicArgc; } call; } u; VMFrame *previous; void *unused; JSFrameRegs regs; JSContext *cx; Value *stackLimit; JSStackFrame *entryfp; #if defined(JS_CPU_X86) void *savedEBX; void *savedEDI; void *savedESI; void *savedEBP; void *savedEIP; # ifdef JS_NO_FASTCALL inline void** returnAddressLocation() { return reinterpret_cast(this) - 5; } # else inline void** returnAddressLocation() { return reinterpret_cast(this) - 1; } # endif #elif defined(JS_CPU_X64) void *savedRBX; # ifdef _WIN64 void *savedRSI; void *savedRDI; # endif void *savedR15; void *savedR14; void *savedR13; void *savedR12; void *savedRBP; void *savedRIP; # ifdef _WIN64 inline void** returnAddressLocation() { return reinterpret_cast(this) - 5; } # else inline void** returnAddressLocation() { return reinterpret_cast(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** returnAddressLocation() { return reinterpret_cast(this) - 1; } #else # error "The VMFrame layout isn't defined for your processor architecture!" #endif JSRuntime *runtime() { return cx->runtime; } JSStackFrame *&fp() { return regs.fp; } mjit::JITScript *jit() { return fp()->jit(); } }; #ifdef JS_CPU_ARM // WARNING: Do not call this function directly from C(++) code because it is not ABI-compliant. extern "C" void JaegerStubVeneer(void); #endif namespace mjit { /* * Trampolines to force returns from jit code. * See also TrampolineCompiler::generateForceReturn(Fast). */ struct Trampolines { typedef void (*TrampolinePtr)(); TrampolinePtr forceReturn; JSC::ExecutablePool *forceReturnPool; #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64) TrampolinePtr forceReturnFast; JSC::ExecutablePool *forceReturnFastPool; #endif }; /* * Method JIT compartment data. Currently, there is exactly one per * JS compartment. It would be safe for multiple JS compartments to * share a JaegerCompartment as long as only one thread can enter * the JaegerCompartment at a time. */ class JaegerCompartment { JSC::ExecutableAllocator *execAlloc; // allocator for jit code Trampolines trampolines; // force-return trampolines VMFrame *activeFrame_; // current active VMFrame void Finish(); public: bool Initialize(); ~JaegerCompartment() { Finish(); } JSC::ExecutablePool *poolForSize(size_t size) { return execAlloc->poolForSize(size); } VMFrame *activeFrame() { return activeFrame_; } void pushActiveFrame(VMFrame *f) { f->previous = activeFrame_; activeFrame_ = f; } void popActiveFrame() { JS_ASSERT(activeFrame_); activeFrame_ = activeFrame_->previous; } Trampolines::TrampolinePtr forceReturnTrampoline() const { return trampolines.forceReturn; } #if (defined(JS_NO_FASTCALL) && defined(JS_CPU_X86)) || defined(_WIN64) Trampolines::TrampolinePtr forceReturnFastTrampoline() const { return trampolines.forceReturnFast; } #endif }; /* * Allocation policy for compiler jstl objects. The goal is to free the * compiler from having to check and propagate OOM after every time we * append to a vector. We do this by reporting OOM to the engine and * setting a flag on the compiler when OOM occurs. The compiler is required * to check for OOM only before trying to use the contents of the list. */ class CompilerAllocPolicy : public ContextAllocPolicy { bool *oomFlag; void *checkAlloc(void *p) { if (!p) *oomFlag = true; return p; } public: CompilerAllocPolicy(JSContext *cx, bool *oomFlag) : ContextAllocPolicy(cx), oomFlag(oomFlag) {} CompilerAllocPolicy(JSContext *cx, Compiler &compiler); void *malloc(size_t bytes) { return checkAlloc(ContextAllocPolicy::malloc(bytes)); } void *realloc(void *p, size_t bytes) { return checkAlloc(ContextAllocPolicy::realloc(p, bytes)); } }; namespace ic { # if defined JS_POLYIC struct PICInfo; struct GetElementIC; struct SetElementIC; # endif # if defined JS_MONOIC struct MICInfo; struct EqualityICInfo; struct TraceICInfo; struct CallICInfo; # endif } } typedef void (JS_FASTCALL *VoidStub)(VMFrame &); typedef void (JS_FASTCALL *VoidVpStub)(VMFrame &, Value *); 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); typedef JSObject * (JS_FASTCALL *JSObjStubFun)(VMFrame &, JSFunction *); typedef void (JS_FASTCALL *VoidStubFun)(VMFrame &, JSFunction *); typedef JSObject * (JS_FASTCALL *JSObjStubJSObj)(VMFrame &, JSObject *); 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 *); typedef JSBool (JS_FASTCALL *BoolStubUInt32)(VMFrame &f, uint32); #ifdef JS_MONOIC typedef void (JS_FASTCALL *VoidStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); typedef void * (JS_FASTCALL *VoidPtrStubCallIC)(VMFrame &, js::mjit::ic::CallICInfo *); typedef void (JS_FASTCALL *VoidStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); typedef void * (JS_FASTCALL *VoidPtrStubMIC)(VMFrame &, js::mjit::ic::MICInfo *); typedef JSBool (JS_FASTCALL *BoolStubEqualityIC)(VMFrame &, js::mjit::ic::EqualityICInfo *); typedef void * (JS_FASTCALL *VoidPtrStubTraceIC)(VMFrame &, js::mjit::ic::TraceICInfo *); #endif #ifdef JS_POLYIC typedef void (JS_FASTCALL *VoidStubPIC)(VMFrame &, js::mjit::ic::PICInfo *); typedef void (JS_FASTCALL *VoidStubGetElemIC)(VMFrame &, js::mjit::ic::GetElementIC *); typedef void (JS_FASTCALL *VoidStubSetElemIC)(VMFrame &f, js::mjit::ic::SetElementIC *); #endif namespace mjit { struct CallSite; struct NativeMapEntry { size_t bcOff; /* bytecode offset in script */ void *ncode; /* pointer to native code */ }; struct JITScript { typedef JSC::MacroAssemblerCodeRef CodeRef; CodeRef code; /* pool & code addresses */ NativeMapEntry *nmap; /* array of NativeMapEntrys, sorted by .bcOff. .ncode values may not be NULL. */ size_t nNmapPairs; /* number of entries in nmap */ void *invokeEntry; /* invoke address */ void *fastEntry; /* cached entry, fastest */ void *arityCheckEntry; /* arity check address */ /* To minimize the size of this struct on 64-bit, put uint32s after all pointers. */ js::mjit::CallSite *callSites; #ifdef JS_MONOIC ic::MICInfo *mics; /* MICs in this script. */ ic::CallICInfo *callICs; /* CallICs in this script. */ ic::EqualityICInfo *equalityICs; ic::TraceICInfo *traceICs; #endif #ifdef JS_POLYIC ic::PICInfo *pics; /* PICs in this script */ ic::GetElementIC *getElems; ic::SetElementIC *setElems; #endif uint32 nCallSites:31; bool singleStepMode:1; /* compiled in "single step mode" */ #ifdef JS_MONOIC uint32 nMICs; /* number of MonoICs */ uint32 nCallICs; /* number of call ICs */ uint32 nEqualityICs; uint32 nTraceICs; #endif #ifdef JS_POLYIC uint32 nPICs; /* number of PolyICs */ uint32 nGetElems; uint32 nSetElems; #endif #ifdef JS_MONOIC // Additional ExecutablePools that IC stubs were generated into. typedef Vector ExecPoolVector; ExecPoolVector execPools; #endif ~JITScript(); bool isValidCode(void *ptr) { char *jitcode = (char *)code.m_code.executableAddress(); char *jcheck = (char *)ptr; return jcheck >= jitcode && jcheck < jitcode + code.m_size; } void nukeScriptDependentICs(); void sweepCallICs(bool purgeAll); void purgeMICs(); void purgePICs(); }; /* * Execute the given mjit code. This is a low-level call and callers must * provide the same guarantees as JaegerShot/CheckStackAndEnterMethodJIT. */ JSBool EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code, Value *stackLimit); /* Execute a method that has been JIT compiled. */ JSBool JaegerShot(JSContext *cx); /* Drop into the middle of a method at an arbitrary point, and execute. */ JSBool JaegerShotAtSafePoint(JSContext *cx, void *safePoint); enum CompileStatus { Compile_Okay, Compile_Abort, Compile_Error }; void JS_FASTCALL ProfileStubCall(VMFrame &f); CompileStatus JS_NEVER_INLINE TryCompile(JSContext *cx, JSStackFrame *fp); void ReleaseScriptCode(JSContext *cx, JSScript *script); static inline CompileStatus CanMethodJIT(JSContext *cx, JSScript *script, JSStackFrame *fp) { if (!cx->methodJitEnabled) return Compile_Abort; JITScriptStatus status = script->getJITStatus(fp->isConstructing()); if (status == JITScript_Invalid) return Compile_Abort; if (status == JITScript_None) return TryCompile(cx, fp); return Compile_Okay; } struct CallSite { uint32 codeOffset; uint32 pcOffset; uint32 id; // Normally, callsite ID is the __LINE__ in the program that added the // callsite. Since traps can be removed, we make sure they carry over // from each compilation, and identify them with a single, canonical // ID. Hopefully a SpiderMonkey file won't have two billion source lines. static const uint32 MAGIC_TRAP_ID = 0xFEDCBABC; void initialize(uint32 codeOffset, uint32 pcOffset, uint32 id) { this->codeOffset = codeOffset; this->pcOffset = pcOffset; this->id = id; } bool isTrap() const { return id == MAGIC_TRAP_ID; } }; /* Re-enables a tracepoint in the method JIT. */ void EnableTraceHint(JSScript *script, jsbytecode *pc, uint16_t index); uintN GetCallTargetCount(JSScript *script, jsbytecode *pc); inline void * bsearch_nmap(NativeMapEntry *nmap, size_t nPairs, size_t bcOff) { size_t lo = 1, hi = nPairs; while (1) { /* current unsearched space is from lo-1 to hi-1, inclusive. */ if (lo > hi) return NULL; /* not found */ size_t mid = (lo + hi) / 2; size_t bcOff_mid = nmap[mid-1].bcOff; if (bcOff < bcOff_mid) { hi = mid-1; continue; } if (bcOff > bcOff_mid) { lo = mid+1; continue; } return nmap[mid-1].ncode; } } } /* namespace mjit */ } /* namespace js */ inline void * JSScript::maybeNativeCodeForPC(bool constructing, jsbytecode *pc) { js::mjit::JITScript *jit = getJIT(constructing); if (!jit) return NULL; JS_ASSERT(pc >= code && pc < code + length); return bsearch_nmap(jit->nmap, jit->nNmapPairs, (size_t)(pc - code)); } inline void * JSScript::nativeCodeForPC(bool constructing, jsbytecode *pc) { js::mjit::JITScript *jit = getJIT(constructing); JS_ASSERT(pc >= code && pc < code + length); void* native = bsearch_nmap(jit->nmap, jit->nNmapPairs, (size_t)(pc - code)); JS_ASSERT(native); return native; } #ifdef _MSC_VER extern "C" void *JaegerThrowpoline(js::VMFrame *vmFrame); #else extern "C" void JaegerThrowpoline(); #endif extern "C" void InjectJaegerReturn(); #endif /* jsjaeger_h__ */