Skip to content

Commit

Permalink
Bug 965880 - OdinMonkey: allow asm.js to change (resize) heap (r=bbou…
Browse files Browse the repository at this point in the history
…vier)
  • Loading branch information
Luke Wagner committed Oct 7, 2014
1 parent 728e340 commit 8322b99
Show file tree
Hide file tree
Showing 13 changed files with 904 additions and 145 deletions.
90 changes: 62 additions & 28 deletions js/src/asmjs/AsmJSLink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,30 @@ ValidateArrayView(JSContext *cx, AsmJSModule::Global &global, HandleValue global
return true;
}

static bool
ValidateByteLength(JSContext *cx, HandleValue globalVal)
{
RootedPropertyName field(cx, cx->names().byteLength);
RootedValue v(cx);
if (!GetDataProperty(cx, globalVal, field, &v))
return false;

if (!v.isObject() || !v.toObject().isBoundFunction())
return LinkFail(cx, "byteLength must be a bound function object");

RootedFunction fun(cx, &v.toObject().as<JSFunction>());

RootedValue boundTarget(cx, ObjectValue(*fun->getBoundFunctionTarget()));
if (!IsNativeFunction(boundTarget, js_fun_call))
return LinkFail(cx, "bound target of byteLength must be Function.prototype.call");

RootedValue boundThis(cx, fun->getBoundFunctionThis());
if (!IsNativeFunction(boundThis, ArrayBufferObject::byteLengthGetter))
return LinkFail(cx, "bound this value must be ArrayBuffer.protototype.byteLength accessor");

return true;
}

