WASM should support faster loads.
authorkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 3 Mar 2017 22:24:21 +0000 (22:24 +0000)
committerkeith_miller@apple.com <keith_miller@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 3 Mar 2017 22:24:21 +0000 (22:24 +0000)
https://bugs.webkit.org/show_bug.cgi?id=162693

Reviewed by Saam Barati.

Source/JavaScriptCore:

This patch adds support for WebAssembly using a 32-bit address
space for memory (along with some extra space for offset
overflow). With a 32-bit address space (we call them
Signaling/fast memories), we reserve the virtual address space for
2^32 + offset bytes of memory and only mark the usable section as
read/write. If wasm code would read/write out of bounds we use a
custom signal handler to catch the SIGBUS. The signal handler then
checks if the faulting instruction is wasm code and tells the
thread to resume executing from the wasm exception
handler. Otherwise, the signal handler crashes the process, as
usual.

All of the allocations of these memories are managed by the
Wasm::Memory class. In order to avoid TLB churn in the OS we cache
old Signaling memories that are no longer in use. Since getting
the wrong memory can cause recompiles, we try to reserve a memory
for modules that do not import a memory. If a module does import a
memory, we try to guess the type of memory we are going to get
based on the last one allocated.

This patch also changes how the wasm JS-api manages objects. Since
we can compile different versions of code, this patch adds a new
JSWebAssemblyCodeBlock class that holds all the information
specific to running a module in a particular bounds checking
mode. Additionally, the Wasm::Memory object is now a reference
counted class that is shared between the JSWebAssemblyMemory
object and the ArrayBuffer that also views it.

* JavaScriptCore.xcodeproj/project.pbxproj:
* jit/JITThunks.cpp:
(JSC::JITThunks::existingCTIStub):
* jit/JITThunks.h:
* jsc.cpp:
(jscmain):
* runtime/Options.h:
* runtime/VM.cpp:
(JSC::VM::VM):
* runtime/VM.h:
* wasm/JSWebAssemblyCodeBlock.h: Copied from Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h.
(JSC::JSWebAssemblyCodeBlock::create):
(JSC::JSWebAssemblyCodeBlock::createStructure):
(JSC::JSWebAssemblyCodeBlock::functionImportCount):
(JSC::JSWebAssemblyCodeBlock::mode):
(JSC::JSWebAssemblyCodeBlock::module):
(JSC::JSWebAssemblyCodeBlock::jsEntrypointCalleeFromFunctionIndexSpace):
(JSC::JSWebAssemblyCodeBlock::wasmEntrypointCalleeFromFunctionIndexSpace):
(JSC::JSWebAssemblyCodeBlock::setJSEntrypointCallee):
(JSC::JSWebAssemblyCodeBlock::setWasmEntrypointCallee):
(JSC::JSWebAssemblyCodeBlock::callees):
(JSC::JSWebAssemblyCodeBlock::offsetOfCallees):
(JSC::JSWebAssemblyCodeBlock::allocationSize):
* wasm/WasmB3IRGenerator.cpp:
(JSC::Wasm::B3IRGenerator::B3IRGenerator):
(JSC::Wasm::getMemoryBaseAndSize):
(JSC::Wasm::B3IRGenerator::emitCheckAndPreparePointer):
(JSC::Wasm::B3IRGenerator::emitLoadOp):
(JSC::Wasm::B3IRGenerator::emitStoreOp):
* wasm/WasmCallingConvention.h:
* wasm/WasmFaultSignalHandler.cpp: Added.
(JSC::Wasm::trapHandler):
(JSC::Wasm::registerCode):
(JSC::Wasm::unregisterCode):
(JSC::Wasm::fastMemoryEnabled):
(JSC::Wasm::enableFastMemory):
* wasm/WasmFaultSignalHandler.h: Copied from Source/JavaScriptCore/wasm/js/JSWebAssemblyCallee.cpp.
* wasm/WasmFormat.h:
(JSC::Wasm::ModuleInformation::importFunctionCount):
(JSC::Wasm::ModuleInformation::hasMemory): Deleted.
* wasm/WasmMemory.cpp:
(JSC::Wasm::mmapBytes):
(JSC::Wasm::Memory::lastAllocatedMode):
(JSC::Wasm::availableFastMemories):
(JSC::Wasm::tryGetFastMemory):
(JSC::Wasm::releaseFastMemory):
(JSC::Wasm::Memory::Memory):
(JSC::Wasm::Memory::createImpl):
(JSC::Wasm::Memory::create):
(JSC::Wasm::Memory::~Memory):
(JSC::Wasm::Memory::grow):
(JSC::Wasm::Memory::dump):
(JSC::Wasm::Memory::makeString):
* wasm/WasmMemory.h:
(JSC::Wasm::Memory::operator bool):
(JSC::Wasm::Memory::size):
(JSC::Wasm::Memory::check):
(JSC::Wasm::Memory::Memory): Deleted.
(JSC::Wasm::Memory::offsetOfMemory): Deleted.
(JSC::Wasm::Memory::offsetOfSize): Deleted.
* wasm/WasmMemoryInformation.cpp:
(JSC::Wasm::MemoryInformation::MemoryInformation):
* wasm/WasmMemoryInformation.h:
(JSC::Wasm::MemoryInformation::hasReservedMemory):
(JSC::Wasm::MemoryInformation::takeReservedMemory):
(JSC::Wasm::MemoryInformation::mode):
* wasm/WasmModuleParser.cpp:
* wasm/WasmModuleParser.h:
(JSC::Wasm::ModuleParser::ModuleParser):
* wasm/WasmPlan.cpp:
(JSC::Wasm::Plan::parseAndValidateModule):
(JSC::Wasm::Plan::run):
* wasm/WasmPlan.h:
(JSC::Wasm::Plan::mode):
* wasm/js/JSWebAssemblyCallee.cpp:
(JSC::JSWebAssemblyCallee::finishCreation):
(JSC::JSWebAssemblyCallee::destroy):
* wasm/js/JSWebAssemblyCodeBlock.cpp: Added.
(JSC::JSWebAssemblyCodeBlock::JSWebAssemblyCodeBlock):
(JSC::JSWebAssemblyCodeBlock::destroy):
(JSC::JSWebAssemblyCodeBlock::isSafeToRun):
(JSC::JSWebAssemblyCodeBlock::visitChildren):
(JSC::JSWebAssemblyCodeBlock::UnconditionalFinalizer::finalizeUnconditionally):
* wasm/js/JSWebAssemblyInstance.cpp:
(JSC::JSWebAssemblyInstance::setMemory):
(JSC::JSWebAssemblyInstance::finishCreation):
(JSC::JSWebAssemblyInstance::visitChildren):
* wasm/js/JSWebAssemblyInstance.h:
(JSC::JSWebAssemblyInstance::module):
(JSC::JSWebAssemblyInstance::codeBlock):
(JSC::JSWebAssemblyInstance::memoryMode):
(JSC::JSWebAssemblyInstance::setMemory): Deleted.
* wasm/js/JSWebAssemblyMemory.cpp:
(JSC::JSWebAssemblyMemory::create):
(JSC::JSWebAssemblyMemory::JSWebAssemblyMemory):
(JSC::JSWebAssemblyMemory::buffer):
(JSC::JSWebAssemblyMemory::grow):
(JSC::JSWebAssemblyMemory::destroy):
* wasm/js/JSWebAssemblyMemory.h:
(JSC::JSWebAssemblyMemory::memory):
(JSC::JSWebAssemblyMemory::offsetOfMemory):
(JSC::JSWebAssemblyMemory::offsetOfSize):
* wasm/js/JSWebAssemblyModule.cpp:
(JSC::JSWebAssemblyModule::buildCodeBlock):
(JSC::JSWebAssemblyModule::create):
(JSC::JSWebAssemblyModule::JSWebAssemblyModule):
(JSC::JSWebAssemblyModule::codeBlock):
(JSC::JSWebAssemblyModule::finishCreation):
(JSC::JSWebAssemblyModule::visitChildren):
(JSC::JSWebAssemblyModule::UnconditionalFinalizer::finalizeUnconditionally): Deleted.
* wasm/js/JSWebAssemblyModule.h:
(JSC::JSWebAssemblyModule::takeReservedMemory):
(JSC::JSWebAssemblyModule::signatureIndexFromFunctionIndexSpace):
(JSC::JSWebAssemblyModule::codeBlock):
(JSC::JSWebAssemblyModule::functionImportCount): Deleted.
(JSC::JSWebAssemblyModule::jsEntrypointCalleeFromFunctionIndexSpace): Deleted.
(JSC::JSWebAssemblyModule::wasmEntrypointCalleeFromFunctionIndexSpace): Deleted.
(JSC::JSWebAssemblyModule::setJSEntrypointCallee): Deleted.
(JSC::JSWebAssemblyModule::setWasmEntrypointCallee): Deleted.
(JSC::JSWebAssemblyModule::callees): Deleted.
(JSC::JSWebAssemblyModule::offsetOfCallees): Deleted.
(JSC::JSWebAssemblyModule::allocationSize): Deleted.
* wasm/js/WebAssemblyFunction.cpp:
(JSC::callWebAssemblyFunction):
* wasm/js/WebAssemblyInstanceConstructor.cpp:
(JSC::constructJSWebAssemblyInstance):
* wasm/js/WebAssemblyMemoryConstructor.cpp:
(JSC::constructJSWebAssemblyMemory):
* wasm/js/WebAssemblyModuleConstructor.cpp:
(JSC::WebAssemblyModuleConstructor::createModule):
* wasm/js/WebAssemblyModuleRecord.cpp:
(JSC::WebAssemblyModuleRecord::link):
(JSC::WebAssemblyModuleRecord::evaluate):

Source/WTF:

Add new forms of dataLog that take a boolean which describes if the log should happen. This makes cases where we have a static const bool for printing nicer since you can do:

dataLogIf(verbose, things, to, print);

instead of:

if (verbose)
    dataLog(things, to, print);

Also, add a operator! to Ref that has the same semantics as C++ refs.

* wtf/DataLog.h:
(WTF::dataLogLn):
(WTF::dataLogIf):
(WTF::dataLogLnIf):
* wtf/Ref.h:
(WTF::Ref::operator!):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@213386 268f45cc-cd09-0410-ab3c-d52691b4dbfc

38 files changed:
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/jit/JITThunks.cpp
Source/JavaScriptCore/jit/JITThunks.h
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/runtime/Options.h
Source/JavaScriptCore/runtime/VM.cpp
Source/JavaScriptCore/runtime/VM.h
Source/JavaScriptCore/wasm/JSWebAssemblyCodeBlock.h [new file with mode: 0644]
Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp
Source/JavaScriptCore/wasm/WasmCallingConvention.h
Source/JavaScriptCore/wasm/WasmFaultSignalHandler.cpp [new file with mode: 0644]
Source/JavaScriptCore/wasm/WasmFaultSignalHandler.h [new file with mode: 0644]
Source/JavaScriptCore/wasm/WasmFormat.h
Source/JavaScriptCore/wasm/WasmMemory.cpp
Source/JavaScriptCore/wasm/WasmMemory.h
Source/JavaScriptCore/wasm/WasmMemoryInformation.cpp
Source/JavaScriptCore/wasm/WasmMemoryInformation.h
Source/JavaScriptCore/wasm/WasmModuleParser.cpp
Source/JavaScriptCore/wasm/WasmModuleParser.h
Source/JavaScriptCore/wasm/WasmPlan.cpp
Source/JavaScriptCore/wasm/WasmPlan.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyCallee.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyCodeBlock.cpp [new file with mode: 0644]
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyInstance.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyMemory.h
Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.cpp
Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h
Source/JavaScriptCore/wasm/js/WebAssemblyFunction.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyInstanceConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyMemoryConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyModuleConstructor.cpp
Source/JavaScriptCore/wasm/js/WebAssemblyModuleRecord.cpp
Source/WTF/ChangeLog
Source/WTF/wtf/DataLog.h
Source/WTF/wtf/Ref.h

