Backed out 4 changesets (bug 1030446) for b2g non-unified build bustage on a CLOSED TREE

Backed out changeset d925c77d4b3f (bug 1030446)
Backed out changeset 988f19a5ea1f (bug 1030446)
Backed out changeset 6e87a7cf7eac (bug 1030446)
Backed out changeset 1c761a455bbf (bug 1030446)
This commit is contained in:
Wes Kocher 2014-06-27 17:34:19 -07:00
parent bedbfd70db
commit 717a1c8217
31 changed files with 436 additions and 396 deletions

View File

@ -98,9 +98,7 @@ assertThrowsValue(function() { f(8,2.4) }, 2.4+36);
assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var identity=imp.identity; function g(x) { x=+x; return +identity(x) } return g'), null, imp)(13.37), 13.37);
// Test asm.js => ion paths
setJitCompilerOption("ion.usecount.trigger", 10);
setJitCompilerOption("baseline.usecount.trigger", 0);
setJitCompilerOption("offthread-compilation.enable", 0);
setJitCompilerOption("ion.usecount.trigger", 20);
// In registers on x64 and ARM, on the stack for x86
function ffiIntFew(a,b,c,d) { return d+1 }
@ -111,32 +109,32 @@ for (var i = 0; i < 40; i++)
// Stack and registers for x64 and ARM, stack for x86
function ffiIntMany(a,b,c,d,e,f,g,h,i,j) { return j+1 }
var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function f(i) { i=i|0; return ffi(i|0,(i+1)|0,(i+2)|0,(i+3)|0,(i+4)|0,(i+5)|0,(i+6)|0,(i+7)|0,(i+8)|0,(i+9)|0)|0 } return f'), null, {ffi:ffiIntMany});
for (var i = 0; i < 15; i++)
for (var i = 0; i < 40; i++)
assertEq(f(i), i+10);
// In registers on x64 and ARM, on the stack for x86
function ffiDoubleFew(a,b,c,d) { return d+1 }
var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function f(i) { i=+i; return +ffi(i,i+1.0,i+2.0,i+3.0) } return f'), null, {ffi:ffiDoubleFew});
for (var i = 0; i < 15; i++)
for (var i = 0; i < 40; i++)
assertEq(f(i), i+4);
// Stack and registers for x64 and ARM, stack for x86
function ffiDoubleMany(a,b,c,d,e,f,g,h,i,j) { return j+1 }
var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function f(i) { i=+i; return +ffi(i,i+1.0,i+2.0,i+3.0,i+4.0,i+5.0,i+6.0,i+7.0,i+8.0,i+9.0) } return f'), null, {ffi:ffiDoubleMany});
for (var i = 0; i < 15; i++)
for (var i = 0; i < 40; i++)
assertEq(f(i), i+10);
// Test the throw path
function ffiThrow(n) { if (n == 14) throw 'yolo'; }
function ffiThrow(n) { if (n == 38) throw 'yolo'; }
var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function f(i) { i=i|0; ffi(i >> 0); } return f'), null, {ffi:ffiThrow});
var i = 0;
try {
for (; i < 15; i++)
for (; i < 40; i++)
f(i);
throw 'assume unreachable';
} catch (e) {
assertEq(e, 'yolo');
assertEq(i, 14);
assertEq(i, 38);
}
// OOL conversion paths

View File

