Bug 1244405 - Odin: refactor ArrayBufferObject::createForWasm out of prepareForAsmJS (r=bbouvier)

--HG--
extra : commitid : 4AqlsoyxJtM
This commit is contained in:
Luke Wagner 2016-02-04 21:37:00 -06:00
parent 9d91571b05
commit e3155ac2ba
3 changed files with 159 additions and 123 deletions

View File

@ -600,7 +600,7 @@ void
Module::specializeToHeap(ArrayBufferObjectMaybeShared* heap)
{
MOZ_ASSERT(usesHeap());
MOZ_ASSERT_IF(heap->is<ArrayBufferObject>(), heap->as<ArrayBufferObject>().isAsmJS());
MOZ_ASSERT_IF(heap->is<ArrayBufferObject>(), heap->as<ArrayBufferObject>().isWasm());
MOZ_ASSERT(!heap_);
MOZ_ASSERT(!rawHeapPtr());

View File

@ -254,7 +254,7 @@ NoteViewBufferWasDetached(ArrayBufferViewObject* view,
ArrayBufferObject::detach(JSContext* cx, Handle<ArrayBufferObject*> buffer,
BufferContents newContents)
{
if (buffer->isAsmJS()) {
if (buffer->isWasm()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY);
return false;
}
@ -362,9 +362,145 @@ ArrayBufferObject::changeContents(JSContext* cx, BufferContents newContents)
changeViewContents(cx, firstView(), oldDataPointer, newContents);
}
/* static */ bool
ArrayBufferObject::prepareForAsmJSNoSignals(JSContext* cx, Handle<ArrayBufferObject*> buffer)
#ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
# ifdef XP_WIN
static void*
AllocateWasmMappedMemory(uint32_t numBytes)
{
MOZ_ASSERT(numBytes % wasm::PageSize == 0);
void* data = VirtualAlloc(nullptr, wasm::MappedSize, MEM_RESERVE, PAGE_NOACCESS);
if (!data)
return nullptr;
if (!VirtualAlloc(data, numBytes, MEM_COMMIT, PAGE_READWRITE)) {
VirtualFree(data, 0, MEM_RELEASE);
return nullptr;
}
MemProfiler::SampleNative(data, numBytes);
return data;
}
static void
ReleaseWasmMappedMemory(void* base)
{
VirtualFree(base, 0, MEM_RELEASE);
MemProfiler::RemoveNative(base);
}
# else // XP_WIN
static void*
AllocateWasmMappedMemory(uint32_t numBytes)
{
void* data = MozTaggedAnonymousMmap(nullptr, wasm::MappedSize, PROT_NONE,
MAP_PRIVATE | MAP_ANON, -1, 0, "wasm-reserved");
if (data == MAP_FAILED)
return nullptr;
if (mprotect(data, numBytes, PROT_READ | PROT_WRITE)) {
munmap(data, wasm::MappedSize);
return nullptr;
}
MemProfiler::SampleNative(data, numBytes);
# if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)
VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)data + numBytes,
wasm::MappedSize - numBytes);
# endif
return data;
}
static void
ReleaseWasmMappedMemory(void* base)
{
munmap(base, wasm::MappedSize);
MemProfiler::RemoveNative(base);
# if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(base, wasm::MappedSize);
# endif
}
# endif // !XP_WIN
#endif // ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
/* static */ ArrayBufferObject*
ArrayBufferObject::createForWasm(JSContext* cx, uint32_t numBytes, bool signalsForOOB)
{
MOZ_ASSERT(numBytes % wasm::PageSize == 0);
if (signalsForOOB) {
#ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
void* data = AllocateWasmMappedMemory(numBytes);
if (!data) {
ReportOutOfMemory(cx);
return nullptr;
}
BufferContents contents = BufferContents::create<WASM_MAPPED>(data);
ArrayBufferObject* buffer = ArrayBufferObject::create(cx, numBytes, contents);
if (!buffer) {
ReleaseWasmMappedMemory(data);
return nullptr;
}
return buffer;
#else
MOZ_CRASH("shouldn't be using signal handlers for out-of-bounds");
#endif
}
auto buffer = ArrayBufferObject::create(cx, numBytes);
if (!buffer)
return nullptr;
buffer->setIsWasmMalloced();
return buffer;
}
/* static */ bool
ArrayBufferObject::prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buffer, bool signalsForOOB)
{
MOZ_ASSERT(buffer->byteLength() % wasm::PageSize == 0);
if (signalsForOOB) {
#ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
if (buffer->isWasmMapped())
return true;
// This can't happen except via the shell toggling signals.enabled.
if (buffer->isWasmMalloced()) {
JS_ReportError(cx, "can't access same buffer with and without signals enabled");
return false;
}
if (buffer->forInlineTypedObject()) {
JS_ReportError(cx, "ArrayBuffer can't be used by asm.js");
return false;
}
void* data = AllocateWasmMappedMemory(buffer->byteLength());
if (!data) {
ReportOutOfMemory(cx);
return false;
}
// Copy over the current contents of the typed array.
memcpy(data, buffer->dataPointer(), buffer->byteLength());
// Swap the new elements into the ArrayBufferObject. Mark the
// ArrayBufferObject so we don't do this again.
BufferContents newContents = BufferContents::create<WASM_MAPPED>(data);
buffer->changeContents(cx, newContents);
MOZ_ASSERT(data == buffer->dataPointer());
return true;
#else
MOZ_CRASH("shouldn't be using signal handlers for out-of-bounds");
#endif // ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
}
if (buffer->forInlineTypedObject()) {
JS_ReportError(cx, "ArrayBuffer can't be used by asm.js");
return false;
@ -378,89 +514,10 @@ ArrayBufferObject::prepareForAsmJSNoSignals(JSContext* cx, Handle<ArrayBufferObj
buffer->changeContents(cx, contents);
}
buffer->setIsAsmJSMalloced();
buffer->setIsWasmMalloced();
return true;
}
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
/* static */ bool
ArrayBufferObject::prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buffer,
bool usesSignalHandlers)
{
// If we can't use signal handlers, just do it like on other platforms.
if (!usesSignalHandlers)
return prepareForAsmJSNoSignals(cx, buffer);
if (buffer->isAsmJSMapped())
return true;
// This can't happen except via the shell toggling signals.enabled.
if (buffer->isAsmJSMalloced()) {
JS_ReportError(cx, "can't access same buffer with and without signals enabled");
return false;
}
if (buffer->forInlineTypedObject()) {
JS_ReportError(cx, "ArrayBuffer can't be used by asm.js");
return false;
}
// Get the entire reserved region (with all pages inaccessible).
void* data;
# ifdef XP_WIN
data = VirtualAlloc(nullptr, wasm::MappedSize, MEM_RESERVE, PAGE_NOACCESS);
if (!data)
return false;
# else
data = MozTaggedAnonymousMmap(nullptr, wasm::MappedSize, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0, "asm-js-reserved");
if (data == MAP_FAILED)
return false;
# endif
// Enable access to the valid region.
MOZ_ASSERT(buffer->byteLength() % wasm::PageSize == 0);
# ifdef XP_WIN
if (!VirtualAlloc(data, buffer->byteLength(), MEM_COMMIT, PAGE_READWRITE)) {
VirtualFree(data, 0, MEM_RELEASE);
MemProfiler::RemoveNative(data);
return false;
}
# else
size_t validLength = buffer->byteLength();
if (mprotect(data, validLength, PROT_READ | PROT_WRITE)) {
munmap(data, wasm::MappedSize);
MemProfiler::RemoveNative(data);
return false;
}
# if defined(MOZ_VALGRIND) && defined(VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE)
// Tell Valgrind/Memcheck to not report accesses in the inaccessible region.
VALGRIND_DISABLE_ADDR_ERROR_REPORTING_IN_RANGE((unsigned char*)data + validLength,
wasm::MappedSize - validLength);
# endif
# endif
// Copy over the current contents of the typed array.
memcpy(data, buffer->dataPointer(), buffer->byteLength());
// Swap the new elements into the ArrayBufferObject. Mark the
// ArrayBufferObject so we don't do this again.
BufferContents newContents = BufferContents::create<ASMJS_MAPPED>(data);
buffer->changeContents(cx, newContents);
MOZ_ASSERT(data == buffer->dataPointer());
return true;
}
#else // ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
bool
ArrayBufferObject::prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buffer,
bool usesSignalHandlers)
{
// Just use the variant with no signals.
MOZ_ASSERT(!usesSignalHandlers);
return prepareForAsmJSNoSignals(cx, buffer);
}
#endif
ArrayBufferObject::BufferContents
ArrayBufferObject::createMappedContents(int fd, size_t offset, size_t length)
{
@ -487,30 +544,6 @@ ArrayBufferObject::dataPointerShared() const
return SharedMem<uint8_t*>::unshared(getSlot(DATA_SLOT).toPrivate());
}
#if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
static void
ReleaseAsmJSMappedData(void* base)
{
# ifdef XP_WIN
VirtualFree(base, 0, MEM_RELEASE);
# else
munmap(base, wasm::MappedSize);
# if defined(MOZ_VALGRIND) && defined(VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE)
// Tell Valgrind/Memcheck to recommence reporting accesses in the
// previously-inaccessible region.
VALGRIND_ENABLE_ADDR_ERROR_REPORTING_IN_RANGE(base, wasm::MappedSize);
# endif
# endif
MemProfiler::RemoveNative(base);
}
#else
static void
ReleaseAsmJSMappedData(void* base)
{
MOZ_CRASH("asm.js only uses mapped buffers when using signal-handler OOB checking");
}
#endif
void
ArrayBufferObject::releaseData(FreeOp* fop)
{
@ -518,15 +551,19 @@ ArrayBufferObject::releaseData(FreeOp* fop)
switch (bufferKind()) {
case PLAIN:
case ASMJS_MALLOCED:
case WASM_MALLOCED:
fop->free_(dataPointer());
break;
case MAPPED:
MemProfiler::RemoveNative(dataPointer());
DeallocateMappedContent(dataPointer(), byteLength());
break;
case ASMJS_MAPPED:
ReleaseAsmJSMappedData(dataPointer());
case WASM_MAPPED:
#ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
ReleaseWasmMappedMemory(dataPointer());
#else
MOZ_CRASH("shouldn't have wasm mapped ArrayBuffer");
#endif
break;
}
}
@ -722,10 +759,10 @@ ArrayBufferObject::addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSizeOf m
case MAPPED:
info->objectsNonHeapElementsMapped += buffer.byteLength();
break;
case ASMJS_MALLOCED:
case WASM_MALLOCED:
info->objectsMallocHeapElementsAsmJS += mallocSizeOf(buffer.dataPointer());
break;
case ASMJS_MAPPED:
case WASM_MAPPED:
info->objectsNonHeapElementsAsmJS += buffer.byteLength();
break;
}

