Bug 846111 - Part 2: Teach VMFunction about parallel execution. (r=nbp)

This commit is contained in:
Shu-yu Guo 2013-04-10 11:04:23 -07:00
parent 11252a0c6d
commit 1ac435419e
19 changed files with 341 additions and 106 deletions

View File

@ -144,6 +144,7 @@ class IonFrameIterator
return type_ == IonFrame_Entry;
}
bool isFunctionFrame() const;
bool isParallelFunctionFrame() const;
bool isConstructing() const;

View File

@ -103,9 +103,9 @@ IonFrameIterator::frameSize() const
// Returns the JSScript associated with the topmost Ion frame.
inline RawScript
GetTopIonJSScript(JSContext *cx, const SafepointIndex **safepointIndexOut, void **returnAddrOut)
GetTopIonJSScript(PerThreadData *pt, const SafepointIndex **safepointIndexOut, void **returnAddrOut)
{
IonFrameIterator iter(cx->mainThread().ionTop);
IonFrameIterator iter(pt->ionTop);
JS_ASSERT(iter.type() == IonFrame_Exit);
++iter;
@ -126,6 +126,13 @@ GetTopIonJSScript(JSContext *cx, const SafepointIndex **safepointIndexOut, void
return iter.script();
}
inline RawScript
GetTopIonJSScript(JSContext *cx, const SafepointIndex **safepointIndexOut, void **returnAddrOut)
{
return GetTopIonJSScript(&cx->mainThread(), safepointIndexOut, returnAddrOut);
}
inline BaselineFrame *
GetTopBaselineFrame(JSContext *cx)
{

View File

@ -26,6 +26,8 @@
#include "Safepoints.h"
#include "VMFunctions.h"
#include "vm/ParallelDo.h"
namespace js {
namespace ion {
@ -61,8 +63,14 @@ IonFrameIterator::checkInvalidation(IonScript **ionScriptOut) const
RawScript script = this->script();
// N.B. the current IonScript is not the same as the frame's
// IonScript if the frame has since been invalidated.
bool invalidated = !script->hasIonScript() ||
!script->ionScript()->containsReturnAddress(returnAddr);
bool invalidated;
if (isParallelFunctionFrame()) {
invalidated = !script->hasParallelIonScript() ||
!script->parallelIonScript()->containsReturnAddress(returnAddr);
} else {
invalidated = !script->hasIonScript() ||
!script->ionScript()->containsReturnAddress(returnAddr);
}
if (!invalidated)
return false;
@ -84,8 +92,10 @@ JSFunction *
IonFrameIterator::callee() const
{
if (isScripted()) {
JS_ASSERT(isFunctionFrame());
return CalleeTokenToFunction(calleeToken());
JS_ASSERT(isFunctionFrame() || isParallelFunctionFrame());
if (isFunctionFrame())
return CalleeTokenToFunction(calleeToken());
return CalleeTokenToParallelFunction(calleeToken());
}
JS_ASSERT(isNative());
@ -95,7 +105,7 @@ IonFrameIterator::callee() const
JSFunction *
IonFrameIterator::maybeCallee() const
{
if ((isScripted() && isFunctionFrame()) || isNative())
if ((isScripted() && (isFunctionFrame() || isParallelFunctionFrame())) || isNative())
return callee();
return NULL;
}
@ -138,6 +148,12 @@ IonFrameIterator::isFunctionFrame() const
return CalleeTokenIsFunction(calleeToken());
}
bool
IonFrameIterator::isParallelFunctionFrame() const
{
return GetCalleeTokenTag(calleeToken()) == CalleeToken_ParallelFunction;
}
bool
IonFrameIterator::isEntryJSFrame() const
{
@ -497,6 +513,23 @@ HandleException(ResumeFromException *rfe)
rfe->stackPointer = iter.fp();
}
void
HandleParallelFailure(ResumeFromException *rfe)
{
ForkJoinSlice *slice = ForkJoinSlice::Current();
IonFrameIterator iter(slice->perThreadData->ionTop);
while (!iter.isEntry()) {
parallel::Spew(parallel::SpewBailouts, "Bailing from VM reentry");
if (!slice->abortedScript && iter.isScripted())
slice->abortedScript = iter.script();
++iter;
}
rfe->kind = ResumeFromException::RESUME_ENTRY_FRAME;
rfe->stackPointer = iter.fp();
}
void
EnsureExitFrame(IonCommonFrameLayout *frame)
{
@ -1091,7 +1124,15 @@ IonFrameIterator::ionScript() const
IonScript *ionScript = NULL;
if (checkInvalidation(&ionScript))
return ionScript;
return script()->ionScript();
switch (GetCalleeTokenTag(calleeToken())) {
case CalleeToken_Function:
case CalleeToken_Script:
return script()->ionScript();
case CalleeToken_ParallelFunction:
return script()->parallelIonScript();
default:
JS_NOT_REACHED("unknown callee token type");
}
}
const SafepointIndex *

View File

@ -28,14 +28,15 @@ typedef void * CalleeToken;
enum CalleeTokenTag
{
CalleeToken_Function = 0x0, // untagged
CalleeToken_Script = 0x1
CalleeToken_Script = 0x1,
CalleeToken_ParallelFunction = 0x2
};
static inline CalleeTokenTag
GetCalleeTokenTag(CalleeToken token)
{
CalleeTokenTag tag = CalleeTokenTag(uintptr_t(token) & 0x3);
JS_ASSERT(tag <= CalleeToken_Script);
JS_ASSERT(tag <= CalleeToken_ParallelFunction);
return tag;
}
static inline CalleeToken
@ -44,6 +45,11 @@ CalleeToToken(JSFunction *fun)
return CalleeToken(uintptr_t(fun) | uintptr_t(CalleeToken_Function));
}
static inline CalleeToken
CalleeToParallelToken(JSFunction *fun)
{
return CalleeToken(uintptr_t(fun) | uintptr_t(CalleeToken_ParallelFunction));
}
static inline CalleeToken
CalleeToToken(RawScript script)
{
return CalleeToken(uintptr_t(script) | uintptr_t(CalleeToken_Script));
@ -59,6 +65,12 @@ CalleeTokenToFunction(CalleeToken token)
JS_ASSERT(CalleeTokenIsFunction(token));
return (JSFunction *)token;
}
static inline RawFunction
CalleeTokenToParallelFunction(CalleeToken token)
{
JS_ASSERT(GetCalleeTokenTag(token) == CalleeToken_ParallelFunction);
return (JSFunction *)(uintptr_t(token) & ~uintptr_t(0x3));
}
static inline RawScript
CalleeTokenToScript(CalleeToken token)
{
@ -74,6 +86,8 @@ ScriptFromCalleeToken(CalleeToken token)
return CalleeTokenToScript(token);
case CalleeToken_Function:
return CalleeTokenToFunction(token)->nonLazyScript();
case CalleeToken_ParallelFunction:
return CalleeTokenToParallelFunction(token)->nonLazyScript();
}
JS_NOT_REACHED("invalid callee token tag");
return NULL;
@ -253,6 +267,7 @@ struct ResumeFromException
};
void HandleException(ResumeFromException *rfe);
void HandleParallelFailure(ResumeFromException *rfe);
void EnsureExitFrame(IonCommonFrameLayout *frame);

View File

@ -980,6 +980,101 @@ MacroAssembler::loadBaselineFramePtr(Register framePtr, Register dest)
subPtr(Imm32(BaselineFrame::Size()), dest);
}
void
MacroAssembler::enterParallelExitFrameAndLoadSlice(const VMFunction *f, Register slice,
Register scratch)
{
// Load the current ForkJoinSlice *. If we need a parallel exit frame,
// chances are we are about to do something very slow anyways, so just
// call ParForkJoinSlice again instead of using the cached version.
setupUnalignedABICall(0, scratch);
callWithABI(JS_FUNC_TO_DATA_PTR(void *, ParForkJoinSlice));
if (ReturnReg != slice)
movePtr(ReturnReg, slice);
// Load the PerThreadData from from the slice.
loadPtr(Address(slice, offsetof(ForkJoinSlice, perThreadData)), scratch);
linkParallelExitFrame(scratch);
// Push the ioncode.
exitCodePatch_ = PushWithPatch(ImmWord(-1));
// Push the VMFunction pointer, to mark arguments.
Push(ImmWord(f));
}
void
MacroAssembler::enterExitFrameAndLoadContext(const VMFunction *f, Register cxReg, Register scratch,
ExecutionMode executionMode)
{
switch (executionMode) {
case SequentialExecution:
// The scratch register is not used for sequential execution.
enterExitFrame(f);
loadJSContext(cxReg);
break;
case ParallelExecution:
enterParallelExitFrameAndLoadSlice(f, cxReg, scratch);
break;
default:
JS_NOT_REACHED("No such execution mode");
}
}
void
MacroAssembler::handleFailure(ExecutionMode executionMode)
{
// Re-entry code is irrelevant because the exception will leave the
// running function and never come back
if (sps_)
sps_->skipNextReenter();
leaveSPSFrame();
void *handler;
switch (executionMode) {
case SequentialExecution:
handler = JS_FUNC_TO_DATA_PTR(void *, ion::HandleException);
break;
case ParallelExecution:
handler = JS_FUNC_TO_DATA_PTR(void *, ion::HandleParallelFailure);
break;
default:
JS_NOT_REACHED("No such execution mode");
}
MacroAssemblerSpecific::handleFailureWithHandler(handler);
// Doesn't actually emit code, but balances the leave()
if (sps_)
sps_->reenter(*this, InvalidReg);
}
void
MacroAssembler::tagCallee(Register callee, ExecutionMode mode)
{
switch (mode) {
case SequentialExecution:
// CalleeToken_Function is untagged, so we don't need to do anything.
return;
case ParallelExecution:
orPtr(Imm32(CalleeToken_ParallelFunction), callee);
return;
default:
JS_NOT_REACHED("No such execution mode");
}
}
void
MacroAssembler::clearCalleeTag(Register callee, ExecutionMode mode)
{
switch (mode) {
case SequentialExecution:
// CalleeToken_Function is untagged, so we don't need to do anything.
return;
case ParallelExecution:
andPtr(Imm32(~0x3), callee);
return;
default:
JS_NOT_REACHED("No such execution mode");
}
}
void printf0_(const char *output) {
printf("%s", output);
}

View File

@ -593,6 +593,12 @@ class MacroAssembler : public MacroAssemblerSpecific
Push(ImmWord(uintptr_t(NULL)));
}
void enterParallelExitFrameAndLoadSlice(const VMFunction *f, Register slice,
Register scratch);
void enterExitFrameAndLoadContext(const VMFunction *f, Register cxReg, Register scratch,
ExecutionMode executionMode);
void leaveExitFrame() {
freeStack(IonExitFooterFrame::Size());
}
@ -639,17 +645,11 @@ class MacroAssembler : public MacroAssemblerSpecific
}
void handleException() {
// Re-entry code is irrelevant because the exception will leave the
// running function and never come back
if (sps_)
sps_->skipNextReenter();
leaveSPSFrame();
MacroAssemblerSpecific::handleException();
// Doesn't actually emit code, but balances the leave()
if (sps_)
sps_->reenter(*this, InvalidReg);
handleFailure(SequentialExecution);
}
void handleFailure(ExecutionMode executionMode);
// see above comment for what is returned
uint32_t callIon(const Register &callee) {
leaveSPSFrame();
@ -692,6 +692,9 @@ class MacroAssembler : public MacroAssemblerSpecific
return truthy ? Assembler::Zero : Assembler::NonZero;
}
void tagCallee(Register callee, ExecutionMode mode);
void clearCalleeTag(Register callee, ExecutionMode mode);
private:
// These two functions are helpers used around call sites throughout the
// assembler. They are called from the above call wrappers to emit the

