☠☠ backed out by 26653529ea8b ☠ ☠ | |
author | Vladimir Vukicevic <vladimir@pobox.com> |
Mon, 18 Mar 2013 19:07:07 -0400 | |
changeset 125554 | fe29b2ae604b7e7cf570bd8c57c8d21415789c9f |
parent 125553 | 8c6ec2899d894a7acb7abf8aa0c4b2852d7e25f7 |
child 125555 | b00eb1ef151749fe8eb31a61227127b1939f1023 |
push id | 24459 |
push user | emorley@mozilla.com |
push date | Wed, 20 Mar 2013 11:46:36 +0000 |
treeherder | mozilla-central@1d6fe70c79c5 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | luke |
bugs | 851964 |
milestone | 22.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/js/src/ion/AsmJS.h +++ b/js/src/ion/AsmJS.h @@ -8,17 +8,17 @@ #if !defined(jsion_asmjs_h__) #define jsion_asmjs_h__ // asm.js compilation is only available on desktop x86/x64 at the moment. // Don't panic, mobile support is coming soon. #if defined(JS_ION) && \ !defined(ANDROID) && \ (defined(JS_CPU_X86) || defined(JS_CPU_X64)) && \ - (defined(__linux__) || defined(XP_WIN)) + (defined(__linux__) || defined(XP_WIN) || defined(XP_MACOSX)) # define JS_ASMJS #endif namespace js { class SPSProfiler; class AsmJSModule; namespace frontend { struct TokenStream; struct ParseNode; }
--- a/js/src/ion/AsmJSModule.h +++ b/js/src/ion/AsmJSModule.h @@ -507,16 +507,21 @@ class AsmJSModule totalBytes_ = totalBytes; } uint8_t *functionCode() const { JS_ASSERT(code_); JS_ASSERT(uintptr_t(code_) % gc::PageSize == 0); return code_; } + bool pcIsInModule(void *pc) const { + return pc >= functionCode() && + pc < (functionCode() + functionBytes()); + } + void setOperationCallbackExit(uint8_t *ptr) { operationCallbackExit_ = ptr; } uint8_t *operationCallbackExit() const { return operationCallbackExit_; } void setIsLinked(Handle<ArrayBufferObject*> maybeHeap) {
--- a/js/src/ion/AsmJSSignalHandlers.cpp +++ b/js/src/ion/AsmJSSignalHandlers.cpp @@ -76,23 +76,16 @@ InnermostAsmJSActivation() { PerThreadData *threadData = TlsPerThreadData.get(); if (!threadData) return NULL; return threadData->asmJSActivationStackFromOwnerThread(); } -static bool -PCIsInModule(const AsmJSModule &module, void *pc) -{ - uint8_t *code = module.functionCode(); - return pc >= code && pc < (code + module.functionBytes()); -} - # if defined(JS_CPU_X64) template <class T> static void SetXMMRegToNaN(bool isFloat32, T *xmm_reg) { if (isFloat32) { JS_STATIC_ASSERT(sizeof(T) == 4 * sizeof(float)); float *floats = reinterpret_cast<float*>(xmm_reg); @@ -108,17 +101,17 @@ SetXMMRegToNaN(bool isFloat32, T *xmm_re } } // Perform a binary search on the projected offsets of the known heap accesses // in the module. static const AsmJSHeapAccess * LookupHeapAccess(const AsmJSModule &module, uint8_t *pc) { - JS_ASSERT(PCIsInModule(module, pc)); + JS_ASSERT(module.pcIsInModule(pc)); size_t targetOffset = pc - module.functionCode(); if (module.numHeapAccesses() == 0) return NULL; size_t low = 0; size_t high = module.numHeapAccesses() - 1; while (high - low >= 2) { @@ -216,30 +209,30 @@ HandleException(PEXCEPTION_POINTERS exce if (!activation) return false; uint8_t **ppc = ContextToPC(context); uint8_t *pc = *ppc; JS_ASSERT(pc == record->ExceptionAddress); const AsmJSModule &module = activation->module(); - if (!PCIsInModule(module, pc)) + if (!module.pcIsInModule(pc)) return false; if (record->NumberParameters < 2) return false; void *faultingAddress = (void*)record->ExceptionInformation[1]; // If we faulted trying to execute code in 'module', this must be an // operation callback (see TriggerOperationCallbackForAsmJSCode). Redirect // execution to a trampoline which will call js_HandleExecutionInterrupt. // The trampoline will jump to activation->resumePC if execution isn't // interrupted. - if (PCIsInModule(module, faultingAddress)) { + if (module.pcIsInModule(faultingAddress)) { activation->setResumePC(pc); *ppc = module.operationCallbackExit(); DWORD oldProtect; if (!VirtualProtect(module.functionCode(), module.functionBytes(), PAGE_EXECUTE, &oldProtect)) MOZ_CRASH(); return true; } @@ -285,16 +278,37 @@ AsmJSExceptionHandler(LPEXCEPTION_POINTE // No need to worry about calling other handlers, the OS does this for us. return EXCEPTION_CONTINUE_SEARCH; } # else // If not Windows, assume Unix # include <signal.h> # include <sys/mman.h> +#if defined(XP_MACOSX) +// Oh boy. We get to do fun things to make this work with the Mach +// exception handling installed by breakpad. Note that we still +// use and set up the signal() infrastructure; we'll fall back to +// that if breakpad is not active. +#include <mach/exc.h> +#include <mach/mig.h> +#include <mach/thread_act.h> + +extern "C" { + // this must be visible to dlsym(), and must have this name. + // Breakpad will call this before doing its thing if it exists + // in the process. + boolean_t BreakpadUserExceptionHandler64(exception_type_t extype, + mach_exception_data_t code, + mach_msg_type_number_t code_count, + mach_port_t thread) + __attribute__((visibility("default"))); +} +#endif + // Unfortunately, we still need OS-specific code to read/write to the thread // state via the mcontext_t. # if defined(__linux__) static uint8_t ** ContextToPC(mcontext_t &context) { # if defined(JS_CPU_X86) JS_STATIC_ASSERT(sizeof(context.gregs[REG_EIP]) == sizeof(void*)); @@ -362,59 +376,70 @@ ContextToPC(mcontext_t context) # else JS_STATIC_ASSERT(sizeof(context->__ss.__rip) == sizeof(void*)); return reinterpret_cast<uint8_t **>(&context->__ss.__rip); # endif } # if defined(JS_CPU_X64) static void +SetFloatStateRegisterToCoercedUndefined(x86_float_state64_t *fs, bool isFloat32, AnyRegister reg) +{ + switch (reg.fpu().code()) { + case JSC::X86Registers::xmm0: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm0); break; + case JSC::X86Registers::xmm1: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm1); break; + case JSC::X86Registers::xmm2: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm2); break; + case JSC::X86Registers::xmm3: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm3); break; + case JSC::X86Registers::xmm4: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm4); break; + case JSC::X86Registers::xmm5: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm5); break; + case JSC::X86Registers::xmm6: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm6); break; + case JSC::X86Registers::xmm7: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm7); break; + case JSC::X86Registers::xmm8: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm8); break; + case JSC::X86Registers::xmm9: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm9); break; + case JSC::X86Registers::xmm10: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm10); break; + case JSC::X86Registers::xmm11: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm11); break; + case JSC::X86Registers::xmm12: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm12); break; + case JSC::X86Registers::xmm13: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm13); break; + case JSC::X86Registers::xmm14: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm14); break; + case JSC::X86Registers::xmm15: SetXMMRegToNaN(isFloat32, &fs->__fpu_xmm15); break; + default: MOZ_CRASH(); + } +} + +static void +SetThreadStateRegisterToCoercedUndefined(x86_thread_state64_t *ss, bool isFloat32, AnyRegister reg) +{ + switch (reg.gpr().code()) { + case JSC::X86Registers::eax: ss->__rax = 0; break; + case JSC::X86Registers::ecx: ss->__rcx = 0; break; + case JSC::X86Registers::edx: ss->__rdx = 0; break; + case JSC::X86Registers::ebx: ss->__rbx = 0; break; + case JSC::X86Registers::esp: ss->__rsp = 0; break; + case JSC::X86Registers::ebp: ss->__rbp = 0; break; + case JSC::X86Registers::esi: ss->__rsi = 0; break; + case JSC::X86Registers::edi: ss->__rdi = 0; break; + case JSC::X86Registers::r8: ss->__r8 = 0; break; + case JSC::X86Registers::r9: ss->__r9 = 0; break; + case JSC::X86Registers::r10: ss->__r10 = 0; break; + case JSC::X86Registers::r11: ss->__r11 = 0; break; + case JSC::X86Registers::r12: ss->__r12 = 0; break; + case JSC::X86Registers::r13: ss->__r13 = 0; break; + case JSC::X86Registers::r14: ss->__r14 = 0; break; + case JSC::X86Registers::r15: ss->__r15 = 0; break; + default: MOZ_CRASH(); + } +} + +static void SetRegisterToCoercedUndefined(mcontext_t &context, bool isFloat32, AnyRegister reg) { - if (reg.isFloat()) { - switch (reg.fpu().code()) { - case JSC::X86Registers::xmm0: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm0); break; - case JSC::X86Registers::xmm1: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm1); break; - case JSC::X86Registers::xmm2: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm2); break; - case JSC::X86Registers::xmm3: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm3); break; - case JSC::X86Registers::xmm4: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm4); break; - case JSC::X86Registers::xmm5: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm5); break; - case JSC::X86Registers::xmm6: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm6); break; - case JSC::X86Registers::xmm7: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm7); break; - case JSC::X86Registers::xmm8: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm8); break; - case JSC::X86Registers::xmm9: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm9); break; - case JSC::X86Registers::xmm10: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm10); break; - case JSC::X86Registers::xmm11: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm11); break; - case JSC::X86Registers::xmm12: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm12); break; - case JSC::X86Registers::xmm13: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm13); break; - case JSC::X86Registers::xmm14: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm14); break; - case JSC::X86Registers::xmm15: SetXMMRegToNaN(isFloat32, &context->__fs.__fpu_xmm15); break; - default: MOZ_CRASH(); - } - } else { - switch (reg.gpr().code()) { - case JSC::X86Registers::eax: context->__ss.__rax = 0; break; - case JSC::X86Registers::ecx: context->__ss.__rcx = 0; break; - case JSC::X86Registers::edx: context->__ss.__rdx = 0; break; - case JSC::X86Registers::ebx: context->__ss.__rbx = 0; break; - case JSC::X86Registers::esp: context->__ss.__rsp = 0; break; - case JSC::X86Registers::ebp: context->__ss.__rbp = 0; break; - case JSC::X86Registers::esi: context->__ss.__rsi = 0; break; - case JSC::X86Registers::edi: context->__ss.__rdi = 0; break; - case JSC::X86Registers::r8: context->__ss.__r8 = 0; break; - case JSC::X86Registers::r9: context->__ss.__r9 = 0; break; - case JSC::X86Registers::r10: context->__ss.__r10 = 0; break; - case JSC::X86Registers::r11: context->__ss.__r11 = 0; break; - case JSC::X86Registers::r12: context->__ss.__r12 = 0; break; - case JSC::X86Registers::r13: context->__ss.__r13 = 0; break; - case JSC::X86Registers::r14: context->__ss.__r14 = 0; break; - case JSC::X86Registers::r15: context->__ss.__r15 = 0; break; - default: MOZ_CRASH(); - } - } + if (reg.isFloat()) + SetFloatStateRegisterToCoercedUndefined(&context->__fs, isFloat32, reg); + else + SetThreadStateRegisterToCoercedUndefined(&context->__ss, isFloat32, reg); } # endif # endif // end of OS-specific mcontext accessors // Be very cautious and default to not handling; we don't want to accidentally // silence real crashes from real bugs. static bool HandleSignal(int signum, siginfo_t *info, void *ctx) @@ -423,27 +448,27 @@ HandleSignal(int signum, siginfo_t *info if (!activation) return false; mcontext_t &context = reinterpret_cast<ucontext_t*>(ctx)->uc_mcontext; uint8_t **ppc = ContextToPC(context); uint8_t *pc = *ppc; const AsmJSModule &module = activation->module(); - if (!PCIsInModule(module, pc)) + if (!module.pcIsInModule(pc)) return false; void *faultingAddress = info->si_addr; // If we faulted trying to execute code in 'module', this must be an // operation callback (see TriggerOperationCallbackForAsmJSCode). Redirect // execution to a trampoline which will call js_HandleExecutionInterrupt. // The trampoline will jump to activation->resumePC if execution isn't // interrupted. - if (PCIsInModule(module, faultingAddress)) { + if (module.pcIsInModule(faultingAddress)) { activation->setResumePC(pc); *ppc = module.operationCallbackExit(); mprotect(module.functionCode(), module.functionBytes(), PROT_EXEC); return true; } # if defined(JS_CPU_X64) // These checks aren't necessary, but, since we can, check anyway to make @@ -494,16 +519,138 @@ AsmJSFaultHandler(int signum, siginfo_t exit(signum); // backstop } else if (sPrevHandler.sa_handler == SIG_DFL || sPrevHandler.sa_handler == SIG_IGN) { sigaction(signum, &sPrevHandler, NULL); } else { sPrevHandler.sa_handler(signum); exit(signum); // backstop } } + +#if defined(XP_MACOSX) + +// This is the Mach-exceptions-in-conjunction-with-breakpad handler. +// There's an unfortunate amount of code duplicated from HandleSignal +// above, but there are enough differences in paths that unifying +// them is a bit of a pain (differences in how you get or set register +// state, how you look up the AsmJS Activation, etc.) +boolean_t +BreakpadUserExceptionHandler64(exception_type_t extype, + mach_exception_data_t code, + mach_msg_type_number_t code_count, + mach_port_t thread) +{ + // we only care about BAD_ACCESS + if (extype != EXC_BAD_ACCESS) + return false; + + kern_return_t kr; + +# if defined(JS_CPU_X64) + x86_thread_state64_t state; + unsigned int count = x86_THREAD_STATE64_COUNT; + unsigned int state_flavor = x86_THREAD_STATE64; + uint8_t **ppc = reinterpret_cast<uint8_t**>(&state.__rip); +#else + x86_thread_state32_t state; + unsigned int count = x86_THREAD_STATE32_COUNT; + unsigned int state_flavor = x86_THREAD_STATE32; + uint8_t **ppc = reinterpret_cast<uint8_t**>(&state.__eip); +#endif + + kr = thread_get_state(thread, state_flavor, (thread_state_t) &state, &count); + if (kr != KERN_SUCCESS) { + // couldn't get state? welp, at least we'll get a breakpad stack! + return false; + } + + uint8_t *pc = *ppc; + + AsmJSActivation *activation = JSRuntime::findAsmJSActivationForPC(pc); + if (!activation) { + return false; + } + + // we know pc is in module because we found it earlier by the pc + const AsmJSModule &module = activation->module(); + + void *faultingAddress = reinterpret_cast<void*>(code[1]); + + // If we faulted trying to execute code in 'module', this must be an + // operation callback (see TriggerOperationCallbackForAsmJSCode). Redirect + // execution to a trampoline which will call js_HandleExecutionInterrupt. + // The trampoline will jump to activation->resumePC if execution isn't + // interrupted. + if (module.pcIsInModule(faultingAddress)) { + activation->setResumePC(pc); + *ppc = module.operationCallbackExit(); + mprotect(module.functionCode(), module.functionBytes(), PROT_EXEC); + + // count better not have been modified since we called thread_get_state above + kr = thread_set_state(thread, state_flavor, (thread_state_t) &state, count); + if (kr != KERN_SUCCESS) { + return false; + } + + return true; + } + +# if defined(JS_CPU_X64) + // These checks aren't necessary, but, since we can, check anyway to make + // sure we aren't covering up a real bug. + if (!module.maybeHeap() || + faultingAddress < module.maybeHeap() || + faultingAddress >= module.maybeHeap() + AsmJSBufferProtectedSize) + { + return false; + } + + const AsmJSHeapAccess *heapAccess = LookupHeapAccess(module, pc); + if (!heapAccess) + return false; + + // We now know that this is an out-of-bounds access made by an asm.js + // load/store that we should handle. If this is a load, assign the + // JS-defined result value to the destination register (ToInt32(undefined) + // or ToNumber(undefined), determined by the type of the destination + // register) and set the PC to the next op. Upon return from the handler, + // execution will resume at this next PC. + if (heapAccess->isLoad()) { + AnyRegister reg = heapAccess->loadedReg(); + if (reg.isFloat()) { + // We have to grab the floating point registers + x86_float_state64_t fstate; + count = x86_FLOAT_STATE64_COUNT; + kr = thread_get_state(thread, x86_FLOAT_STATE64, (thread_state_t) &fstate, &count); + if (kr != KERN_SUCCESS) + return false; + + SetFloatStateRegisterToCoercedUndefined(&fstate, heapAccess->isFloat32Load(), reg); + + kr = thread_set_state(thread, x86_FLOAT_STATE64, (thread_state_t) &fstate, x86_FLOAT_STATE64_COUNT); + if (kr != KERN_SUCCESS) + return false; + } else { + SetThreadStateRegisterToCoercedUndefined(&state, heapAccess->isFloat32Load(), reg); + } + } + + *ppc += heapAccess->opLength(); + + kr = thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t) &state, x86_THREAD_STATE64_COUNT); + if (kr != KERN_SUCCESS) + return false; + + return true; +# else + return false; +# endif +} +#endif + # endif #endif // JS_ASMJS bool EnsureAsmJSSignalHandlersInstalled() { #if defined(JS_ASMJS) SignalMutex::Lock lock;
--- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -84,18 +84,19 @@ #include "vm/Stack-inl.h" #include "vm/String-inl.h" #if ENABLE_YARR_JIT #include "assembler/jit/ExecutableAllocator.h" #include "methodjit/Logging.h" #endif -#ifdef JS_METHODJIT +#ifdef JS_ION #include "ion/Ion.h" +#include "ion/AsmJSModule.h" #endif using namespace js; using namespace js::gc; using namespace js::types; using mozilla::Maybe; @@ -962,25 +963,59 @@ JSRuntime::init(uint32_t maxbytes) if (useHelperThreads() && !sourceCompressorThread.init()) return false; #endif if (!evalCache.init()) return false; nativeStackBase = GetNativeStackBase(); + +#ifdef XP_MACOSX + if (!runtimeListLock_) { + runtimeListLock_ = PR_NewLock(); + runtimeListHead_ = NULL; + } + + PR_Lock(runtimeListLock_); + runtimeListNext_ = runtimeListHead_; + runtimeListHead_ = this; + PR_Unlock(runtimeListLock_); +#endif + return true; } JSRuntime::~JSRuntime() { #ifdef JS_THREADSAFE clearOwnerThread(); #endif +#ifdef XP_MACOSX + { + PR_Lock(runtimeListLock_); + JSRuntime *r = runtimeListHead_; + // find ourselves to remove us from the list + if (r == this) { + runtimeListHead_ = runtimeListNext_; + } else { + // look for the node that has us in the next + // pointer + while (r && r->runtimeListNext_ != this) + r = r->runtimeListNext_; + JS_ASSERT(r); + r->runtimeListNext_ = runtimeListNext_; + } + + runtimeListNext_ = NULL; + PR_Unlock(runtimeListLock_); + } +#endif + /* * Even though all objects in the compartment are dead, we may have keep * some filenames around because of gcKeepAtoms. */ FreeScriptData(this); #ifdef JS_THREADSAFE # ifdef JS_ION
--- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -44,16 +44,17 @@ #include "jsopcode.h" #include "jspubtd.h" #include "jsscript.h" #include "jsstr.h" #include "jsworkers.h" #ifdef JS_ION #include "ion/Ion.h" #include "ion/IonFrames.h" +#include "ion/AsmJSModule.h" #endif #ifdef JS_METHODJIT # include "assembler/assembler/MacroAssembler.h" # include "methodjit/MethodJIT.h" #endif #include "gc/Marking.h" #include "js/CharacterEncoding.h" @@ -1401,16 +1402,41 @@ JSRuntime::onOutOfMemory(void *p, size_t p = js_realloc(p, nbytes); if (p) return p; if (cx) js_ReportOutOfMemory(cx); return NULL; } +#ifdef XP_MACOSX +PRLock *JSRuntime::runtimeListLock_ = NULL; +JSRuntime *JSRuntime::runtimeListHead_ = NULL; + +AsmJSActivation* +JSRuntime::findAsmJSActivationForPC(void *pc) +{ + PR_Lock(runtimeListLock_); + + AsmJSActivation *activation = NULL; + + JSRuntime *r = runtimeListHead_; + while (r) { + activation = r->mainThread.asmJSActivationStackFromAnyThread(); + if (activation && activation->module().pcIsInModule(pc)) + break; + activation = NULL; + r = r->runtimeListNext_; + } + + PR_Unlock(runtimeListLock_); + return activation; +} +#endif + void JSContext::purge() { if (!activeCompilations) { js_delete(parseMapPool_); parseMapPool_ = NULL; } }
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -672,16 +672,28 @@ struct JSRuntime : js::RuntimeFriendFiel void *ownerThread_; public: #else public: void abortIfWrongThread() const {} void assertValidThread() const {} #endif +#ifdef XP_MACOSX + // On OSX, we need a way to find all the live runtimes from an arbitrary + // thread (specifically, the exception thread, for fixing up Odin + // accesses). + private: + static PRLock *runtimeListLock_; + static JSRuntime *runtimeListHead_; + JSRuntime *runtimeListNext_; + public: + static js::AsmJSActivation* findAsmJSActivationForPC(void *pc); +#endif + /* Keeper of the contiguous stack used by all contexts in this thread. */ js::StackSpace stackSpace; /* Temporary arena pool used while compiling and decompiling. */ static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 4 * 1024; js::LifoAlloc tempLifoAlloc; /*