@ -17,14 +17,10 @@ function dumpStack()
stack = new Error().stack
}
setJitCompilerOption("ion.usecount.trigger", 10);
setJitCompilerOption("baseline.usecount.trigger", 0);
setJitCompilerOption("offthread-compilation.enable", 0);
var callFFI = asmCompile('global', 'ffis', USE_ASM + "var ffi=ffis.ffi; function f() { return ffi()|0 } return f");
var f = asmLink(callFFI, null, {ffi:dumpStack});
for (var i = 0; i < 15; i++) {
for (var i = 0; i < 5000; i++) {
stack = null;
f();
matchStack(stack, ['dumpStack', 'f']);
@ -46,7 +42,7 @@ matchStack(stack, ["dumpStack", "f", "middle", "f"]);
function returnStackDumper() { return { valueOf:function() { stack = new Error().stack } } }
var f = asmLink(callFFI, null, {ffi:returnStackDumper});
for (var i = 0; i < 15; i++) {
for (var i = 0; i < 5000; i++) {
stack = null;
f();
matchStack(stack, ['valueOf', 'f']);

View File

@ -16,6 +16,7 @@
#include "jsprf.h"
#include "prmjtime.h"
#include "assembler/assembler/MacroAssembler.h"
#include "frontend/Parser.h"
#include "jit/AsmJSLink.h"
#include "jit/AsmJSModule.h"
@ -1408,6 +1409,9 @@ class MOZ_STACK_CLASS ModuleCompiler
return false;
return exits_.add(p, Move(exitDescriptor), *exitIndex);
}
bool addFunctionName(PropertyName *name, uint32_t *index) {
return module_->addFunctionName(name, index);
}
// Note a constraint on the minimum size of the heap. The heap size is
// constrained when linking to be at least the maximum of all such constraints.
@ -1439,11 +1443,6 @@ class MOZ_STACK_CLASS ModuleCompiler
bool finishGeneratingFunction(Func &func, MIRGenerator &mir, CodeGenerator &codegen) {
JS_ASSERT(func.defined() && func.code()->bound());
uint32_t beginOffset = func.code()->offset();
uint32_t endOffset = masm_.currentOffset();
if (!module_->addFunctionCodeRange(func.name(), beginOffset, endOffset))
return false;
jit::IonScriptCounts *counts = codegen.extractScriptCounts();
if (counts && !module_->addFunctionCounts(counts)) {
js_delete(counts);
@ -1481,19 +1480,15 @@ class MOZ_STACK_CLASS ModuleCompiler
module_->finishFunctionBodies(masm_.currentOffset());
}
void startGeneratingEntry(unsigned exportIndex) {
module_->exportedFunction(exportIndex).initCodeOffset(masm_.currentOffset());
}
bool finishGeneratingEntry(unsigned exportIndex) {
return module_->addEntryCodeRange(exportIndex, masm_.currentOffset());
}
void setInterpExitOffset(unsigned exitIndex) {
module_->exit(exitIndex).initInterpOffset(masm_.currentOffset());
}
void setIonExitOffset(unsigned exitIndex) {
module_->exit(exitIndex).initIonOffset(masm_.currentOffset());
}
void setEntryOffset(unsigned exportIndex) {
module_->exportedFunction(exportIndex).initCodeOffset(masm_.currentOffset());
}
void buildCompilationTimeReport(bool storedInCache, ScopedJSFreePtr<char> *out) {
ScopedJSFreePtr<char> slowFuns;
@ -1813,6 +1808,7 @@ class FunctionCompiler
ModuleCompiler & m_;
LifoAlloc & lifo_;
ParseNode * fn_;
uint32_t functionNameIndex_;
LocalMap locals_;
VarInitializerVector varInitializers_;
@ -1833,11 +1829,15 @@ class FunctionCompiler
LabeledBlockMap labeledBreaks_;
LabeledBlockMap labeledContinues_;
static const uint32_t NO_FUNCTION_NAME_INDEX = UINT32_MAX;
JS_STATIC_ASSERT(NO_FUNCTION_NAME_INDEX > CallSiteDesc::FUNCTION_NAME_INDEX_MAX);
public:
FunctionCompiler(ModuleCompiler &m, ParseNode *fn, LifoAlloc &lifo)
: m_(m),
lifo_(lifo),
fn_(fn),
functionNameIndex_(NO_FUNCTION_NAME_INDEX),
locals_(m.cx()),
varInitializers_(m.cx()),
alloc_(nullptr),
@ -2279,7 +2279,12 @@ class FunctionCompiler
uint32_t line, column;
m_.tokenStream().srcCoords.lineNumAndColumnIndex(call.node_->pn_pos.begin, &line, &column);
CallSiteDesc desc(line, column);
if (functionNameIndex_ == NO_FUNCTION_NAME_INDEX) {
if (!m_.addFunctionName(FunctionName(fn_), &functionNameIndex_))
return false;
}
CallSiteDesc desc(line, column, functionNameIndex_);
MAsmJSCall *ins = MAsmJSCall::New(alloc(), desc, callee, call.regArgs_, returnType,
call.spIncrement_);
if (!ins)
@ -5950,7 +5955,7 @@ GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFu
// PushRegsInMask(NonVolatileRegs).
masm.setFramePushed(0);
// See AsmJSFrameSize comment in Assembler-shared.h.
// See AsmJSFrameSize comment in Assembler-*.h.
#if defined(JS_CODEGEN_ARM)
masm.push(lr);
#endif // JS_CODEGEN_ARM
@ -6025,7 +6030,7 @@ GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFu
// Call into the real function.
AssertStackAlignment(masm);
masm.call(func.code());
masm.call(CallSiteDesc::Entry(), func.code());
// Pop the stack and recover the original 'argv' argument passed to the
// trampoline (which was pushed on the stack).
@ -6209,18 +6214,14 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript
m.setInterpExitOffset(exitIndex);
masm.setFramePushed(0);
// See AsmJSFrameSize comment in Assembler-shared.h.
// See AsmJSFrameSize comment in Assembler-*.h.
#if defined(JS_CODEGEN_ARM)
masm.push(lr);
#elif defined(JS_CODEGEN_MIPS)
#endif
#if defined(JS_CODEGEN_MIPS)
masm.push(ra);
#endif
// Store the frame pointer in AsmJSActivation::exitFP for stack unwinding.
Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
LoadAsmJSActivationIntoRegister(masm, activation);
masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitFP()));
MIRType typeArray[] = { MIRType_Pointer, // cx
MIRType_Pointer, // exitDatum
MIRType_Int32, // argc
@ -6239,11 +6240,16 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript
// Fill the argument array.
unsigned offsetToCallerStackArgs = AsmJSFrameSize + masm.framePushed();
Register scratch = ABIArgGenerator::NonArgReturnVolatileReg1;
Register scratch = ABIArgGenerator::NonArgReturnVolatileReg0;
FillArgumentArray(m, exit.sig().args(), offsetToArgv, offsetToCallerStackArgs, scratch);
// Prepare the arguments for the call to InvokeFromAsmJS_*.
ABIArgMIRTypeIter i(invokeArgTypes);
Register activation = ABIArgGenerator::NonArgReturnVolatileReg1;
LoadAsmJSActivationIntoRegister(masm, activation);
// Record sp in the AsmJSActivation for stack-walking.
masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitSP()));
// argument 0: cx
if (i->kind() == ABIArg::GPR) {
@ -6284,16 +6290,16 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript
AssertStackAlignment(masm);
switch (exit.sig().retType().which()) {
case RetType::Void:
masm.call(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_Ignore));
masm.callExit(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_Ignore), i.stackBytesConsumedSoFar());
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
break;
case RetType::Signed:
masm.call(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_ToInt32));
masm.callExit(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_ToInt32), i.stackBytesConsumedSoFar());
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.unboxInt32(argv, ReturnReg);
break;
case RetType::Double:
masm.call(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_ToNumber));
masm.callExit(AsmJSImmPtr(AsmJSImm_InvokeFromAsmJS_ToNumber), i.stackBytesConsumedSoFar());
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.loadDouble(argv, ReturnDoubleReg);
break;
@ -6305,11 +6311,6 @@ GenerateFFIInterpreterExit(ModuleCompiler &m, const ModuleCompiler::ExitDescript
// Note: the caller is IonMonkey code which means there are no non-volatile
// registers to restore.
masm.freeStack(stackDec);
// Clear exitFP before the frame is destroyed.
LoadAsmJSActivationIntoRegister(masm, activation);
masm.storePtr(ImmWord(0), Address(activation, AsmJSActivation::offsetOfExitFP()));
masm.ret();
}
@ -6334,6 +6335,9 @@ GenerateOOLConvert(ModuleCompiler &m, RetType retType, Label *throwLabel)
Register activation = ABIArgGenerator::NonArgReturnVolatileReg1;
LoadAsmJSActivationIntoRegister(masm, activation);
// Record sp in the AsmJSActivation for stack-walking.
masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitSP()));
// Store real arguments
ABIArgMIRTypeIter i(callArgTypes);
@ -6361,12 +6365,12 @@ GenerateOOLConvert(ModuleCompiler &m, RetType retType, Label *throwLabel)
AssertStackAlignment(masm);
switch (retType.which()) {
case RetType::Signed:
masm.call(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToInt32));
masm.callExit(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToInt32), i.stackBytesConsumedSoFar());
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.unboxInt32(Address(StackPointer, offsetToArgv), ReturnReg);
break;
case RetType::Double:
masm.call(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToNumber));
masm.callExit(AsmJSImmPtr(AsmJSImm_CoerceInPlace_ToNumber), i.stackBytesConsumedSoFar());
masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
masm.loadDouble(Address(StackPointer, offsetToArgv), ReturnDoubleReg);
break;
@ -6384,22 +6388,19 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
m.setIonExitOffset(exitIndex);
masm.setFramePushed(0);
// See AsmJSFrameSize comment in Assembler-shared.h.
#if defined(JS_CODEGEN_ARM)
masm.push(lr);
#elif defined(JS_CODEGEN_MIPS)
masm.push(ra);
#endif
// Store the frame pointer in AsmJSActivation::exitFP for stack unwinding.
Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
LoadAsmJSActivationIntoRegister(masm, activation);
masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitFP()));
// Ion does not preserve nonvolatile registers, so we have to preserve them.
// See AsmJSFrameSize comment in Assembler-*.h.
#if defined(JS_CODEGEN_X64)
masm.Push(HeapReg);
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
#elif defined(JS_CODEGEN_ARM)
masm.push(lr);
// The GlobalReg (r10) and HeapReg (r11) also need to be restored before
// returning to asm.js code.
// The NANReg also needs to be restored, but is a constant and is reloaded before
// returning to asm.js code.
masm.PushRegsInMask(GeneralRegisterSet((1<<GlobalReg.code()) | (1<<HeapReg.code())));
#elif defined(JS_CODEGEN_MIPS)
masm.push(ra);
masm.PushRegsInMask(GeneralRegisterSet((1<<GlobalReg.code()) | (1<<HeapReg.code())));
#endif
@ -6497,6 +6498,19 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
Register reg2 = AsmJSIonExitRegE2;
Register reg3 = AsmJSIonExitRegE3;
LoadAsmJSActivationIntoRegister(masm, reg0);
// Record sp in the AsmJSActivation for stack-walking.
#if defined(JS_CODEGEN_MIPS)
// Add a flag to indicate to AsmJSFrameIterator that we are calling
// into Ion, since the offset from SP to the return address is
// different when calling Ion vs. the native ABI.
masm.ma_or(reg1, StackPointer, Imm32(0x1));
masm.storePtr(reg1, Address(reg0, AsmJSActivation::offsetOfExitSP()));
#else
masm.storePtr(StackPointer, Address(reg0, AsmJSActivation::offsetOfExitSP()));
#endif
// The following is inlined:
// JSContext *cx = activation->cx();
// Activation *act = cx->mainThread().activation();
@ -6510,7 +6524,6 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
size_t offsetOfJitTop = offsetof(JSRuntime, mainThread) + offsetof(PerThreadData, jitTop);
size_t offsetOfJitJSContext = offsetof(JSRuntime, mainThread) +
offsetof(PerThreadData, jitJSContext);
LoadAsmJSActivationIntoRegister(masm, reg0);
masm.loadPtr(Address(reg0, AsmJSActivation::offsetOfContext()), reg3);
masm.loadPtr(Address(reg3, JSContext::offsetOfRuntime()), reg0);
masm.loadPtr(Address(reg0, offsetOfActivation), reg1);
@ -6582,19 +6595,13 @@ GenerateFFIIonExit(ModuleCompiler &m, const ModuleCompiler::ExitDescriptor &exit
masm.bind(&done);
masm.freeStack(stackDec);
// Restore non-volatile registers saved in the prologue.
#if defined(JS_CODEGEN_X64)
masm.Pop(HeapReg);
#elif defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
#endif
#if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS)
masm.loadConstantDouble(GenericNaN(), NANReg);
masm.PopRegsInMask(GeneralRegisterSet((1<<GlobalReg.code()) | (1<<HeapReg.code())));
#endif
// Clear exitFP before the frame is destroyed.
LoadAsmJSActivationIntoRegister(masm, activation);
masm.storePtr(ImmWord(0), Address(activation, AsmJSActivation::offsetOfExitFP()));
masm.ret();
JS_ASSERT(masm.framePushed() == 0);
@ -6635,20 +6642,18 @@ GenerateStackOverflowExit(ModuleCompiler &m, Label *throwLabel)
masm.align(CodeAlignment);
masm.bind(&m.stackOverflowLabel());
// The stack-overflow is checked before bumping the stack.
masm.setFramePushed(0);
// Store the frame pointer in AsmJSActivation::exitFP for stack unwinding.
Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
LoadAsmJSActivationIntoRegister(masm, activation);
masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitFP()));
MIRTypeVector argTypes(m.cx());
argTypes.infallibleAppend(MIRType_Pointer); // cx
unsigned stackDec = StackDecrementForCall(masm, argTypes, MaybeRetAddr);
masm.reserveStack(stackDec);
Register activation = ABIArgGenerator::NonArgReturnVolatileReg0;
LoadAsmJSActivationIntoRegister(masm, activation);
// Record sp in the AsmJSActivation for stack-walking.
masm.storePtr(StackPointer, Address(activation, AsmJSActivation::offsetOfExitSP()));
ABIArgMIRTypeIter i(argTypes);
// argument 0: cx
@ -6663,11 +6668,7 @@ GenerateStackOverflowExit(ModuleCompiler &m, Label *throwLabel)
JS_ASSERT(i.done());
AssertStackAlignment(masm);
masm.call(AsmJSImmPtr(AsmJSImm_ReportOverRecursed));
// Clear exitFP before the frame is destroyed.
LoadAsmJSActivationIntoRegister(masm, activation);
masm.storePtr(ImmWord(0), Address(activation, AsmJSActivation::offsetOfExitFP()));
masm.callExit(AsmJSImmPtr(AsmJSImm_ReportOverRecursed), i.stackBytesConsumedSoFar());
// Don't worry about restoring the stack; throwLabel will pop everything.
masm.jump(throwLabel);
@ -6866,10 +6867,10 @@ static bool
GenerateStubs(ModuleCompiler &m)
{
for (unsigned i = 0; i < m.module().numExportedFunctions(); i++) {
m.startGeneratingEntry(i);
m.setEntryOffset(i);
if (!GenerateEntry(m, m.module().exportedFunction(i)))
return false;
if (m.masm().oom() || !m.finishGeneratingEntry(i))
if (m.masm().oom())
return false;
}

View File

@ -1,75 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jit/AsmJSFrameIterator.h"
#include "jit/AsmJS.h"
#include "jit/AsmJSModule.h"
using namespace js;
using namespace js::jit;
static void *
ReturnAddressFromFP(uint8_t *fp)
{
// In asm.js code, the "frame" consists of a single word: the saved
// return address of the caller.
static_assert(AsmJSFrameSize == sizeof(void*), "Frame size mismatch");
return *(uint8_t**)fp;
}
AsmJSFrameIterator::AsmJSFrameIterator(const AsmJSActivation &activation)
: module_(&activation.module()),
fp_(activation.exitFP())
{
if (!fp_)
return;
settle(ReturnAddressFromFP(fp_));
}
void
AsmJSFrameIterator::operator++()
{
JS_ASSERT(!done());
fp_ += callsite_->stackDepth();
settle(ReturnAddressFromFP(fp_));
}
void
AsmJSFrameIterator::settle(void *returnAddress)
{
const AsmJSModule::CodeRange *codeRange = module_->lookupCodeRange(ReturnAddressFromFP(fp_));
JS_ASSERT(codeRange);
codeRange_ = codeRange;
switch (codeRange->kind()) {
case AsmJSModule::CodeRange::Entry:
fp_ = nullptr;
JS_ASSERT(done());
return;
case AsmJSModule::CodeRange::Function:
callsite_ = module_->lookupCallSite(returnAddress);
JS_ASSERT(callsite_);
break;
}
}
JSAtom *
AsmJSFrameIterator::functionDisplayAtom() const
{
JS_ASSERT(!done());
return reinterpret_cast<const AsmJSModule::CodeRange*>(codeRange_)->functionName(*module_);
}
unsigned
AsmJSFrameIterator::computeLine(uint32_t *column) const
{
JS_ASSERT(!done());
if (column)
*column = callsite_->column();
return callsite_->line();
}

View File

@ -1,44 +0,0 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef jit_AsmJSFrameIterator_h
#define jit_AsmJSFrameIterator_h
#include <stdint.h>
class JSAtom;
namespace js {
class AsmJSActivation;
class AsmJSModule;
namespace jit { struct CallSite; }
// Iterates over the frames of a single AsmJSActivation.
class AsmJSFrameIterator
{
const AsmJSModule *module_;
const jit::CallSite *callsite_;
uint8_t *fp_;
// Really, a const AsmJSModule::CodeRange*, but no forward declarations of
// nested classes, so use void* to avoid pulling in all of AsmJSModule.h.
const void *codeRange_;
void settle(void *returnAddress);
public:
explicit AsmJSFrameIterator() : module_(nullptr) {}
explicit AsmJSFrameIterator(const AsmJSActivation &activation);
void operator++();
bool done() const { return !fp_; }
JSAtom *functionDisplayAtom() const;
unsigned computeLine(uint32_t *column) const;
};
} // namespace js
#endif // jit_AsmJSFrameIterator_h

View File

@ -34,6 +34,101 @@ using namespace js::jit;
using mozilla::IsNaN;
using mozilla::PodZero;
static uint8_t *
ReturnAddressForExitCall(uint8_t **psp)
{
uint8_t *sp = *psp;
#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
// For calls to Ion/C++ on x86/x64, the exitSP is the SP right before the call
// to C++. Since the call instruction pushes the return address, we know
// that the return address is 1 word below exitSP.
return *(uint8_t**)(sp - sizeof(void*));
#elif defined(JS_CODEGEN_ARM)
// For calls to Ion/C++ on ARM, the *caller* pushes the return address on
// the stack. For Ion, this is just part of the ABI. For C++, the return
// address is explicitly pushed before the call since we cannot expect the
// callee to immediately push lr. This means that exitSP points to the
// return address.
return *(uint8_t**)sp;
#elif defined(JS_CODEGEN_MIPS)
// On MIPS we have two cases: an exit to C++ will store the return address
// at ShadowStackSpace above sp; an exit to Ion will store the return
// address at sp. To distinguish the two cases, the low bit of sp (which is
// aligned and therefore zero) is set for Ion exits.
if (uintptr_t(sp) & 0x1) {
sp = *psp -= 0x1; // Clear the low bit
return *(uint8_t**)sp;
}
return *(uint8_t**)(sp + ShadowStackSpace);
#else
# error "Unknown architecture!"
#endif
}
static uint8_t *
ReturnAddressForJitCall(uint8_t *sp)
{
// Once inside JIT code, sp always points to the word before the return
// address.
return *(uint8_t**)(sp - sizeof(void*));
}
AsmJSFrameIterator::AsmJSFrameIterator(const AsmJSActivation *activation)
: module_(nullptr)
{
if (!activation || activation->isInterruptedSP())
return;
module_ = &activation->module();
sp_ = activation->exitSP();
settle(ReturnAddressForExitCall(&sp_));
}
void
AsmJSFrameIterator::operator++()
{
settle(ReturnAddressForJitCall(sp_));
}
void
AsmJSFrameIterator::settle(uint8_t *returnAddress)
{
callsite_ = module_->lookupCallSite(returnAddress);
if (!callsite_ || callsite_->isEntry()) {
module_ = nullptr;
return;
}
if (callsite_->isEntry()) {
module_ = nullptr;
return;
}
sp_ += callsite_->stackDepth();
if (callsite_->isExit())
return settle(ReturnAddressForJitCall(sp_));
JS_ASSERT(callsite_->isNormal());
}
JSAtom *
AsmJSFrameIterator::functionDisplayAtom() const
{
JS_ASSERT(!done());
return module_->functionName(callsite_->functionNameIndex());
}
unsigned
AsmJSFrameIterator::computeLine(uint32_t *column) const
{
JS_ASSERT(!done());
if (column)
*column = callsite_->column();
return callsite_->line();
}
static bool
CloneModule(JSContext *cx, MutableHandle<AsmJSModuleObject*> moduleObj)
{

View File

@ -9,8 +9,31 @@
#include "NamespaceImports.h"
class JSAtom;
namespace js {
class AsmJSActivation;
class AsmJSModule;
namespace jit { struct CallSite; }
// Iterates over the frames of a single AsmJSActivation.
class AsmJSFrameIterator
{
const AsmJSModule *module_;
const jit::CallSite *callsite_;
uint8_t *sp_;
void settle(uint8_t *returnAddress);
public:
explicit AsmJSFrameIterator(const AsmJSActivation *activation);
void operator++();
bool done() const { return !module_; }
JSAtom *functionDisplayAtom() const;
unsigned computeLine(uint32_t *column) const;
};
#ifdef JS_ION
// Create a new JSFunction to replace originalFun as the representation of the

View File

@ -167,7 +167,6 @@ AsmJSModule::addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t *asmJSModu
exits_.sizeOfExcludingThis(mallocSizeOf) +
exports_.sizeOfExcludingThis(mallocSizeOf) +
callSites_.sizeOfExcludingThis(mallocSizeOf) +
codeRanges_.sizeOfExcludingThis(mallocSizeOf) +
functionNames_.sizeOfExcludingThis(mallocSizeOf) +
heapAccesses_.sizeOfExcludingThis(mallocSizeOf) +
functionCounts_.sizeOfExcludingThis(mallocSizeOf) +
@ -190,11 +189,11 @@ struct CallSiteRetAddrOffset
};
const CallSite *
AsmJSModule::lookupCallSite(void *returnAddress) const
AsmJSModule::lookupCallSite(uint8_t *returnAddress) const
{
JS_ASSERT(isFinished());
uint32_t target = ((uint8_t*)returnAddress) - code_;
uint32_t target = returnAddress - code_;
size_t lowerBound = 0;
size_t upperBound = callSites_.length();
@ -205,45 +204,6 @@ AsmJSModule::lookupCallSite(void *returnAddress) const
return &callSites_[match];
}
namespace js {
// Create an ordering on CodeRange and pc offsets suitable for BinarySearch.
// Stick these in the same namespace as AsmJSModule so that argument-dependent
// lookup will find it.
bool
operator==(size_t pcOffset, const AsmJSModule::CodeRange &rhs)
{
return pcOffset >= rhs.beginOffset() && pcOffset < rhs.endOffset();
}
bool
operator<=(const AsmJSModule::CodeRange &lhs, const AsmJSModule::CodeRange &rhs)
{
return lhs.beginOffset() <= rhs.beginOffset();
}
bool
operator<(size_t pcOffset, const AsmJSModule::CodeRange &rhs)
{
return pcOffset < rhs.beginOffset();
}
} // namespace js
const AsmJSModule::CodeRange *
AsmJSModule::lookupCodeRange(void *pc) const
{
JS_ASSERT(isFinished());
uint32_t target = ((uint8_t*)pc) - code_;
size_t lowerBound = 0;
size_t upperBound = codeRanges_.length();
size_t match;
if (!BinarySearch(codeRanges_, lowerBound, upperBound, target, &match))
return nullptr;
return &codeRanges_[match];
}
struct HeapAccessOffset
{
const AsmJSHeapAccessVector &accesses;
@ -254,12 +214,12 @@ struct HeapAccessOffset
};
const AsmJSHeapAccess *
AsmJSModule::lookupHeapAccess(void *pc) const
AsmJSModule::lookupHeapAccess(uint8_t *pc) const
{
JS_ASSERT(isFinished());
JS_ASSERT(containsPC(pc));
uint32_t target = ((uint8_t*)pc) - code_;
uint32_t target = pc - code_;
size_t lowerBound = 0;
size_t upperBound = heapAccesses_.length();
@ -333,11 +293,6 @@ AsmJSModule::finish(ExclusiveContext *cx, TokenStream &tokenStream, MacroAssembl
CallSite &c = callSites_[i];
c.setReturnAddressOffset(masm.actualOffset(c.returnAddressOffset()));
}
for (size_t i = 0; i < codeRanges_.length(); i++) {
CodeRange &c = codeRanges_[i];
c.beginOffset_ = masm.actualOffset(c.beginOffset_);
c.endOffset_ = masm.actualOffset(c.endOffset_);
}
#endif
JS_ASSERT(pod.functionBytes_ % AsmJSPageSize == 0);
@ -1129,7 +1084,6 @@ AsmJSModule::serializedSize() const
SerializedVectorSize(exits_) +
SerializedVectorSize(exports_) +
SerializedPodVectorSize(callSites_) +
SerializedPodVectorSize(codeRanges_) +
SerializedVectorSize(functionNames_) +
SerializedPodVectorSize(heapAccesses_) +
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
@ -1150,7 +1104,6 @@ AsmJSModule::serialize(uint8_t *cursor) const
cursor = SerializeVector(cursor, exits_);
cursor = SerializeVector(cursor, exports_);
cursor = SerializePodVector(cursor, callSites_);
cursor = SerializePodVector(cursor, codeRanges_);
cursor = SerializeVector(cursor, functionNames_);
cursor = SerializePodVector(cursor, heapAccesses_);
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
@ -1177,7 +1130,6 @@ AsmJSModule::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
(cursor = DeserializeVector(cx, cursor, &exits_)) &&
(cursor = DeserializeVector(cx, cursor, &exports_)) &&
(cursor = DeserializePodVector(cx, cursor, &callSites_)) &&
(cursor = DeserializePodVector(cx, cursor, &codeRanges_)) &&
(cursor = DeserializeVector(cx, cursor, &functionNames_)) &&
(cursor = DeserializePodVector(cx, cursor, &heapAccesses_)) &&
#if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
@ -1248,7 +1200,6 @@ AsmJSModule::clone(JSContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) con
!CloneVector(cx, exits_, &out.exits_) ||
!CloneVector(cx, exports_, &out.exports_) ||
!ClonePodVector(cx, callSites_, &out.callSites_) ||
!ClonePodVector(cx, codeRanges_, &out.codeRanges_) ||
!CloneVector(cx, functionNames_, &out.functionNames_) ||
!ClonePodVector(cx, heapAccesses_, &out.heapAccesses_) ||
!staticLinkData_.clone(cx, &out.staticLinkData_))

View File

@ -313,33 +313,6 @@ class AsmJSModule
bool clone(ExclusiveContext *cx, ExportedFunction *out) const;
};
class CodeRange
{
public:
enum Kind { Entry, Function };
private:
Kind kind_;
uint32_t beginOffset_;
uint32_t endOffset_;
uint32_t functionNameIndex_;
friend class AsmJSModule;
CodeRange(Kind kind, uint32_t beginOffset, uint32_t endOffset)
: kind_(kind), beginOffset_(beginOffset), endOffset_(endOffset)
{}
public:
CodeRange() {}
Kind kind() const { return kind_; }
uint32_t beginOffset() const { return beginOffset_; }
uint32_t endOffset() const { return endOffset_; }
PropertyName *functionName(const AsmJSModule &module) const {
JS_ASSERT(kind_ == Function);
return module.functionNames_[functionNameIndex_].name();
}
};
class Name
{
PropertyName *name_;
@ -506,7 +479,6 @@ class AsmJSModule
Vector<Exit, 0, SystemAllocPolicy> exits_;
Vector<ExportedFunction, 0, SystemAllocPolicy> exports_;
Vector<jit::CallSite, 0, SystemAllocPolicy> callSites_;
Vector<CodeRange, 0, SystemAllocPolicy> codeRanges_;
Vector<Name, 0, SystemAllocPolicy> functionNames_;
Vector<jit::AsmJSHeapAccess, 0, SystemAllocPolicy> heapAccesses_;
Vector<jit::IonScriptCounts*, 0, SystemAllocPolicy> functionCounts_;
@ -694,21 +666,17 @@ class AsmJSModule
if (len > pod.minHeapLength_)
pod.minHeapLength_ = len;
}
bool addFunctionCodeRange(PropertyName *name, uint32_t beginOffset, uint32_t endOffset) {
bool addFunctionName(PropertyName *name, uint32_t *nameIndex) {
JS_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
JS_ASSERT(name->isTenured());
JS_ASSERT(beginOffset <= endOffset);
JS_ASSERT_IF(!codeRanges_.empty(), codeRanges_.back().endOffset() <= beginOffset);
if (functionNames_.length() >= UINT32_MAX)
if (functionNames_.length() > jit::CallSiteDesc::FUNCTION_NAME_INDEX_MAX)
return false;
CodeRange codeRange(CodeRange::Function, beginOffset, endOffset);
codeRange.functionNameIndex_ = functionNames_.length();
return functionNames_.append(name) && codeRanges_.append(codeRange);
*nameIndex = functionNames_.length();
return functionNames_.append(name);
}
bool addEntryCodeRange(unsigned exportIndex, uint32_t endOffset) {
uint32_t beginOffset = exports_[exportIndex].pod.codeOffset_;
CodeRange codeRange(CodeRange::Entry, beginOffset, endOffset);
return codeRanges_.append(codeRange);
PropertyName *functionName(uint32_t i) const {
JS_ASSERT(isFinished());
return functionNames_[i].name();
}
bool addExit(unsigned ffiIndex, unsigned *exitIndex) {
JS_ASSERT(isFinishedWithModulePrologue() && !isFinishedWithFunctionBodies());
@ -884,15 +852,11 @@ class AsmJSModule
// Lookup a callsite by the return pc (from the callee to the caller).
// Return null if no callsite was found.
const jit::CallSite *lookupCallSite(void *returnAddress) const;
// Lookup the name the code range containing the given pc. Return null if no
// code range was found.
const CodeRange *lookupCodeRange(void *pc) const;
const jit::CallSite *lookupCallSite(uint8_t *returnAddress) const;
// Lookup a heap access site by the pc which performs the access. Return
// null if no heap access was found.
const jit::AsmJSHeapAccess *lookupHeapAccess(void *pc) const;
const jit::AsmJSHeapAccess *lookupHeapAccess(uint8_t *pc) const;
// The global data section is placed after the executable code (i.e., at
// offset codeBytes_) in the module's linear allocation. The global data

View File

@ -356,7 +356,7 @@ HandleSimulatorInterrupt(JSRuntime *rt, AsmJSActivation *activation, void *fault
if (module.containsPC((void *)rt->mainThread.simulator()->get_pc()) &&
module.containsPC(faultingAddress))
{
activation->setResumePC(nullptr);
activation->setInterrupted(nullptr);
int32_t nextpc = int32_t(module.interruptExit());
rt->mainThread.simulator()->set_resume_pc(nextpc);
return true;
@ -465,7 +465,7 @@ HandleException(PEXCEPTION_POINTERS exception)
// The trampoline will jump to activation->resumePC if execution isn't
// interrupted.
if (module.containsPC(faultingAddress)) {
activation->setResumePC(pc);
activation->setInterrupted(pc);
*ppc = module.interruptExit();
JSRuntime::AutoLockForInterrupt lock(rt);
@ -668,7 +668,7 @@ HandleMachException(JSRuntime *rt, const ExceptionRequest &request)
// The trampoline will jump to activation->resumePC if execution isn't
// interrupted.
if (module.containsPC(faultingAddress)) {
activation->setResumePC(pc);
activation->setInterrupted(pc);
*ppc = module.interruptExit();
JSRuntime::AutoLockForInterrupt lock(rt);
@ -918,7 +918,7 @@ HandleSignal(int signum, siginfo_t *info, void *ctx)
// The trampoline will jump to activation->resumePC if execution isn't
// interrupted.
if (module.containsPC(faultingAddress)) {
activation->setResumePC(pc);
activation->setInterrupted(pc);
*ppc = module.interruptExit();
JSRuntime::AutoLockForInterrupt lock(rt);

View File

@ -8573,7 +8573,7 @@ CodeGenerator::visitAsmJSCall(LAsmJSCall *ins)
masm.call(mir->desc(), ToRegister(ins->getOperand(mir->dynamicCalleeOperandIndex())));
break;
case MAsmJSCall::Callee::Builtin:
masm.call(AsmJSImmPtr(callee.builtin()));
masm.call(mir->desc(), AsmJSImmPtr(callee.builtin()));
break;
}

View File

@ -141,6 +141,12 @@ static const uint32_t StackAlignment = 8;
static const uint32_t CodeAlignment = 8;
static const bool StackKeptAligned = true;
// As an invariant across architectures, within asm.js code:
// $sp % StackAlignment = (AsmJSFrameSize + masm.framePushed) % StackAlignment
// To achieve this on ARM, the first instruction of the asm.js prologue pushes
// lr without incrementing masm.framePushed.
static const uint32_t AsmJSFrameSize = sizeof(void*);
static const Scale ScalePointer = TimesFour;
class Instruction;

View File

@ -53,7 +53,7 @@ CodeGeneratorARM::generateAsmJSPrologue(Label *stackOverflowLabel)
{
JS_ASSERT(gen->compilingAsmJS());
// See comment in Assembler-shared.h about AsmJSFrameSize.
// See comment in Assembler-arm.h about AsmJSFrameSize.
masm.push(lr);
// The asm.js over-recursed handler wants to be able to assume that SP

View File

@ -1789,17 +1789,6 @@ MacroAssemblerARMCompat::callIon(Register callee)
}
}
void
MacroAssemblerARMCompat::callIonFromAsmJS(Register callee)
{
ma_callIonNoPush(callee);
// The Ion ABI has the callee pop the return address off the stack.
// The asm.js caller assumes that the call leaves sp unchanged, so bump
// the stack.
subPtr(Imm32(sizeof(void*)), sp);
}
void
MacroAssemblerARMCompat::reserveStack(uint32_t amount)
{
@ -3635,6 +3624,19 @@ MacroAssemblerARM::ma_call(ImmPtr dest)
as_blx(CallReg);
}
void
MacroAssemblerARM::ma_callAndStoreRet(const Register r, uint32_t stackArgBytes)
{
// Note: this function stores the return address to sp[0]. The caller must
// anticipate this by pushing additional space on the stack. The ABI does
// not provide space for a return address so this function may only be
// called if no argument are passed.
JS_ASSERT(stackArgBytes == 0);
AutoForbidPools afp(this);
as_dtr(IsStore, 32, Offset, pc, DTRAddr(sp, DtrOffImm(0)));
as_blx(r);
}
void
MacroAssemblerARMCompat::breakpoint()
{

View File

@ -397,6 +397,9 @@ class MacroAssemblerARM : public Assembler
void ma_call(ImmPtr dest);
// calls reg, storing the return address into sp[0]
void ma_callAndStoreRet(const Register reg, uint32_t stackArgBytes);
// Float registers can only be loaded/stored in continuous runs
// when using vstm/vldm.
// This function breaks set into continuous runs and loads/stores
@ -563,13 +566,38 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
ma_movPatchable(ImmPtr(c->raw()), ScratchRegister, Always, rs);
ma_callIonHalfPush(ScratchRegister);
}
void appendCallSite(const CallSiteDesc &desc) {
// Add an extra sizeof(void*) to include the return address that was
// pushed by the call instruction (see CallSite::stackDepth).
enoughMemory_ &= append(CallSite(desc, currentOffset(), framePushed_ + AsmJSFrameSize));
}
void call(const CallSiteDesc &desc, const Register reg) {
call(reg);
enoughMemory_ &= append(desc, currentOffset(), framePushed_);
appendCallSite(desc);
}
void call(const CallSiteDesc &desc, Label *label) {
call(label);
enoughMemory_ &= append(desc, currentOffset(), framePushed_);
appendCallSite(desc);
}
void call(const CallSiteDesc &desc, AsmJSImmPtr imm) {
call(imm);
appendCallSite(desc);
}
void callExit(AsmJSImmPtr imm, uint32_t stackArgBytes) {
movePtr(imm, CallReg);
ma_callAndStoreRet(CallReg, stackArgBytes);
appendCallSite(CallSiteDesc::Exit());
}
void callIonFromAsmJS(const Register reg) {
ma_callIonNoPush(reg);
appendCallSite(CallSiteDesc::Exit());
// The Ion ABI has the callee pop the return address off the stack.
// The asm.js caller assumes that the call leaves sp unchanged, so bump
// the stack.
subPtr(Imm32(sizeof(void*)), sp);
}
void branch(JitCode *c) {
@ -1259,7 +1287,6 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
// Makes an Ion call using the only two methods that it is sane for
// indep code to make a call
void callIon(Register callee);
void callIonFromAsmJS(Register callee);
void reserveStack(uint32_t amount);
void freeStack(uint32_t amount);

View File

@ -4070,7 +4070,7 @@ Simulator::execute()
int32_t rpc = resume_pc_;
if (MOZ_UNLIKELY(rpc != 0)) {
// AsmJS signal handler ran and we have to adjust the pc.
activation->setResumePC((void *)get_pc());
activation->setInterrupted((void *)get_pc());
set_pc(rpc);
resume_pc_ = 0;
}

View File

@ -152,6 +152,12 @@ static const uint32_t StackAlignment = 8;
static const uint32_t CodeAlignment = 4;
static const bool StackKeptAligned = true;
// As an invariant across architectures, within asm.js code:
// $sp % StackAlignment = (AsmJSFrameSize + masm.framePushed) % StackAlignment
// To achieve this on MIPS, the first instruction of the asm.js prologue pushes
// ra without incrementing masm.framePushed.
static const uint32_t AsmJSFrameSize = sizeof(void*);
static const Scale ScalePointer = TimesFour;
// MIPS instruction types

View File

@ -52,7 +52,7 @@ CodeGeneratorMIPS::generateAsmJSPrologue(Label *stackOverflowLabel)
{
JS_ASSERT(gen->compilingAsmJS());
// See comment in Assembler-shared.h about AsmJSFrameSize.
// See comment in Assembler-mips.h about AsmJSFrameSize.
masm.push(ra);
// The asm.js over-recursed handler wants to be able to assume that SP

View File

@ -1525,16 +1525,6 @@ MacroAssemblerMIPSCompat::callIon(Register callee)
ma_callIon(callee);
}
}
void
MacroAssemblerMIPSCompat::callIonFromAsmJS(Register callee)
{
ma_callIonNoPush(reg);
// The Ion ABI has the callee pop the return address off the stack.
// The asm.js caller assumes that the call leaves sp unchanged, so bump
// the stack.
subPtr(Imm32(sizeof(void*)), StackPointer);
}
void
MacroAssemblerMIPSCompat::reserveStack(uint32_t amount)
@ -2970,6 +2960,21 @@ MacroAssemblerMIPS::ma_callIonHalfPush(const Register r)
as_sw(ra, StackPointer, 0);
}
void
MacroAssemblerMIPS::ma_callAndStoreRet(const Register r, uint32_t stackArgBytes)
{
// Note: this function stores the return address to sp[16]. The caller
// must anticipate this by reserving additional space on the stack.
// The ABI does not provide space for a return address so this function
// stores 'ra' before any ABI arguments.
// This function may only be called if there are 4 or less arguments.
JS_ASSERT(stackArgBytes == 4 * sizeof(uintptr_t));
// This is a MIPS hack to push return address during jalr delay slot.
as_jalr(r);
as_sw(ra, StackPointer, 4 * sizeof(uintptr_t));
}
void
MacroAssemblerMIPS::ma_call(ImmPtr dest)
{

View File

@ -301,6 +301,9 @@ class MacroAssemblerMIPS : public Assembler
// calls an ion function, assuming that the stack is currently not 8 byte aligned
void ma_callIonHalfPush(const Register reg);
// calls reg, storing the return address into sp[stackArgBytes]
void ma_callAndStoreRet(const Register reg, uint32_t stackArgBytes);
void ma_call(ImmPtr dest);
void ma_jump(ImmPtr dest);
@ -412,13 +415,38 @@ class MacroAssemblerMIPSCompat : public MacroAssemblerMIPS
ma_liPatchable(ScratchRegister, Imm32((uint32_t)c->raw()));
ma_callIonHalfPush(ScratchRegister);
}
void appendCallSite(const CallSiteDesc &desc) {
// Add an extra sizeof(void*) to include the return address that was
// pushed by the call instruction (see CallSite::stackDepth).
enoughMemory_ &= append(CallSite(desc, currentOffset(), framePushed_ + AsmJSFrameSize));
}
void call(const CallSiteDesc &desc, const Register reg) {
call(reg);
enoughMemory_ &= append(desc, currentOffset(), framePushed_);
appendCallSite(desc);
}
void call(const CallSiteDesc &desc, Label *label) {
call(label);
enoughMemory_ &= append(desc, currentOffset(), framePushed_);
appendCallSite(desc);
}
void call(const CallSiteDesc &desc, AsmJSImmPtr imm) {
call(imm);
appendCallSite(desc);
}
void callExit(AsmJSImmPtr imm, uint32_t stackArgBytes) {
movePtr(imm, CallReg);
ma_callAndStoreRet(CallReg, stackArgBytes);
appendCallSite(CallSiteDesc::Exit());
}
void callIonFromAsmJS(const Register reg) {
ma_callIonNoPush(reg);
appendCallSite(CallSiteDesc::Exit());
// The Ion ABI has the callee pop the return address off the stack.
// The asm.js caller assumes that the call leaves sp unchanged, so bump
// the stack.
subPtr(Imm32(sizeof(void*)), StackPointer);
}
void branch(JitCode *c) {
@ -965,7 +993,6 @@ public:
// Makes an Ion call using the only two methods that it is sane for
// indep code to make a call
void callIon(Register callee);
void callIonFromAsmJS(Register callee);
void reserveStack(uint32_t amount);
void freeStack(uint32_t amount);

View File

@ -577,19 +577,48 @@ class CodeLocationLabel
}
};
// While the frame-pointer chain allows the stack to be unwound without
// metadata, Error.stack still needs to know the line/column of every call in
// the chain. A CallSiteDesc describes the line/column of a single callsite.
// A CallSiteDesc is created by callers of MacroAssembler.
// Describes the user-visible properties of a callsite.
//
// A few general notes about the stack-walking supported by CallSite(Desc):
// - This information facilitates stack-walking performed by FrameIter which
// is used by Error.stack and other user-visible stack-walking functions.
// - Ion/asm.js calling conventions do not maintain a frame-pointer so
// stack-walking must lookup the stack depth based on the PC.
// - Stack-walking only occurs from C++ after a synchronous calls (JS-to-JS and
// JS-to-C++). Thus, we do not need to map arbitrary PCs to stack-depths,
// just the return address at callsites.
// - An exception to the above rule is the interrupt callback which can happen
// at arbitrary PCs. In such cases, we drop frames from the stack-walk. In
// the future when a full PC->stack-depth map is maintained, we handle this
// case.
class CallSiteDesc
{
uint32_t line_;
uint32_t column_;
uint32_t functionNameIndex_;
static const uint32_t sEntryTrampoline = UINT32_MAX;
static const uint32_t sExit = UINT32_MAX - 1;
public:
static const uint32_t FUNCTION_NAME_INDEX_MAX = UINT32_MAX - 2;
CallSiteDesc() {}
CallSiteDesc(uint32_t line, uint32_t column) : line_(line), column_(column) {}
uint32_t line() const { return line_; }
uint32_t column() const { return column_; }
CallSiteDesc(uint32_t line, uint32_t column, uint32_t functionNameIndex)
: line_(line), column_(column), functionNameIndex_(functionNameIndex)
{}
static CallSiteDesc Entry() { return CallSiteDesc(0, 0, sEntryTrampoline); }
static CallSiteDesc Exit() { return CallSiteDesc(0, 0, sExit); }
bool isEntry() const { return functionNameIndex_ == sEntryTrampoline; }
bool isExit() const { return functionNameIndex_ == sExit; }
bool isNormal() const { return !(isEntry() || isExit()); }
uint32_t line() const { JS_ASSERT(isNormal()); return line_; }
uint32_t column() const { JS_ASSERT(isNormal()); return column_; }
uint32_t functionNameIndex() const { JS_ASSERT(isNormal()); return functionNameIndex_; }
};
// Adds to CallSiteDesc the metadata necessary to walk the stack given an
@ -612,21 +641,13 @@ struct CallSite : public CallSiteDesc
uint32_t returnAddressOffset() const { return returnAddressOffset_; }
// The stackDepth measures the amount of stack space pushed since the
// function was called. In particular, this includes the pushed return
// address on all archs (whether or not the call instruction pushes the
// return address (x86/x64) or the prologue does (ARM/MIPS).
uint32_t stackDepth() const { return stackDepth_; }
// function was called. In particular, this includes the word pushed by the
// call instruction on x86/x64.
uint32_t stackDepth() const { JS_ASSERT(!isEntry()); return stackDepth_; }
};
typedef Vector<CallSite, 0, SystemAllocPolicy> CallSiteVector;
// As an invariant across architectures, within asm.js code:
// $sp % StackAlignment = (AsmJSFrameSize + masm.framePushed) % StackAlignment
// AsmJSFrameSize is 1 word, for the return address pushed by the call (or, in
// the case of ARM/MIPS, by the first instruction of the prologue). This means
// masm.framePushed never includes the pushed return address.
static const uint32_t AsmJSFrameSize = sizeof(void*);
// Summarizes a heap access made by asm.js code that needs to be patched later
// and/or looked up by the asm.js signal handlers. Different architectures need
// to know different things (x64: offset and length, ARM: where to patch in
@ -800,11 +821,7 @@ class AssemblerShared
return !enoughMemory_;
}
bool append(const CallSiteDesc &desc, size_t currentOffset, size_t framePushed) {
// framePushed does not include AsmJSFrameSize, so add it in here (see
// CallSite::stackDepth).
return callsites_.append(CallSite(desc, currentOffset, framePushed + AsmJSFrameSize));
}
bool append(CallSite callsite) { return callsites_.append(callsite); }
CallSiteVector &&extractCallSites() { return Move(callsites_); }
bool append(AsmJSHeapAccess access) { return asmJSHeapAccesses_.append(access); }

View File

@ -667,23 +667,26 @@ class MacroAssemblerX86Shared : public Assembler
bool buildFakeExitFrame(Register scratch, uint32_t *offset);
void callWithExitFrame(JitCode *target);
void call(const CallSiteDesc &desc, Label *label) {
call(label);
enoughMemory_ &= append(desc, currentOffset(), framePushed_);
}
void call(const CallSiteDesc &desc, Register reg) {
call(reg);
enoughMemory_ &= append(desc, currentOffset(), framePushed_);
}
void callIon(Register callee) {
call(callee);
}
void callIonFromAsmJS(Register callee) {
call(callee);
void appendCallSite(const CallSiteDesc &desc) {
// Add an extra sizeof(void*) to include the return address that was
// pushed by the call instruction (see CallSite::stackDepth).
enoughMemory_ &= append(CallSite(desc, currentOffset(), framePushed_ + AsmJSFrameSize));
}
void call(AsmJSImmPtr target) {
mov(target, eax);
call(eax);
void call(const CallSiteDesc &desc, Label *label) {
call(label);
appendCallSite(desc);
}
void call(const CallSiteDesc &desc, Register reg) {
call(reg);
appendCallSite(desc);
}
void callIonFromAsmJS(Register reg) {
call(CallSiteDesc::Exit(), reg);
}
void checkStackAlignment() {

View File

@ -185,6 +185,13 @@ static const uint32_t StackAlignment = 16;
static const bool StackKeptAligned = false;
static const uint32_t CodeAlignment = 8;
// As an invariant across architectures, within asm.js code:
// $sp % StackAlignment = (AsmJSFrameSize + masm.framePushed) % StackAlignment
// On x64, this naturally falls out of the fact that the 'call' instruction
// pushes the return address on the stack and masm.framePushed = 0 at the first
// instruction of the prologue.
static const uint32_t AsmJSFrameSize = sizeof(void*);
static const Scale ScalePointer = TimesEight;
} // namespace jit

View File

@ -100,6 +100,18 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
void call(ImmPtr target) {
call(ImmWord(uintptr_t(target.value)));
}
void call(AsmJSImmPtr target) {
mov(target, rax);
call(rax);
}
void call(const CallSiteDesc &desc, AsmJSImmPtr target) {
call(target);
appendCallSite(desc);
}
void callExit(AsmJSImmPtr target, uint32_t stackArgBytes) {
call(CallSiteDesc::Exit(), target);
}
// Refers to the upper 32 bits of a 64-bit Value operand.
// On x86_64, the upper 32 bits do not necessarily only contain the type.

View File

@ -113,6 +113,13 @@ static const uint32_t StackAlignment = 4;
static const bool StackKeptAligned = false;
static const uint32_t CodeAlignment = 8;
// As an invariant across architectures, within asm.js code:
// $sp % StackAlignment = (AsmJSFrameSize + masm.framePushed) % StackAlignment
// On x86, this naturally falls out of the fact that the 'call' instruction
// pushes the return address on the stack and masm.framePushed = 0 at the first
// instruction of the prologue.
static const uint32_t AsmJSFrameSize = sizeof(void*);
struct ImmTag : public Imm32
{
ImmTag(JSValueTag mask)
@ -375,6 +382,13 @@ class Assembler : public AssemblerX86Shared
JmpSrc src = masm.call();
addPendingJump(src, target, Relocation::HARDCODED);
}
void call(AsmJSImmPtr target) {
// Moving to a register is suboptimal. To fix (use a single
// call-immediate instruction) we'll need to distinguish a new type of
// relative patch to an absolute address in AsmJSAbsoluteLink.
mov(target, eax);
call(eax);
}
// Emit a CALL or CMP (nop) instruction. ToggleCall can be used to patch
// this instruction.

View File

@ -1114,6 +1114,13 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
Push(dynStack);
call(target);
}
void call(const CallSiteDesc &desc, AsmJSImmPtr target) {
call(target);
appendCallSite(desc);
}
void callExit(AsmJSImmPtr target, uint32_t stackArgBytes) {
call(CallSiteDesc::Exit(), target);
}
#ifdef JSGC_GENERATIONAL
void branchPtrInNurseryRange(Condition cond, Register ptr, Register temp, Label *label);

View File

@ -54,6 +54,8 @@ using namespace js::frontend;
using mozilla::ArrayLength;
using mozilla::PodCopy;
using mozilla::Range;
using mozilla::RangedPtr;
static bool
fun_getProperty(JSContext *cx, HandleObject obj_, HandleId id, MutableHandleValue vp)
@ -763,7 +765,7 @@ js::FindBody(JSContext *cx, HandleFunction fun, HandleLinearString src, size_t *
if (!stableChars.initTwoByte(cx, src))
return false;
const mozilla::Range<const jschar> srcChars = stableChars.twoByteRange();
const Range<const jschar> srcChars = stableChars.twoByteRange();
TokenStream ts(cx, options, srcChars.start().get(), srcChars.length(), nullptr);
int nest = 0;
bool onward = true;
@ -799,7 +801,7 @@ js::FindBody(JSContext *cx, HandleFunction fun, HandleLinearString src, size_t *
*bodyStart = ts.currentToken().pos.begin;
if (braced)
*bodyStart += 1;
mozilla::RangedPtr<const jschar> end = srcChars.end();
RangedPtr<const jschar> end = srcChars.end();
if (end[-1] == '}') {
end--;
} else {
@ -1739,7 +1741,7 @@ FunctionConstructor(JSContext *cx, unsigned argc, Value *vp, GeneratorKind gener
if (!stableChars.initTwoByte(cx, str))
return false;
mozilla::Range<const jschar> chars = stableChars.twoByteRange();
Range<const jschar> chars = stableChars.twoByteRange();
SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller()
? SourceBufferHolder::GiveOwnership
: SourceBufferHolder::NoOwnership;

View File

@ -247,7 +247,6 @@ if CONFIG['ENABLE_ION']:
'irregexp/NativeRegExpMacroAssembler.cpp',
'jit/AliasAnalysis.cpp',
'jit/AsmJS.cpp',
'jit/AsmJSFrameIterator.cpp',
'jit/AsmJSLink.cpp',
'jit/AsmJSModule.cpp',
'jit/AsmJSSignalHandlers.cpp',

View File

@ -586,7 +586,7 @@ FrameIter::settleOnActivation()
}
if (activation->isAsmJS()) {
data_.asmJSFrames_ = AsmJSFrameIterator(*data_.activations_->asAsmJS());
data_.asmJSFrames_ = AsmJSFrameIterator(data_.activations_->asAsmJS());
if (data_.asmJSFrames_.done()) {
++data_.activations_;
@ -638,7 +638,7 @@ FrameIter::Data::Data(ThreadSafeContext *cx, SavedOption savedOption,
#ifdef JS_ION
, jitFrames_((uint8_t *)nullptr, SequentialExecution)
, ionInlineFrameNo_(0)
, asmJSFrames_()
, asmJSFrames_(nullptr)
#endif
{
}
@ -1683,7 +1683,7 @@ AsmJSActivation::AsmJSActivation(JSContext *cx, AsmJSModule &module)
errorRejoinSP_(nullptr),
profiler_(nullptr),
resumePC_(nullptr),
exitFP_(nullptr)
exitSP_(nullptr)
{
if (cx->runtime()->spsProfiler.enabled()) {
// Use a profiler string that matches jsMatch regex in

View File

@ -12,7 +12,7 @@
#include "jsfun.h"
#include "jsscript.h"
#include "jit/AsmJSFrameIterator.h"
#include "jit/AsmJSLink.h"
#include "jit/JitFrameIterator.h"
#ifdef CHECK_OSIPOINT_REGISTERS
#include "jit/Registers.h" // for RegisterDump
@ -1511,7 +1511,9 @@ class AsmJSActivation : public Activation
void *errorRejoinSP_;
SPSProfiler *profiler_;
void *resumePC_;
uint8_t *exitFP_;
uint8_t *exitSP_;
static const intptr_t InterruptedSP = -1;
public:
AsmJSActivation(JSContext *cx, AsmJSModule &module);
@ -1527,18 +1529,16 @@ class AsmJSActivation : public Activation
// Initialized by JIT code:
static unsigned offsetOfErrorRejoinSP() { return offsetof(AsmJSActivation, errorRejoinSP_); }
static unsigned offsetOfExitFP() { return offsetof(AsmJSActivation, exitFP_); }
static unsigned offsetOfExitSP() { return offsetof(AsmJSActivation, exitSP_); }
// Set from SIGSEGV handler:
void setResumePC(void *pc) { resumePC_ = pc; }
void setInterrupted(void *pc) { resumePC_ = pc; exitSP_ = (uint8_t*)InterruptedSP; }
bool isInterruptedSP() const { return exitSP_ == (uint8_t*)InterruptedSP; }
// If pc is in C++/Ion code, exitFP points to the innermost asm.js frame
// (the one that called into C++). While in asm.js code, exitFP is either
// null or points to the innermost asm.js frame. Thus, it is always valid to
// unwind a non-null exitFP. The only way C++ can observe a null exitFP is
// asychronous interruption of asm.js execution (viz., via the profiler,
// a signal handler, or the interrupt exit).
uint8_t *exitFP() const { return exitFP_; }
// Note: exitSP is the sp right before the call instruction. On x86, this
// means before the return address is pushed on the stack, on ARM, this
// means after.
uint8_t *exitSP() const { JS_ASSERT(!isInterruptedSP()); return exitSP_; }
};
// A FrameIter walks over the runtime's stack of JS script activations,

View File

@ -41,10 +41,7 @@ BinarySearch(const Container& aContainer, size_t aBegin, size_t aEnd,
size_t high = aEnd;
while (low != high) {
size_t middle = low + (high - low) / 2;
// Allow any intermediate type so long as it provides a suitable ordering
// relation.
const auto& middleValue = aContainer[middle];
const T& middleValue = aContainer[middle];
MOZ_ASSERT(aContainer[low] <= aContainer[middle]);
MOZ_ASSERT(aContainer[middle] <= aContainer[high - 1]);