static bool
ValidateMathBuiltinFunction(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal)
{
Expand Down Expand Up @@ -441,9 +465,8 @@ LinkModuleToHeap(JSContext *cx, AsmJSModule &module, Handle<ArrayBufferObjectMay
MOZ_ASSERT((module.minHeapLength() - 1) <= INT32_MAX);
if (heapLength < module.minHeapLength()) {
ScopedJSFreePtr<char> msg(
JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (which is the "
"largest constant heap access offset rounded up to the next valid "
"heap size).",
JS_smprintf("ArrayBuffer byteLength of 0x%x is less than 0x%x (the size implied "
"by const heap accesses and/or change-heap minimum-length requirements).",
heapLength,
module.minHeapLength()));
return LinkFail(cx, msg.get());
Expand Down Expand Up @@ -472,17 +495,9 @@ DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
{
module.setIsDynamicallyLinked();

RootedValue globalVal(cx);
if (args.length() > 0)
globalVal = args[0];

RootedValue importVal(cx);
if (args.length() > 1)
importVal = args[1];

RootedValue bufferVal(cx);
if (args.length() > 2)
bufferVal = args[2];
HandleValue globalVal = args.get(0);
HandleValue importVal = args.get(1);
HandleValue bufferVal = args.get(2);

Rooted<ArrayBufferObjectMaybeShared *> heap(cx);
if (module.hasArrayView()) {
Expand Down Expand Up @@ -514,6 +529,10 @@ DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
if (!ValidateArrayView(cx, global, globalVal))
return false;
break;
case AsmJSModule::Global::ByteLength:
if (!ValidateByteLength(cx, globalVal))
return false;
break;
case AsmJSModule::Global::MathBuiltinFunction:
if (!ValidateMathBuiltinFunction(cx, global, globalVal))
return false;
Expand Down Expand Up @@ -541,12 +560,32 @@ DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module)
return true;
}

static bool
ChangeHeap(JSContext *cx, AsmJSModule &module, CallArgs args)
{
HandleValue bufferArg = args.get(0);
if (!IsArrayBuffer(bufferArg)) {
ReportIncompatible(cx, args);
return false;
}

Rooted<ArrayBufferObject*> newBuffer(cx, &bufferArg.toObject().as<ArrayBufferObject>());
bool rval = module.changeHeap(newBuffer, cx);

args.rval().set(BooleanValue(rval));
return true;
}

// An asm.js function stores, in its extended slots:
// - a pointer to the module from which it was returned
// - its index in the ordered list of exported functions
static const unsigned ASM_MODULE_SLOT = 0;
static const unsigned ASM_EXPORT_INDEX_SLOT = 1;

static unsigned
FunctionToExportedFunctionIndex(HandleFunction fun)
{
MOZ_ASSERT(IsAsmJSFunction(fun));
Value v = fun->getExtendedSlot(ASM_EXPORT_INDEX_SLOT);
return v.toInt32();
}
Expand All @@ -564,18 +603,18 @@ FunctionToEnclosingModule(HandleFunction fun)
return fun->getExtendedSlot(ASM_MODULE_SLOT).toObject().as<AsmJSModuleObject>().module();
}

// The JSNative for the functions nested in an asm.js module. Calling this
// native will trampoline into generated code.
// This is the js::Native for functions exported by an asm.js module.
static bool
CallAsmJS(JSContext *cx, unsigned argc, Value *vp)
{
CallArgs callArgs = CallArgsFromVp(argc, vp);
RootedFunction callee(cx, &callArgs.callee().as<JSFunction>());

// An asm.js function stores, in its extended slots:
// - a pointer to the module from which it was returned
// - its index in the ordered list of exported functions
AsmJSModule &module = FunctionToEnclosingModule(callee);
const AsmJSModule::ExportedFunction &func = FunctionToExportedFunction(callee, module);

// The heap-changing function is a special-case and is implemented by C++.
if (func.isChangeHeap())
return ChangeHeap(cx, module, callArgs);

// Enable/disable profiling in the asm.js module to match the current global
// profiling state. Don't do this if the module is already active on the
Expand All @@ -584,11 +623,6 @@ CallAsmJS(JSContext *cx, unsigned argc, Value *vp)
if (module.profilingEnabled() != cx->runtime()->spsProfiler.enabled() && !module.active())
module.setProfilingEnabled(cx->runtime()->spsProfiler.enabled(), cx);

// An exported function points to the code as well as the exported
// function's signature, which implies the dynamic coercions performed on
// the arguments.
const AsmJSModule::ExportedFunction &func = FunctionToExportedFunction(callee, module);

// The calling convention for an external call into asm.js is to pass an
// array of 16-byte values where each value contains either a coerced int32
// (in the low word), a double value (in the low dword) or a SIMD vector
Expand Down Expand Up @@ -704,9 +738,9 @@ NewExportedFunction(JSContext *cx, const AsmJSModule::ExportedFunction &func,
HandleObject moduleObj, unsigned exportIndex)
{
RootedPropertyName name(cx, func.name());
JSFunction *fun = NewFunction(cx, NullPtr(), CallAsmJS, func.numArgs(),
JSFunction::ASMJS_CTOR, cx->global(), name,
JSFunction::ExtendedFinalizeKind);
unsigned numArgs = func.isChangeHeap() ? 1 : func.numArgs();
JSFunction *fun = NewFunction(cx, NullPtr(), CallAsmJS, numArgs, JSFunction::ASMJS_CTOR,
cx->global(), name, JSFunction::ExtendedFinalizeKind);
if (!fun)
return nullptr;

Expand Down
64 changes: 45 additions & 19 deletions js/src/asmjs/AsmJSModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -337,8 +337,10 @@ AsmJSModule::finish(ExclusiveContext *cx, TokenStream &tokenStream, MacroAssembl
AsmJSHeapAccess &a = heapAccesses_[i];
a.setOffset(masm.actualOffset(a.offset()));
}
for (unsigned i = 0; i < numExportedFunctions(); i++)
exportedFunction(i).updateCodeOffset(masm);
for (unsigned i = 0; i < numExportedFunctions(); i++) {
if (!exportedFunction(i).isChangeHeap())
exportedFunction(i).updateCodeOffset(masm);
}
for (unsigned i = 0; i < numExits(); i++)
exit(i).updateOffsets(masm);
for (size_t i = 0; i < callSites_.length(); i++) {
Expand Down Expand Up @@ -765,13 +767,13 @@ AsmJSModule::initHeap(Handle<ArrayBufferObjectMaybeShared *> heap, JSContext *cx
X86Assembler::setPointer(addr, (void *)(heapOffset + disp));
}
#elif defined(JS_CODEGEN_X64)
int32_t heapLength = int32_t(intptr_t(heap->byteLength()));
if (usesSignalHandlersForOOB())
return;
// If we cannot use the signal handlers, we need to patch the heap length
// checks at the right places. All accesses that have been recorded are the
// only ones that need bound checks (see also
// CodeGeneratorX64::visitAsmJS{Load,Store}Heap)
int32_t heapLength = int32_t(intptr_t(heap->byteLength()));
for (size_t i = 0; i < heapAccesses_.length(); i++) {
const jit::AsmJSHeapAccess &access = heapAccesses_[i];
if (access.hasLengthCheck())
Expand All @@ -787,8 +789,29 @@ AsmJSModule::initHeap(Handle<ArrayBufferObjectMaybeShared *> heap, JSContext *cx
}

void
AsmJSModule::restoreToInitialState(uint8_t *prevCode,
ArrayBufferObjectMaybeShared *maybePrevBuffer,
AsmJSModule::restoreHeapToInitialState(ArrayBufferObjectMaybeShared *maybePrevBuffer)
{
#if defined(JS_CODEGEN_X86)
if (maybePrevBuffer) {
// Subtract out the base-pointer added by AsmJSModule::initHeap.
uint8_t *ptrBase = maybePrevBuffer->dataPointer();
for (unsigned i = 0; i < heapAccesses_.length(); i++) {
const jit::AsmJSHeapAccess &access = heapAccesses_[i];
void *addr = access.patchOffsetAt(code_);
uint8_t *ptr = reinterpret_cast<uint8_t*>(X86Assembler::getPointer(addr));
MOZ_ASSERT(ptr >= ptrBase);
X86Assembler::setPointer(addr, (void *)(ptr - ptrBase));
}
}
#endif

maybeHeap_ = nullptr;
heapDatum() = nullptr;
}

void
AsmJSModule::restoreToInitialState(ArrayBufferObjectMaybeShared *maybePrevBuffer,
uint8_t *prevCode,
ExclusiveContext *cx)
{
#ifdef DEBUG
Expand Down Expand Up @@ -817,19 +840,7 @@ AsmJSModule::restoreToInitialState(uint8_t *prevCode,
}
#endif

if (maybePrevBuffer) {
#if defined(JS_CODEGEN_X86)
// Subtract out the base-pointer added by AsmJSModule::initHeap.
uint8_t *ptrBase = maybePrevBuffer->dataPointer();
for (unsigned i = 0; i < heapAccesses_.length(); i++) {
const jit::AsmJSHeapAccess &access = heapAccesses_[i];
void *addr = access.patchOffsetAt(code_);
uint8_t *ptr = reinterpret_cast<uint8_t*>(X86Assembler::getPointer(addr));
MOZ_ASSERT(ptr >= ptrBase);
X86Assembler::setPointer(addr, (void *)(ptr - ptrBase));
}
#endif
}
restoreHeapToInitialState(maybePrevBuffer);
}

static void
Expand Down Expand Up @@ -1550,7 +1561,22 @@ AsmJSModule::clone(JSContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) con
// flush all of them at once.
out.setAutoFlushICacheRange();

out.restoreToInitialState(code_, maybeHeap_, cx);
out.restoreToInitialState(maybeHeap_, code_, cx);
return true;
}

bool
AsmJSModule::changeHeap(Handle<ArrayBufferObject*> newBuffer, JSContext *cx)
{
uint32_t heapLength = newBuffer->byteLength();
if (heapLength & pod.heapLengthMask_ || heapLength < pod.minHeapLength_)
return false;

MOZ_ASSERT(IsValidAsmJSHeapLength(heapLength));
MOZ_ASSERT(!IsDeprecatedAsmJSHeapLength(heapLength));

restoreHeapToInitialState(maybeHeap_);
initHeap(newBuffer, cx);
return true;
}

Expand Down
Loading

0 comments on commit 8322b99

Please sign in to comment.