index d8a8330..00b12c6 100644 (file)
@@ -1,3 +1,172 @@
+2017-03-03  Keith Miller  <keith_miller@apple.com>
+
+        WASM should support faster loads.
+        https://bugs.webkit.org/show_bug.cgi?id=162693
+
+        Reviewed by Saam Barati.
+
+        This patch adds support for WebAssembly using a 32-bit address
+        space for memory (along with some extra space for offset
+        overflow). With a 32-bit address space (we call them
+        Signaling/fast memories), we reserve the virtual address space for
+        2^32 + offset bytes of memory and only mark the usable section as
+        read/write. If wasm code would read/write out of bounds we use a
+        custom signal handler to catch the SIGBUS. The signal handler then
+        checks if the faulting instruction is wasm code and tells the
+        thread to resume executing from the wasm exception
+        handler. Otherwise, the signal handler crashes the process, as
+        usual.
+
+        All of the allocations of these memories are managed by the
+        Wasm::Memory class. In order to avoid TLB churn in the OS we cache
+        old Signaling memories that are no longer in use. Since getting
+        the wrong memory can cause recompiles, we try to reserve a memory
+        for modules that do not import a memory. If a module does import a
+        memory, we try to guess the type of memory we are going to get
+        based on the last one allocated.
+
+        This patch also changes how the wasm JS-api manages objects. Since
+        we can compile different versions of code, this patch adds a new
+        JSWebAssemblyCodeBlock class that holds all the information
+        specific to running a module in a particular bounds checking
+        mode. Additionally, the Wasm::Memory object is now a reference
+        counted class that is shared between the JSWebAssemblyMemory
+        object and the ArrayBuffer that also views it.
+
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * jit/JITThunks.cpp:
+        (JSC::JITThunks::existingCTIStub):
+        * jit/JITThunks.h:
+        * jsc.cpp:
+        (jscmain):
+        * runtime/Options.h:
+        * runtime/VM.cpp:
+        (JSC::VM::VM):
+        * runtime/VM.h:
+        * wasm/JSWebAssemblyCodeBlock.h: Copied from Source/JavaScriptCore/wasm/js/JSWebAssemblyModule.h.
+        (JSC::JSWebAssemblyCodeBlock::create):
+        (JSC::JSWebAssemblyCodeBlock::createStructure):
+        (JSC::JSWebAssemblyCodeBlock::functionImportCount):
+        (JSC::JSWebAssemblyCodeBlock::mode):
+        (JSC::JSWebAssemblyCodeBlock::module):
+        (JSC::JSWebAssemblyCodeBlock::jsEntrypointCalleeFromFunctionIndexSpace):
+        (JSC::JSWebAssemblyCodeBlock::wasmEntrypointCalleeFromFunctionIndexSpace):
+        (JSC::JSWebAssemblyCodeBlock::setJSEntrypointCallee):
+        (JSC::JSWebAssemblyCodeBlock::setWasmEntrypointCallee):
+        (JSC::JSWebAssemblyCodeBlock::callees):
+        (JSC::JSWebAssemblyCodeBlock::offsetOfCallees):
+        (JSC::JSWebAssemblyCodeBlock::allocationSize):
+        * wasm/WasmB3IRGenerator.cpp:
+        (JSC::Wasm::B3IRGenerator::B3IRGenerator):
+        (JSC::Wasm::getMemoryBaseAndSize):
+        (JSC::Wasm::B3IRGenerator::emitCheckAndPreparePointer):
+        (JSC::Wasm::B3IRGenerator::emitLoadOp):
+        (JSC::Wasm::B3IRGenerator::emitStoreOp):
+        * wasm/WasmCallingConvention.h:
+        * wasm/WasmFaultSignalHandler.cpp: Added.
+        (JSC::Wasm::trapHandler):
+        (JSC::Wasm::registerCode):
+        (JSC::Wasm::unregisterCode):
+        (JSC::Wasm::fastMemoryEnabled):
+        (JSC::Wasm::enableFastMemory):
+        * wasm/WasmFaultSignalHandler.h: Copied from Source/JavaScriptCore/wasm/js/JSWebAssemblyCallee.cpp.
+        * wasm/WasmFormat.h:
+        (JSC::Wasm::ModuleInformation::importFunctionCount):
+        (JSC::Wasm::ModuleInformation::hasMemory): Deleted.
+        * wasm/WasmMemory.cpp:
+        (JSC::Wasm::mmapBytes):
+        (JSC::Wasm::Memory::lastAllocatedMode):
+        (JSC::Wasm::availableFastMemories):
+        (JSC::Wasm::tryGetFastMemory):
+        (JSC::Wasm::releaseFastMemory):
+        (JSC::Wasm::Memory::Memory):
+        (JSC::Wasm::Memory::createImpl):
+        (JSC::Wasm::Memory::create):
+        (JSC::Wasm::Memory::~Memory):
+        (JSC::Wasm::Memory::grow):
+        (JSC::Wasm::Memory::dump):
+        (JSC::Wasm::Memory::makeString):
+        * wasm/WasmMemory.h:
+        (JSC::Wasm::Memory::operator bool):
+        (JSC::Wasm::Memory::size):
+        (JSC::Wasm::Memory::check):
+        (JSC::Wasm::Memory::Memory): Deleted.
+        (JSC::Wasm::Memory::offsetOfMemory): Deleted.
+        (JSC::Wasm::Memory::offsetOfSize): Deleted.
+        * wasm/WasmMemoryInformation.cpp:
+        (JSC::Wasm::MemoryInformation::MemoryInformation):
+        * wasm/WasmMemoryInformation.h:
+        (JSC::Wasm::MemoryInformation::hasReservedMemory):
+        (JSC::Wasm::MemoryInformation::takeReservedMemory):
+        (JSC::Wasm::MemoryInformation::mode):
+        * wasm/WasmModuleParser.cpp:
+        * wasm/WasmModuleParser.h:
+        (JSC::Wasm::ModuleParser::ModuleParser):
+        * wasm/WasmPlan.cpp:
+        (JSC::Wasm::Plan::parseAndValidateModule):
+        (JSC::Wasm::Plan::run):
+        * wasm/WasmPlan.h:
+        (JSC::Wasm::Plan::mode):
+        * wasm/js/JSWebAssemblyCallee.cpp:
+        (JSC::JSWebAssemblyCallee::finishCreation):
+        (JSC::JSWebAssemblyCallee::destroy):
+        * wasm/js/JSWebAssemblyCodeBlock.cpp: Added.
+        (JSC::JSWebAssemblyCodeBlock::JSWebAssemblyCodeBlock):
+        (JSC::JSWebAssemblyCodeBlock::destroy):
+        (JSC::JSWebAssemblyCodeBlock::isSafeToRun):
+        (JSC::JSWebAssemblyCodeBlock::visitChildren):
+        (JSC::JSWebAssemblyCodeBlock::UnconditionalFinalizer::finalizeUnconditionally):
+        * wasm/js/JSWebAssemblyInstance.cpp:
+        (JSC::JSWebAssemblyInstance::setMemory):
+        (JSC::JSWebAssemblyInstance::finishCreation):
+        (JSC::JSWebAssemblyInstance::visitChildren):
+        * wasm/js/JSWebAssemblyInstance.h:
+        (JSC::JSWebAssemblyInstance::module):
+        (JSC::JSWebAssemblyInstance::codeBlock):
+        (JSC::JSWebAssemblyInstance::memoryMode):
+        (JSC::JSWebAssemblyInstance::setMemory): Deleted.
+        * wasm/js/JSWebAssemblyMemory.cpp:
+        (JSC::JSWebAssemblyMemory::create):
+        (JSC::JSWebAssemblyMemory::JSWebAssemblyMemory):
+        (JSC::JSWebAssemblyMemory::buffer):
+        (JSC::JSWebAssemblyMemory::grow):
+        (JSC::JSWebAssemblyMemory::destroy):
+        * wasm/js/JSWebAssemblyMemory.h:
+        (JSC::JSWebAssemblyMemory::memory):
+        (JSC::JSWebAssemblyMemory::offsetOfMemory):
+        (JSC::JSWebAssemblyMemory::offsetOfSize):
+        * wasm/js/JSWebAssemblyModule.cpp:
+        (JSC::JSWebAssemblyModule::buildCodeBlock):
+        (JSC::JSWebAssemblyModule::create):
+        (JSC::JSWebAssemblyModule::JSWebAssemblyModule):
+        (JSC::JSWebAssemblyModule::codeBlock):
+        (JSC::JSWebAssemblyModule::finishCreation):
+        (JSC::JSWebAssemblyModule::visitChildren):
+        (JSC::JSWebAssemblyModule::UnconditionalFinalizer::finalizeUnconditionally): Deleted.
+        * wasm/js/JSWebAssemblyModule.h:
+        (JSC::JSWebAssemblyModule::takeReservedMemory):
+        (JSC::JSWebAssemblyModule::signatureIndexFromFunctionIndexSpace):
+        (JSC::JSWebAssemblyModule::codeBlock):
+        (JSC::JSWebAssemblyModule::functionImportCount): Deleted.
+        (JSC::JSWebAssemblyModule::jsEntrypointCalleeFromFunctionIndexSpace): Deleted.
+        (JSC::JSWebAssemblyModule::wasmEntrypointCalleeFromFunctionIndexSpace): Deleted.
+        (JSC::JSWebAssemblyModule::setJSEntrypointCallee): Deleted.
+        (JSC::JSWebAssemblyModule::setWasmEntrypointCallee): Deleted.
+        (JSC::JSWebAssemblyModule::callees): Deleted.
+        (JSC::JSWebAssemblyModule::offsetOfCallees): Deleted.
+        (JSC::JSWebAssemblyModule::allocationSize): Deleted.
+        * wasm/js/WebAssemblyFunction.cpp:
+        (JSC::callWebAssemblyFunction):
+        * wasm/js/WebAssemblyInstanceConstructor.cpp:
+        (JSC::constructJSWebAssemblyInstance):
+        * wasm/js/WebAssemblyMemoryConstructor.cpp:
+        (JSC::constructJSWebAssemblyMemory):
+        * wasm/js/WebAssemblyModuleConstructor.cpp:
+        (JSC::WebAssemblyModuleConstructor::createModule):
+        * wasm/js/WebAssemblyModuleRecord.cpp:
+        (JSC::WebAssemblyModuleRecord::link):
+        (JSC::WebAssemblyModuleRecord::evaluate):
+
 2017-03-03  Mark Lam  <mark.lam@apple.com>
 
         Gardening: fix broken ARM64 build.
index 82d8d98..71173e2 100644 (file)
                535557161D9DFA32006D583B /* WasmMemory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 535557151D9DFA32006D583B /* WasmMemory.cpp */; };
                5370B4F51BF26202005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5370B4F31BF25EA2005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.cpp */; };
                5370B4F61BF26205005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.h in Headers */ = {isa = PBXBuildFile; fileRef = 5370B4F41BF25EA2005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.h */; };
+               5381B9371E60E9660090F794 /* WasmFaultSignalHandler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5381B9361E60E9660090F794 /* WasmFaultSignalHandler.cpp */; };
+               5381B9391E60E97D0090F794 /* WasmFaultSignalHandler.h in Headers */ = {isa = PBXBuildFile; fileRef = 5381B9381E60E97D0090F794 /* WasmFaultSignalHandler.h */; };
+               5381B9401E65DFEB0090F794 /* JSWebAssemblyCodeBlock.h in Headers */ = {isa = PBXBuildFile; fileRef = 5381B93F1E65DFEB0090F794 /* JSWebAssemblyCodeBlock.h */; };
+               5383AA301E65E8A100A532FC /* JSWebAssemblyCodeBlock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5383AA2F1E65E8A100A532FC /* JSWebAssemblyCodeBlock.cpp */; };
                53917E7B1B7906FA000EBD33 /* JSGenericTypedArrayViewPrototypeFunctions.h in Headers */ = {isa = PBXBuildFile; fileRef = 53917E7A1B7906E4000EBD33 /* JSGenericTypedArrayViewPrototypeFunctions.h */; };
                539FB8BA1C99DA7C00940FA1 /* JSArrayInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 539FB8B91C99DA7C00940FA1 /* JSArrayInlines.h */; };
                53B0BE341E561AC900A8FC29 /* GetterSetterAccessCase.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 53B0BE331E561AC900A8FC29 /* GetterSetterAccessCase.cpp */; };
                535557151D9DFA32006D583B /* WasmMemory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmMemory.cpp; sourceTree = "<group>"; };
                5370B4F31BF25EA2005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AdaptiveInferredPropertyValueWatchpointBase.cpp; sourceTree = "<group>"; };
                5370B4F41BF25EA2005C40FC /* AdaptiveInferredPropertyValueWatchpointBase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AdaptiveInferredPropertyValueWatchpointBase.h; sourceTree = "<group>"; };
+               5381B9361E60E9660090F794 /* WasmFaultSignalHandler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WasmFaultSignalHandler.cpp; sourceTree = "<group>"; };
+               5381B9381E60E97D0090F794 /* WasmFaultSignalHandler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WasmFaultSignalHandler.h; sourceTree = "<group>"; };
+               5381B93F1E65DFEB0090F794 /* JSWebAssemblyCodeBlock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSWebAssemblyCodeBlock.h; sourceTree = "<group>"; };
+               5383AA2F1E65E8A100A532FC /* JSWebAssemblyCodeBlock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = JSWebAssemblyCodeBlock.cpp; path = js/JSWebAssemblyCodeBlock.cpp; sourceTree = "<group>"; };
                53917E7A1B7906E4000EBD33 /* JSGenericTypedArrayViewPrototypeFunctions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSGenericTypedArrayViewPrototypeFunctions.h; sourceTree = "<group>"; };
                53917E7C1B791106000EBD33 /* JSTypedArrayViewPrototype.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSTypedArrayViewPrototype.h; sourceTree = "<group>"; };
                53917E831B791CB8000EBD33 /* TypedArrayPrototype.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; name = TypedArrayPrototype.js; path = builtins/TypedArrayPrototype.js; sourceTree = SOURCE_ROOT; };
                                53F40E961D5A7BEC0099A1B6 /* WasmModuleParser.cpp */,
                                53F40E941D5A7AEF0099A1B6 /* WasmModuleParser.h */,
                                ADB6F67C1E15D7500082F384 /* WasmPageCount.cpp */,
+                               5381B9361E60E9660090F794 /* WasmFaultSignalHandler.cpp */,
+                               5381B9381E60E97D0090F794 /* WasmFaultSignalHandler.h */,
                                79B759731DFA4C600052174C /* WasmPageCount.h */,
                                53F40E8C1D5901F20099A1B6 /* WasmParser.h */,
                                531374BE1D5CE95000AF7A0B /* WasmPlan.cpp */,
                        children = (
                                79E423E01DEE65320078D355 /* JSWebAssemblyCallee.cpp */,
                                79E423E11DEE65320078D355 /* JSWebAssemblyCallee.h */,
+                               5383AA2F1E65E8A100A532FC /* JSWebAssemblyCodeBlock.cpp */,
+                               5381B93F1E65DFEB0090F794 /* JSWebAssemblyCodeBlock.h */,
                                AD2FCBA61DB58DA400B3E736 /* JSWebAssemblyCompileError.cpp */,
                                AD2FCBA71DB58DA400B3E736 /* JSWebAssemblyCompileError.h */,
                                796FB4391DFF8C3F0039C95D /* JSWebAssemblyHelpers.h */,
                                0FEC85721BDACDC70080FF74 /* AirBasicBlock.h in Headers */,
                                0FB3878E1BFBC44D00E3AB1E /* AirBlockWorklist.h in Headers */,
                                0F61832A1C45BF070072450B /* AirCCallingConvention.h in Headers */,
+                               5381B9401E65DFEB0090F794 /* JSWebAssemblyCodeBlock.h in Headers */,
                                0FEC85741BDACDC70080FF74 /* AirCCallSpecial.h in Headers */,
                                0FEC85761BDACDC70080FF74 /* AirCode.h in Headers */,
                                0F10F1A31C420BF0001C07D2 /* AirCustom.h in Headers */,
                                A7D89CF417A0B8CC00773AD8 /* DFGBlockInsertionSet.h in Headers */,
                                0FC3CCFC19ADA410006AC72A /* DFGBlockMap.h in Headers */,
                                0FC3CCFD19ADA410006AC72A /* DFGBlockMapInlines.h in Headers */,
+                               5381B9391E60E97D0090F794 /* WasmFaultSignalHandler.h in Headers */,
                                0FC3CCFE19ADA410006AC72A /* DFGBlockSet.h in Headers */,
                                0FBF158D19B7A53100695DD0 /* DFGBlockSetInlines.h in Headers */,
                                0FC3CD0019ADA410006AC72A /* DFGBlockWorklist.h in Headers */,
                                0FC841681BA8C3210061837D /* DFGInferredTypeCheck.cpp in Sources */,
                                A704D90517A0BAA8006BA554 /* DFGInPlaceAbstractState.cpp in Sources */,
                                0F3BD1B71B896A0700598AA6 /* DFGInsertionSet.cpp in Sources */,
+                               5381B9371E60E9660090F794 /* WasmFaultSignalHandler.cpp in Sources */,
                                0F300B7B18AB1B1400A6D72E /* DFGIntegerCheckCombiningPhase.cpp in Sources */,
                                0F898F311B27689F0083A33C /* DFGIntegerRangeOptimizationPhase.cpp in Sources */,
                                0FC97F3D18202119002C9B26 /* DFGInvalidationPointInjectionPhase.cpp in Sources */,
                                86C568E011A213EE0007F7F0 /* MacroAssemblerARM.cpp in Sources */,
                                FEB137571BB11EF900CD5100 /* MacroAssemblerARM64.cpp in Sources */,
                                A729009C17976C6000317298 /* MacroAssemblerARMv7.cpp in Sources */,
+                               5383AA301E65E8A100A532FC /* JSWebAssemblyCodeBlock.cpp in Sources */,
                                0F6DB7EC1D617D1100CDBF8E /* MacroAssemblerCodeRef.cpp in Sources */,
                                FE68C6381B90DE0B0042BCB3 /* MacroAssemblerPrinter.cpp in Sources */,
                                A7A4AE0817973B26005612B1 /* MacroAssemblerX86Common.cpp in Sources */,
index de48e62..546e14c 100644 (file)
@@ -82,6 +82,15 @@ MacroAssemblerCodeRef JITThunks::ctiStub(VM* vm, ThunkGenerator generator)
     return entry.iterator->value;
 }
 