View File

@ -11,6 +11,9 @@
#include "jspubtd.h"
namespace js {
class ForkJoinSlice;
namespace ion {
enum DataType {
@ -19,7 +22,8 @@ enum DataType {
Type_Int32,
Type_Object,
Type_Value,
Type_Handle
Type_Handle,
Type_ParallelResult
};
struct PopValues
@ -97,6 +101,9 @@ struct VMFunction
// arguments of the VM wrapper.
uint64_t argumentRootTypes;
// Does this function take a ForkJoinSlice * or a JSContext *?
ExecutionMode executionMode;
// Number of Values the VM wrapper should pop from the stack when it returns.
// Used by baseline IC stubs so that they can use tail calls to call the VM
// wrapper.
@ -169,23 +176,32 @@ struct VMFunction
explicitArgs(0),
argumentProperties(0),
outParam(Type_Void),
returnType(Type_Void)
returnType(Type_Void),
executionMode(SequentialExecution),
extraValuesToPop(0)
{
}
VMFunction(void *wrapped, uint32_t explicitArgs, uint32_t argumentProperties, uint64_t argRootTypes,
DataType outParam, DataType returnType, uint32_t extraValuesToPop = 0)
VMFunction(void *wrapped, uint32_t explicitArgs, uint32_t argumentProperties,
uint64_t argRootTypes, DataType outParam, DataType returnType,
ExecutionMode executionMode, uint32_t extraValuesToPop = 0)
: wrapped(wrapped),
explicitArgs(explicitArgs),
argumentProperties(argumentProperties),
outParam(outParam),
returnType(returnType),
argumentRootTypes(argRootTypes),
executionMode(executionMode),
extraValuesToPop(extraValuesToPop)
{
// Check for valid failure/return type.
JS_ASSERT_IF(outParam != Type_Void, returnType == Type_Bool);
JS_ASSERT(returnType == Type_Bool || returnType == Type_Object);
JS_ASSERT_IF(outParam != Type_Void && executionMode == SequentialExecution,
returnType == Type_Bool);
JS_ASSERT_IF(executionMode == ParallelExecution, returnType == Type_ParallelResult);
JS_ASSERT(returnType == Type_Bool ||
returnType == Type_Object ||
returnType == Type_ParallelResult);
}
VMFunction(const VMFunction &o)
@ -213,6 +229,7 @@ template <> struct TypeToDataType<Handle<StaticBlockObject *> > { static const D
template <> struct TypeToDataType<HandleScript> { static const DataType result = Type_Handle; };
template <> struct TypeToDataType<HandleValue> { static const DataType result = Type_Handle; };
template <> struct TypeToDataType<MutableHandleValue> { static const DataType result = Type_Handle; };
template <> struct TypeToDataType<ParallelResult> { static const DataType result = Type_ParallelResult; };
// Convert argument types to properties of the argument known by the jit.
template <class T> struct TypeToArgProperties {
@ -288,6 +305,14 @@ template <> struct OutParamToDataType<int *> { static const DataType result = Ty
template <> struct OutParamToDataType<uint32_t *> { static const DataType result = Type_Int32; };
template <> struct OutParamToDataType<MutableHandleValue> { static const DataType result = Type_Handle; };
template <class> struct MatchContext { };
template <> struct MatchContext<JSContext *> {
static const ExecutionMode execMode = SequentialExecution;
};
template <> struct MatchContext<ForkJoinSlice *> {
static const ExecutionMode execMode = ParallelExecution;
};
#define FOR_EACH_ARGS_1(Macro, Sep, Last) Macro(1) Last(1)
#define FOR_EACH_ARGS_2(Macro, Sep, Last) FOR_EACH_ARGS_1(Macro, Sep, Sep) Macro(2) Last(2)
#define FOR_EACH_ARGS_3(Macro, Sep, Last) FOR_EACH_ARGS_2(Macro, Sep, Sep) Macro(3) Last(3)
@ -303,6 +328,9 @@ template <> struct OutParamToDataType<MutableHandleValue> { static const DataTyp
#define NOTHING(_)
#define FUNCTION_INFO_STRUCT_BODY(ForEachNb) \
static inline ExecutionMode executionMode() { \
return MatchContext<Context>::execMode; \
} \
static inline DataType returnType() { \
return TypeToDataType<R>::result; \
} \
@ -315,16 +343,17 @@ template <> struct OutParamToDataType<MutableHandleValue> { static const DataTyp
static inline size_t explicitArgs() { \
return NbArgs() - (outParam() != Type_Void ? 1 : 0); \
} \
static inline uint32_t argumentProperties() { \
static inline uint32_t argumentProperties() { \
return ForEachNb(COMPUTE_ARG_PROP, SEP_OR, NOTHING); \
} \
static inline uint64_t argumentRootTypes() { \
static inline uint64_t argumentRootTypes() { \
return ForEachNb(COMPUTE_ARG_ROOT, SEP_OR, NOTHING); \
} \
FunctionInfo(pf fun, PopValues extraValuesToPop = PopValues(0)) \
: VMFunction(JS_FUNC_TO_DATA_PTR(void *, fun), explicitArgs(), \
argumentProperties(),argumentRootTypes(), \
outParam(), returnType(), extraValuesToPop.numValues) \
outParam(), returnType(), executionMode(), \
extraValuesToPop.numValues) \
{ }
template <typename Fun>
@ -332,10 +361,13 @@ struct FunctionInfo {
};
// VMFunction wrapper with no explicit arguments.
template <class R>
struct FunctionInfo<R (*)(JSContext *)> : public VMFunction {
typedef R (*pf)(JSContext *);
template <class R, class Context>
struct FunctionInfo<R (*)(Context)> : public VMFunction {
typedef R (*pf)(Context);
static inline ExecutionMode executionMode() {
return MatchContext<Context>::execMode;
}
static inline DataType returnType() {
return TypeToDataType<R>::result;
}
@ -354,50 +386,51 @@ struct FunctionInfo<R (*)(JSContext *)> : public VMFunction {
FunctionInfo(pf fun)
: VMFunction(JS_FUNC_TO_DATA_PTR(void *, fun), explicitArgs(),
argumentProperties(), argumentRootTypes(),
outParam(), returnType())
outParam(), returnType(), executionMode())
{ }
};
// Specialize the class for each number of argument used by VMFunction.
// Keep it verbose unless you find a readable macro for it.
template <class R, class A1>
struct FunctionInfo<R (*)(JSContext *, A1)> : public VMFunction {
typedef R (*pf)(JSContext *, A1);
template <class R, class Context, class A1>
struct FunctionInfo<R (*)(Context, A1)> : public VMFunction {
typedef R (*pf)(Context, A1);
FUNCTION_INFO_STRUCT_BODY(FOR_EACH_ARGS_1)
};
template <class R, class A1, class A2>
struct FunctionInfo<R (*)(JSContext *, A1, A2)> : public VMFunction {
typedef R (*pf)(JSContext *, A1, A2);
template <class R, class Context, class A1, class A2>
struct FunctionInfo<R (*)(Context, A1, A2)> : public VMFunction {
typedef R (*pf)(Context, A1, A2);
FUNCTION_INFO_STRUCT_BODY(FOR_EACH_ARGS_2)
};
template <class R, class A1, class A2, class A3>
struct FunctionInfo<R (*)(JSContext *, A1, A2, A3)> : public VMFunction {
typedef R (*pf)(JSContext *, A1, A2, A3);
template <class R, class Context, class A1, class A2, class A3>
struct FunctionInfo<R (*)(Context, A1, A2, A3)> : public VMFunction {
typedef R (*pf)(Context, A1, A2, A3);
FUNCTION_INFO_STRUCT_BODY(FOR_EACH_ARGS_3)
};
template <class R, class A1, class A2, class A3, class A4>
struct FunctionInfo<R (*)(JSContext *, A1, A2, A3, A4)> : public VMFunction {
typedef R (*pf)(JSContext *, A1, A2, A3, A4);
template <class R, class Context, class A1, class A2, class A3, class A4>
struct FunctionInfo<R (*)(Context, A1, A2, A3, A4)> : public VMFunction {
typedef R (*pf)(Context, A1, A2, A3, A4);
FUNCTION_INFO_STRUCT_BODY(FOR_EACH_ARGS_4)
};
template <class R, class A1, class A2, class A3, class A4, class A5>
struct FunctionInfo<R (*)(JSContext *, A1, A2, A3, A4, A5)> : public VMFunction {
typedef R (*pf)(JSContext *, A1, A2, A3, A4, A5);
template <class R, class Context, class A1, class A2, class A3, class A4, class A5>
struct FunctionInfo<R (*)(Context, A1, A2, A3, A4, A5)> : public VMFunction {
typedef R (*pf)(Context, A1, A2, A3, A4, A5);
FUNCTION_INFO_STRUCT_BODY(FOR_EACH_ARGS_5)
};
template <class R, class A1, class A2, class A3, class A4, class A5, class A6>
struct FunctionInfo<R (*)(JSContext *, A1, A2, A3, A4, A5, A6)> : public VMFunction {
typedef R (*pf)(JSContext *, A1, A2, A3, A4, A5, A6);
template <class R, class Context, class A1, class A2, class A3, class A4, class A5, class A6>
struct FunctionInfo<R (*)(Context, A1, A2, A3, A4, A5, A6)> : public VMFunction {
typedef R (*pf)(Context, A1, A2, A3, A4, A5, A6);
FUNCTION_INFO_STRUCT_BODY(FOR_EACH_ARGS_6)
};
#undef FUNCTION_INFO_STRUCT_BODY
#undef FOR_EACH_ARGS_6
#undef FOR_EACH_ARGS_5
#undef FOR_EACH_ARGS_4
#undef FOR_EACH_ARGS_3

View File

@ -2830,6 +2830,12 @@ MacroAssemblerARMCompat::linkExitFrame() {
ma_str(StackPointer, Operand(ScratchRegister, 0));
}
void
MacroAssemblerARMCompat::linkParallelExitFrame(const Register &pt)
{
ma_str(StackPointer, Operand(pt, offsetof(PerThreadData, ionTop)));
}
// ARM says that all reads of pc will return 8 higher than the
// address of the currently executing instruction. This means we are
// correctly storing the address of the instruction after the call
@ -3134,7 +3140,7 @@ MacroAssemblerARMCompat::callWithABI(const Address &fun, Result result)
}
void
MacroAssemblerARMCompat::handleException()
MacroAssemblerARMCompat::handleFailureWithHandler(void *handler)
{
// Reserve space for exception information.
int size = (sizeof(ResumeFromException) + 7) & ~7;
@ -3144,7 +3150,7 @@ MacroAssemblerARMCompat::handleException()
// Ask for an exception handler.
setupUnalignedABICall(1, r1);
passABIArg(r0);
callWithABI(JS_FUNC_TO_DATA_PTR(void *, ion::HandleException));
callWithABI(handler);
Label catch_;
Label entryFrame;

View File

@ -972,7 +972,8 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM
}
void linkExitFrame();
void handleException();
void linkParallelExitFrame(const Register &pt);
void handleFailureWithHandler(void *handler);
/////////////////////////////////////////////////////////////////
// Common interface.

View File

@ -367,6 +367,7 @@ IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
// Load the number of |undefined|s to push into r6.
masm.ma_ldr(DTRAddr(sp, DtrOffImm(IonRectifierFrameLayout::offsetOfCalleeToken())), r1);
masm.clearCalleeTag(r1, mode);
masm.ma_ldrh(EDtrAddr(r1, EDtrOffImm(offsetof(JSFunction, nargs))), r6);
masm.ma_sub(r6, r8, r2);
@ -603,13 +604,16 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
// Wrapper register set is a superset of Volatile register set.
JS_STATIC_ASSERT((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0);
// The context is the first argument; r0 is the first argument register.
Register cxreg = r0;
// Stack is:
// ... frame ...
// +8 [args] + argPadding
// +0 ExitFrame
//
// We're aligned to an exit frame, so link it up.
masm.enterExitFrame(&f);
masm.enterExitFrameAndLoadContext(&f, cxreg, regs.getAny(), f.executionMode);
// Save the base of the argument set stored on the stack.
Register argsBase = InvalidReg;
@ -648,13 +652,7 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
break;
}
Register temp = regs.getAny();
masm.setupUnalignedABICall(f.argc(), temp);
// Initialize and set the context parameter.
// r0 is the first argument register.
Register cxreg = r0;
masm.loadJSContext(cxreg);
masm.setupUnalignedABICall(f.argc(), regs.getAny());
masm.passABIArg(cxreg);
size_t argDisp = 0;
@ -692,10 +690,20 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
masm.callWithABI(f.wrapped);
// Test for failure.
Label exception;
// Called functions return bools, which are 0/false and non-zero/true
masm.ma_cmp(r0, Imm32(0));
masm.ma_b(&exception, Assembler::Zero);
Label failure;
switch (f.failType()) {
case Type_Object:
case Type_Bool:
// Called functions return bools, which are 0/false and non-zero/true
masm.branch32(Assembler::Equal, r0, Imm32(0), &failure);
break;
case Type_ParallelResult:
masm.branch32(Assembler::NotEqual, r0, Imm32(TP_SUCCESS), &failure);
break;
default:
JS_NOT_REACHED("unknown failure kind");
break;
}
// Load the outparam and free any allocated stack.
switch (f.outParam) {
@ -719,8 +727,8 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
f.explicitStackSlots() * sizeof(void *) +
f.extraValuesToPop * sizeof(Value)));
masm.bind(&exception);
masm.handleException();
masm.bind(&failure);
masm.handleFailure(f.executionMode);
Linker linker(masm);
IonCode *wrapper = linker.newCode(cx, JSC::OTHER_CODE);

View File

@ -463,12 +463,16 @@ class CodeLocationJump
void repoint(IonCode *code, MacroAssembler* masm = NULL);
bool isSet() const {
return raw_ != (uint8_t *) 0xdeadc0de;
}
uint8_t *raw() const {
JS_ASSERT(absolute_ && raw_ != (uint8_t *) 0xdeadc0de);
JS_ASSERT(absolute_ && isSet());
return raw_;
}
uint8_t *offset() const {
JS_ASSERT(!absolute_ && raw_ != (uint8_t *) 0xdeadc0de);
JS_ASSERT(!absolute_ && isSet());
return raw_;
}
@ -526,12 +530,16 @@ class CodeLocationLabel
void repoint(IonCode *code, MacroAssembler *masm = NULL);
bool isSet() {
return raw_ != (uint8_t *) 0xdeadc0de;
}
uint8_t *raw() {
JS_ASSERT(absolute_ && raw_ != (uint8_t *) 0xdeadc0de);
JS_ASSERT(absolute_ && isSet());
return raw_;
}
uint8_t *offset() {
JS_ASSERT(!absolute_ && raw_ != (uint8_t *) 0xdeadc0de);
JS_ASSERT(!absolute_ && isSet());
return raw_;
}
};

View File

@ -173,6 +173,7 @@ class CodeGeneratorShared : public LInstructionVisitor
return index;
}
public:
// This is needed by addCache to update the cache with the jump
// informations provided by the out-of-line path.
IonCache *getCache(size_t index) {

View File

@ -180,7 +180,7 @@ MacroAssemblerX64::callWithABI(Address fun, Result result)
}
void
MacroAssemblerX64::handleException()
MacroAssemblerX64::handleFailureWithHandler(void *handler)
{
// Reserve space for exception information.
subq(Imm32(sizeof(ResumeFromException)), rsp);
@ -189,7 +189,7 @@ MacroAssemblerX64::handleException()
// Ask for an exception handler.
setupUnalignedABICall(1, rcx);
passABIArg(rax);
callWithABI(JS_FUNC_TO_DATA_PTR(void *, ion::HandleException));
callWithABI(handler);
Label catch_;
Label entryFrame;

View File

@ -975,7 +975,7 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
void callWithABI(void *fun, Result result = GENERAL);
void callWithABI(Address fun, Result result = GENERAL);
void handleException();
void handleFailureWithHandler(void *handler);
void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
shlq(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
@ -983,7 +983,7 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
}
// Save an exit frame (which must be aligned to the stack pointer) to
// ThreadData::ionTop.
// ThreadData::ionTop of the main thread.
void linkExitFrame() {
mov(ImmWord(GetIonContext()->runtime), ScratchReg);
mov(StackPointer, Operand(ScratchReg, offsetof(JSRuntime, mainThread.ionTop)));
@ -996,6 +996,12 @@ class MacroAssemblerX64 : public MacroAssemblerX86Shared
call(target);
}
// Save an exit frame to the thread data of the current thread, given a
// register that holds a PerThreadData *.
void linkParallelExitFrame(const Register &pt) {
mov(StackPointer, Operand(pt, offsetof(PerThreadData, ionTop)));
}
void enterOsr(Register calleeToken, Register code) {
push(Imm32(0)); // num actual args.
push(calleeToken);

View File

@ -333,6 +333,7 @@ IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
// Load the number of |undefined|s to push into %rcx.
masm.movq(Operand(rsp, IonRectifierFrameLayout::offsetOfCalleeToken()), rax);
masm.clearCalleeTag(rax, mode);
masm.movzwl(Operand(rax, offsetof(JSFunction, nargs)), rcx);
masm.subq(r8, rcx);
@ -501,6 +502,9 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
// Wrapper register set is a superset of Volatile register set.
JS_STATIC_ASSERT((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0);
// The context is the first argument.
Register cxreg = IntArgReg0;
// Stack is:
// ... frame ...
// +12 [args]
@ -508,7 +512,7 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
// +0 returnAddress
//
// We're aligned to an exit frame, so link it up.
masm.enterExitFrame(&f);
masm.enterExitFrameAndLoadContext(&f, cxreg, regs.getAny(), f.executionMode);
// Save the current stack pointer as the base for copying arguments.
Register argsBase = InvalidReg;
@ -544,12 +548,7 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
break;
}
Register temp = regs.getAny();
masm.setupUnalignedABICall(f.argc(), temp);
// Initialize the context parameter.
Register cxreg = IntArgReg0;
masm.loadJSContext(cxreg);
masm.setupUnalignedABICall(f.argc(), regs.getAny());
masm.passABIArg(cxreg);
size_t argDisp = 0;
@ -582,15 +581,17 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
masm.callWithABI(f.wrapped);
// Test for failure.
Label exception;
Label failure;
switch (f.failType()) {
case Type_Object:
masm.testq(rax, rax);
masm.j(Assembler::Zero, &exception);
masm.branchTestPtr(Assembler::Zero, rax, rax, &failure);
break;
case Type_Bool:
masm.testb(rax, rax);
masm.j(Assembler::Zero, &exception);
masm.j(Assembler::Zero, &failure);
break;
case Type_ParallelResult:
masm.branchPtr(Assembler::NotEqual, rax, Imm32(TP_SUCCESS), &failure);
break;
default:
JS_NOT_REACHED("unknown failure kind");
@ -619,8 +620,8 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
f.explicitStackSlots() * sizeof(void *) +
f.extraValuesToPop * sizeof(Value)));
masm.bind(&exception);
masm.handleException();
masm.bind(&failure);
masm.handleFailure(f.executionMode);
Linker linker(masm);
IonCode *wrapper = linker.newCode(cx, JSC::OTHER_CODE);

View File

@ -70,7 +70,9 @@ class Registers {
(1 << JSC::X86Registers::edi) |
(1 << JSC::X86Registers::ebp);
static const uint32_t WrapperMask = VolatileMask;
static const uint32_t WrapperMask =
VolatileMask |
(1 << JSC::X86Registers::ebx);
static const uint32_t SingleByteRegs =
(1 << JSC::X86Registers::eax) |

View File

@ -195,7 +195,7 @@ MacroAssemblerX86::callWithABI(const Address &fun, Result result)
}
void
MacroAssemblerX86::handleException()
MacroAssemblerX86::handleFailureWithHandler(void *handler)
{
// Reserve space for exception information.
subl(Imm32(sizeof(ResumeFromException)), esp);
@ -204,7 +204,7 @@ MacroAssemblerX86::handleException()
// Ask for an exception handler.
setupUnalignedABICall(1, ecx);
passABIArg(eax);
callWithABI(JS_FUNC_TO_DATA_PTR(void *, ion::HandleException));
callWithABI(handler);
Label catch_;
Label entryFrame;

View File

@ -854,7 +854,7 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
void callWithABI(const Address &fun, Result result = GENERAL);
// Used from within an Exit frame to handle a pending exception.
void handleException();
void handleFailureWithHandler(void *handler);
void makeFrameDescriptor(Register frameSizeReg, FrameType type) {
shll(Imm32(FRAMESIZE_SHIFT), frameSizeReg);
@ -862,7 +862,7 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
}
// Save an exit frame (which must be aligned to the stack pointer) to
// ThreadData::ionTop.
// ThreadData::ionTop of the main thread.
void linkExitFrame() {
JSCompartment *compartment = GetIonContext()->compartment;
movl(StackPointer, Operand(&compartment->rt->mainThread.ionTop));
@ -875,6 +875,12 @@ class MacroAssemblerX86 : public MacroAssemblerX86Shared
call(target);
}
// Save an exit frame to the thread data of the current thread, given a
// register that holds a PerThreadData *.
void linkParallelExitFrame(const Register &pt) {
movl(StackPointer, Operand(pt, offsetof(PerThreadData, ionTop)));
}
void enterOsr(Register calleeToken, Register code) {
push(Imm32(0)); // num actual args.
push(calleeToken);

View File

@ -315,6 +315,7 @@ IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void *
// Load the number of |undefined|s to push into %ecx.
masm.movl(Operand(esp, IonRectifierFrameLayout::offsetOfCalleeToken()), eax);
masm.clearCalleeTag(eax, mode);
masm.movzwl(Operand(eax, offsetof(JSFunction, nargs)), ecx);
masm.subl(esi, ecx);
@ -521,6 +522,9 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
// Wrapper register set is a superset of Volatile register set.
JS_STATIC_ASSERT((Register::Codes::VolatileMask & ~Register::Codes::WrapperMask) == 0);
// The context is the first argument.
Register cxreg = regs.takeAny();
// Stack is:
// ... frame ...
// +8 [args]
@ -528,7 +532,7 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
// +0 returnAddress
//
// We're aligned to an exit frame, so link it up.
masm.enterExitFrame(&f);
masm.enterExitFrameAndLoadContext(&f, cxreg, regs.getAny(), f.executionMode);
// Save the current stack pointer as the base for copying arguments.
Register argsBase = InvalidReg;
@ -563,12 +567,7 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
break;
}
Register temp = regs.getAny();
masm.setupUnalignedABICall(f.argc(), temp);
// Initialize the context parameter.
Register cxreg = regs.takeAny();
masm.loadJSContext(cxreg);
masm.setupUnalignedABICall(f.argc(), regs.getAny());
masm.passABIArg(cxreg);
size_t argDisp = 0;
@ -607,15 +606,17 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
masm.callWithABI(f.wrapped);
// Test for failure.
Label exception;
Label failure;
switch (f.failType()) {
case Type_Object:
masm.testl(eax, eax);
masm.j(Assembler::Zero, &exception);
masm.branchTestPtr(Assembler::Zero, eax, eax, &failure);
break;
case Type_Bool:
masm.testb(eax, eax);
masm.j(Assembler::Zero, &exception);
masm.j(Assembler::Zero, &failure);
break;
case Type_ParallelResult:
masm.branchPtr(Assembler::NotEqual, eax, Imm32(TP_SUCCESS), &failure);
break;
default:
JS_NOT_REACHED("unknown failure kind");
@ -644,8 +645,8 @@ IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
f.explicitStackSlots() * sizeof(void *) +
f.extraValuesToPop * sizeof(Value)));
masm.bind(&exception);
masm.handleException();
masm.bind(&failure);
masm.handleFailure(f.executionMode);
Linker linker(masm);
IonCode *wrapper = linker.newCode(cx, JSC::OTHER_CODE);