Wasm should support call_indirect
[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.size = moduleParser.functionIndexSpace().size();
76         m_functionIndexSpace.buffer = moduleParser.functionIndexSpace().releaseBuffer();
77     }
78     if (verbose)
79         dataLogLn("Parsed module.");
80
81     auto tryReserveCapacity = [this] (auto& vector, size_t size, const char* what) {
82         if (UNLIKELY(!vector.tryReserveCapacity(size))) {
83             StringBuilder builder;
84             builder.appendLiteral("Failed allocating enough space for ");
85             builder.appendNumber(size);
86             builder.append(what);
87             m_errorMessage = builder.toString();
88             return false;
89         }
90         return true;
91     };
92     Vector<Vector<UnlinkedWasmToWasmCall>> unlinkedWasmToWasmCalls;
93     if (!tryReserveCapacity(m_wasmToJSStubs, m_moduleInformation->importFunctions.size(), " WebAssembly to JavaScript stubs")
94         || !tryReserveCapacity(unlinkedWasmToWasmCalls, m_functionLocationInBinary.size(), " unlinked WebAssembly to WebAssembly calls")
95         || !tryReserveCapacity(m_wasmInternalFunctions, m_functionLocationInBinary.size(), " WebAssembly functions"))
96         return;
97
98     for (unsigned importIndex = 0; importIndex < m_moduleInformation->imports.size(); ++importIndex) {
99         Import* import = &m_moduleInformation->imports[importIndex];
100         if (import->kind != External::Function)
101             continue;
102         unsigned importFunctionIndex = m_wasmToJSStubs.size();
103         if (verbose)
104             dataLogLn("Processing import function number ", importFunctionIndex, ": ", import->module, ": ", import->field);
105         Signature* signature = m_moduleInformation->importFunctions.at(import->kindIndex);
106         m_wasmToJSStubs.uncheckedAppend(importStubGenerator(m_vm, m_callLinkInfos, signature, importFunctionIndex));
107         m_functionIndexSpace.buffer.get()[importFunctionIndex].code = m_wasmToJSStubs[importFunctionIndex].code().executableAddress();
108     }
109
110     for (unsigned functionIndex = 0; functionIndex < m_functionLocationInBinary.size(); ++functionIndex) {
111         if (verbose)
112             dataLogLn("Processing function starting at: ", m_functionLocationInBinary[functionIndex].start, " and ending at: ", m_functionLocationInBinary[functionIndex].end);
113         const uint8_t* functionStart = m_source + m_functionLocationInBinary[functionIndex].start;
114         size_t functionLength = m_functionLocationInBinary[functionIndex].end - m_functionLocationInBinary[functionIndex].start;
115         ASSERT(functionLength <= m_sourceLength);
116         Signature* signature = m_moduleInformation->internalFunctionSignatures[functionIndex];
117         unsigned functionIndexSpace = m_wasmToJSStubs.size() + functionIndex;
118         ASSERT(m_functionIndexSpace.buffer.get()[functionIndexSpace].signature == signature);
119
120         String error = validateFunction(functionStart, functionLength, signature, m_functionIndexSpace, *m_moduleInformation);
121         if (!error.isNull()) {
122             if (verbose) {
123                 for (unsigned i = 0; i < functionLength; ++i)
124                     dataLog(RawPointer(reinterpret_cast<void*>(functionStart[i])), ", ");
125                 dataLogLn();
126             }
127             m_errorMessage = error;
128             return;
129         }
130
131         unlinkedWasmToWasmCalls.uncheckedAppend(Vector<UnlinkedWasmToWasmCall>());
132         m_wasmInternalFunctions.uncheckedAppend(parseAndCompile(*m_vm, functionStart, functionLength, signature, unlinkedWasmToWasmCalls.at(functionIndex), m_functionIndexSpace, *m_moduleInformation));
133         m_functionIndexSpace.buffer.get()[functionIndexSpace].code = m_wasmInternalFunctions[functionIndex]->code->code().executableAddress();
134     }
135
136     // Patch the call sites for each WebAssembly function.
137     for (auto& unlinked : unlinkedWasmToWasmCalls) {
138         for (auto& call : unlinked)
139             MacroAssembler::repatchCall(call.callLocation, CodeLocationLabel(m_functionIndexSpace.buffer.get()[call.functionIndex].code));
140     }
141
142     m_failed = false;
143 }
144
145 void Plan::initializeCallees(JSGlobalObject* globalObject, std::function<void(unsigned, JSWebAssemblyCallee*)> callback)
146 {
147     ASSERT(!failed());
148     for (unsigned internalFunctionIndex = 0; internalFunctionIndex < m_wasmInternalFunctions.size(); ++internalFunctionIndex) {
149         WasmInternalFunction* function = m_wasmInternalFunctions[internalFunctionIndex].get();
150         CodeLocationDataLabelPtr calleeMoveLocation = function->calleeMoveLocation;
151         JSWebAssemblyCallee* callee = JSWebAssemblyCallee::create(globalObject->vm(), WTFMove(function->code), WTFMove(function->jsToWasmEntryPoint));
152
153         MacroAssembler::repatchPointer(calleeMoveLocation, callee);
154
155         if (verbose)
156             dataLogLn("Made Wasm callee: ", RawPointer(callee));
157
158         callback(internalFunctionIndex, callee);
159     }
160 }
161
162 Plan::~Plan() { }
163
164 } } // namespace JSC::Wasm
165
166 #endif // ENABLE(WEBASSEMBLY)