+MacroAssemblerCodeRef JITThunks::existingCTIStub(ThunkGenerator generator)
+{
+    LockHolder locker(m_lock);
+    CTIStubMap::iterator entry = m_ctiStubMap.find(generator);
+    if (entry == m_ctiStubMap.end())
+        return MacroAssemblerCodeRef();
+    return entry->value;
+}
+
 void JITThunks::finalize(Handle<Unknown> handle, void*)
 {
     auto* nativeExecutable = static_cast<NativeExecutable*>(handle.get().asCell());
index addcf23..7e06e71 100644 (file)
@@ -58,6 +58,7 @@ public:
     MacroAssemblerCodePtr ctiNativeTailCallWithoutSavedTags(VM*);    
 
     MacroAssemblerCodeRef ctiStub(VM*, ThunkGenerator);
+    MacroAssemblerCodeRef existingCTIStub(ThunkGenerator);
 
     NativeExecutable* hostFunctionStub(VM*, NativeFunction, NativeFunction constructor, const String& name);
     NativeExecutable* hostFunctionStub(VM*, NativeFunction, NativeFunction constructor, ThunkGenerator, Intrinsic, const DOMJIT::Signature*, const String& name);
index 80e1d50..12cb302 100644 (file)
@@ -72,6 +72,7 @@
 #include "SuperSampler.h"
 #include "TestRunnerUtils.h"
 #include "TypeProfilerLog.h"
+#include "WasmFaultSignalHandler.h"
 #include "WasmPlan.h"
 #include "WasmMemory.h"
 #include <locale.h>
@@ -3773,6 +3774,9 @@ int jscmain(int argc, char** argv)
     WTF::initializeMainThread();
     JSC::initializeThreading();
     startTimeoutThreadIfNeeded();
+#if ENABLE(WEBASSEMBLY)
+    JSC::Wasm::enableFastMemory();
+#endif
 
     int result;
     result = runJSC(
index 65b9829..602e15d 100644 (file)
@@ -430,6 +430,8 @@ typedef const char* optionString;
     \
     v(bool, useWebAssembly, true, Normal, "Expose the WebAssembly global object.") \
     v(bool, simulateWebAssemblyLowMemory, false, Normal, "If true, the Memory object won't mmap the full 'maximum' range and instead will allocate the minimum required amount.") \
+    v(bool, useWebAssemblyFastMemory, true, Normal, "If true, we will try to use a 32-bit address space with a signal handler to bounds check wasm memory.")
+
 
 enum OptionEquivalence {
     SameOption,
index bd387de..d4952a3 100644 (file)
@@ -241,6 +241,7 @@ VM::VM(VMType vmType, HeapType heapType)
 #if ENABLE(WEBASSEMBLY)
     webAssemblyCalleeStructure.set(*this, JSWebAssemblyCallee::createStructure(*this, 0, jsNull()));
     webAssemblyToJSCalleeStructure.set(*this, WebAssemblyToJSCallee::createStructure(*this, 0, jsNull()));
+    webAssemblyCodeBlockStructure.set(*this, JSWebAssemblyCodeBlock::createStructure(*this, 0, jsNull()));
     webAssemblyToJSCallee.set(*this, WebAssemblyToJSCallee::create(*this, webAssemblyToJSCalleeStructure.get()));
 #endif
     moduleProgramExecutableStructure.set(*this, ModuleProgramExecutable::createStructure(*this, 0, jsNull()));
index 74e645f..84394cf 100644 (file)
@@ -334,6 +334,7 @@ public:
 #if ENABLE(WEBASSEMBLY)
     Strong<Structure> webAssemblyCalleeStructure;
     Strong<Structure> webAssemblyToJSCalleeStructure;
+    Strong<Structure> webAssemblyCodeBlockStructure;
     Strong<JSCell> webAssemblyToJSCallee;
 #endif
     Strong<Structure> moduleProgramExecutableStructure;
diff --git a/Source/JavaScriptCore/wasm/JSWebAssemblyCodeBlock.h b/Source/JavaScriptCore/wasm/JSWebAssemblyCodeBlock.h
new file mode 100644 (file)
index 0000000..f67d2d2
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#pragma once
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "JSCell.h"
+#include "JSWebAssemblyCallee.h"
+#include "UnconditionalFinalizer.h"
+#include "WasmFormat.h"
+#include <wtf/Bag.h>
+#include <wtf/Vector.h>
+
+namespace JSC {
+
+class JSWebAssemblyModule;
+class JSWebAssemblyMemory;
+
+class JSWebAssemblyCodeBlock : public JSCell {
+public:
+    typedef JSCell Base;
+    static const unsigned StructureFlags = Base::StructureFlags | StructureIsImmortal;
+
+    static JSWebAssemblyCodeBlock* create(VM& vm, JSWebAssemblyModule* owner, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmExitStubs>&& exitStubs, Wasm::Memory::Mode mode, unsigned calleeCount)
+    {
+        auto* result = new (NotNull, allocateCell<JSWebAssemblyCodeBlock>(vm.heap, allocationSize(calleeCount))) JSWebAssemblyCodeBlock(vm, owner, std::forward<Bag<CallLinkInfo>>(callLinkInfos), std::forward<Vector<Wasm::WasmExitStubs>>(exitStubs), mode, calleeCount);
+        result->finishCreation(vm);
+        return result;
+    }
+
+    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+    {
+        return Structure::create(vm, globalObject, prototype, TypeInfo(CellType, StructureFlags), info());
+    }
+
+    unsigned functionImportCount() const { return m_wasmExitStubs.size(); }
+    Wasm::Memory::Mode mode() const { return m_mode; }
+    JSWebAssemblyModule* module() const { return m_module.get(); }
+    bool isSafeToRun(JSWebAssemblyMemory*);
+
+    JSWebAssemblyCallee* jsEntrypointCalleeFromFunctionIndexSpace(unsigned functionIndexSpace)
+    {
+        RELEASE_ASSERT(functionIndexSpace >= functionImportCount());
+        unsigned calleeIndex = functionIndexSpace - functionImportCount();
+        RELEASE_ASSERT(calleeIndex < m_calleeCount);
+        return callees()[calleeIndex].get();
+    }
+
+    JSWebAssemblyCallee* wasmEntrypointCalleeFromFunctionIndexSpace(unsigned functionIndexSpace)
+    {
+        RELEASE_ASSERT(functionIndexSpace >= functionImportCount());
+        unsigned calleeIndex = functionIndexSpace - functionImportCount();
+        RELEASE_ASSERT(calleeIndex < m_calleeCount);
+        return callees()[calleeIndex + m_calleeCount].get();
+    }
+
+    void setJSEntrypointCallee(VM& vm, unsigned calleeIndex, JSWebAssemblyCallee* callee)
+    {
+        RELEASE_ASSERT(calleeIndex < m_calleeCount);
+        callees()[calleeIndex].set(vm, this, callee);
+    }
+
+    void setWasmEntrypointCallee(VM& vm, unsigned calleeIndex, JSWebAssemblyCallee* callee)
+    {
+        RELEASE_ASSERT(calleeIndex < m_calleeCount);
+        callees()[calleeIndex + m_calleeCount].set(vm, this, callee);
+    }
+
+    WriteBarrier<JSWebAssemblyCallee>* callees()
+    {
+        return bitwise_cast<WriteBarrier<JSWebAssemblyCallee>*>(bitwise_cast<char*>(this) + offsetOfCallees());
+    }
+
+private:
+    JSWebAssemblyCodeBlock(VM&, JSWebAssemblyModule*, Bag<CallLinkInfo>&&, Vector<Wasm::WasmExitStubs>&&, Wasm::Memory::Mode, unsigned calleeCount);
+    DECLARE_EXPORT_INFO;
+    static const bool needsDestruction = true;
+    static void destroy(JSCell*);
+    static void visitChildren(JSCell*, SlotVisitor&);
+
+    static size_t offsetOfCallees()
+    {
+        return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<JSWebAssemblyCallee>)>(sizeof(JSWebAssemblyCodeBlock));
+    }
+
+    static size_t allocationSize(unsigned numCallees)
+    {
+        return offsetOfCallees() + sizeof(WriteBarrier<JSWebAssemblyCallee>) * numCallees * 2;
+    }
+
+    class UnconditionalFinalizer : public JSC::UnconditionalFinalizer {
+        void finalizeUnconditionally() override;
+    };
+
+    WriteBarrier<JSWebAssemblyModule> m_module;
+    UnconditionalFinalizer m_unconditionalFinalizer;
+    Bag<CallLinkInfo> m_callLinkInfos;
+    Vector<Wasm::WasmExitStubs> m_wasmExitStubs;
+    Wasm::Memory::Mode m_mode;
+    unsigned m_calleeCount;
+};
+
+} // namespace JSC
+
+#endif // ENABLE(WEBASSEMBLY)
index 2ff3050..f17de92 100644 (file)
@@ -207,6 +207,7 @@ public:
 
 private:
     ExpressionType emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOp);
+    B3::Kind memoryKind(B3::Opcode memoryOp);
     ExpressionType emitLoadOp(LoadOpType, Origin, ExpressionType pointer, uint32_t offset);
     void emitStoreOp(StoreOpType, Origin, ExpressionType pointer, ExpressionType value, uint32_t offset);
 
@@ -259,7 +260,7 @@ B3IRGenerator::B3IRGenerator(VM& vm, const ModuleInformation& info, Procedure& p
     for (const PinnedSizeRegisterInfo& regInfo : pinnedRegs.sizeRegisters)
         m_proc.pinRegister(regInfo.sizeRegister);
 
-    if (info.hasMemory()) {
+    if (info.memory) {
         m_proc.setWasmBoundsCheckGenerator([=] (CCallHelpers& jit, GPRReg pinnedGPR, unsigned) {
             AllowMacroScratchRegisterUsage allowScratch(jit);
             ASSERT_UNUSED(pinnedGPR, m_memorySizeGPR == pinnedGPR);
@@ -282,8 +283,8 @@ static MemoryBaseAndSize getMemoryBaseAndSize(VM& vm, Value* instance, Procedure
 {
     Value* memoryObject = block->appendNew<MemoryValue>(proc, Load, pointerType(), Origin(), instance, JSWebAssemblyInstance::offsetOfMemory());
 
-    static_assert(sizeof(decltype(vm.topJSWebAssemblyInstance->memory()->memory()->memory())) == sizeof(void*), "codegen relies on this size");
-    static_assert(sizeof(decltype(vm.topJSWebAssemblyInstance->memory()->memory()->size())) == sizeof(uint64_t), "codegen relies on this size");
+    static_assert(sizeof(decltype(vm.topJSWebAssemblyInstance->memory()->memory().memory())) == sizeof(void*), "codegen relies on this size");
+    static_assert(sizeof(decltype(vm.topJSWebAssemblyInstance->memory()->memory().size())) == sizeof(uint64_t), "codegen relies on this size");
     MemoryBaseAndSize result;
     result.base = block->appendNew<MemoryValue>(proc, Load, pointerType(), Origin(), memoryObject, JSWebAssemblyMemory::offsetOfMemory());
     result.size = block->appendNew<MemoryValue>(proc, Load, Int64, Origin(), memoryObject, JSWebAssemblyMemory::offsetOfSize());
@@ -454,9 +455,12 @@ auto B3IRGenerator::setGlobal(uint32_t index, ExpressionType value) -> PartialRe
 
 inline Value* B3IRGenerator::emitCheckAndPreparePointer(ExpressionType pointer, uint32_t offset, uint32_t sizeOfOperation)
 {
-    ASSERT(m_memoryBaseGPR && m_memorySizeGPR);
-    ASSERT(sizeOfOperation + offset > offset);
-    m_currentBlock->appendNew<WasmBoundsCheckValue>(m_proc, Origin(), pointer, m_memorySizeGPR, sizeOfOperation + offset - 1);
+    ASSERT(m_memoryBaseGPR);
+    if (m_info.memory.mode() == Memory::Mode::BoundsChecking) {
+        ASSERT(m_memorySizeGPR);
+        ASSERT(sizeOfOperation + offset > offset);
+        m_currentBlock->appendNew<WasmBoundsCheckValue>(m_proc, Origin(), pointer, m_memorySizeGPR, sizeOfOperation + offset - 1);
+    }
     pointer = m_currentBlock->appendNew<Value>(m_proc, ZExt32, Origin(), pointer);
     return m_currentBlock->appendNew<WasmAddressValue>(m_proc, Origin(), pointer, m_memoryBaseGPR);
 }
@@ -486,70 +490,77 @@ inline uint32_t sizeOfLoadOp(LoadOpType op)
     RELEASE_ASSERT_NOT_REACHED();
 }
 
+inline B3::Kind B3IRGenerator::memoryKind(B3::Opcode memoryOp)
+{
+    if (m_info.memory.mode() == Memory::Signaling)
+        return trapping(memoryOp);
+    return memoryOp;
+}
+
 inline Value* B3IRGenerator::emitLoadOp(LoadOpType op, Origin origin, ExpressionType pointer, uint32_t offset)
 {
     switch (op) {
     case LoadOpType::I32Load8S: {
-        return m_currentBlock->appendNew<MemoryValue>(m_proc, Load8S, origin, pointer, offset);
+        return m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load8S), origin, pointer, offset);
     }
 
     case LoadOpType::I64Load8S: {
-        Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8S, origin, pointer, offset);
+        Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load8S), origin, pointer, offset);
         return m_currentBlock->appendNew<Value>(m_proc, SExt32, origin, value);
     }
 
     case LoadOpType::I32Load8U: {
-        return m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, origin, pointer, offset);
+        return m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load8Z), origin, pointer, offset);
     }
 
     case LoadOpType::I64Load8U: {
-        Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load8Z, origin, pointer, offset);
+        Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load8Z), origin, pointer, offset);
         return m_currentBlock->appendNew<Value>(m_proc, ZExt32, origin, value);
     }
 
     case LoadOpType::I32Load16S: {
-        return m_currentBlock->appendNew<MemoryValue>(m_proc, Load16S, origin, pointer, offset);
+        return m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load16S), origin, pointer, offset);
     }
     case LoadOpType::I64Load16S: {
-        Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load16S, origin, pointer, offset);
+        Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load16S), origin, pointer, offset);
         return m_currentBlock->appendNew<Value>(m_proc, SExt32, origin, value);
     }
 
     case LoadOpType::I32Load: {
-        return m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, origin, pointer, offset);
+        return m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load), Int32, origin, pointer, offset);
     }
 
     case LoadOpType::I64Load32U: {
-        Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, origin, pointer, offset);
+        Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load), Int32, origin, pointer, offset);
         return m_currentBlock->appendNew<Value>(m_proc, ZExt32, origin, value);
     }
 
     case LoadOpType::I64Load32S: {
-        Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int32, origin, pointer, offset);
+        Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load), Int32, origin, pointer, offset);
         return m_currentBlock->appendNew<Value>(m_proc, SExt32, origin, value);
     }
 
     case LoadOpType::I64Load: {
-        return m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Int64, origin, pointer, offset);
+        return m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load), Int64, origin, pointer, offset);
     }
 
     case LoadOpType::F32Load: {
-        return m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Float, origin, pointer, offset);
+        return m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load), Float, origin, pointer, offset);
     }
 
     case LoadOpType::F64Load: {
-        return m_currentBlock->appendNew<MemoryValue>(m_proc, Load, Double, origin, pointer, offset);
+        return m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load), Double, origin, pointer, offset);
     }
 
     // FIXME: B3 doesn't support Load16Z yet. We should lower to that value when
     // it's added. https://bugs.webkit.org/show_bug.cgi?id=165884
     case LoadOpType::I32Load16U: {
-        Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load16S, origin, pointer, offset);
+        Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load16S), origin, pointer, offset);
         return m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(), value,
                 m_currentBlock->appendNew<Const32Value>(m_proc, Origin(), 0x0000ffff));
     }
     case LoadOpType::I64Load16U: {
-        Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, Load16S, origin, pointer, offset);
+        Value* value = m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Load16S), origin, pointer, offset);
         Value* partialResult = m_currentBlock->appendNew<Value>(m_proc, BitAnd, Origin(), value,
                 m_currentBlock->appendNew<Const32Value>(m_proc, Origin(), 0x0000ffff));
 
