d89a1b33c28fbe171c37638189cf9550d1e049f0
[WebKit-https.git] / Source / JavaScriptCore / wasm / WasmPlan.cpp
1 /*
2  * Copyright (C) 2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "WasmPlan.h"
28
29 #if ENABLE(WEBASSEMBLY)
30
31 #include "B3Compilation.h"
32 #include "JSCInlines.h"
33 #include "JSGlobalObject.h"
34 #include "JSWebAssemblyCallee.h"
35 #include "WasmB3IRGenerator.h"
36 #include "WasmBinding.h"
37 #include "WasmCallingConvention.h"
38 #include "WasmMemory.h"
39 #include "WasmModuleParser.h"
40 #include "WasmValidate.h"
41 #include <wtf/DataLog.h>
42 #include <wtf/StdLibExtras.h>
43 #include <wtf/text/StringBuilder.h>
44
45 namespace JSC { namespace Wasm {
46
47 static const bool verbose = false;
48     
49 Plan::Plan(VM* vm, Vector<uint8_t> source)
50     : Plan(vm, source.data(), source.size())
51 {
52 }
53
54 Plan::Plan(VM* vm, const uint8_t* source, size_t sourceLength)
55     : m_vm(vm)
56     , m_source(source)
57     , m_sourceLength(sourceLength)
58 {
59 }
60
61 void Plan::run()
62 {
63     if (verbose)
64         dataLogLn("Starting plan.");
65     {
66         ModuleParser moduleParser(m_vm, m_source, m_sourceLength);
67         if (!moduleParser.parse()) {
68             if (verbose)
69                 dataLogLn("Parsing module failed: ", moduleParser.errorMessage());
70             m_errorMessage = moduleParser.errorMessage();
71             return;
72         }
73         m_moduleInformation = WTFMove(moduleParser.moduleInformation());
74         m_functionLocationInBinary = WTFMove(moduleParser.functionLocationInBinary());
75         m_functionIndexSpace = WTFMove(moduleParser.functionIndexSpace());
76     }
77     if (verbose)
78         dataLogLn("Parsed module.");
79
80     auto tryReserveCapacity = [this] (auto& vector, size_t size, const char* what) {
81         if (UNLIKELY(!vector.tryReserveCapacity(size))) {
82             StringBuilder builder;
83             builder.appendLiteral("Failed allocating enough space for ");
84             builder.appendNumber(size);
85             builder.append(what);
86             m_errorMessage = builder.toString();
87             return false;
88         }
89         return true;
90     };
91     Vector<Vector<UnlinkedWasmToWasmCall>> unlinkedWasmToWasmCalls;
92     if (!tryReserveCapacity(m_wasmToJSStubs, m_moduleInformation->importFunctions.size(), " WebAssembly to JavaScript stubs")
93         || !tryReserveCapacity(unlinkedWasmToWasmCalls, m_functionLocationInBinary.size(), " unlinked WebAssembly to WebAssembly calls")
94         || !tryReserveCapacity(m_wasmInternalFunctions, m_functionLocationInBinary.size(), " WebAssembly functions"))
95         return;
96
97     for (unsigned importIndex = 0; importIndex < m_moduleInformation->imports.size(); ++importIndex) {
98         Import* import = &m_moduleInformation->imports[importIndex];
99         if (import->kind != External::Function)
100             continue;
101         unsigned importFunctionIndex = m_wasmToJSStubs.size();
102         if (verbose)
103             dataLogLn("Processing import function number ", importFunctionIndex, ": ", import->module, ": ", import->field);
104         Signature* signature = m_moduleInformation->importFunctions.at(import->kindIndex);
105         m_wasmToJSStubs.uncheckedAppend(importStubGenerator(m_vm, m_callLinkInfos, signature, importFunctionIndex));
106         m_functionIndexSpace[importFunctionIndex].code = m_wasmToJSStubs[importFunctionIndex].code().executableAddress();
107     }
108
109     for (unsigned functionIndex = 0; functionIndex < m_functionLocationInBinary.size(); ++functionIndex) {
110         if (verbose)
111             dataLogLn("Processing function starting at: ", m_functionLocationInBinary[functionIndex].start, " and ending at: ", m_functionLocationInBinary[functionIndex].end);
112         const uint8_t* functionStart = m_source + m_functionLocationInBinary[functionIndex].start;
113         size_t functionLength = m_functionLocationInBinary[functionIndex].end - m_functionLocationInBinary[functionIndex].start;
114         ASSERT(functionLength <= m_sourceLength);
115         Signature* signature = m_moduleInformation->internalFunctionSignatures[functionIndex];
116         unsigned functionIndexSpace = m_wasmToJSStubs.size() + functionIndex;
117         ASSERT(m_functionIndexSpace[functionIndexSpace].signature == signature);
118
119         String error = validateFunction(functionStart, functionLength, signature, m_functionIndexSpace, m_moduleInformation->memory);
120         if (!error.isNull()) {
121             if (verbose) {
122                 for (unsigned i = 0; i < functionLength; ++i)
123                     dataLog(RawPointer(reinterpret_cast<void*>(functionStart[i])), ", ");
124                 dataLogLn();
125             }
126             m_errorMessage = error;
127             return;
128         }
129
130         unlinkedWasmToWasmCalls.uncheckedAppend(Vector<UnlinkedWasmToWasmCall>());
131         m_wasmInternalFunctions.uncheckedAppend(parseAndCompile(*m_vm, functionStart, functionLength, m_moduleInformation->memory, signature, unlinkedWasmToWasmCalls.at(functionIndex), m_functionIndexSpace));
132         m_functionIndexSpace[functionIndexSpace].code = m_wasmInternalFunctions[functionIndex]->code->code().executableAddress();
133     }
134
135     // Patch the call sites for each WebAssembly function.
136     for (auto& unlinked : unlinkedWasmToWasmCalls) {
137         for (auto& call : unlinked)
138             MacroAssembler::repatchCall(call.callLocation, CodeLocationLabel(m_functionIndexSpace[call.functionIndex].code));
139     }
140
141     m_failed = false;
142 }
143
144 void Plan::initializeCallees(JSGlobalObject* globalObject, std::function<void(unsigned, JSWebAssemblyCallee*)> callback)
145 {
146     ASSERT(!failed());
147     for (unsigned internalFunctionIndex = 0; internalFunctionIndex < m_wasmInternalFunctions.size(); ++internalFunctionIndex) {
148         WasmInternalFunction* function = m_wasmInternalFunctions[internalFunctionIndex].get();
149         CodeLocationDataLabelPtr calleeMoveLocation = function->calleeMoveLocation;
150         JSWebAssemblyCallee* callee = JSWebAssemblyCallee::create(globalObject->vm(), WTFMove(function->code), WTFMove(function->jsToWasmEntryPoint));
151
152         MacroAssembler::repatchPointer(calleeMoveLocation, callee);
153
154         if (verbose)
155             dataLogLn("Made Wasm callee: ", RawPointer(callee));
156
157         callback(internalFunctionIndex, callee);
158     }
159 }
160
161 Plan::~Plan() { }
162
163 } } // namespace JSC::Wasm
164
165 #endif // ENABLE(WEBASSEMBLY)