diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/wasm/WasmPlan.cpp | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/JavaScriptCore/wasm/WasmPlan.cpp')
-rw-r--r-- | Source/JavaScriptCore/wasm/WasmPlan.cpp | 273 |
1 files changed, 273 insertions, 0 deletions
diff --git a/Source/JavaScriptCore/wasm/WasmPlan.cpp b/Source/JavaScriptCore/wasm/WasmPlan.cpp new file mode 100644 index 000000000..5980b51e8 --- /dev/null +++ b/Source/JavaScriptCore/wasm/WasmPlan.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (C) 2016 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 "WasmPlan.h" + +#if ENABLE(WEBASSEMBLY) + +#include "B3Compilation.h" +#include "JSCInlines.h" +#include "JSGlobalObject.h" +#include "JSWebAssemblyCallee.h" +#include "WasmB3IRGenerator.h" +#include "WasmBinding.h" +#include "WasmCallingConvention.h" +#include "WasmMemory.h" +#include "WasmModuleParser.h" +#include "WasmValidate.h" +#include <wtf/DataLog.h> +#include <wtf/Locker.h> +#include <wtf/MonotonicTime.h> +#include <wtf/NumberOfCores.h> +#include <wtf/StdLibExtras.h> +#include <wtf/text/StringBuilder.h> + +namespace JSC { namespace Wasm { + +static const bool verbose = false; + +Plan::Plan(VM* vm, Vector<uint8_t> source) + : Plan(vm, source.data(), source.size()) +{ +} + +Plan::Plan(VM* vm, const uint8_t* source, size_t sourceLength) + : m_vm(vm) + , m_source(source) + , m_sourceLength(sourceLength) +{ +} + +bool Plan::parseAndValidateModule() +{ + MonotonicTime startTime; + if (verbose || Options::reportCompileTimes()) + startTime = MonotonicTime::now(); + + { + ModuleParser moduleParser(m_vm, m_source, m_sourceLength); + auto parseResult = moduleParser.parse(); + if (!parseResult) { + m_errorMessage = parseResult.error(); + return false; + } + m_moduleInformation = WTFMove(parseResult->module); + m_functionLocationInBinary = WTFMove(parseResult->functionLocationInBinary); + m_moduleSignatureIndicesToUniquedSignatureIndices = WTFMove(parseResult->moduleSignatureIndicesToUniquedSignatureIndices); + } + + for (unsigned functionIndex = 0; functionIndex < m_functionLocationInBinary.size(); ++functionIndex) { + if (verbose) + dataLogLn("Processing function starting at: ", m_functionLocationInBinary[functionIndex].start, " and ending at: ", m_functionLocationInBinary[functionIndex].end); + const uint8_t* functionStart = m_source + m_functionLocationInBinary[functionIndex].start; + size_t functionLength = m_functionLocationInBinary[functionIndex].end - m_functionLocationInBinary[functionIndex].start; + ASSERT(functionLength <= m_sourceLength); + SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex]; + const Signature* signature = SignatureInformation::get(m_vm, signatureIndex); + + auto validationResult = validateFunction(m_vm, functionStart, functionLength, signature, *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices); + if (!validationResult) { + if (verbose) { + for (unsigned i = 0; i < functionLength; ++i) + dataLog(RawPointer(reinterpret_cast<void*>(functionStart[i])), ", "); + dataLogLn(); + } + m_errorMessage = makeString(validationResult.error(), ", in function at index ", String::number(functionIndex)); // FIXME make this an Expected. + return false; + } + } + + if (verbose || Options::reportCompileTimes()) + dataLogLn("Took ", (MonotonicTime::now() - startTime).microseconds(), " us to validate module"); + return true; +} + +// We are creating a bunch of threads that touch the main thread's stack. This will make ASAN unhappy. +// 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() +{ + if (!parseAndValidateModule()) + return; + + auto tryReserveCapacity = [this] (auto& vector, size_t size, const char* what) { + if (UNLIKELY(!vector.tryReserveCapacity(size))) { + StringBuilder builder; + builder.appendLiteral("Failed allocating enough space for "); + builder.appendNumber(size); + builder.append(what); + m_errorMessage = builder.toString(); + return false; + } + return true; + }; + + if (!tryReserveCapacity(m_wasmExitStubs, m_moduleInformation->importFunctionSignatureIndices.size(), " WebAssembly to JavaScript stubs") + || !tryReserveCapacity(m_unlinkedWasmToWasmCalls, m_functionLocationInBinary.size(), " unlinked WebAssembly to WebAssembly calls") + || !tryReserveCapacity(m_wasmInternalFunctions, m_functionLocationInBinary.size(), " WebAssembly functions") + || !tryReserveCapacity(m_compilationContexts, m_functionLocationInBinary.size(), " compilation contexts")) + return; + + m_unlinkedWasmToWasmCalls.resize(m_functionLocationInBinary.size()); + m_wasmInternalFunctions.resize(m_functionLocationInBinary.size()); + m_compilationContexts.resize(m_functionLocationInBinary.size()); + + for (unsigned importIndex = 0; importIndex < m_moduleInformation->imports.size(); ++importIndex) { + Import* import = &m_moduleInformation->imports[importIndex]; + if (import->kind != ExternalKind::Function) + continue; + unsigned importFunctionIndex = m_wasmExitStubs.size(); + if (verbose) + dataLogLn("Processing import function number ", importFunctionIndex, ": ", import->module, ": ", import->field); + SignatureIndex signatureIndex = m_moduleInformation->importFunctionSignatureIndices.at(import->kindIndex); + m_wasmExitStubs.uncheckedAppend(exitStubGenerator(m_vm, m_callLinkInfos, signatureIndex, importFunctionIndex)); + } + + m_currentIndex = 0; + + auto doWork = [this] { + while (true) { + uint32_t functionIndex; + { + auto locker = holdLock(m_lock); + if (m_currentIndex >= m_functionLocationInBinary.size()) + return; + functionIndex = m_currentIndex; + ++m_currentIndex; + } + + const uint8_t* functionStart = m_source + m_functionLocationInBinary[functionIndex].start; + size_t functionLength = m_functionLocationInBinary[functionIndex].end - m_functionLocationInBinary[functionIndex].start; + ASSERT(functionLength <= m_sourceLength); + SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex]; + const Signature* signature = SignatureInformation::get(m_vm, signatureIndex); + unsigned functionIndexSpace = m_wasmExitStubs.size() + functionIndex; + ASSERT_UNUSED(functionIndexSpace, m_moduleInformation->signatureIndexFromFunctionIndexSpace(functionIndexSpace) == signatureIndex); + ASSERT(validateFunction(m_vm, functionStart, functionLength, signature, *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices)); + + m_unlinkedWasmToWasmCalls[functionIndex] = Vector<UnlinkedWasmToWasmCall>(); + auto parseAndCompileResult = parseAndCompile(*m_vm, m_compilationContexts[functionIndex], functionStart, functionLength, signature, m_unlinkedWasmToWasmCalls[functionIndex], *m_moduleInformation, m_moduleSignatureIndicesToUniquedSignatureIndices); + + if (UNLIKELY(!parseAndCompileResult)) { + auto locker = holdLock(m_lock); + if (!m_errorMessage) { + // Multiple compiles could fail simultaneously. We arbitrarily choose the first. + m_errorMessage = makeString(parseAndCompileResult.error(), ", in function at index ", String::number(functionIndex)); // FIXME make this an Expected. + } + m_currentIndex = m_functionLocationInBinary.size(); + + // We will terminate on the next execution. + continue; + } + + m_wasmInternalFunctions[functionIndex] = WTFMove(*parseAndCompileResult); + } + }; + + MonotonicTime startTime; + if (verbose || Options::reportCompileTimes()) + startTime = MonotonicTime::now(); + + uint32_t threadCount = Options::useConcurrentJIT() ? WTF::numberOfProcessorCores() : 1; + uint32_t numWorkerThreads = threadCount - 1; + Vector<ThreadIdentifier> threads; + threads.reserveCapacity(numWorkerThreads); + for (uint32_t i = 0; i < numWorkerThreads; i++) + threads.uncheckedAppend(createThread("jsc.wasm-b3-compilation.thread", doWork)); + + doWork(); // Let the main thread do some work too. + + for (uint32_t i = 0; i < numWorkerThreads; i++) + waitForThreadCompletion(threads[i]); + + for (uint32_t functionIndex = 0; functionIndex < m_functionLocationInBinary.size(); functionIndex++) { + { + CompilationContext& context = m_compilationContexts[functionIndex]; + SignatureIndex signatureIndex = m_moduleInformation->internalFunctionSignatureIndices[functionIndex]; + String signatureDescription = SignatureInformation::get(m_vm, signatureIndex)->toString(); + { + LinkBuffer linkBuffer(*m_vm, *context.wasmEntrypointJIT, nullptr); + m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation = + std::make_unique<B3::Compilation>(FINALIZE_CODE(linkBuffer, ("WebAssembly function[%i] %s", functionIndex, signatureDescription.ascii().data())), WTFMove(context.wasmEntrypointByproducts)); + } + + { + LinkBuffer linkBuffer(*m_vm, *context.jsEntrypointJIT, nullptr); + linkBuffer.link(context.jsEntrypointToWasmEntrypointCall, FunctionPtr(m_wasmInternalFunctions[functionIndex]->wasmEntrypoint.compilation->code().executableAddress())); + + m_wasmInternalFunctions[functionIndex]->jsToWasmEntrypoint.compilation = + std::make_unique<B3::Compilation>(FINALIZE_CODE(linkBuffer, ("JavaScript->WebAssembly entrypoint[%i] %s", functionIndex, signatureDescription.ascii().data())), WTFMove(context.jsEntrypointByproducts)); + } + } + } + + if (verbose || Options::reportCompileTimes()) { + dataLogLn("Took ", (MonotonicTime::now() - startTime).microseconds(), + " us to compile and link the module"); + } + + // Patch the call sites for each WebAssembly function. + for (auto& unlinked : m_unlinkedWasmToWasmCalls) { + for (auto& call : unlinked) { + void* executableAddress; + if (m_moduleInformation->isImportedFunctionFromFunctionIndexSpace(call.functionIndex)) { + // FIXME imports could have been linked in B3, instead of generating a patchpoint. This condition should be replaced by a RELEASE_ASSERT. https://bugs.webkit.org/show_bug.cgi?id=166462 + executableAddress = call.target == UnlinkedWasmToWasmCall::Target::ToJs + ? m_wasmExitStubs.at(call.functionIndex).wasmToJs.code().executableAddress() + : m_wasmExitStubs.at(call.functionIndex).wasmToWasm.code().executableAddress(); + } else { + ASSERT(call.target != UnlinkedWasmToWasmCall::Target::ToJs); + executableAddress = m_wasmInternalFunctions.at(call.functionIndex - m_wasmExitStubs.size())->wasmEntrypoint.compilation->code().executableAddress(); + } + MacroAssembler::repatchCall(call.callLocation, CodeLocationLabel(executableAddress)); + } + } + + m_failed = false; +} + +void Plan::initializeCallees(JSGlobalObject* globalObject, std::function<void(unsigned, JSWebAssemblyCallee*, JSWebAssemblyCallee*)> callback) +{ + ASSERT(!failed()); + for (unsigned internalFunctionIndex = 0; internalFunctionIndex < m_wasmInternalFunctions.size(); ++internalFunctionIndex) { + WasmInternalFunction* function = m_wasmInternalFunctions[internalFunctionIndex].get(); + + JSWebAssemblyCallee* jsEntrypointCallee = JSWebAssemblyCallee::create(globalObject->vm(), WTFMove(function->jsToWasmEntrypoint)); + MacroAssembler::repatchPointer(function->jsToWasmCalleeMoveLocation, jsEntrypointCallee); + + JSWebAssemblyCallee* wasmEntrypointCallee = JSWebAssemblyCallee::create(globalObject->vm(), WTFMove(function->wasmEntrypoint)); + MacroAssembler::repatchPointer(function->wasmCalleeMoveLocation, wasmEntrypointCallee); + + callback(internalFunctionIndex, jsEntrypointCallee, wasmEntrypointCallee); + } +} + +Plan::~Plan() { } + +} } // namespace JSC::Wasm + +#endif // ENABLE(WEBASSEMBLY) |