@@ -631,7 +642,7 @@ inline void B3IRGenerator::emitStoreOp(StoreOpType op, Origin origin, Expression
         FALLTHROUGH;
 
     case StoreOpType::I32Store8:
-        m_currentBlock->appendNew<MemoryValue>(m_proc, Store8, origin, value, pointer, offset);
+        m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Store8), origin, value, pointer, offset);
         return;
 
     case StoreOpType::I64Store16:
@@ -639,7 +650,7 @@ inline void B3IRGenerator::emitStoreOp(StoreOpType op, Origin origin, Expression
         FALLTHROUGH;
 
     case StoreOpType::I32Store16:
-        m_currentBlock->appendNew<MemoryValue>(m_proc, Store16, origin, value, pointer, offset);
+        m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Store16), origin, value, pointer, offset);
         return;
 
     case StoreOpType::I64Store32:
@@ -650,7 +661,7 @@ inline void B3IRGenerator::emitStoreOp(StoreOpType op, Origin origin, Expression
     case StoreOpType::I32Store:
     case StoreOpType::F32Store:
     case StoreOpType::F64Store:
-        m_currentBlock->appendNew<MemoryValue>(m_proc, Store, origin, value, pointer, offset);
+        m_currentBlock->appendNew<MemoryValue>(m_proc, memoryKind(Store), origin, value, pointer, offset);
         return;
     }
     RELEASE_ASSERT_NOT_REACHED();
index d79014f..e604ab2 100644 (file)
@@ -40,6 +40,7 @@
 #include "LinkBuffer.h"
 #include "RegisterSet.h"
 #include "WasmFormat.h"