View File

@ -123,8 +123,8 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
enum BufferKind {
PLAIN = 0, // malloced or inline data
ASMJS_MALLOCED = 1,
ASMJS_MAPPED = 2,
WASM_MALLOCED = 1,
WASM_MAPPED = 2,
MAPPED = 3,
KIND_MASK = 0x3
@ -255,7 +255,7 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
// Return whether the buffer is allocated by js_malloc and should be freed
// with js_free.
bool hasMallocedContents() const {
return (ownsData() && isPlain()) || isAsmJSMalloced();
return (ownsData() && isPlain()) || isWasmMalloced();
}
static void addSizeOfExcludingThis(JSObject* obj, mozilla::MallocSizeOf mallocSizeOf,
@ -308,15 +308,14 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
BufferKind bufferKind() const { return BufferKind(flags() & BUFFER_KIND_MASK); }
bool isPlain() const { return bufferKind() == PLAIN; }
bool isAsmJSMapped() const { return bufferKind() == ASMJS_MAPPED; }
bool isAsmJSMalloced() const { return bufferKind() == ASMJS_MALLOCED; }
bool isAsmJS() const { return isAsmJSMapped() || isAsmJSMalloced(); }
bool isWasmMapped() const { return bufferKind() == WASM_MAPPED; }
bool isWasmMalloced() const { return bufferKind() == WASM_MALLOCED; }
bool isWasm() const { return isWasmMapped() || isWasmMalloced(); }
bool isMapped() const { return bufferKind() == MAPPED; }
bool isDetached() const { return flags() & DETACHED; }
static bool prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buffer,
bool usesSignalHandlers);
static bool prepareForAsmJSNoSignals(JSContext* cx, Handle<ArrayBufferObject*> buffer);
static ArrayBufferObject* createForWasm(JSContext* cx, uint32_t numBytes, bool signalsForOOB);
static bool prepareForAsmJS(JSContext* cx, Handle<ArrayBufferObject*> buffer, bool signalsForOOB);
static void finalize(FreeOp* fop, JSObject* obj);
@ -352,7 +351,7 @@ class ArrayBufferObject : public ArrayBufferObjectMaybeShared
bool hasTypedObjectViews() const { return flags() & TYPED_OBJECT_VIEWS; }
void setIsAsmJSMalloced() { setFlags((flags() & ~KIND_MASK) | ASMJS_MALLOCED); }
void setIsWasmMalloced() { setFlags((flags() & ~KIND_MASK) | WASM_MALLOCED); }
void setIsDetached() { setFlags(flags() | DETACHED); }
void initialize(size_t byteLength, BufferContents contents, OwnsState ownsState) {