From 50c976c56d3d2a9977a465e6821147f59af80f01 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Wed, 18 Jun 2014 08:57:19 -0500 Subject: [PATCH] Bug 1026877 - OdinMonkey: tidy up AsmJSModule, part 1 (r=bbouvier) --HG-- extra : rebase_source : c0c4face5eb8aa70e1f9cc89fc13e3c7522a1f88 --- js/src/jit/AsmJS.cpp | 129 +------------------- js/src/jit/AsmJSLink.cpp | 21 +--- js/src/jit/AsmJSModule.cpp | 186 ++++++++++++++++++++++++++++- js/src/jit/AsmJSModule.h | 56 ++------- js/src/jit/AsmJSSignalHandlers.cpp | 33 +---- 5 files changed, 202 insertions(+), 223 deletions(-) diff --git a/js/src/jit/AsmJS.cpp b/js/src/jit/AsmJS.cpp index c43dcab842c5..e7e60198e125 100644 --- a/js/src/jit/AsmJS.cpp +++ b/js/src/jit/AsmJS.cpp @@ -1506,86 +1506,15 @@ class MOZ_STACK_CLASS ModuleCompiler bool finish(ScopedJSDeletePtr *module) { - module_->initFuncEnd(tokenStream().currentToken().pos.end, - tokenStream().peekTokenPos().end); masm_.finish(); if (masm_.oom()) return false; - module_->assignCallSites(masm_.extractCallSites()); - module_->assignHeapAccesses(masm_.extractAsmJSHeapAccesses()); - -#if defined(JS_CODEGEN_ARM) - // Now that compilation has finished, we need to update offsets to - // reflect actual offsets (an ARM distinction). - for (unsigned i = 0; i < module_->numHeapAccesses(); i++) { - AsmJSHeapAccess &a = module_->heapAccess(i); - a.setOffset(masm_.actualOffset(a.offset())); - } - for (unsigned i = 0; i < module_->numCallSites(); i++) { - CallSite &c = module_->callSite(i); - c.setReturnAddressOffset(masm_.actualOffset(c.returnAddressOffset())); - } -#endif - - // The returned memory is owned by module_. - if (!module_->allocateAndCopyCode(cx_, masm_)) + if (!module_->finish(cx_, tokenStream(), masm_, interruptLabel_)) return false; - // c.f. JitCode::copyFrom - JS_ASSERT(masm_.jumpRelocationTableBytes() == 0); - JS_ASSERT(masm_.dataRelocationTableBytes() == 0); - JS_ASSERT(masm_.preBarrierTableBytes() == 0); - JS_ASSERT(!masm_.hasEnteredExitFrame()); - -#if defined(MOZ_VTUNE) || defined(JS_ION_PERF) - // Fix up the code offsets. Note the endCodeOffset should not be - // filtered through 'actualOffset' as it is generated using 'size()' - // rather than a label. - for (unsigned i = 0; i < module_->numProfiledFunctions(); i++) { - AsmJSModule::ProfiledFunction &func = module_->profiledFunction(i); - func.pod.startCodeOffset = masm_.actualOffset(func.pod.startCodeOffset); - } -#endif - -#ifdef JS_ION_PERF - for (unsigned i = 0; i < module_->numPerfBlocksFunctions(); i++) { - AsmJSModule::ProfiledBlocksFunction &func = module_->perfProfiledBlocksFunction(i); - func.pod.startCodeOffset = masm_.actualOffset(func.pod.startCodeOffset); - func.endInlineCodeOffset = masm_.actualOffset(func.endInlineCodeOffset); - BasicBlocksVector &basicBlocks = func.blocks; - for (uint32_t i = 0; i < basicBlocks.length(); i++) { - Record &r = basicBlocks[i]; - r.startOffset = masm_.actualOffset(r.startOffset); - r.endOffset = masm_.actualOffset(r.endOffset); - } - } -#endif - - module_->setInterruptOffset(masm_.actualOffset(interruptLabel_.offset())); - - // CodeLabels produced during codegen - for (size_t i = 0; i < masm_.numCodeLabels(); i++) { - CodeLabel src = masm_.codeLabel(i); - int32_t labelOffset = src.dest()->offset(); - int32_t targetOffset = masm_.actualOffset(src.src()->offset()); - // The patched uses of a label embed a linked list where the - // to-be-patched immediate is the offset of the next to-be-patched - // instruction. - while (labelOffset != LabelBase::INVALID_OFFSET) { - size_t patchAtOffset = masm_.labelOffsetToPatchOffset(labelOffset); - AsmJSModule::RelativeLink link(AsmJSModule::RelativeLink::CodeLabel); - link.patchAtOffset = patchAtOffset; - link.targetOffset = targetOffset; - if (!module_->addRelativeLink(link)) - return false; - - labelOffset = Assembler::extractCodeLabelOffset(module_->codeBase() + - patchAtOffset); - } - } - - // Function-pointer-table entries + // Finally, convert all the function-pointer table elements into + // RelativeLinks that will be patched by AsmJSModule::staticallyLink. for (unsigned tableIndex = 0; tableIndex < funcPtrTables_.length(); tableIndex++) { FuncPtrTable &table = funcPtrTables_[tableIndex]; unsigned tableBaseOffset = module_->offsetOfGlobalData() + table.globalDataOffset(); @@ -1598,58 +1527,6 @@ class MOZ_STACK_CLASS ModuleCompiler } } -#if defined(JS_CODEGEN_X86) - // Global data accesses in x86 need to be patched with the absolute - // address of the global. Globals are allocated sequentially after the - // code section so we can just use an RelativeLink. - for (unsigned i = 0; i < masm_.numAsmJSGlobalAccesses(); i++) { - AsmJSGlobalAccess a = masm_.asmJSGlobalAccess(i); - AsmJSModule::RelativeLink link(AsmJSModule::RelativeLink::InstructionImmediate); - link.patchAtOffset = masm_.labelOffsetToPatchOffset(a.patchAt.offset()); - link.targetOffset = module_->offsetOfGlobalData() + a.globalDataOffset; - if (!module_->addRelativeLink(link)) - return false; - } -#endif - -#if defined(JS_CODEGEN_X64) - // Global data accesses on x64 use rip-relative addressing and thus do - // not need patching after deserialization. - uint8_t *code = module_->codeBase(); - for (unsigned i = 0; i < masm_.numAsmJSGlobalAccesses(); i++) { - AsmJSGlobalAccess a = masm_.asmJSGlobalAccess(i); - masm_.patchAsmJSGlobalAccess(a.patchAt, code, module_->globalData(), a.globalDataOffset); - } -#endif - -#if defined(JS_CODEGEN_MIPS) - // On MIPS we need to update all the long jumps because they contain an - // absolute adress. - for (size_t i = 0; i < masm_.numLongJumps(); i++) { - uint32_t patchAtOffset = masm_.longJump(i); - - AsmJSModule::RelativeLink link(AsmJSModule::RelativeLink::InstructionImmediate); - link.patchAtOffset = patchAtOffset; - - InstImm *inst = (InstImm *)(module_->codeBase() + patchAtOffset); - link.targetOffset = Assembler::extractLuiOriValue(inst, inst->next()) - - (uint32_t)module_->codeBase(); - - if (!module_->addRelativeLink(link)) - return false; - } -#endif - - // Absolute links - for (size_t i = 0; i < masm_.numAsmJSAbsoluteLinks(); i++) { - AsmJSAbsoluteLink src = masm_.asmJSAbsoluteLink(i); - AsmJSModule::AbsoluteLink link; - link.patchAt = CodeOffsetLabel(masm_.actualOffset(src.patchAt.offset())); - link.target = src.target; - if (!module_->addAbsoluteLink(link)) - return false; - } - *module = module_.forget(); return true; } diff --git a/js/src/jit/AsmJSLink.cpp b/js/src/jit/AsmJSLink.cpp index dd4285671351..1cc12c466ced 100644 --- a/js/src/jit/AsmJSLink.cpp +++ b/js/src/jit/AsmJSLink.cpp @@ -6,7 +6,6 @@ #include "jit/AsmJSLink.h" -#include "mozilla/BinarySearch.h" #include "mozilla/PodOperations.h" #ifdef MOZ_VTUNE @@ -32,7 +31,6 @@ using namespace js; using namespace js::jit; -using mozilla::BinarySearch; using mozilla::IsNaN; using mozilla::PodZero; @@ -93,30 +91,15 @@ AsmJSFrameIterator::operator++() settle(ReturnAddressForJitCall(sp_)); } -struct GetCallSite -{ - const AsmJSModule &module; - explicit GetCallSite(const AsmJSModule &module) : module(module) {} - uint32_t operator[](size_t index) const { - return module.callSite(index).returnAddressOffset(); - } -}; - void AsmJSFrameIterator::settle(uint8_t *returnAddress) { - uint32_t target = returnAddress - module_->codeBase(); - size_t lowerBound = 0; - size_t upperBound = module_->numCallSites(); - - size_t match; - if (!BinarySearch(GetCallSite(*module_), lowerBound, upperBound, target, &match)) { + callsite_ = module_->lookupCallSite(returnAddress); + if (!callsite_ || callsite_->isEntry()) { module_ = nullptr; return; } - callsite_ = &module_->callSite(match); - if (callsite_->isEntry()) { module_ = nullptr; return; diff --git a/js/src/jit/AsmJSModule.cpp b/js/src/jit/AsmJSModule.cpp index 0d9b4bbe2aef..c0ee37f9abb2 100644 --- a/js/src/jit/AsmJSModule.cpp +++ b/js/src/jit/AsmJSModule.cpp @@ -12,6 +12,7 @@ # include #endif +#include "mozilla/BinarySearch.h" #include "mozilla/Compression.h" #include "mozilla/PodOperations.h" #include "mozilla/TaggedAnonymousMemory.h" @@ -35,6 +36,7 @@ using namespace js; using namespace jit; using namespace frontend; +using mozilla::BinarySearch; using mozilla::PodCopy; using mozilla::PodEqual; using mozilla::Compression::LZ4; @@ -70,6 +72,54 @@ AsmJSModule::initHeap(Handle heap, JSContext *cx) #endif } +struct CallSiteRetAddrOffset +{ + const CallSiteVector &callSites; + explicit CallSiteRetAddrOffset(const CallSiteVector &callSites) : callSites(callSites) {} + uint32_t operator[](size_t index) const { + return callSites[index].returnAddressOffset(); + } +}; + +const CallSite * +AsmJSModule::lookupCallSite(uint8_t *returnAddress) const +{ + uint32_t target = returnAddress - code_; + size_t lowerBound = 0; + size_t upperBound = callSites_.length(); + + size_t match; + if (!BinarySearch(CallSiteRetAddrOffset(callSites_), lowerBound, upperBound, target, &match)) + return nullptr; + + return &callSites_[match]; +} + +struct HeapAccessOffset +{ + const AsmJSHeapAccessVector &accesses; + explicit HeapAccessOffset(const AsmJSHeapAccessVector &accesses) : accesses(accesses) {} + uintptr_t operator[](size_t index) const { + return accesses[index].offset(); + } +}; + +const AsmJSHeapAccess * +AsmJSModule::lookupHeapAccess(uint8_t *pc) const +{ + JS_ASSERT(containsPC(pc)); + + uint32_t target = pc - code_; + size_t lowerBound = 0; + size_t upperBound = heapAccesses_.length(); + + size_t match; + if (!BinarySearch(HeapAccessOffset(heapAccesses_), lowerBound, upperBound, target, &match)) + return nullptr; + + return &heapAccesses_[match]; +} + static uint8_t * AllocateExecutableMemory(ExclusiveContext *cx, size_t totalBytes) { @@ -105,9 +155,15 @@ DeallocateExecutableMemory(uint8_t *code, size_t totalBytes) } bool -AsmJSModule::allocateAndCopyCode(ExclusiveContext *cx, MacroAssembler &masm) +AsmJSModule::finish(ExclusiveContext *cx, TokenStream &tokenStream, MacroAssembler &masm, + const Label &interruptLabel) { - JS_ASSERT(!code_); + uint32_t endBeforeCurly = tokenStream.currentToken().pos.end; + uint32_t endAfterCurly = tokenStream.peekTokenPos().end; + JS_ASSERT(endBeforeCurly >= offsetToEndOfUseAsm_); + JS_ASSERT(endAfterCurly >= offsetToEndOfUseAsm_); + pod.funcLength_ = endBeforeCurly - funcStart_; + pod.funcLengthWithRightBrace_ = endAfterCurly - funcStart_; // The global data section sits immediately after the executable (and // other) data allocated by the MacroAssembler, so ensure it is @@ -118,12 +174,138 @@ AsmJSModule::allocateAndCopyCode(ExclusiveContext *cx, MacroAssembler &masm) // units of pages. pod.totalBytes_ = AlignBytes(pod.codeBytes_ + globalDataBytes(), AsmJSPageSize); + JS_ASSERT(!code_); code_ = AllocateExecutableMemory(cx, pod.totalBytes_); if (!code_) return false; + // Copy the code from the MacroAssembler into its final resting place in the + // AsmJSModule. JS_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0); masm.executableCopy(code_); + + // c.f. JitCode::copyFrom + JS_ASSERT(masm.jumpRelocationTableBytes() == 0); + JS_ASSERT(masm.dataRelocationTableBytes() == 0); + JS_ASSERT(masm.preBarrierTableBytes() == 0); + JS_ASSERT(!masm.hasEnteredExitFrame()); + + // Copy over metadata, making sure to update all offsets on ARM. + + staticLinkData_.interruptExitOffset = masm.actualOffset(interruptLabel.offset()); + + // Heap-access metadata used for link-time patching and fault-handling. + heapAccesses_ = masm.extractAsmJSHeapAccesses(); + + // Call-site metadata used for stack unwinding. + callSites_ = masm.extractCallSites(); + +#if defined(JS_CODEGEN_ARM) + // ARM requires the offsets to be updated. + for (size_t i = 0; i < heapAccesses_.length(); i++) { + AsmJSHeapAccess &a = heapAccesses_[i]; + a.setOffset(masm.actualOffset(a.offset())); + } + for (size_t i = 0; i < callSites_.length(); i++) { + CallSite &c = callSites_[i]; + c.setReturnAddressOffset(masm.actualOffset(c.returnAddressOffset())); + } +#endif + + // Absolute link metadata: absolute addresses that refer to some fixed + // address in the address space. + for (size_t i = 0; i < masm.numAsmJSAbsoluteLinks(); i++) { + AsmJSAbsoluteLink src = masm.asmJSAbsoluteLink(i); + AbsoluteLink link; + link.patchAt = CodeOffsetLabel(masm.actualOffset(src.patchAt.offset())); + link.target = src.target; + if (!staticLinkData_.absoluteLinks.append(link)) + return false; + } + + // Relative link metadata: absolute addresses that refer to another point within + // the asm.js module. + + // CodeLabels are used for switch cases and loads from doubles in the + // constant pool. + for (size_t i = 0; i < masm.numCodeLabels(); i++) { + CodeLabel src = masm.codeLabel(i); + int32_t labelOffset = src.dest()->offset(); + int32_t targetOffset = masm.actualOffset(src.src()->offset()); + // The patched uses of a label embed a linked list where the + // to-be-patched immediate is the offset of the next to-be-patched + // instruction. + while (labelOffset != LabelBase::INVALID_OFFSET) { + size_t patchAtOffset = masm.labelOffsetToPatchOffset(labelOffset); + RelativeLink link(RelativeLink::CodeLabel); + link.patchAtOffset = patchAtOffset; + link.targetOffset = targetOffset; + if (!staticLinkData_.relativeLinks.append(link)) + return false; + + labelOffset = Assembler::extractCodeLabelOffset(code_ + patchAtOffset); + } + } + +#if defined(JS_CODEGEN_X86) + // Global data accesses in x86 need to be patched with the absolute + // address of the global. Globals are allocated sequentially after the + // code section so we can just use an RelativeLink. + for (size_t i = 0; i < masm.numAsmJSGlobalAccesses(); i++) { + AsmJSGlobalAccess a = masm.asmJSGlobalAccess(i); + RelativeLink link(RelativeLink::InstructionImmediate); + link.patchAtOffset = masm.labelOffsetToPatchOffset(a.patchAt.offset()); + link.targetOffset = offsetOfGlobalData() + a.globalDataOffset; + if (!staticLinkData_.relativeLinks.append(link)) + return false; + } +#endif + +#if defined(JS_CODEGEN_MIPS) + // On MIPS we need to update all the long jumps because they contain an + // absolute adress. + for (size_t i = 0; i < masm.numLongJumps(); i++) { + RelativeLink link(RelativeLink::InstructionImmediate); + link.patchAtOffset = masm.longJump(i); + InstImm *inst = (InstImm *)(code_ + masm.longJump(i)); + link.targetOffset = Assembler::extractLuiOriValue(inst, inst->next()) - (uint32_t)code_; + if (!staticLinkData_.relativeLinks.append(link)) + return false; + } +#endif + +#if defined(JS_CODEGEN_X64) + // Global data accesses on x64 use rip-relative addressing and thus do + // not need patching after deserialization. + for (size_t i = 0; i < masm.numAsmJSGlobalAccesses(); i++) { + AsmJSGlobalAccess a = masm.asmJSGlobalAccess(i); + masm.patchAsmJSGlobalAccess(a.patchAt, code_, globalData(), a.globalDataOffset); + } +#endif + +#if defined(MOZ_VTUNE) || defined(JS_ION_PERF) + // Fix up the code offsets. Note the endCodeOffset should not be + // filtered through 'actualOffset' as it is generated using 'size()' + // rather than a label. + for (size_t i = 0; i < profiledFunctions_.length(); i++) { + ProfiledFunction &pf = profiledFunctions_[i]; + pf.pod.startCodeOffset = masm.actualOffset(pf.pod.startCodeOffset); + } +#endif +#ifdef JS_ION_PERF + for (size_t i = 0; i < perfProfiledBlocksFunctions_.length(); i++) { + ProfiledBlocksFunction &pbf = perfProfiledBlocksFunctions_[i]; + pbf.pod.startCodeOffset = masm.actualOffset(pbf.pod.startCodeOffset); + pbf.endInlineCodeOffset = masm.actualOffset(pbf.endInlineCodeOffset); + BasicBlocksVector &basicBlocks = pbf.blocks; + for (uint32_t i = 0; i < basicBlocks.length(); i++) { + Record &r = basicBlocks[i]; + r.startOffset = masm.actualOffset(r.startOffset); + r.endOffset = masm.actualOffset(r.endOffset); + } + } +#endif + return true; } diff --git a/js/src/jit/AsmJSModule.h b/js/src/jit/AsmJSModule.h index dcc52b16674c..279bb72a7136 100644 --- a/js/src/jit/AsmJSModule.h +++ b/js/src/jit/AsmJSModule.h @@ -25,6 +25,8 @@ namespace js { +namespace frontend { class TokenStream; } + // These EcmaScript-defined coercions form the basis of the asm.js type system. enum AsmJSCoercion { @@ -544,24 +546,16 @@ class AsmJSModule return scriptSource_; } - /* - * funcStart() refers to the offset in the ScriptSource to the beginning - * of the function. If the function has been created with the Function - * constructor, this will be the first character in the function source. - * Otherwise, it will be the opening parenthesis of the arguments list. - */ + // funcStart() refers to the offset in the ScriptSource to the beginning + // of the function. If the function has been created with the Function + // constructor, this will be the first character in the function source. + // Otherwise, it will be the opening parenthesis of the arguments list. uint32_t funcStart() const { return funcStart_; } uint32_t offsetToEndOfUseAsm() const { return offsetToEndOfUseAsm_; } - void initFuncEnd(uint32_t endBeforeCurly, uint32_t endAfterCurly) { - JS_ASSERT(endBeforeCurly >= offsetToEndOfUseAsm_); - JS_ASSERT(endAfterCurly >= offsetToEndOfUseAsm_); - pod.funcLength_ = endBeforeCurly - funcStart_; - pod.funcLengthWithRightBrace_ = endAfterCurly - funcStart_; - } uint32_t funcEndBeforeCurly() const { return funcStart_ + pod.funcLength_; } @@ -817,31 +811,8 @@ class AsmJSModule return pc >= code_ && pc < (code_ + functionBytes()); } - void assignHeapAccesses(jit::AsmJSHeapAccessVector &&accesses) { - heapAccesses_ = Move(accesses); - } - unsigned numHeapAccesses() const { - return heapAccesses_.length(); - } - const jit::AsmJSHeapAccess &heapAccess(unsigned i) const { - return heapAccesses_[i]; - } - jit::AsmJSHeapAccess &heapAccess(unsigned i) { - return heapAccesses_[i]; - } - - void assignCallSites(jit::CallSiteVector &&callsites) { - callSites_ = Move(callsites); - } - unsigned numCallSites() const { - return callSites_.length(); - } - const jit::CallSite &callSite(unsigned i) const { - return callSites_[i]; - } - jit::CallSite &callSite(unsigned i) { - return callSites_[i]; - } + const jit::CallSite *lookupCallSite(uint8_t *returnAddress) const; + const jit::AsmJSHeapAccess *lookupHeapAccess(uint8_t *pc) const; void initHeap(Handle heap, JSContext *cx); @@ -853,19 +824,12 @@ class AsmJSModule return pod.minHeapLength_; } - bool allocateAndCopyCode(ExclusiveContext *cx, jit::MacroAssembler &masm); + bool finish(ExclusiveContext *cx, frontend::TokenStream &tokenStream, jit::MacroAssembler &masm, + const jit::Label &interruptLabel); - // StaticLinkData setters (called after finishing compilation, before - // staticLink). bool addRelativeLink(RelativeLink link) { return staticLinkData_.relativeLinks.append(link); } - bool addAbsoluteLink(AbsoluteLink link) { - return staticLinkData_.absoluteLinks.append(link); - } - void setInterruptOffset(uint32_t offset) { - staticLinkData_.interruptExitOffset = offset; - } void restoreToInitialState(ArrayBufferObject *maybePrevBuffer, ExclusiveContext *cx); void setAutoFlushICacheRange(); diff --git a/js/src/jit/AsmJSSignalHandlers.cpp b/js/src/jit/AsmJSSignalHandlers.cpp index cf9dc728a195..58486976da59 100644 --- a/js/src/jit/AsmJSSignalHandlers.cpp +++ b/js/src/jit/AsmJSSignalHandlers.cpp @@ -6,8 +6,6 @@ #include "jit/AsmJSSignalHandlers.h" -#include "mozilla/BinarySearch.h" - #include "assembler/assembler/MacroAssembler.h" #include "jit/AsmJSModule.h" @@ -213,31 +211,6 @@ SetXMMRegToNaN(bool isFloat32, T *xmm_reg) dbls[1] = 0; } } - -struct GetHeapAccessOffset -{ - const AsmJSModule &module; - explicit GetHeapAccessOffset(const AsmJSModule &module) : module(module) {} - uintptr_t operator[](size_t index) const { - return module.heapAccess(index).offset(); - } -}; - -// 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(module.containsPC(pc)); - - uintptr_t pcOff = pc - module.codeBase(); - - size_t match; - if (!BinarySearch(GetHeapAccessOffset(module), 0, module.numHeapAccesses(), pcOff, &match)) - return nullptr; - - return &module.heapAccess(match); -} #endif #if defined(XP_WIN) @@ -507,7 +480,7 @@ HandleException(PEXCEPTION_POINTERS exception) return false; } - const AsmJSHeapAccess *heapAccess = LookupHeapAccess(module, pc); + const AsmJSHeapAccess *heapAccess = module.lookupHeapAccess(pc); if (!heapAccess) return false; @@ -713,7 +686,7 @@ HandleMachException(JSRuntime *rt, const ExceptionRequest &request) return false; } - const AsmJSHeapAccess *heapAccess = LookupHeapAccess(module, pc); + const AsmJSHeapAccess *heapAccess = module.lookupHeapAccess(pc); if (!heapAccess) return false; @@ -960,7 +933,7 @@ HandleSignal(int signum, siginfo_t *info, void *ctx) return false; } - const AsmJSHeapAccess *heapAccess = LookupHeapAccess(module, pc); + const AsmJSHeapAccess *heapAccess = module.lookupHeapAccess(pc); if (!heapAccess) return false;