+#include "WasmSignature.h"
 
 namespace JSC { namespace Wasm {
 
diff --git a/Source/JavaScriptCore/wasm/WasmFaultSignalHandler.cpp b/Source/JavaScriptCore/wasm/WasmFaultSignalHandler.cpp
new file mode 100644 (file)
index 0000000..f46c5e6
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "WasmFaultSignalHandler.h"
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "ExecutableAllocator.h"
+#include "VM.h"
+#include "WasmExceptionType.h"
+
+#include <wtf/Lock.h>
+#include <wtf/NeverDestroyed.h>
+
+namespace JSC { namespace Wasm {
+
+
+namespace {
+static const bool verbose = false;
+}
+
+static struct sigaction oldSigBusHandler;
+static bool fastHandlerInstalled { false };
+static StaticLock codeLocationsLock;
+static LazyNeverDestroyed<HashSet<std::tuple<VM*, void*, void*>>> codeLocations; // (vm, start, end)
+
+#if CPU(X86_64)
+#define InstructionPointerGPR context->__ss.__rip
+#define FirstArgumentGPR context->__ss.__rsi
+#else
+#define InstructionPointerGPR context->__ss.__pc
+#define FirstArgumentGPR context->__ss.__x[1]
+#endif
+
+static void trapHandler(int signal, siginfo_t*, void* ucontext)
+{
+    mcontext_t context = static_cast<ucontext_t*>(ucontext)->uc_mcontext;
+    void* faultingInstruction = reinterpret_cast<void*>(InstructionPointerGPR);
+    dataLogLnIf(verbose, "starting handler for fault at: ", RawPointer(faultingInstruction));
+
+    dataLogLnIf(verbose, "JIT memory start: ", RawPointer(reinterpret_cast<void*>(startOfFixedExecutableMemoryPool)), " end: ", RawPointer(reinterpret_cast<void*>(endOfFixedExecutableMemoryPool)));
+    // First we need to make sure we are in JIT code before we can aquire any locks. Otherwise,
+    // we might have crashed in code that is already holding one of the locks we want to aquire.
+    if (reinterpret_cast<void*>(startOfFixedExecutableMemoryPool) <= faultingInstruction
+        && faultingInstruction < reinterpret_cast<void*>(endOfFixedExecutableMemoryPool)) {
+
+        LockHolder locker(codeLocationsLock);
+        for (auto range : codeLocations.get()) {
+            VM* vm;
+            void* start;
+            void* end;
+            std::tie(vm, start, end) = range;
+            dataLogLnIf(verbose, "function start: ", RawPointer(start), " end: ", RawPointer(end));
+            if (start <= faultingInstruction && faultingInstruction < end) {
+                dataLogLnIf(verbose, "found match");
+                MacroAssemblerCodeRef exceptionStub = vm->jitStubs->existingCTIStub(throwExceptionFromWasmThunkGenerator);
+                // If for whatever reason we don't have a stub then we should just treat this like a regular crash.
+                if (!exceptionStub)
+                    break;
+                dataLogLnIf(verbose, "found stub: ", RawPointer(exceptionStub.code().executableAddress()));
+                FirstArgumentGPR = static_cast<uint64_t>(ExceptionType::OutOfBoundsMemoryAccess);
+                InstructionPointerGPR = reinterpret_cast<uint64_t>(exceptionStub.code().executableAddress());
+                return;
+            }
+        }
+    }
+
+    // Since we only use fast memory in processes we control, if we restore we will just fall back to the default handler.
+    sigaction(signal, &oldSigBusHandler, nullptr);
+}
+
+void registerCode(VM& vm, void* start, void* end)
+{
+    if (!fastMemoryEnabled())
+        return;
+    LockHolder locker(codeLocationsLock);
+    codeLocations->add(std::make_tuple(&vm, start, end));
+}
+
+void unregisterCode(VM& vm, void* start, void* end)
+{
+    if (!fastMemoryEnabled())
+        return;
+    LockHolder locker(codeLocationsLock);
+    codeLocations->remove(std::make_tuple(&vm, start, end));
+}
+
+bool fastMemoryEnabled()
+{
+    return fastHandlerInstalled;
+}
+
+void enableFastMemory()
+{
+    static std::once_flag once;
+    std::call_once(once, [] {
+        if (!Options::useWebAssemblyFastMemory())
+            return;
+
+        struct sigaction action;
+
+        action.sa_sigaction = trapHandler;
+        sigfillset(&action.sa_mask);
+        action.sa_flags = SA_SIGINFO;
+        
+        if (!sigaction(SIGBUS, &action, &oldSigBusHandler)) {
+            fastHandlerInstalled = true;
+            codeLocations.construct();
+        }
+
+    });
+}
+    
+} } // namespace JSC::Wasm
+
+#endif // ENABLE(WEBASSEMBLY)
+
diff --git a/Source/JavaScriptCore/wasm/WasmFaultSignalHandler.h b/Source/JavaScriptCore/wasm/WasmFaultSignalHandler.h
new file mode 100644 (file)
index 0000000..6319e01
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#if ENABLE(WEBASSEMBLY)
+
+namespace JSC {
+
+class VM;
+
+namespace Wasm {
+
+void registerCode(VM&, void* start, void* end);
+void unregisterCode(VM&, void* start, void* end);
+
+bool fastMemoryEnabled();
+JS_EXPORT_PRIVATE void enableFastMemory();
+
+} } // namespace JSC::Wasm
+
+#endif // ENABLE(WEBASSEMBLY)
index dc63474..1fc574e 100644 (file)
@@ -256,7 +256,6 @@ struct ModuleInformation {
     }
 
     uint32_t importFunctionCount() const { return importFunctionSignatureIndices.size(); }
-    bool hasMemory() const { return !!memory; }
 
     ~ModuleInformation();
 };
index aad91c1..91c642e 100644 (file)
 
 #if ENABLE(WEBASSEMBLY)
 
+#include "VM.h"
+#include "WasmFaultSignalHandler.h"
+
 #include <wtf/HexNumber.h>
+#include <wtf/NeverDestroyed.h>
 #include <wtf/PrintStream.h>
 #include <wtf/text/WTFString.h>
 
@@ -38,86 +42,200 @@ namespace {
 const bool verbose = false;
 }
 
-void Memory::dump(PrintStream& out) const
+inline bool mmapBytes(size_t bytes, void*& memory)
 {
-    String memoryHex;
-    WTF::appendUnsigned64AsHex((uint64_t)(uintptr_t)m_memory, memoryHex);
-    out.print("Memory at 0x", memoryHex, ", size ", m_size, "B capacity ", m_mappedCapacity, "B, initial ", m_initial, " maximum ", m_maximum, " mode ", makeString(m_mode));
+    dataLogIf(verbose, "Attempting to mmap ", bytes, " bytes: ");
+    // FIXME: It would be nice if we had a VM tag for wasm memory. https://bugs.webkit.org/show_bug.cgi?id=163600
+    void* result = mmap(nullptr, bytes, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
+    if (result == MAP_FAILED) {
+        dataLogLnIf(verbose, "failed");
+        return false;
+    }
+    dataLogLnIf(verbose, "succeeded");
+    memory = result;
+    return true;
 }
 
-const char* Memory::makeString(Mode mode) const
+// We use this as a heuristic to guess what mode a memory import will be. Most of the time we expect users to
+// allocate the memory they are going to pass to all their modules right before compilation.
+static Memory::Mode lastAllocatedMemoryMode { Memory::Mode::Signaling };
+
+Memory::Mode Memory::lastAllocatedMode()
 {
-    switch (mode) {
-    case Mode::BoundsChecking: return "BoundsChecking";
+    return lastAllocatedMemoryMode;
+}
+
+static_assert(sizeof(uint64_t) == sizeof(size_t), "We rely on allowing the maximum size of Memory we map to be 2^33 which is larger than fits in a 32-bit integer that we'd pass to mprotect if this didn't hold.");
+
+static const size_t fastMemoryMappedBytes = (static_cast<size_t>(std::numeric_limits<uint32_t>::max()) + 1) * 2; // pointer max + offset max. This is all we need since a load straddling readable memory will trap.
+static const unsigned maxFastMemories = 4;
+static unsigned allocatedFastMemories { 0 };
+static StaticLock memoryLock;
+inline Deque<void*, maxFastMemories>& availableFastMemories(const LockHolder&)
+{
+    static NeverDestroyed<Deque<void*, maxFastMemories>> availableFastMemories;
+    return availableFastMemories;
+}
+
+inline bool tryGetFastMemory(VM& vm, void*& memory, size_t& mappedCapacity, Memory::Mode& mode)
+{
+    // We might GC here so we should be holding the API lock.
+    // FIXME: We should be able to syncronously trigger the GC from another thread.
+    ASSERT(vm.currentThreadIsHoldingAPILock());
+    if (!fastMemoryEnabled())
+        return false;
+
+    // We need to be sure we have a stub prior to running code.
+    if (!vm.getCTIStub(throwExceptionFromWasmThunkGenerator).size())
+        return false;
+
+    auto dequeFastMemory = [&] () -> bool {
+        // FIXME: We should eventually return these to the OS if we go some number of GCs
+        // without using them.
+        LockHolder locker(memoryLock);
+        if (!availableFastMemories(locker).isEmpty()) {
+            memory = availableFastMemories(locker).takeFirst();
+            mappedCapacity = fastMemoryMappedBytes;
+            mode = Memory::Signaling;
+            return true;
+        }
+        return false;
+    };
+
+    ASSERT(allocatedFastMemories <= maxFastMemories);
+    if (dequeFastMemory())
+        return true;
+
+    // If we have allocated all the fast memories... too bad.
+    if (allocatedFastMemories == maxFastMemories) {
+        // There is a reasonable chance that another module has died but has not been collected yet. Don't lose hope yet!
+        vm.heap.collectSync();
+        return dequeFastMemory();
     }
-    RELEASE_ASSERT_NOT_REACHED();
-    return "";
+
+    if (mmapBytes(fastMemoryMappedBytes, memory)) {
+        mappedCapacity = fastMemoryMappedBytes;
+        mode = Memory::Signaling;
+        allocatedFastMemories++;
+    }
+    return memory;
+}
+
+inline void releaseFastMemory(void*& memory, size_t writableSize, size_t mappedCapacity, Memory::Mode mode)
+{
+    if (mode != Memory::Signaling || !memory)
+        return;
+
+    RELEASE_ASSERT(memory && mappedCapacity == fastMemoryMappedBytes);
+    ASSERT(fastMemoryEnabled());
+
+    memset(memory, 0, writableSize);
+    if (mprotect(memory, writableSize, PROT_NONE))
+        CRASH();
+
+    LockHolder locker(memoryLock);
+    ASSERT(availableFastMemories(locker).size() < allocatedFastMemories);
+    availableFastMemories(locker).append(memory);
+    memory = nullptr;
 }
 
-static_assert(sizeof(uint64_t) == sizeof(size_t), "We rely on allowing the maximum size of Memory we map to be 2^32 which is larger than fits in a 32-bit integer that we'd pass to mprotect if this didn't hold.");
+Memory::Memory(PageCount initial, PageCount maximum)
+    : m_initial(initial)
+    , m_maximum(maximum)
+{
+    ASSERT(!initial.bytes());
+}
 
-Memory::Memory(PageCount initial, PageCount maximum, bool& failed)
-    : m_size(initial.bytes())
+Memory::Memory(void* memory, PageCount initial, PageCount maximum, size_t mappedCapacity, Mode mode)
+    : m_memory(memory)
+    , m_size(initial.bytes())
     , m_initial(initial)
     , m_maximum(maximum)
-    , m_mode(Mode::BoundsChecking)
-    // FIXME: If we add signal based bounds checking then we need extra space for overflow on load.
-    // see: https://bugs.webkit.org/show_bug.cgi?id=162693
+    , m_mappedCapacity(mappedCapacity)
+    , m_mode(mode)
+{
+    dataLogLnIf(verbose, "Memory::Memory allocating ", *this);
+}
+
+RefPtr<Memory> Memory::createImpl(VM& vm, PageCount initial, PageCount maximum, std::optional<Mode> requiredMode)
 {
     RELEASE_ASSERT(!maximum || maximum >= initial); // This should be guaranteed by our caller.
 
-    m_mappedCapacity = maximum ? maximum.bytes() : PageCount::max().bytes();
-    if (!m_mappedCapacity) {
+    Mode mode = requiredMode ? *requiredMode : BoundsChecking;
+    const size_t size = initial.bytes();
+    size_t mappedCapacity = maximum ? maximum.bytes() : PageCount::max().bytes();
+    void* memory = nullptr;
+
+    auto makeEmptyMemory = [&] () -> RefPtr<Memory> {
+        if (mode == Signaling)
+            return nullptr;
+
+        lastAllocatedMemoryMode = BoundsChecking;
+        return adoptRef(new Memory(initial, maximum));
+    };
+
+    if (!mappedCapacity) {
         // This means we specified a zero as maximum (which means we also have zero as initial size).
-        RELEASE_ASSERT(m_size == 0);
-        m_memory = nullptr;
-        m_mappedCapacity = 0;
-        failed = false;
-        if (verbose)
-            dataLogLn("Memory::Memory allocating nothing ", *this);
-        return;
+        RELEASE_ASSERT(!size);
+        dataLogLnIf(verbose, "Memory::create allocating nothing");
+        return makeEmptyMemory();
     }
 
-    // FIXME: It would be nice if we had a VM tag for wasm memory. https://bugs.webkit.org/show_bug.cgi?id=163600
-    void* result = Options::simulateWebAssemblyLowMemory() ? MAP_FAILED : mmap(nullptr, m_mappedCapacity, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
-    if (result == MAP_FAILED) {
-        // Try again with a different number.
-        if (verbose)
-            dataLogLn("Memory::Memory mmap failed once for capacity, trying again", *this);
-        m_mappedCapacity = m_size;
-        if (!m_mappedCapacity) {
-            m_memory = nullptr;
-            failed = false;
-            if (verbose)
-                dataLogLn("Memory::Memory mmap not trying again because size is zero ", *this);
-            return;
-        }
+    bool canUseFastMemory = !requiredMode || requiredMode == Signaling;
+    if (!canUseFastMemory || !tryGetFastMemory(vm, memory, mappedCapacity, mode)) {
+        if (mode == Signaling)
+            return nullptr;
+
+        if (Options::simulateWebAssemblyLowMemory() ? true : !mmapBytes(mappedCapacity, memory)) {
+            // Try again with a different number.
+            dataLogLnIf(verbose, "Memory::create mmap failed once for capacity, trying again");
+            mappedCapacity = size;
+            if (!mappedCapacity) {
+                dataLogLnIf(verbose, "Memory::create mmap not trying again because size is zero");
+                return makeEmptyMemory();
+            }
 
-        result = mmap(nullptr, m_mappedCapacity, PROT_NONE, MAP_PRIVATE | MAP_ANON, -1, 0);
-        if (result == MAP_FAILED) {
-            if (verbose)
-                dataLogLn("Memory::Memory mmap failed twice ", *this);
-            failed = true;
-            return;
+            if (!mmapBytes(mappedCapacity, memory)) {
+                dataLogLnIf(verbose, "Memory::create mmap failed twice");
+                return nullptr;
+            }
         }
     }
 
-    ASSERT(m_size <= m_mappedCapacity);
-    {
-        bool success = !mprotect(result, static_cast<size_t>(m_size), PROT_READ | PROT_WRITE);
-        RELEASE_ASSERT(success);
+    ASSERT(memory && size <= mappedCapacity);
+    if (mprotect(memory, size, PROT_READ | PROT_WRITE)) {
+        dataLogLnIf(verbose, "Memory::create mprotect failed");
+        releaseFastMemory(memory, 0, mappedCapacity, mode);
+        if (memory) {
+            if (munmap(memory, mappedCapacity))
+                CRASH();
+        }
+        return nullptr;
     }
+    
+    lastAllocatedMemoryMode = mode;
+    dataLogLnIf(verbose, "Memory::create mmap succeeded");
+    return adoptRef(new Memory(memory, initial, maximum, mappedCapacity, mode));
+}
 
-    m_memory = result;
-    failed = false;
-    if (verbose)
-        dataLogLn("Memory::Memory mmap succeeded ", *this);
+RefPtr<Memory> Memory::create(VM& vm, PageCount initial, PageCount maximum, std::optional<Mode> mode)
+{
+    RELEASE_ASSERT(!maximum || maximum >= initial); // This should be guaranteed by our caller.
+    RefPtr<Memory> result = createImpl(vm, initial, maximum, mode);
+    if (result) {
+        if (result->mode() == Signaling)
+            RELEASE_ASSERT(result->m_mappedCapacity == fastMemoryMappedBytes);
+        if (mode)
+            ASSERT(*mode == result->mode());
+        ASSERT(lastAllocatedMemoryMode == result->mode());
+    }
+    return result;
 }
 
 Memory::~Memory()
 {
-    if (verbose)
-        dataLogLn("Memory::~Memory ", *this);
+    dataLogLnIf(verbose, "Memory::~Memory ", *this);
+    releaseFastMemory(m_memory, m_size, m_mappedCapacity, m_mode);
     if (m_memory) {
         if (munmap(m_memory, m_mappedCapacity))
             CRASH();
@@ -128,23 +246,25 @@ bool Memory::grow(PageCount newSize)
 {
     RELEASE_ASSERT(newSize > PageCount::fromBytes(m_size));
 
-    if (verbose)
-        dataLogLn("Memory::grow to ", newSize, " from ", *this);
+    dataLogLnIf(verbose, "Memory::grow to ", newSize, " from ", *this);
 
     if (maximum() && newSize > maximum())
         return false;
 
-    uint64_t desiredSize = newSize.bytes();
+    size_t desiredSize = newSize.bytes();
 
     if (m_memory && desiredSize <= m_mappedCapacity) {
-        bool success = !mprotect(static_cast<uint8_t*>(m_memory) + m_size, static_cast<size_t>(desiredSize - m_size), PROT_READ | PROT_WRITE);
-        RELEASE_ASSERT(success);
+        if (mprotect(static_cast<uint8_t*>(m_memory) + m_size, static_cast<size_t>(desiredSize - m_size), PROT_READ | PROT_WRITE)) {
+            dataLogLnIf(verbose, "Memory::grow in-place failed ", *this);
+            return false;
+        }
+
         m_size = desiredSize;
-        if (verbose)
-            dataLogLn("Memory::grow in-place ", *this);
+        dataLogLnIf(verbose, "Memory::grow in-place ", *this);
         return true;
     }
 
+    ASSERT(mode() != Signaling);
     // Otherwise, let's try to make some new memory.
     void* newMemory = mmap(nullptr, desiredSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
     if (newMemory == MAP_FAILED)
@@ -159,11 +279,26 @@ bool Memory::grow(PageCount newSize)
     m_mappedCapacity = desiredSize;
     m_size = desiredSize;
 
-    if (verbose)
-        dataLogLn("Memory::grow ", *this);
+    dataLogLnIf(verbose, "Memory::grow ", *this);
     return true;
 }
 
+void Memory::dump(PrintStream& out) const
+{
+    out.print("Memory at ", RawPointer(m_memory), ", size ", m_size, "B capacity ", m_mappedCapacity, "B, initial ", m_initial, " maximum ", m_maximum, " mode ", makeString(m_mode));
+}
+
+const char* Memory::makeString(Mode mode) const
+{
+    switch (mode) {
+    case Mode::BoundsChecking: return "BoundsChecking";
+    case Mode::Signaling: return "Signaling";
+    case Mode::NumberOfModes: break;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+    return "";
+}
+
 } // namespace JSC
 
 } // namespace Wasm
index 236bf85..3384a42 100644 (file)
 
 #if ENABLE(WEBASSEMBLY)
 
-#include "WasmCallingConvention.h"
 #include "WasmPageCount.h"
 
+#include <wtf/Optional.h>
+#include <wtf/RefCounted.h>
+#include <wtf/RefPtr.h>
+
 namespace WTF {
 class PrintStream;
 }
 
-namespace JSC { namespace Wasm {
+namespace JSC {
+
+class VM;
+
+namespace Wasm {
 
-class Memory {
+class Memory : public RefCounted<Memory> {
     WTF_MAKE_NONCOPYABLE(Memory);
     WTF_MAKE_FAST_ALLOCATED;
 public:
     void dump(WTF::PrintStream&) const;
 
     // FIXME: We should support other modes. see: https://bugs.webkit.org/show_bug.cgi?id=162693
-    enum class Mode {
-        BoundsChecking
+    enum Mode {
+        BoundsChecking,
+        Signaling,
+        NumberOfModes
     };
     const char* makeString(Mode) const;
 
+    explicit operator bool() const { return !!m_memory; }
+
+    static RefPtr<Memory> create(VM&, PageCount initial, PageCount maximum, std::optional<Mode> requiredMode = std::nullopt);
+
     Memory() = default;
-    JS_EXPORT_PRIVATE Memory(PageCount initial, PageCount maximum, bool& failed);
-    Memory(Memory&& other)
-        : m_memory(other.m_memory)
-        , m_size(other.m_size)
-        , m_initial(other.m_initial)
-        , m_maximum(other.m_maximum)
-        , m_mappedCapacity(other.m_mappedCapacity)
-        , m_mode(other.m_mode)
-    {
-        // Moving transfers ownership of the allocated memory.
-        other.m_memory = nullptr;
-    }
     ~Memory();
 
     void* memory() const { return m_memory; }
-    uint64_t size() const { return m_size; }
+    size_t size() const { return m_size; }
     PageCount sizeInPages() const { return PageCount::fromBytes(m_size); }
 
     PageCount initial() const { return m_initial; }
     PageCount maximum() const { return m_maximum; }
 
+    static Mode lastAllocatedMode();
     Mode mode() const { return m_mode; }
 
+    // grow() should only be called from the JSWebAssemblyMemory object since that object needs to update internal
+    // pointers with the current base and size.
     bool grow(PageCount);
 
-    static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(Memory, m_memory); }
-    static ptrdiff_t offsetOfSize() { return OBJECT_OFFSETOF(Memory, m_size); }
-    
+    void check() {  ASSERT(!deletionHasBegun()); }
 private:
+    static RefPtr<Memory> createImpl(VM&, PageCount initial, PageCount maximum, std::optional<Mode> requiredMode = std::nullopt);
+    Memory(void* memory, PageCount initial, PageCount maximum, size_t mappedCapacity, Mode);
+    Memory(PageCount initial, PageCount maximum);
+
+    // FIXME: we should move these to the instance to avoid a load on instance->instance calls.
     void* m_memory { nullptr };
-    uint64_t m_size { 0 };
+    size_t m_size { 0 };
     PageCount m_initial;
     PageCount m_maximum;
-    uint64_t m_mappedCapacity { 0 };
+    size_t m_mappedCapacity { 0 };
     Mode m_mode { Mode::BoundsChecking };
 };
 
index 6b063d3..24f1815 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(WEBASSEMBLY)
 
 #include "WasmCallingConvention.h"
+#include "WasmMemory.h"
 #include <wtf/NeverDestroyed.h>
 
 namespace JSC { namespace Wasm {
@@ -48,6 +49,8 @@ const PinnedRegisterInfo& PinnedRegisterInfo::get()
         Vector<unsigned> pinnedSizes = { 0 };
         unsigned remainingPinnedRegisters = pinnedSizes.size() + 1;
         jscCallingConvention().m_calleeSaveRegisters.forEach([&] (Reg reg) {
+            if (!reg.isGPR())
+                return;
             GPRReg gpr = reg.gpr();
             if (!remainingPinnedRegisters || RegisterSet::stackRegisters().get(reg))
                 return;
@@ -71,7 +74,7 @@ PinnedRegisterInfo::PinnedRegisterInfo(Vector<PinnedSizeRegisterInfo>&& sizeRegi
 {
 }
 
-MemoryInformation::MemoryInformation(PageCount initial, PageCount maximum,  bool isImport)
+MemoryInformation::MemoryInformation(VM& vm, PageCount initial, PageCount maximum, std::optional<Memory::Mode> recompileMode, bool isImport)
     : m_initial(initial)
     , m_maximum(maximum)
     , m_isImport(isImport)
@@ -79,6 +82,19 @@ MemoryInformation::MemoryInformation(PageCount initial, PageCount maximum,  bool
     RELEASE_ASSERT(!!m_initial);
     RELEASE_ASSERT(!m_maximum || m_maximum >= m_initial);
     ASSERT(!!*this);
+
+    if (!recompileMode) {
+        if (!isImport) {
+            m_reservedMemory = Memory::create(vm, initial, maximum, Memory::Signaling);
+            if (m_reservedMemory) {
+                ASSERT(!!*m_reservedMemory);
+                m_mode = m_reservedMemory->mode();
+                return;
+            }
+        }
+        m_mode = Memory::lastAllocatedMode();
+    } else
+        m_mode = *recompileMode;
 }
 
 } } // namespace JSC::Wasm
index 6e7f521..5a9a77a 100644 (file)
@@ -28,7 +28,9 @@
 #if ENABLE(WEBASSEMBLY)
 
 #include "GPRInfo.h"
+#include "WasmMemory.h"
 #include "WasmPageCount.h"
+#include <wtf/Ref.h>
 #include <wtf/Vector.h>
 
 namespace JSC { namespace Wasm {
@@ -52,17 +54,22 @@ public:
         ASSERT(!*this);
     }
 
-    MemoryInformation(PageCount initial, PageCount maximum, bool isImport);
+    MemoryInformation(VM&, PageCount initial, PageCount maximum, std::optional<Memory::Mode>, bool isImport);
 
     PageCount initial() const { return m_initial; }
     PageCount maximum() const { return m_maximum; }
+    bool hasReservedMemory() const { return m_reservedMemory; }
+    RefPtr<Memory> takeReservedMemory() { ASSERT(hasReservedMemory()); return m_reservedMemory.release(); }
+    Memory::Mode mode() const { return m_mode; }
     bool isImport() const { return m_isImport; }
 
     explicit operator bool() const { return !!m_initial; }
 
 private:
+    RefPtr<Memory> m_reservedMemory;
     PageCount m_initial { };
     PageCount m_maximum { };
+    Memory::Mode m_mode { Memory::Mode::BoundsChecking };
     bool m_isImport { false };
 };
 
index ce4b296..8d45f95 100644 (file)
@@ -316,7 +316,7 @@ auto ModuleParser::parseMemoryHelper(bool isImport) -> PartialResult
     ASSERT(initialPageCount);
     ASSERT(!maximumPageCount || maximumPageCount >= initialPageCount);
 
-    m_result.module->memory = MemoryInformation(initialPageCount, maximumPageCount, isImport);
+    m_result.module->memory = MemoryInformation(*m_vm, initialPageCount, maximumPageCount, m_mode, isImport);
     return { };
 }
 
index 41b57fc..94ba14e 100644 (file)
@@ -30,6 +30,7 @@
 #include "WasmFormat.h"
 #include "WasmOps.h"
 #include "WasmParser.h"
+#include <wtf/Optional.h>
 #include <wtf/Vector.h>
 
 namespace JSC { namespace Wasm {
@@ -43,12 +44,9 @@ struct ModuleParserResult {
 class ModuleParser : public Parser<ModuleParserResult> {
 public:
 
-    ModuleParser(VM* vm, const uint8_t* sourceBuffer, size_t sourceLength)
+    ModuleParser(VM* vm, const uint8_t* sourceBuffer, size_t sourceLength, std::optional<Memory::Mode> mode)
         : Parser(vm, sourceBuffer, sourceLength)
-    {
-    }
-    ModuleParser(VM* vm, const Vector<uint8_t>& sourceBuffer)
-        : ModuleParser(vm, sourceBuffer.data(), sourceBuffer.size())
+        , m_mode(mode)
     {
     }
 
@@ -68,6 +66,7 @@ private:
     PartialResult WARN_UNUSED_RETURN parseInitExpr(uint8_t&, uint64_t&, Type& initExprType);
 
     ModuleParserResult m_result;
+    std::optional<Memory::Mode> m_mode { std::nullopt };
     bool m_hasTable { false };
 };
 
index 5980b51..4835aa2 100644 (file)
@@ -35,6 +35,7 @@
 #include "WasmB3IRGenerator.h"
 #include "WasmBinding.h"
 #include "WasmCallingConvention.h"
+#include "WasmFaultSignalHandler.h"
 #include "WasmMemory.h"
 #include "WasmModuleParser.h"
 #include "WasmValidate.h"
@@ -61,14 +62,14 @@ Plan::Plan(VM* vm, const uint8_t* source, size_t sourceLength)
 {
 }
 
-bool Plan::parseAndValidateModule()
+bool Plan::parseAndValidateModule(std::optional<Memory::Mode> recompileMode)
 {
     MonotonicTime startTime;
     if (verbose || Options::reportCompileTimes())
         startTime = MonotonicTime::now();
 
     {
-        ModuleParser moduleParser(m_vm, m_source, m_sourceLength);
+        ModuleParser moduleParser(m_vm, m_source, m_sourceLength, recompileMode);
         auto parseResult = moduleParser.parse();
         if (!parseResult) {
             m_errorMessage = parseResult.error();
@@ -109,10 +110,12 @@ bool Plan::parseAndValidateModule()
 // The reason this is OK is that we guarantee that the main thread doesn't continue until all threads
 // that could touch its stack are done executing.
 SUPPRESS_ASAN 
-void Plan::run()
+void Plan::run(std::optional<Memory::Mode> recompileMode)
 {
-    if (!parseAndValidateModule())
+    if (!parseAndValidateModule(recompileMode))
         return;
+    if (recompileMode)
+        ASSERT(m_moduleInformation->memory.mode() == recompileMode);
 
     auto tryReserveCapacity = [this] (auto& vector, size_t size, const char* what) {
         if (UNLIKELY(!vector.tryReserveCapacity(size))) {
index 6980bdb..d070375 100644 (file)
@@ -49,9 +49,9 @@ public:
     JS_EXPORT_PRIVATE Plan(VM*, const uint8_t*, size_t);
     JS_EXPORT_PRIVATE ~Plan();
 
-    bool parseAndValidateModule();
+    bool parseAndValidateModule(std::optional<Memory::Mode> = std::nullopt);
 
-    JS_EXPORT_PRIVATE void run();
+    JS_EXPORT_PRIVATE void run(std::optional<Memory::Mode> = std::nullopt);
 
     JS_EXPORT_PRIVATE void initializeCallees(JSGlobalObject*, std::function<void(unsigned, JSWebAssemblyCallee*, JSWebAssemblyCallee*)>);
 
@@ -92,6 +92,8 @@ public:
         return WTFMove(m_wasmExitStubs);
     }
 
+    Memory::Mode mode() const { return m_moduleInformation->memory.mode(); }
+
 private:
     std::unique_ptr<ModuleInformation> m_moduleInformation;
     Vector<FunctionLocationInBinary> m_functionLocationInBinary;
index c27712a..5171278 100644 (file)
@@ -29,6 +29,7 @@
 #if ENABLE(WEBASSEMBLY)
 
 #include "JSCInlines.h"
+#include "WasmFaultSignalHandler.h"
 
 namespace JSC {
 
@@ -43,11 +44,13 @@ void JSWebAssemblyCallee::finishCreation(VM& vm, Wasm::Entrypoint&& entrypoint)
     Base::finishCreation(vm);
 
     m_entrypoint = WTFMove(entrypoint);
+    Wasm::registerCode(vm, m_entrypoint.compilation->codeRef().executableMemory()->start(), m_entrypoint.compilation->codeRef().executableMemory()->end());
 }
 
 void JSWebAssemblyCallee::destroy(JSCell* cell)
 {
     JSWebAssemblyCallee* thisObject = static_cast<JSWebAssemblyCallee*>(cell);
+    Wasm::unregisterCode(*cell->vm(), thisObject->m_entrypoint.compilation->codeRef().executableMemory()->start(), thisObject->m_entrypoint.compilation->codeRef().executableMemory()->end());
     thisObject->JSWebAssemblyCallee::~JSWebAssemblyCallee();
 }
 
diff --git a/Source/JavaScriptCore/wasm/js/JSWebAssemblyCodeBlock.cpp b/Source/JavaScriptCore/wasm/js/JSWebAssemblyCodeBlock.cpp
new file mode 100644 (file)
index 0000000..b21f8a5
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "JSWebAssemblyCodeBlock.h"
+
+#if ENABLE(WEBASSEMBLY)
+
+#include "JSCInlines.h"
+#include "JSWebAssemblyMemory.h"
+#include "JSWebAssemblyModule.h"
+
+namespace JSC {
+
+const ClassInfo JSWebAssemblyCodeBlock::s_info = { "WebAssemblyCodeBlock", nullptr, 0, CREATE_METHOD_TABLE(JSWebAssemblyCodeBlock) };
+
+JSWebAssemblyCodeBlock::JSWebAssemblyCodeBlock(VM& vm, JSWebAssemblyModule* owner, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmExitStubs>&& wasmExitStubs, Wasm::Memory::Mode mode, unsigned calleeCount)
+    : Base(vm, vm.webAssemblyCodeBlockStructure.get())
+    , m_callLinkInfos(WTFMove(callLinkInfos))
+    , m_wasmExitStubs(WTFMove(wasmExitStubs))
+    , m_mode(mode)
+    , m_calleeCount(calleeCount)
+{
+    m_module.set(vm, this, owner);
+    memset(callees(), 0, m_calleeCount * sizeof(WriteBarrier<JSWebAssemblyCallee>) * 2);
+}
+
+void JSWebAssemblyCodeBlock::destroy(JSCell* cell)
+{
+    static_cast<JSWebAssemblyCodeBlock*>(cell)->JSWebAssemblyCodeBlock::~JSWebAssemblyCodeBlock();
+}
+
+bool JSWebAssemblyCodeBlock::isSafeToRun(JSWebAssemblyMemory* memory)
+{
+    if (mode() == Wasm::Memory::Signaling)
+        return memory->memory().mode() == mode();
+    return true;
+}
+
+void JSWebAssemblyCodeBlock::visitChildren(JSCell* cell, SlotVisitor& visitor)
+{
+    JSWebAssemblyCodeBlock* thisObject = jsCast<JSWebAssemblyCodeBlock*>(cell);
+    ASSERT_GC_OBJECT_INHERITS(thisObject, info());
+
+    Base::visitChildren(thisObject, visitor);
+    visitor.append(thisObject->m_module);
+    for (unsigned i = 0; i < thisObject->m_calleeCount * 2; i++)
+        visitor.append(thisObject->callees()[i]);
+
+    visitor.addUnconditionalFinalizer(&thisObject->m_unconditionalFinalizer);
+}
+
+void JSWebAssemblyCodeBlock::UnconditionalFinalizer::finalizeUnconditionally()
+{
+    JSWebAssemblyCodeBlock* thisObject = bitwise_cast<JSWebAssemblyCodeBlock*>(
+        bitwise_cast<char*>(this) - OBJECT_OFFSETOF(JSWebAssemblyCodeBlock, m_unconditionalFinalizer));
+    for (auto iter = thisObject->m_callLinkInfos.begin(); !!iter; ++iter)
+        (*iter)->visitWeak(*thisObject->vm());
+}
+
+} // namespace JSC
+
+#endif // ENABLE(WEBASSEMBLY)
index fbc1ceb..128025e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 
 namespace JSC {
 
+void JSWebAssemblyInstance::setMemory(VM& vm, ExecState* exec, JSWebAssemblyMemory* memory)
+{
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    // We create stub memories even for modules that should eventually get a memory so we want to avoid recompling there.
+    if (memory->memory()) {
+        auto codeBlock = m_codeBlock->module()->codeBlock(vm, exec, memory);
+        RETURN_IF_EXCEPTION(scope,);
+        m_codeBlock.set(vm, this, codeBlock);
+    }
+    m_memory.set(vm, this, memory);
+}
+
 JSWebAssemblyInstance* JSWebAssemblyInstance::create(VM& vm, Structure* structure, JSWebAssemblyModule* module, JSModuleNamespaceObject* moduleNamespaceObject)
 {
     // FIXME: These objects could be pretty big we should try to throw OOM here.
@@ -67,7 +79,7 @@ void JSWebAssemblyInstance::finishCreation(VM& vm, JSWebAssemblyModule* module,
     m_globals = MallocPtr<uint64_t>::malloc(extraMemorySize);
     heap()->reportExtraMemoryAllocated(extraMemorySize);
 
-    m_module.set(vm, this, module);
+    m_codeBlock.set(vm, this, module->codeBlock());
     m_moduleNamespaceObject.set(vm, this, moduleNamespaceObject);
     putDirect(vm, Identifier::fromString(&vm, "exports"), moduleNamespaceObject, None);
 }
@@ -83,7 +95,7 @@ void JSWebAssemblyInstance::visitChildren(JSCell* cell, SlotVisitor& visitor)
     ASSERT_GC_OBJECT_INHERITS(thisObject, info());
 
     Base::visitChildren(thisObject, visitor);
-    visitor.append(thisObject->m_module);
+    visitor.append(thisObject->m_codeBlock);
     visitor.append(thisObject->m_moduleNamespaceObject);
     visitor.append(thisObject->m_memory);
     visitor.append(thisObject->m_table);
index 99a2c90..8923e16 100644 (file)
@@ -29,6 +29,7 @@
 
 #include "JSDestructibleObject.h"
 #include "JSObject.h"
+#include "JSWebAssemblyCodeBlock.h"
 #include "JSWebAssemblyMemory.h"
 #include "JSWebAssemblyTable.h"
 
@@ -47,10 +48,16 @@ public:
 
     DECLARE_INFO;
 
-    JSWebAssemblyModule* module()
+    JSWebAssemblyModule* module() const
     {
-        ASSERT(m_module);
-        return m_module.get();
+        ASSERT(m_codeBlock);
+        return m_codeBlock->module();
+    }
+
+    JSWebAssemblyCodeBlock* codeBlock() const
+    {
+        ASSERT(m_codeBlock);
+        return m_codeBlock.get();
     }
 
     WriteBarrier<JSCell>* importFunction(unsigned idx)
@@ -70,7 +77,9 @@ public:
     }
 
     JSWebAssemblyMemory* memory() { return m_memory.get(); }
-    void setMemory(VM& vm, JSWebAssemblyMemory* memory) { m_memory.set(vm, this, memory); }
+    // Calling this might trigger a recompile.
+    void setMemory(VM&, ExecState*, JSWebAssemblyMemory*);
+    Wasm::Memory::Mode memoryMode() { return memory()->memory().mode(); }
 
     JSWebAssemblyTable* table() { return m_table.get(); }
     void setTable(VM& vm, JSWebAssemblyTable* table) { m_table.set(vm, this, table); }
@@ -104,7 +113,7 @@ protected:
     }
 
 private:
-    WriteBarrier<JSWebAssemblyModule> m_module;
+    WriteBarrier<JSWebAssemblyCodeBlock> m_codeBlock;
     WriteBarrier<JSModuleNamespaceObject> m_moduleNamespaceObject;
     WriteBarrier<JSWebAssemblyMemory> m_memory;
     WriteBarrier<JSWebAssemblyTable> m_table;
index f4253b5..bf51b4d 100644 (file)
@@ -37,9 +37,10 @@ namespace JSC {
 
 const ClassInfo JSWebAssemblyMemory::s_info = { "WebAssembly.Memory", &Base::s_info, 0, CREATE_METHOD_TABLE(JSWebAssemblyMemory) };
 
-JSWebAssemblyMemory* JSWebAssemblyMemory::create(VM& vm, Structure* structure, Wasm::Memory&& memory)
+JSWebAssemblyMemory* JSWebAssemblyMemory::create(VM& vm, Structure* structure, Ref<Wasm::Memory>&& memory)
 {
-    auto* instance = new (NotNull, allocateCell<JSWebAssemblyMemory>(vm.heap)) JSWebAssemblyMemory(vm, structure, std::forward<Wasm::Memory>(memory));
+    auto* instance = new (NotNull, allocateCell<JSWebAssemblyMemory>(vm.heap)) JSWebAssemblyMemory(vm, structure, WTFMove(memory));
+    instance->m_memory->check();
     instance->finishCreation(vm);
     return instance;
 }
@@ -49,10 +50,13 @@ Structure* JSWebAssemblyMemory::createStructure(VM& vm, JSGlobalObject* globalOb
     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
 }
 
-JSWebAssemblyMemory::JSWebAssemblyMemory(VM& vm, Structure* structure, Wasm::Memory&& memory)
+JSWebAssemblyMemory::JSWebAssemblyMemory(VM& vm, Structure* structure, Ref<Wasm::Memory>&& memory)
     : Base(vm, structure)
     , m_memory(WTFMove(memory))
 {
+    ASSERT(m_memory->refCount() == 1);
+    m_memoryBase = m_memory->memory();
+    m_memorySize = m_memory->size();
 }
 
 JSArrayBuffer* JSWebAssemblyMemory::buffer(VM& vm, JSGlobalObject* globalObject)
@@ -60,12 +64,10 @@ JSArrayBuffer* JSWebAssemblyMemory::buffer(VM& vm, JSGlobalObject* globalObject)
     if (m_bufferWrapper)
         return m_bufferWrapper.get();
 
-    auto destructor = [] (void*) {
-        // We don't need to do anything here to destroy the memory.
-        // The ArrayBuffer backing the JSArrayBuffer is only owned by us,
-        // so we guarantee its lifecycle.
-    };
-    m_buffer = ArrayBuffer::createFromBytes(memory()->memory(), memory()->size(), WTFMove(destructor));
+    // We can't use a ref here since it doesn't have a copy constructor...
+    Ref<Wasm::Memory> protectedMemory = m_memory.get();
+    auto destructor = [protectedMemory = WTFMove(protectedMemory)] (void*) { };
+    m_buffer = ArrayBuffer::createFromBytes(memory().memory(), memory().size(), WTFMove(destructor));
     m_bufferWrapper.set(vm, this, JSArrayBuffer::create(vm, globalObject->m_arrayBufferStructure.get(), m_buffer.get()));
     RELEASE_ASSERT(m_bufferWrapper);
     return m_bufferWrapper.get();
@@ -76,7 +78,7 @@ Wasm::PageCount JSWebAssemblyMemory::grow(ExecState* exec, uint32_t delta, bool
     VM& vm = exec->vm();
     auto throwScope = DECLARE_THROW_SCOPE(vm);
 
-    Wasm::PageCount oldPageCount = memory()->sizeInPages();
+    Wasm::PageCount oldPageCount = memory().sizeInPages();
 
     if (!Wasm::PageCount::isValid(delta)) {
         if (shouldThrowExceptionsOnFailure)
@@ -92,12 +94,16 @@ Wasm::PageCount JSWebAssemblyMemory::grow(ExecState* exec, uint32_t delta, bool
     }
 
     if (delta) {
-        bool success = memory()->grow(newSize);
+        bool success = memory().grow(newSize);
         if (!success) {
+            ASSERT(m_memoryBase == memory().memory());
+            ASSERT(m_memorySize == memory().size());
             if (shouldThrowExceptionsOnFailure)
                 throwException(exec, throwScope, createOutOfMemoryError(exec));
             return Wasm::PageCount();
         }
+        m_memoryBase = memory().memory();
+        m_memorySize = memory().size();
     }
 
     // We need to clear out the old array buffer because it might now be pointing
@@ -110,6 +116,7 @@ Wasm::PageCount JSWebAssemblyMemory::grow(ExecState* exec, uint32_t delta, bool
         m_bufferWrapper.clear();
     }
 
+    memory().check();
     return oldPageCount;
 }
 
@@ -123,13 +130,6 @@ void JSWebAssemblyMemory::destroy(JSCell* cell)
 {
     auto memory = static_cast<JSWebAssemblyMemory*>(cell);
     ASSERT(memory->classInfo() == info());
-    VM& vm = *memory->vm();
-
-    if (memory->m_buffer) {
-        ArrayBufferContents dummyContents;
-        memory->m_buffer->transferTo(vm, dummyContents);
-        memory->m_buffer = nullptr;
-    }
 
     memory->JSWebAssemblyMemory::~JSWebAssemblyMemory();
 }
index fff1415..0eb188d 100644 (file)
@@ -41,25 +41,27 @@ class JSWebAssemblyMemory : public JSDestructibleObject {
 public:
     typedef JSDestructibleObject Base;
 
-    static JSWebAssemblyMemory* create(VM&, Structure*, Wasm::Memory&&);
+    static JSWebAssemblyMemory* create(VM&, Structure*, Ref<Wasm::Memory>&&);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_INFO;
 
-    Wasm::Memory* memory() { return &m_memory; }
+    Wasm::Memory& memory() { return m_memory.get(); }
     JSArrayBuffer* buffer(VM& vm, JSGlobalObject*);
     Wasm::PageCount grow(ExecState*, uint32_t delta, bool shouldThrowExceptionsOnFailure);
 
-    static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(JSWebAssemblyMemory, m_memory) + Wasm::Memory::offsetOfMemory(); }
-    static ptrdiff_t offsetOfSize() { return OBJECT_OFFSETOF(JSWebAssemblyMemory, m_memory) + Wasm::Memory::offsetOfSize(); }
+    static ptrdiff_t offsetOfMemory() { return OBJECT_OFFSETOF(JSWebAssemblyMemory, m_memoryBase); }
+    static ptrdiff_t offsetOfSize() { return OBJECT_OFFSETOF(JSWebAssemblyMemory, m_memorySize); }
 
-protected:
-    JSWebAssemblyMemory(VM&, Structure*, Wasm::Memory&&);
+private:
+    JSWebAssemblyMemory(VM&, Structure*, Ref<Wasm::Memory>&&);
     void finishCreation(VM&);
     static void destroy(JSCell*);
     static void visitChildren(JSCell*, SlotVisitor&);
 
-    Wasm::Memory m_memory;
+    void* m_memoryBase;
+    size_t m_memorySize;
+    Ref<Wasm::Memory> m_memory;
     WriteBarrier<JSArrayBuffer> m_bufferWrapper;
     RefPtr<ArrayBuffer> m_buffer;
 };
index 08fa584..56f20b4 100644 (file)
 
 #include "JSCInlines.h"
 #include "JSWebAssemblyCallee.h"
+#include "JSWebAssemblyCodeBlock.h"
+#include "JSWebAssemblyCompileError.h"
+#include "JSWebAssemblyMemory.h"
 #include "WasmFormat.h"
 #include "WasmMemory.h"
+#include "WasmPlan.h"
 #include <wtf/StdLibExtras.h>
 
 namespace JSC {
 
 const ClassInfo JSWebAssemblyModule::s_info = { "WebAssembly.Module", &Base::s_info, nullptr, CREATE_METHOD_TABLE(JSWebAssemblyModule) };
 
-JSWebAssemblyModule* JSWebAssemblyModule::create(VM& vm, Structure* structure, std::unique_ptr<Wasm::ModuleInformation>&& moduleInformation, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmExitStubs>&& wasmExitStubs, SymbolTable* exportSymbolTable, unsigned calleeCount)
+JSWebAssemblyCodeBlock* JSWebAssemblyModule::buildCodeBlock(VM& vm, ExecState* exec, Wasm::Plan& plan, std::optional<Wasm::Memory::Mode> mode)
 {
-    auto* instance = new (NotNull, allocateCell<JSWebAssemblyModule>(vm.heap, allocationSize(calleeCount))) JSWebAssemblyModule(vm, structure, std::forward<std::unique_ptr<Wasm::ModuleInformation>>(moduleInformation), std::forward<Bag<CallLinkInfo>>(callLinkInfos), std::forward<Vector<Wasm::WasmExitStubs>>(wasmExitStubs), calleeCount);
-    instance->finishCreation(vm, exportSymbolTable);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    // On failure, a new WebAssembly.CompileError is thrown.
+    plan.run(mode);
+    if (plan.failed()) {
+        throwException(exec, scope, createJSWebAssemblyCompileError(exec, vm, plan.errorMessage()));
+        return nullptr;
+    }
+    if (mode)
+        ASSERT(*mode == plan.mode());
+
+    unsigned calleeCount = plan.internalFunctionCount();
+    auto* codeBlock = JSWebAssemblyCodeBlock::create(vm, this, plan.takeCallLinkInfos(), plan.takeWasmExitStubs(), plan.mode(), calleeCount);
+
+    plan.initializeCallees(exec->jsCallee()->globalObject(),
+        [&] (unsigned calleeIndex, JSWebAssemblyCallee* jsEntrypointCallee, JSWebAssemblyCallee* wasmEntrypointCallee) {
+            codeBlock->setJSEntrypointCallee(vm, calleeIndex, jsEntrypointCallee);
+            codeBlock->setWasmEntrypointCallee(vm, calleeIndex, wasmEntrypointCallee);
+        });
+    return codeBlock;
+}
+
+JSWebAssemblyModule* JSWebAssemblyModule::create(VM& vm, ExecState* exec, Structure* structure, uint8_t* source, size_t byteSize)
+{
+    auto* instance = new (NotNull, allocateCell<JSWebAssemblyModule>(vm.heap)) JSWebAssemblyModule(vm, structure);
+
+    instance->finishCreation(vm, exec, source, byteSize);
     return instance;
 }
 
@@ -50,21 +78,61 @@ Structure* JSWebAssemblyModule::createStructure(VM& vm, JSGlobalObject* globalOb
     return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
 }
 
-JSWebAssemblyModule::JSWebAssemblyModule(VM& vm, Structure* structure, std::unique_ptr<Wasm::ModuleInformation>&& moduleInformation, Bag<CallLinkInfo>&& callLinkInfos, Vector<Wasm::WasmExitStubs>&& wasmExitStubs, unsigned calleeCount)
+JSWebAssemblyModule::JSWebAssemblyModule(VM& vm, Structure* structure)
     : Base(vm, structure)
-    , m_moduleInformation(WTFMove(moduleInformation))
-    , m_callLinkInfos(WTFMove(callLinkInfos))
-    , m_wasmExitStubs(WTFMove(wasmExitStubs))
-    , m_calleeCount(calleeCount)
 {
-    memset(callees(), 0, m_calleeCount * sizeof(WriteBarrier<JSWebAssemblyCallee>) * 2);
 }
 
-void JSWebAssemblyModule::finishCreation(VM& vm, SymbolTable* exportSymbolTable)
+JSWebAssemblyCodeBlock* JSWebAssemblyModule::codeBlock(VM& vm, ExecState* exec, JSWebAssemblyMemory* memory)
+{
+    Wasm::Memory::Mode mode = memory->memory().mode();
+
+    for (unsigned i = 0; i < Wasm::Memory::NumberOfModes; ++i) {
+        if (m_codeBlocks[i] && m_codeBlocks[i]->isSafeToRun(memory))
+            return m_codeBlocks[i].get();
+    }
+
+    ASSERT(!m_codeBlocks[mode]);
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    // We don't have a code block for this mode, we need to recompile...
+    Wasm::Plan plan(&vm, static_cast<uint8_t*>(m_sourceBuffer->data()), m_sourceBuffer->byteLength());
+
+    auto* codeBlock = buildCodeBlock(vm, exec, plan, mode);
+    RETURN_IF_EXCEPTION(scope, nullptr);
+
+    ASSERT(plan.exports().size() == m_exportSymbolTable->size());
+    if (!ASSERT_DISABLED) {
+        for (auto& exp : plan.exports())
+            ASSERT_UNUSED(exp, m_exportSymbolTable->contains(exp.field.impl()));
+    }
+
+    ASSERT(mode == codeBlock->mode());
+    m_codeBlocks[mode].set(vm, this, codeBlock);
+    return codeBlock;
+}
+
+void JSWebAssemblyModule::finishCreation(VM& vm, ExecState* exec, uint8_t* source, size_t byteSize)
 {
     Base::finishCreation(vm);
     ASSERT(inherits(vm, info()));
+
+    auto scope = DECLARE_THROW_SCOPE(vm);
+    Wasm::Plan plan(&vm, source, byteSize);
+
+    auto* codeBlock = buildCodeBlock(vm, exec, plan);
+    RETURN_IF_EXCEPTION(scope,);
+
+    // On success, a new WebAssembly.Module object is returned with [[Module]] set to the validated Ast.module.
+    SymbolTable* exportSymbolTable = SymbolTable::create(vm);
+    for (auto& exp : plan.exports()) {
+        auto offset = exportSymbolTable->takeNextScopeOffset(NoLockingNecessary);
+        exportSymbolTable->set(NoLockingNecessary, exp.field.impl(), SymbolTableEntry(VarOffset(offset)));
+    }
+
+    m_sourceBuffer = ArrayBuffer::create(source, byteSize);
+    m_moduleInformation = plan.takeModuleInformation();
     m_exportSymbolTable.set(vm, this, exportSymbolTable);
+    m_codeBlocks[codeBlock->mode()].set(vm, this, codeBlock);
 }
 
 void JSWebAssemblyModule::destroy(JSCell* cell)
@@ -79,18 +147,8 @@ void JSWebAssemblyModule::visitChildren(JSCell* cell, SlotVisitor& visitor)
 
     Base::visitChildren(thisObject, visitor);
     visitor.append(thisObject->m_exportSymbolTable);
-    for (unsigned i = 0; i < thisObject->m_calleeCount * 2; i++)
-        visitor.append(thisObject->callees()[i]);
-
-    visitor.addUnconditionalFinalizer(&thisObject->m_unconditionalFinalizer);
-}
-
-void JSWebAssemblyModule::UnconditionalFinalizer::finalizeUnconditionally()
-{
-    JSWebAssemblyModule* thisObject = bitwise_cast<JSWebAssemblyModule*>(
-        bitwise_cast<char*>(this) - OBJECT_OFFSETOF(JSWebAssemblyModule, m_unconditionalFinalizer));
-    for (auto iter = thisObject->m_callLinkInfos.begin(); !!iter; ++iter)
-        (*iter)->visitWeak(*thisObject->vm());
+    for (unsigned i = 0; i < Wasm::Memory::NumberOfModes; ++i)
+        visitor.append(thisObject->m_codeBlocks[i]);
 }
 
 } // namespace JSC
index 7e051f5..704068c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2016 Apple Inc. All rights reserved.
+ * Copyright (C) 2016-2017 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -29,7 +29,7 @@
 
 #include "JSDestructibleObject.h"
 #include "JSObject.h"
-#include "JSWebAssemblyCallee.h"
+#include "JSWebAssemblyCodeBlock.h"
 #include "UnconditionalFinalizer.h"
 #include "WasmFormat.h"
 #include <wtf/Bag.h>
 
 namespace JSC {
 
+namespace Wasm {
+class Plan;
+}
+
 class SymbolTable;
+class JSWebAssemblyMemory;
 
 class JSWebAssemblyModule : public JSDestructibleObject {
 public:
     typedef JSDestructibleObject Base;
 
-    static JSWebAssemblyModule* create(VM&, Structure*, std::unique_ptr<Wasm::ModuleInformation>&&, Bag<CallLinkInfo>&&, Vector<Wasm::WasmExitStubs>&&, SymbolTable*, unsigned);
+    static JSWebAssemblyModule* create(VM&, ExecState*, Structure*, uint8_t* source, size_t byteSize);
     static Structure* createStructure(VM&, JSGlobalObject*, JSValue);
 
     DECLARE_INFO;
 
     const Wasm::ModuleInformation& moduleInformation() const { return *m_moduleInformation.get(); }
+    RefPtr<Wasm::Memory> takeReservedMemory() { return m_moduleInformation->memory.takeReservedMemory(); }
     SymbolTable* exportSymbolTable() const { return m_exportSymbolTable.get(); }
     Wasm::SignatureIndex signatureIndexFromFunctionIndexSpace(unsigned functionIndexSpace) const
     {
         return m_moduleInformation->signatureIndexFromFunctionIndexSpace(functionIndexSpace);
     }
-    unsigned functionImportCount() const { return m_wasmExitStubs.size(); }
-
-    JSWebAssemblyCallee* jsEntrypointCalleeFromFunctionIndexSpace(unsigned functionIndexSpace)
-    {
-        RELEASE_ASSERT(functionIndexSpace >= functionImportCount());
-        unsigned calleeIndex = functionIndexSpace - functionImportCount();
-        RELEASE_ASSERT(calleeIndex < m_calleeCount);
-        return callees()[calleeIndex].get();
-    }
-
-    JSWebAssemblyCallee* wasmEntrypointCalleeFromFunctionIndexSpace(unsigned functionIndexSpace)
-    {
-        RELEASE_ASSERT(functionIndexSpace >= functionImportCount());
-        unsigned calleeIndex = functionIndexSpace - functionImportCount();
-        RELEASE_ASSERT(calleeIndex < m_calleeCount);
-        return callees()[calleeIndex + m_calleeCount].get();
-    }
-
-    void setJSEntrypointCallee(VM& vm, unsigned calleeIndex, JSWebAssemblyCallee* callee)
-    {
-        RELEASE_ASSERT(calleeIndex < m_calleeCount);
-        callees()[calleeIndex].set(vm, this, callee);
-    }
 
-    void setWasmEntrypointCallee(VM& vm, unsigned calleeIndex, JSWebAssemblyCallee* callee)
-    {
-        RELEASE_ASSERT(calleeIndex < m_calleeCount);
-        callees()[calleeIndex + m_calleeCount].set(vm, this, callee);
-    }
+    // Returns the code block that this module was originally compiled expecting to use. This won't need to recompile.
+    JSWebAssemblyCodeBlock* codeBlock() { return m_codeBlocks[m_moduleInformation->memory.mode()].get(); }
+    // Returns the appropriate code block for the given memory, possibly triggering a recompile.
+    JSWebAssemblyCodeBlock* codeBlock(VM&, ExecState*, JSWebAssemblyMemory*);
 
-    WriteBarrier<JSWebAssemblyCallee>* callees()
-    {
-        return bitwise_cast<WriteBarrier<JSWebAssemblyCallee>*>(bitwise_cast<char*>(this) + offsetOfCallees());
-    }
+private:
+    JSWebAssemblyCodeBlock* buildCodeBlock(VM&, ExecState*, Wasm::Plan&, std::optional<Wasm::Memory::Mode> mode = std::nullopt);
 
-protected:
-    JSWebAssemblyModule(VM&, Structure*, std::unique_ptr<Wasm::ModuleInformation>&&, Bag<CallLinkInfo>&&, Vector<Wasm::WasmExitStubs>&&, unsigned calleeCount);
-    void finishCreation(VM&, SymbolTable*);
+    JSWebAssemblyModule(VM&, Structure*);
+    void finishCreation(VM&, ExecState*, uint8_t* source, size_t byteSize);
     static void destroy(JSCell*);
     static void visitChildren(JSCell*, SlotVisitor&);
 
-private:
-    static size_t offsetOfCallees()
-    {
-        return WTF::roundUpToMultipleOf<sizeof(WriteBarrier<JSWebAssemblyCallee>)>(sizeof(JSWebAssemblyModule));
-    }
-
-    static size_t allocationSize(unsigned numCallees)
-    {
-        return offsetOfCallees() + sizeof(WriteBarrier<JSWebAssemblyCallee>) * numCallees * 2;
-    }
-
-    class UnconditionalFinalizer : public JSC::UnconditionalFinalizer { 
-        void finalizeUnconditionally() override;
-    };
-
-    UnconditionalFinalizer m_unconditionalFinalizer;
+    RefPtr<ArrayBuffer> m_sourceBuffer;
     std::unique_ptr<Wasm::ModuleInformation> m_moduleInformation;
-    Bag<CallLinkInfo> m_callLinkInfos;
     WriteBarrier<SymbolTable> m_exportSymbolTable;
-    Vector<Wasm::WasmExitStubs> m_wasmExitStubs;
-    unsigned m_calleeCount;
+    WriteBarrier<JSWebAssemblyCodeBlock> m_codeBlocks[Wasm::Memory::NumberOfModes];
 };
 
 } // namespace JSC
index 5ae817f..04ed666 100644 (file)
@@ -56,6 +56,8 @@ static EncodedJSValue JSC_HOST_CALL callWebAssemblyFunction(ExecState* exec)
     Wasm::SignatureIndex signatureIndex = wasmFunction->signatureIndex();
     const Wasm::Signature* signature = Wasm::SignatureInformation::get(&vm, signatureIndex);
 
+    // Make sure that the memory we think we are going to run with matches the one we expect.
+    ASSERT(wasmFunction->instance()->codeBlock()->isSafeToRun(wasmFunction->instance()->memory()));
     {
         // Check if we have a disallowed I64 use.
 
index 9747678..af5a5a0 100644 (file)
@@ -84,11 +84,7 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
 
     JSWebAssemblyInstance* instance = JSWebAssemblyInstance::create(vm, instanceStructure, jsModule, moduleRecord->getModuleNamespace(exec));
     RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
-    {
-        // Always start with a dummy Memory, so that wasm -> wasm thunks avoid checking for a nullptr Memory when trying to set pinned registers.
-        Wasm::Memory memory;
-        instance->setMemory(vm, JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), WTFMove(memory)));
-    }
+
 
     // Let funcs, memories and tables be initially-empty lists of callable JavaScript objects, WebAssembly.Memory objects and WebAssembly.Table objects, respectively.
     // Let imports be an initially-empty list of external values.
@@ -178,12 +174,12 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
                 return JSValue::encode(throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import is not an instance of WebAssembly.Memory"))));
 
             Wasm::PageCount expectedInitial = moduleInformation.memory.initial();
-            Wasm::PageCount actualInitial = memory->memory()->initial();
+            Wasm::PageCount actualInitial = memory->memory().initial();
             if (actualInitial < expectedInitial)
                 return JSValue::encode(throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import provided an 'initial' that is too small"))));
 
             if (Wasm::PageCount expectedMaximum = moduleInformation.memory.maximum()) {
-                Wasm::PageCount actualMaximum = memory->memory()->maximum();
+                Wasm::PageCount actualMaximum = memory->memory().maximum();
                 if (!actualMaximum) {
                     return JSValue::encode(
                         throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory import did not have a 'maximum' but the module requires that it does"))));
@@ -194,9 +190,11 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
                         throwException(exec, throwScope, createJSWebAssemblyLinkError(exec, vm, ASCIILiteral("Memory imports 'maximum' is larger than the module's expected 'maximum'"))));
                 }
             }
+
             // ii. Append v to memories.
             // iii. Append v.[[Memory]] to imports.
-            instance->setMemory(vm, memory);
+            instance->setMemory(vm, exec, memory);
+            RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
             break;
         }
         case Wasm::ExternalKind::Global: {
@@ -237,12 +235,17 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
         if (moduleInformation.memory && !hasMemoryImport) {
             RELEASE_ASSERT(!moduleInformation.memory.isImport());
             // We create a memory when it's a memory definition.
-            bool failed;
-            Wasm::Memory memory(moduleInformation.memory.initial(), moduleInformation.memory.maximum(), failed);
-            if (failed)
-                return JSValue::encode(throwException(exec, throwScope, createOutOfMemoryError(exec)));
-            instance->setMemory(vm,
-               JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), WTFMove(memory)));
+            RefPtr<Wasm::Memory> memory;
+            if (moduleInformation.memory.hasReservedMemory())
+                memory = jsModule->takeReservedMemory();
+            else {
+                memory = Wasm::Memory::create(vm, moduleInformation.memory.initial(), moduleInformation.memory.maximum());
+                if (!memory)
+                    return JSValue::encode(throwException(exec, throwScope, createOutOfMemoryError(exec)));
+            }
+            instance->setMemory(vm, exec,
+                JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), memory.releaseNonNull()));
+            RETURN_IF_EXCEPTION(throwScope, encodedJSValue());
         }
     }
 
@@ -265,6 +268,11 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyInstance(ExecState* ex
         }
     }
 
+    if (!instance->memory()) {
+        // Make sure we have a dummy memory, so that wasm -> wasm thunks avoid checking for a nullptr Memory when trying to set pinned registers.
+        instance->setMemory(vm, exec, JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), adoptRef(*(new Wasm::Memory()))));
+    }
+
     // Globals
     {
         ASSERT(numImportGlobals == moduleInformation.firstInternalGlobal);
index 6c887af..51129e3 100644 (file)
@@ -96,12 +96,11 @@ static EncodedJSValue JSC_HOST_CALL constructJSWebAssemblyMemory(ExecState* exec
         }
     }
 
-    bool failed;
-    Wasm::Memory memory(initialPageCount, maximumPageCount, failed);
-    if (failed)
+    RefPtr<Wasm::Memory> memory = Wasm::Memory::create(vm, initialPageCount, maximumPageCount);
+    if (!memory)
         return JSValue::encode(throwException(exec, throwScope, createOutOfMemoryError(exec)));
 
-    return JSValue::encode(JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), WTFMove(memory)));
+    return JSValue::encode(JSWebAssemblyMemory::create(vm, exec->lexicalGlobalObject()->WebAssemblyMemoryStructure(), adoptRef(*memory.leakRef())));
 }
 
 static EncodedJSValue JSC_HOST_CALL callJSWebAssemblyMemory(ExecState* state)
index 8129592..973ba75 100644 (file)
@@ -80,31 +80,8 @@ JSValue WebAssemblyModuleConstructor::createModule(ExecState* state, Structure*
     uint8_t* base = getWasmBufferFromValue(state, state->argument(0), byteOffset, byteSize);
     RETURN_IF_EXCEPTION(scope, { });
 
-    Wasm::Plan plan(&vm, base + byteOffset, byteSize);
-    // On failure, a new WebAssembly.CompileError is thrown.
-    plan.run();
-    if (plan.failed())
-        return throwException(state, scope, createJSWebAssemblyCompileError(state, vm, plan.errorMessage()));
-
-    // On success, a new WebAssembly.Module object is returned with [[Module]] set to the validated Ast.module.
-
-    // The export symbol table is the same for all Instances of a Module.
-    SymbolTable* exportSymbolTable = SymbolTable::create(vm);
-    for (auto& exp : plan.exports()) {
-        auto offset = exportSymbolTable->takeNextScopeOffset(NoLockingNecessary);
-        exportSymbolTable->set(NoLockingNecessary, exp.field.impl(), SymbolTableEntry(VarOffset(offset)));
-    }
-
-    // Only wasm-internal functions have a callee, stubs to JS do not.
-    unsigned calleeCount = plan.internalFunctionCount();
-    JSWebAssemblyModule* result = JSWebAssemblyModule::create(vm, structure, plan.takeModuleInformation(), plan.takeCallLinkInfos(), plan.takeWasmExitStubs(), exportSymbolTable, calleeCount);
-    plan.initializeCallees(state->jsCallee()->globalObject(), 
-        [&] (unsigned calleeIndex, JSWebAssemblyCallee* jsEntrypointCallee, JSWebAssemblyCallee* wasmEntrypointCallee) {
-            result->setJSEntrypointCallee(vm, calleeIndex, jsEntrypointCallee);
-            result->setWasmEntrypointCallee(vm, calleeIndex, wasmEntrypointCallee);
-        });
-
-    return result;
+    scope.release();
+    return JSWebAssemblyModule::create(vm, state, structure, base + byteOffset, byteSize);
 }
 
 WebAssemblyModuleConstructor* WebAssemblyModuleConstructor::create(VM& vm, Structure* structure, WebAssemblyModulePrototype* thisPrototype)
index 5aded8f..0668b1e 100644 (file)
@@ -92,10 +92,11 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
     auto* globalObject = state->lexicalGlobalObject();
 
     JSWebAssemblyModule* module = instance->module();
+    JSWebAssemblyCodeBlock* codeBlock = instance->codeBlock();
     const Wasm::ModuleInformation& moduleInformation = module->moduleInformation();
 
     SymbolTable* exportSymbolTable = module->exportSymbolTable();
-    unsigned functionImportCount = module->functionImportCount();
+    unsigned functionImportCount = codeBlock->functionImportCount();
 
     // FIXME wire up the imports. https://bugs.webkit.org/show_bug.cgi?id=165118
 
@@ -116,8 +117,8 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
             //     a. Let func be an Exported Function Exotic Object created from c.
             //     b. Append func to funcs.
             //     c. Return func.
-            JSWebAssemblyCallee* jsEntrypointCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex);
-            JSWebAssemblyCallee* wasmEntrypointCallee = module->wasmEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex);
+            JSWebAssemblyCallee* jsEntrypointCallee = codeBlock->jsEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex);
+            JSWebAssemblyCallee* wasmEntrypointCallee = codeBlock->wasmEntrypointCalleeFromFunctionIndexSpace(exp.kindIndex);
             Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(exp.kindIndex);
             const Wasm::Signature* signature = Wasm::SignatureInformation::get(&vm, signatureIndex);
             WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->argumentCount(), exp.field.string(), instance, jsEntrypointCallee, wasmEntrypointCallee, signatureIndex);
@@ -182,12 +183,12 @@ void WebAssemblyModuleRecord::link(ExecState* state, JSWebAssemblyInstance* inst
         // The start function must not take any arguments or return anything. This is enforced by the parser.
         ASSERT(!signature->argumentCount());
         ASSERT(signature->returnType() == Wasm::Void);
-        if (startFunctionIndexSpace < module->functionImportCount()) {
+        if (startFunctionIndexSpace < codeBlock->functionImportCount()) {
             JSCell* startFunction = instance->importFunction(startFunctionIndexSpace)->get();
             m_startFunction.set(vm, this, startFunction);
         } else {
-            JSWebAssemblyCallee* jsEntrypointCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(startFunctionIndexSpace);
-            JSWebAssemblyCallee* wasmEntrypointCallee = module->wasmEntrypointCalleeFromFunctionIndexSpace(startFunctionIndexSpace);
+            JSWebAssemblyCallee* jsEntrypointCallee = codeBlock->jsEntrypointCalleeFromFunctionIndexSpace(startFunctionIndexSpace);
+            JSWebAssemblyCallee* wasmEntrypointCallee = codeBlock->wasmEntrypointCalleeFromFunctionIndexSpace(startFunctionIndexSpace);
             WebAssemblyFunction* function = WebAssemblyFunction::create(vm, globalObject, signature->argumentCount(), "start", instance, jsEntrypointCallee, wasmEntrypointCallee, signatureIndex);
             m_startFunction.set(vm, this, function);
         }
@@ -211,6 +212,7 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* state)
 
     {
         JSWebAssemblyModule* module = m_instance->module();
+        JSWebAssemblyCodeBlock* codeBlock = m_instance->codeBlock();
         const Wasm::ModuleInformation& moduleInformation = module->moduleInformation();
         JSWebAssemblyTable* table = m_instance->table();
         for (const Wasm::Element& element : moduleInformation.elements) {
@@ -233,13 +235,13 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* state)
                 // for the import.
                 // https://bugs.webkit.org/show_bug.cgi?id=165510
                 uint32_t functionIndex = element.functionIndices[i];
-                if (functionIndex < module->functionImportCount()) {
+                if (functionIndex < codeBlock->functionImportCount()) {
                     return JSValue::decode(
                         throwVMRangeError(state, scope, ASCIILiteral("Element is setting the table value with an import. This is not yet implemented. FIXME.")));
                 }
 
-                JSWebAssemblyCallee* jsEntrypointCallee = module->jsEntrypointCalleeFromFunctionIndexSpace(functionIndex);
-                JSWebAssemblyCallee* wasmEntrypointCallee = module->wasmEntrypointCalleeFromFunctionIndexSpace(functionIndex);
+                JSWebAssemblyCallee* jsEntrypointCallee = codeBlock->jsEntrypointCalleeFromFunctionIndexSpace(functionIndex);
+                JSWebAssemblyCallee* wasmEntrypointCallee = codeBlock->wasmEntrypointCalleeFromFunctionIndexSpace(functionIndex);
                 Wasm::SignatureIndex signatureIndex = module->signatureIndexFromFunctionIndexSpace(functionIndex);
                 const Wasm::Signature* signature = Wasm::SignatureInformation::get(&vm, signatureIndex);
                 // FIXME: Say we export local function "foo" at function index 0.
@@ -259,8 +261,8 @@ JSValue WebAssemblyModuleRecord::evaluate(ExecState* state)
         const Vector<Wasm::Segment::Ptr>& data = m_instance->module()->moduleInformation().data;
         JSWebAssemblyMemory* jsMemory = m_instance->memory();
         if (!data.isEmpty()) {
-            uint8_t* memory = reinterpret_cast<uint8_t*>(jsMemory->memory()->memory());
-            uint64_t sizeInBytes = jsMemory->memory()->size();
+            uint8_t* memory = reinterpret_cast<uint8_t*>(jsMemory->memory().memory());
+            uint64_t sizeInBytes = jsMemory->memory().size();
             for (auto& segment : data) {
                 if (segment->sizeInBytes) {
                     uint32_t offset;
index 1d73576..24987c3 100644 (file)
@@ -1,3 +1,28 @@
+2017-03-03  Keith Miller  <keith_miller@apple.com>
+
+        WASM should support faster loads.
+        https://bugs.webkit.org/show_bug.cgi?id=162693
+
+        Reviewed by Saam Barati.
+
+        Add new forms of dataLog that take a boolean which describes if the log should happen. This makes cases where we have a static const bool for printing nicer since you can do:
+
+        dataLogIf(verbose, things, to, print);
+
+        instead of:
+
+        if (verbose)
+            dataLog(things, to, print);
+
+        Also, add a operator! to Ref that has the same semantics as C++ refs.
+
+        * wtf/DataLog.h:
+        (WTF::dataLogLn):
+        (WTF::dataLogIf):
+        (WTF::dataLogLnIf):
+        * wtf/Ref.h:
+        (WTF::Ref::operator!):
+
 2017-03-02  Jer Noble  <jer.noble@apple.com>
 
         Sufficently large timeValue and timeScale arguments to MediaTime will cause wrapping in toTimeScale().
index 103424e..4be9481 100644 (file)
@@ -49,13 +49,29 @@ void dataLog(const Types&... values)
 template<typename... Types>
 void dataLogLn(const Types&... values)
 {
-    dataFile().print(values..., "\n");
+    dataLog(values..., "\n");
+}
+
+template<typename... Types>
+void dataLogIf(bool shouldLog, const Types&... values)
+{
+    if (shouldLog)
+        dataLog(values...);
+}
+
+template<typename... Types>
+void dataLogLnIf(bool shouldLog, const Types&... values)
+{
+    if (shouldLog)
+        dataLogLn(values...);
 }
 
 } // namespace WTF
 
 using WTF::dataLog;
 using WTF::dataLogLn;
+using WTF::dataLogIf;
+using WTF::dataLogLnIf;
 using WTF::dataLogF;
 using WTF::dataLogFString;
 
index b3ac4fb..830964f 100644 (file)
@@ -137,6 +137,7 @@ public:
     T* ptr() const { ASSERT(m_ptr); return m_ptr; }
     T& get() const { ASSERT(m_ptr); return *m_ptr; }
     operator T&() const { ASSERT(m_ptr); return *m_ptr; }
+    bool operator!() const { ASSERT(m_ptr); return !*m_ptr; }
 
     template<typename U> Ref<T> replace(Ref<U>&&) WARN_UNUSED_RETURN;